📄 startup.asm
字号:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; EXPORTS:
;
; extern unsigned long _conv_mem_size, _ext_mem_size;
; extern unsigned long _init_ramdisk_adr, _init_ramdisk_size;
;
; void halt(void);
; unsigned long get_page_fault_adr(void);
; unsigned long get_page_dir(void);
; void set_page_dir(unsigned long cr3);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; IMPORTS
; from linker script:
EXTERN code, data, end
%macro EXPORT 1
GLOBAL %1
%1:
GLOBAL _%1
_%1:
%endmacro
%macro IMPORT 1
%ifdef UNDERBARS
EXTERN _%1 ; GCC for DOS (DJGPP; COFF)
%define %1 _%1
%else
EXTERN %1 ; GCC for Linux (ELF)
%endif
%endmacro
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; pmode entry point
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[SECTION .dtext] ; discardable kernel code
[BITS 32]
EXPORT entry
; where did the loader put us?
call where_am_i
where_am_i:
pop esi ; ESI=physical adr (where we are)
sub esi,where_am_i ; subtract virtual adr (where we want to be)
; now ESI=virt-to-phys
; all data segment accesses must be relative to ESI until we activate paging
; virt_adr + virt_to_phys = virt_adr + (phys - virt) = phys_adr
; phys_adr - virt_to_phys = phys_adr - (phys - virt) = virt_adr
; save kernel virt_to_phys conversion value
mov [esi + _kvirt_to_phys],esi
; loader GDT is gone (or not what we want); so set up a new one.
; GDTR = PHYSICAL address of gdt_ptr ('esi + gdt_ptr', not 'gdt_ptr')
; gdt_ptr = VIRTUAL address of new GDT ('gdt', not 'esi + gdt')
; Do NOT reload any segment registers until paging is enabled
lgdt [esi + gdt_ptr]
; set up kernel page tables
call init_paging
jnc paging1
mov byte [0B8000h],'P' ; 'P'==error setting up page tables
jmp short $ ; freeze
paging1:
lea eax,[esi + kernel_page_dir]
mov cr3,eax
mov eax,cr0
; or eax,080000000h ; xxx - make sure you have a 486+
; or eax,080010000h ; set the 486 WP (page write protect) bit
or eax,0C0010000h ; set the 486 CE (cache enable) and WP bits
mov cr0,eax
; NOW reload segment registers; starting with CS (far jump)
jmp SYS_CODE_SEL:paging2
paging2:
mov ax,SYS_DATA_SEL
mov ds,eax
mov ss,eax
mov es,eax
mov fs,eax
mov gs,eax
; now we can use absolute addresses in the data and BSS segments
mov esp,stack ; set up pmode stack
; set up TSS descriptor, then load task register
mov eax,tss
mov [gdt1 + 2],ax
shr eax,16
mov [gdt1 + 4],al
mov [gdt1 + 7],ah
mov ax,TSS_SEL
ltr ax
; set up interrupt handlers, then load IDT register
mov ecx,(idt_end - idt) >> 3 ; number of exception handlers
mov ebx,idt
mov edx,isr0
do_idt:
mov eax,edx ; EAX=offset of entry point
mov [ebx],ax ; set low 16 bits of gate offset
shr eax,16
mov [ebx + 6],ax ; set high 16 bits of gate offset
add ebx,8 ; 8 bytes/interrupt gate
add edx,(isr1 - isr0) ; 9 bytes/stub (push+push+32-bit jmp)
loop do_idt
lidt [idt_ptr]
IMPORT main
call main ; call C code
jmp $ ; freeze
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: map_page
; action: maps one page into the page tables
; in: EAX=page virtual address
; EBX=page directory physical address
; DX=page privilege
; EDI=page physical address
; out (error): CY=1 if a necessary page table is not installed
; out (success): CY=0
; modifies: (nothing)
; notes: all address translation must be turned off when
; this function is called: no paging,
; and base address of data segment == 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
map_page:
push ebx
push eax
; get index-into-page-directory from page virtual address
shr eax,22
; add index to page directory physical address
shl eax,2
add ebx,eax
pop eax
push eax
; get page directory entry (PDE)
mov ebx,[ebx]
; convert PDE to page table physical address
; error if no page table installed
and ebx,0FFFFF000h
stc
je map_page_err
; get index-into-page-table from page virtual address
shr eax,12
and eax,000003FFh
shl eax,2
; add index to page table physical address, forming physical
; address of page table entry (PTE)
add ebx,eax
; create PTE from page physical address and privilege
mov eax,edi
and eax,0FFFFF000h
or ax,dx
; store PTE
mov [ebx],eax
map_page_err:
pop eax
pop ebx
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: map_pages
; action: maps a range of pages with contiguous virtual
; and physical addresses into the page tables
; in: EAX=first page virtual address
; EBX=page directory physical address
; ECX=range size, in bytes
; DX=page privilege
; EDI=first page physical address
; out (error): CY=1 if a necessary page table is not installed
; out (success): CY=0
; modifies: (nothing)
; notes: all address translation must be turned off when
; this function is called: no paging,
; and base address of data segment == 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
map_pages:
pusha
and ecx,0FFFFF000h
je map_pages_2
map_pages_1:
call map_page
jc map_pages_2
add edi,4096
add eax,4096
sub ecx,4096
jne map_pages_1
map_pages_2:
popa
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: init_paging
; action:
; in: ESI=kernel virt-to-phys
; out (error): CY=1 if a necessary page table is not installed
; out (success): CY=0
; modifies: (nothing)
; notes: all address translation must be turned off when
; this function is called: no paging,
; and base address of data segment == 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
init_paging:
pusha
; get physical address of kernel page dir to EBX
lea ebx,[esi + kernel_page_dir]
; make an entry in kernel page dir for ram_page_table
lea eax,[esi + ram_page_table]
or al,7 ; ring 3, writable, present
mov [ebx + 0],eax
; make an entry in kernel page dir for kernel_page_table
lea eax,[esi + kernel_page_table]
or al,7 ; ring 3, writable, present
; xxx - this assumes kernel is compiled to run at C0000000h
mov [ebx + 3072],eax
; identity-map conventional memory, except for page 0
;
; xxx - can not access the real-mode interrupt vector table at 0000:0000
; or the BIOS data segment at 0040:0000
mov dx,3 ; privilege = ring 0, writable, present
mov eax,1000h ; virtual address
mov edi,eax ; physical address is the same
mov ecx,[esi + _conv_mem_size]
call map_pages
jc near init_paging_err
; identity-map video memory
mov eax,0A0000h ; virtual address
mov edi,eax ; physical address
mov ecx,20000h
call map_pages
jc near init_paging_err
; identity-map BIOS memory read-only
;
; xxx - there might be read-write memory in this area on some
; plug-in cards...maybe get a memory map via INT 15h AX=E820h
mov dx,1 ; privilege = ring 0, read-only, present
mov eax,0C0000h ; virtual address
mov edi,eax ; physical address
mov ecx,40000h
call map_pages
jc init_paging_err
; identity-map 1 meg extended memory
; xxx - do I need to do this?
%if 1
mov dx,3 ; privilege = ring 0, writable, present
mov eax,100000h ; virtual address
mov edi,eax ; physical address
mov ecx,100000h
call map_pages
jc init_paging_err
%endif
; map kernel code read-only
mov dx,1 ; privilege = ring 0, read-only, present
mov eax,code ; virtual address
mov edi,eax
add edi,esi ; physical address
mov ecx,data
sub ecx,code
call map_pages
jc init_paging_err
; map kernel data
mov dx,3 ; privilege = ring 0, writable, present
mov eax,data ; virtual address
mov edi,eax
add edi,esi ; physical address
mov ecx,end
sub ecx,data
call map_pages
init_paging_err:
popa
ret
; regular (non-discardable) kernel code
[SECTION .text]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: halt
; action: halts CPU until an interrupt occurs
; in: (nothing)
; out: (nothing)
; modifies: (nothing)
; notes: system will freeze if interrupts are disabled
; C prototype: void halt(void);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXPORT halt
hlt
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: get_page_fault_adr
; action: gets virtual address of latest page fault
; from register CR2
; in: (nothing)
; out: EAX=page fault address
; modifies: EAX
; notes: C prototype: unsigned long get_page_fault_adr(void);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXPORT get_page_fault_adr
mov eax,cr2
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: get_page_dir
; action: reads physical address of current page directory
; from CPU register CR3
; in: (nothing)
; out: EAX=page directory address
; modifies: EAX
; notes: C prototype: unsigned long get_page_dir(void);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXPORT get_page_dir
mov eax,cr3
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: set_page_dir
; action: stores physical address of new page directory
; in CPU register CR3
; in: [esp + 8] (left-most argument) is new CR3 value
; out: (nothing)
; modifies: (nothing)
; notes: this flushes the entire TLB (the page table cache)
; C prototype: void set_page_dir(unsigned long cr3);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXPORT set_page_dir
push eax ; we declared it void; must save EAX
mov eax,[esp + 8]
mov cr3,eax
pop eax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; interrupt/exception handlers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
fault1:
push gs ; push segment registers
push fs
push es
push ds
pusha ; push EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX
mov ax,SYS_DATA_SEL ; put known-good values in seg regs
mov ds,eax
mov es,eax
mov fs,eax
mov gs,eax
IMPORT fault
call fault
; fault() returns old value of _curr_task
; save previous krnl_esp
mov ebx,eax
mov [ebx + 0],esp
; load new krnl_esp
IMPORT _curr_task
mov esi,[_curr_task]
mov esp,[esi + 0]
; reloading CR3 flushes the TLB (the page table cache)
; This is slow, so do it only if a task-switch occurs
cmp ebx,esi
je no_switch
mov eax,[esi + 4]
mov cr3,eax
no_switch:
lea eax,[esp + 76]
mov [tss_esp0],eax
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -