Loading the second bootloader queue

I am trying to create a small operating system for x86 machines and started writing code for a fairly minimal bootloader. The bootloader I created is pretty simple, it loads a small second bootloader from a sector located immediately after the main boot record and jumps to this code. The bootloader code in the master boot record seems to be working fine, the problem occurs when it tries to go to the second stage bootloader. It is assumed that this second stage loader should output a letter indicating success (letter S) so that I can say that the code is executing. The problem is that nothing appears on the screen, so I suspect that the second stage bootloader has never been executed. The code I used was as follows:

Bootloader in master boot record:

[BITS 16] ; 16 bit mode [ORG 0x7C00] ; Boot loader start address Boot: ; Initial, dl contains drive number ; Set data segment to code segment mov ax, cs mov ds, ax mov es, ax ; Set the stack segment to 0xA000 add ax, 0xA000 mov ss, ax mov sp, 0x00 ; Reset the drive, dl contains drive number mov ah, 0x00 int 0x13 ; Read from drive, dl contains drive number ; Set up output location to 0x7E00: 0x00 mov ax, 0x7E00 mov es, ax ; Load to 0x7E00 : 0x00 mov bx, 0x00 ReadDrive: mov ah, 0x02 mov al, 0x01 ; Read 1 sector mov ch, 0x00 ; Read on cylinder 0 mov cl, 0x02 ; Read sector 2 mov dh, 0x00 ; Head number 0 int 0x13 jnc Success ; Print error (character F) mov al, 0x46 call PrintChar jmp ReadDrive ; Retry PrintChar: ; Prints a single character pusha mov ah, 0x09 mov bh, 0x00 mov bl, 0x0F mov cx, 0x01 int 0x10 popa ret Success: jmp 0x7E00:0x00 ; Jump to 2nd stage bootloader TIMES 510 - ($ - $$) db 0 DW 0xAA55 ; Boot signature 

Code for the second stage bootloader:

 [BITS 16] [ORG 0x7E00] Boot2: ; Prints the character S to the screen mov al, 0x53 mov ah, 0x09 mov bh, 0x00 mov bl, 0x0F mov cx, 0x01 int 0x10 jmp $ ; Loop forever TIMES 512 - ($ - $$) db 0 ; Fill rest of block 

This code has been compiled and written to disk using the following code:

 nasm boot.asm -o boot.bin -f bin nasm boot2.asm -o boot2.bin -f bin dd if=boot.bin of=/dev/sd3 bs=512 dd if=boot2.bin of=/dev/sd3 bs=512 seek=1 

The device this code was written to was a 16 gigabyte USB drive. The computer that I used to download this code supports booting from USB and loads them like any other hard drive. What is the reason the code is not working?

+4
source share
1 answer

There are a number of problems in the code. I will try to identify some of them. Some useful reference material can be found in some of the answers I wrote for Stackoveflow.

  • General bootloader tips that contain general recommendations and assumptions that you do not want to make in the bootloader
  • Information about errors associated with improper DS setup and garbage collection when accessing memory variables. This refers to the second stage.
  • An answer to a question that resembles yours may also provide some useful information.

Stack

You configured the stack, but it potentially overlaps the video memory. Although this is probably not related to your problems, this is a potential problem. With this code:

 add ax, 0xA000 mov ss, ax mov sp, 0x00 

You set SS = 0xa000 and SP = 0x0000. This sets the stack, but unfortunately the first value inserted on the stack will be 0xa000: (0x0000-2) = 0xa000: 0xfffe. 0xa000: 0xfffe may be in the video memory. Perhaps you intended to make ss = 0x9000, so the first value on the stack should be at 0x9000: 0xfffe. Thereโ€™s a snag too. Extended Bios Data Area (EBDA) may be located in this region. Some BIOSes incorrectly return the wrong size for this area. In most cases, it is 0k to 4k in size just below the physical address 0xa0000. If you consider the worst case scenarios, I would go with the stack just below that.

 add ax, 0x9000 mov ss, ax mov sp, 0xF000 ; Bottom of stack at 0x9000:0xF000 

Memory address 0x7e00

There are two problems here. In your question, you suggest you try reading the second step in the area just above the bootloader. That would be at physical address 0x7e00. Your code does this:

 ; Read from drive, dl contains drive number ; Set up output location to 0x7E00: 0x00 mov ax, 0x7E00 mov es, ax ; Load to 0x7E00 : 0x00 mov bx, 0x00 

16-bit Segment: offset pairs use this calculation to match the physical memory address: (segment <4) + offset (<4 - the same as multiplying by 16). This means that 0x7E00: 0x00 is the address of the physical memory (0x7E00 <4) + 0 = 0x7e000. This is clearly wrong. I believe that you intended to do this:

 mov ax, 0x07E0 mov es, ax ; Load to 0x07E0:0x00 mov bx, 0x00 

0x07E0: 0x00 - physical memory address (0x07E0 <4) + 0 = 0x7e00. This is the area just above the bootloader loaded into memory at the physical address 0x7c00. A similar problem occurs when you FAR JMP in the second step with this code:

 jmp 0x7E00:0x00 ; Jump to 2nd stage bootloader 

Must be:

 jmp 0x07E0:0x00 ; Jump to 2nd stage bootloader 

Potential problems for the second stage

If you mentioned the previously proposed change ( jmp 0x07E0:0x00 ), then FAR JMP will change CS: IP to CS = 0x07E0 (segment), IP = 0x0000 (offset) and continue execution there. You need your ORG directive to match the offset (IP) from which you go from the first step. Since the offset (IP) is 0x0000, your ORG directive should match:

 [ORG 0x0000] 

You also need to make sure that when your second phase starts loading, DS is also configured to match. One way to achieve this is to explicitly copy the CS code segment into the DS data segment. This can be done with the code at the top of the second step as follows:

 mov ax, cs mov ds, ax 

Without a properly configured DS data segment, all variable references will use the wrong segment and probably will not indicate where they actually reside in memory. Your code currently has no variables, so you won't notice a problem.


Do not assume that the 1st step is invoked by the BIOS with CS: IP = 0x0000: 0x7c00

In my general bootloader tips mentioned in this answer prolog, tip number 1 is very important:

  • When the BIOS goes to your code, you cannot rely on CS, DS, ES, SS, SP registers having real or expected values. They must be configured properly at bootloader startup. You can be guaranteed that your bootloader will be loaded and started from the physical address 0x00007c00 and that the boot disk number will be loaded into the DL register.

In your code, your loader has the following:

 [BITS 16] ; 16 bit mode [ORG 0x7C00] ; Boot loader start address Boot: ; Initial, dl contains drive number ; Set data segment to code segment mov ax, cs mov ds, ax mov es, ax 

[ORG 0x7C00] excellent, but there is speculation that the CS segment is set to 0x0000 when it reaches our bootloader. Then we set DS = CS. The usual wisdom for a naive bootloader is that the BIOS switches to 0x0000: 0x7c00 (CS: IP). The ORG must match the offset (in this case, IP). The problem is that the BIOS actually goes to the physical address 0x00007c00, but it can do this with a lot of CS: IP pairs.

The BIOS may have JMP'ed FAR (or equivalent) to our code with jmp 0x07c0:0x0000 , and some emulators and real hardware do this this way. 0x07c0: 0x0000 - physical address (0x07c0 <4) + 0 = 0x7c00. This is fine, but note that IP = 0x0000. We installed [ORG 0x7C00] . That would be a mismatch! How do we get around this if we really donโ€™t know what CS: the IP pair the BIOS calls us to do? Simple - do not copy CS to DS in the first step of the bootloader. Since we need an offset of 0x7c00, DS must be 0x0000 to work. We must explicitly place 0x0000 in our data segment (DS). The code might look like this:

 [BITS 16] ; 16 bit mode [ORG 0x7C00] ; Boot loader start address Boot: ; Initial, dl contains drive number ; Set data segment to code segment xor ax, ax ; AX=0 mov ds, ax ; DS=0 mov es, ax ; ES=0 
+5
source

Source: https://habr.com/ru/post/1013776/


All Articles