我們要將 Orange'S 安裝到硬碟上,並實現硬碟啟動。
回憶軟碟啟動的過程:
- BIOS 將開機磁區讀入記憶體 0000:7C00 處
- 跳轉到 0000:7C00 處開始執行開機程式碼
- 開機程式碼從軟碟中找到 loader.bin,並將其讀入記憶體
- 跳轉到 loader.bin 開始執行
- loader.bin 從軟碟中找到 kernel.bin,並將其讀入記憶體
- 跳轉到 kernel.bin 開始執行,到此可認為啟動過程結束
- 系統執行中
第 1 步中,係由 CMOS 來決定。第 3 步和第 5 步中,
- 軟碟啟動:程式碼將在軟碟中尋找 loader.bin 和 kernel.bin
- 硬碟啟動:需要讓開機磁區程式碼從硬碟中尋找 loader.bin 並讓 loader 從硬碟中尋找 kernel.bin
故我們必須重寫 boot.asm 和 loader.asm,讓它們讀取硬碟而不是軟碟。新的檔我們取名為 hdboot.asm 和 hdldr.asm。
程式碼
include/sys/proto.h
PUBLIC int do_lseek(); // 新增
boot/hdboot.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; hdboot.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2008
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
org 0x7c00 ; bios always loads boot sector to 0000:7C00
jmp boot_start
%include "load.inc"
STACK_BASE equ 0x7C00 ; base address of stack when booting
TRANS_SECT_NR equ 2
SECT_BUF_SIZE equ TRANS_SECT_NR * 512
disk_address_packet: db 0x10 ; [ 0] Packet size in bytes.
db 0 ; [ 1] Reserved, must be 0.
db TRANS_SECT_NR ; [ 2] Nr of blocks to transfer.
db 0 ; [ 3] Reserved, must be 0.
dw 0 ; [ 4] Addr of transfer - Offset
dw SUPER_BLK_SEG ; [ 6] buffer. - Seg
dd 0 ; [ 8] LBA. Low 32-bits.
dd 0 ; [12] LBA. High 32-bits.
err:
mov dh, 3 ; "Error 0 "
call disp_str ; display the string
jmp $
boot_start:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, STACK_BASE
call clear_screen
mov dh, 0 ; "Booting "
call disp_str ; display the string
;; read the super block to SUPER_BLK_SEG::0
mov dword [disk_address_packet + 8], ROOT_BASE + 1
call read_sector
mov ax, SUPER_BLK_SEG
mov fs, ax
mov dword [disk_address_packet + 4], LOADER_OFF
mov dword [disk_address_packet + 6], LOADER_SEG
;; get the sector nr of `/' (ROOT_INODE), it'll be stored in eax
mov eax, [fs:SB_ROOT_INODE]
call get_inode
;; read `/' into ex:bx
mov dword [disk_address_packet + 8], eax
call read_sector
;; let's search `/' for the loader
mov si, LoaderFileName
push bx ; <- save
.str_cmp:
;; before comparation:
;; es:bx -> dir_entry @ disk
;; ds:si -> filename we want
add bx, [fs:SB_DIR_ENT_FNAME_OFF]
.1:
lodsb ; ds:si -> al
cmp al, byte [es:bx]
jz .2
jmp .different ; oops
.2: ; so far so good
cmp al, 0 ; both arrive at a '\0', match
jz .found
inc bx ; next char @ disk
jmp .1 ; on and on
.different:
pop bx ; -> restore
add bx, [fs:SB_DIR_ENT_SIZE]
sub ecx, [fs:SB_DIR_ENT_SIZE]
jz .not_found
mov dx, SECT_BUF_SIZE
cmp bx, dx
jge .not_found
push bx
mov si, LoaderFileName
jmp .str_cmp
.not_found:
mov dh, 2
call disp_str
jmp $
.found:
pop bx
add bx, [fs:SB_DIR_ENT_INODE_OFF]
mov eax, [es:bx] ; eax <- inode nr of loader
call get_inode ; eax <- start sector nr of loader
mov dword [disk_address_packet + 8], eax
load_loader:
call read_sector
cmp ecx, SECT_BUF_SIZE
jl .done
sub ecx, SECT_BUF_SIZE ; bytes_left -= SECT_BUF_SIZE
add word [disk_address_packet + 4], SECT_BUF_SIZE ; transfer buffer
jc err
add dword [disk_address_packet + 8], TRANS_SECT_NR ; LBA
jmp load_loader
.done:
mov dh, 1
call disp_str
jmp LOADER_SEG:LOADER_OFF
jmp $
;============================================================================
;字元串
;----------------------------------------------------------------------------
LoaderFileName db "hdldr.bin", 0 ; LOADER 之檔案名
; 為簡化程式碼, 下面每個字元串的長度均為 MessageLength
MessageLength equ 9
BootMessage: db "BootingHD"; 9字元, 不夠則用空格補齊. 序號 0
Message1 db "HD Boot "; 9字元, 不夠則用空格補齊. 序號 1
Message2 db "No LOADER"; 9字元, 不夠則用空格補齊. 序號 2
Message3 db "Error 0 "; 9字元, 不夠則用空格補齊. 序號 3
;============================================================================
clear_screen:
mov ax, 0x600 ; AH = 6, AL = 0
mov bx, 0x700 ; 黑底白字(BL = 0x7)
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0x184f ; 右下角: (80, 50)
int 0x10 ; int 0x10
ret
;----------------------------------------------------------------------------
; 函數名: disp_str
;----------------------------------------------------------------------------
; 作用:
; 顯示一個字元串, 函數開始時 dh 中應該是字元串序號(0-based)
disp_str:
mov ax, MessageLength
mul dh
add ax, BootMessage
mov bp, ax ; `.
mov ax, ds ; | ES:BP = 串位址
mov es, ax ; /
mov cx, MessageLength ; CX = 串長度
mov ax, 0x1301 ; AH = 0x13, AL = 0x1
mov bx, 0x7 ; 頁號為0(BH = 0) 黑底白字(BL = 0x7)
mov dl, 0
int 0x10 ; int 0x10
ret
;----------------------------------------------------------------------------
; read_sector
;----------------------------------------------------------------------------
; Entry:
; - fields disk_address_packet should have been filled
; before invoking the routine
; Exit:
; - es:bx -> data read
; registers changed:
; - eax, ebx, dl, si, es
read_sector:
xor ebx, ebx
mov ah, 0x42
mov dl, 0x80
mov si, disk_address_packet
int 0x13
mov ax, [disk_address_packet + 6]
mov es, ax
mov bx, [disk_address_packet + 4]
ret
;----------------------------------------------------------------------------
; get_inode
;----------------------------------------------------------------------------
; Entry:
; - eax : inode nr.
; Exit:
; - eax : sector nr.
; - ecx : the_inode.i_size
; - es:ebx : inodes sector buffer
; registers changed:
; - eax, ebx, ecx, edx
get_inode:
dec eax ; eax <- inode_nr -1
mov bl, [fs:SB_INODE_SIZE]
mul bl ; eax <- (inode_nr - 1) * INODE_SIZE
mov edx, SECT_BUF_SIZE
sub edx, dword [fs:SB_INODE_SIZE]
cmp eax, edx
jg err
push eax
mov ebx, [fs:SB_NR_IMAP_SECTS]
mov edx, [fs:SB_NR_SMAP_SECTS]
lea eax, [ebx+edx+ROOT_BASE+2]
mov dword [disk_address_packet + 8], eax
call read_sector
pop eax ; [es:ebx+eax] -> the inode
mov edx, dword [fs:SB_INODE_ISIZE_OFF]
add edx, ebx
add edx, eax ; [es:edx] -> the_inode.i_size
mov ecx, [es:edx] ; ecx <- the_inode.i_size
; es:[ebx+eax] -> the_inode.i_start_sect
add ax, word [fs:SB_INODE_START_OFF]
add bx, ax
mov eax, [es:bx]
add eax, ROOT_BASE ; eax <- the_inode.i_start_sect
ret
times 510-($-$$) db 0 ; 填充剩下的空間,使生成的二進制程式碼恰好為512字元
dw 0xaa55 ; 結束標誌
boot/hdldr.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; loader.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
org 0100h
jmp LABEL_START ; Start
%include "load.inc"
%include "pm.inc"
TRANS_SECT_NR equ 2
SECT_BUF_SIZE equ TRANS_SECT_NR * 512
disk_address_packet: db 0x10 ; [ 0] Packet size in bytes. Must be 0x10 or greater.
db 0 ; [ 1] Reserved, must be 0.
sect_cnt: db TRANS_SECT_NR ; [ 2] Number of blocks to transfer.
db 0 ; [ 3] Reserved, must be 0.
dw KERNEL_FILE_OFF ; [ 4] Address of transfer buffer. Offset
dw KERNEL_FILE_SEG ; [ 6] Seg
lba_addr: dd 0 ; [ 8] Starting LBA address. Low 32-bits.
dd 0 ; [12] Starting LBA address. High 32-bits.
; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------
; 段基址 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR | DA_32 | DA_LIMIT_4K ; 0 ~ 4G
LABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K ; 0 ~ 4G
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW | DA_DPL3 ; 显存首位址
; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------
GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1 ; 段界限
dd LOADER_PHY_ADDR + LABEL_GDT ; 基位址 (让基位址八字节对齐将起到优化速度之效果,目前懒得改)
; The GDT is not a segment itself; instead, it is a data structure in linear address space.
; The base linear address and limit of the GDT must be loaded into the GDTR register. -- IA-32 Software Developer’s Manual, Vol.3A
; GDT 选择子 ----------------------------------------------------------------------------------
SelectorFlatC equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3
; GDT 选择子 ----------------------------------------------------------------------------------
BaseOfStack equ 0100h
err:
mov dh, 5 ; "Error 0 "
call real_mode_disp_str ; display the string
jmp $
LABEL_START: ; <--- 从这里开始 *************
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
mov dh, 0 ; "Loading "
call real_mode_disp_str ; 显示字元串
; 得到内存数
mov ebx, 0 ; ebx = 后续值, 开始时需为 0
mov di, _MemChkBuf ; es:di 指向一个位址范围描述符结构(Address Range Descriptor Structure)
.MemChkLoop:
mov eax, 0E820h ; eax = 0000E820h
mov ecx, 20 ; ecx = 位址范围描述符结构的大小
mov edx, 0534D4150h ; edx = 'SMAP'
int 15h ; int 15h
jc .MemChkFail
add di, 20
inc dword [_dwMCRNumber] ; dwMCRNumber = ARDS 的个数
cmp ebx, 0
jne .MemChkLoop
jmp .MemChkOK
.MemChkFail:
mov dword [_dwMCRNumber], 0
.MemChkOK:
;; get the sector nr of `/' (ROOT_INODE), it'll be stored in eax
mov eax, [fs:SB_ROOT_INODE] ; fs -> super_block (see hdboot.asm)
call get_inode
;; read `/' into es:bx
mov dword [disk_address_packet + 8], eax
call read_sector
;; let's search `/' for the kernel
mov si, KernelFileName
push bx ; <- save
.str_cmp:
;; before comparation:
;; es:bx -> dir_entry @ disk
;; ds:si -> filename we want
add bx, [fs:SB_DIR_ENT_FNAME_OFF]
.1:
lodsb ; ds:si -> al
cmp al, byte [es:bx]
jz .2
jmp .different ; oops
.2: ; so far so good
cmp al, 0 ; both arrive at a '\0', match
jz .found
inc bx ; next char @ disk
jmp .1 ; on and on
.different:
pop bx ; -> restore
add bx, [fs:SB_DIR_ENT_SIZE]
sub ecx, [fs:SB_DIR_ENT_SIZE]
jz .not_found
push bx
mov si, KernelFileName
jmp .str_cmp
.not_found:
mov dh, 3
call real_mode_disp_str
jmp $
.found:
pop bx
add bx, [fs:SB_DIR_ENT_INODE_OFF]
mov eax, [es:bx] ; eax <- inode nr of kernel
call get_inode ; eax <- start sector nr of kernel
mov dword [disk_address_packet + 8], eax
load_kernel:
call read_sector
cmp ecx, SECT_BUF_SIZE
jl .done
sub ecx, SECT_BUF_SIZE ; bytes_left -= SECT_BUF_SIZE
add word [disk_address_packet + 4], SECT_BUF_SIZE ; transfer buffer
jc .1
jmp .2
.1:
add word [disk_address_packet + 6], 1000h
.2:
add dword [disk_address_packet + 8], TRANS_SECT_NR ; LBA
jmp load_kernel
.done:
mov dh, 2
call real_mode_disp_str
; 下面准备跳入保护模式 -------------------------------------------
; 加载 GDTR
lgdt [GdtPtr]
; 关中断
cli
; 打开位址线A20
in al, 92h
or al, 00000010b
out 92h, al
; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
; 真正进入保护模式
jmp dword SelectorFlatC:(LOADER_PHY_ADDR+LABEL_PM_START)
jmp $ ; never arrive here
;============================================================================
;变量
;----------------------------------------------------------------------------
wSectorNo dw 0 ; 要读取的扇区号
bOdd db 0 ; 奇数还是偶数
dwKernelSize dd 0 ; KERNEL.BIN 檔案大小
;============================================================================
;字元串
;----------------------------------------------------------------------------
KernelFileName db "kernel.bin", 0 ; KERNEL.BIN 之檔案名
; 为简化代码, 下面每个字元串的长度均为 MessageLength
MessageLength equ 9
LoadMessage: db "Loading "
Message1 db " "
Message2 db "in HD LDR"
Message3 db "No KERNEL"
Message4 db "Too Large"
Message5 db "Error 0 "
;============================================================================
;============================================================================
clear_screen:
mov ax, 0x600 ; AH = 6, AL = 0
mov bx, 0x700 ; 黑底白字(BL = 0x7)
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0x184f ; 右下角: (80, 50)
int 0x10 ; int 0x10
ret
;----------------------------------------------------------------------------
; 函数名: disp_str
;----------------------------------------------------------------------------
; 作用:
; 显示一个字元串, 函数开始时 dh 中应该是字元串序号(0-based)
real_mode_disp_str:
mov ax, MessageLength
mul dh
add ax, LoadMessage
mov bp, ax ; ┓
mov ax, ds ; ┣ ES:BP = 串位址
mov es, ax ; ┛
mov cx, MessageLength ; CX = 串长度
mov ax, 0x1301 ; AH = 0x13, AL = 0x1
mov bx, 0x7 ; 页号为0(BH = 0) 黑底白字(BL = 0x7)
mov dl, 0
int 0x10 ; int 0x10
ret
;----------------------------------------------------------------------------
; read_sector
;----------------------------------------------------------------------------
; before:
; - fields disk_address_packet should have been filled
; before invoking the routine
; after:
; - es:bx -> data read
; registers changed:
; - eax, ebx, dl, si, es
read_sector:
xor ebx, ebx
;mov dword [disk_address_packet + 8], eax
mov dword [disk_address_packet + 12], 0
mov ah, 0x42
mov dl, 0x80
mov si, disk_address_packet
int 0x13
mov ax, [disk_address_packet + 6]
mov es, ax
mov bx, [disk_address_packet + 4]
ret
;----------------------------------------------------------------------------
; get_inode
;----------------------------------------------------------------------------
; before:
; - eax : inode nr.
; after:
; - eax : sector nr.
; - ecx : the_inode.i_size
; - es:ebx : inodes sector buffer
; registers changed:
; - eax, ebx, ecx, edx
get_inode:
dec eax ; eax <- inode_nr -1
mov bl, [fs:SB_INODE_SIZE]
mul bl ; eax <- (inode_nr - 1) * INODE_SIZE
mov edx, SECT_BUF_SIZE
sub edx, dword [fs:SB_INODE_SIZE]
cmp eax, edx
jg err
push eax
mov ebx, [fs:SB_NR_IMAP_SECTS]
mov edx, [fs:SB_NR_SMAP_SECTS]
lea eax, [ebx+edx+ROOT_BASE+2]
mov dword [disk_address_packet + 8], eax
call read_sector
pop eax ; [es:ebx+eax] -> the inode
mov edx, dword [fs:SB_INODE_ISIZE_OFF]
add edx, ebx
add edx, eax ; [es:edx] -> the_inode.i_size
mov ecx, [es:edx] ; ecx <- the_inode.i_size
add ax, word [fs:SB_INODE_START_OFF]; es:[ebx+eax] -> the_inode.i_start_sect
add bx, ax
mov eax, [es:bx]
add eax, ROOT_BASE ; eax <- the_inode.i_start_sect
ret
; 从此以后的代码在保护模式下执行 ----------------------------------------------------
; 32 位代码段. 由实模式跳入 ---------------------------------------------------------
[SECTION .s32]
ALIGN 32
[BITS 32]
LABEL_PM_START:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorFlatRW
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov esp, TopOfStack
call DispMemInfo
;;; call DispReturn
;;; call DispHDInfo ; int 13h 读出的硬盘 geometry 好像有点不对头,不知道为什么,干脆不管它了
call SetupPaging
;mov ah, 0Fh ; 0000: 黑底 1111: 白字
;mov al, 'P'
;mov [gs:((80 * 0 + 39) * 2)], ax ; 螢幕第 0 行, 第 39 列。
call InitKernel
;jmp $
mov dword [BOOT_PARAM_ADDR], BOOT_PARAM_MAGIC ; BootParam[0] = BootParamMagic;
mov eax, [dwMemSize] ;
mov [BOOT_PARAM_ADDR + 4], eax ; BootParam[1] = MemSize;
mov eax, KERNEL_FILE_SEG
shl eax, 4
add eax, KERNEL_FILE_OFF
mov [BOOT_PARAM_ADDR + 8], eax ; BootParam[2] = KernelFilePhyAddr;
;***************************************************************
jmp SelectorFlatC:KRNL_ENT_PT_PHY_ADDR ; 正式进入内核 *
;***************************************************************
; 記憶體看上去是這樣的:
; ┃ ┃
; ┃ . ┃
; ┃ . ┃
; ┃ . ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; ┃■■■■■■Page Tables■■■■■■┃
; ┃■■■■■(大小由LOADER決定)■■■■┃
; 00101000h ┃■■■■■■■■■■■■■■■■■■┃ PAGE_TBL_BASE
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 00100000h ┃■■■■Page Directory Table■■■■┃ PAGE_DIR_BASE <- 1M
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; F0000h ┃□□□□□□□System ROM□□□□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; E0000h ┃□□□□Expansion of system ROM □□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; C0000h ┃□□□Reserved for ROM expansion□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃ B8000h ← gs
; A0000h ┃□□□Display adapter reserved□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; 9FC00h ┃□□extended BIOS data area (EBDA)□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 90000h ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 80000h ┃■■■■■■■KERNEL.BIN■■■■■■┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 30000h ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KRNL_ENT_PT_PHY_ADDR)
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ ┃
; 7E00h ┃ F R E E ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 7C00h ┃■■■■■■BOOT SECTOR■■■■■■┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ ┃
; 500h ┃ F R E E ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; 400h ┃□□□□ROM BIOS parameter area □□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇┃
; 0h ┃◇◇◇◇◇◇Int Vectors◇◇◇◇◇◇┃
; ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
;
;
; ┏━━━┓ ┏━━━┓
; ┃■■■┃ 我們使用 ┃□□□┃ 不能使用的記憶體
; ┗━━━┛ ┗━━━┛
; ┏━━━┓ ┏━━━┓
; ┃ ┃ 未使用空間 ┃◇◇◇┃ 可以覆蓋的記憶體
; ┗━━━┛ ┗━━━┛
;
; 注:KERNEL 的位置實際上是很靈活的,可以通過同時改變 LOAD.INC 中的 KRNL_ENT_PT_PHY_ADDR 和 MAKEFILE 中參數 -Ttext 的值來改變。
; 比如,如果把 KRNL_ENT_PT_PHY_ADDR 和 -Ttext 的值都改為 0x400400,則 KERNEL 就會被加載到記憶體 0x400000(4M) 處,入口在 0x400400。
;
; ------------------------------------------------------------------------
; 顯示 AL 中的數字
; ------------------------------------------------------------------------
DispAL:
push ecx
push edx
push edi
mov edi, [dwDispPos]
mov ah, 0Fh ; 0000b: 黑底 1111b: 白字
mov dl, al
shr al, 4
mov ecx, 2
.begin:
and al, 01111b
cmp al, 9
ja .1
add al, '0'
jmp .2
.1:
sub al, 0Ah
add al, 'A'
.2:
mov [gs:edi], ax
add edi, 2
mov al, dl
loop .begin
;add edi, 2
mov [dwDispPos], edi
pop edi
pop edx
pop ecx
ret
; DispAL 結束-------------------------------------------------------------
; ------------------------------------------------------------------------
; 顯示一個整形數
; ------------------------------------------------------------------------
DispInt:
mov eax, [esp + 4]
shr eax, 24
call DispAL
mov eax, [esp + 4]
shr eax, 16
call DispAL
mov eax, [esp + 4]
shr eax, 8
call DispAL
mov eax, [esp + 4]
call DispAL
mov ah, 07h ; 0000b: 黑底 0111b: 灰字
mov al, 'h'
push edi
mov edi, [dwDispPos]
mov [gs:edi], ax
add edi, 4
mov [dwDispPos], edi
pop edi
ret
; DispInt 結束------------------------------------------------------------
; ------------------------------------------------------------------------
; 顯示一個字元串
; ------------------------------------------------------------------------
DispStr:
push ebp
mov ebp, esp
push ebx
push esi
push edi
mov esi, [ebp + 8] ; pszInfo
mov edi, [dwDispPos]
mov ah, 0Fh
.1:
lodsb
test al, al
jz .2
cmp al, 0Ah ; 是ENTER嗎?
jnz .3
push eax
mov eax, edi
mov bl, 160
div bl
and eax, 0FFh
inc eax
mov bl, 160
mul bl
mov edi, eax
pop eax
jmp .1
.3:
mov [gs:edi], ax
add edi, 2
jmp .1
.2:
mov [dwDispPos], edi
pop edi
pop esi
pop ebx
pop ebp
ret
; DispStr 結束------------------------------------------------------------
; ------------------------------------------------------------------------
; 換行
; ------------------------------------------------------------------------
DispReturn:
push szReturn
call DispStr ;printf("\n");
add esp, 4
ret
; DispReturn 結束---------------------------------------------------------
; ------------------------------------------------------------------------
; 記憶體拷貝,仿 memcpy
; ------------------------------------------------------------------------
; void* MemCpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
MemCpy:
push ebp
mov ebp, esp
push esi
push edi
push ecx
mov edi, [ebp + 8] ; Destination
mov esi, [ebp + 12] ; Source
mov ecx, [ebp + 16] ; Counter
.1:
cmp ecx, 0 ; 判斷計數器
jz .2 ; 計數器為零時跳出
mov al, [ds:esi] ; ┓
inc esi ; ┃
; ┣ 逐字元移動
mov byte [es:edi], al ; ┃
inc edi ; ┛
dec ecx ; 計數器減一
jmp .1 ; 循環
.2:
mov eax, [ebp + 8] ; 返回值
pop ecx
pop edi
pop esi
mov esp, ebp
pop ebp
ret ; 函數結束,返回
; MemCpy 結束-------------------------------------------------------------
; 顯示記憶體訊息 --------------------------------------------------------------
DispMemInfo:
push esi
push edi
push ecx
push szMemChkTitle
call DispStr
add esp, 4
mov esi, MemChkBuf
mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一個ARDS(Address Range Descriptor Structure)結構
.loop: ;{
mov edx, 5 ; for(int j=0;j<5;j++) // 每次得到一個ARDS中的成員,共5個成員
mov edi, ARDStruct ; { // 依次顯示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type
.1: ;
push dword [esi] ;
call DispInt ; DispInt(MemChkBuf[j*4]); // 顯示一個成員
pop eax ;
stosd ; ARDStruct[j*4] = MemChkBuf[j*4];
add esi, 4 ;
dec edx ;
cmp edx, 0 ;
jnz .1 ; }
call DispReturn ; printf("\n");
cmp dword [dwType], 1 ; if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2
jne .2 ; {
mov eax, [dwBaseAddrLow] ;
add eax, [dwLengthLow] ;
cmp eax, [dwMemSize] ; if(BaseAddrLow + LengthLow > MemSize)
jb .2 ;
mov [dwMemSize], eax ; MemSize = BaseAddrLow + LengthLow;
.2: ; }
loop .loop ;}
;
call DispReturn ;printf("\n");
push szRAMSize ;
call DispStr ;printf("RAM size:");
add esp, 4 ;
;
push dword [dwMemSize] ;
call DispInt ;DispInt(MemSize);
add esp, 4 ;
pop ecx
pop edi
pop esi
ret
; ---------------------------------------------------------------------------
;;; ; 顯示記憶體訊息 --------------------------------------------------------------
;;; DispHDInfo:
;;; push eax
;;; cmp dword [dwNrHead], 0FFFFh
;;; je .nohd
;;; push szCylinder
;;; call DispStr ; printf("C:");
;;; add esp, 4
;;; push dword [dwNrCylinder] ; NR Cylinder
;;; call DispInt
;;; pop eax
;;; push szHead
;;; call DispStr ; printf(" H:");
;;; add esp, 4
;;; push dword [dwNrHead] ; NR Head
;;; call DispInt
;;; pop eax
;;; push szSector
;;; call DispStr ; printf(" S:");
;;; add esp, 4
;;; push dword [dwNrSector] ; NR Sector
;;; call DispInt
;;; pop eax
;;; jmp .hdinfo_finish
;;; .nohd:
;;; push szNOHD
;;; call DispStr ; printf("No hard drive. System halt.");
;;; add esp, 4
;;; jmp $ ; 沒有硬碟,死在這裡
;;; .hdinfo_finish:
;;; call DispReturn
;;; pop eax
;;; ret
;;; ; ---------------------------------------------------------------------------
; 啟動分頁機制 --------------------------------------------------------------
SetupPaging:
; 根據記憶體大小計算應初始化多少PDE以及多少頁表
xor edx, edx
mov eax, [dwMemSize]
mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一個頁表對應的記憶體大小
div ebx
mov ecx, eax ; 此時 ecx 為頁表的個數,也即 PDE 應該的個數
test edx, edx
jz .no_remainder
inc ecx ; 如果餘數不為 0 就需增加一個頁表
.no_remainder:
push ecx ; 暫存頁表個數
; 為簡化處理, 所有線性位址對應相等的物理位址. 並且不考慮記憶體空洞.
; 首先初始化頁目錄
mov ax, SelectorFlatRW
mov es, ax
mov edi, PAGE_DIR_BASE ; 此段首位址為 PAGE_DIR_BASE
xor eax, eax
mov eax, PAGE_TBL_BASE | PG_P | PG_USU | PG_RWW
.1:
stosd
add eax, 4096 ; 為了簡化, 所有頁表在記憶體中是連續的.
loop .1
; 再初始化所有頁表
pop eax ; 頁表個數
mov ebx, 1024 ; 每個頁表 1024 個 PTE
mul ebx
mov ecx, eax ; PTE個數 = 頁表個數 * 1024
mov edi, PAGE_TBL_BASE ; 此段首位址為 PAGE_TBL_BASE
xor eax, eax
mov eax, PG_P | PG_USU | PG_RWW
.2:
stosd
add eax, 4096 ; 每一頁指向 4K 的空間
loop .2
mov eax, PAGE_DIR_BASE
mov cr3, eax
mov eax, cr0
or eax, 80000000h
mov cr0, eax
jmp short .3
.3:
nop
ret
; 分頁機制啟動完畢 ----------------------------------------------------------
; InitKernel ---------------------------------------------------------------------------------
; 將 KERNEL.BIN 的內容經過整理對齊後放到新的位置
; --------------------------------------------------------------------------------------------
InitKernel: ; 遍歷每一個 Program Header,根據 Program Header 中的訊息來確定把什麼放進記憶體,放到什麼位置,以及放多少。
xor esi, esi
mov cx, word [KERNEL_FILE_PHY_ADDR + 2Ch] ; ┓ ecx <- pELFHdr->e_phnum
movzx ecx, cx ; ┛
mov esi, [KERNEL_FILE_PHY_ADDR + 1Ch] ; esi <- pELFHdr->e_phoff
add esi, KERNEL_FILE_PHY_ADDR ; esi <- OffsetOfKernel + pELFHdr->e_phoff
.Begin:
mov eax, [esi + 0]
cmp eax, 0 ; PT_NULL
jz .NoAction
push dword [esi + 010h] ; size ┓
mov eax, [esi + 04h] ; ┃
add eax, KERNEL_FILE_PHY_ADDR ; ┣ ::memcpy( (void*)(pPHdr->p_vaddr),
push eax ; src ┃ uchCode + pPHdr->p_offset,
push dword [esi + 08h] ; dst ┃ pPHdr->p_filesz;
call MemCpy ; ┃
add esp, 12 ; ┛
.NoAction:
add esi, 020h ; esi += pELFHdr->e_phentsize
dec ecx
jnz .Begin
ret
; InitKernel ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; SECTION .data1 之開始 ---------------------------------------------------------------------------------------------
[SECTION .data1]
ALIGN 32
LABEL_DATA:
; 實模式下使用這些符號
; 字元串
_szMemChkTitle: db "BaseAddrL BaseAddrH LengthLow LengthHigh Type", 0Ah, 0
_szRAMSize: db "RAM size: ", 0
;;; _szCylinder db "HD Info : C=", 0
;;; _szHead db " H=", 0
;;; _szSector db " S=", 0
;;; _szNOHD db "No hard drive. System halt.", 0
_szReturn: db 0Ah, 0
;; 變量
;;; _dwNrCylinder dd 0
;;; _dwNrHead dd 0
;;; _dwNrSector dd 0
_dwMCRNumber: dd 0 ; Memory Check Result
_dwDispPos: dd (80 * 7 + 0) * 2 ; 螢幕第 7 行, 第 0 列。
_dwMemSize: dd 0
_ARDStruct: ; Address Range Descriptor Structure
_dwBaseAddrLow: dd 0
_dwBaseAddrHigh: dd 0
_dwLengthLow: dd 0
_dwLengthHigh: dd 0
_dwType: dd 0
_MemChkBuf: times 256 db 0
;
;; 保護模式下使用這些符號
szMemChkTitle equ LOADER_PHY_ADDR + _szMemChkTitle
szRAMSize equ LOADER_PHY_ADDR + _szRAMSize
;;; szCylinder equ LOADER_PHY_ADDR + _szCylinder
;;; szHead equ LOADER_PHY_ADDR + _szHead
;;; szSector equ LOADER_PHY_ADDR + _szSector
;;; szNOHD equ LOADER_PHY_ADDR + _szNOHD
szReturn equ LOADER_PHY_ADDR + _szReturn
;;; dwNrCylinder equ LOADER_PHY_ADDR + _dwNrCylinder
;;; dwNrHead equ LOADER_PHY_ADDR + _dwNrHead
;;; dwNrSector equ LOADER_PHY_ADDR + _dwNrSector
dwDispPos equ LOADER_PHY_ADDR + _dwDispPos
dwMemSize equ LOADER_PHY_ADDR + _dwMemSize
dwMCRNumber equ LOADER_PHY_ADDR + _dwMCRNumber
ARDStruct equ LOADER_PHY_ADDR + _ARDStruct
dwBaseAddrLow equ LOADER_PHY_ADDR + _dwBaseAddrLow
dwBaseAddrHigh equ LOADER_PHY_ADDR + _dwBaseAddrHigh
dwLengthLow equ LOADER_PHY_ADDR + _dwLengthLow
dwLengthHigh equ LOADER_PHY_ADDR + _dwLengthHigh
dwType equ LOADER_PHY_ADDR + _dwType
MemChkBuf equ LOADER_PHY_ADDR + _MemChkBuf
; 堆疊就在資料段的末尾
StackSpace: times 1000h db 0
TopOfStack equ LOADER_PHY_ADDR + $ ; 堆疊頂
; SECTION .data1 之結束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
command/Makefile
# commands/Makefile
#ENTRYPOINT = 0x1000
HD = ../80m.img
ASM = nasm
DASM = objdump
CC = gcc
LD = ld
ASMFLAGS = -I ../include/ -f elf
CFLAGS = -I ../include/ -m32 -c -fno-builtin -fno-stack-protector -Wall
LDFLAGS = -Ttext 0x1000 -s -melf_i386
DASMFLAGS = -D
LIB = ../lib/orangescrt.a
BIN = echo pwd
# All Phony Targets
.PHONY : everything final clean realclean disasm all install
# Default starting position
everything : $(BIN)
install : all clean
cp ../boot/hdldr.bin ./ -v
cp ../kernel.bin ./ -v
tar vcf inst.tar kernel.bin $(BIN) hdldr.bin
dd if=inst.tar of=$(HD) seek=`echo "obase=10;ibase=16;(\`egrep -e '^ROOT_BASE' ../boot/include/load.inc | sed -e 's/.*0x//g'\`+\`egrep -e '#define[[:space:]]*INSTALL_START_SECT' ../include/sys/config.h | sed -e 's/.*0x//g'\`)*200" | bc` bs=1 count=`ls -l inst.tar | awk -F " " '{print $$5}'` conv=notrunc
all : realclean everything
final : all clean
clean :
rm -f *.o
realclean :
rm -f $(BIN) *.o
kernel.bin :
cp ../kernel.bin ./
start.o : start.asm
$(ASM) $(ASMFLAGS) -o $@ $<
echo.o: echo.c ../include/type.h ../include/stdio.h
$(CC) $(CFLAGS) -o $@ $<
echo : echo.o start.o $(LIB)
$(LD) $(LDFLAGS) -o $@ $?
pwd.o: pwd.c ../include/type.h ../include/stdio.h
$(CC) $(CFLAGS) -o $@ $<
pwd : pwd.o start.o $(LIB)
$(LD) $(LDFLAGS) -o $@ $?
Makefile
#########################
# Makefile for Orange'S #
#########################
# Entry point of Orange'S
# It must have the same value with 'KernelEntryPointPhyAddr' in load.inc!
ENTRYPOINT = 0x1000
FD = a.img
HD = 80m.img
# Programs, flags, etc.
ASM = nasm
DASM = objdump
CC = gcc
LD = ld
ASMBFLAGS = -I boot/include/
ASMKFLAGS = -I include/ -I include/sys/ -f elf
CFLAGS = -I include/ -I include/sys/ -m32 -c -fno-builtin -fno-stack-protector
LDFLAGS = -s -melf_i386 -Ttext $(ENTRYPOINT) -Map krnl.map
DASMFLAGS = -D
ARFLAGS = rcs
# This Program
ORANGESBOOT = boot/boot.bin boot/hdboot.bin boot/loader.bin boot/hdldr.bin
ORANGESKERNEL = kernel.bin
LIB = lib/orangescrt.a
OBJS = kernel/kernel.o kernel/start.o kernel/main.o\
kernel/clock.o kernel/keyboard.o kernel/tty.o kernel/console.o\
kernel/i8259.o kernel/global.o kernel/protect.o kernel/proc.o\
kernel/systask.o kernel/hd.o\
kernel/kliba.o kernel/klib.o\
lib/syslog.o\
mm/main.o mm/forkexit.o mm/exec.o\
fs/main.o fs/open.o fs/misc.o fs/read_write.o\
fs/link.o\
fs/disklog.o
LOBJS = lib/syscall.o\
lib/printf.o lib/vsprintf.o\
lib/string.o lib/misc.o\
lib/open.o lib/read.o lib/write.o lib/close.o lib/unlink.o\
lib/getpid.o lib/stat.o\
lib/lseek.o\
lib/fork.o lib/exit.o lib/wait.o lib/exec.o
DASMOUTPUT = kernel.bin.asm
# All Phony Targets
.PHONY : everything final image clean realclean disasm all buildimg
# Default starting position
nop :
@echo "why not \`make image' huh? :)"
everything : $(ORANGESBOOT) $(ORANGESKERNEL)
all : realclean everything
image : realclean everything clean buildimg app vdi
clean :
rm -f $(OBJS) $(LOBJS)
cd ./command/; make clean
realclean :
rm -f $(OBJS) $(LOBJS) $(LIB) $(ORANGESBOOT) $(ORANGESKERNEL)
cd ./command/; make realclean
disasm :
$(DASM) $(DASMFLAGS) $(ORANGESKERNEL) > $(DASMOUTPUT)
# We assume that "a.img" exists in current folder
buildimg :
dd if=boot/boot.bin of=$(FD) bs=512 count=1 conv=notrunc
dd if=boot/hdboot.bin of=$(HD) bs=1 count=446 conv=notrunc
dd if=boot/hdboot.bin of=$(HD) seek=510 skip=510 bs=1 count=2 conv=notrunc
#dd if=boot/hdboot.bin of=$(HD) seek=`echo "obase=10;ibase=16;\`egrep -e '^ROOT_BASE' boot/include/load.inc | sed -e 's/.*0x//g'\`*200" | bc` bs=1 count=446 conv=notrunc
#dd if=boot/hdboot.bin of=$(HD) seek=`echo "obase=10;ibase=16;\`egrep -e '^ROOT_BASE' boot/include/load.inc | sed -e 's/.*0x//g'\`*200+1FE" | bc` skip=510 bs=1 count=2 conv=notrunc
mkdir tmp
sleep 1
sudo mount -o loop a.img tmp
sleep 1
sudo cp -fv boot/loader.bin tmp
sudo cp -fv kernel.bin tmp
sleep 1
sudo umount tmp
sleep 1
rmdir tmp
app:
cd ./command/; make install
vdi:
rm 80m.vdi
VBoxManage convertfromraw -format VDI 80m.img 80m.vdi
VBoxManage internalcommands sethduuid 80m.vdi ccea0f68-01ea-4b9e-8160-e43ef528ba6e
debug:
rm t.img
VBoxManage clonehd -format RAW ccea0f68-01ea-4b9e-8160-e43ef528ba6e t.img
xxd -u -a -g 1 -c 16 -s 0xAD9C00 -l 20000 t.img
boot/boot.bin : boot/boot.asm boot/include/load.inc boot/include/fat12hdr.inc
$(ASM) $(ASMBFLAGS) -o $@ $<
boot/hdboot.bin : boot/hdboot.asm boot/include/load.inc boot/include/fat12hdr.inc
$(ASM) $(ASMBFLAGS) -o $@ $<
boot/loader.bin : boot/loader.asm boot/include/load.inc boot/include/fat12hdr.inc boot/include/pm.inc
$(ASM) $(ASMBFLAGS) -o $@ $<
boot/hdldr.bin : boot/hdldr.asm boot/include/load.inc boot/include/fat12hdr.inc boot/include/pm.inc
$(ASM) $(ASMBFLAGS) -o $@ $<
$(ORANGESKERNEL) : $(OBJS) $(LIB)
$(LD) $(LDFLAGS) -o $(ORANGESKERNEL) $^
$(LIB) : $(LOBJS)
$(AR) $(ARFLAGS) $@ $^
kernel/kernel.o : kernel/kernel.asm
$(ASM) $(ASMKFLAGS) -o $@ $<
lib/syscall.o : lib/syscall.asm
$(ASM) $(ASMKFLAGS) -o $@ $<
kernel/start.o: kernel/start.c
$(CC) $(CFLAGS) -o $@ $<
kernel/main.o: kernel/main.c
$(CC) $(CFLAGS) -o $@ $<
kernel/clock.o: kernel/clock.c
$(CC) $(CFLAGS) -o $@ $<
kernel/keyboard.o: kernel/keyboard.c
$(CC) $(CFLAGS) -o $@ $<
kernel/tty.o: kernel/tty.c
$(CC) $(CFLAGS) -o $@ $<
kernel/console.o: kernel/console.c
$(CC) $(CFLAGS) -o $@ $<
kernel/i8259.o: kernel/i8259.c
$(CC) $(CFLAGS) -o $@ $<
kernel/global.o: kernel/global.c
$(CC) $(CFLAGS) -o $@ $<
kernel/protect.o: kernel/protect.c
$(CC) $(CFLAGS) -o $@ $<
kernel/proc.o: kernel/proc.c
$(CC) $(CFLAGS) -o $@ $<
lib/printf.o: lib/printf.c
$(CC) $(CFLAGS) -o $@ $<
lib/vsprintf.o: lib/vsprintf.c
$(CC) $(CFLAGS) -o $@ $<
kernel/systask.o: kernel/systask.c
$(CC) $(CFLAGS) -o $@ $<
kernel/hd.o: kernel/hd.c
$(CC) $(CFLAGS) -o $@ $<
kernel/klib.o: kernel/klib.c
$(CC) $(CFLAGS) -o $@ $<
lib/misc.o: lib/misc.c
$(CC) $(CFLAGS) -o $@ $<
kernel/kliba.o : kernel/kliba.asm
$(ASM) $(ASMKFLAGS) -o $@ $<
lib/string.o : lib/string.asm
$(ASM) $(ASMKFLAGS) -o $@ $<
lib/open.o: lib/open.c
$(CC) $(CFLAGS) -o $@ $<
lib/read.o: lib/read.c
$(CC) $(CFLAGS) -o $@ $<
lib/write.o: lib/write.c
$(CC) $(CFLAGS) -o $@ $<
lib/close.o: lib/close.c
$(CC) $(CFLAGS) -o $@ $<
lib/unlink.o: lib/unlink.c
$(CC) $(CFLAGS) -o $@ $<
lib/getpid.o: lib/getpid.c
$(CC) $(CFLAGS) -o $@ $<
lib/syslog.o: lib/syslog.c
$(CC) $(CFLAGS) -o $@ $<
lib/fork.o: lib/fork.c
$(CC) $(CFLAGS) -o $@ $<
lib/exit.o: lib/exit.c
$(CC) $(CFLAGS) -o $@ $<
lib/wait.o: lib/wait.c
$(CC) $(CFLAGS) -o $@ $<
lib/exec.o: lib/exec.c
$(CC) $(CFLAGS) -o $@ $<
lib/stat.o: lib/stat.c
$(CC) $(CFLAGS) -o $@ $<
lib/lseek.o: lib/lseek.c
$(CC) $(CFLAGS) -o $@ $<
mm/main.o: mm/main.c
$(CC) $(CFLAGS) -o $@ $<
mm/forkexit.o: mm/forkexit.c
$(CC) $(CFLAGS) -o $@ $<
mm/exec.o: mm/exec.c
$(CC) $(CFLAGS) -o $@ $<
fs/main.o: fs/main.c
$(CC) $(CFLAGS) -o $@ $<
fs/open.o: fs/open.c
$(CC) $(CFLAGS) -o $@ $<
fs/read_write.o: fs/read_write.c
$(CC) $(CFLAGS) -o $@ $<
fs/link.o: fs/link.c
$(CC) $(CFLAGS) -o $@ $<
fs/disklog.o: fs/disklog.c
$(CC) $(CFLAGS) -o $@ $<
 |
執行畫面(最後成果) |
 |
執行畫面(直接從硬碟啟動,尚未從硬碟啟動) |
 |
執行畫面(必要動作,從軟碟安裝作業系統至硬碟) |
注意事項
第一次必須從軟碟開機:因為硬碟裡面沒有 loader 和 kernel(類似於安裝作業系統至硬碟)。軟碟作兩件事:
- 透過 mkfs() 將硬碟的相應分區做成 Orange'S FS
- 將 cmd.tar 解開,這時 FS 中就有 hdldr.bin 和 kernel.bin
目錄結構
.
├── 80m.img
├── 80m.vdi
├── a.img
├── boot
│ ├── boot.asm
│ ├── hdboot.asm
│ ├── hdldr.asm
│ ├── include
│ │ ├── fat12hdr.inc
│ │ ├── load.inc
│ │ └── pm.inc
│ └── loader.asm
├── command
│ ├── echo.c
│ ├── hdldr.bin
│ ├── inst.tar
│ ├── kernel.bin
│ ├── Makefile
│ ├── pwd.c
│ └── start.asm
├── fs
│ ├── disklog.c
│ ├── link.c
│ ├── main.c
│ ├── misc.c
│ ├── open.c
│ └── read_write.c
├── include
│ ├── stdio.h
│ ├── string.h
│ ├── sys
│ │ ├── config.h
│ │ ├── console.h
│ │ ├── const.h
│ │ ├── fs.h
│ │ ├── global.h
│ │ ├── hd.h
│ │ ├── keyboard.h
│ │ ├── keymap.h
│ │ ├── proc.h
│ │ ├── protect.h
│ │ ├── proto.h
│ │ ├── sconst.inc
│ │ └── tty.h
│ └── type.h
├── kernel
│ ├── clock.c
│ ├── console.c
│ ├── global.c
│ ├── hd.c
│ ├── i8259.c
│ ├── kernel.asm
│ ├── keyboard.c
│ ├── kliba.asm
│ ├── klib.c
│ ├── main.c
│ ├── proc.c
│ ├── protect.c
│ ├── start.c
│ ├── systask.c
│ └── tty.c
├── lib
│ ├── close.c
│ ├── exec.c
│ ├── exit.c
│ ├── fork.c
│ ├── getpid.c
│ ├── lseek.c
│ ├── misc.c
│ ├── open.c
│ ├── printf.c
│ ├── read.c
│ ├── stat.c
│ ├── string.asm
│ ├── syscall.asm
│ ├── syslog.c
│ ├── unlink.c
│ ├── vsprintf.c
│ ├── wait.c
│ └── write.c
├── Makefile
└── mm
├── exec.c
├── forkexit.c
└── main.c
留言
張貼留言