2

I was doing a hello world project to run on my x86 bare metal physical machine , but the code didn't run (boot) and the machine proceded for the next boot device, I wrote the code in assembly , the following steps was what I have done :

step 1 : code to display characters A abd B by Bios calls

.L1 :
mov %ah,0x0E     
mov %bh,0x00     
mov %bl,0x07     
mov %al,65   
INT $0x10    

mov %ah,0x0E     
mov %bh,0x00     
mov %bl,0x07     
mov %al,66   
INT $0x10   
jmp .L1

step 2 :

assembling the code and producing the code in binary format :

as -o Code Code.txt
objcopy -O binary Code binfile

step 3 :

opened HxD and displayed the binary form of the assembly code and padded it with zeros till the last two bytes of the first 512 byte which i padded them with 55AA (in hex) to look like :

enter image description here

step 4 :

copied the image to the flash memory:

dd bs=512 count=1 if=binfile of="\\.\e:"

(Note : I am using dd on windows)


  • Note : relied on the fact that the bootstrap code in the first sector will display the two characters didn't use anu further boot loader cause thats my first project so i manged to keep it simple.

  • The chacters are not displayed and a message invalid bootable device is displayed, the device skip to next device and boot windows normally .. what I am missing here ?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Eng_Boody
  • 183
  • 8
  • In AT&T syntax, the instruction `mov %ah,0x0E` stores the contents of the `ah` register into memory location `0x0e`, which is surely not what you intend. What you want here is `mov $0x0E, %ah`, where the `$` indicates an immediate operand. However, gnu as is generally not recommended for 16-bit code, so you may want to switch to nasm or another assembler instead. – Nate Eldredge Mar 26 '20 at 00:50
  • I'm not qualified to answer your question, but I *think* the last two bytes should be 0xAA55 on little endian. – rfmodulator Mar 26 '20 at 00:52
  • Also, you'll have to read your dd manual to see if this is really writing to sector zero of `e:`. It may instead be writing to the first sector of whichever partition is mapped to `e:`. – Nate Eldredge Mar 26 '20 at 00:52
  • 1
    @rfmodulator: I think that part is correct: for a master boot record, it's supposed to be the word `0xAA55`, which is the two bytes `0x55 0xAA`. – Nate Eldredge Mar 26 '20 at 00:54
  • It's also possible that your BIOS is expecting to see a valid partition table in the master boot record, in which case you'll have to insert appropriate data in your file (or partition the device first and write your code within the existing valid MBR). – Nate Eldredge Mar 26 '20 at 00:56
  • @NateEldredge I'm probably incorrect, but shouldn't that mean that in the hex editor you see AA 55? – rfmodulator Mar 26 '20 at 00:58
  • 2
    @rfmodulator: No, I don't think so. The hex editor is displaying bytes. The byte at offset 510 is `55` and the byte at offset 511 is `AA`, which together in little endian form the word `0xAA55`. – Nate Eldredge Mar 26 '20 at 00:59
  • @NateEldredge , even if the code is not doing what is intended to be done .. it should freeze and go into infinite loop not to proceed , concerning the partition table I tried making MBR formatting using diskpart .. then take image ...to local disk modify first 512 byte only to keep the partiion table .. write the modified image back and still didn't at least go to infinite loop – Eng_Boody Mar 26 '20 at 01:00
  • 1
    @NateEldredge Okay, thanks for the clarification... I assume UEFI would also have to be disabled? Sorry to hijack Eng_Boody! – rfmodulator Mar 26 '20 at 01:01
  • 1
    But the partition table is *in* the first 512 bytes, so by writing your file, you've overwritten it with zeros. – Nate Eldredge Mar 26 '20 at 01:01
  • Oh, and you have to add `.code16` at the top of your assembly file to get gnu as to even try to produce 16 bit code. At present it's producing 32 bit code, which is another problem. – Nate Eldredge Mar 26 '20 at 01:01
  • 1
    @rfmodulator: Yes, that's another good point: the BIOS may not be set to boot from an MBR. – Nate Eldredge Mar 26 '20 at 01:06
  • @NateEldredge no my machine is real and it is 64 bit not 16 .. I am not using emulators / simulators.. but a question to u what is the offeset that represent the bountry between the partition table and and the last bit of the bootstrap ? .. – Eng_Boody Mar 26 '20 at 01:06
  • But an x86 machine always boots in 16-bit mode (real mode). If you want to get into 32 or 64 bit mode, your boot code has to make that switch by itself. You'll see this done in the boot code of Linux or any other OS. – Nate Eldredge Mar 26 '20 at 01:07
  • 1
    @rfmodulator I opened the bios config and enabled legacy boot and fixed the boot sequence already .. i didn't miss that – Eng_Boody Mar 26 '20 at 01:07
  • The structure of an MBR should be well documented. If https://en.wikipedia.org/wiki/Master_boot_record is correct, the partition table starts at offset 446. – Nate Eldredge Mar 26 '20 at 01:09
  • another point I am in doubt of but not sure about it (it need standard specs) is @ wikipedia page .. it makes difference between legacy MBR and modern standard MBR , see this https://en.wikipedia.org/wiki/Master_boot_record in structure but the size is the same , both end by same signature at last two bytes – Eng_Boody Mar 26 '20 at 01:11
  • 1
    They are backwards compatible. The "modern standard" one has you put certain data in space that would otherwise be available for arbitrary code. I would expect that the "classical generic" format would work, since your BIOS should be able to boot a very old OS that never knew about the "modern standard" format. – Nate Eldredge Mar 26 '20 at 01:17
  • Something not mentioned, but not an immediate problem until you start using absolute addresses (like the address of a label) - you are converting an object file to a binary file. This will work as long as there are no relocations fixups but will fail miserably if there are. You can use `as` to assemble with `as --32 -o Code.o Code.txt` then use LD to link like: `ld -melf_i386 -Ttext=0x7c00 -o Code Code.o` and then use objcopy to convert the ELF executable to binary with `objcopy -O binary Code binfile` – Michael Petch Mar 26 '20 at 16:36

1 Answers1

3

Collecting some issues discussed in the comments:

  1. Every x86 machine boots in 16-bit real mode, so your boot code needs to be 16-bit code. You need to specify .code16 at the top of your source file to get gnu as to produce that. If you want to eventually execute in 64-bit long mode, you have to write the code to make the mode switch. This is well documented in the CPU manuals, but is a bit complicated. (Note that after doing so, you will no longer be able to call the int 10h BIOS functions, as they are also 16-bit code.)

  2. In AT&T assembly syntax, which is what gnu as uses by default, loading immediate 0x0e into the ah register is written as mov $0x0e, %ah. Your version will instead store the contents of %ah into memory location 0x0e, which is not what you want. This applies to nearly every instruction in your code. But you may want to instead use a different assembler such as nasm that is better designed to deal with 16-bit code.

  3. The master boot record (sector zero of a hard disk) is supposed to contain the partition table for the disk, starting at offset 446. Yours only contains zeros there. In some sense that should not matter, because your code never gets as far as attempting to read anything from the disk, but it may be leading your BIOS to decide that the MBR is invalid and the disk is not bootable.

  4. I'm not convinced that your dd command is actually writing to the master boot record of the disk. It may instead be writing to the boot sector of one of its partitions. I'm not very familiar with dd for windows so you'll need to sort that out for yourself.

I am not sure if your problem is caused by one of these issues or something else, but you'll need to eventually fix them all anyway.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • 3
    It is in fact writing to the volume boot record (partition) and not the MBR. One way around that is to use [Chrysocome DD](http://www.chrysocome.net/dd) . The latest version has the OD= option where you specify the drive letter (ie: E:) and OD= will start writing relative to the beginning of the media and not the volume boot record. I wrote some info in this answer: https://stackoverflow.com/a/34108769/3857942 . If you are booting from USB you may need a BPB if BIOS is booting as USB FDD media. I discuss potential issues here: https://stackoverflow.com/a/47320115/3857942 – Michael Petch Mar 26 '20 at 03:14
  • 1
    Oops the link to a previous answer that discusses DD on Windows was this answer: https://stackoverflow.com/a/32897160/3857942 . That answer also discusses the problem with writing to the VBR and not the MBR. – Michael Petch Mar 26 '20 at 03:19
  • Fun fact: in this particular case, `mov reg8, imm16` actually has the same machine code in both 16 and 64-bit mode. (mov [absolute], reg8` doesn't though. So (un?)fortunately the OP's code would happen to work after just fixing `.intel_syntax noprefix` writing it properly in AT&T syntax, even without `.code16`. But yes you definitely want `.code16` to assemble code that will execute in 16-bit mode. Anything using 16-bit or 32-bit registers or 16/32-bit immediates or displacements will be the wrong size with/without a `66` or `67` prefix.) – Peter Cordes Mar 26 '20 at 06:06
  • 1
    @PeterCordes Surely you mean `mov re8, imm8`. – fuz Mar 26 '20 at 10:57
  • @fuz: yes, thanks. I was already thinking about the imm16 case where instruction length depends on operand-size. >. – Peter Cordes Mar 26 '20 at 10:58