C Bootloader Experiment

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

C Bootloader Experiment

Post by 01000101 »

I got bored today and decided to try peicing together a C bootloader using GCC in the DJGPP environment. I don't plan on using this for anything too useful, but maybe it (once fixed) can help some others. I have the core of the code finished, but I just can't seem to either get the GDT installed correctly or I'm making a bad far jump. I haven't spent too much time on this so i appologize for its rough edges =). If you can fix it so that it jumps into PMode correctly, it's all yours. It was a fun experiment. I would like to get this solved just to know what the issue is. I'm not the greatest with AT&T syntax, as this makes this my 2-3 time using it. I have a feeling that the error has something to do with a label address not being correct.

This code takes up almost an entire sector of a floppy. The global 'start' label is only used for the linker I used.

Code: Select all


	asm(".code16gcc\n");
	asm(".globl start\n");
	asm("start:\n");
	asm("movw $0, %ax; movw %ax, %bx; movw %ax, %cx; movw %ax, %dx;");
	asm(".extern _main\n");
	asm("jmp _main;\n");

	asm("gdt_start: \
		.quad 0x0000000000000000; \
		.quad 0x00cf9a000000ffff; \
		.quad 0x00cf92000000ffff; \
		gdt_48: \
		.word (gdt_48 - gdt_start - 1); \
		.long gdt_start;");

void reset_floppy()
{
	unsigned char status = 0;

	__asm__ __volatile__("movb %1, %%ah;\
						  int $0x13;\
						  movb %%ah, %0;"
						  :"=r"(status)
						  :"r"(status)
						  :"%ax");
	if(status){reset_floppy();}
}

void load_floppy_sector(unsigned short segment, unsigned short offset,
						unsigned char nSectors, unsigned char Cylinder, unsigned char Start_Sector, unsigned char Head)
{
	unsigned char Command = 2, Status = 0;
    unsigned short ax, bx, cx, dx;
	
	ax = ((((Command << 8) & 0xFF00)) + (nSectors & 0x00FF));;
	bx = offset;
	cx = ((((Cylinder << 8) & 0xFF00))  + (Start_Sector & 0x00FF));
	dx = ((Head << 8) & 0xFF00);

	__asm__ __volatile__("movw %1, %%ax;\
						  movw %%ax, %%es;\
						  movw %2, %%ax;\
						  movw %3, %%bx;\
						  movw %4, %%cx;\
						  movw %5, %%dx;\
						  int $0x13;\
						  movb %%ah, %0;"
						  :"=m"(Status)
						  :"m"(segment), "m"(ax), "m"(bx), "m"(cx), "m"(dx)
						  :"%ax","%bx","%cx","%dx");
}

void main()
{

	reset_floppy();
	load_floppy_sector(0x0000, 0x1000, 18, 0, 1, 0); /* place 18 sectors starting at 0x1000:0x0000 */

	asm("inb $0x64, %al; \
		 movb $0xDF, %al; \
		 outb %al, $0x64;");

	asm("lgdt gdt_48; \
		 movl %cr0, %eax; \
		 or $1, %eax; \
		 movl %eax, %cr0; \
		 ljmp $0x08, $enter_pmode; \
		 hlt;");
}   
	asm(".code32 \
	     .globl enter_pmode; \
	      enter_pmode: \
	      hlt;");

asm(".org (0x200 - 2); .word 0xAA55;\n");
/* anything placed past here is in sector 2 of the floppy */


to compile just run a simple batch script

Code: Select all

gcc -ffreestanding -c "boot.c" -o boot.o
ld -T link.ld -o kernel.bin boot.o
User avatar
suthers
Member
Member
Posts: 672
Joined: Tue Feb 20, 2007 3:00 pm
Location: London UK
Contact:

Re: C Bootloader Experiment

Post by suthers »

Nice experiment... :D
Jules
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Re: C Bootloader Experiment

Post by 01000101 »

ok, I meshed some of my own code with the GDT setup code from Brans tutorial at OSDever.net. Now it works fine and enters pmode.

Code: Select all

   asm(".code16gcc\n");
   asm(".globl start\n");
   asm("start:\n");
   asm("cli; movw $0, %ax; movw %ax, %bx; movw %ax, %cx; movw %ax, %dx;");
   asm(".extern _main\n");
   asm("jmp _main;\n");

struct gdt_entry
{
    unsigned short limit_low;
    unsigned short base_low;
    unsigned char base_middle;
    unsigned char access;
    unsigned char granularity;
    unsigned char base_high;
} __attribute__((packed));

struct gdt_ptr
{
    unsigned short limit;
    unsigned int base;
} __attribute__((packed));

struct gdt_entry gdt[3];
struct gdt_ptr gp;

void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
    gdt[num].base_low = (base & 0xFFFF);
    gdt[num].base_middle = (base >> 16) & 0xFF;
    gdt[num].base_high = (base >> 24) & 0xFF;
    gdt[num].limit_low = (limit & 0xFFFF);
    gdt[num].granularity = ((limit >> 16) & 0x0F);
    gdt[num].granularity |= (gran & 0xF0);
    gdt[num].access = access;
}

void gdt_install()
{
    gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
    gp.base = (unsigned int)&gdt;
    gdt_set_gate(0, 0, 0, 0, 0);
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
    asm("lgdt (_gp); \
         movl %cr0, %eax; \
         or $1, %eax; \
         movl %eax, %cr0; \
         movw $0x10, %ax; \
         movw %ax, %ds; \
         movw %ax, %es; \
         movw %ax, %fs; \
         movw %ax, %gs; \
         movw %ax, %ss; \
         jmp $0x08, $(0x7C00 + _pmode_entry_point);");
}

void main()
{
   gdt_install();
}

void pmode_entry_point()
{
     asm(".code32;");
     asm("cli;hlt;");
}

asm(".org (0x200 - 2); .word 0xAA55;\n");
/* anything placed past here is in sector 2 of the floppy */

compile with the same batch script from the first post.
have fun.
User avatar
suthers
Member
Member
Posts: 672
Joined: Tue Feb 20, 2007 3:00 pm
Location: London UK
Contact:

Re: C Bootloader Experiment

Post by suthers »

even better, I don't have time to compile it now, but I'll do it later, where does it place es:sp though?
Jules
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Re: C Bootloader Experiment

Post by 01000101 »

I'm not 100% sure. I'm not at that computer at the moment, but Ill check a little later.

I didn't set a stack start position, but it can be easily added. This is also because I didn't put alot of effort into this, I just wanted to get a somewhat working model out there so that all the people that ask about C bootloaders can not be discouraged from the start.
User avatar
suthers
Member
Member
Posts: 672
Joined: Tue Feb 20, 2007 3:00 pm
Location: London UK
Contact:

Re: C Bootloader Experiment

Post by suthers »

Well it's very good for boredom code (You should see mine :lol:, even I don't know what I was thinking after... :lol: )
Jules
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Re: C Bootloader Experiment

Post by 01000101 »

ok, here is a fully working bootloader that reads 18 floppy disk sectors into memory, loads PMode, and jumps to the loaded kernel.

Very basic and very rough looking, but it is a working C bootloader for people to experiment with.
due to size restraints from the high amount of code generated from the compiler, I was unable to include an extensible read_sectors function as it overflowed the 512 byte restriction. If anyone can squeeze one in that would be great. Also, set the 'phys' or similar variables in your linker script to 0x00000000. If it is set too high, it breaks the 16bit limit and errors.

@suthers: fixed the issue you mentioned before.
Attachments
kernel.c
kernel
(143 Bytes) Downloaded 222 times
boot.c
bootloader
(2.26 KiB) Downloaded 205 times
cg123
Member
Member
Posts: 41
Joined: Wed Sep 27, 2006 2:34 pm

Re: C Bootloader Experiment

Post by cg123 »

I'm not sure if it'll help all that much, but have you tried compiling with -Os?
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Re: C Bootloader Experiment

Post by 01000101 »

yes, and it seems to place 90% of what is supposed to be in sector 1, in sector 2.
I strongly suggest staying away from bootloader optimizations as any sort of deviation from the intended will result in chaos.

It is known that C functions are very code heavy just to make the function declaration. If you want to slim down the bootloader, id suggest cramming all of the C code into one big function instead of my many functions.
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Re: C Bootloader Experiment

Post by Alboin »

It is known that C functions are very code heavy just to make the function declaration. If you want to slim down the bootloader, id suggest cramming all of the C code into one big function instead of my many functions.
Couldn't you just inline them, like: (From the GCC manual.)

Code: Select all

inline void foo (const char) __attribute__((always_inline));
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Re: C Bootloader Experiment

Post by 01000101 »

Alboin wrote:
It is known that C functions are very code heavy just to make the function declaration. If you want to slim down the bootloader, id suggest cramming all of the C code into one big function instead of my many functions.
Couldn't you just inline them, like: (From the GCC manual.)

Code: Select all

inline void foo (const char) __attribute__((always_inline));
indeed. should I post a wiki article about making a C bootloader? if I did, you could just edit it yourself, and maybe some other people could edit it as well until it has more room to add more functionality.
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Re: C Bootloader Experiment

Post by Alboin »

01000101 wrote:indeed. should I post a wiki article about making a C bootloader? if I did, you could just edit it yourself, and maybe some other people could edit it as well until it has more room to add more functionality.
We're all about being 'complete', so I suppose that it couldn't hurt. I would think that a 'disclaimer' or sorts would be needed, however, to advise that this might not work on all compilers, versions, etc.
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
01000101
Member
Member
Posts: 1599
Joined: Fri Jun 22, 2007 12:47 pm
Contact:

Re: C Bootloader Experiment

Post by 01000101 »

actually, I will probably end up re-writing the gdt stuff that way it has none of brans code in it (even though I think it is PD as well). I would end up releasing a revised version under public domain and without any liabilities expressed.
FlashBurn
Member
Member
Posts: 313
Joined: Fri Oct 20, 2006 10:14 am

Re: C Bootloader Experiment

Post by FlashBurn »

Ok, I also tried to write my loader with gcc and the example code which was given at the start.

It works, but there is only one problem. GCC produces 32bit assembly code :(

Code: Select all

	.file	"loader.c"
/APP
	.code16gcc
	.globl _start
	_start:
	cli
	push %cs
	xorw %bx,%bx
	pop %ax
	movw %bx,%ss
	movw %ax,%ds
	movw %ax,%es
	movw %bx,%fs
	movw %bx,%gs
	sti
	jmp loader
/NO_APP
	.text
.globl loader
	.type	loader, @function
loader:
	pushl	%ebp
	movl	%esp, %ebp
.L2:
/APP
/ 18 "loader.c" 1
	hlt
/ 0 "" 2
/NO_APP
	jmp	.L2
	.size	loader, .-loader
	.ident	"GCC: (GNU) 4.3.1"
The problem there is the function prologue and the epilogue will be the same. Is there some switch so that gcc produces 16bit code?

I dissambled the code and it was really 16bit code, except of the function prologue ("pushl %ebp; movl %esp, %ebp")!
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: C Bootloader Experiment

Post by Brendan »

Hi,
FlashBurn wrote:I dissambled the code and it was really 16bit code, except of the function prologue ("pushl %ebp; movl %esp, %ebp")!
Are you sure it's 32-bit code? It looks like 16-bit code using 32-bit registers to me.... ;)


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply