首页 > 代码库 > MIT JOS学习笔记01(2016.10.22)
MIT JOS学习笔记01(2016.10.22)
一、环境配置
关于MIT课程中使用的JOS的配置教程网上已经有很多了,在这里就不做介绍,个人使用的是Ubuntu 16.04 + qemu。另注,本文章中贴出的代码均是JOS中未经修改的源代码,其中有一些细节是MIT课程中要求学生自己实现的。
二、代码分析
1.boot.S(AT&T汇编格式) / boot.asm
1 #include <inc/mmu.h> 2 3 # Start the CPU: switch to 32-bit protected mode, jump into C. 4 # The BIOS loads this code from the first sector of the hard disk into 5 # memory at physical address 0x7c00 and starts executing in real mode 6 # with %cs=0 %ip=7c00. 7 8 .set PROT_MODE_CSEG, 0x8 # kernel code segment selector 9 .set PROT_MODE_DSEG, 0x10 # kernel data segment selector 10 .set CR0_PE_ON, 0x1 # protected mode enable flag 11 12 .globl start 13 start: 14 .code16 # Assemble for 16-bit mode 15 cli # Disable interrupts 16 cld # String operations increment 17 18 # Set up the important data segment registers (DS, ES, SS). 19 xorw %ax,%ax # Segment number zero 20 movw %ax,%ds # -> Data Segment 21 movw %ax,%es # -> Extra Segment 22 movw %ax,%ss # -> Stack Segment 23 24 # Enable A20: 25 # For backwards compatibility with the earliest PCs, physical 26 # address line 20 is tied low, so that addresses higher than 27 # 1MB wrap around to zero by default. This code undoes this. 28 seta20.1: 29 inb $0x64,%al # Wait for not busy 30 testb $0x2,%al 31 jnz seta20.1 32 33 movb $0xd1,%al # 0xd1 -> port 0x64 34 outb %al,$0x64 35 36 seta20.2: 37 inb $0x64,%al # Wait for not busy 38 testb $0x2,%al 39 jnz seta20.2 40 41 movb $0xdf,%al # 0xdf -> port 0x60 42 outb %al,$0x60 43 44 # Switch from real to protected mode, using a bootstrap GDT 45 # and segment translation that makes virtual addresses 46 # identical to their physical addresses, so that the 47 # effective memory map does not change during the switch. 48 lgdt gdtdesc 49 movl %cr0, %eax 50 orl $CR0_PE_ON, %eax 51 movl %eax, %cr0 52 53 # Jump to next instruction, but in 32-bit code segment. 54 # Switches processor into 32-bit mode. 55 ljmp $PROT_MODE_CSEG, $protcseg 56 57 .code32 # Assemble for 32-bit mode 58 protcseg: 59 # Set up the protected-mode data segment registers 60 movw $PROT_MODE_DSEG, %ax # Our data segment selector 61 movw %ax, %ds # -> DS: Data Segment 62 movw %ax, %es # -> ES: Extra Segment 63 movw %ax, %fs # -> FS 64 movw %ax, %gs # -> GS 65 movw %ax, %ss # -> SS: Stack Segment 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), loop. 72 spin: 73 jmp spin 74 75 # Bootstrap GDT 76 .p2align 2 # force 4 byte alignment 77 gdt: 78 SEG_NULL # null seg 79 SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg 80 SEG(STA_W, 0x0, 0xffffffff) # data seg 81 82 gdtdesc: 83 .word 0x17 # sizeof(gdt) - 1 84 .long gdt # address gdt
1 obj/boot/boot.out: 文件格式 elf32-i386 2 3 4 Disassembly of section .text: 5 6 00007c00 <start>: 7 .set CR0_PE_ON, 0x1 # protected mode enable flag 8 9 .globl start 10 start: 11 .code16 # Assemble for 16-bit mode 12 cli # Disable interrupts 13 7c00: fa cli 14 cld # String operations increment 15 7c01: fc cld 16 17 # Set up the important data segment registers (DS, ES, SS). 18 xorw %ax,%ax # Segment number zero 19 7c02: 31 c0 xor %eax,%eax 20 movw %ax,%ds # -> Data Segment 21 7c04: 8e d8 mov %eax,%ds 22 movw %ax,%es # -> Extra Segment 23 7c06: 8e c0 mov %eax,%es 24 movw %ax,%ss # -> Stack Segment 25 7c08: 8e d0 mov %eax,%ss 26 27 00007c0a <seta20.1>: 28 # Enable A20: 29 # For backwards compatibility with the earliest PCs, physical 30 # address line 20 is tied low, so that addresses higher than 31 # 1MB wrap around to zero by default. This code undoes this. 32 seta20.1: 33 inb $0x64,%al # Wait for not busy 34 7c0a: e4 64 in $0x64,%al 35 testb $0x2,%al 36 7c0c: a8 02 test $0x2,%al 37 jnz seta20.1 38 7c0e: 75 fa jne 7c0a <seta20.1> 39 40 movb $0xd1,%al # 0xd1 -> port 0x64 41 7c10: b0 d1 mov $0xd1,%al 42 outb %al,$0x64 43 7c12: e6 64 out %al,$0x64 44 45 00007c14 <seta20.2>: 46 47 seta20.2: 48 inb $0x64,%al # Wait for not busy 49 7c14: e4 64 in $0x64,%al 50 testb $0x2,%al 51 7c16: a8 02 test $0x2,%al 52 jnz seta20.2 53 7c18: 75 fa jne 7c14 <seta20.2> 54 55 movb $0xdf,%al # 0xdf -> port 0x60 56 7c1a: b0 df mov $0xdf,%al 57 outb %al,$0x60 58 7c1c: e6 60 out %al,$0x60 59 60 # Switch from real to protected mode, using a bootstrap GDT 61 # and segment translation that makes virtual addresses 62 # identical to their physical addresses, so that the 63 # effective memory map does not change during the switch. 64 lgdt gdtdesc 65 7c1e: 0f 01 16 lgdtl (%esi) 66 7c21: 64 7c 0f fs jl 7c33 <protcseg+0x1> 67 movl %cr0, %eax 68 7c24: 20 c0 and %al,%al 69 orl $CR0_PE_ON, %eax 70 7c26: 66 83 c8 01 or $0x1,%ax 71 movl %eax, %cr0 72 7c2a: 0f 22 c0 mov %eax,%cr0 73 74 # Jump to next instruction, but in 32-bit code segment. 75 # Switches processor into 32-bit mode. 76 ljmp $PROT_MODE_CSEG, $protcseg 77 7c2d: ea .byte 0xea 78 7c2e: 32 7c 08 00 xor 0x0(%eax,%ecx,1),%bh 79 80 00007c32 <protcseg>: 81 82 .code32 # Assemble for 32-bit mode 83 protcseg: 84 # Set up the protected-mode data segment registers 85 movw $PROT_MODE_DSEG, %ax # Our data segment selector 86 7c32: 66 b8 10 00 mov $0x10,%ax 87 movw %ax, %ds # -> DS: Data Segment 88 7c36: 8e d8 mov %eax,%ds 89 movw %ax, %es # -> ES: Extra Segment 90 7c38: 8e c0 mov %eax,%es 91 movw %ax, %fs # -> FS 92 7c3a: 8e e0 mov %eax,%fs 93 movw %ax, %gs # -> GS 94 7c3c: 8e e8 mov %eax,%gs 95 movw %ax, %ss # -> SS: Stack Segment 96 7c3e: 8e d0 mov %eax,%ss 97 98 # Set up the stack pointer and call into C. 99 movl $start, %esp 100 7c40: bc 00 7c 00 00 mov $0x7c00,%esp 101 call bootmain 102 7c45: e8 cb 00 00 00 call 7d15 <bootmain> 103 104 00007c4a <spin>: 105 106 # If bootmain returns (it shouldn‘t), loop. 107 spin: 108 jmp spin 109 7c4a: eb fe jmp 7c4a <spin> 110 111 00007c4c <gdt>: 112 ... 113 7c54: ff (bad) 114 7c55: ff 00 incl (%eax) 115 7c57: 00 00 add %al,(%eax) 116 7c59: 9a cf 00 ff ff 00 00 lcall $0x0,$0xffff00cf 117 7c60: 00 .byte 0x0 118 7c61: 92 xchg %eax,%edx 119 7c62: cf iret 120 ... 121 122 00007c64 <gdtdesc>: 123 7c64: 17 pop %ss 124 7c65: 00 4c 7c 00 add %cl,0x0(%esp,%edi,2) 125 ... 126 127 00007c6a <waitdisk>: 128 } 129 } 130 131 void 132 waitdisk(void) 133 { 134 7c6a: 55 push %ebp 135 136 static __inline uint8_t 137 inb(int port) 138 { 139 uint8_t data; 140 __asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port)); 141 7c6b: ba f7 01 00 00 mov $0x1f7,%edx 142 7c70: 89 e5 mov %esp,%ebp 143 7c72: ec in (%dx),%al 144 // wait for disk reaady 145 while ((inb(0x1F7) & 0xC0) != 0x40) 146 7c73: 83 e0 c0 and $0xffffffc0,%eax 147 7c76: 3c 40 cmp $0x40,%al 148 7c78: 75 f8 jne 7c72 <waitdisk+0x8> 149 /* do nothing */; 150 } 151 7c7a: 5d pop %ebp 152 7c7b: c3 ret 153 154 00007c7c <readsect>: 155 156 void 157 readsect(void *dst, uint32_t offset) 158 { 159 7c7c: 55 push %ebp 160 7c7d: 89 e5 mov %esp,%ebp 161 7c7f: 57 push %edi 162 7c80: 8b 4d 0c mov 0xc(%ebp),%ecx 163 // wait for disk to be ready 164 waitdisk(); 165 7c83: e8 e2 ff ff ff call 7c6a <waitdisk> 166 } 167 168 static __inline void 169 outb(int port, uint8_t data) 170 { 171 __asm __volatile("outb %0,%w1" : : "a" (data), "d" (port)); 172 7c88: ba f2 01 00 00 mov $0x1f2,%edx 173 7c8d: b0 01 mov $0x1,%al 174 7c8f: ee out %al,(%dx) 175 7c90: ba f3 01 00 00 mov $0x1f3,%edx 176 7c95: 88 c8 mov %cl,%al 177 7c97: ee out %al,(%dx) 178 7c98: 89 c8 mov %ecx,%eax 179 7c9a: ba f4 01 00 00 mov $0x1f4,%edx 180 7c9f: c1 e8 08 shr $0x8,%eax 181 7ca2: ee out %al,(%dx) 182 7ca3: 89 c8 mov %ecx,%eax 183 7ca5: ba f5 01 00 00 mov $0x1f5,%edx 184 7caa: c1 e8 10 shr $0x10,%eax 185 7cad: ee out %al,(%dx) 186 7cae: 89 c8 mov %ecx,%eax 187 7cb0: ba f6 01 00 00 mov $0x1f6,%edx 188 7cb5: c1 e8 18 shr $0x18,%eax 189 7cb8: 83 c8 e0 or $0xffffffe0,%eax 190 7cbb: ee out %al,(%dx) 191 7cbc: ba f7 01 00 00 mov $0x1f7,%edx 192 7cc1: b0 20 mov $0x20,%al 193 7cc3: ee out %al,(%dx) 194 outb(0x1F5, offset >> 16); 195 outb(0x1F6, (offset >> 24) | 0xE0); 196 outb(0x1F7, 0x20); // cmd 0x20 - read sectors 197 198 // wait for disk to be ready 199 waitdisk(); 200 7cc4: e8 a1 ff ff ff call 7c6a <waitdisk> 201 } 202 203 static __inline void 204 insl(int port, void *addr, int cnt) 205 { 206 __asm __volatile("cld\n\trepne\n\tinsl" : 207 7cc9: 8b 7d 08 mov 0x8(%ebp),%edi 208 7ccc: b9 80 00 00 00 mov $0x80,%ecx 209 7cd1: ba f0 01 00 00 mov $0x1f0,%edx 210 7cd6: fc cld 211 7cd7: f2 6d repnz insl (%dx),%es:(%edi) 212 213 // read a sector 214 insl(0x1F0, dst, SECTSIZE/4); 215 } 216 7cd9: 5f pop %edi 217 7cda: 5d pop %ebp 218 7cdb: c3 ret 219 220 00007cdc <readseg>: 221 222 // Read ‘count‘ bytes at ‘offset‘ from kernel into physical address ‘pa‘. 223 // Might copy more than asked 224 void 225 readseg(uint32_t pa, uint32_t count, uint32_t offset) 226 { 227 7cdc: 55 push %ebp 228 7cdd: 89 e5 mov %esp,%ebp 229 7cdf: 57 push %edi 230 7ce0: 56 push %esi 231 232 // round down to sector boundary 233 pa &= ~(SECTSIZE - 1); 234 235 // translate from bytes to sectors, and kernel starts at sector 1 236 offset = (offset / SECTSIZE) + 1; 237 7ce1: 8b 7d 10 mov 0x10(%ebp),%edi 238 239 // Read ‘count‘ bytes at ‘offset‘ from kernel into physical address ‘pa‘. 240 // Might copy more than asked 241 void 242 readseg(uint32_t pa, uint32_t count, uint32_t offset) 243 { 244 7ce4: 53 push %ebx 245 uint32_t end_pa; 246 247 end_pa = pa + count; 248 7ce5: 8b 75 0c mov 0xc(%ebp),%esi 249 250 // Read ‘count‘ bytes at ‘offset‘ from kernel into physical address ‘pa‘. 251 // Might copy more than asked 252 void 253 readseg(uint32_t pa, uint32_t count, uint32_t offset) 254 { 255 7ce8: 8b 5d 08 mov 0x8(%ebp),%ebx 256 257 // round down to sector boundary 258 pa &= ~(SECTSIZE - 1); 259 260 // translate from bytes to sectors, and kernel starts at sector 1 261 offset = (offset / SECTSIZE) + 1; 262 7ceb: c1 ef 09 shr $0x9,%edi 263 void 264 readseg(uint32_t pa, uint32_t count, uint32_t offset) 265 { 266 uint32_t end_pa; 267 268 end_pa = pa + count; 269 7cee: 01 de add %ebx,%esi 270 271 // round down to sector boundary 272 pa &= ~(SECTSIZE - 1); 273 274 // translate from bytes to sectors, and kernel starts at sector 1 275 offset = (offset / SECTSIZE) + 1; 276 7cf0: 47 inc %edi 277 uint32_t end_pa; 278 279 end_pa = pa + count; 280 281 // round down to sector boundary 282 pa &= ~(SECTSIZE - 1); 283 7cf1: 81 e3 00 fe ff ff and $0xfffffe00,%ebx 284 offset = (offset / SECTSIZE) + 1; 285 286 // If this is too slow, we could read lots of sectors at a time. 287 // We‘d write more to memory than asked, but it doesn‘t matter -- 288 // we load in increasing order. 289 while (pa < end_pa) { 290 7cf7: 39 f3 cmp %esi,%ebx 291 7cf9: 73 12 jae 7d0d <readseg+0x31> 292 // Since we haven‘t enabled paging yet and we‘re using 293 // an identity segment mapping (see boot.S), we can 294 // use physical addresses directly. This won‘t be the 295 // case once JOS enables the MMU. 296 readsect((uint8_t*) pa, offset); 297 7cfb: 57 push %edi 298 7cfc: 53 push %ebx 299 pa += SECTSIZE; 300 offset++; 301 7cfd: 47 inc %edi 302 // Since we haven‘t enabled paging yet and we‘re using 303 // an identity segment mapping (see boot.S), we can 304 // use physical addresses directly. This won‘t be the 305 // case once JOS enables the MMU. 306 readsect((uint8_t*) pa, offset); 307 pa += SECTSIZE; 308 7cfe: 81 c3 00 02 00 00 add $0x200,%ebx 309 while (pa < end_pa) { 310 // Since we haven‘t enabled paging yet and we‘re using 311 // an identity segment mapping (see boot.S), we can 312 // use physical addresses directly. This won‘t be the 313 // case once JOS enables the MMU. 314 readsect((uint8_t*) pa, offset); 315 7d04: e8 73 ff ff ff call 7c7c <readsect> 316 pa += SECTSIZE; 317 offset++; 318 7d09: 58 pop %eax 319 7d0a: 5a pop %edx 320 7d0b: eb ea jmp 7cf7 <readseg+0x1b> 321 } 322 } 323 7d0d: 8d 65 f4 lea -0xc(%ebp),%esp 324 7d10: 5b pop %ebx 325 7d11: 5e pop %esi 326 7d12: 5f pop %edi 327 7d13: 5d pop %ebp 328 7d14: c3 ret 329 330 00007d15 <bootmain>: 331 void readsect(void*, uint32_t); 332 void readseg(uint32_t, uint32_t, uint32_t); 333 334 void 335 bootmain(void) 336 { 337 7d15: 55 push %ebp 338 7d16: 89 e5 mov %esp,%ebp 339 7d18: 56 push %esi 340 7d19: 53 push %ebx 341 struct Proghdr *ph, *eph; 342 343 // read 1st page off disk 344 readseg((uint32_t) ELFHDR, SECTSIZE*8, 0); 345 7d1a: 6a 00 push $0x0 346 7d1c: 68 00 10 00 00 push $0x1000 347 7d21: 68 00 00 01 00 push $0x10000 348 7d26: e8 b1 ff ff ff call 7cdc <readseg> 349 350 // is this a valid ELF? 351 if (ELFHDR->e_magic != ELF_MAGIC) 352 7d2b: 83 c4 0c add $0xc,%esp 353 7d2e: 81 3d 00 00 01 00 7f cmpl $0x464c457f,0x10000 354 7d35: 45 4c 46 355 7d38: 75 37 jne 7d71 <bootmain+0x5c> 356 goto bad; 357 358 // load each program segment (ignores ph flags) 359 ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); 360 7d3a: a1 1c 00 01 00 mov 0x1001c,%eax 361 eph = ph + ELFHDR->e_phnum; 362 7d3f: 0f b7 35 2c 00 01 00 movzwl 0x1002c,%esi 363 // is this a valid ELF? 364 if (ELFHDR->e_magic != ELF_MAGIC) 365 goto bad; 366 367 // load each program segment (ignores ph flags) 368 ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); 369 7d46: 8d 98 00 00 01 00 lea 0x10000(%eax),%ebx 370 eph = ph + ELFHDR->e_phnum; 371 7d4c: c1 e6 05 shl $0x5,%esi 372 7d4f: 01 de add %ebx,%esi 373 for (; ph < eph; ph++) 374 7d51: 39 f3 cmp %esi,%ebx 375 7d53: 73 16 jae 7d6b <bootmain+0x56> 376 // p_pa is the load address of this segment (as well 377 // as the physical address) 378 readseg(ph->p_pa, ph->p_memsz, ph->p_offset); 379 7d55: ff 73 04 pushl 0x4(%ebx) 380 7d58: ff 73 14 pushl 0x14(%ebx) 381 goto bad; 382 383 // load each program segment (ignores ph flags) 384 ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); 385 eph = ph + ELFHDR->e_phnum; 386 for (; ph < eph; ph++) 387 7d5b: 83 c3 20 add $0x20,%ebx 388 // p_pa is the load address of this segment (as well 389 // as the physical address) 390 readseg(ph->p_pa, ph->p_memsz, ph->p_offset); 391 7d5e: ff 73 ec pushl -0x14(%ebx) 392 7d61: e8 76 ff ff ff call 7cdc <readseg> 393 goto bad; 394 395 // load each program segment (ignores ph flags) 396 ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); 397 eph = ph + ELFHDR->e_phnum; 398 for (; ph < eph; ph++) 399 7d66: 83 c4 0c add $0xc,%esp 400 7d69: eb e6 jmp 7d51 <bootmain+0x3c> 401 // as the physical address) 402 readseg(ph->p_pa, ph->p_memsz, ph->p_offset); 403 404 // call the entry point from the ELF header 405 // note: does not return! 406 ((void (*)(void)) (ELFHDR->e_entry))(); 407 7d6b: ff 15 18 00 01 00 call *0x10018 408 } 409 410 static __inline void 411 outw(int port, uint16_t data) 412 { 413 __asm __volatile("outw %0,%w1" : : "a" (data), "d" (port)); 414 7d71: ba 00 8a 00 00 mov $0x8a00,%edx 415 7d76: b8 00 8a ff ff mov $0xffff8a00,%eax 416 7d7b: 66 ef out %ax,(%dx) 417 7d7d: b8 00 8e ff ff mov $0xffff8e00,%eax 418 7d82: 66 ef out %ax,(%dx) 419 7d84: eb fe jmp 7d84 <bootmain+0x6f>
boot.S和boot.asm的代码如上所示,其中boot.asm是boot.S的反汇编文件,boot.S的作用是将处理器从实模式切换到保护模式,然后再进行后续的加载内核程序的操作。为什么要让处理器切换到保护模式下工作?这就要从PC物理内存的分布来考虑,以现在4G的内存为例,PC物理内存的分布大致可以用以下的图来表示:
在早期16bit的8088处理器上,地址总线为20位,可寻址空间为2^20,因此只能访问最下方1MB的内存。之后随着技术发展,Intel公司的处理器发展到了32bit寻址,为了兼容原来的软硬件,保留了PC物理内存中最下方1MB内存的布局和使用方式,把这之上的内存高地址部分设置为扩展内存(尽管这部分内存有一部分预留给了32bit的设备,如上图所述)。
MIT JOS学习笔记01(2016.10.22)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。