1

I am writing my own bootloader in NASM x86 on x86_64 architechture, for starters I am just trying to copy the existing bootloader into second sector by using dd, then copy it back and run it from assembly.

file.asm

org 0x7c00

jmp 0:start

start:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x8000

mov           ah, 0x02
mov           al, 1
mov           dl, 0x80
mov           ch, 0
mov           dh, 0
mov           cl, 3
mov           bx, 0x7e00
int           0x13

jmp 0x7e00
times 510-($-$$) db 0
dw 0xaa55

times (1024 - ($ - $$)) db 0x00
third_sector:
mov           ah, 0x02
mov           al, 1
mov           dl, 0x80
mov           ch, 0
mov           dh, 0
mov           cl, 2
mov           bx, 0x7c00
int           0x13

jmp           0x7c00

The code sets up the stack, then pats itself to 1024 bytes with zeroes to have the last bit of code in third sector of the disk, then it loads the second sector from the disk into the first one and jumps to the first sector.

src.sh

#!/bin/bash
dd bs=1 count=512 if=/dev/c0d0 of=tmp
nasm -f bin file.asm -o file 
dd bs=1 count=1200 if=file of=/dev/c0d0
dd bs=1 count=512 seek=512 if=tmp of=/dev/c0d0

What I am doing is copying the original bootloader into temporary file called tmp, then compiling my programme and putting it into old bootloader's place (/dev/c0d0 since I am working on MINIX 3.3.0), then moving old bootloader into second sector.

The result is "Booting from hard disk" string, which is fine, and then I get "NetBSD MBR boot Error P" which corresponds to "no netBSD partition".

Edit: Pasted the wrong asm file. Second edit: Error changed, but still persists.

MWaw
  • 144
  • 1
  • 2
  • 11
  • is that source complete? What instructions are after `mov sp, 0x8000`?? BTW, you can include the old bootloader directly into your source by `incbin`... but if that old bootloader is already in second sector, you can just compile + store first sector loader only. And move that "third_sector" code into first sector loader, then move it to some spare memory to be not overwritten by loading original, load original, and jump to the start of it. – Ped7g Apr 02 '17 at 16:37
  • @Ped7g I've never heard of incbin, can you elaborate on that? But still, I might not be able to use that, so I'd like to do it the 'hard' way. – MWaw Apr 02 '17 at 16:44
  • Can you check, if the old bootloader starts by the safety `jmp 0:0x7c00` far jump? – Ped7g Apr 02 '17 at 16:46
  • @Ped7g The same error persists, netBSD doesn't see a partition, but from what I see here it is after MBR stage https://wiki.netbsd.org/how_netbsd_boots_on_x86/. – MWaw Apr 02 '17 at 16:50
  • I don't know what you did, I asked about disassembly (start of) of original bootloader... about your current code. I'm not os-dev interested, so I never followed it closely, but doesn't the BIOS load only first boot sector, ie. only 512B? So your third part is not loaded? You have to cram everything into your first 512B I believe, and either load further sectors from there, or move the code bits up by memcpy, so you can load original boot sector from 7c00 and execute it. – Ped7g Apr 02 '17 at 16:54
  • @Ped7g I kinda don't know how to get the original bootloader code, so sorry for that. About the second part - from what I read and heard, it is possible to first read sectors then jump to them in that fashion, I can cram everything in first 512B without trouble, but then after reading the old bootloader I can no longer jump to the beginning. – MWaw Apr 02 '17 at 17:06

2 Answers2

2

I can cram everything in first 512B without trouble, but then after reading the old bootloader I can no longer jump to the beginning

Of course you can't both run in the 7C00-7DFF area, and also load other sector into it, so what you need is to prepare some code which will run outside of 7C00-7DFF, it will load the original boot sector to the expected 0000:7C00, and execute it just as BIOS would.

I tried to modify your code to work like that, but I have no means to debug/verify if it works, so proceed with caution (I used ndisasm to disassemble resulting binary, and everything looks OK, ie. the relocated code does not depend on it's address, so you can move it freely to any memory, and it should load+execute that sector to 7C00.

Just make sure you don't add anything to that code, what would break after relocation to 7e00.

org 0x7c00

jmp 0:start

start:
mov ax, cs     ; ax = 0
mov ds, ax
mov es, ax
mov ss, ax
mov sp, ax     ; ss:sp = 0:0
; relocate second boot loader code outside of 7C00-7DFF (to 7E00)
mov si, boot_loader_code   ; address of code to relocate
mov di, 0x7e00             ; new address of code
mov cx, boot_loader_code_length
cld
rep movsb
; execute the relocated second boot loader
jmp 0x7e00

boot_loader_code:
    ; load second boot loader to 0:7c00 and execute it
    mov     ah, 0x02    ; load sector service
    mov     al, 1       ; load 1 sector
    ; mov     dl, 0x80    ; drive 0
    ; DL is set by BIOS originally
    ; and this code did/will not change it
    mov     ch, 0       ; track/cylinder
    mov     dh, 0       ; head number
    mov     cl, 3       ; sector number
    mov     bx, 0x7c00  ; es:bx pointer to buffer
    int     0x13        ; BIOS "load sector" service
    jmp     0:0x7c00
boot_loader_code_length EQU ($ - boot_loader_code)

    times   510-($-$$) db 0
    dw      0xaa55

still isn't working

Well, you can hardly program in assembler without debugger, there's too much what can go wrong and every detail may matter. When you are experienced in asm, you can pull it out, but if you are learning, get some PC emulator where you can also debug things, like bochs. And use documentation first (like that incbin comment from you, it's described in NASM docs of course, what it does exactly).

I adjusted the source by comments of Michael, but I have still no means to verify/debug it (too lazy to install bochs + learn how to set up bootloader sector), but the code itself is "ok", it does mostly what comments say. Whether that is enough or correct to boot a PC machine, that's different question. It just fixes obvious problem of your code in question to trying to have "third" part outside of your 512B single sector block.

Ped7g
  • 16,236
  • 3
  • 26
  • 63
  • Well, still isn't working, right now it just freezes, but thanks for the input! – MWaw Apr 02 '17 at 18:52
  • 1
    If you are going to use `movsb` and related string instructions I highly recommend using _CLD_ to clear the direction flag to guarantee forward movement. As well the boot drive the BIOS booted from is place in the _DL_ register. Usually frowned upon to hard code a drive value like 0x80. Use the one passed to the original bootloader and ensure that value is still in _DL_ prior to the final `jmp 0:0x7c00` – Michael Petch Apr 02 '17 at 18:59
  • 1
    I'd consider changing SP to 0 instead of 8000.You'll be moving code into the same 512 byte area where the stack is growing down through. It may seem insignificant but some `int 0x13` calls (depends on BIOS) may allocate a fair amount of stack space to do the work. By setting SP to 0 you can stay in the first 64kb but start the stack pointer growing down from the top of the first 64kb area resulting in less likelihood the stack will interfere with running code. Often I'll set SP to 0x7c00 so it grows down towards bottom of memory. I usually make sure where I put it there is a good 4kb available – Michael Petch Apr 02 '17 at 19:36
  • @MichaelPetch thanks, I tried to patch it to follow your advices, *looks* good to me. But I don't plan to actually try it out, so who knows... – Ped7g Apr 02 '17 at 19:49
  • 1
    This isn't actually the problem. The code in the question loads the third sector of the disk to 0x7e00, jumps to it, and it's that code that loads the second sector of the to 0x7c00 and then jump to it. – Ross Ridge Apr 02 '17 at 21:18
2

The NetBSD volume boot record (called a partition boot record or PBR by NetBSD) reads the first block of the disk it was booted from and checks to see if its the same as itself. If it's not then it assumes the disk has an MBR and tries to find the NetBSD boot partition in the MBR. It does this check by a simple comparison of the first 4 bytes of itself as loaded in memory, and the first four bytes of the first sector on disk.

Since your replacement boot sector doesn't start with the exact same 4 bytes its interpreted as an MBR with a partition table. Since your boot sector doesn't actually have an MBR partition table the NetBSD VBR isn't able to find the NetBSD partition and so prints error P (maybe or error no slice, depending on how it was configured).

To solve this problem you'll need to copy your disk's partition table into your replacement MBR. Note this also necessary because NetBSD itself (or whatever you're booting) will also want to read the partition table.

If your disk doesn't have an MBR with a partition table, and instead starts with the NetBSD VBR (PBR) then there isn't much you can do to fix the problem. In that case when the NetBSD VBR boots normally, it loads the first 15 sectors into memory at 0000:1000, checks that the first four bytes match itself as described above, and if so jumps to the code loaded at 0000:1000. Since you're changing the first 3 sectors of the disk you're also overwriting the NetBSD disk label at sector 2 and the start of what NetBSD calls the phase 1 bootstrap (bootxx) at sector 3.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112