Implementing bootloader #2

Started by ggnfs000, January 07, 2017, 06:01:22 PM

Previous topic - Next topic

ggnfs000

Implementing bootloader #2

5/5/16 update: I found several defects plus did following to pinpoint the underlying problem, which turned out to be BIOS Int13h extended disk read 42h was causing system to freeze. I had many useful suggestions from O/S developers forum including setup stack as Int 13h function might very well use the stack and use Linux's QEMU to debug.
Here is the memory configuration map at a time bootloader is operating:

To setup stack, used the memory address at 0x03c0 for size of 64 bytes.

DOS header at 0x0000 as a result of building exe file is not used, therefore simply ignored.
The code segment starts at file offset 0x0200 and first 512 bytes from this offset is copied to sector 0 by copyraw.exe. Therefore BIOS will copy this sector to memory address 0x7c00 and give control of execution to this piece of code.
The stack segment size of 64 bytes are the last 64 bytes of this sector, which trashes the partition table however I decided to keep it this way for now. Once bootloader needs to load the file from the FAT32 formatted partition, this stack will need to be located somewhere else.
After that DAP area starts from 0x0400 till 0x600 which is aligned at memory address 0x7e00h. The DAP area itself is only 16 bytes, but I just allocate the whole 512 bytes at 7e00h in case. Perhaps rest of the area at 0x7e00 would have better been used for stack.
The offset 0x0600 is the start of executable code that will be loaded by this bootloader. This is aligned at memory address 0x8000. Therefore bootloader will copy to this area and jump to this address afterward.

Here are the part of code which shows how it was implemented:

code segment para public use16 'code'
assume cs:code, ds:data,ss:sta

...

;   Prepare to jump and perform the jump.

    sub     si, si
    mov     ds, si
    mov     si, 8000h           ; (DS:SI) = location, 0:8000h to jump to, pt of no ret.

    jmp     data:JmpAddr


;   Should never reach here. Also up to this point should better not exceed 512 bytes.

    main    endp
org 200h-64
code    ends
sta segment para stack use16 'stack'
    db      64 dup (033h)
sta ends
data segment para public 'data'


JmpAddr:
    fileEnd    db  55h, 0aah, 66h, 0bbh
data ends
    end     main



Once boot.asm is built as boot.bin, the offsets were checked by using Dos Navigator utility and hiew.ru disassembler:


Found a lot of bug in the rest of boot.asm and exp.asm once everything fixed, bootloader is finally working. I discovered the Oracle Virtual Box has a very nice full-fledged debug capability which I immediately used it to fix the last issue: jumping to 8000h was not working.

To turn on oracle's Virtual box debugging feature while starting the Virtual Machine, I launched the VM with debugging option:

virtualbox --startvm <vmname> --dbg

Resulting VM window has debug menu from which the debug console opened and all the register dump/memory unassembly and single-stepping and other useful commands were available.

Through single step, found out the compiler has interpreted the jump to offset 0x0400 in boot.asm as relative jump from code segment:

 JMP 20:200h (relative jump offset from code segment)
instead of
JMP 0:8000h (absolute jump to this memory address)

In addition, the compiler would not allow (will not compile) if I do an absolute jump so decided to just type in the opcode directly:

db      0eah, 00h, 80h, 00h, 00h

and bootloader is finally functioning. This is the opcode for absolute jump to 0000:8000h.
In the picture below line containing arrya of E4 is printed by bootloader and 56 is printed by executable code loaded by bootloader.


There was one single problem with this absolute jump:

db      0eah, 00h, 80h, 00h, 00h

Once this absolute jump is performed, the CS:IP is loaded with 0000:8000h. That would be problematic when the loaded executable uses instruction involving offsets with regards to the beginning of code segment. For example, following code will not work:


jmp   @f
labelSomeWhereInCodeSegment:
db    'Message1$'
@@: 

lea si, cs:labelSomeWhereInCodeSegment 
...
<code to print the bytes at
labelSomeWhereInCodeSegment>

...
Why? Because the SI in the example above will be loaded with offset with regards to CS or code segment. But after the jump is made, CS=0x0000, but executable code is loaded to segment at 0x8000. So if the label is 0x51 bytes from the 0x8000 segment, desired location of the printed message is 0x8051 but in actuality it will access memory area 0x0051 since CS=0000.
To alleviate this problem, small modification to absolute jump will take care of this problem:


db      0eah, 00h, 80h, 00h, 00h   ; jump to 0000:8000
changed to:
db      0eah, 00h, 00h, 00h, 08h   ; jump to 0800:0000

This will not change the address of the jump address it will only change the way it is loaded to CS:IP after the jump:
With ea00800000 it is jump to 0000:8000 and once jump is performed, CS:IP will be 0000:8000 which we discussed above it is problematic. We want CS pointing to segment at 8000 so that instruction such that loading effective address: lea, CS:<label> and offset CS:<label> to work.
Change to second jump instruction ea00000008 is jump to 0800:0000 after that CS:IP will be 800:0000. It is still the same location (800:0000=0000:8000) that both of these are jumping to but once jump is made on the way change is made, CS is finally pointing to segment at 8000h instead of 0000h.

5.10.2016.
Couple of maintenance work needed in order to do a smoother development further:
- Earlier I have noted that the hyper-V formatted vhd and other types of HDD will not boot after written by copyraw.exe. Also SimNow did not work either. Only HDD works is the Virtual Box vdi created on my workstation boots the bootloader written by my copyraw.exe. So far I haven't figured it out why. To make matter more peculiar, another instance of VM in my HP server with same VDI formatted HDD fails to boot. This might be worth troubleshooting.
- Recently, I changed the the bootloader code to place the stack segment after DAP area, so that partition table on MBR (sector 0) will not be trashed. This dictates, I need to develop some snippet in bootloader code that can open and load executable file in FAT32 formatted HDD. This way, only bootloader needs to be copied in raw, rest of the disk content will be regular FAT32 along with partition table in MBR. Extra benefit on this implementation is that once bootloader is present, no need use copyraw, unless MBR is corrupted, the executable file simply needs to be copied to HDD and expects the system to boot from it. I will have to think about how to load files from FAT32 file system and the code should fit also within 1 sector (512) - 64 (ptable) on HDD.







Source: Implementing bootloader #2