跳到主要內容

檔案系統-建立檔案、與其它相關函數

file descriptor


一個檔案在檔案系統中涉及五個要素:

  • 檔案內容(資料)所佔用的磁區
  • i-node
  • i-node 在 inode-map 中佔用的一位
  • 資料磁區在 sector-map 中佔用的一位或多位
  • 檔案在目錄中佔有的目錄項(direntry)

建立檔案

  • 為檔案內容(資料)分配磁區
  • 在 inode_array 中分配一個 i-node
  • 在 inode-map 中分配一位
  • 在 sector-map 中分配一位或多位
  • 在相應目錄中寫入一個目錄項(direntry)

刪除檔案

  • 釋放 inode-map 中的相應位
  • 釋放 sector-map 中的相應位
  • 刪除根目錄中的目錄項

程式碼

boot/boot.asm

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                               boot.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                     Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


;%define _BOOT_DEBUG_ ; 做 Boot Sector 時一定將此行註解掉!將此行打開後用 nasm Boot.asm -o Boot.com 做成一個.COM檔案易於測試

%ifdef _BOOT_DEBUG_
 org  0100h   ; 測試狀態, 做成 .COM 檔案, 可測試
%else
 org  07c00h   ; Boot 狀態, Bios 將把 Boot Sector 加載到 0:7C00 處並開始執行
%endif

;================================================================================================
%ifdef _BOOT_DEBUG_
BaseOfStack  equ 0100h ; 測試狀態下堆疊基位址(堆疊底, 從這個位置向低位址生長)
%else
BaseOfStack  equ 07c00h ; Boot狀態下堆疊基位址(堆疊底, 從這個位置向低位址生長)
%endif

%include "load.inc"
;================================================================================================

 jmp short LABEL_START  ; Start to boot.
 nop    ; 這個 nop 不可少

; 下面是 FAT12 磁碟的頭, 之所以包含它是因為下面用到了磁碟的一些訊息
%include "fat12hdr.inc"

LABEL_START:
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, BaseOfStack

 ; 清屏
 mov ax, 0600h  ; AH = 6,  AL = 0h
 mov bx, 0700h  ; 黑底白字(BL = 07h)
 mov cx, 0   ; 左上角: (0, 0)
 mov dx, 0184fh  ; 右下角: (80, 50)
 int 10h   ; int 10h

 mov dh, 0   ; "Booting  "
 call DispStr   ; 顯示字元串

 xor ah, ah ; ┓
 xor dl, dl ; ┣ 軟體驅動復位
 int 13h ; ┛

; 下面在 A 碟的根目錄尋找 LOADER.BIN
 mov word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
 cmp word [wRootDirSizeForLoop], 0 ; ┓
 jz LABEL_NO_LOADERBIN  ; ┣ 判斷根目錄區是不是已經讀完
 dec word [wRootDirSizeForLoop] ; ┛ 如果讀完表示沒有找到 LOADER.BIN
 mov ax, LOADER_SEG
 mov es, ax   ; es <- LOADER_SEG
 mov bx, LOADER_OFF  ; bx <- LOADER_OFF 於是, es:bx = LOADER_SEG:LOADER_OFF
 mov ax, [wSectorNo]  ; ax <- Root Directory 中的某 Sector 號
 mov cl, 1
 call ReadSector

 mov si, LoaderFileName ; ds:si -> "LOADER  BIN"
 mov di, LOADER_OFF  ; es:di -> LOADER_SEG:0100 = LOADER_SEG*10h+100
 cld
 mov dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:
 cmp dx, 0     ; ┓循環次數控制,
 jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ; ┣如果已經讀完了一個 Sector,
 dec dx     ; ┛就跳到下一個 Sector
 mov cx, 11
LABEL_CMP_FILENAME:
 cmp cx, 0
 jz LABEL_FILENAME_FOUND ; 如果比較了 11 個字元都相等, 表示找到
 dec cx
 lodsb    ; ds:si -> al
 cmp al, byte [es:di]
 jz LABEL_GO_ON
 jmp LABEL_DIFFERENT  ; 只要發現不一樣的字元就表明本 DirectoryEntry 不是
; 我們要找的 LOADER.BIN
LABEL_GO_ON:
 inc di
 jmp LABEL_CMP_FILENAME ; 繼續循環

LABEL_DIFFERENT:
 and di, 0FFE0h  ; else ┓ di &= E0 為了讓它指向本項目開頭
 add di, 20h   ;      ┃
 mov si, LoaderFileName ;      ┣ di += 20h  下一個目錄項目
 jmp LABEL_SEARCH_FOR_LOADERBIN;    ┛

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
 add word [wSectorNo], 1
 jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_NO_LOADERBIN:
 mov dh, 2   ; "No LOADER."
 call DispStr   ; 顯示字元串
%ifdef _BOOT_DEBUG_
 mov ax, 4c00h  ; ┓
 int 21h   ; ┛沒有找到 LOADER.BIN, 回到 DOS
%else
 jmp $   ; 沒有找到 LOADER.BIN, 死循環在這裡
%endif

LABEL_FILENAME_FOUND:   ; 找到 LOADER.BIN 後便來到這裡繼續
 mov ax, RootDirSectors
 and di, 0FFE0h  ; di -> 當前項目的開始
 add di, 01Ah  ; di -> 首 Sector
 mov cx, word [es:di]
 push cx   ; 保存此 Sector 在 FAT 中的序號
 add cx, ax
 add cx, DeltaSectorNo ; 這句完成時 cl 裡面變成 LOADER.BIN 的起始磁區號 (從 0 開始數的序號)
 mov ax, LOADER_SEG
 mov es, ax   ; es <- LOADER_SEG
 mov bx, LOADER_OFF  ; bx <- LOADER_OFF 於是, es:bx = LOADER_SEG:LOADER_OFF = LOADER_SEG * 10h + LOADER_OFF
 mov ax, cx   ; ax <- Sector 號

LABEL_GOON_LOADING_FILE:
 push ax   ; ┓
 push bx   ; ┃
 mov ah, 0Eh   ; ┃ 每讀一個磁區就在 "Booting  " 後面打一個點, 形成這樣的效果:
 mov al, '.'   ; ┃
 mov bl, 0Fh   ; ┃ Booting ......
 int 10h   ; ┃
 pop bx   ; ┃
 pop ax   ; ┛

 mov cl, 1
 call ReadSector
 pop ax   ; 取出此 Sector 在 FAT 中的序號
 call GetFATEntry
 cmp ax, 0FFFh
 jz LABEL_FILE_LOADED
 push ax   ; 保存 Sector 在 FAT 中的序號
 mov dx, RootDirSectors
 add ax, dx
 add ax, DeltaSectorNo
 add bx, [BPB_BytsPerSec]
 jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:

 mov dh, 1   ; "Ready."
 call DispStr   ; 顯示字元串

; *****************************************************************************************************
 jmp LOADER_SEG:LOADER_OFF ; 這一句正式跳轉到已加載到記憶體中的 LOADER.BIN 的開始處
      ; 開始執行 LOADER.BIN 的程式碼
      ; Boot Sector 的使命到此結束
; *****************************************************************************************************



;============================================================================
;變量
;----------------------------------------------------------------------------
wRootDirSizeForLoop dw RootDirSectors ; Root Directory 占用的磁區數, 在循環中會遞減至零.
wSectorNo  dw 0  ; 要讀取的磁區號
bOdd   db 0  ; 奇數還是偶數

;============================================================================
;字元串
;----------------------------------------------------------------------------
LoaderFileName  db "LOADER  BIN", 0 ; LOADER.BIN 之檔案名
; 為簡化程式碼, 下面每個字元串的長度均為 MessageLength
MessageLength  equ 9
BootMessage:  db "Booting  "; 9字元, 不夠則用空格補齊. 序號 0
Message1  db "Ready.   "; 9字元, 不夠則用空格補齊. 序號 1
Message2  db "No LOADER"; 9字元, 不夠則用空格補齊. 序號 2
;============================================================================


;----------------------------------------------------------------------------
; 函數名: DispStr
;----------------------------------------------------------------------------
; 作用:
; 顯示一個字元串, 函數開始時 dh 中應該是字元串序號(0-based)
DispStr:
 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, 01301h  ; AH = 13,  AL = 01h
 mov bx, 0007h  ; 頁號為0(BH = 0) 黑底白字(BL = 07h)
 mov dl, 0
 int 10h   ; int 10h
 ret


;----------------------------------------------------------------------------
; 函數名: ReadSector
;----------------------------------------------------------------------------
; 作用:
; 從第 ax 個 Sector 開始, 將 cl 個 Sector 讀入 es:bx 中
ReadSector:
 ; -----------------------------------------------------------------------
 ; 怎樣由磁區號求磁區在磁碟中的位置 (磁區號 -> 柱面號, 起始磁區, 磁頭號)
 ; -----------------------------------------------------------------------
 ; 設磁區號為 x
 ;                           ┌ 柱面號 = y >> 1
 ;       x           ┌ 商 y ┤
 ; -------------- => ┤      └ 磁頭號 = y & 1
 ;  每磁道磁區數     │
 ;                   └ 余 z => 起始磁區號 = z + 1
 push bp
 mov bp, sp
 sub esp, 2   ; 辟出兩個字元的堆疊區域保存要讀的磁區數: byte [bp-2]

 mov byte [bp-2], cl
 push bx   ; 保存 bx
 mov bl, [BPB_SecPerTrk] ; bl: 除數
 div bl   ; y 在 al 中, z 在 ah 中
 inc ah   ; z ++
 mov cl, ah   ; cl <- 起始磁區號
 mov dh, al   ; dh <- y
 shr al, 1   ; y >> 1 (其實是 y/BPB_NumHeads, 這裡BPB_NumHeads=2)
 mov ch, al   ; ch <- 柱面號
 and dh, 1   ; dh & 1 = 磁頭號
 pop bx   ; 恢復 bx
 ; 至此, "柱面號, 起始磁區, 磁頭號" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
 mov dl, [BS_DrvNum]  ; 驅動器號 (0 表示 A 碟)
.GoOnReading:
 mov ah, 2   ; 讀
 mov al, byte [bp-2]  ; 讀 al 個磁區
 int 13h
 jc .GoOnReading  ; 如果讀取錯誤 CF 會被置為 1, 這時就不停地讀, 直到正確為止

 add esp, 2
 pop bp

 ret

;----------------------------------------------------------------------------
; 函數名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
; 找到序號為 ax 的 Sector 在 FAT 中的項目, 結果放在 ax 中
; 需要注意的是, 中間需要讀 FAT 的磁區到 es:bx 處, 所以函數一開始保存了 es 和 bx
GetFATEntry:
 push es
 push bx
 push ax
 mov ax, LOADER_SEG ; ┓
 sub ax, 0100h  ; ┣ 在 LOADER_SEG 後面留出 4K 空間用於存放 FAT
 mov es, ax   ; ┛
 pop ax
 mov byte [bOdd], 0
 mov bx, 3
 mul bx   ; dx:ax = ax * 3
 mov bx, 2
 div bx   ; dx:ax / 2  ==>  ax <- 商, dx <- 餘數
 cmp dx, 0
 jz LABEL_EVEN
 mov byte [bOdd], 1
LABEL_EVEN:;偶數
 xor dx, dx   ; 現在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面來計算 FATEntry 在哪個磁區中(FAT占用不止一個磁區)
 mov bx, [BPB_BytsPerSec]
 div bx   ; dx:ax / BPB_BytsPerSec  ==> ax <- 商   (FATEntry 所在的磁區相對於 FAT 來說的磁區號)
     ;    dx <- 餘數 (FATEntry 在磁區內的偏移)。
 push dx
 mov bx, 0   ; bx <- 0 於是, es:bx = (LOADER_SEG - 100):00 = (LOADER_SEG - 100) * 10h
 add ax, SectorNoOfFAT1 ; 此句執行之後的 ax 就是 FATEntry 所在的磁區號
 mov cl, 2
 call ReadSector  ; 讀取 FATEntry 所在的磁區, 一次讀兩個, 避免在邊界發生錯誤, 因為一個 FATEntry 可能跨越兩個磁區
 pop dx
 add bx, dx
 mov ax, [es:bx]
 cmp byte [bOdd], 1
 jnz LABEL_EVEN_2
 shr ax, 4
LABEL_EVEN_2:
 and ax, 0FFFh

LABEL_GET_FAT_ENRY_OK:

 pop bx
 pop es
 ret
;----------------------------------------------------------------------------

times  510-($-$$) db 0 ; 填充剩下的空間,使生成的二進制程式碼恰好為512字元
dw  0xaa55    ; 結束標誌

boot/loader.asm

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                               loader.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                     Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


org  0100h

 jmp LABEL_START  ; Start

; 下面是 FAT12 磁碟的頭, 之所以包含它是因為下面用到了磁碟的一些訊息
%include "fat12hdr.inc"
%include "load.inc"
%include "pm.inc"


; 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


LABEL_START:   ; <--- 從這裡開始 *************
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, BaseOfStack

 mov dh, 0   ; "Loading  "
 call DispStrRealMode  ; 顯示字元串

 ; 得到記憶體數
 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:

 ; 下面在 A 碟的根目錄尋找 KERNEL.BIN
 mov word [wSectorNo], SectorNoOfRootDirectory
 xor ah, ah ; ┓
 xor dl, dl ; ┣ 軟體驅動復位
 int 13h ; ┛
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
 cmp word [wRootDirSizeForLoop], 0 ; ┓
 jz LABEL_NO_KERNELBIN  ; ┣ 判斷根目錄區是不是已經讀完, 如果讀完表示沒有找到 KERNEL.BIN
 dec word [wRootDirSizeForLoop] ; ┛
 mov ax, KERNEL_FILE_SEG
 mov es, ax   ; es <- KERNEL_FILE_SEG
 mov bx, KERNEL_FILE_OFF ; bx <- KERNEL_FILE_OFF 於是, es:bx = KERNEL_FILE_SEG:KERNEL_FILE_OFF = KERNEL_FILE_SEG * 10h + KERNEL_FILE_OFF
 mov ax, [wSectorNo]  ; ax <- Root Directory 中的某 Sector 號
 mov cl, 1
 call ReadSector

 mov si, KernelFileName ; ds:si -> "KERNEL  BIN"
 mov di, KERNEL_FILE_OFF ; es:di -> KERNEL_FILE_SEG:???? = KERNEL_FILE_SEG*10h+????
 cld
 mov dx, 10h
LABEL_SEARCH_FOR_KERNELBIN:
 cmp dx, 0     ; ┓
 jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ; ┣ 循環次數控制, 如果已經讀完了一個 Sector, 就跳到下一個 Sector
 dec dx     ; ┛
 mov cx, 11
LABEL_CMP_FILENAME:
 cmp cx, 0   ; ┓
 jz LABEL_FILENAME_FOUND ; ┣ 循環次數控制, 如果比較了 11 個字元都相等, 表示找到
 dec cx   ; ┛
 lodsb    ; ds:si -> al
 cmp al, byte [es:di] ; if al == es:di
 jz LABEL_GO_ON
 jmp LABEL_DIFFERENT
LABEL_GO_ON:
 inc di
 jmp LABEL_CMP_FILENAME ; 繼續循環

LABEL_DIFFERENT:
 and di, 0FFE0h  ; else┓ 這時di的值不知道是什麼, di &= e0 為了讓它是 20h 的倍數
 add di, 20h   ;     ┃
 mov si, KernelFileName ;     ┣ di += 20h  下一個目錄項目
 jmp LABEL_SEARCH_FOR_KERNELBIN;   ┛

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
 add word [wSectorNo], 1
 jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_NO_KERNELBIN:
 mov dh, 3   ; "No KERNEL."
 call DispStrRealMode  ; 顯示字元串
 jmp $   ; 沒有找到 KERNEL.BIN, 死循環在這裡

LABEL_FILENAME_FOUND:   ; 找到 KERNEL.BIN 後便來到這裡繼續
 mov ax, RootDirSectors
 and di, 0FFF0h  ; di -> 當前項目的開始

 push eax
 mov eax, [es : di + 01Ch]  ; ┓
 mov dword [dwKernelSize], eax ; ┛保存 KERNEL.BIN 檔案大小
 cmp eax, KERNEL_VALID_SPACE
 ja .1
 pop eax
 jmp .2
.1:
 mov dh, 4   ; "Too Large"
 call DispStrRealMode  ; 顯示字元串
 jmp $   ; KERNEL.BIN 太大,死循環在這裡
.2:
 add di, 01Ah  ; di -> 首 Sector
 mov cx, word [es:di]
 push cx   ; 保存此 Sector 在 FAT 中的序號
 add cx, ax
 add cx, DeltaSectorNo ; 這時 cl 裡面是 LOADER.BIN 的起始磁區號 (從 0 開始數的序號)
 mov ax, KERNEL_FILE_SEG
 mov es, ax   ; es <- KERNEL_FILE_SEG
 mov bx, KERNEL_FILE_OFF ; bx <- KERNEL_FILE_OFF 於是, es:bx = KERNEL_FILE_SEG:KERNEL_FILE_OFF = KERNEL_FILE_SEG * 10h + KERNEL_FILE_OFF
 mov ax, cx   ; ax <- Sector 號

LABEL_GOON_LOADING_FILE:
 push ax   ; `.
 push bx   ;  |
 mov ah, 0Eh   ;  | 每讀一個磁區就在 "Loading  " 後面
 mov al, '.'   ;  | 打一個點, 形成這樣的效果:
 mov bl, 0Fh   ;  | Loading ......
 int 10h   ;  |
 pop bx   ;  |
 pop ax   ; /

 mov cl, 1
 call ReadSector
 pop ax   ; 取出此 Sector 在 FAT 中的序號
 call GetFATEntry
 cmp ax, 0FFFh
 jz LABEL_FILE_LOADED
 push ax   ; 保存 Sector 在 FAT 中的序號
 mov dx, RootDirSectors
 add ax, dx
 add ax, DeltaSectorNo
 add bx, [BPB_BytsPerSec]
 jc .1   ; 如果 bx 重新變成 0,說明內核大於 64KB
 jmp .2
.1:
 push ax   ; es += 0x1000  ← es 指向下一個段
 mov ax, es
 add ax, 1000h
 mov es, ax
 pop ax
.2:
 jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:

 call KillMotor  ; 關閉軟體驅動馬達

;;;  ;; 取硬碟訊息
;;;  xor eax, eax
;;;  mov ah, 08h  ; Code for drive parameters
;;;  mov dx, 80h  ; hard drive
;;;  int 0x13
;;;  jb .hderr  ; No such drive?
;;;  ;; cylinder number
;;;  xor ax, ax  ; ax <- 0
;;;  mov ah, cl  ; ax <- cl
;;;  shr ah, 6
;;;  and ah, 3     ; cl bits 7-6: high two bits of maximum cylinder number
;;;  mov al, ch  ; CH = low eight bits of maximum cylinder number
;;;  ;; sector number
;;;  and cl, 3Fh  ; cl bits 5-0: max sector number (1-origin)
;;;  ;; head number
;;;  inc dh  ; dh = 1 + max head number (0-origin)
;;;  mov [_dwNrHead], dh
;;;  mov [_dwNrSector], cl
;;;  mov [_dwNrCylinder], ax
;;;  jmp .hdok
;;; .hderr:
;;;  mov dword [_dwNrHead], 0FFFFh
;;; .hdok:
 ;; 將硬碟引導磁區內容讀入記憶體 0500h 處
 xor     ax, ax
 mov     es, ax
 mov     ax, 0201h       ; AH = 02
                         ; AL = number of sectors to read (must be nonzero)
 mov     cx, 1           ; CH = low eight bits of cylinder number
                         ; CL = sector number 1-63 (bits 0-5)
                         ;      high two bits of cylinder (bits 6-7, hard disk only)
 mov     dx, 80h         ; DH = head number
                         ; DL = drive number (bit 7 set for hard disk)
 mov     bx, 500h        ; ES:BX -> data buffer
 int     13h
 ;; 硬碟操作完畢

 mov dh, 2   ; "Ready."
 call DispStrRealMode  ; 顯示字元串


; 下面準備跳入保護模式 -------------------------------------------

; 加載 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)


;============================================================================
;變量
;----------------------------------------------------------------------------
wRootDirSizeForLoop dw RootDirSectors ; Root Directory 占用的磁區數
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 "Ready.   "
Message3  db "No KERNEL"
Message4  db "Too Large"
;============================================================================

;----------------------------------------------------------------------------
; 函數名: DispStrRealMode
;----------------------------------------------------------------------------
; 運行環境:
; 實模式(保護模式下顯示字元串由函數 DispStr 完成)
; 作用:
; 顯示一個字元串, 函數開始時 dh 中應該是字元串序號(0-based)
DispStrRealMode:
 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, 01301h  ; AH = 13,  AL = 01h
 mov bx, 0007h  ; 頁號為0(BH = 0) 黑底白字(BL = 07h)
 mov dl, 0
 add dh, 3   ; 從第 3 行往下顯示
 int 10h   ; int 10h
 ret
;----------------------------------------------------------------------------
; 函數名: ReadSector
;----------------------------------------------------------------------------
; 作用:
; 從序號(Directory Entry 中的 Sector 號)為 ax 的的 Sector 開始, 將 cl 個 Sector 讀入 es:bx 中
ReadSector:
 ; -----------------------------------------------------------------------
 ; 怎樣由磁區號求磁區在磁碟中的位置 (磁區號 -> 柱面號, 起始磁區, 磁頭號)
 ; -----------------------------------------------------------------------
 ; 設磁區號為 x
 ;                           ┌ 柱面號 = y >> 1
 ;       x           ┌ 商 y ┤
 ; -------------- => ┤      └ 磁頭號 = y & 1
 ;  每磁道磁區數     │
 ;                   └ 余 z => 起始磁區號 = z + 1
 push bp
 mov bp, sp
 sub esp, 2   ; 辟出兩個字元的堆疊區域保存要讀的磁區數: byte [bp-2]

 mov byte [bp-2], cl
 push bx   ; 保存 bx
 mov bl, [BPB_SecPerTrk] ; bl: 除數
 div bl   ; y 在 al 中, z 在 ah 中
 inc ah   ; z ++
 mov cl, ah   ; cl <- 起始磁區號
 mov dh, al   ; dh <- y
 shr al, 1   ; y >> 1 (其實是 y/BPB_NumHeads, 這裡BPB_NumHeads=2)
 mov ch, al   ; ch <- 柱面號
 and dh, 1   ; dh & 1 = 磁頭號
 pop bx   ; 恢復 bx
 ; 至此, "柱面號, 起始磁區, 磁頭號" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
 mov dl, [BS_DrvNum]  ; 驅動器號 (0 表示 A 碟)
.GoOnReading:
 mov ah, 2   ; 讀
 mov al, byte [bp-2]  ; 讀 al 個磁區
 int 13h
 jc .GoOnReading  ; 如果讀取錯誤 CF 會被置為 1, 這時就不停地讀, 直到正確為止

 add esp, 2
 pop bp

 ret

;----------------------------------------------------------------------------
; 函數名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
; 找到序號為 ax 的 Sector 在 FAT 中的項目, 結果放在 ax 中
; 需要注意的是, 中間需要讀 FAT 的磁區到 es:bx 處, 所以函數一開始保存了 es 和 bx
GetFATEntry:
 push es
 push bx
 push ax
 mov ax, KERNEL_FILE_SEG ; ┓
 sub ax, 0100h  ; ┣ 在 KERNEL_FILE_SEG 後面留出 4K 空間用於存放 FAT
 mov es, ax   ; ┛
 pop ax
 mov byte [bOdd], 0
 mov bx, 3
 mul bx   ; dx:ax = ax * 3
 mov bx, 2
 div bx   ; dx:ax / 2  ==>  ax <- 商, dx <- 餘數
 cmp dx, 0
 jz LABEL_EVEN
 mov byte [bOdd], 1
LABEL_EVEN:;偶數
 xor dx, dx   ; 現在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面來計算 FATEntry 在哪個磁區中(FAT占用不止一個磁區)
 mov bx, [BPB_BytsPerSec]
 div bx   ; dx:ax / BPB_BytsPerSec  ==> ax <- 商   (FATEntry 所在的磁區相對於 FAT 來說的磁區號)
     ;    dx <- 餘數 (FATEntry 在磁區內的偏移)。
 push dx
 mov bx, 0   ; bx <- 0 於是, es:bx = (KERNEL_FILE_SEG - 100):00 = (KERNEL_FILE_SEG - 100) * 10h
 add ax, SectorNoOfFAT1 ; 此句執行之後的 ax 就是 FATEntry 所在的磁區號
 mov cl, 2
 call ReadSector  ; 讀取 FATEntry 所在的磁區, 一次讀兩個, 避免在邊界發生錯誤, 因為一個 FATEntry 可能跨越兩個磁區
 pop dx
 add bx, dx
 mov ax, [es:bx]
 cmp byte [bOdd], 1
 jnz LABEL_EVEN_2
 shr ax, 4
LABEL_EVEN_2:
 and ax, 0FFFh

LABEL_GET_FAT_ENRY_OK:

 pop bx
 pop es
 ret
;----------------------------------------------------------------------------


;----------------------------------------------------------------------------
; 函數名: KillMotor
;----------------------------------------------------------------------------
; 作用:
; 關閉軟體驅動馬達
KillMotor:
 push dx
 mov dx, 03F2h
 mov al, 0
 out dx, al
 pop dx
 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
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;       70000h ┃■■■■■■■KERNEL.BIN■■■■■■┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃ 7C00h~7DFFh : BOOT SECTOR, overwritten by the kernel
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;        1000h ┃■■■■■■■■KERNEL■■■■■■■┃ 1000h ← KERNEL 入口 (KRNL_ENT_PT_PHY_ADDR)
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃                                    ┃
 ;         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 ; 是回车吗?
 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 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

boot/include/load.inc

BaseOfLoader  equ  09000h ; LOADER.BIN 被加載到的位置 ----  段地址
OffsetOfLoader  equ   0100h ; LOADER.BIN 被加載到的位置 ---- 偏移地址

BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ; LOADER.BIN 被加載到的位置 ---- 物理地址 (= BaseOfLoader * 10h)


BaseOfKernelFile equ  08000h ; KERNEL.BIN 被加載到的位置 ----  段地址
OffsetOfKernelFile equ      0h ; KERNEL.BIN 被加載到的位置 ---- 偏移地址

BaseOfKernelFilePhyAddr equ BaseOfKernelFile * 10h
KernelEntryPointPhyAddr equ 030400h ; 注意:1、必須與 MAKEFILE 中參數 -Ttext 的值相等!!
     ;       2、這是個地址而非僅僅是個偏移,如果 -Ttext 的值為 0x400400,則它的值也應該是 0x400400。

PageDirBase  equ 200000h ; 頁目錄開始地址:  2M
PageTblBase  equ 201000h ; 頁表開始地址:   2M + 4K

fs/disklog.c

/*************************************************************************//**
 *****************************************************************************
 * @file   disklog.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Thu Nov 20 16:22:45 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "config.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
#include "hd.h"
#include "fs.h"

static char _buf[SECTOR_SIZE];

/*****************************************************************************
 *                                do_disklog
 *****************************************************************************/
/**
 * Perform syslog() system call .
 *
 * @return
 *****************************************************************************/
PUBLIC int do_disklog()
{
 char buf[STR_DEFAULT_LEN];

 /* get parameters from the message */
 int str_len = fs_msg.CNT; /* length of filename */
 int src = fs_msg.source; /* caller proc nr. */
 assert(str_len < STR_DEFAULT_LEN);
 phys_copy((void*)va2la(TASK_FS, buf),    /* to   */
    (void*)va2la(src, fs_msg.BUF), /* from */
    str_len);
 buf[str_len] = 0; /* terminate the string */

 return disklog(buf);
}

/*****************************************************************************
 *                                disklog
 *****************************************************************************/
/**
 * <Ring 1> This routine handles the DEV_LOG message.
 *
 * @param p  Ptr to the MESSAGE.
 *****************************************************************************/
PUBLIC int disklog(char * logstr)
{
 int device = root_inode->i_dev;
 struct super_block * sb = get_super_block(device);
 int nr_log_blk0_nr = sb->nr_sects - NR_SECTS_FOR_LOG; /* 0x9D41-0x800=0x9541 */

 static int pos = 0;
 if (!pos) { /* first time invoking this routine */

#ifdef SET_LOG_SECT_SMAP_AT_STARTUP
  /*
   * set sector-map so that other files cannot use the log sectors
   */

  int bits_per_sect = SECTOR_SIZE * 8; /* 4096 */

  int smap_blk0_nr = 1 + 1 + sb->nr_imap_sects; /* 3 */
  int sect_nr  = smap_blk0_nr + nr_log_blk0_nr / bits_per_sect; /* 3+9=12 */
  int byte_off = (nr_log_blk0_nr % bits_per_sect) / 8; /* 168 */
  int bit_off  = (nr_log_blk0_nr % bits_per_sect) % 8; /* 1 */
  int sect_cnt = NR_SECTS_FOR_LOG / bits_per_sect + 2; /* 1 */
  int bits_left= NR_SECTS_FOR_LOG; /* 2048 */

  int i;
  for (i = 0; i < sect_cnt; i++) {
   RD_SECT(device, sect_nr + i); /* RD_SECT(?, 12) */

   for (; byte_off < SECTOR_SIZE && bits_left > 0; byte_off++) {
    for (; bit_off < 8; bit_off++) { /* repeat till enough bits are set */
     assert(((fsbuf[byte_off] >> bit_off) & 1) == 0);
     fsbuf[byte_off] |= (1 << bit_off);
     if (--bits_left  == 0)
      break;
    }
    bit_off = 0;
   }
   byte_off = 0;
   bit_off = 0;

   WR_SECT(device, sect_nr + i);

   if (bits_left == 0)
    break;
  }
  assert(bits_left == 0);
#endif /* SET_LOG_SECT_SMAP_AT_STARTUP */

  pos = 0x40;

#ifdef MEMSET_LOG_SECTS
  /* write padding stuff to log sectors */
  int chunk = min(MAX_IO_BYTES, FSBUF_SIZE >> SECTOR_SIZE_SHIFT);
  assert(chunk == 256);
  int sects_left = NR_SECTS_FOR_LOG;
  for (i = nr_log_blk0_nr;
       i < nr_log_blk0_nr + NR_SECTS_FOR_LOG;
       i += chunk) {
   memset(fsbuf, 0x20, chunk*SECTOR_SIZE);
   rw_sector(DEV_WRITE,
      device,
      i * SECTOR_SIZE,
      chunk * SECTOR_SIZE,
      TASK_FS,
      fsbuf);
   sects_left -= chunk;
  }
  if (sects_left != 0)
   panic("sects_left should be 0, current: %d.", sects_left);
#endif /* MEMSET_LOG_SECTS */
 }

 char * p = logstr;
 int bytes_left = strlen(logstr);

 int sect_nr = nr_log_blk0_nr + (pos >> SECTOR_SIZE_SHIFT);

 while (bytes_left) {
  RD_SECT(device, sect_nr);

  int off = pos % SECTOR_SIZE;
  int bytes = min(bytes_left, SECTOR_SIZE - off);

  memcpy(&fsbuf[off], p, bytes);
  off += bytes;
  bytes_left -= bytes;

  WR_SECT(device, sect_nr);
  sect_nr++;
  pos += bytes;
  p += bytes;
 }

 /* write `pos' into the log file header */
 RD_SECT(device, nr_log_blk0_nr);

 sprintf((char*)fsbuf, "%8d\n", pos);
 memset(fsbuf+9, ' ', 22);
 fsbuf[31] = '\n';

 memset(fsbuf+32, ' ', 31);
 fsbuf[63] = '\n';

 WR_SECT(device, nr_log_blk0_nr);
 memset(fsbuf+64, fsbuf[32+19], 512-64);
 WR_SECT(device, nr_log_blk0_nr + NR_SECTS_FOR_LOG - 1);

 return pos;
}


/*****************************************************************************
 *                                dump_fd_graph
 *****************************************************************************/
/**
 * Output a dot graph.
 *
 *****************************************************************************/
PUBLIC void dump_fd_graph(const char * fmt, ...)
{
 int i;
 char title[STR_DEFAULT_LEN];

 va_list arg = (va_list)((char*)(&fmt) + 4); /**
           * 4: size of `fmt' in
           *    the stack
           */
 i = vsprintf(title, fmt, arg);
 assert(strlen(title) == i);

 static int graph_idx = 0;
 char filename[MAX_FILENAME_LEN+1];

 char * proc_flags[32];
 proc_flags[0x02] = "SENDING";
 proc_flags[0x04] = "RECEIVING";
 proc_flags[0x08] = "WAITING";
 proc_flags[0x10] = "FREE_SLOT";

 struct proc_fdesc_map {
  int pid; /* PID */
  int filp; /* idx of proc_table[pid].filp[] */
  int desc; /* idx of f_desc_table[] */
 } pfm[256];
 int pfm_idx = 0;

 struct fdesc_inode_map {
  int desc; /* idx of f_desc_table[] */
  int inode; /* idx of inode_table[] */
 } fim[256];
 int fim_idx = 0;

 /* head */
 SYSLOG("digraph filedesc%02d {\n", graph_idx++);
 SYSLOG("\tgraph [\n");
 SYSLOG("  rankdir = \"LR\"\n");
 SYSLOG(" ];\n");
 SYSLOG(" node [\n");
 SYSLOG("  fontsize = \"16\"\n");
 SYSLOG("  shape = \"ellipse\"\n");
 SYSLOG(" ];\n");
 SYSLOG(" edge [\n");
 SYSLOG(" ];\n");

 int k;
 struct proc* p_proc = proc_table;
 SYSLOG("\n\tsubgraph cluster_0 {\n");
 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p_proc++) {
  /* skip procs which open no files */
  for (k = 0; k < NR_FILES; k++) {
   if (p_proc->filp[k] != 0)
    break;
  }
  if (k == NR_FILES)
   continue;

  SYSLOG("\t\t\"proc%d\" [\n", i);
  SYSLOG("\t\t\tlabel = \"<f0>%s (%d) "
         "|<f1> p_flags:%d(%s)",
         p_proc->name,
         i,
         p_proc->p_flags,
         proc_flags[p_proc->p_flags]);
  int fnr = 3;
  for (k = 0; k < NR_FILES; k++) {
   if (p_proc->filp[k] == 0)
    continue;

   int fdesc_tbl_idx = p_proc->filp[k] - f_desc_table;
   SYSLOG("\t|<f%d> filp[%d]: %d",
          fnr,
          k,
          fdesc_tbl_idx);
   pfm[pfm_idx].pid = i;
   pfm[pfm_idx].filp = fnr;
   pfm[pfm_idx].desc = fdesc_tbl_idx;
   fnr++;
   pfm_idx++;
  }

  SYSLOG("\t\"\n");
  SYSLOG("\t\t\tshape = \"record\"\n");
  SYSLOG("\t\t];\n");
 }
 SYSLOG("\t\tlabel = \"procs\";\n");
 SYSLOG("\t}\n");

 SYSLOG("\n\tsubgraph cluster_1 {\n");
 for (i = 0; i < NR_FILE_DESC; i++) {
  if (f_desc_table[i].fd_inode == 0)
   continue;

  int inode_tbl_idx = f_desc_table[i].fd_inode - inode_table;
  SYSLOG("\t\t\"filedesc%d\" [\n", i);
  SYSLOG("\t\t\tlabel = \"<f0>filedesc %d"
         "|<f1> fd_mode:%d"
         "|<f2> fd_pos:%d"
         "|<f4> fd_inode:%d",
         i,
         f_desc_table[i].fd_mode,
         f_desc_table[i].fd_pos,
         inode_tbl_idx);
  fim[fim_idx].desc = i;
  fim[fim_idx].inode = inode_tbl_idx;
  fim_idx++;

  SYSLOG("\t\"\n");
  SYSLOG("\t\t\tshape = \"record\"\n");
  SYSLOG("\t\t];\n");
 }
 SYSLOG("\t\tlabel = \"filedescs\";\n");
 SYSLOG("\t}\n");

 SYSLOG("\n\tsubgraph cluster_2 {\n");
 for (i = 0; i < NR_INODE; i++) {
  if (inode_table[i].i_cnt == 0)
   continue;

  SYSLOG("\t\t\"inode%d\" [\n", i);
  SYSLOG("\t\t\tlabel = \"<f0>inode %d"
         "|<f1> i_mode:0x%x"
         "|<f2> i_size:0x%x"
         "|<f3> i_start_sect:0x%x"
         "|<f4> i_nr_sects:0x%x"
         "|<f5> i_dev:0x%x"
         "|<f6> i_cnt:%d"
         "|<f7> i_num:%d",
         inode_table[i].i_num,
         inode_table[i].i_mode,
         inode_table[i].i_size,
         inode_table[i].i_start_sect,
         inode_table[i].i_nr_sects,
         inode_table[i].i_dev,
         inode_table[i].i_cnt,
         inode_table[i].i_num);
  assert(filename != 0);

  SYSLOG("\t\"\n");
  SYSLOG("\t\t\tshape = \"record\"\n");
  SYSLOG("\t\t];\n");
 }
 SYSLOG("\t\tlabel = \"inodes\";\n");
 SYSLOG("\t}\n");

 SYSLOG("\n\tsubgraph cluster_3 {\n");
 SYSLOG("\n\t\tstyle=filled;\n");
 SYSLOG("\n\t\tcolor=lightgrey;\n");
 int smap_flag = 0;
 int bit_start = 0;
 /* i:     sector index */
 int j; /* byte index */
 /* k:     bit index */
 struct super_block * sb = get_super_block(root_inode->i_dev);
 int smap_blk0_nr = 1 + 1 + sb->nr_imap_sects;
 for (i = 0; i < sb->nr_smap_sects; i++) { /* smap_blk0_nr + i : current sect nr. */
  RD_SECT(root_inode->i_dev, smap_blk0_nr + i);
  memcpy(_buf, fsbuf, SECTOR_SIZE);
  for (j = 0; j < SECTOR_SIZE; j++) {
   for (k = 0; k < 8; k++) {
    if (!smap_flag) {
     if ((_buf[j] >> k ) & 1) {
      smap_flag = 1;
      bit_start = (i * SECTOR_SIZE + j) * 8 + k;
     }
     else {
      continue;
     }
    }
    else {
     if ((_buf[j] >> k ) & 1) {
      continue;
     }
     else {
      smap_flag = 0;
      int bit_end = (i * SECTOR_SIZE + j) * 8 + k - 1;
      SYSLOG("\t\t\"sector %xh\" [\n", bit_start);
      SYSLOG("\t\t\tlabel = \"<f0>sect %xh-%xh",
             bit_start,
             bit_end);
      SYSLOG("\t\"\n");
      SYSLOG("\t\t\tshape = \"record\"\n");
      SYSLOG("\t\t];\n");
     }
    }
   }
  }
 }
 SYSLOG("\t\tlabel = \"sector map (dev size: %xh)\";\n", sb->nr_sects);
 SYSLOG("\t}\n");

 SYSLOG("\n\tsubgraph cluster_4 {\n");
 SYSLOG("\n\t\tstyle=filled;\n");
 SYSLOG("\n\t\tcolor=lightgrey;\n");
 SYSLOG("\t\t\"imap\" [\n");
 SYSLOG("\t\t\tlabel = \"<f0>bits");
 /* i:     sector index */
 /* j:     byte index */
 /* k:     bit index */
 int imap_blk0_nr = 1 + 1;
 for (i = 0; i < sb->nr_imap_sects; i++) { /* smap_blk0_nr + i : current sect nr. */
  RD_SECT(root_inode->i_dev, imap_blk0_nr + i);
  memcpy(_buf, fsbuf, SECTOR_SIZE);
  for (j = 0; j < SECTOR_SIZE; j++) {
   for (k = 0; k < 8; k++) {
    if ((_buf[j] >> k ) & 1) {
     int bit_nr = (i * SECTOR_SIZE + j) * 8 + k;
     SYSLOG("| %xh ", bit_nr);
    }
   }
  }
 }
 SYSLOG("\t\"\n");
 SYSLOG("\t\t\tshape = \"record\"\n");
 SYSLOG("\t\t];\n");
 SYSLOG("\t\tlabel = \"inode map\";\n");
 SYSLOG("\t}\n");

 SYSLOG("\n\tsubgraph cluster_5 {\n");
 SYSLOG("\n\t\tstyle=filled;\n");
 SYSLOG("\n\t\tcolor=lightgrey;\n");
 sb = get_super_block(root_inode->i_dev);
 int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects;
 RD_SECT(root_inode->i_dev, blk_nr);
 memcpy(_buf, fsbuf, SECTOR_SIZE);

 char * p = _buf;
 for (i = 0; i < SECTOR_SIZE / sizeof(struct inode); i++,p+=INODE_SIZE) {
  struct inode * pinode = (struct inode*)p;
  if (pinode->i_start_sect == 0)
   continue;
  int start_sect;
  int end_sect;
  if (pinode->i_mode != I_CHAR_SPECIAL) {
   if (pinode->i_start_sect < sb->n_1st_sect) {
    panic("should not happen: %x < %x.",
          pinode->i_start_sect,
          sb->n_1st_sect);
   }
   start_sect =  pinode->i_start_sect - sb->n_1st_sect + 1;
   end_sect = start_sect + pinode->i_nr_sects - 1;
   SYSLOG("\t\t\"inodearray%d\" [\n", i+1);
   SYSLOG("\t\t\tlabel = \"<f0> %d"
          "|<f2> i_size:0x%x"
          "|<f3> sect: %xh-%xh",
          i+1,
          pinode->i_size,
          start_sect,
          end_sect);

   SYSLOG("\t\"\n");
   SYSLOG("\t\t\tshape = \"record\"\n");
   SYSLOG("\t\t];\n");
  }
  else {
   start_sect = MAJOR(pinode->i_start_sect);
   end_sect = MINOR(pinode->i_start_sect);
   SYSLOG("\t\t\"inodearray%d\" [\n", i+1);
   SYSLOG("\t\t\tlabel = \"<f0> %d"
          "|<f2> i_size:0x%x"
          "|<f3> dev nr: (%xh,%xh)",
          i+1,
          pinode->i_size,
          start_sect,
          end_sect);

   SYSLOG("\t\"\n");
   SYSLOG("\t\t\tshape = \"record\"\n");
   SYSLOG("\t\t];\n");
  }
 }
 SYSLOG("\t\tlabel = \"inode array\";\n");
 SYSLOG("\t}\n");

 SYSLOG("\n\tsubgraph cluster_6 {\n");
 SYSLOG("\n\t\tstyle=filled;\n");
 SYSLOG("\n\t\tcolor=lightgrey;\n");
 sb = get_super_block(root_inode->i_dev);
 int dir_blk0_nr = root_inode->i_start_sect;
 int nr_dir_blks = (root_inode->i_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
 int nr_dir_entries =
   root_inode->i_size / DIR_ENTRY_SIZE; /**
            * including unused slots
            * (the file has been deleted
            * but the slot is still there)
            */
 int m = 0;
 struct dir_entry * pde;
 for (i = 0; i < nr_dir_blks; i++) {
  RD_SECT(root_inode->i_dev, dir_blk0_nr + i);
  memcpy(_buf, fsbuf, SECTOR_SIZE);
  pde = (struct dir_entry *)_buf;
  for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++,pde++) {
   if (pde->inode_nr) {
    memcpy(filename, pde->name, MAX_FILENAME_LEN);
    if (filename[0] == '.')
     filename[0] = '/';
    SYSLOG("\t\t\"rootdirent%d\" [\n", pde->inode_nr);
    SYSLOG("\t\t\tlabel = \"<f0> %d"
           "|<f2> %s",
           pde->inode_nr,
           filename);
    SYSLOG("\t\"\n");
    SYSLOG("\t\t\tshape = \"record\"\n");
    SYSLOG("\t\t];\n");

    SYSLOG("\t"
           "\"inodearray%d\":f0"
           " -> "
           "\"rootdirent%d\":f0"
           ";\n",
           pde->inode_nr, pde->inode_nr);
   }
  }
  if (m > nr_dir_entries) /* all entries have been iterated */
   break;
 }

 SYSLOG("\t\tlabel = \"root dir\";\n");
 SYSLOG("\t}\n");

 for (i = 0; i < pfm_idx; i++) {
  SYSLOG("\t\"proc%d\":f%d -> \"filedesc%d\":f0;\n",
         pfm[i].pid,
         pfm[i].filp,
         pfm[i].desc);
 }

 for (i = 0; i < fim_idx; i++) {
  SYSLOG("\t\"filedesc%d\":f4 -> \"inode%d\":f6;\n",
         fim[i].desc,
         fim[i].inode);
 }

 for (i = 0; i < NR_INODE; i++) {
  if (inode_table[i].i_cnt != 0)
   SYSLOG("\t\"inode%d\":f7 -> \"inodearray%d\":f0;\n",
          i,
          inode_table[i].i_num);
 }

 /* tail */
 SYSLOG("\tlabel = \"%s\";\n", title);
 SYSLOG("}\n");

 /* separator */
 SYSLOG("--separator--\n");
 /* int pos = SYSLOG("--separator--\n"); */
 /* printl("dump_fd_graph(%s)::logpos:%d\n", title, pos); */
}

fs/link.c

/*************************************************************************//**
 *****************************************************************************
 * @file   link.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Tue Jun  3 17:05:10 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"


/*****************************************************************************
 *                                do_unlink
 *****************************************************************************/
/**
 * Remove a file.
 *
 * @note We clear the i-node in inode_array[] although it is not really needed.
 *       We don't clear the data bytes so the file is recoverable.
 *
 * @return On success, zero is returned.  On error, -1 is returned.
 *****************************************************************************/
PUBLIC int do_unlink()
{
 char pathname[MAX_PATH];

 /* get parameters from the message */
 int name_len = fs_msg.NAME_LEN; /* length of filename */
 int src = fs_msg.source; /* caller proc nr. */
 assert(name_len < MAX_PATH);
 phys_copy((void*)va2la(TASK_FS, pathname),
    (void*)va2la(src, fs_msg.PATHNAME),
    name_len);
 pathname[name_len] = 0;

 if (strcmp(pathname , "/") == 0) {
  printl("FS:do_unlink():: cannot unlink the root\n");
  return -1;
 }

 int inode_nr = search_file(pathname);
 if (inode_nr == INVALID_INODE) { /* file not found */
  printl("FS::do_unlink():: search_file() returns "
   "invalid inode: %s\n", pathname);
  return -1;
 }

 char filename[MAX_PATH];
 struct inode * dir_inode;
 if (strip_path(filename, pathname, &dir_inode) != 0)
  return -1;

 struct inode * pin = get_inode(dir_inode->i_dev, inode_nr);

 if (pin->i_mode != I_REGULAR) { /* can only remove regular files */
  printl("cannot remove file %s, because "
         "it is not a regular file.\n",
         pathname);
  return -1;
 }

 if (pin->i_cnt > 1) { /* the file was opened */
  printl("cannot remove file %s, because pin->i_cnt is %d.\n",
         pathname, pin->i_cnt);
  return -1;
 }

 struct super_block * sb = get_super_block(pin->i_dev);

 /*************************/
 /* free the bit in i-map */
 /*************************/
 int byte_idx = inode_nr / 8;
 int bit_idx = inode_nr % 8;
 assert(byte_idx < SECTOR_SIZE); /* we have only one i-map sector */
 /* read sector 2 (skip bootsect and superblk): */
 RD_SECT(pin->i_dev, 2);
 assert(fsbuf[byte_idx % SECTOR_SIZE] & (1 << bit_idx));
 fsbuf[byte_idx % SECTOR_SIZE] &= ~(1 << bit_idx);
 WR_SECT(pin->i_dev, 2);

 /**************************/
 /* free the bits in s-map */
 /**************************/
 /*
  *           bit_idx: bit idx in the entire i-map
  *     ... ____|____
  *                  \        .-- byte_cnt: how many bytes between
  *                   \      |              the first and last byte
  *        +-+-+-+-+-+-+-+-+ V +-+-+-+-+-+-+-+-+
  *    ... | | | | | |*|*|*|...|*|*|*|*| | | | |
  *        +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
  *         0 1 2 3 4 5 6 7     0 1 2 3 4 5 6 7
  *  ...__/
  *      byte_idx: byte idx in the entire i-map
  */
 bit_idx  = pin->i_start_sect - sb->n_1st_sect + 1;
 byte_idx = bit_idx / 8;
 int bits_left = pin->i_nr_sects;
 int byte_cnt = (bits_left - (8 - (bit_idx % 8))) / 8;

 /* current sector nr. */
 int s = 2  /* 2: bootsect + superblk */
  + sb->nr_imap_sects + byte_idx / SECTOR_SIZE;

 RD_SECT(pin->i_dev, s);

 int i;
 /* clear the first byte */
 for (i = bit_idx % 8; (i < 8) && bits_left; i++,bits_left--) {
  assert((fsbuf[byte_idx % SECTOR_SIZE] >> i & 1) == 1);
  fsbuf[byte_idx % SECTOR_SIZE] &= ~(1 << i);
 }

 /* clear bytes from the second byte to the second to last */
 int k;
 i = (byte_idx % SECTOR_SIZE) + 1; /* the second byte */
 for (k = 0; k < byte_cnt; k++,i++,bits_left-=8) {
  if (i == SECTOR_SIZE) {
   i = 0;
   WR_SECT(pin->i_dev, s);
   RD_SECT(pin->i_dev, ++s);
  }
  assert(fsbuf[i] == 0xFF);
  fsbuf[i] = 0;
 }

 /* clear the last byte */
 if (i == SECTOR_SIZE) {
  i = 0;
  WR_SECT(pin->i_dev, s);
  RD_SECT(pin->i_dev, ++s);
 }
 unsigned char mask = ~((unsigned char)(~0) << bits_left);
 assert((fsbuf[i] & mask) == mask);
 fsbuf[i] &= (~0) << bits_left;
 WR_SECT(pin->i_dev, s);

 /***************************/
 /* clear the i-node itself */
 /***************************/
 pin->i_mode = 0;
 pin->i_size = 0;
 pin->i_start_sect = 0;
 pin->i_nr_sects = 0;
 sync_inode(pin);
 /* release slot in inode_table[] */
 put_inode(pin);

 /************************************************/
 /* set the inode-nr to 0 in the directory entry */
 /************************************************/
 int dir_blk0_nr = dir_inode->i_start_sect;
 int nr_dir_blks = (dir_inode->i_size + SECTOR_SIZE) / SECTOR_SIZE;
 int nr_dir_entries =
  dir_inode->i_size / DIR_ENTRY_SIZE; /* including unused slots
           * (the file has been
           * deleted but the slot
           * is still there)
           */
 int m = 0;
 struct dir_entry * pde = 0;
 int flg = 0;
 int dir_size = 0;

 for (i = 0; i < nr_dir_blks; i++) {
  RD_SECT(dir_inode->i_dev, dir_blk0_nr + i);

  pde = (struct dir_entry *)fsbuf;
  int j;
  for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++,pde++) {
   if (++m > nr_dir_entries)
    break;

   if (pde->inode_nr == inode_nr) {
    /* pde->inode_nr = 0; */
    memset(pde, 0, DIR_ENTRY_SIZE);
    WR_SECT(dir_inode->i_dev, dir_blk0_nr + i);
    flg = 1;
    break;
   }

   if (pde->inode_nr != INVALID_INODE)
    dir_size += DIR_ENTRY_SIZE;
  }

  if (m > nr_dir_entries || /* all entries have been iterated OR */
      flg) /* file is found */
   break;
 }
 assert(flg);
 if (m == nr_dir_entries) { /* the file is the last one in the dir */
  dir_inode->i_size = dir_size;
  sync_inode(dir_inode);
 }

 return 0;
}

fs/main.c

/*************************************************************************//**
 *****************************************************************************
 * @file   main.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2007
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "config.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

#include "hd.h"

PRIVATE void init_fs();
PRIVATE void mkfs();
PRIVATE void read_super_block(int dev);

/*****************************************************************************
 *                                task_fs
 *****************************************************************************/
/**
 * <Ring 1> The main loop of TASK FS.
 *
 *****************************************************************************/
PUBLIC void task_fs()
{
 printl("Task FS begins.\n");

 init_fs();

 while (1) {
  send_recv(RECEIVE, ANY, &fs_msg);

  int msgtype = fs_msg.type;
  int src = fs_msg.source;
  pcaller = &proc_table[src];

  switch (msgtype) {
  case OPEN:
   fs_msg.FD = do_open();
   break;
  case CLOSE:
   fs_msg.RETVAL = do_close();
   break;
  case READ:
  case WRITE:
   fs_msg.CNT = do_rdwt();
   break;
  case UNLINK:
   fs_msg.RETVAL = do_unlink();
   break;
  /* case LSEEK: */
  /*  fs_msg.OFFSET = do_lseek(); */
  /*  break; */
  /* case RESUME_PROC: */
  /*  src = fs_msg.PROC_NR; */
  /*  break; */
  /* case FORK: */
  /*  fs_msg.RETVAL = fs_fork(); */
  /*  break; */
  /* case EXIT: */
  /*  fs_msg.RETVAL = fs_exit(); */
  /*  break; */
  /* case STAT: */
  /*  fs_msg.RETVAL = do_stat(); */
  /*  break; */
  default:
   dump_msg("FS::unknown message:", &fs_msg);
   assert(0);
   break;
  }

#ifdef ENABLE_DISK_LOG
  char * msg_name[128];
  msg_name[OPEN]   = "OPEN";
  msg_name[CLOSE]  = "CLOSE";
  msg_name[READ]   = "READ";
  msg_name[WRITE]  = "WRITE";
  msg_name[LSEEK]  = "LSEEK";
  msg_name[UNLINK] = "UNLINK";
  /* msg_name[FORK]   = "FORK"; */
  /* msg_name[EXIT]   = "EXIT"; */
  /* msg_name[STAT]   = "STAT"; */

  switch (msgtype) {
  case CLOSE:
  case UNLINK:
   //dump_fd_graph("%s just finished.", msg_name[msgtype]);
   //panic("");
  case OPEN:
  case READ:
  case WRITE:
  /* case FORK: */
  /* case LSEEK: */
  /* case EXIT: */
  /* case STAT: */
   break;
  /* case RESUME_PROC: */
  case DISK_LOG:
   break;
  default:
   assert(0);
  }
#endif

  /* reply */
  fs_msg.type = SYSCALL_RET;
  send_recv(SEND, src, &fs_msg);
 }
}

/*****************************************************************************
 *                                init_fs
 *****************************************************************************/
/**
 * <Ring 1> Do some preparation.
 *
 *****************************************************************************/
PRIVATE void init_fs()
{
 int i;

 /* f_desc_table[] */
 for (i = 0; i < NR_FILE_DESC; i++)
  memset(&f_desc_table[i], 0, sizeof(struct file_desc));

 /* inode_table[] */
 for (i = 0; i < NR_INODE; i++)
  memset(&inode_table[i], 0, sizeof(struct inode));

 /* super_block[] */
 struct super_block * sb = super_block;
 for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
  sb->sb_dev = NO_DEV;

 /* open the device: hard disk */
 MESSAGE driver_msg;
 driver_msg.type = DEV_OPEN;
 driver_msg.DEVICE = MINOR(ROOT_DEV);
 assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);

 /* make FS */
 mkfs();

 /* load super block of ROOT */
 read_super_block(ROOT_DEV);

 sb = get_super_block(ROOT_DEV);
 assert(sb->magic == MAGIC_V1);

 root_inode = get_inode(ROOT_DEV, ROOT_INODE);
}

/*****************************************************************************
 *                                mkfs
 *****************************************************************************/
/**
 * <Ring 1> Make a available Orange'S FS in the disk. It will
 *          - Write a super block to sector 1.
 *          - Create three special files: dev_tty0, dev_tty1, dev_tty2
 *          - Create the inode map
 *          - Create the sector map
 *          - Create the inodes of the files
 *          - Create `/', the root directory
 *****************************************************************************/
PRIVATE void mkfs()
{
 MESSAGE driver_msg;
 int i, j;

 int bits_per_sect = SECTOR_SIZE * 8; /* 8 bits per byte */

 /* get the geometry of ROOTDEV */
 struct part_info geo;
 driver_msg.type  = DEV_IOCTL;
 driver_msg.DEVICE = MINOR(ROOT_DEV);
 driver_msg.REQUEST = DIOCTL_GET_GEO;
 driver_msg.BUF  = &geo;
 driver_msg.PROC_NR = TASK_FS;
 assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);

 printl("dev size: 0x%x sectors\n", geo.size);

 /************************/
 /*      super block     */
 /************************/
 struct super_block sb;
 sb.magic   = MAGIC_V1;
 sb.nr_inodes   = bits_per_sect;
 sb.nr_inode_sects = sb.nr_inodes * INODE_SIZE / SECTOR_SIZE;
 sb.nr_sects   = geo.size; /* partition size in sector */
 sb.nr_imap_sects  = 1;
 sb.nr_smap_sects  = sb.nr_sects / bits_per_sect + 1;
 sb.n_1st_sect   = 1 + 1 +   /* boot sector & super block */
  sb.nr_imap_sects + sb.nr_smap_sects + sb.nr_inode_sects;
 sb.root_inode   = ROOT_INODE;
 sb.inode_size   = INODE_SIZE;
 struct inode x;
 sb.inode_isize_off= (int)&x.i_size - (int)&x;
 sb.inode_start_off= (int)&x.i_start_sect - (int)&x;
 sb.dir_ent_size   = DIR_ENTRY_SIZE;
 struct dir_entry de;
 sb.dir_ent_inode_off = (int)&de.inode_nr - (int)&de;
 sb.dir_ent_fname_off = (int)&de.name - (int)&de;

 memset(fsbuf, 0x90, SECTOR_SIZE);
 memcpy(fsbuf, &sb, SUPER_BLOCK_SIZE);

 /* write the super block */
 WR_SECT(ROOT_DEV, 1);

 printl("devbase:0x%x00, sb:0x%x00, imap:0x%x00, smap:0x%x00\n"
        "        inodes:0x%x00, 1st_sector:0x%x00\n",
        geo.base * 2,
        (geo.base + 1) * 2,
        (geo.base + 1 + 1) * 2,
        (geo.base + 1 + 1 + sb.nr_imap_sects) * 2,
        (geo.base + 1 + 1 + sb.nr_imap_sects + sb.nr_smap_sects) * 2,
        (geo.base + sb.n_1st_sect) * 2);

 /************************/
 /*       inode map      */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 for (i = 0; i < (NR_CONSOLES + 2); i++)
  fsbuf[0] |= 1 << i;

 assert(fsbuf[0] == 0x1F);/* 0001 1111 :
      *    | ||||
      *    | |||`--- bit 0 : reserved
      *    | ||`---- bit 1 : the first inode,
      *    | ||              which indicates `/'
      *    | |`----- bit 2 : /dev_tty0
      *    | `------ bit 3 : /dev_tty1
      *    `-------- bit 4 : /dev_tty2
      */
 WR_SECT(ROOT_DEV, 2);

 /************************/
 /*      secter map      */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 int nr_sects = NR_DEFAULT_FILE_SECTS + 1;
 /*             ~~~~~~~~~~~~~~~~~~~|~   |
  *                                |    `--- bit 0 is reserved
  *                                `-------- for `/'
  */
 for (i = 0; i < nr_sects / 8; i++)
  fsbuf[i] = 0xFF;

 for (j = 0; j < nr_sects % 8; j++)
  fsbuf[i] |= (1 << j);

 WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects);

 /* zeromemory the rest sector-map */
 memset(fsbuf, 0, SECTOR_SIZE);
 for (i = 1; i < sb.nr_smap_sects; i++)
  WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + i);

 /************************/
 /*       inodes         */
 /************************/
 /* inode of `/' */
 memset(fsbuf, 0, SECTOR_SIZE);
 struct inode * pi = (struct inode*)fsbuf;
 pi->i_mode = I_DIRECTORY;
 pi->i_size = DIR_ENTRY_SIZE * 4; /* 4 files:
       * `.',
       * `dev_tty0', `dev_tty1', `dev_tty2',
       */
 pi->i_start_sect = sb.n_1st_sect;
 pi->i_nr_sects = NR_DEFAULT_FILE_SECTS;
 /* inode of `/dev_tty0~2' */
 for (i = 0; i < NR_CONSOLES; i++) {
  pi = (struct inode*)(fsbuf + (INODE_SIZE * (i + 1)));
  pi->i_mode = I_CHAR_SPECIAL;
  pi->i_size = 0;
  pi->i_start_sect = MAKE_DEV(DEV_CHAR_TTY, i);
  pi->i_nr_sects = 0;
 }
 WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + sb.nr_smap_sects);

 /************************/
 /*          `/'         */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 struct dir_entry * pde = (struct dir_entry *)fsbuf;

 pde->inode_nr = 1;
 strcpy(pde->name, ".");

 /* dir entries of `/dev_tty0~2' */
 for (i = 0; i < NR_CONSOLES; i++) {
  pde++;
  pde->inode_nr = i + 2; /* dev_tty0's inode_nr is 2 */
  sprintf(pde->name, "dev_tty%d", i);
 }
 WR_SECT(ROOT_DEV, sb.n_1st_sect);
}

/*****************************************************************************
 *                                rw_sector
 *****************************************************************************/
/**
 * <Ring 1> R/W a sector via messaging with the corresponding driver.
 *
 * @param io_type  DEV_READ or DEV_WRITE
 * @param dev      device nr
 * @param pos      Byte offset from/to where to r/w.
 * @param bytes    r/w count in bytes.
 * @param proc_nr  To whom the buffer belongs.
 * @param buf      r/w buffer.
 *
 * @return Zero if success.
 *****************************************************************************/
PUBLIC int rw_sector(int io_type, int dev, u64 pos, int bytes, int proc_nr,
       void* buf)
{
 MESSAGE driver_msg;

 driver_msg.type  = io_type;
 driver_msg.DEVICE = MINOR(dev);
 driver_msg.POSITION = pos;
 driver_msg.BUF  = buf;
 driver_msg.CNT  = bytes;
 driver_msg.PROC_NR = proc_nr;
 assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &driver_msg);

 return 0;
}


/*****************************************************************************
 *                                read_super_block
 *****************************************************************************/
/**
 * <Ring 1> Read super block from the given device then write it into a free
 *          super_block[] slot.
 *
 * @param dev  From which device the super block comes.
 *****************************************************************************/
PRIVATE void read_super_block(int dev)
{
 int i;
 MESSAGE driver_msg;

 driver_msg.type  = DEV_READ;
 driver_msg.DEVICE = MINOR(dev);
 driver_msg.POSITION = SECTOR_SIZE * 1;
 driver_msg.BUF  = fsbuf;
 driver_msg.CNT  = SECTOR_SIZE;
 driver_msg.PROC_NR = TASK_FS;
 assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &driver_msg);

 /* find a free slot in super_block[] */
 for (i = 0; i < NR_SUPER_BLOCK; i++)
  if (super_block[i].sb_dev == NO_DEV)
   break;
 if (i == NR_SUPER_BLOCK)
  panic("super_block slots used up");

 assert(i == 0); /* currently we use only the 1st slot */

 struct super_block * psb = (struct super_block *)fsbuf;

 super_block[i] = *psb;
 super_block[i].sb_dev = dev;
}


/*****************************************************************************
 *                                get_super_block
 *****************************************************************************/
/**
 * <Ring 1> Get the super block from super_block[].
 *
 * @param dev Device nr.
 *
 * @return Super block ptr.
 *****************************************************************************/
PUBLIC struct super_block * get_super_block(int dev)
{
 struct super_block * sb = super_block;
 for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
  if (sb->sb_dev == dev)
   return sb;

 panic("super block of devie %d not found.\n", dev);

 return 0;
}


/*****************************************************************************
 *                                get_inode
 *****************************************************************************/
/**
 * <Ring 1> Get the inode ptr of given inode nr. A cache -- inode_table[] -- is
 * maintained to make things faster. If the inode requested is already there,
 * just return it. Otherwise the inode will be read from the disk.
 *
 * @param dev Device nr.
 * @param num I-node nr.
 *
 * @return The inode ptr requested.
 *****************************************************************************/
PUBLIC struct inode * get_inode(int dev, int num)
{
 if (num == 0)
  return 0;

 struct inode * p;
 struct inode * q = 0;
 for (p = &inode_table[0]; p < &inode_table[NR_INODE]; p++) {
  if (p->i_cnt) { /* not a free slot */
   if ((p->i_dev == dev) && (p->i_num == num)) {
    /* this is the inode we want */
    p->i_cnt++;
    return p;
   }
  }
  else {  /* a free slot */
   if (!q) /* q hasn't been assigned yet */
    q = p; /* q <- the 1st free slot */
  }
 }

 if (!q)
  panic("the inode table is full");

 q->i_dev = dev;
 q->i_num = num;
 q->i_cnt = 1;

 struct super_block * sb = get_super_block(dev);
 int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
  ((num - 1) / (SECTOR_SIZE / INODE_SIZE));
 RD_SECT(dev, blk_nr);
 struct inode * pinode =
  (struct inode*)((u8*)fsbuf +
    ((num - 1 ) % (SECTOR_SIZE / INODE_SIZE))
     * INODE_SIZE);
 q->i_mode = pinode->i_mode;
 q->i_size = pinode->i_size;
 q->i_start_sect = pinode->i_start_sect;
 q->i_nr_sects = pinode->i_nr_sects;
 return q;
}

/*****************************************************************************
 *                                put_inode
 *****************************************************************************/
/**
 * Decrease the reference nr of a slot in inode_table[]. When the nr reaches
 * zero, it means the inode is not used any more and can be overwritten by
 * a new inode.
 *
 * @param pinode I-node ptr.
 *****************************************************************************/
PUBLIC void put_inode(struct inode * pinode)
{
 assert(pinode->i_cnt > 0);
 pinode->i_cnt--;
}

/*****************************************************************************
 *                                sync_inode
 *****************************************************************************/
/**
 * <Ring 1> Write the inode back to the disk. Commonly invoked as soon as the
 *          inode is changed.
 *
 * @param p I-node ptr.
 *****************************************************************************/
PUBLIC void sync_inode(struct inode * p)
{
 struct inode * pinode;
 struct super_block * sb = get_super_block(p->i_dev);
 int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
  ((p->i_num - 1) / (SECTOR_SIZE / INODE_SIZE));
 RD_SECT(p->i_dev, blk_nr);
 pinode = (struct inode*)((u8*)fsbuf +
     (((p->i_num - 1) % (SECTOR_SIZE / INODE_SIZE))
      * INODE_SIZE));
 pinode->i_mode = p->i_mode;
 pinode->i_size = p->i_size;
 pinode->i_start_sect = p->i_start_sect;
 pinode->i_nr_sects = p->i_nr_sects;
 WR_SECT(p->i_dev, blk_nr);
}

fs/misc.c

/*************************************************************************//**
 *****************************************************************************
 * @file   misc.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

/* Orange'S FS */

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
#include "hd.h"
#include "fs.h"

/*****************************************************************************
 *                                search_file
 *****************************************************************************/
/**
 * Search the file and return the inode_nr.
 *
 * @param[in] path The full path of the file to search.
 * @return         Ptr to the i-node of the file if successful, otherwise zero.
 *
 * @see open()
 * @see do_open()
 *****************************************************************************/
PUBLIC int search_file(char * path)
{
 int i, j;

 char filename[MAX_PATH];
 memset(filename, 0, MAX_FILENAME_LEN);
 struct inode * dir_inode;
 if (strip_path(filename, path, &dir_inode) != 0)
  return 0;

 if (filename[0] == 0) /* path: "/" */
  return dir_inode->i_num;

 /**
  * Search the dir for the file.
  */
 int dir_blk0_nr = dir_inode->i_start_sect;
 int nr_dir_blks = (dir_inode->i_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
 int nr_dir_entries =
   dir_inode->i_size / DIR_ENTRY_SIZE; /**
            * including unused slots
            * (the file has been deleted
            * but the slot is still there)
            */
 int m = 0;
 struct dir_entry * pde;
 for (i = 0; i < nr_dir_blks; i++) {
  RD_SECT(dir_inode->i_dev, dir_blk0_nr + i);
  pde = (struct dir_entry *)fsbuf;
  for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++,pde++) {
   if (memcmp(filename, pde->name, MAX_FILENAME_LEN) == 0)
    return pde->inode_nr;
   if (++m > nr_dir_entries)
    break;
  }
  if (m > nr_dir_entries) /* all entries have been iterated */
   break;
 }

 /* file not found */
 return 0;
}

/*****************************************************************************
 *                                strip_path
 *****************************************************************************/
/**
 * Get the basename from the fullpath.
 *
 * In Orange'S FS v1.0, all files are stored in the root directory.
 * There is no sub-folder thing.
 *
 * This routine should be called at the very beginning of file operations
 * such as open(), read() and write(). It accepts the full path and returns
 * two things: the basename and a ptr of the root dir's i-node.
 *
 * e.g. After stip_path(filename, "/blah", ppinode) finishes, we get:
 *      - filename: "blah"
 *      - *ppinode: root_inode
 *      - ret val:  0 (successful)
 *
 * Currently an acceptable pathname should begin with at most one `/'
 * preceding a filename.
 *
 * Filenames may contain any character except '/' and '\\0'.
 *
 * @param[out] filename The string for the result.
 * @param[in]  pathname The full pathname.
 * @param[out] ppinode  The ptr of the dir's inode will be stored here.
 *
 * @return Zero if success, otherwise the pathname is not valid.
 *****************************************************************************/
PUBLIC int strip_path(char * filename, const char * pathname,
        struct inode** ppinode)
{
 const char * s = pathname;
 char * t = filename;

 if (s == 0)
  return -1;

 if (*s == '/')
  s++;

 while (*s) {  /* check each character */
  if (*s == '/')
   return -1;
  *t++ = *s++;
  /* if filename is too long, just truncate it */
  if (t - filename >= MAX_FILENAME_LEN)
   break;
 }
 *t = 0;

 *ppinode = root_inode;

 return 0;
}

fs/open.c

/*************************************************************************//**
 *****************************************************************************
 * @file   fs/open.c
 * The file contains:
 *   - do_open()
 *   - do_close()
 *   - do_lseek()
 *   - create_file()
 * @author Forrest Yu
 * @date   2007
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

PRIVATE struct inode * create_file(char * path, int flags);
PRIVATE int alloc_imap_bit(int dev);
PRIVATE int alloc_smap_bit(int dev, int nr_sects_to_alloc);
PRIVATE struct inode * new_inode(int dev, int inode_nr, int start_sect);
PRIVATE void new_dir_entry(struct inode * dir_inode, int inode_nr, char * filename);

/*****************************************************************************
 *                                do_open
 *****************************************************************************/
/**
 * Open a file and return the file descriptor.
 *
 * @return File descriptor if successful, otherwise a negative error code.
 *****************************************************************************/
PUBLIC int do_open()
{
 int fd = -1;  /* return value */

 char pathname[MAX_PATH];

 /* get parameters from the message */
 int flags = fs_msg.FLAGS; /* access mode */
 int name_len = fs_msg.NAME_LEN; /* length of filename */
 int src = fs_msg.source; /* caller proc nr. */
 assert(name_len < MAX_PATH);
 phys_copy((void*)va2la(TASK_FS, pathname),
    (void*)va2la(src, fs_msg.PATHNAME),
    name_len);
 pathname[name_len] = 0;

 /* find a free slot in PROCESS::filp[] */
 int i;
 for (i = 0; i < NR_FILES; i++) {
  if (pcaller->filp[i] == 0) {
   fd = i;
   break;
  }
 }
 if ((fd < 0) || (fd >= NR_FILES))
  panic("filp[] is full (PID:%d)", proc2pid(pcaller));

 /* find a free slot in f_desc_table[] */
 for (i = 0; i < NR_FILE_DESC; i++)
  if (f_desc_table[i].fd_inode == 0)
   break;
 if (i >= NR_FILE_DESC)
  panic("f_desc_table[] is full (PID:%d)", proc2pid(pcaller));

 int inode_nr = search_file(pathname);

 struct inode * pin = 0;
 if (flags & O_CREAT) {
  if (inode_nr) {
   printl("file exists.\n");
   return -1;
  }
  else {
   pin = create_file(pathname, flags);
  }
 }
 else {
  assert(flags & O_RDWR);

  char filename[MAX_PATH];
  struct inode * dir_inode;
  if (strip_path(filename, pathname, &dir_inode) != 0)
   return -1;
  pin = get_inode(dir_inode->i_dev, inode_nr);
 }

 if (pin) {
  /* connects proc with file_descriptor */
  pcaller->filp[fd] = &f_desc_table[i];

  /* connects file_descriptor with inode */
  f_desc_table[i].fd_inode = pin;

  f_desc_table[i].fd_mode = flags;
  /* f_desc_table[i].fd_cnt = 1; */
  f_desc_table[i].fd_pos = 0;

  int imode = pin->i_mode & I_TYPE_MASK;

  if (imode == I_CHAR_SPECIAL) {
   MESSAGE driver_msg;
   driver_msg.type = DEV_OPEN;
   int dev = pin->i_start_sect;
   driver_msg.DEVICE = MINOR(dev);
   assert(MAJOR(dev) == 4);
   assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
   send_recv(BOTH,
      dd_map[MAJOR(dev)].driver_nr,
      &driver_msg);
  }
  else if (imode == I_DIRECTORY) {
   assert(pin->i_num == ROOT_INODE);
  }
  else {
   assert(pin->i_mode == I_REGULAR);
  }
 }
 else {
  return -1;
 }

 return fd;
}

/*****************************************************************************
 *                                create_file
 *****************************************************************************/
/**
 * Create a file and return it's inode ptr.
 *
 * @param[in] path   The full path of the new file
 * @param[in] flags  Attribiutes of the new file
 *
 * @return           Ptr to i-node of the new file if successful, otherwise 0.
 *
 * @see open()
 * @see do_open()
 *
 * @todo return values of routines called, return values of self.
 *****************************************************************************/
PRIVATE struct inode * create_file(char * path, int flags)
{
 char filename[MAX_PATH];
 struct inode * dir_inode;
 if (strip_path(filename, path, &dir_inode) != 0)
  return 0;

 int inode_nr = alloc_imap_bit(dir_inode->i_dev);
 int free_sect_nr = alloc_smap_bit(dir_inode->i_dev,
       NR_DEFAULT_FILE_SECTS);
 struct inode *newino = new_inode(dir_inode->i_dev, inode_nr,
      free_sect_nr);

 new_dir_entry(dir_inode, newino->i_num, filename);

 return newino;
}

/*****************************************************************************
 *                                do_close
 *****************************************************************************/
/**
 * Handle the message CLOSE.
 *
 * @return Zero if success.
 *****************************************************************************/
PUBLIC int do_close()
{
 int fd = fs_msg.FD;
 put_inode(pcaller->filp[fd]->fd_inode);
 pcaller->filp[fd]->fd_inode = 0;
 pcaller->filp[fd] = 0;

 return 0;
}

/*****************************************************************************
 *                                do_lseek
 *****************************************************************************/
/**
 * Handle the message LSEEK.
 *
 * @return The new offset in bytes from the beginning of the file if successful,
 *         otherwise a negative number.
 *****************************************************************************/
PUBLIC int do_lseek()
{
 int fd = fs_msg.FD;
 int off = fs_msg.OFFSET;
 int whence = fs_msg.WHENCE;

 int pos = pcaller->filp[fd]->fd_pos;
 int f_size = pcaller->filp[fd]->fd_inode->i_size;

 switch (whence) {
 case SEEK_SET:
  pos = off;
  break;
 case SEEK_CUR:
  pos += off;
  break;
 case SEEK_END:
  pos = f_size + off;
  break;
 default:
  return -1;
  break;
 }
 if ((pos > f_size) || (pos < 0)) {
  return -1;
 }
 pcaller->filp[fd]->fd_pos = pos;
 return pos;
}

/*****************************************************************************
 *                                alloc_imap_bit
 *****************************************************************************/
/**
 * Allocate a bit in inode-map.
 *
 * @param dev  In which device the inode-map is located.
 *
 * @return  I-node nr.
 *****************************************************************************/
PRIVATE int alloc_imap_bit(int dev)
{
 int inode_nr = 0;
 int i, j, k;

 int imap_blk0_nr = 1 + 1; /* 1 boot sector & 1 super block */
 struct super_block * sb = get_super_block(dev);

 for (i = 0; i < sb->nr_imap_sects; i++) {
  RD_SECT(dev, imap_blk0_nr + i);

  for (j = 0; j < SECTOR_SIZE; j++) {
   /* skip `11111111' bytes */
   if (fsbuf[j] == 0xFF)
    continue;
   /* skip `1' bits */
   for (k = 0; ((fsbuf[j] >> k) & 1) != 0; k++) {}
   /* i: sector index; j: byte index; k: bit index */
   inode_nr = (i * SECTOR_SIZE + j) * 8 + k;
   fsbuf[j] |= (1 << k);
   /* write the bit to imap */
   WR_SECT(dev, imap_blk0_nr + i);
   break;
  }

  return inode_nr;
 }

 /* no free bit in imap */
 panic("inode-map is probably full.\n");

 return 0;
}

/*****************************************************************************
 *                                alloc_smap_bit
 *****************************************************************************/
/**
 * Allocate a bit in sector-map.
 *
 * @param dev  In which device the sector-map is located.
 * @param nr_sects_to_alloc  How many sectors are allocated.
 *
 * @return  The 1st sector nr allocated.
 *****************************************************************************/
PRIVATE int alloc_smap_bit(int dev, int nr_sects_to_alloc)
{
 /* int nr_sects_to_alloc = NR_DEFAULT_FILE_SECTS; */

 int i; /* sector index */
 int j; /* byte index */
 int k; /* bit index */

 struct super_block * sb = get_super_block(dev);

 int smap_blk0_nr = 1 + 1 + sb->nr_imap_sects;
 int free_sect_nr = 0;

 for (i = 0; i < sb->nr_smap_sects; i++) { /* smap_blk0_nr + i :
           current sect nr. */
  RD_SECT(dev, smap_blk0_nr + i);

  /* byte offset in current sect */
  for (j = 0; j < SECTOR_SIZE && nr_sects_to_alloc > 0; j++) {
   k = 0;
   if (!free_sect_nr) {
    /* loop until a free bit is found */
    if (fsbuf[j] == 0xFF) continue;
    for (; ((fsbuf[j] >> k) & 1) != 0; k++) {}
    free_sect_nr = (i * SECTOR_SIZE + j) * 8 +
     k - 1 + sb->n_1st_sect;
   }

   for (; k < 8; k++) { /* repeat till enough bits are set */
    assert(((fsbuf[j] >> k) & 1) == 0);
    fsbuf[j] |= (1 << k);
    if (--nr_sects_to_alloc == 0)
     break;
   }
  }

  if (free_sect_nr) /* free bit found, write the bits to smap */
   WR_SECT(dev, smap_blk0_nr + i);

  if (nr_sects_to_alloc == 0)
   break;
 }

 assert(nr_sects_to_alloc == 0);

 return free_sect_nr;
}

/*****************************************************************************
 *                                new_inode
 *****************************************************************************/
/**
 * Generate a new i-node and write it to disk.
 *
 * @param dev  Home device of the i-node.
 * @param inode_nr  I-node nr.
 * @param start_sect  Start sector of the file pointed by the new i-node.
 *
 * @return  Ptr of the new i-node.
 *****************************************************************************/
PRIVATE struct inode * new_inode(int dev, int inode_nr, int start_sect)
{
 struct inode * new_inode = get_inode(dev, inode_nr);

 new_inode->i_mode = I_REGULAR;
 new_inode->i_size = 0;
 new_inode->i_start_sect = start_sect;
 new_inode->i_nr_sects = NR_DEFAULT_FILE_SECTS;

 new_inode->i_dev = dev;
 new_inode->i_cnt = 1;
 new_inode->i_num = inode_nr;

 /* write to the inode array */
 sync_inode(new_inode);

 return new_inode;
}

/*****************************************************************************
 *                                new_dir_entry
 *****************************************************************************/
/**
 * Write a new entry into the directory.
 * 
 * @param dir_inode  I-node of the directory.
 * @param inode_nr   I-node nr of the new file.
 * @param filename   Filename of the new file.
 *****************************************************************************/
PRIVATE void new_dir_entry(struct inode *dir_inode,int inode_nr,char *filename)
{
 /* write the dir_entry */
 int dir_blk0_nr = dir_inode->i_start_sect;
 int nr_dir_blks = (dir_inode->i_size + SECTOR_SIZE) / SECTOR_SIZE;
 int nr_dir_entries =
  dir_inode->i_size / DIR_ENTRY_SIZE; /**
           * including unused slots
           * (the file has been
           * deleted but the slot
           * is still there)
           */
 int m = 0;
 struct dir_entry * pde;
 struct dir_entry * new_de = 0;

 int i, j;
 for (i = 0; i < nr_dir_blks; i++) {
  RD_SECT(dir_inode->i_dev, dir_blk0_nr + i);

  pde = (struct dir_entry *)fsbuf;
  for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++,pde++) {
   if (++m > nr_dir_entries)
    break;

   if (pde->inode_nr == 0) { /* it's a free slot */
    new_de = pde;
    break;
   }
  }
  if (m > nr_dir_entries ||/* all entries have been iterated or */
      new_de)              /* free slot is found */
   break;
 }
 if (!new_de) { /* reached the end of the dir */
  new_de = pde;
  dir_inode->i_size += DIR_ENTRY_SIZE;
 }
 new_de->inode_nr = inode_nr;
 strcpy(new_de->name, filename);

 /* write dir block -- ROOT dir block */
 WR_SECT(dir_inode->i_dev, dir_blk0_nr + i);

 /* update dir inode */
 sync_inode(dir_inode);
}

fs/read_write.c

/*************************************************************************//**
 *****************************************************************************
 * @file   read_write.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"


/*****************************************************************************
 *                                do_rdwt
 *****************************************************************************/
/**
 * Read/Write file and return byte count read/written.
 *
 * Sector map is not needed to update, since the sectors for the file have been
 * allocated and the bits are set when the file was created.
 *
 * @return How many bytes have been read/written.
 *****************************************************************************/
PUBLIC int do_rdwt()
{
 int fd = fs_msg.FD; /**< file descriptor. */
 void * buf = fs_msg.BUF;/**< r/w buffer */
 int len = fs_msg.CNT; /**< r/w bytes */

 int src = fs_msg.source;  /* caller proc nr. */

 assert((pcaller->filp[fd] >= &f_desc_table[0]) &&
        (pcaller->filp[fd] < &f_desc_table[NR_FILE_DESC]));

 if (!(pcaller->filp[fd]->fd_mode & O_RDWR))
  return 0;

 int pos = pcaller->filp[fd]->fd_pos;

 struct inode * pin = pcaller->filp[fd]->fd_inode;

 assert(pin >= &inode_table[0] && pin < &inode_table[NR_INODE]);

 int imode = pin->i_mode & I_TYPE_MASK;

 if (imode == I_CHAR_SPECIAL) {
  int t = fs_msg.type == READ ? DEV_READ : DEV_WRITE;
  fs_msg.type = t;

  int dev = pin->i_start_sect;
  assert(MAJOR(dev) == 4);

  fs_msg.DEVICE = MINOR(dev);
  fs_msg.BUF = buf;
  fs_msg.CNT = len;
  fs_msg.PROC_NR = src;
  assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
  send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &fs_msg);
  assert(fs_msg.CNT == len);

  return fs_msg.CNT;
 }
 else {
  assert(pin->i_mode == I_REGULAR || pin->i_mode == I_DIRECTORY);
  assert((fs_msg.type == READ) || (fs_msg.type == WRITE));

  int pos_end;
  if (fs_msg.type == READ)
   pos_end = min(pos + len, pin->i_size);
  else  /* WRITE */
   pos_end = min(pos + len, pin->i_nr_sects * SECTOR_SIZE);

  int off = pos % SECTOR_SIZE;
  int rw_sect_min=pin->i_start_sect+(pos>>SECTOR_SIZE_SHIFT);
  int rw_sect_max=pin->i_start_sect+(pos_end>>SECTOR_SIZE_SHIFT);

  int chunk = min(rw_sect_max - rw_sect_min + 1,
    FSBUF_SIZE >> SECTOR_SIZE_SHIFT);

  int bytes_rw = 0;
  int bytes_left = len;
  int i;
  for (i = rw_sect_min; i <= rw_sect_max; i += chunk) {
   /* read/write this amount of bytes every time */
   int bytes = min(bytes_left, chunk * SECTOR_SIZE - off);
   rw_sector(DEV_READ,
      pin->i_dev,
      i * SECTOR_SIZE,
      chunk * SECTOR_SIZE,
      TASK_FS,
      fsbuf);

   if (fs_msg.type == READ) {
    phys_copy((void*)va2la(src, buf + bytes_rw),
       (void*)va2la(TASK_FS, fsbuf + off),
       bytes);
   }
   else { /* WRITE */
    phys_copy((void*)va2la(TASK_FS, fsbuf + off),
       (void*)va2la(src, buf + bytes_rw),
       bytes);
    rw_sector(DEV_WRITE,
       pin->i_dev,
       i * SECTOR_SIZE,
       chunk * SECTOR_SIZE,
       TASK_FS,
       fsbuf);
   }
   off = 0;
   bytes_rw += bytes;
   pcaller->filp[fd]->fd_pos += bytes;
   bytes_left -= bytes;
  }

  if (pcaller->filp[fd]->fd_pos > pin->i_size) {
   /* update inode::size */
   pin->i_size = pcaller->filp[fd]->fd_pos;
   /* write the updated i-node back to disk */
   sync_inode(pin);
  }

  return bytes_rw;
 }
}

include/sys/const.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            const.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#ifndef _ORANGES_CONST_H_
#define _ORANGES_CONST_H_

/* max() & min() */
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))

/* Color */
/*
 * e.g. MAKE_COLOR(BLUE, RED)
 *      MAKE_COLOR(BLACK, RED) | BRIGHT
 *      MAKE_COLOR(BLACK, RED) | BRIGHT | FLASH
 */
#define BLACK   0x0     /* 0000 */
#define WHITE   0x7     /* 0111 */
#define RED     0x4     /* 0100 */
#define GREEN   0x2     /* 0010 */
#define BLUE    0x1     /* 0001 */
#define FLASH   0x80    /* 1000 0000 */
#define BRIGHT  0x08    /* 0000 1000 */
#define MAKE_COLOR(x,y) ((x<<4) | y) /* MAKE_COLOR(Background,Foreground) */

/* GDT 和 IDT 中描述符的個數 */
#define GDT_SIZE 128
#define IDT_SIZE 256

/* 權限 */
#define PRIVILEGE_KRNL 0
#define PRIVILEGE_TASK 1
#define PRIVILEGE_USER 3
/* RPL */
#define RPL_KRNL SA_RPL0
#define RPL_TASK SA_RPL1
#define RPL_USER SA_RPL3

/* Process */
#define SENDING   0x02 /* set when proc trying to send */
#define RECEIVING 0x04 /* set when proc trying to recv */

/* TTY */
#define NR_CONSOLES 3 /* consoles */

/* 8259A interrupt controller ports. */
#define INT_M_CTL 0x20 /* I/O port for interrupt controller         <Master> */
#define INT_M_CTLMASK 0x21 /* setting bits in this port disables ints   <Master> */
#define INT_S_CTL 0xA0 /* I/O port for second interrupt controller  <Slave>  */
#define INT_S_CTLMASK 0xA1 /* setting bits in this port disables ints   <Slave>  */

/* 8253/8254 PIT (Programmable Interval Timer) */
#define TIMER0         0x40 /* I/O port for timer channel 0 */
#define TIMER_MODE     0x43 /* I/O port for timer mode control */
#define RATE_GENERATOR 0x34 /* 00-11-010-0 :
        * Counter0 - LSB then MSB - rate generator - binary
        */
#define TIMER_FREQ     1193182L/* clock frequency for timer in PC and AT */
#define HZ             100  /* clock freq (software settable on IBM-PC) */

/* AT keyboard */
/* 8042 ports */
#define KB_DATA  0x60 /* I/O port for keyboard data
     Read : Read Output Buffer
     Write: Write Input Buffer(8042 Data&8048 Command) */
#define KB_CMD  0x64 /* I/O port for keyboard command
     Read : Read Status Register
     Write: Write Input Buffer(8042 Command) */
#define LED_CODE 0xED
#define KB_ACK  0xFA

/* VGA */
#define CRTC_ADDR_REG 0x3D4 /* CRT Controller Registers - Addr Register */
#define CRTC_DATA_REG 0x3D5 /* CRT Controller Registers - Data Register */
#define START_ADDR_H 0xC /* reg index of video mem start addr (MSB) */
#define START_ADDR_L 0xD /* reg index of video mem start addr (LSB) */
#define CURSOR_H 0xE /* reg index of cursor position (MSB) */
#define CURSOR_L 0xF /* reg index of cursor position (LSB) */
#define V_MEM_BASE 0xB8000 /* base of color video memory */
#define V_MEM_SIZE 0x8000 /* 32K: B8000H -> BFFFFH */

/* Hardware interrupts */
#define NR_IRQ  16 /* Number of IRQs */
#define CLOCK_IRQ 0
#define KEYBOARD_IRQ 1
#define CASCADE_IRQ 2 /* cascade enable for 2nd AT controller */
#define ETHER_IRQ 3 /* default ethernet interrupt vector */
#define SECONDARY_IRQ 3 /* RS232 interrupt vector for port 2 */
#define RS232_IRQ 4 /* RS232 interrupt vector for port 1 */
#define XT_WINI_IRQ 5 /* xt winchester */
#define FLOPPY_IRQ 6 /* floppy disk */
#define PRINTER_IRQ 7
#define AT_WINI_IRQ 14 /* at winchester */

/* tasks */
/* 注意 TASK_XXX 的定義要與 global.c 中對應 */
#define INVALID_DRIVER -20
#define INTERRUPT -10
#define TASK_TTY 0
#define TASK_SYS 1
#define TASK_HD  2
#define TASK_FS  3
/* #define TASK_MM 4 */
#define ANY  (NR_TASKS + NR_PROCS + 10)
#define NO_TASK  (NR_TASKS + NR_PROCS + 20)

/* system call */
#define NR_SYS_CALL 3

/* ipc */
#define SEND  1
#define RECEIVE  2
#define BOTH  3 /* BOTH = (SEND | RECEIVE) */

/* magic chars used by `printx' */
#define MAG_CH_PANIC '\002'
#define MAG_CH_ASSERT '\003'

/**
 * @enum msgtype
 * @brief MESSAGE types
 */
enum msgtype {
 /*
  * when hard interrupt occurs, a msg (with type==HARD_INT) will
  * be sent to some tasks
  */
 HARD_INT = 1,

 /* SYS task */
 GET_TICKS, GET_PID,

 /* FS */
 OPEN, CLOSE, READ, WRITE, LSEEK, STAT, UNLINK,

 /* TTY, SYS, FS, MM, etc */
 SYSCALL_RET,

 /* message type for drivers */
 DEV_OPEN = 1001,
 DEV_CLOSE,
 DEV_READ,
 DEV_WRITE,
 DEV_IOCTL,

 /* for debug */
 DISK_LOG
};

/* macros for messages */
#define FD  u.m3.m3i1
#define PATHNAME u.m3.m3p1
#define FLAGS  u.m3.m3i1
#define NAME_LEN u.m3.m3i2
#define CNT  u.m3.m3i2
#define REQUEST  u.m3.m3i2
#define PROC_NR  u.m3.m3i3
#define DEVICE  u.m3.m3i4
#define POSITION u.m3.m3l1
#define BUF  u.m3.m3p2
#define OFFSET  u.m3.m3i2
#define WHENCE  u.m3.m3i3

#define PID  u.m3.m3i2
/* #define STATUS  u.m3.m3i1 */
#define RETVAL  u.m3.m3i1









#define DIOCTL_GET_GEO 1

/* Hard Drive */
#define SECTOR_SIZE  512
#define SECTOR_BITS  (SECTOR_SIZE * 8)
#define SECTOR_SIZE_SHIFT 9

/* major device numbers (corresponding to kernel/global.c::dd_map[]) */
#define NO_DEV   0
#define DEV_FLOPPY  1
#define DEV_CDROM  2
#define DEV_HD   3
#define DEV_CHAR_TTY  4
#define DEV_SCSI  5
/* make device number from major and minor numbers */
#define MAJOR_SHIFT  8
#define MAKE_DEV(a,b)  ((a << MAJOR_SHIFT) | b)
/* separate major and minor numbers from device number */
#define MAJOR(x)  ((x >> MAJOR_SHIFT) & 0xFF)
#define MINOR(x)  (x & 0xFF)

#define INVALID_INODE  0
#define ROOT_INODE  1

#define MAX_DRIVES  2
#define NR_PART_PER_DRIVE 4
#define NR_SUB_PER_PART  16
#define NR_SUB_PER_DRIVE (NR_SUB_PER_PART * NR_PART_PER_DRIVE)
#define NR_PRIM_PER_DRIVE (NR_PART_PER_DRIVE + 1)

/**
 * @def MAX_PRIM
 * Defines the max minor number of the primary partitions.
 * If there are 2 disks, prim_dev ranges in hd[0-9], this macro will
 * equals 9.
 */
#define MAX_PRIM  (MAX_DRIVES * NR_PRIM_PER_DRIVE - 1)

#define MAX_SUBPARTITIONS (NR_SUB_PER_DRIVE * MAX_DRIVES)

/* device numbers of hard disk */
#define MINOR_hd1a  0x10
#define MINOR_hd2a  (MINOR_hd1a+NR_SUB_PER_PART)

#define ROOT_DEV  MAKE_DEV(DEV_HD, MINOR_BOOT)

#define P_PRIMARY 0
#define P_EXTENDED 1

#define ORANGES_PART 0x99 /* Orange'S partition */
#define NO_PART  0x00 /* unused entry */
#define EXT_PART 0x05 /* extended partition */

#define NR_FILES 64
#define NR_FILE_DESC 64 /* FIXME */
#define NR_INODE 64 /* FIXME */
#define NR_SUPER_BLOCK 8


/* INODE::i_mode (octal, lower 12 bits reserved) */
#define I_TYPE_MASK     0170000
#define I_REGULAR       0100000
#define I_BLOCK_SPECIAL 0060000
#define I_DIRECTORY     0040000
#define I_CHAR_SPECIAL  0020000
#define I_NAMED_PIPE 0010000

#define is_special(m) ((((m) & I_TYPE_MASK) == I_BLOCK_SPECIAL) || \
    (((m) & I_TYPE_MASK) == I_CHAR_SPECIAL))

#define NR_DEFAULT_FILE_SECTS 2048 /* 2048 * 512 = 1MB */



#endif /* _ORANGES_CONST_H_ */

include/sys/proc.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                               proc.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/


struct stackframe { /* proc_ptr points here    ↑ Low   */
 u32 gs;  /* ┓      │   */
 u32 fs;  /* ┃      │   */
 u32 es;  /* ┃      │   */
 u32 ds;  /* ┃      │   */
 u32 edi;  /* ┃      │   */
 u32 esi;  /* ┣ pushed by save()    │   */
 u32 ebp;  /* ┃      │   */
 u32 kernel_esp; /* <- 'popad' will ignore it   │   */
 u32 ebx;  /* ┃      ↑棧從高地址往低地址增長*/
 u32 edx;  /* ┃      │   */
 u32 ecx;  /* ┃      │   */
 u32 eax;  /* ┛      │   */
 u32 retaddr; /* return address for assembly code save() │   */
 u32 eip;  /*  ┓      │   */
 u32 cs;  /*  ┃      │   */
 u32 eflags;  /*  ┣ these are pushed by CPU during interrupt │   */
 u32 esp;  /*  ┃      │   */
 u32 ss;  /*  ┛      ┷High   */
};


struct proc {
 struct stackframe regs;    /* process registers saved in stack frame */

 u16 ldt_sel;               /* gdt selector giving ldt base and limit */
 struct descriptor ldts[LDT_SIZE]; /* local descs for code and data */

        int ticks;                 /* remained ticks */
        int priority;

 u32 pid;                   /* process id passed in from MM */
 char name[16];     /* name of the process */

 int  p_flags;              /**
        * process flags.
        * A proc is runnable iff p_flags==0
        */

 MESSAGE * p_msg;
 int p_recvfrom;
 int p_sendto;

 int has_int_msg;           /**
        * nonzero if an INTERRUPT occurred when
        * the task is not ready to deal with it.
        */

 struct proc * q_sending;   /**
        * queue of procs sending messages to
        * this proc
        */
 struct proc * next_sending;/**
        * next proc in the sending
        * queue (q_sending)
        */

 int nr_tty;

 struct file_desc * filp[NR_FILES];
};

struct task {
 task_f initial_eip;
 int stacksize;
 char name[32];
};

#define proc2pid(x) (x - proc_table)

/* Number of tasks & procs */
#define NR_TASKS 4
#define NR_PROCS 3
#define FIRST_PROC proc_table[0]
#define LAST_PROC proc_table[NR_TASKS + NR_PROCS - 1]

/* stacks of tasks */
#define STACK_SIZE_TTY  0x8000
#define STACK_SIZE_SYS  0x8000
#define STACK_SIZE_HD  0x8000
#define STACK_SIZE_FS  0x8000
#define STACK_SIZE_TESTA 0x8000
#define STACK_SIZE_TESTB 0x8000
#define STACK_SIZE_TESTC 0x8000

#define STACK_SIZE_TOTAL (STACK_SIZE_TTY + \
    STACK_SIZE_SYS + \
    STACK_SIZE_HD + \
    STACK_SIZE_FS + \
    STACK_SIZE_TESTA + \
    STACK_SIZE_TESTB + \
    STACK_SIZE_TESTC)

include/sys/proto.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            proto.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/* kliba.asm */
PUBLIC void out_byte(u16 port, u8 value);
PUBLIC u8 in_byte(u16 port);
PUBLIC void disp_str(char * info);
PUBLIC void disp_color_str(char * info, int color);
PUBLIC void disable_irq(int irq);
PUBLIC void enable_irq(int irq);
PUBLIC void disable_int();
PUBLIC void enable_int();
PUBLIC void port_read(u16 port, void* buf, int n);
PUBLIC void port_write(u16 port, void* buf, int n);
PUBLIC void glitter(int row, int col);

/* string.asm */
PUBLIC char* strcpy(char* dst, const char* src);

/* protect.c */
PUBLIC void init_prot();
PUBLIC u32 seg2phys(u16 seg);

/* klib.c */
PUBLIC void delay(int time);
PUBLIC void disp_int(int input);
PUBLIC char * itoa(char * str, int num);

/* kernel.asm */
PUBLIC void restart();

/* main.c */
PUBLIC int  get_ticks();
PUBLIC void TestA();
PUBLIC void TestB();
PUBLIC void TestC();
PUBLIC void panic(const char *fmt, ...);

/* i8259.c */
PUBLIC void init_8259A();
PUBLIC void put_irq_handler(int irq, irq_handler handler);
PUBLIC void spurious_irq(int irq);

/* clock.c */
PUBLIC void clock_handler(int irq);
PUBLIC void init_clock();
PUBLIC void milli_delay(int milli_sec);

/* kernel/hd.c */
PUBLIC void task_hd();
PUBLIC void hd_handler(int irq);

/* keyboard.c */
PUBLIC void init_keyboard();
PUBLIC void keyboard_read(TTY* p_tty);

/* tty.c */
PUBLIC void task_tty();
PUBLIC void in_process(TTY* p_tty, u32 key);

/* systask.c */
PUBLIC void task_sys();

/* fs/main.c */
PUBLIC void   task_fs();
PUBLIC int   rw_sector(int io_type, int dev, u64 pos,
       int bytes, int proc_nr, void * buf);
PUBLIC struct inode *  get_inode(int dev, int num);
PUBLIC void   put_inode(struct inode * pinode);
PUBLIC void   sync_inode(struct inode * p);
PUBLIC struct super_block * get_super_block(int dev);

/* fs/open.c */
PUBLIC int  do_open();
PUBLIC int  do_close();

/* fs/read_write.c */
PUBLIC int  do_rdwt();

/* fs/link.c */
PUBLIC int  do_unlink();

/* fs/misc.c */
PUBLIC int  do_stat();
PUBLIC int  strip_path(char * filename, const char * pathname,
       struct inode** ppinode);
PUBLIC int  search_file(char * path);

/* fs/disklog.c */
PUBLIC int  do_disklog();
PUBLIC int  disklog(char * logstr); /* for debug */
PUBLIC void  dump_fd_graph(const char * fmt, ...);

/* console.c */
PUBLIC void out_char(CONSOLE* p_con, char ch);
PUBLIC void scroll_screen(CONSOLE* p_con, int direction);
PUBLIC void select_console(int nr_console);
PUBLIC void init_screen(TTY* p_tty);
PUBLIC int  is_current_console(CONSOLE* p_con);

/* printf.c */
PUBLIC  int     printf(const char *fmt, ...);
#define printl printf

/* vsprintf.c */
PUBLIC  int     vsprintf(char *buf, const char *fmt, va_list args);
PUBLIC int sprintf(char *buf, const char *fmt, ...);

/* proc.c */
PUBLIC void schedule();
PUBLIC void* va2la(int pid, void* va);
PUBLIC int ldt_seg_linear(struct proc* p, int idx);
PUBLIC void reset_msg(MESSAGE* p);
PUBLIC void dump_msg(const char * title, MESSAGE* m);
PUBLIC void dump_proc(struct proc * p);
PUBLIC int send_recv(int function, int src_dest, MESSAGE* msg);
PUBLIC void inform_int(int task_nr);

/* lib/misc.c */
PUBLIC void spin(char * func_name);

/* 以下是系統調用相關 */

/* 系統調用 - 系統級 */
/* proc.c */
PUBLIC int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p);
PUBLIC int sys_printx(int _unused1, int _unused2, char* s, struct proc * p_proc);

/* syscall.asm */
PUBLIC  void    sys_call();             /* int_handler */

/* 系統調用 - 用戶級 */
PUBLIC int sendrec(int function, int src_dest, MESSAGE* p_msg);
PUBLIC int printx(char* str);

kernel/main.c

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            main.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"


/*======================================================================*
                            kernel_main
 *======================================================================*/
PUBLIC int kernel_main()
{
 disp_str("-----\"kernel_main\" begins-----\n");

 struct task* p_task;
 struct proc* p_proc= proc_table;
 char* p_task_stack = task_stack + STACK_SIZE_TOTAL;
 u16   selector_ldt = SELECTOR_LDT_FIRST;
        u8    privilege;
        u8    rpl;
 int   eflags;
 int   i, j;
 int   prio;
 for (i = 0; i < NR_TASKS+NR_PROCS; i++) {
         if (i < NR_TASKS) {     /* 任務 */
                        p_task    = task_table + i;
                        privilege = PRIVILEGE_TASK;
                        rpl       = RPL_TASK;
                        eflags    = 0x1202; /* IF=1, IOPL=1, bit 2 is always 1 */
   prio      = 15;
                }
                else {                  /* 用戶進程 */
                        p_task    = user_proc_table + (i - NR_TASKS);
                        privilege = PRIVILEGE_USER;
                        rpl       = RPL_USER;
                        eflags    = 0x202; /* IF=1, bit 2 is always 1 */
   prio      = 5;
                }

  strcpy(p_proc->name, p_task->name); /* name of the process */
  p_proc->pid = i;   /* pid */

  p_proc->ldt_sel = selector_ldt;

  memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3],
         sizeof(struct descriptor));
  p_proc->ldts[0].attr1 = DA_C | privilege << 5;
  memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3],
         sizeof(struct descriptor));
  p_proc->ldts[1].attr1 = DA_DRW | privilege << 5;
  p_proc->regs.cs = (0 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.ds = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.es = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.fs = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.ss = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | rpl;

  p_proc->regs.eip = (u32)p_task->initial_eip;
  p_proc->regs.esp = (u32)p_task_stack;
  p_proc->regs.eflags = eflags;

  p_proc->nr_tty  = 0;

  p_proc->p_flags = 0;
  p_proc->p_msg = 0;
  p_proc->p_recvfrom = NO_TASK;
  p_proc->p_sendto = NO_TASK;
  p_proc->has_int_msg = 0;
  p_proc->q_sending = 0;
  p_proc->next_sending = 0;

  for (j = 0; j < NR_FILES; j++)
   p_proc->filp[j] = 0;

  p_proc->ticks = p_proc->priority = prio;

  p_task_stack -= p_task->stacksize;
  p_proc++;
  p_task++;
  selector_ldt += 1 << 3;
 }

        proc_table[NR_TASKS + 0].nr_tty = 0;
        proc_table[NR_TASKS + 1].nr_tty = 1;
        proc_table[NR_TASKS + 2].nr_tty = 1;

 k_reenter = 0;
 ticks = 0;

 p_proc_ready = proc_table;

 init_clock();
        init_keyboard();

 restart();

 while(1){}
}


/*****************************************************************************
 *                                get_ticks
 *****************************************************************************/
PUBLIC int get_ticks()
{
 MESSAGE msg;
 reset_msg(&msg);
 msg.type = GET_TICKS;
 send_recv(BOTH, TASK_SYS, &msg);
 return msg.RETVAL;
}


/*======================================================================*
                               TestA
 *======================================================================*/
void TestA()
{
 int fd;
 int i, n;

 char filename[MAX_FILENAME_LEN+1] = "blah";
 const char bufw[] = "abcde";
 const int rd_bytes = 3;
 char bufr[rd_bytes];

 assert(rd_bytes <= strlen(bufw));

 /* create */
 fd = open(filename, O_CREAT | O_RDWR);
 assert(fd != -1);
 printf("File created: %s (fd %d)\n", filename, fd);

 /* write */
 n = write(fd, bufw, strlen(bufw));
 assert(n == strlen(bufw));

 /* close */
 close(fd);

 /* open */
 fd = open(filename, O_RDWR);
 assert(fd != -1);
 printf("File opened. fd: %d\n", fd);

 /* read */
 n = read(fd, bufr, rd_bytes);
 assert(n == rd_bytes);
 bufr[n] = 0;
 printf("%d bytes read: %s\n", n, bufr);

 /* close */
 close(fd);

 char * filenames[] = {"/foo", "/bar", "/baz"};

 /* create files */
 for (i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) {
  fd = open(filenames[i], O_CREAT | O_RDWR);
  assert(fd != -1);
  printf("File created: %s (fd %d)\n", filenames[i], fd);
  close(fd);
 }

 char * rfilenames[] = {"/bar", "/foo", "/baz", "/dev_tty0"};

 /* remove files */
 for (i = 0; i < sizeof(rfilenames) / sizeof(rfilenames[0]); i++) {
  if (unlink(rfilenames[i]) == 0)
   printf("File removed: %s\n", rfilenames[i]);
  else
   printf("Failed to remove file: %s\n", rfilenames[i]);
 }

 spin("TestA");
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestB()
{
 while(1){
  printf("B");
  milli_delay(200);
 }
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestC()
{
 /* assert(0); */
 while(1){
  printf("C");
  milli_delay(200);
 }
}

/*****************************************************************************
 *                                panic
 *****************************************************************************/
PUBLIC void panic(const char *fmt, ...)
{
 int i;
 char buf[256];

 /* 4 is the size of fmt in the stack */
 va_list arg = (va_list)((char*)&fmt + 4);

 i = vsprintf(buf, fmt, arg);

 printl("%c !!panic!! %s", MAG_CH_PANIC, buf);

 /* should never arrive here */
 __asm__ __volatile__("ud2");
}

kernel/systask.c

/*************************************************************************//**
 *****************************************************************************
 * @file   systask.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2007
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"


/*****************************************************************************
 *                                task_sys
 *****************************************************************************/
/**
 * <Ring 1> The main loop of TASK SYS.
 *
 *****************************************************************************/
PUBLIC void task_sys()
{
 MESSAGE msg;
 while (1) {
  send_recv(RECEIVE, ANY, &msg);
  int src = msg.source;

  switch (msg.type) {
  case GET_TICKS:
   msg.RETVAL = ticks;
   send_recv(SEND, src, &msg);
   break;
  case GET_PID:
   msg.type = SYSCALL_RET;
   msg.PID = src;
   send_recv(SEND, src, &msg);
   break;
  default:
   panic("unknown msg type");
   break;
  }
 }
}

lib/close.c

/*************************************************************************//**
 *****************************************************************************
 * @file   close.c
 * @brief  
 * @author Forrest Y. Yu
 * @date   Mon Apr 21 17:08:19 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

/*****************************************************************************
 *                                close
 *****************************************************************************/
/**
 * Close a file descriptor.
 * 
 * @param fd  File descriptor.
 * 
 * @return Zero if successful, otherwise -1.
 *****************************************************************************/
PUBLIC int close(int fd)
{
 MESSAGE msg;
 msg.type   = CLOSE;
 msg.FD     = fd;

 send_recv(BOTH, TASK_FS, &msg);

 return msg.RETVAL;
}

lib/getpid.c

/*************************************************************************//**
 *****************************************************************************
 * @file   getpid.c
 * @brief  getpid()
 * @author Forrest Y. Yu
 * @date   Mon Nov 10 19:04:07 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"


/*****************************************************************************
 *                                getpid
 *****************************************************************************/
/**
 * Get the PID.
 * 
 * @return The PID.
 *****************************************************************************/
PUBLIC int getpid()
{
 MESSAGE msg;
 msg.type = GET_PID;

 send_recv(BOTH, TASK_SYS, &msg);
 assert(msg.type == SYSCALL_RET);

 return msg.PID;
}

lib/misc.c

/*************************************************************************//**
 *****************************************************************************
 * @file   misc.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

/*****************************************************************************
 *                                memcmp
 *****************************************************************************/
/**
 * Compare memory areas.
 *
 * @param s1  The 1st area.
 * @param s2  The 2nd area.
 * @param n   The first n bytes will be compared.
 *
 * @return  an integer less than, equal to, or greater than zero if the first
 *          n bytes of s1 is found, respectively, to be less than, to match,
 *          or  be greater than the first n bytes of s2.
 *****************************************************************************/
PUBLIC int memcmp(const void * s1, const void *s2, int n)
{
 if ((s1 == 0) || (s2 == 0)) { /* for robustness */
  return (s1 - s2);
 }

 const char * p1 = (const char *)s1;
 const char * p2 = (const char *)s2;
 int i;
 for (i = 0; i < n; i++,p1++,p2++) {
  if (*p1 != *p2) {
   return (*p1 - *p2);
  }
 }
 return 0;
}

/*****************************************************************************
 *                                strcmp
 *****************************************************************************/
/**
 * Compare two strings.
 *
 * @param s1  The 1st string.
 * @param s2  The 2nd string.
 *
 * @return  an integer less than, equal to, or greater than zero if s1 (or the
 *          first n bytes thereof) is  found,  respectively,  to  be less than,
 *          to match, or be greater than s2.
 *****************************************************************************/
PUBLIC int strcmp(const char * s1, const char *s2)
{
 if ((s1 == 0) || (s2 == 0)) { /* for robustness */
  return (s1 - s2);
 }

 const char * p1 = s1;
 const char * p2 = s2;

 for (; *p1 && *p2; p1++,p2++) {
  if (*p1 != *p2) {
   break;
  }
 }

 return (*p1 - *p2);
}

/*****************************************************************************
 *                                strcat
 *****************************************************************************/
/**
 * Concatenate two strings.
 *
 * @param s1  The 1st string.
 * @param s2  The 2nd string.
 *
 * @return  Ptr to the 1st string.
 *****************************************************************************/
PUBLIC char * strcat(char * s1, const char *s2)
{
 if ((s1 == 0) || (s2 == 0)) { /* for robustness */
  return 0;
 }

 char * p1 = s1;
 for (; *p1; p1++) {}

 const char * p2 = s2;
 for (; *p2; p1++,p2++) {
  *p1 = *p2;
 }
 *p1 = 0;

 return s1;
}

/*****************************************************************************
 *                                spin
 *****************************************************************************/
PUBLIC void spin(char * func_name)
{
 printl("\nspinning in %s ...\n", func_name);
 while (1) {}
}


/*****************************************************************************
 *                           assertion_failure
 *************************************************************************//**
 * Invoked by assert().
 *
 * @param exp       The failure expression itself.
 * @param file      __FILE__
 * @param base_file __BASE_FILE__
 * @param line      __LINE__
 *****************************************************************************/
PUBLIC void assertion_failure(char *exp, char *file, char *base_file, int line)
{
 printl("%c  assert(%s) failed: file: %s, base_file: %s, ln%d",
        MAG_CH_ASSERT,
        exp, file, base_file, line);

 /**
  * If assertion fails in a TASK, the system will halt before
  * printl() returns. If it happens in a USER PROC, printl() will
  * return like a common routine and arrive here.
  * @see sys_printx()
  *
  * We use a forever loop to prevent the proc from going on:
  */
 spin("assertion_failure()");

 /* should never arrive here */
        __asm__ __volatile__("ud2");
}

lib/open.c

/*************************************************************************//**
 *****************************************************************************
 * @file   open.c
 * @brief  open()
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

/*****************************************************************************
 *                                open
 *****************************************************************************/
/**
 * open/create a file.
 *
 * @param pathname  The full path of the file to be opened/created.
 * @param flags     O_CREAT, O_RDWR, etc.
 *
 * @return File descriptor if successful, otherwise -1.
 *****************************************************************************/
PUBLIC int open(const char *pathname, int flags)
{
 MESSAGE msg;

 msg.type = OPEN;

 msg.PATHNAME = (void*)pathname;
 msg.FLAGS = flags;
 msg.NAME_LEN = strlen(pathname);

 send_recv(BOTH, TASK_FS, &msg);
 assert(msg.type == SYSCALL_RET);

 return msg.FD;
}

lib/printf.c

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                              printf.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

/******************************************************************************************
                        可變參數函數調用原理(其中涉及的數字皆為舉例)
===========================================================================================

i = 0x23;
j = 0x78;
char fmt[] = "%x%d";
printf(fmt, i, j);

        push    j
        push    i
        push    fmt
        call    printf
        add     esp, 3 * 4


                ┃        HIGH        ┃                        ┃        HIGH        ┃
                ┃        ...         ┃                        ┃        ...         ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
                ┃                    ┃                 0x32010┃        '\0'        ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
         0x3046C┃        0x78        ┃                 0x3200c┃         d          ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
   arg = 0x30468┃        0x23        ┃                 0x32008┃         %          ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
         0x30464┃      0x32000 ───╂────┐       0x32004┃         x          ┃
                ┣━━━━━━━━━━┫        │              ┣━━━━━━━━━━┫
                ┃                    ┃        └──→ 0x32000┃         %          ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
                ┃        ...         ┃                        ┃        ...         ┃
                ┃        LOW         ┃                        ┃        LOW         ┃

實際上,調用 vsprintf 的情形是這樣的:

        vsprintf(buf, 0x32000, 0x30468);

******************************************************************************************/

/*======================================================================*
                                 printf
 *======================================================================*/
int printf(const char *fmt, ...)
{
 int i;
 char buf[256];

 va_list arg = (va_list)((char*)(&fmt) + 4); /*4是參數fmt所占堆棧中的大小*/
 i = vsprintf(buf, fmt, arg);
 buf[i] = 0;
 printx(buf);

 return i;
}

lib/read.c

/*************************************************************************//**
 *****************************************************************************
 * @file   read.c
 * @brief  read()
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

/*****************************************************************************
 *                                read
 *****************************************************************************/
/**
 * Read from a file descriptor.
 *
 * @param fd     File descriptor.
 * @param buf    Buffer to accept the bytes read.
 * @param count  How many bytes to read.
 *
 * @return  On success, the number of bytes read are returned.
 *          On error, -1 is returned.
 *****************************************************************************/
PUBLIC int read(int fd, void *buf, int count)
{
 MESSAGE msg;
 msg.type = READ;
 msg.FD   = fd;
 msg.BUF  = buf;
 msg.CNT  = count;

 send_recv(BOTH, TASK_FS, &msg);

 return msg.CNT;
}

lib/syscall.asm

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                               syscall.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                     Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

%include "sconst.inc"

INT_VECTOR_SYS_CALL equ 0x90
_NR_printx     equ 0
_NR_sendrec     equ 1

; 導出符號
global printx
global sendrec

bits 32
[section .text]

; ====================================================================================
;                  sendrec(int function, int src_dest, MESSAGE* msg);
; ====================================================================================
; Never call sendrec() directly, call send_recv() instead.
sendrec:
 mov eax, _NR_sendrec
 mov ebx, [esp + 4] ; function
 mov ecx, [esp + 8] ; src_dest
 mov edx, [esp + 12] ; p_msg
 int INT_VECTOR_SYS_CALL
 ret

; ====================================================================================
;                          void printx(char* s);
; ====================================================================================
printx:
 mov eax, _NR_printx
 mov edx, [esp + 4]
 int INT_VECTOR_SYS_CALL
 ret

lib/syslog.c

/*************************************************************************//**
 *****************************************************************************
 * @file   syslog.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Thu Nov 20 17:02:42 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"


/*****************************************************************************
 *                                syslog
 *****************************************************************************/
/**
 * Write log directly to the disk by sending message to FS.
 *
 * @param fmt The format string.
 *
 * @return How many chars have been printed.
 *****************************************************************************/
PUBLIC int syslog(const char *fmt, ...)
{
 int i;
 char buf[STR_DEFAULT_LEN];

 va_list arg = (va_list)((char*)(&fmt) + 4); /**
           * 4: size of `fmt' in
           *    the stack
           */
 i = vsprintf(buf, fmt, arg);
 assert(strlen(buf) == i);

 if (getpid() == TASK_FS) { /* in FS */
  return disklog(buf);
 }
 else {   /* any proc which is not FS */
  MESSAGE msg;
  msg.type = DISK_LOG;
  msg.BUF= buf;
  msg.CNT = i;
  send_recv(BOTH, TASK_FS, &msg);
  if (i != msg.CNT) {
   panic("failed to write log");
  }

  return msg.RETVAL;
 }
}

lib/unlink.c

/*************************************************************************//**
 *****************************************************************************
 * @file   unlink.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Tue Jun  3 16:12:05 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

/*****************************************************************************
 *                                unlink
 *****************************************************************************/
/**
 * Delete a file.
 *
 * @param pathname  The full path of the file to delete.
 *
 * @return Zero if successful, otherwise -1.
 *****************************************************************************/
PUBLIC int unlink(const char * pathname)
{
 MESSAGE msg;
 msg.type   = UNLINK;

 msg.PATHNAME = (void*)pathname;
 msg.NAME_LEN = strlen(pathname);

 send_recv(BOTH, TASK_FS, &msg);

 return msg.RETVAL;
}

lib/vsprintf.c

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                              vsprintf.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"



/*======================================================================*
                                i2a
 *======================================================================*/
PRIVATE char* i2a(int val, int base, char ** ps)
{
 int m = val % base;
 int q = val / base;
 if (q) {
  i2a(q, base, ps);
 }
 *(*ps)++ = (m < 10) ? (m + '0') : (m - 10 + 'A');

 return *ps;
}


/*======================================================================*
                                vsprintf
 *======================================================================*/
/*
 *  為更好地理解此函數的原理,可參考 printf 的注釋部分。
 */
PUBLIC int vsprintf(char *buf, const char *fmt, va_list args)
{
 char* p;

 va_list p_next_arg = args;
 int m;

 char inner_buf[STR_DEFAULT_LEN];
 char cs;
 int align_nr;

 for (p=buf;*fmt;fmt++) {
  if (*fmt != '%') {
   *p++ = *fmt;
   continue;
  }
  else {  /* a format string begins */
   align_nr = 0;
  }

  fmt++;

  if (*fmt == '%') {
   *p++ = *fmt;
   continue;
  }
  else if (*fmt == '0') {
   cs = '0';
   fmt++;
  }
  else {
   cs = ' ';
  }
  while (((unsigned char)(*fmt) >= '0') && ((unsigned char)(*fmt) <= '9')) {
   align_nr *= 10;
   align_nr += *fmt - '0';
   fmt++;
  }

  char * q = inner_buf;
  memset(q, 0, sizeof(inner_buf));

  switch (*fmt) {
  case 'c':
   *q++ = *((char*)p_next_arg);
   p_next_arg += 4;
   break;
  case 'x':
   m = *((int*)p_next_arg);
   i2a(m, 16, &q);
   p_next_arg += 4;
   break;
  case 'd':
   m = *((int*)p_next_arg);
   if (m < 0) {
    m = m * (-1);
    *q++ = '-';
   }
   i2a(m, 10, &q);
   p_next_arg += 4;
   break;
  case 's':
   strcpy(q, (*((char**)p_next_arg)));
   q += strlen(*((char**)p_next_arg));
   p_next_arg += 4;
   break;
  default:
   break;
  }

  int k;
  for (k = 0; k < ((align_nr > strlen(inner_buf)) ? (align_nr - strlen(inner_buf)) : 0); k++) {
   *p++ = cs;
  }
  q = inner_buf;
  while (*q) {
   *p++ = *q++;
  }
 }

 *p = 0;

 return (p - buf);
}


/*======================================================================*
                                 sprintf
 *======================================================================*/
int sprintf(char *buf, const char *fmt, ...)
{
 va_list arg = (va_list)((char*)(&fmt) + 4);        /* 4 是參數 fmt 所占堆棧中的大小 */
 return vsprintf(buf, fmt, arg);
}

lib/write.c

/*************************************************************************//**
 *****************************************************************************
 * @file   write.c
 * @brief  write()
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

/*****************************************************************************
 *                                write
 *****************************************************************************/
/**
 * Write to a file descriptor.
 *
 * @param fd     File descriptor.
 * @param buf    Buffer including the bytes to write.
 * @param count  How many bytes to write.
 *
 * @return  On success, the number of bytes written are returned.
 *          On error, -1 is returned.
 *****************************************************************************/
PUBLIC int write(int fd, const void *buf, int count)
{
 MESSAGE msg;
 msg.type = WRITE;
 msg.FD   = fd;
 msg.BUF  = (void*)buf;
 msg.CNT  = count;

 send_recv(BOTH, TASK_FS, &msg);

 return msg.CNT;
}

include/sys/config.h

#define ENABLE_DISK_LOG       // 以下新增
#define SET_LOG_SECT_SMAP_AT_STARTUP
#define MEMSET_LOG_SECTS
#define NR_SECTS_FOR_LOG  NR_DEFAULT_FILE_SECTS

include/sys/global.h

EXTERN struct file_desc f_desc_table[NR_FILE_DESC];    // 以下新增
EXTERN struct inode  inode_table[NR_INODE];
EXTERN struct super_block super_block[NR_SUPER_BLOCK];
EXTERN MESSAGE   fs_msg;
EXTERN struct proc *  pcaller;
EXTERN struct inode *  root_inode;

include/sys/hd.h

// 新增
#define MAX_IO_BYTES 256 /* how many sectors does one IO can handle */

include/string.h

PUBLIC int strlen(char* p_str);   // 刪除

PUBLIC int strlen(const char* p_str);   // 以下新增
PUBLIC int memcmp(const void * s1, const void *s2, int n);
PUBLIC int strcmp(const char * s1, const char *s2);
PUBLIC char* strcat(char * s1, const char *s2);

include/type.h

// 以下新增
/* routine types */
#define PUBLIC  /* PUBLIC is the opposite of PRIVATE */
#define PRIVATE static /* PRIVATE x limits the scope of x */

kernel/clock.c, kernel/console.c, kernel/global.c, kernel/hd.c, kernel/i8259.c, kernel/keyboard.c, kernel/proc.c, kernel/protect.c, kernel/start.c, kernel/tty.c, lib/klib.c

#include "stdio.h"    // 新增

刪除檔案:kernel/printf.c, kernel/syscall.asm, kernel/vsprintf.c

執行結果

留言

這個網誌中的熱門文章

用 C# 批次控制 Word 合併列印

前由 我有全區的電話資料,問題在於我要依不同里別來製作出電話簿。結果如下圖: 單純採用合併列印無法達成我的需求。解決方法係用「功能變數」儲存上一個里別,與現在里別進行比較:若不同,則換頁。不過,這樣功能變數還蠻長的。最後,我還是採用 C# 來解決。 解決方案 用 C# 控制 WORD 中合併列印的「資料來源 Data Source」,給予不同里別的「sqlstatement」。迴圈處理不同的里別即可。但可預見其處理過程會很慢,不過還好,我可以不用在意它,有跑出結果即可。 程式碼 IList<string> areas = new List<string>() { "後壁", "侯伯", "嘉苳", "土溝", "嘉田", "嘉民", "菁豊", "崁頂", "後廍", "墨林", "菁寮", "新嘉", "頂長", "平安", "仕安", "竹新", "新東", "長安", "頂安", "福安", "烏樹" }; string root = @"D:\"; // 根目錄 string data = root + @"\data.docm"; // 資料檔(即資料來源) string template = root + @"\template.docx"; // 已設定好格式與合併欄位的 Word 檔 string output = @"d:\Final"; // 輸出之資料夾 object oMissing = System.Reflection.Missing.Va...

VLC c# 順利編譯

原文網址: http://www.cnblogs.com/haibindev/archive/2011/12/21/2296173.html 原文作者: haibindev 原文標題:c#万能视频播放器 本文的重點在於修正 class VlcPlayer,使其能順利在 VC# Express 2010 .Net Framework 4 下順利編譯。 修正重點在於 CallingConvention = CallingConvention. StdCall 改成 CallingConvention = CallingConvention. Cdecl using System; using System.Runtime.InteropServices; using System.Security; using System.Text; namespace VlcDotNet { class VlcPlayer { private IntPtr libvlc_instance_; private IntPtr libvlc_media_player_; private double duration_; public VlcPlayer(string pluginPath) { string plugin_arg = "--plugin-path=" + pluginPath; string[] arguments = { "-I", "dummy", "--ignore-config", "--no-video-title", plugin_arg }; libvlc_instance_ = LibVlcAPI.libvlc_new(arguments); libvlc_media_player_ = LibVlcAPI.libvlc_media_player_new(libvlc_instance_); } public ...

[Symfony+Doctrine] 透過非 Id 來使用 Pessimistic Lock

根據 文件 ,Doctrine 有 Pessimistic Lock,又分為兩種: LockMode::PESSIMISTIC_WRITE:對應至 MySQL 的 Select FOR UPDATE LockMode::PESSIMISTIC_READ:對應至 MySQL 的 Select LOCK IN SHARE MODE 差別在於 LOCK IN SHARE MODE 會將在 row 的資料鎖定(row-level lock),在非同一交易(Transaction)下,不給寫入,其他交易可以讀取, 且可以繼續 Select LOCK IN SHARE MODE 。而 FOR UPDATE 不僅鎖定該資料,在非同一交易下,不給寫入,其它交易可以讀取, 但不能 Select LOCK IN SHARE MODE 。MySQL 文件有更詳細的比較與情境使用的說明,參考 網址 。 現在問題是,我們要完全採用 ORM 來處理資料。Doctrine 的文件提到 EntityManager::find 可以採用 Pessimistic Lock, 但 find 是透過 id 來處理 。而其他 find 系列函數(包括:findAll, findBy, findOneBy)皆不支援 LockMode。 因此,勢必要有方法來「透過非 id 來使用 Pessimistic Lock」。透過查看原始碼,簡單的方法是有的,解法之範例如下: 19 public function depositAction() 20 { 21 22 $em = $this->getDoctrine()->getManager(); 23 24 $em->transactional(function ($em) { 25 $entityName = 'AcmeTrainingBundle:Account'; 26 $lockMode = LockMode::PESSIMISTIC_READ; 27 $orderBy = null; 28 $...