Madhusoodan P
Madhusoodan P

Reputation: 667

Int 10H not working in QEMU

I am learning x86 real mode programming, and have written a small bootloader using QEMU to test it. I have chosen GNU assembler for learning.

Here is assembly code:

#
# boot.s
# 

.section .text
.globl start
start:
//setup stack    
    mov     $0x7c0,     %ax
    mov     %ax,        %ss
    mov     $512,       %sp

    //setup video
    mov     $0x0,       %eax
    mov     $0x0,       %al
    int     $0x10

    //print a character say 'm'
    mov     $'m',      %al
    mov     $0x0E,      %ah
    int     $0x10
1:
    jmp 1b

The following text is shown on the QEMU display:

Booting from Hard disk...

The problem: The message above is printed, and it remains like that appearing to do nothing.

The script I used to assemble, link is:

> to assemble :     gcc -c boot.s
> to link     :     ld -T link.ld boot.o -o b.bin
> to put on bootsector of Hard-disk image
    dd if=b.bin of=HD.img conv=notrunc
> to attach boot magic 
    echo -ne "\x55\xaa" | dd seek=510 bs=1 of=HD.img
> to emulate:        qemu-system-i386 HD.img

The linker script copied from an online tutorial I saw somewhere, since I didn't know how to create one myself:

OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys) {
    code = .;
    *(.text)
    *(.rodata)
    . = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
    data = .;
    *(.data)
    . = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
    bss = .;
    *(.bss)
    . = ALIGN(4096);
}
end = .;
}

Do I need to specify any extra arguments or there is some mistake in code? I thought it's the setting up of stack but tried many possibilities but it didn't work.

How do I get past the hard disk booting message and have my bootloader display the letter m on the screen?

My working platform is Fedora 23.

Upvotes: 3

Views: 1142

Answers (1)

Michael Petch
Michael Petch

Reputation: 47573

It appears that your linker script was written for an environment where a protected mode kernel would be loaded at 0x00100000. This is common for bootloaders conforming to the multiboot specification. Real mode bootloader like yours will be read from the first 512 bytes of the disk and placed at 0x7c00 in memory. You'll need a linker script that uses an origin point of 0x7c00. You can also use the linker script to output the boot disk signature 0xAA55.

A basic bootloader linker script could look something like this link.ld:

OUTPUT_FORMAT("binary");
ENTRY(start);
SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00) {
        *(.text);
    }
    .data : SUBALIGN(0) {
        *(.data);
        *(.rodata);
    }
    .bss : SUBALIGN(4) {
        __bss_start = .;
        *(COMMON);
        *(.bss)
        . = ALIGN(4);
        __bss_end = .;
    }
    __bss_sizel = SIZEOF(.bss)>>2;
    __bss_sizeb = SIZEOF(.bss);

    /* Boot signature */
    .sig : AT(0x7DFE) {
        SHORT(0xaa55);
    }
}

Your boot.s is not being told to generate 16-bit code. You can place the directive .code16 at the top of your assembly file to force it to generate 16-bit instructions. You will also have to amend your GCC command to compile as 16-bit code, and the linker will need the -melf_i386 option.

Your bootloader code should really set the DS register as well as the stack. Since the linker script assumes code is located at memory location 0x07c00 we'll need to set DS to zero. In segmented model a physical address = (segment<<4)+offset . DS set to 0, and an origin (base offset) of 0x7c00 would map to (0<<4)+0x7c00 = physical address 0x07c00 which is where our bootloader is loaded by the BIOS.

I wrote an answer with some general bootloader tip that may be of some value.

Cleaned up boot.s could look like:

#
# boot.s
#

.code16
.section .text
.globl start
start:
    //setup stack
    mov     $0x7c0, %ax
    mov     %ax, %ss
    mov     $512, %sp

    xor     %ax, %ax    # AX = 0
    mov     %ax, %ds    # Set DS = 0 since origin point is 0x7c00

    //setup video
    xor     %ax, %ax    # Zero 16-bit AX register (includes AL and AH)
    //mov   $0x0, %ax   # Works but is not preferred for zeroing a reg
    int     $0x10

    //print a character say 'm'
    mov     $'m',      %al
    mov     $0x0E,      %ah
    int     $0x10
1:
    jmp 1b

To assemble, link and generate a disk image you could use:

gcc -c boot.s -m16
ld -melf_i386 -T link.ld boot.o -o b.bin -nostdlib --nmagic
dd if=b.bin of=HD.img conv=notrunc

You could then run it using the image as a floppy:

qemu-system-i386 -fda HD.img

Or as a hard drive image using:

qemu-system-i386 -hda HD.img

Upvotes: 8

Related Questions