作業系統實現之記憶體分頁機制.虛擬空間
阿新 • • 發佈:2019-01-30
這裡我們將把頁目錄表放在0x100000處.頁表也挨著頁目錄表放在0x101000處(第二個頁表.當然在此之前應該把實體記憶體給算出來.這裡可以使用bios中斷來獲取實體記憶體
%include "boot.inc" section loader vstart=loader_base_addr ;------------全域性描述符表的定義 gdt_base: dd 0x00000000 ;全域性描述表.第一個描述符要為空 dd 0x00000000 code_base: dd 0x0000FFFF dd desc_code_high4 data_stack_desc: dd 0x0000ffff dd desc_data_high4 video_desc: dd 0x80000007 ;段界限 limit=0x7fff.基址位於0xb8000 dd desc_video_high4 ;---------GDT的屬性 gdt_size equ $-gdt_base ;GDT大小 gdt_limit equ gdt_size ;GDT限制 times 60 dq 0 ;預留60個描述符的空位置 ;以下定義一個數用來儲存筆記本的記憶體大小,以上一共0x200個位元組(64*8=0x200) total_mem_bytes dd 0 ;----------定義段選擇子------------- selector_code equ (0x0001<<3)+ti_gdt+rpl0 selector_data equ (0x0002<<3)+ti_gdt+rpl0 selector_video equ (0x0003<<3)+ti_gdt+rpl0 ;定義gdt的指標,前2個位元組為gdt界限,後4個位元組為gdt起始地址 gdt_ptr dw gdt_limit dd gdt_base ;-----定義ards結構體數量------------- ards_buf times 244 db 0 ards_nr dw 0 ;用於記錄ards結構體數量 ;以上一共0x300個位元組------------------ loader_start: ;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 獲取記憶體佈局 ------- xor ebx, ebx ;第一次呼叫時,ebx值要為0 mov edx, 0x534d4150 ;edx只賦值一次,迴圈體中不會改變 mov di, ards_buf ;ards結構緩衝區 .e820_mem_get_loop: ;迴圈獲取每個ARDS記憶體範圍描述結構 mov eax, 0x0000e820 ;執行int 0x15後,eax值變為0x534d4150,所以每次執行int前都要更新為子功能號。 mov ecx, 20 ;ARDS地址範圍描述符結構大小是20位元組 int 0x15 jc .e820_failed_so_try_e801 ;若cf位為1則有錯誤發生,嘗試0xe801子功能 add di, cx ;使di增加20位元組指向緩衝區中新的ARDS結構位置 inc word [ards_nr] ;記錄ARDS數量 cmp ebx, 0 ;若ebx為0且cf不為1,這說明ards全部返回,當前已是最後一個 jnz .e820_mem_get_loop ;在所有ards結構中,找出(base_add_low + length_low)的最大值,即記憶體的容量。 mov cx, [ards_nr] ;遍歷每一個ARDS結構體,迴圈次數是ARDS的數量 mov ebx, ards_buf xor edx, edx ;edx為最大的記憶體容量,在此先清0 .find_max_mem_area: ;無須判斷type是否為1,最大的記憶體塊一定是可被使用 mov eax, [ebx] ;base_add_low add eax, [ebx+8] ;length_low add ebx, 20 ;指向緩衝區中下一個ARDS結構 cmp edx, eax ;氣泡排序,找出最大,edx暫存器始終是最大的記憶體容量 jge .next_ards mov edx, eax ;edx為總記憶體大小 .next_ards: loop .find_max_mem_area jmp .mem_get_ok ;------ int 15h ax = E801h 獲取記憶體大小,最大支援4G ------ ; 返回後, ax cx 值一樣,以KB為單位,bx dx值一樣,以64KB為單位 ; 在ax和cx暫存器中為低16M,在bx和dx暫存器中為16MB到4G。 .e820_failed_so_try_e801: mov ax,0xe801 int 0x15 jc .e801_failed_so_try88 ;若當前e801方法失敗,就嘗試0x88方法 ;1 先算出低15M的記憶體,ax和cx中是以KB為單位的記憶體數量,將其轉換為以byte為單位 mov cx,0x400 ;cx和ax值一樣,cx用做乘數 mul cx shl edx,16 and eax,0x0000FFFF or edx,eax add edx, 0x100000 ;ax只是15MB,故要加1MB mov esi,edx ;先把低15MB的記憶體容量存入esi暫存器備份 ;2 再將16MB以上的記憶體轉換為byte為單位,暫存器bx和dx中是以64KB為單位的記憶體數量 xor eax,eax mov ax,bx mov ecx, 0x10000 ;0x10000十進位制為64KB mul ecx ;32位乘法,預設的被乘數是eax,積為64位,高32位存入edx,低32位存入eax. add esi,eax ;由於此方法只能測出4G以內的記憶體,故32位eax足夠了,edx肯定為0,只加eax便可 mov edx,esi ;edx為總記憶體大小 jmp .mem_get_ok ;----------------- int 15h ah = 0x88 獲取記憶體大小,只能獲取64M之內 ---------- .e801_failed_so_try88: ;int 15後,ax存入的是以kb為單位的記憶體容量 mov ah, 0x88 int 0x15 jc .error_hlt and eax,0x0000FFFF ;16位乘法,被乘數是ax,積為32位.積的高16位在dx中,積的低16位在ax中 mov cx, 0x400 ;0x400等於1024,將ax中的記憶體容量換為以byte為單位 mul cx shl edx, 16 ;把dx移到高16位 or edx, eax ;把積的低16位組合到edx,為32位的積 add edx,0x100000 ;0x88子功能只會返回1MB以上的記憶體,故實際記憶體大小要加上1MB .mem_get_ok: mov [total_mem_bytes], edx ;將記憶體換為byte單位後存入total_mem_bytes處。 ;-----進入保護模式---------- in al,0x92 or al,0000_0010B out 0x92,al ;----------載入GDT---------- lgdt [gdt_ptr] ;---------cr0第0位置為1,表示開啟保護模式----------- mov eax,cr0 or eax,0x00000001 mov cr0,eax jmp dword selector_code:p_mode_start .error_hlt: ;出錯則掛起 hlt [bits 32] p_mode_start: mov ax,selector_data mov ds,ax mov es,ax mov ss,ax mov esp,loader_stack_top mov ax,selector_video mov gs,ax ;建立頁目錄表 call set_page sgdt [gdt_ptr] ;載入GDT mov ebx,[gdt_ptr+2] or dword [ebx+0x18+4] ,0xc0000000 ;+4是因為.段描述的高4位元組的段基址是31-24. ;將gdt的基址加上0xc00000000使其成為核心的高地址 add dword [gdt_ptr+2],0xc0000000 add esp,0xc0000000 ;-----頁目錄表賦給cr3----- mov eax,page_dir_table_pos mov cr3,eax ;開啟cr0的pg位(31位) mov eax,cr0 or eax,0x80000000 mov cr0, eax lgdt[gdt_ptr] mov byte [gs:160],'V' jmp $ ;------------建立頁目錄表以及頁表 set_page: mov ecx,4096 mov esi,0 .clear_page_dir: mov byte [page_dir_table_pos +esi],0 inc esi loop .clear_page_dir ;-------建立PDE------- create_pde: mov eax,page_dir_table_pos add eax,0x1000 ;頁表地址 mov ebx,eax ;ebx指向第一個頁表地址 or eax, pg_us_u | pg_rw_w | pg_p mov [page_dir_table_pos +0x0],eax ;將第一個頁表寫到頁目錄表的第一項 mov [page_dir_table_pos+0xc00],eax ;一個頁表項佔用4位元組,0xc00表示第768個頁表佔用的目錄項,0xc00以上的目錄項用於核心空間 sub eax,0x1000 mov [page_dir_table_pos+4092],eax ;頁目錄表的最後一個頁目錄項指向它自己 ;---------建立頁表項pte------------- mov ecx,256 ;1M低端記憶體/4k=256 mov esi,0 mov edx,pg_us_u | pg_rw_w |pg_p .create_pte: mov [ebx+esi*4],edx add edx,4096 inc esi loop .create_pte ;--------建立核心跟頁表的pde mov eax,page_dir_table_pos add eax,0x2000 ;指向第二個頁表 or eax,pg_us_u | pg_rw_w | pg_p mov ebx,page_dir_table_pos mov esi,769 mov ecx,254 .create_kernel_pde: mov [ebx+esi*4],eax inc esi add eax,0x1000 loop .create_kernel_pde ret