首页 > 代码库 > 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 shouldnt), 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
boot.S
技术分享
  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 shouldnt), 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     // Wed write more to memory than asked, but it doesnt 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 havent enabled paging yet and were using
293         // an identity segment mapping (see boot.S), we can
294         // use physical addresses directly.  This wont 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 havent enabled paging yet and were using
303         // an identity segment mapping (see boot.S), we can
304         // use physical addresses directly.  This wont 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 havent enabled paging yet and were using
311         // an identity segment mapping (see boot.S), we can
312         // use physical addresses directly.  This wont 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.asm

   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)