/* Copyright 2009-2013 Charles Lohr under the MIT/X11 License. This code is a bootloader and switches to flat 4G code, 4G data protected mode. It then calls C code. */ //Useful article: http://thiscouldbebetter.wordpress.com/2011/03/17/entering-protected-mode-from-assembly .code16 .global loader //Code runs from 0x7c00 .align 4 .org 0x00 bootstart: //Configure the stack mov $0xc000, %sp //Configure the segments mov $0x0000, %ax mov %ax, %ds mov %ax, %es mov %ax, %fs mov %ax, %gs mov %ax, %ss //First, preserve drive letter. push %dx //Print out the letter 1, so we know it's working. mov $0x0e31, %ax mov $0x00, %bx int $0x10 //Just in case we have a dumb bios that set our code segment. ljmp $0x00, $continuestart continuestart: //Copy over first few sectors of hard disk into ram. mov %al, %dl mov $0x02, %ah //Command to read sectors. mov $0x20, %al //# of sectors to read (16kB) mov $0x0002, %cx //Track = 0, sector = 2 pop %dx mov $0x00, %dh //Head = 0, Drive = (whatever drive we started with) mov $0x0000, %bx mov %bx, %es //Segment of output mov $0x7e00, %bx //Pointer of output int $0x13 push %ax mov %ah, %al mov $0x0e, %ah //Should read 0 to say everything was fine with the read call. add $0x30, %al mov $0x00, %ebx int $0x10 pop %ax mov $0x0e, %ah add $0x30, %al mov $0x00, %ebx //should be the number of bytes read int $0x10 mov $0x0e, %ah mov (0x7e02), %al //Should be lower case x mov $0x00, %ebx int $0x10 //Setup our real big GDT lgdt (GDT_ptr) //I am certain this gets called. I can see the on-screen text. mov $0xb800, %ax mov %ax, %ds movb $0x91, (32) movb $0xf1, (33) //prints a small 0x91 a couple of columns in. mov $0x0000, %ax mov %ax, %ds cli //XXX Consider disabling the NMI //Enter protected mode mov %cr0, %eax or $0x01, %al mov %eax, %cr0 //Jump to correct data selector, and enter 32-bit mode jmpl $0x08, $later2 later2: .code32 //Setup data selectors mov $0x10, %ax mov %ax, %ds mov %ax, %es mov %ax, %fs mov %ax, %gs mov %ax, %ss //This does not get called in real life, but does in the emulator. mov $0x3131, %ax mov $0xb8004, %edx movw %ax, (%edx) //Enable the FPU mov %cr0, %eax and $0xfffffff3,%eax mov %eax, %cr0 fninit jmpl $0x08, $main //$0x7e00 //Start of main program .align 8 //------------------------------------------------------------------------ //Transfer to real mode: //Based roughly off of http://www.rohitab.com/discuss/topic/35103-switch-between-real-mode-and-protected-mode/ .align 4 idt_real: .word 0x3ff .long 0x00000000 .align 4 .global CallInterruptFrom32 CallInterruptFrom32: .code32: cli mov $0x20, %eax //Real mode, 16-bit, data selector (Selector #4) mov %eax, %ds mov %eax, %es mov %eax, %fs mov %eax, %gs mov %eax, %ss jmpl $0x18, $IntTCToRM IntTCToRM: .code16 //Now in 16-bit mov %cr0, %eax mov %eax, (savecr0) and $0x7FFFFFFE, %eax mov %eax, %cr0 jmpl $0x00, $enter_realmode16 enter_realmode16: lidt idt_real //Now in real mode land. pusha mov $0x00, %ax mov %ax, %ss mov ci32ax, %ax mov ci32bx, %bx mov ci32cx, %cx mov ci32dx, %dx mov ci32bp, %bp mov ci32di, %di mov ci32si, %si mov ci32gs, %gs mov ci32fs, %fs mov ci32es, %es mov ci32ds, %ds sti .byte 0xcd .global ci32int ci32int: .byte 0x00 cli mov %ax, ci32ax mov %bx, ci32bx mov %cx, ci32cx mov %dx, ci32dx mov %bp, ci32bp mov %di, ci32di mov %si, ci32si mov %gs, ci32gs mov %fs, ci32fs mov %es, ci32es mov %ds, ci32ds popa //Now, how do we get out of this mess? mov %cr0, %eax or $1, %eax mov %eax, %cr0 jmpl $0x08, $enter_protmode32 .code32 enter_protmode32: //Setup data selectors mov $0x10, %ax mov %ax, %ds mov %ax, %es mov %ax, %fs mov %ax, %gs mov %ax, %ss ret //GDT, Flat 4GiB //http://wiki.osdev.org/GDT_Tutorial GDT: .long 0x00000000 .long 0x00000000 //Descriptor (0x9a, RW, Execute) (Code32) .long 0x0000ffff .long 0x00CF9800 //Descriptor (0x92, RW) (Data32) .long 0x0000ffff .long 0x00cf9200 //Code16 .long 0x0000ffff .long 0x000f9a00 //Data16 .long 0x0000ffff .long 0x000f9200 GDT_end: .align 8 GDT_ptr: .word GDT_end - GDT - 1// For limit storage .long GDT // For base storage //Disk Signature .org 0x1b8 .long 0xefcdab89 .short 0x0000 //Disk entry 1. .byte 0x80 //Active partition 1 (and bootable) .byte 0x00 .byte 0x02 //CHS address .byte 0x00 .byte 0x56 //Partition type .byte 0x03 .byte 0x0b //CHS adderss of last sector .byte 0x00 .long 0x01 .long 0xc7 //Number of sectors in partition. //Area for setting variables to call the interrupt //We are putting this here since the values must be all zeroes anyway. //In fact, we can put anything that's all zeroes on boot here. .global CI32Params, ci32ax, ci32bx, ci32cx, ci32dx, ci32ss .global ci32sp, ci32bp, ci32di, ci32si, ci32gs, ci32fs, ci32es, ci32ds CI32Params: ci32ax: .word 0x0000 ci32bx: .word 0x0000 ci32cx: .word 0x0000 ci32dx: .word 0x0000 ci32bp: .word 0x0000 ci32di: .word 0x0000 ci32si: .word 0x0000 ci32gs: .word 0x0000 ci32fs: .word 0x0000 ci32es: .word 0x0000 ci32ds: .word 0x0000 idt_prot: .word 0x0000 .long 0x00000000 savecr0: .long 0x00000000 //still have about 70 bytes left between zero-init values and 0x55aa signature //.long 0xCCCCCCCC //MBR Signature .org 0x1fe .byte 0x55 .byte 0xaa /* .section ".endprog", "ax" .extern end_of_kernel end_of_kernel: // leave // ret */ /* //Enable a20 // mov $0x2401, %ax // int $0x15 inb $0x92, %al or $2, %al outb %al, $0x92 in $0x92, %al test $2, %al jnz after or $2, %al and $0xFE, %al out %al, $0x92 after: //Verify a20 was set. // mov %ah, %al mov $0x0e, %ah add $0x30, %al mov $0x00, %ebx int $0x10 */