121a88fd4Skaashoek#include "asm.h" 255e95b16Srtm 35d1f4b8aSrsc# Start the first CPU: switch to 32-bit protected mode, jump into C. 45d1f4b8aSrsc# The BIOS loads this code from the first sector of the hard disk into 55d1f4b8aSrsc# memory at physical address 0x7c00 and starts executing in real mode 65d1f4b8aSrsc# with %cs=0 %ip=7c00. 755e95b16Srtm 8*e97519a6Srsc#define SEG_KCODE 1 // kernel code 9*e97519a6Srsc#define SEG_KDATA 2 // kernel data+stack 10*e97519a6Srsc#define SEG_KCPU 3 // kernel per-cpu data 11*e97519a6Srsc 12*e97519a6Srsc#define CR0_PE 1 // protected mode enable bit 1355e95b16Srtm 14b7f653dcSrsc.code16 # Assemble for 16-bit mode 152bc72bddSrsc.globl start 16a650c606Srscstart: 1755e95b16Srtm cli # Disable interrupts 1855e95b16Srtm 1955e95b16Srtm # Set up the important data segment registers (DS, ES, SS). 2055e95b16Srtm xorw %ax,%ax # Segment number zero 2155e95b16Srtm movw %ax,%ds # -> Data Segment 2255e95b16Srtm movw %ax,%es # -> Extra Segment 2355e95b16Srtm movw %ax,%ss # -> Stack Segment 2455e95b16Srtm 250cfc7290Srsc # Enable A20: 2615868c4bSrsc # For backwards compatibility with the earliest PCs, physical 2715868c4bSrsc # address line 20 is tied low, so that addresses higher than 2815868c4bSrsc # 1MB wrap around to zero by default. This code undoes this. 29a650c606Srscseta20.1: 305d1f4b8aSrsc inb $0x64,%al # Wait for not busy 315d1f4b8aSrsc testb $0x2,%al 325d1f4b8aSrsc jnz seta20.1 335d1f4b8aSrsc 345d1f4b8aSrsc movb $0xd1,%al # 0xd1 -> port 0x64 355d1f4b8aSrsc outb %al,$0x64 36a650c606Srsc 37a650c606Srscseta20.2: 385d1f4b8aSrsc inb $0x64,%al # Wait for not busy 395d1f4b8aSrsc testb $0x2,%al 405d1f4b8aSrsc jnz seta20.2 415d1f4b8aSrsc 425d1f4b8aSrsc movb $0xdf,%al # 0xdf -> port 0x60 435d1f4b8aSrsc outb %al,$0x60 4455e95b16Srtm 452bc72bddSrsc//PAGEBREAK! 462bc72bddSrsc # Switch from real to protected mode, using a bootstrap GDT 472bc72bddSrsc # and segment translation that makes virtual addresses 48411ee741Srtm # identical to physical addresses, so that the 492bc72bddSrsc # effective memory map does not change during the switch. 502bc72bddSrsc lgdt gdtdesc 512bc72bddSrsc movl %cr0, %eax 52b7f653dcSrsc orl $CR0_PE, %eax 532bc72bddSrsc movl %eax, %cr0 542bc72bddSrsc 552bc72bddSrsc # Jump to next instruction, but in 32-bit code segment. 562bc72bddSrsc # Switches processor into 32-bit mode. 57*e97519a6Srsc ljmp $(SEG_KCODE<<3), $start32 5855e95b16Srtm 592bc72bddSrsc.code32 # Assemble for 32-bit mode 60b7f653dcSrscstart32: 6155e95b16Srtm # Set up the protected-mode data segment registers 62*e97519a6Srsc movw $(SEG_KDATA<<3), %ax # Our data segment selector 6355e95b16Srtm movw %ax, %ds # -> DS: Data Segment 6455e95b16Srtm movw %ax, %es # -> ES: Extra Segment 65*e97519a6Srsc movw %ax, %ss # -> SS: Stack Segment 66*e97519a6Srsc movw $(SEG_KCPU<<3), %ax # Our per-cpu segment selector 6755e95b16Srtm movw %ax, %fs # -> FS 6855e95b16Srtm movw %ax, %gs # -> GS 695d1f4b8aSrsc 702bc72bddSrsc # Set up the stack pointer and call into C. 715d1f4b8aSrsc movl $start, %esp 72c35c064eSrsc call bootmain 735d1f4b8aSrsc 74b7f653dcSrsc # If bootmain returns (it shouldn't), trigger a Bochs 75b7f653dcSrsc # breakpoint if running under Bochs, then loop. 76b7f653dcSrsc movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 77b7f653dcSrsc movw %ax, %dx 78b7f653dcSrsc outw %ax, %dx 79b7f653dcSrsc movw $0x8e00, %ax # 0x8e00 -> port 0x8a00 80b7f653dcSrsc outw %ax, %dx 81a650c606Srscspin: 822bc72bddSrsc jmp spin 83a650c606Srsc 845d1f4b8aSrsc# Bootstrap GDT 8555e95b16Srtm.p2align 2 # force 4 byte alignment 8655e95b16Srtmgdt: 877abf49d2Skaashoek SEG_NULLASM # null seg 887abf49d2Skaashoek SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg 897abf49d2Skaashoek SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg 90*e97519a6Srsc SEG_ASM(STA_W, 0x100, 0xffffffff) # per-cpu data seg; 0x100 is okay for now 915d1f4b8aSrsc 9255e95b16Srtmgdtdesc: 93*e97519a6Srsc .word 0x1f # sizeof(gdt) - 1 9455e95b16Srtm .long gdt # address gdt 95