0

Okay, so I've been trying to make a two-step bootloader in assembly/C but I haven't been able to get the JMP working. At first I thought the read was failing, but, after the following test I ruled that out:

__asm__ __volatile__(
    "xorw %ax, %ax;"
    "movw %ax, %ds;"
    "movw %ax, %es;"
    "movb $0x02, %ah;"
    "movb $0x01, %al;"
    "movw $0x7E00, %bx;"
    "movw $0x0003, %cx;"
    "xorb %dh, %dh;"
    "int $0x13;"
    "movb 0x7E00, %al;"
    "movb $0x0e, %ah;"
    "int $0x10;"
    //"jmp 0x7E00"
);

This printed 'f' as expected (the first byte of the sector is 0x66 which is the ASCII code for 'f') proving that the read is successful and the jmp is the problem. This is my code:

__asm__(".code16\n");
__asm__(".code16gcc\n");
__asm__("jmpl $0x0000, $main\n");
void main(){
    __asm__ __volatile__(
        "xorw %ax, %ax;"
        "movw %ax, %ds;"
        "movw %ax, %es;"
        "movb $0x02, %ah;"
        "movb $0x01, %al;"
        "movw $0x7E00, %bx;"
        "movw $0x0003, %cx;"
        "xorb %dh, %dh;"
        "int $0x13;"
        "jmp $0x200;"
    );
}

When run, my program simply hangs, this means that the program is probably jumping to the wrong location in memory. By the way, I am obviously running this in real mode under VMWare player. I am compiling this with the following commands:

gcc -c -0s -march=i686 -ffreestanding -Wall -Werror boot.c -o boot.o
ld -static -Ttest.ld -nostdlib --nmagic -o boot.elf boot.o --no-check-sections
objcopy -0 binary boot.elf boot.bin

and this is test.ld:

ENTRY(main);
SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00)
    {
        *(.test);
    }
    .sig : AT(0x7DFE)
    {
        SHORT(0xAA55);
    }
}

Note: I have confirmed this is not a problem with the inline asm - I have tried a pure assembly implementation too with the same results - the only reason I am using C is because I plan on expanding this a bit and I am much more comfortable with C loops and functions...

EDIT: I've uploaded the first three sectors of my floppy drive here

EDIT 2: I have been unable to get my boot loader working using any of the suggestions and, based on advice from @RossRidge I have written an assembly version of the same program and a simple assembly program to echo input. Sadly these aren't working either..

Bootloader:

org 0x7c00
xor ax, ax
mov ds, ax
mov es, ax
mov ah, 0x02
mov al, 0x01
mov bx, 0x7E00
mov cx, 0x0003
xor dh, dh
int 0x13
jmp 0x7E00

Program in sector 3:

xor ax, ax
int 0x16
mov ah, 0xe
int 0x10

These are both compiled with: nasm Linux/boot.S -o Linux/asm.bin and behave the same as their C counterparts..

DividedByZero
  • 4,333
  • 2
  • 19
  • 33
  • 1
    What is at location `0x7e00`? – Michael Burr May 23 '15 at 21:00
  • Have you tried telling your C compiler to give you the assembly output to verify that everything is actually where you expect it to be? Additionally, I am not sure that I see the point of moving $ff into $7e00 if you're only going to overwrite this with data. Lastly, where is the code that you're loading at $7e00? Are you certain that it's not this code that is blowing up? You might try running under Bochs with a debugger attached to see what's really happening. – David Hoelzer May 23 '15 at 21:01
  • @MichaelBurr I am reading sector 3 (1 based) of the boot disk to this location in memory. Another program is stored at this sector and I have confirmed it's not this code that's not working by booting into it directly. – DividedByZero May 23 '15 at 21:05
  • @DavidHoelzer I used the `movb $0xff, 0x7E00;` line for debugging.. forgot to remove it :P I checked the program at sector 3 and it is perfectly fine. I will check the assembly produced by gcc in a minute – DividedByZero May 23 '15 at 21:09
  • 2
    Don't use inline assembly to write boot sectors, use normal assembly and the assembler directly. – Ross Ridge May 23 '15 at 21:23
  • @RossRidge I agree with you but there are two reasons for me using C: I thought my assembly was causing problems and so I thought I would use a more familiar language and I also needed to add a few more functions to my boot loader which are easier to implement in C – DividedByZero May 23 '15 at 21:32
  • 3
    Inline assembly doesn't make assembly any more easy to understand, and you can't use your 32-bit compiler to generate code for a 16-bit boot sector. Even with a 16-bit compiler it's not very practical to write boot sector code in C. Boot sectors are the domain of pure assembly programs. – Ross Ridge May 23 '15 at 21:36
  • @RossRidge I have written an assembly version of both programs and added them to my question because they behave the same as the C versions – DividedByZero May 24 '15 at 15:45

2 Answers2

3

I am pretty sure your assembler is generating the wrong jump offset, it's probably interpreting the 0x7e00 as relative in the current text section, but that's mapped at 0x7c00 by the linker script so your jump probably goes to 0x7c00+0x7e00=0xfa00 instead. As an ugly workaround you could try jmp 0x200 instead or use an indirect jump to hide it from your tools such as mov $0x7e00, %ax; jmp *%ax. Alternatively jmp .text+0x200 could also work.

Also note that writing 16 bit asm in a 32 bit C compiler will generate wrong code, which is also hinted by the first byte of the loaded sector being 0x66 which is the operand size override prefix. Since the compiler assumes 32 bit mode, it will generate your 16 bit code with prefixes, which when run in 16 bit mode will switch back to 32 bits. In general, it's not a good idea to abuse inline asm for such purpose, you should use a separate asm file and make sure you tell your assembler that the code is intended for 16 bit real mode.

PS: learn to use a debugger.


Update: The following code when assembled using nasm boot.asm -o boot.bin works fine in qemu and bochs:

org 0x7c00
xor ax, ax
mov ds, ax
mov es, ax
mov ah, 0x02
mov al, 0x01
mov bx, 0x7E00
mov cx, 0x0003
xor dh, dh
int 0x13
jmp 0x7E00

times 510-($-$$) db 0
dw 0xaa55
; second sector
times 512 db 0
; Program in sector 3:
xor ax, ax
int 0x16
mov ah, 0xe
int 0x10
jmp $
times 1536-($-$$) db 0

You can download an image here (limited offer ;)).

Jester
  • 56,577
  • 4
  • 81
  • 125
  • Thanks, I haven't been able to find a debugger for systems programming - as an application programmer, systems programming is a new world for me.. can you recommend a good debugger? – DividedByZero May 23 '15 at 21:26
  • Yes. Connect GDB to Bochs as I suggested in a comment to your original post. :) – David Hoelzer May 23 '15 at 21:28
  • Depends on your environment and what you consider "good". For a beginner debugging boot sectors, `bochs` built-in debugger is normally sufficient. `gdb` can also connect to `bochs` or `qemu` but `gdb` is not very good with 16 bit code. No idea what works with vmware. Also in this case simply disassembling the generated code to verify it's correct is sufficient. – Jester May 23 '15 at 21:28
  • Suggest you run QEMU under linux for better debugging – abligh May 23 '15 at 21:41
  • @Jester I tried all three of your suggestions but sadly none worked for me – DividedByZero May 23 '15 at 21:54
  • Edit your question, add all the code so we can try. Optionally, upload the first 3 sectors of your image somewhere. – Jester May 23 '15 at 21:57
  • The first instruction of your 3rd sector is `jmp dword 0x0:0x7cfd` which points back to the 1st sector and that's clearly wrong. The `jmp` you originally asked about is now fine, it does jump to `0x7e00`. – Jester May 23 '15 at 22:44
  • @RandomUser VMWare is a very poor choice. Not only is it difficult to get a debugger attached to it properly but it will not mimic actual hardware, especially if you start dealing with saga modes and such. – David Hoelzer May 24 '15 at 09:29
  • @davidhoelzer I'm sorry but I'm totally blank about "saga modes and such" – DividedByZero May 24 '15 at 09:49
  • Oh, sorry, Autocorrect got me. SVGA modes and such. You'll find that VMWare does not actually emulate all hardware well. VirtualBox, Qemu and Bochs tend to be better for boot sector style development, in my opinion, in addition to having better external debugger attachment support. – David Hoelzer May 24 '15 at 10:04
  • @Jester I have written the same programs in assembly and obviously there are no linker scripts either but they behave the same. I also removed the `jmp dword 0x0:0x7cfd` line but once again, no luck.. I've edited my question to include the new code – DividedByZero May 24 '15 at 15:47
  • The `Edit 2` code only works if you use `org 0x7c00` otherwise the jump will again go to the wrong place. Also, that code has no end, you should at least put an endless loop (`jmp $`). – Jester May 24 '15 at 17:50
  • @Jester after adding this line at the top of my bootloader code `org ox7c00` I tested it and, once again, it doesn't work.. :( – DividedByZero May 24 '15 at 20:36
  • Added full code I tested with to my answer, along with an image file. – Jester May 24 '15 at 21:09
  • @Jester I am.. confused.. the image I made looks exactly the same as yours (I checked in a hex editor) and yet yours works but mine doesn't.. Thanks anyway – DividedByZero May 24 '15 at 21:32
0

I have written many boot loaders.

There needs to be two(2) separate executables,

1) the boot loader
2) the main code

so the loaded code (the main program) can be updated without doing anything to the boot loader.

The boot loader, when done needs to jump to a known location.

The main program needs to place a jump instruction at the known location, that jumps to a function in the librt library. (usually start())

start() will set I/O, heap, etc and call main()

Both the linker command file for the boot loader and the linker command file for the main program must agreed on the address of the known location

The known location is usually the only thing in a unique section of both linker command files.

The boot loader must be smart enough to place all the parts of the main program in the right areas in memory.

It is best if the main program is in motorola S1 (or similar) format rather than a raw .elf or .coff format; otherwise the boot loader can get very big very quickly.

user3629249
  • 16,402
  • 1
  • 16
  • 17