|
9#
楼主 |
发表于 2014-11-15 08:43:50
|
只看该作者
本帖最后由 不点 于 2014-11-25 11:30 编辑
init_mem 函数很重要,一字一句详细分析一下。
- align 4
- proc init_mem
- ; calculate maximum allocatable address and number of allocatable pages
- mov edi, BOOT_VARS-OS_BASE + 0x9104 ; 从 0000:9104 开始,记录的是可用内存块的数组。
- mov ecx, [edi-4] ; 而 0000:9100 处的四字节记录了可用内存块的个数。
- xor esi, esi; esi will hold total amount of memory
- xor edx, edx; edx will hold maximum allocatable address
- .calcmax:
- ; round all to pages
- mov eax, [edi] ; 取内存块起始地址到 eax
- cmp [edi+16], byte 1 ; 检查内存块类型是否是可用内存
- jne .unusable ; 不是可用内存,跳过
-
- test eax, 0xFFF ; 检查起始地址是否为页面对齐
- jz @f ; 是的,跳过
- neg eax ; 不是按页对齐,取反它
- and eax, 0xFFF ; 屏蔽掉高位,只留下余数
- add [edi], eax ; 把它加到内存起始地址上,让其变成页面对齐
- adc dword [edi+4], 0 ; 进位时,调整高 32 位值
- sub [edi+8], eax ; 相应地,内存块长度也要适当减少
- sbb dword [edi+12], 0 ; 借位时,调整高 32 位的值
- jc .unusable ; 万一不够减,说明它太小,就忽略这块内存
- @@:
- and dword [edi+8], not 0xFFF ; 割掉内存块长度的尾巴, 让其 4K 对齐
- jz .unusable ; 割掉以后长度为 0, 无用了,忽略这块内存
- ; ignore memory after 4 Gb
- cmp dword [edi+4], 0 ; 起始地址高 32 位是否为 0?
- jnz .unusable ; 不是 0, 说明位于 4G 以上,忽略这块内存
- mov eax, [edi] ; 内存块起始于 4G 以内,取其起始地址到 eax
- cmp dword [edi+12], 0 ; 内存块长度高 32 位是否为 0?
- jnz .overflow ; 不是 0, 说明长度超过 4G, 需要截短
- add eax, [edi+8] ; 长度不超过 4G, 加到 eax 上,得到内存块的尾端地址
- jnc @f ; 没有发生进位,不用截短
- .overflow:
- mov eax, 0xFFFFF000 ; 截短, 尾部使用最高地址减去 4K
- @@:
- cmp edx, eax ; edx 比尾部地址大吗?
- jae @f ; 已经够大,跳过
- mov edx, eax ; 不够大,让其取当前最大的尾部地址值
- @@:
- sub eax, [edi] ; 尾部地址减去起始地址,等于内存块长度
- mov [edi+8], eax ; 修正长度低 32 位,但高 32 位也应该清 0, 却忘记了!
- add esi, eax ; 累加到 esi, 记录 4G 以内的全部可用内存量。
- jmp .usable
- .unusable:
- ; and dword [edi+8], 0
- .usable:
- add edi, 20 ; 处理下一个内存块
- loop .calcmax ; 循环计算最大值
- .calculated: ; 最大值计算完成,保存可用内存量到两个地址处
- mov [MEM_AMOUNT-OS_BASE], esi ; 可用内存量记录在地址 0xFE8C 处
- mov [pg_data.mem_amount-OS_BASE], esi
- shr esi, 12 ; 变成页面个数, 加以保存
- mov [pg_data.pages_count-OS_BASE], esi
-
- shr edx, 12 ; 把最高尾端地址也转化为页面值
- add edx, 31 ; 加上尾巴, 准备按 32 对齐
- and edx, not 31 ; 割掉尾巴
- shr edx, 3 ; 右移 3 位,就是除以 8, 理解不了----这可能是个错误。假如不是错误的话,那就是把每个页面对应于一个 bit,8 个页面就对应于一个字节,所以要除以 8,得到页映射位图所需要的字节数。
- mov [pg_data.pagemap_size-OS_BASE], edx
-
- add edx, (sys_pgmap-OS_BASE)+4095 ; 从系统页映射(应该是位图)基地址开始,加上页映射位图所需要的字节数
- and edx, not 4095 ; 再向上进行页对齐
- mov [tmp_page_tabs], edx ; 就得到了下一个临时页表的基地址。
-
- mov edx, esi ; 取可用页面的总数
- and edx, -1024 ; 向下页对齐
- cmp edx, (OS_BASE/4096) ; 比用户程序虚拟内存页数多吗?
- jbe @F ; 不多,跳过
- mov edx, (OS_BASE/4096) ; 多了,截短, 只使用所需要的页数
- jmp .set
- @@:
- cmp edx, (HEAP_BASE-OS_BASE+HEAP_MIN_SIZE)/4096 ; 比内核所需要的页数少吗? HEAP_BASE位于8M处,HEAP_MIN_SIZE=16M
- jae .set ; 不少,跳过
- mov edx, (HEAP_BASE-OS_BASE+HEAP_MIN_SIZE)/4096 ; 少了,扩大到内核所需要的页面数
- .set:
- mov [pg_data.kernel_pages-OS_BASE], edx ; 这就得到了内核要处理的页面总数
- 页面总容量的范围是 24M 至 2G。
- shr edx, 10 ; 每个页表可以含有 1024 个页面指针,所以,除以 1024
- mov [pg_data.kernel_tables-OS_BASE], edx ; 得到内核所需要的页表数。
- 每个页表对应于 4M 空间,所以,最多只有 512 个页表。
-
- ; 清除内核进程空间 8K,页目录被清零。
- xor eax, eax
- mov edi, sys_proc-OS_BASE ; EDI=0x6F000
- mov ecx, 8192/4
- cld
- rep stosd
-
- mov edx, (sys_proc-OS_BASE+PROC.pdt_0)+ 0x800; (OS_BASE shr 20) 指向 kernel 所对应的页目录项
- bt [cpu_caps-OS_BASE], CAPS_PSE ; CPU 支持页面大小扩展吗?
- jnc .no_PSE ; 不支持,跳过
-
- mov ebx, cr4
- or ebx, CR4_PSE
- mov eax, PG_LARGE+PG_SW ; 页目录项直接使用 4M 的大页面
- mov cr4, ebx ; 开启页面大小扩展功能
- dec [pg_data.kernel_tables-OS_BASE] ; 内核页表个数减少 1 个
-
- mov [edx], eax ; 写入第一个页目录项,指向物理地址 0 处的 4M 空间。
- add edx, 4 ; 这是下一个页目录项的指针
-
- mov edi, [tmp_page_tabs]
- jmp .map_kernel_heap ; new kernel fits to the first 4Mb - nothing to do with ".map_low"
- .no_PSE:
- 先填充临时页表,它包含了从物理地址0到tmp_page_tabs的全部页
- tmp_page_tabs 大约位于 0x344000,那么,总共的页数就是 0x344 个。
- mov eax, PG_SW
- mov ecx, [tmp_page_tabs]
- shr ecx, 12
- .map_low:
- mov edi, [tmp_page_tabs]
- @@: ;
- stosd
- add eax, 0x1000
- dec ecx
- jnz @B
- 0x344 个指针的长度是 0x344 × 4 = 0xD10 = 3344 字节。
- 写完临时页表之后,紧接着是内核页表。
- .map_kernel_heap:
- 首先清空内核页表,它最大有 2M 的长度。
- mov ecx, [pg_data.kernel_tables-OS_BASE]
- shl ecx, 10
- xor eax, eax
- rep stosd
- 开始写内核页目录项,指向临时页表,在 PSE 的情况,它是指向清空了的内核页表;
- 在 no_PSE 的情况,它是指向临时页表,最终映射到物理地址 0。
- 这里比较混乱。在 PSE 的情况,4M 是已经完全映射的。而在 no_PSE 的情况,4M 没有完全映射。
- mov ecx, [pg_data.kernel_tables-OS_BASE]
- mov eax, [tmp_page_tabs]
- or eax, PG_SW
- mov edi, edx
-
- .map_kernel_tabs:
- stosd
- add eax, 0x1000
- dec ecx
- jnz .map_kernel_tabs
- 内核页目录项写完了,再把页目录所在的地址映射到 page_tabs 处。
- mov dword [sys_proc-OS_BASE+PROC.pdt_0+(page_tabs shr 20)], sys_proc+PROC.pdt_0+PG_SW-OS_BASE
-
- mov edi, (sys_proc+PROC.pdt_0-OS_BASE)
- lea esi, [edi+(OS_BASE shr 20)]
- movsd
- movsd
- ret
- endp
复制代码
|
|