4,436
社区成员
发帖
与我相关
我的任务
分享
80/*
81 * 32-bit kernel entrypoint; only used by the boot CPU. On entry,
82 * %esi points to the real-mode code as a 32-bit pointer.
83 * CS and DS must be 4 GB flat segments, but we don't depend on
84 * any particular GDT layout, because we load our own as soon as we
85 * can.
86 */
87__HEAD
88ENTRY(startup_32)
89 movl pa(stack_start),%ecx
90
91 /* test KEEP_SEGMENTS flag to see if the bootloader is asking
92 us to not reload segments */
93 testb $(1<<6), BP_loadflags(%esi)
94 jnz 2f
95
96/*
97 * Set segments to known values.
98 */
99 lgdt pa(boot_gdt_descr)
100 movl $(__BOOT_DS),%eax
101 movl %eax,%ds
102 movl %eax,%es
103 movl %eax,%fs
104 movl %eax,%gs
105 movl %eax,%ss
1062:
107 leal -__PAGE_OFFSET(%ecx),%esp
108
109/*
110 * Clear BSS first so that there are no surprises...
111 */
112 cld
113 xorl %eax,%eax
114 movl $pa(__bss_start),%edi
115 movl $pa(__bss_stop),%ecx
116 subl %edi,%ecx
117 shrl $2,%ecx
118 rep ; stosl
上面这段代码来自3.14.3的head_32.S,启动时,内核被加载道物理地址0x01000000处,这个地址是由.config文件里的CONFIG_PHYSICAL_START定义的。入口在startup_32处,可以看到,这里用pa宏将stack_start,boot_gdt_descr,__bss_start,__bss_stop等转换成了物理地址来访问,
然后内核会建立临时页表
208page_pde_offset = (__PAGE_OFFSET >> 20);
209
210 movl $pa(__brk_base), %edi
211 movl $pa(initial_page_table), %edx
212 movl $PTE_IDENT_ATTR, %eax
21310:
214 leal PDE_IDENT_ATTR(%edi),%ecx /* Create PDE entry */
215 movl %ecx,(%edx) /* Store identity PDE entry */
216 movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */
217 addl $4,%edx
218 movl $1024, %ecx
21911:
220 stosl
221 addl $0x1000,%eax
222 loop 11b
223 /*
224 * End condition: we must map up to the end + MAPPING_BEYOND_END.
225 */
226 movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp
227 cmpl %ebp,%eax
228 jb 10b
229 addl $__PAGE_OFFSET, %edi
230 movl %edi, pa(_brk_end)
231 shrl $12, %eax
232 movl %eax, pa(max_pfn_mapped)
233
234 /* Do early initialization of the fixmap area */
235 movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
236 movl %eax,pa(initial_page_table+0xffc)
把0x00000000一直到内核结束这部分的物理地址分别映射到从0x00000000和从0xC0000000开始的对应虚拟地址,打个比方说,虚拟地址0x00000001和0xC0000001都映射到同样的物理地址0x00000001处。
最后开启分页
387/*
388 * Enable paging
389 */
390 movl $pa(initial_page_table), %eax
391 movl %eax,%cr3 /* set the page table pointer.. */
392 movl $CR0_STATE,%eax
393 movl %eax,%cr0 /* ..and set paging (PG) bit */
394 ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */
3951:
396 /* Shift the stack pointer to a virtual address */
397 addl $__PAGE_OFFSET, %esp
可以看到,直到最后开启分页前,还是用pa宏来访问物理地址,当开启分页后,内核会用一个长跳转跳转到内核空间,这时候,因为对应的页表已经建立,可以使用虚拟地址了,后面也就不需要使用pa宏了。