xv6/bootasm.S
1 #include "asm.h" 2 #include "memlayout.h" 3 #include "mmu.h" 4 5 # Start the first CPU: switch to 32-bit protected mode, jump into C. 6 # The BIOS loads this code from the first sector of the hard disk into 7 # memory at physical address 0x7c00 and starts executing in real mode 8 # with %cs=0 %ip=7c00. 9 10 .code16 # Assemble for 16-bit mode 11 .globl start 12 start: 13 cli # BIOS enabled interrupts; disable 14 15 # Zero data segment registers DS, ES, and SS. 16 xorw %ax,%ax # Set %ax to zero 17 movw %ax,%ds # -> Data Segment 18 movw %ax,%es # -> Extra Segment 19 movw %ax,%ss # -> Stack Segment 20 21 # Physical address line A20 is tied to zero so that the first PCs 22 # with 2 MB would run software that assumed 1 MB. Undo that. 23 seta20.1: 24 inb $0x64,%al # Wait for not busy 25 testb $0x2,%al 26 jnz seta20.1 27 28 movb $0xd1,%al # 0xd1 -> port 0x64 29 outb %al,$0x64 30 31 seta20.2: 32 inb $0x64,%al # Wait for not busy 33 testb $0x2,%al 34 jnz seta20.2 35 36 movb $0xdf,%al # 0xdf -> port 0x60 37 outb %al,$0x60 38 39 # Switch from real to protected mode. Use a bootstrap GDT that makes 40 # virtual addresses map directly to physical addresses so that the 41 # effective memory map doesn’t change during the transition. 42 lgdt gdtdesc 43 movl %cr0, %eax 44 orl $CR0_PE, %eax 45 movl %eax, %cr0 46 47 48 49 50 51 # Complete transition to 32-bit protected mode by using long jmp 52 # to reload %cs and %eip. The segment descriptors are set up with no 53 # translation, so that the mapping is still the identity mapping. 54 ljmp $(SEG_KCODE<<3), $start32 55 56 .code32 # Tell assembler to generate 32-bit code now. 57 start32: 58 # Set up the protected-mode data segment registers 59 movw $(SEG_KDATA<<3), %ax # Our data segment selector 60 movw %ax, %ds # -> DS: Data Segment 61 movw %ax, %es # -> ES: Extra Segment 62 movw %ax, %ss # -> SS: Stack Segment 63 movw $0, %ax # Zero segments not ready for use 64 movw %ax, %fs # -> FS 65 movw %ax, %gs # -> GS 66 67 # Set up the stack pointer and call into C. 68 movl $start, %esp 69 call bootmain 70 71 # If bootmain returns (it shouldn’t), trigger a Bochs 72 # breakpoint if running under Bochs, then loop. 73 movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 74 movw %ax, %dx 75 outw %ax, %dx 76 movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 77 outw %ax, %dx 78 spin: 79 jmp spin 80 81 # Bootstrap GDT 82 .p2align 2 # force 4 byte alignment 83 gdt: 84 SEG_NULLASM # null seg 85 SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg 86 SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg 87 88 gdtdesc: 89 .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 90 .long gdt # address gdt
xv6/bootmain.c
1 // Boot loader. 2 // 3 // Part of the boot sector, along with bootasm.S, which calls bootmain(). 4 // bootasm.S has put the processor into protected 32-bit mode. 5 // bootmain() loads an ELF kernel image from the disk starting at 6 // sector 1 and then jumps to the kernel entry routine. 7 8 #include "types.h" 9 #include "elf.h" 10 #include "x86.h" 11 #include "memlayout.h" 12 13 #define SECTSIZE 512 14 15 void readseg(uchar*, uint, uint); 16 17 void 18 bootmain(void) 19 { 20 struct elfhdr *elf; 21 struct proghdr *ph, *eph; 22 void (*entry)(void); 23 uchar* pa; 24 25 elf = (struct elfhdr*)0x10000; // scratch space 26 27 // Read 1st page off disk 28 readseg((uchar*)elf, 4096, 0); 29 30 // Is this an ELF executable? 31 if(elf->magic != ELF_MAGIC) 32 return; // let bootasm.S handle error 33 34 // Load each program segment (ignores ph flags). 35 ph = (struct proghdr*)((uchar*)elf + elf->phoff); 36 eph = ph + elf->phnum; 37 for(; ph < eph; ph++){ 38 pa = (uchar*)ph->paddr; 39 readseg(pa, ph->filesz, ph->off); 40 if(ph->memsz > ph->filesz) 41 stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz); 42 } 43 44 // Call the entry point from the ELF header. 45 // Does not return! 46 entry = (void(*)(void))(elf->entry); 47 entry(); 48 } 49 50 51 void 52 waitdisk(void) 53 { 54 // Wait for disk ready. 55 while((inb(0x1F7) & 0xC0) != 0x40) 56 ; 57 } 58 59 // Read a single sector at offset into dst. 60 void 61 readsect(void *dst, uint offset) 62 { 63 // Issue command. 64 waitdisk(); 65 outb(0x1F2, 1); // count = 1 66 outb(0x1F3, offset); 67 outb(0x1F4, offset >> 8); 68 outb(0x1F5, offset >> 16); 69 outb(0x1F6, (offset >> 24) | 0xE0); 70 outb(0x1F7, 0x20); // cmd 0x20 - read sectors 71 72 // Read data. 73 waitdisk(); 74 insl(0x1F0, dst, SECTSIZE/4); 75 } 76 77 // Read ’count’ bytes at ’offset’ from kernel into physical address ’pa’. 78 // Might copy more than asked. 79 void 80 readseg(uchar* pa, uint count, uint offset) 81 { 82 uchar* epa; 83 84 epa = pa + count; 85 86 // Round down to sector boundary. 87 pa -= offset % SECTSIZE; 88 89 // Translate from bytes to sectors; kernel starts at sector 1. 90 offset = (offset / SECTSIZE) + 1; 91 92 // If this is too slow, we could read lots of sectors at a time. 93 // We’d write more to memory than asked, but it doesn’t matter -- 94 // we load in increasing order. 95 for(; pa < epa; pa += SECTSIZE, offset++) 96 readsect(pa, offset); 97 }