0

I tried to use below sample bootloader from Internet.

__asm__(".code16gcc\n");
__asm__ ("jmpl  $0, $main\n");


#define __NOINLINE  __attribute__((noinline))
#define __REGPARM   __attribute__ ((regparm(3)))
#define __NORETURN  __attribute__((noreturn))

/* BIOS interrupts must be done with inline assembly */
void    __NOINLINE __REGPARM print(const char   *s){

    __asm__ __volatile__("movb %0 , %%al\n" : :"c"(*s):"memory");
    //__asm__ __volatile__("movb $'W' , %al\n");
    __asm__ __volatile__("movb $0x0e, %ah\n");
    __asm__ __volatile__("int  $0x10\n");
}
/* and for everything else you can use C! Be it traversing the filesystem, or verifying the kernel image etc.*/

void __NORETURN main(){
    __asm__ __volatile__ ("movb  $0x0, %al\n");
    print("woo hoo!\r\n:)");
    while(1);
}

For linking below basic linker script used.

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

I prepared a binary bootable image and copied to floppy using below commands.

gcc -c -Os -g -march=i686 -ffreestanding -Wall -Werror -I. test.c -o test.o 
ld  -m elf_i386 -static -Ttest.ld -nostdlib --nmagic -o test.elf test.o 
objcopy -O binary test.elf test.bin
sudo dd if=test5.bin of=/dev/fd0

As we can easily notice I am trying to print character 'w' only, from the string "woo hoo!" passed to print() function. But when I tried to boot it on Virtual Machine, it did not print anything.

However if I comment 1st statement of print() and uncomment second statement, character 'W' is printed as below, which proves compilation and boot stamp are all fine. This is a successful test case using movb $'W' , %al:

Success Case

I tried my best searching questions for Linker script, gcc options, inline assembly and real vs virtual machine, but could not fix this problem.

Also it could be problem of how static variables are saved in case of boot-loader which I am yet unaware of.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • You have two potential problems here. First of all, I assume from this code you are new to _GCC_ assembler templates. It would be much easier to create an assembler file with your bootloader than to use inline assembly. inline assembly is fraught with gotchyas. You need to understand constraints (input, output, clobbers etc). Second of all your `main` doesn't seem to set the _DS_ register to 0 (since you are using an origin point of 0x7c00). BIOSes don't guarantee that _DS_ will be set properly when it reaches your bootloader, and that would cause your string to be read from the wrong place. – Michael Petch Apr 21 '16 at 13:28
  • I also don't recommend using `__asm__(".code16gcc\n");` for generating 16-bit code, It is not exactly 16-bit code. It uses prefixes to force 16-bit operands. If you know what you are doing you can get away with this. I recommend doing this strictly as assembler code to start. – Michael Petch Apr 21 '16 at 13:31
  • You can find additional information on coding up a bootloader (and not making assumptions) in my [General Bootloader tips](http://stackoverflow.com/a/32705076/3857942) (Stackoverflow answer) – Michael Petch Apr 21 '16 at 13:34
  • You should also be using an i386 or i686 _GCC_ cross compiler to avoid other possible issues. [OSDev Wiki](http://wiki.osdev.org/Main_Page) has a lot of good information. – Michael Petch Apr 21 '16 at 13:37
  • Thanks Michael, I will go through your answers and tips in detail and try it further. I understand your comment about using assembly only code. But I want to have control over inline assembly so that I can mixup C code and assembly on need basis. – Manoj Sharma Apr 23 '16 at 03:46
  • If you go down this route then the bug giving you a headache is mentioned in my first comment. In `main` the first thing you'll want to do is set _DS_ to zero since you are using an origin point of 0x7c00 (in the linker script). Segment:offset (base of your code and data) should be a physical address of 0x07c00 for a bootloader. If you don't set _DS_ to zero your data (string) will be accessed from the wrong location. You can set _DS_ to zero by doing something like `xor %ax, %ax` `mov %ax, %ds`. I highly recommend per by bootloader tips setting up a stack yourself in `main` as well. – Michael Petch Apr 23 '16 at 20:22
  • Thanks a lot for your tips, finally I could print the string. Though I havnt setup my stack manually yet but using default DS value was the biggest mistake. Its a great heads up for me, learnt quite important things. – Manoj Sharma Apr 25 '16 at 01:15
  • No problem, I was pretty sure _DS_ being wrong was your problem. Without the stack being set manually you rely on whatever the stack was the BIOS was using. If you start loading things in memory you could eventually clobber the stack, so it is usually a good idea to set the stack somewhere that you won't overwrite with your own code. – Michael Petch Apr 25 '16 at 01:18

0 Answers0