This commit is contained in:
201850075 2022-12-28 17:16:30 +08:00
parent 7166483892
commit 93a432ec36
29 changed files with 3938 additions and 2 deletions

113
Makefile Normal file
View File

@ -0,0 +1,113 @@
#########################
# Makefile for Orange'S #
#########################
# Entry point of Orange'S
# It must have the same value with 'KernelEntryPointPhyAddr' in load.inc!
ENTRYPOINT = 0x30400
# Offset of entry point in kernel file
# It depends on ENTRYPOINT
ENTRYOFFSET = 0x400
# Programs, flags, etc.
ASM = nasm
DASM = ndisasm
CC = gcc
LD = ld
ASMBFLAGS = -I boot/include/
ASMKFLAGS = -I include/ -f elf32
CFLAGS = -I include/ -c -fno-builtin -m32 -fno-stack-protector -std=c99
LDFLAGS = -s -Ttext $(ENTRYPOINT) -m elf_i386
DASMFLAGS = -u -o $(ENTRYPOINT) -e $(ENTRYOFFSET)
# This Program
ORANGESBOOT = boot/boot.bin boot/loader.bin
ORANGESKERNEL = kernel.bin
OBJS = kernel/kernel.o kernel/syscall.o kernel/start.o kernel/main.o kernel/clock.o\
kernel/i8259.o kernel/global.o kernel/protect.o kernel/proc.o\
lib/kliba.o lib/klib.o lib/string.o
DASMOUTPUT = kernel.bin.asm
# All Phony Targets
.PHONY : everything final image clean realclean disasm all buildimg
# Default starting position
nop :
@echo "why not \`make image' huh? :)"
everything : $(ORANGESBOOT) $(ORANGESKERNEL)
all : realclean everything
image : realclean everything clean buildimg
run:
make image
clean :
rm -f $(OBJS)
realclean :
rm -f $(OBJS) $(ORANGESBOOT) $(ORANGESKERNEL)
disasm :
$(DASM) $(DASMFLAGS) $(ORANGESKERNEL) > $(DASMOUTPUT)
# We assume that "a.img" exists in current folder
buildimg :
dd if=boot/boot.bin of=a.img bs=512 count=1 conv=notrunc
sudo mount -o loop a.img /home/lighthouse/tmp/mount
sudo cp -fv boot/loader.bin /home/lighthouse/tmp/mount
sudo cp -fv kernel.bin /home/lighthouse/tmp/mount
sudo umount /home/lighthouse/tmp/mount
boot/boot.bin : boot/boot.asm boot/include/load.inc boot/include/fat12hdr.inc
$(ASM) $(ASMBFLAGS) -o $@ $<
boot/loader.bin : boot/loader.asm boot/include/load.inc boot/include/fat12hdr.inc boot/include/pm.inc
$(ASM) $(ASMBFLAGS) -o $@ $<
$(ORANGESKERNEL) : $(OBJS)
$(LD) $(LDFLAGS) -o $(ORANGESKERNEL) $(OBJS)
kernel/kernel.o : kernel/kernel.asm include/sconst.inc
$(ASM) $(ASMKFLAGS) -o $@ $<
kernel/syscall.o : kernel/syscall.asm include/sconst.inc
$(ASM) $(ASMKFLAGS) -o $@ $<
kernel/start.o: kernel/start.c include/type.h include/const.h include/protect.h include/string.h include/proc.h include/proto.h \
include/global.h
$(CC) $(CFLAGS) -o $@ $<
kernel/main.o: kernel/main.c include/type.h include/const.h include/protect.h include/string.h include/proc.h include/proto.h \
include/global.h
$(CC) $(CFLAGS) -o $@ $<
kernel/clock.o: kernel/clock.c
$(CC) $(CFLAGS) -o $@ $<
kernel/i8259.o: kernel/i8259.c include/type.h include/const.h include/protect.h include/proto.h
$(CC) $(CFLAGS) -o $@ $<
kernel/global.o: kernel/global.c include/type.h include/const.h include/protect.h include/proc.h \
include/global.h include/proto.h
$(CC) $(CFLAGS) -o $@ $<
kernel/protect.o: kernel/protect.c include/type.h include/const.h include/protect.h include/proc.h include/proto.h \
include/global.h
$(CC) $(CFLAGS) -o $@ $<
kernel/proc.o: kernel/proc.c
$(CC) $(CFLAGS) -o $@ $<
lib/klib.o: lib/klib.c include/type.h include/const.h include/protect.h include/string.h include/proc.h include/proto.h \
include/global.h
$(CC) $(CFLAGS) -o $@ $<
lib/kliba.o : lib/kliba.asm
$(ASM) $(ASMKFLAGS) -o $@ $<
lib/string.o : lib/string.asm
$(ASM) $(ASMKFLAGS) -o $@ $<

View File

@ -1,2 +0,0 @@
# r

BIN
a.img Normal file

Binary file not shown.

25
bochsrc Normal file
View File

@ -0,0 +1,25 @@
###############################################################
# Configuration file for Bochs
###############################################################
# how much memory the emulated machine will have
megs: 32
# filename of ROM images
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/vgabios/vgabios.bin
# what disk images will be used
floppya: 1_44=a.img, status=inserted
# choose the boot disk.
boot: a
# where do we send log messages?
# log: bochsout.txt
# disable the mouse
mouse: enabled=0
# enable key mapping, using US layout as default.
keyboard_mapping: enabled=1, map=/usr/share/bochs/keymaps/x11-pc-us.map

288
boot/boot.asm Normal file
View File

@ -0,0 +1,288 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; 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, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx = BaseOfLoader:OffsetOfLoader
mov ax, [wSectorNo] ; ax <- Root Directory 中的某 Sector 号
mov cl, 1
call ReadSector
mov si, LoaderFileName ; ds:si -> "LOADER BIN"
mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100 = BaseOfLoader*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, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx = BaseOfLoader:OffsetOfLoader = BaseOfLoader * 10h + OffsetOfLoader
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 BaseOfLoader:OffsetOfLoader ; 这一句正式跳转到已加载到内存中的 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, BaseOfLoader ; ┓
sub ax, 0100h ; ┣ 在 BaseOfLoader 后面留出 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 = (BaseOfLoader - 100):00 = (BaseOfLoader - 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 ; 结束标志

43
boot/include/fat12hdr.inc Normal file
View File

@ -0,0 +1,43 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; fat12hdr.inc
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; FAT12 磁盘的头
; ----------------------------------------------------------------------
BS_OEMName DB 'ForrestY' ; OEM String, 必须 8 个字节
BPB_BytsPerSec DW 512 ; 每扇区字节数
BPB_SecPerClus DB 1 ; 每簇多少扇区
BPB_RsvdSecCnt DW 1 ; Boot 记录占用多少扇区
BPB_NumFATs DB 2 ; 共有多少 FAT
BPB_RootEntCnt DW 224 ; 根目录文件数最大值
BPB_TotSec16 DW 2880 ; 逻辑扇区总数
BPB_Media DB 0xF0 ; 媒体描述符
BPB_FATSz16 DW 9 ; 每FAT扇区数
BPB_SecPerTrk DW 18 ; 每磁道扇区数
BPB_NumHeads DW 2 ; 磁头数(面数)
BPB_HiddSec DD 0 ; 隐藏扇区数
BPB_TotSec32 DD 0 ; 如果 wTotalSectorCount 0 由这个值记录扇区数
BS_DrvNum DB 0 ; 中断 13 的驱动器号
BS_Reserved1 DB 0 ; 未使用
BS_BootSig DB 29h ; 扩展引导标记 (29h)
BS_VolID DD 0 ; 卷序列号
BS_VolLab DB 'OrangeS0.02'; 卷标, 必须 11 个字节
BS_FileSysType DB 'FAT12 ' ; 文件系统类型, 必须 8个字节
;------------------------------------------------------------------------
; -------------------------------------------------------------------------
; 基于 FAT12 头的一些常量定义,如果头信息改变,下面的常量可能也要做相应改变
; -------------------------------------------------------------------------
FATSz equ 9 ; BPB_FATSz16
RootDirSectors equ 14 ; 根目录占用空间: RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec 1)) / BPB_BytsPerSec; 但如果按照此公式代码过长
SectorNoOfRootDirectory equ 19 ; Root Directory 的第一个扇区号 = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz)
SectorNoOfFAT1 equ 1 ; FAT1 的第一个扇区号 = BPB_RsvdSecCnt
DeltaSectorNo equ 17 ; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2
; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo

24
boot/include/load.inc Normal file
View File

@ -0,0 +1,24 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; load.inc
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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

322
boot/include/pm.inc Normal file
View File

@ -0,0 +1,322 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; pm.inc
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; 描述符图示
; 图示一
;
; ------ ┏━━┳━━┓高地址
; 7
; ┣━━┫
;
; 字节 7
;
; ┣━━┫
; 0
; ------ ┣━━╋━━┫
; 7 G
; ┣━━╉──┨
; 6 D
; ┣━━╉──┨
; 5 0
; ┣━━╉──┨
; 4 AVL┃
; 字节 6 ┣━━╉──┨
; 3
; ┣━━┫
; 2
; ┣━━┫
; 1
; ┣━━┫
; 0
; ------ ┣━━╋━━┫
; 7 P
; ┣━━╉──┨
; 6
; ┣━━┫ DPL┃
; 5
; ┣━━╉──┨
; 4 S
; 字节 5 ┣━━╉──┨
; 3
; ┣━━┫ T
; 2 Y
; ┣━━┫ P
; 1 E
; ┣━━┫
; 0
; ------ ┣━━╋━━┫
; 23
; ┣━━┫
; 22
; ┣━━┫
;
; 字节
; 2, 3, 4
; ┣━━┫
; 1
; ┣━━┫
; 0
; ------ ┣━━╋━━┫
; 15
; ┣━━┫
; 14
; ┣━━┫
;
; 字节 0,1
;
; ┣━━┫
; 1
; ┣━━┫
; 0
; ------ ┗━━┻━━┛低地址
;
; 图示二
; 高地址………………………………………………………………………低地址
; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
; |7654321076543210765432107654321076543210765432107654321076543210| <- 8 字节
; |--------========--------========--------========--------========|
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓
; ┃31..24 (见下图) 段基址(23..0) 段界限(15..0)
;
; 基址2┃③│②│ ①┃基址1b│ 基址1a 段界限1
; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫
; %6 %5 %4 %3 %2 %1
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛
; \_________
; \__________________
; \________________________________________________
; \
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
; 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
; ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
; G D 0 AVL┃ 段界限 2 (19..16) P DPL S TYPE
; ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
; : 属性 2 : 段界限 2 : 属性1
; ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
; 高地址 低地址
;
;
; 说明:
;
; (1) P: 存在(Present)位。
; P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中;
; P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。
;
; (2) DPL: 表示描述符特权级(Descriptor Privilege level)共2位。它规定了所描述段的特权级用于特权检查以决定对该段能否访问。
;
; (3) S: 说明描述符的类型。
; 对于存储段描述符而言S=1,以区别与系统段描述符和门描述符(S=0)
;
; (4) TYPE: 说明存储段描述符所描述的存储段的具体属性。
;
;
; 数据段类型 类型值 说明
; ----------------------------------
; 0 只读
; 1 只读、已访问
; 2 /
; 3 /写、已访问
; 4 只读、向下扩展
; 5 只读、向下扩展、已访问
; 6 /写、向下扩展
; 7 /写、向下扩展、已访问
;
;
; 类型值 说明
; 代码段类型 ----------------------------------
; 8 只执行
; 9 只执行、已访问
; A 执行/
; B 执行/读、已访问
; C 只执行、一致码段
; D 只执行、一致码段、已访问
; E 执行/读、一致码段
; F 执行/读、一致码段、已访问
;
;
; 系统段类型 类型编码 说明
; ----------------------------------
; 0 <未定义>
; 1 可用286TSS
; 2 LDT
; 3 忙的286TSS
; 4 286调用门
; 5 任务门
; 6 286中断门
; 7 286陷阱门
; 8 未定义
; 9 可用386TSS
; A <未定义>
; B 忙的386TSS
; C 386调用门
; D <未定义>
; E 386中断门
; F 386陷阱门
;
; (5) G: 段界限粒度(Granularity)位。
; G=0 表示界限粒度为字节;
; G=1 表示界限粒度为4K 字节。
; 注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。
;
; (6) D: D位是一个很特殊的位在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同。
; 在描述可执行段的描述符中D位决定了指令使用的地址及操作数所默认的大小。
; D=1表示默认情况下指令使用32位地址及32位或8位操作数这样的代码段也称为32位代码段
; D=0 表示默认情况下使用16位地址及16位或8位操作数这样的代码段也称为16位代码段它与80286兼容。可以使用地址大小前缀和操作数大小前缀分别改变默认的地址或操作数的大小。
; 在向下扩展数据段的描述符中D位决定段的上部边界。
; D=1表示段的上部界限为4G
; D=0表示段的上部界限为64K这是为了与80286兼容。
; 在描述由SS寄存器寻址的段描述符中D位决定隐式的堆栈访问指令(如PUSH和POP指令)使用何种堆栈指针寄存器。
; D=1表示使用32位堆栈指针寄存器ESP
; D=0表示使用16位堆栈指针寄存器SP这与80286兼容。
;
; (7) AVL: 软件可利用位。80386对该位的使用未左规定Intel公司也保证今后开发生产的处理器只要与80386兼容就不会对该位的使用做任何定义或规定。
;
;----------------------------------------------------------------------------
; 描述符类型值说明
; 其中:
; DA_ : Descriptor Attribute
; D : 数据段
; C : 代码段
; S : 系统段
; R : 只读
; RW : 读写
; A : 已访问
; 其它 : 可按照字面意思理解
;----------------------------------------------------------------------------
DA_32 EQU 4000h ; 32 位段
DA_LIMIT_4K EQU 8000h ; 段界限粒度为 4K 字节
DA_DPL0 EQU 00h ; DPL = 0
DA_DPL1 EQU 20h ; DPL = 1
DA_DPL2 EQU 40h ; DPL = 2
DA_DPL3 EQU 60h ; DPL = 3
;----------------------------------------------------------------------------
; 存储段描述符类型值说明
;----------------------------------------------------------------------------
DA_DR EQU 90h ; 存在的只读数据段类型值
DA_DRW EQU 92h ; 存在的可读写数据段属性值
DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值
DA_C EQU 98h ; 存在的只执行代码段属性值
DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值
DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值
DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值
;----------------------------------------------------------------------------
; 系统段描述符类型值说明
;----------------------------------------------------------------------------
DA_LDT EQU 82h ; 局部描述符表段类型值
DA_TaskGate EQU 85h ; 任务门类型值
DA_386TSS EQU 89h ; 可用 386 任务状态段类型值
DA_386CGate EQU 8Ch ; 386 调用门类型值
DA_386IGate EQU 8Eh ; 386 中断门类型值
DA_386TGate EQU 8Fh ; 386 陷阱门类型值
;----------------------------------------------------------------------------
; 选择子图示:
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
; 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
; ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫
; 描述符索引 TI RPL
; ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
;
; RPL(Requested Privilege Level): 请求特权级,用于特权检查。
;
; TI(Table Indicator): 引用描述符表指示位
; TI=0 指示从全局描述符表GDT中读取描述符
; TI=1 指示从局部描述符表LDT中读取描述符。
;
;----------------------------------------------------------------------------
; 选择子类型值说明
; 其中:
; SA_ : Selector Attribute
SA_RPL0 EQU 0 ;
SA_RPL1 EQU 1 ; RPL
SA_RPL2 EQU 2 ;
SA_RPL3 EQU 3 ;
SA_TIG EQU 0 ; ┓TI
SA_TIL EQU 4 ;
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
; 分页机制使用的常量说明
;----------------------------------------------------------------------------
PG_P EQU 1 ; 页存在属性位
PG_RWR EQU 0 ; R/W 属性位值, /执行
PG_RWW EQU 2 ; R/W 属性位值, //执行
PG_USS EQU 0 ; U/S 属性位值, 系统级
PG_USU EQU 4 ; U/S 属性位值, 用户级
;----------------------------------------------------------------------------
; =========================================
; FLAGS - Intel 8086 Family Flags Register
; =========================================
;
; |11|10|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|
; | | | | | | | | | | | | | | | | | '--- CF……Carry Flag
; | | | | | | | | | | | | | | | | '--- 1
; | | | | | | | | | | | | | | | '--- PF……Parity Flag
; | | | | | | | | | | | | | | '--- 0
; | | | | | | | | | | | | | '--- AF……Auxiliary Flag
; | | | | | | | | | | | | '--- 0
; | | | | | | | | | | | '--- ZF……Zero Flag
; | | | | | | | | | | '--- SF……Sign Flag
; | | | | | | | | | '--- TF……Trap Flag (Single Step)
; | | | | | | | | '--- IF……Interrupt Flag
; | | | | | | | '--- DF……Direction Flag
; | | | | | | '--- OF……Overflow flag
; | | | | '----- IOPL……I/O Privilege Level (286+ only)
; | | | '----- NT……Nested Task Flag (286+ only)
; | | '----- 0
; | '----- RF……Resume Flag (386+ only)
; '------ VM……Virtual Mode Flag (386+ only)
;
; : see PUSHF POPF STI CLI STD CLD
;
; ------------------------------------------------------------------------------------------------------
;
; 描述符
; usage: Descriptor Base, Limit, Attr
; Base: dd
; Limit: dd (low 20 bits available)
; Attr: dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限 1 (2 字节)
dw %1 & 0FFFFh ; 段基址 1 (2 字节)
db (%1 >> 16) & 0FFh ; 段基址 2 (1 字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性 1 + 段界限 2 + 属性 2 (2 字节)
db (%1 >> 24) & 0FFh ; 段基址 3 (1 字节)
%endmacro ; 8 字节
;
;
; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移 1 (2 字节)
dw %1 ; 选择子 (2 字节)
dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性 (2 字节)
dw ((%2 >> 16) & 0FFFFh) ; 偏移 2 (2 字节)
%endmacro ; 8 字节
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

787
boot/loader.asm Normal file
View File

@ -0,0 +1,787 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; 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 BaseOfLoaderPhyAddr + LABEL_GDT ; 基地址
; 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, BaseOfKernelFile
mov es, ax ; es <- BaseOfKernelFile
mov bx, OffsetOfKernelFile ; bx <- OffsetOfKernelFile 于是, es:bx = BaseOfKernelFile:OffsetOfKernelFile = BaseOfKernelFile * 10h + OffsetOfKernelFile
mov ax, [wSectorNo] ; ax <- Root Directory 中的某 Sector 号
mov cl, 1
call ReadSector
mov si, KernelFileName ; ds:si -> "KERNEL BIN"
mov di, OffsetOfKernelFile ; es:di -> BaseOfKernelFile:???? = BaseOfKernelFile*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, 2 ; "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 文件大小
pop eax
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, BaseOfKernelFile
mov es, ax ; es <- BaseOfKernelFile
mov bx, OffsetOfKernelFile ; bx <- OffsetOfKernelFile 于是, es:bx = BaseOfKernelFile:OffsetOfKernelFile = BaseOfKernelFile * 10h + OffsetOfKernelFile
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]
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:
call KillMotor ; 关闭软驱马达
mov dh, 1 ; "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:(BaseOfLoaderPhyAddr+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 "Ready. "
Message2 db "No KERNEL"
;============================================================================
;----------------------------------------------------------------------------
; 函数名: 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, BaseOfKernelFile ; ┓
sub ax, 0100h ; ┣ 在 BaseOfKernelFile 后面留出 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 = (BaseOfKernelFile - 100):00 = (BaseOfKernelFile - 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
push szMemChkTitle
call DispStr
add esp, 4
call DispMemInfo
call SetupPaging
;mov ah, 0Fh ; 0000: 黑底 1111: 白字
;mov al, 'P'
;mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列。
call InitKernel
;jmp $
;***************************************************************
jmp SelectorFlatC:KernelEntryPointPhyAddr ; 正式进入内核 *
;***************************************************************
; 内存看上去是这样的:
; ┃ ┃
; ┃ . ┃
; ┃ . ┃
; ┃ . ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; ┃■■■■■■Page Tables■■■■■■┃
; ┃■■■■■(大小由LOADER决定)■■■■┃
; 00101000h ┃■■■■■■■■■■■■■■■■■■┃ PageTblBase
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 00100000h ┃■■■■Page Directory Table■■■■┃ PageDirBase <- 1M
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; F0000h ┃□□□□□□□System ROM□□□□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; E0000h ┃□□□□Expansion of system ROM □□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; C0000h ┃□□□Reserved for ROM expansion□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃ B8000h ← gs
; A0000h ┃□□□Display adapter reserved□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; 9FC00h ┃□□extended BIOS data area (EBDA)□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 90000h ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 80000h ┃■■■■■■■KERNEL.BIN■■■■■■┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 30000h ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KernelEntryPointPhyAddr)
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ ┃
; 7E00h ┃ F R E E ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; 7C00h ┃■■■■■■BOOT SECTOR■■■■■■┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ ┃
; 500h ┃ F R E E ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□┃
; 400h ┃□□□□ROM BIOS parameter area □□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇┃
; 0h ┃◇◇◇◇◇◇Int Vectors◇◇◇◇◇◇┃
; ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
;
;
; ┏━━━┓ ┏━━━┓
; ┃■■■┃ 我们使用 ┃□□□┃ 不能使用的内存
; ┗━━━┛ ┗━━━┛
; ┏━━━┓ ┏━━━┓
; ┃ ┃ 未使用空间 ┃◇◇◇┃ 可以覆盖的内存
; ┗━━━┛ ┗━━━┛
;
; 注KERNEL 的位置实际上是很灵活的,可以通过同时改变 LOAD.INC 中的 KernelEntryPointPhyAddr 和 MAKEFILE 中参数 -Ttext 的值来改变。
; 比如,如果把 KernelEntryPointPhyAddr 和 -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
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 ; { // 依次显示BaseAddrLowBaseAddrHighLengthLowLengthHighType
.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
; ---------------------------------------------------------------------------
; 启动分页机制 --------------------------------------------------------------
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, PageDirBase ; 此段首地址为 PageDirBase
xor eax, eax
mov eax, PageTblBase | 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, PageTblBase ; 此段首地址为 PageTblBase
xor eax, eax
mov eax, PG_P | PG_USU | PG_RWW
.2:
stosd
add eax, 4096 ; 每一页指向 4K 的空间
loop .2
mov eax, PageDirBase
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 [BaseOfKernelFilePhyAddr + 2Ch]; ┓ ecx <- pELFHdr->e_phnum
movzx ecx, cx ; ┛
mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi <- pELFHdr->e_phoff
add esi, BaseOfKernelFilePhyAddr ; 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, BaseOfKernelFilePhyAddr ; ┣ ::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
_szReturn: db 0Ah, 0
;; 变量
_dwMCRNumber: dd 0 ; Memory Check Result
_dwDispPos: dd (80 * 6 + 0) * 2 ; 屏幕第 6 行, 第 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 BaseOfLoaderPhyAddr + _szMemChkTitle
szRAMSize equ BaseOfLoaderPhyAddr + _szRAMSize
szReturn equ BaseOfLoaderPhyAddr + _szReturn
dwDispPos equ BaseOfLoaderPhyAddr + _dwDispPos
dwMemSize equ BaseOfLoaderPhyAddr + _dwMemSize
dwMCRNumber equ BaseOfLoaderPhyAddr + _dwMCRNumber
ARDStruct equ BaseOfLoaderPhyAddr + _ARDStruct
dwBaseAddrLow equ BaseOfLoaderPhyAddr + _dwBaseAddrLow
dwBaseAddrHigh equ BaseOfLoaderPhyAddr + _dwBaseAddrHigh
dwLengthLow equ BaseOfLoaderPhyAddr + _dwLengthLow
dwLengthHigh equ BaseOfLoaderPhyAddr + _dwLengthHigh
dwType equ BaseOfLoaderPhyAddr + _dwType
MemChkBuf equ BaseOfLoaderPhyAddr + _MemChkBuf
; 堆栈就在数据段的末尾
StackSpace: times 1000h db 0
TopOfStack equ BaseOfLoaderPhyAddr + $ ; 栈顶
; SECTION .data1 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

69
include/const.h Normal file
View File

@ -0,0 +1,69 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
const.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#ifndef _ORANGES_CONST_H_
#define _ORANGES_CONST_H_
/* EXTERN */
#define EXTERN extern /* EXTERN is defined as extern except in global.c */
/* 函数类型 */
#define PUBLIC /* PUBLIC is the opposite of PRIVATE */
#define PRIVATE static /* PRIVATE x limits the scope of x */
/* Boolean */
#define TRUE 1
#define FALSE 0
/* 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
/* 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) */
/* 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 */
/* system call */
//TODO 系统调用数量
//不被分配时间片的系统调用、打印字符串的系统调用、PV操作的系统调用
#define NR_SYS_CALL 5
#endif /* _ORANGES_CONST_H_ */

31
include/global.h Normal file
View File

@ -0,0 +1,31 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
global.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* EXTERN is defined as extern except in global.c */
#ifdef GLOBAL_VARIABLES_HERE
#undef EXTERN
#define EXTERN
#endif
EXTERN int ticks;
EXTERN int disp_pos;
EXTERN u8 gdt_ptr[6]; // 0~15:Limit 16~47:Base
EXTERN DESCRIPTOR gdt[GDT_SIZE];
EXTERN u8 idt_ptr[6]; // 0~15:Limit 16~47:Base
EXTERN GATE idt[IDT_SIZE];
EXTERN u32 k_reenter;
EXTERN TSS tss;
EXTERN PROCESS* p_proc_ready;
extern PROCESS proc_table[];
extern char task_stack[];
extern TASK task_table[];
extern irq_handler irq_table[];

94
include/proc.h Normal file
View File

@ -0,0 +1,94 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
proc.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
typedef struct s_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 */
}STACK_FRAME;
//相互持有引用的定义
typedef struct s_proc PROCESS;
typedef struct semaphore SEMAPHORE;
//TODO 信号量
struct semaphore{
int value; //当前资源最大允许个数
int s; //头指针
int e; //尾指针
PROCESS *queue[1000]; //进程队列
};
//TODO
struct s_proc {
STACK_FRAME regs; /* process registers saved in stack frame */
u16 ldt_sel; /* gdt selector giving ldt base and limit */
DESCRIPTOR ldts[LDT_SIZE]; /* local descriptors for code and data */
int ticks; /* remained ticks */
int priority;
u32 pid; /* process id passed in from MM */
char p_name[16]; /* name of the process */
int wake; //TODO 进程睡眠到什么时间
SEMAPHORE *waiting_semaphore; //等待信号量
};
typedef struct s_task {
task_f initial_eip;
int stacksize;
char name[32];
}TASK;
//TODO 6个任务
/* Number of tasks */
#define NR_TASKS 6
/* stacks of tasks */
// #define STACK_SIZE_TESTA 0x8000
// #define STACK_SIZE_TESTB 0x8000
// #define STACK_SIZE_TESTC 0x8000
#define STACK_SIZE_A 0x8000
#define STACK_SIZE_READER_B 0x8000
#define STACK_SIZE_READER_C 0x8000
#define STACK_SIZE_READER_D 0x8000
#define STACK_SIZE_WRITER_E 0x8000
#define STACK_SIZE_WRITER_F 0x8000
// #define STACK_SIZE_TOTAL (STACK_SIZE_TESTA + \
// STACK_SIZE_TESTB + \
// STACK_SIZE_TESTC)
#define STACK_SIZE_TOTAL (STACK_SIZE_A + \
STACK_SIZE_READER_B + \
STACK_SIZE_READER_C + \
STACK_SIZE_READER_D + \
STACK_SIZE_WRITER_E + \
STACK_SIZE_WRITER_F)

154
include/protect.h Normal file
View File

@ -0,0 +1,154 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
protect.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#ifndef _ORANGES_PROTECT_H_
#define _ORANGES_PROTECT_H_
/* 存储段描述符/系统段描述符 */
typedef struct s_descriptor /* 共 8 个字节 */
{
u16 limit_low; /* Limit */
u16 base_low; /* Base */
u8 base_mid; /* Base */
u8 attr1; /* P(1) DPL(2) DT(1) TYPE(4) */
u8 limit_high_attr2; /* G(1) D(1) 0(1) AVL(1) LimitHigh(4) */
u8 base_high; /* Base */
}DESCRIPTOR;
/* 门描述符 */
typedef struct s_gate
{
u16 offset_low; /* Offset Low */
u16 selector; /* Selector */
u8 dcount; /* 该字段只在调用门描述符中有效。
*/
u8 attr; /* P(1) DPL(2) DT(1) TYPE(4) */
u16 offset_high; /* Offset High */
}GATE;
typedef struct s_tss {
u32 backlink;
u32 esp0; /* stack pointer to use during interrupt */
u32 ss0; /* " segment " " " " */
u32 esp1;
u32 ss1;
u32 esp2;
u32 ss2;
u32 cr3;
u32 eip;
u32 flags;
u32 eax;
u32 ecx;
u32 edx;
u32 ebx;
u32 esp;
u32 ebp;
u32 esi;
u32 edi;
u32 es;
u32 cs;
u32 ss;
u32 ds;
u32 fs;
u32 gs;
u32 ldt;
u16 trap;
u16 iobase; /* I/O位图基址大于或等于TSS段界限就表示没有I/O许可位图 */
/*u8 iomap[2];*/
}TSS;
/* GDT */
/* 描述符索引 */
#define INDEX_DUMMY 0 // ┓
#define INDEX_FLAT_C 1 // ┣ LOADER 里面已经确定了的.
#define INDEX_FLAT_RW 2 // ┃
#define INDEX_VIDEO 3 // ┛
#define INDEX_TSS 4
#define INDEX_LDT_FIRST 5
/* 选择子 */
#define SELECTOR_DUMMY 0 // ┓
#define SELECTOR_FLAT_C 0x08 // ┣ LOADER 里面已经确定了的.
#define SELECTOR_FLAT_RW 0x10 // ┃
#define SELECTOR_VIDEO (0x18+3) // ┛<-- RPL=3
#define SELECTOR_TSS 0x20 // TSS. 从外层跳到内存时 SS 和 ESP 的值从里面获得.
#define SELECTOR_LDT_FIRST 0x28
#define SELECTOR_KERNEL_CS SELECTOR_FLAT_C
#define SELECTOR_KERNEL_DS SELECTOR_FLAT_RW
#define SELECTOR_KERNEL_GS SELECTOR_VIDEO
/* 每个任务有一个单独的 LDT, 每个 LDT 中的描述符个数: */
#define LDT_SIZE 2
/* 描述符类型值说明 */
#define DA_32 0x4000 /* 32 位段 */
#define DA_LIMIT_4K 0x8000 /* 段界限粒度为 4K 字节 */
#define DA_DPL0 0x00 /* DPL = 0 */
#define DA_DPL1 0x20 /* DPL = 1 */
#define DA_DPL2 0x40 /* DPL = 2 */
#define DA_DPL3 0x60 /* DPL = 3 */
/* 存储段描述符类型值说明 */
#define DA_DR 0x90 /* 存在的只读数据段类型值 */
#define DA_DRW 0x92 /* 存在的可读写数据段属性值 */
#define DA_DRWA 0x93 /* 存在的已访问可读写数据段类型值 */
#define DA_C 0x98 /* 存在的只执行代码段属性值 */
#define DA_CR 0x9A /* 存在的可执行可读代码段属性值 */
#define DA_CCO 0x9C /* 存在的只执行一致代码段属性值 */
#define DA_CCOR 0x9E /* 存在的可执行可读一致代码段属性值 */
/* 系统段描述符类型值说明 */
#define DA_LDT 0x82 /* 局部描述符表段类型值 */
#define DA_TaskGate 0x85 /* 任务门类型值 */
#define DA_386TSS 0x89 /* 可用 386 任务状态段类型值 */
#define DA_386CGate 0x8C /* 386 调用门类型值 */
#define DA_386IGate 0x8E /* 386 中断门类型值 */
#define DA_386TGate 0x8F /* 386 陷阱门类型值 */
/* 选择子类型值说明 */
/* 其中, SA_ : Selector Attribute */
#define SA_RPL_MASK 0xFFFC
#define SA_RPL0 0
#define SA_RPL1 1
#define SA_RPL2 2
#define SA_RPL3 3
#define SA_TI_MASK 0xFFFB
#define SA_TIG 0
#define SA_TIL 4
/* 中断向量 */
#define INT_VECTOR_DIVIDE 0x0
#define INT_VECTOR_DEBUG 0x1
#define INT_VECTOR_NMI 0x2
#define INT_VECTOR_BREAKPOINT 0x3
#define INT_VECTOR_OVERFLOW 0x4
#define INT_VECTOR_BOUNDS 0x5
#define INT_VECTOR_INVAL_OP 0x6
#define INT_VECTOR_COPROC_NOT 0x7
#define INT_VECTOR_DOUBLE_FAULT 0x8
#define INT_VECTOR_COPROC_SEG 0x9
#define INT_VECTOR_INVAL_TSS 0xA
#define INT_VECTOR_SEG_NOT 0xB
#define INT_VECTOR_STACK_FAULT 0xC
#define INT_VECTOR_PROTECTION 0xD
#define INT_VECTOR_PAGE_FAULT 0xE
#define INT_VECTOR_COPROC_ERR 0x10
/* 中断向量 */
#define INT_VECTOR_IRQ0 0x20
#define INT_VECTOR_IRQ8 0x28
/* 系统调用 */
#define INT_VECTOR_SYS_CALL 0x90
/* 宏 */
/* 线性地址 → 物理地址 */
#define vir2phys(seg_base, vir) (u32)(((u32)seg_base) + (u32)(vir))
#endif /* _ORANGES_PROTECT_H_ */

75
include/proto.h Normal file
View File

@ -0,0 +1,75 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
proto.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "proc.h"
/* klib.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);
/* protect.c */
PUBLIC void init_prot();
PUBLIC u32 seg2phys(u16 seg);
/* klib.c */
PUBLIC void delay(int time);
/* kernel.asm */
void restart();
/* main.c */
void TestA();
void TestB();
void TestC();
/* i8259.c */
PUBLIC void put_irq_handler(int irq, irq_handler handler);
PUBLIC void spurious_irq(int irq);
/* clock.c */
PUBLIC void clock_handler(int irq);
/* 以下是系统调用相关 */
/* proc.c */
PUBLIC int sys_get_ticks(); /* sys_call */
PUBLIC void sys_ms_delay_proc(int); /* TODO 被分配的时间片的操作*/
/* syscall.asm */
PUBLIC void sys_call(); /* int_handler */
PUBLIC int get_ticks();
PUBLIC void sys_p_impl(SEMAPHORE *);
PUBLIC void sys_v_impl(SEMAPHORE *);
//TODO 新增的系统调用
//打印字符串的系统调用封装内核函数disp_str
PUBLIC void print_str(char *);
PUBLIC void sys_print_str(char *);
//不被分配时间片的系统调用
PUBLIC void sys_ms_delay(int);
PUBLIC void ms_delay(int);
//pv实现
PUBLIC void sys_p(SEMAPHORE *);
PUBLIC void sys_v(SEMAPHORE *);
PUBLIC void p(SEMAPHORE *);
PUBLIC void v(SEMAPHORE *);
//TODO 读者写者问题调用
void A();
void READER_B();
void READER_C();
void READER_D();
void WRITER_E();
void WRITER_F();

44
include/sconst.inc Normal file
View File

@ -0,0 +1,44 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; sconst.inc
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P_STACKBASE equ 0
GSREG equ P_STACKBASE
FSREG equ GSREG + 4
ESREG equ FSREG + 4
DSREG equ ESREG + 4
EDIREG equ DSREG + 4
ESIREG equ EDIREG + 4
EBPREG equ ESIREG + 4
KERNELESPREG equ EBPREG + 4
EBXREG equ KERNELESPREG + 4
EDXREG equ EBXREG + 4
ECXREG equ EDXREG + 4
EAXREG equ ECXREG + 4
RETADR equ EAXREG + 4
EIPREG equ RETADR + 4
CSREG equ EIPREG + 4
EFLAGSREG equ CSREG + 4
ESPREG equ EFLAGSREG + 4
SSREG equ ESPREG + 4
P_STACKTOP equ SSREG + 4
P_LDT_SEL equ P_STACKTOP
P_LDT equ P_LDT_SEL + 4
TSS3_S_SP0 equ 4
INT_M_CTL equ 0x20 ; I/O port for interrupt controller <Master>
INT_M_CTLMASK equ 0x21 ; setting bits in this port disables ints <Master>
INT_S_CTL equ 0xA0 ; I/O port for second interrupt controller <Slave>
INT_S_CTLMASK equ 0xA1 ; setting bits in this port disables ints <Slave>
EOI equ 0x20
; 以下选择子值必须与 protect.h 中保持一致!!!
SELECTOR_FLAT_C equ 0x08 ; LOADER 里面已经确定了的.
SELECTOR_TSS equ 0x20 ; TSS. 从外层跳到内存时 SS ESP 的值从里面获得.
SELECTOR_KERNEL_CS equ SELECTOR_FLAT_C

9
include/string.h Normal file
View File

@ -0,0 +1,9 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
string.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
PUBLIC void* memcpy(void* p_dst, void* p_src, int size);
PUBLIC void memset(void* p_dst, char ch, int size);

24
include/type.h Normal file
View File

@ -0,0 +1,24 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
type.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#ifndef _ORANGES_TYPE_H_
#define _ORANGES_TYPE_H_
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef void (*int_handler) ();
typedef void (*task_f) ();
typedef void (*irq_handler) (int irq);
typedef void* system_call;
#endif /* _ORANGES_TYPE_H_ */

45
kernel/clock.c Normal file
View File

@ -0,0 +1,45 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
clock.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "proto.h"
#include "string.h"
#include "global.h"
/*======================================================================*
clock_handler
*======================================================================*/
PUBLIC void clock_handler(int irq)
{
ticks++;
p_proc_ready->ticks--;
if (k_reenter != 0) {
return;
}
if (p_proc_ready->ticks > 0) {
return;
}
schedule();
}
/*======================================================================*
milli_delay
*======================================================================*/
PUBLIC void milli_delay(int milli_sec)
{
int t = get_ticks();
while(((get_ticks() - t) * 1000 / HZ) < milli_sec) {}
}

46
kernel/global.c Normal file
View File

@ -0,0 +1,46 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
global.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#define GLOBAL_VARIABLES_HERE
#include "type.h"
#include "const.h"
#include "protect.h"
#include "proto.h"
#include "global.h"
PUBLIC PROCESS proc_table[NR_TASKS];
PUBLIC char task_stack[STACK_SIZE_TOTAL];
// PUBLIC TASK task_table[NR_TASKS] = {{TestA, STACK_SIZE_TESTA, "TestA"},
// {TestB, STACK_SIZE_TESTB, "TestB"},
// {TestC, STACK_SIZE_TESTC, "TestC"}};
PUBLIC TASK
task_table[NR_TASKS] = {
{READER_B,STACK_SIZE_READER_B,"READER_B"},
{READER_C,STACK_SIZE_READER_C,"READER_C"},
{READER_D,STACK_SIZE_READER_D,"READER_D"},
{WRITER_E,STACK_SIZE_WRITER_E,"WRITER_E"},
{WRITER_F,STACK_SIZE_WRITER_F,"WRITER_F"},
{A,STACK_SIZE_A,"A"}
};
PUBLIC irq_handler irq_table[NR_IRQ];
//TODO系统调用表中的顺序要和asm文件中的系统调用好保持一致
PUBLIC system_call
sys_call_table[NR_SYS_CALL] = {
sys_get_ticks,
sys_print_str,
sys_ms_delay,
sys_p,
sys_v
};

56
kernel/i8259.c Normal file
View File

@ -0,0 +1,56 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
i8259.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "proto.h"
#include "global.h"
/*======================================================================*
init_8259A
*======================================================================*/
PUBLIC void init_8259A()
{
out_byte(INT_M_CTL, 0x11); // Master 8259, ICW1.
out_byte(INT_S_CTL, 0x11); // Slave 8259, ICW1.
out_byte(INT_M_CTLMASK, INT_VECTOR_IRQ0); // Master 8259, ICW2. 设置 '主8259' 的中断入口地址为 0x20.
out_byte(INT_S_CTLMASK, INT_VECTOR_IRQ8); // Slave 8259, ICW2. 设置 '从8259' 的中断入口地址为 0x28
out_byte(INT_M_CTLMASK, 0x4); // Master 8259, ICW3. IR2 对应 '从8259'.
out_byte(INT_S_CTLMASK, 0x2); // Slave 8259, ICW3. 对应 '主8259' 的 IR2.
out_byte(INT_M_CTLMASK, 0x1); // Master 8259, ICW4.
out_byte(INT_S_CTLMASK, 0x1); // Slave 8259, ICW4.
out_byte(INT_M_CTLMASK, 0xFF); // Master 8259, OCW1.
out_byte(INT_S_CTLMASK, 0xFF); // Slave 8259, OCW1.
int i;
for (i = 0; i < NR_IRQ; i++) {
irq_table[i] = spurious_irq;
}
}
/*======================================================================*
spurious_irq
*======================================================================*/
PUBLIC void spurious_irq(int irq)
{
disp_str("spurious_irq: ");
disp_int(irq);
disp_str("\n");
}
/*======================================================================*
put_irq_handler
*======================================================================*/
PUBLIC void put_irq_handler(int irq, irq_handler handler)
{
disable_irq(irq);
irq_table[irq] = handler;
}

371
kernel/kernel.asm Normal file
View File

@ -0,0 +1,371 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; kernel.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%include "sconst.inc"
; 导入函数
extern cstart
extern kernel_main
extern exception_handler
extern spurious_irq
extern clock_handler
extern disp_str
extern delay
extern irq_table
; 导入全局变量
extern gdt_ptr
extern idt_ptr
extern p_proc_ready
extern tss
extern disp_pos
extern k_reenter
extern sys_call_table
bits 32
[SECTION .data]
clock_int_msg db "^", 0
[SECTION .bss]
StackSpace resb 2 * 1024
StackTop: ; 栈顶
[section .text] ; 代码在此
global _start ; 导出 _start
global restart
global sys_call
global divide_error
global single_step_exception
global nmi
global breakpoint_exception
global overflow
global bounds_check
global inval_opcode
global copr_not_available
global double_fault
global copr_seg_overrun
global inval_tss
global segment_not_present
global stack_exception
global general_protection
global page_fault
global copr_error
global hwint00
global hwint01
global hwint02
global hwint03
global hwint04
global hwint05
global hwint06
global hwint07
global hwint08
global hwint09
global hwint10
global hwint11
global hwint12
global hwint13
global hwint14
global hwint15
_start:
; 此时内存看上去是这样的(更详细的内存情况在 LOADER.ASM 中有说明):
; ┃ ┃
; ┃ ... ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■Page Tables■■■■■■┃
; ┃■■■■■(大小由LOADER决定)■■■■┃ PageTblBase
; 00101000h ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■Page Directory Table■■■■┃ PageDirBase = 1M
; 00100000h ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□ Hardware Reserved □□□□┃ B8000h ← gs
; 9FC00h ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
; 90000h ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■KERNEL.BIN■■■■■■┃
; 80000h ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KernelEntryPointPhyAddr)
; 30000h ┣━━━━━━━━━━━━━━━━━━┫
; ┋ ... ┋
; ┋ ┋
; 0h ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
;
;
; GDT 以及相应的描述符是这样的:
;
; Descriptors Selectors
; ┏━━━━━━━━━━━━━━━━━━┓
; ┃ Dummy Descriptor ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ DESC_FLAT_C (04G) ┃ 8h = cs
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ DESC_FLAT_RW (04G) ┃ 10h = ds, es, fs, ss
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ DESC_VIDEO ┃ 1Bh = gs
; ┗━━━━━━━━━━━━━━━━━━┛
;
; 注意! 在使用 C 代码的时候一定要保证 ds, es, ss 这几个段寄存器的值是一样的
; 因为编译器有可能编译出使用它们的代码, 而编译器默认它们是一样的. 比如串拷贝操作会用到 ds 和 es.
;
;
; 把 esp 从 LOADER 挪到 KERNEL
mov esp, StackTop ; 堆栈在 bss 段中
mov dword [disp_pos], 0
sgdt [gdt_ptr] ; cstart() 中将会用到 gdt_ptr
call cstart ; 在此函数中改变了gdt_ptr让它指向新的GDT
lgdt [gdt_ptr] ; 使用新的GDT
lidt [idt_ptr]
jmp SELECTOR_KERNEL_CS:csinit
csinit: ; “这个跳转指令强制使用刚刚初始化的结构”——<<OS:D&I 2nd>> P90.
;jmp 0x40:0
;ud2
xor eax, eax
mov ax, SELECTOR_TSS
ltr ax
;sti
jmp kernel_main
;hlt
; 中断和异常 -- 硬件中断
; ---------------------------------
%macro hwint_master 1
call save
in al, INT_M_CTLMASK ; `.
or al, (1 << %1) ; | 屏蔽当前中断
out INT_M_CTLMASK, al ; /
mov al, EOI ; `. 置EOI位
out INT_M_CTL, al ; /
sti ; CPU在响应中断的过程中会自动关中断这句之后就允许响应新的中断
push %1 ; `.
call [irq_table + 4 * %1] ; | 中断处理程序
pop ecx ; /
cli
in al, INT_M_CTLMASK ; `.
and al, ~(1 << %1) ; | 恢复接受当前中断
out INT_M_CTLMASK, al ; /
ret
%endmacro
ALIGN 16
hwint00: ; Interrupt routine for irq 0 (the clock).
hwint_master 0
ALIGN 16
hwint01: ; Interrupt routine for irq 1 (keyboard)
hwint_master 1
ALIGN 16
hwint02: ; Interrupt routine for irq 2 (cascade!)
hwint_master 2
ALIGN 16
hwint03: ; Interrupt routine for irq 3 (second serial)
hwint_master 3
ALIGN 16
hwint04: ; Interrupt routine for irq 4 (first serial)
hwint_master 4
ALIGN 16
hwint05: ; Interrupt routine for irq 5 (XT winchester)
hwint_master 5
ALIGN 16
hwint06: ; Interrupt routine for irq 6 (floppy)
hwint_master 6
ALIGN 16
hwint07: ; Interrupt routine for irq 7 (printer)
hwint_master 7
; ---------------------------------
%macro hwint_slave 1
push %1
call spurious_irq
add esp, 4
hlt
%endmacro
; ---------------------------------
ALIGN 16
hwint08: ; Interrupt routine for irq 8 (realtime clock).
hwint_slave 8
ALIGN 16
hwint09: ; Interrupt routine for irq 9 (irq 2 redirected)
hwint_slave 9
ALIGN 16
hwint10: ; Interrupt routine for irq 10
hwint_slave 10
ALIGN 16
hwint11: ; Interrupt routine for irq 11
hwint_slave 11
ALIGN 16
hwint12: ; Interrupt routine for irq 12
hwint_slave 12
ALIGN 16
hwint13: ; Interrupt routine for irq 13 (FPU exception)
hwint_slave 13
ALIGN 16
hwint14: ; Interrupt routine for irq 14 (AT winchester)
hwint_slave 14
ALIGN 16
hwint15: ; Interrupt routine for irq 15
hwint_slave 15
; 中断和异常 -- 异常
divide_error:
push 0xFFFFFFFF ; no err code
push 0 ; vector_no = 0
jmp exception
single_step_exception:
push 0xFFFFFFFF ; no err code
push 1 ; vector_no = 1
jmp exception
nmi:
push 0xFFFFFFFF ; no err code
push 2 ; vector_no = 2
jmp exception
breakpoint_exception:
push 0xFFFFFFFF ; no err code
push 3 ; vector_no = 3
jmp exception
overflow:
push 0xFFFFFFFF ; no err code
push 4 ; vector_no = 4
jmp exception
bounds_check:
push 0xFFFFFFFF ; no err code
push 5 ; vector_no = 5
jmp exception
inval_opcode:
push 0xFFFFFFFF ; no err code
push 6 ; vector_no = 6
jmp exception
copr_not_available:
push 0xFFFFFFFF ; no err code
push 7 ; vector_no = 7
jmp exception
double_fault:
push 8 ; vector_no = 8
jmp exception
copr_seg_overrun:
push 0xFFFFFFFF ; no err code
push 9 ; vector_no = 9
jmp exception
inval_tss:
push 10 ; vector_no = A
jmp exception
segment_not_present:
push 11 ; vector_no = B
jmp exception
stack_exception:
push 12 ; vector_no = C
jmp exception
general_protection:
push 13 ; vector_no = D
jmp exception
page_fault:
push 14 ; vector_no = E
jmp exception
copr_error:
push 0xFFFFFFFF ; no err code
push 16 ; vector_no = 10h
jmp exception
exception:
call exception_handler
add esp, 4*2 ; 让栈顶指向 EIP堆栈中从顶向下依次是EIP、CS、EFLAGS
hlt
; ====================================================================================
; save
; ====================================================================================
save:
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx
mov esi, esp ;esi = 进程表起始地址
inc dword [k_reenter] ;k_reenter++;
cmp dword [k_reenter], 0 ;if(k_reenter ==0)
jne .1 ;{
mov esp, StackTop ; mov esp, StackTop <--切换到内核栈
push restart ; push restart
jmp [esi + RETADR - P_STACKBASE]; return;
.1: ;} else { 已经在内核栈,不需要再切换
push restart_reenter ; push restart_reenter
jmp [esi + RETADR - P_STACKBASE]; return;
;}
; ====================================================================================
; sys_call
; ====================================================================================
sys_call:
call save
sti
call [sys_call_table + eax * 4]
mov [esi + EAXREG - P_STACKBASE], eax
cli
ret
; ====================================================================================
; restart
; ====================================================================================
restart:
mov esp, [p_proc_ready]
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword [tss + TSS3_S_SP0], eax
restart_reenter:
dec dword [k_reenter]
pop gs
pop fs
pop es
pop ds
popad
add esp, 4
iretd

387
kernel/main.c Normal file
View File

@ -0,0 +1,387 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
main.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "proto.h"
#include "string.h"
#include "global.h"
//TODO 所有要改动的超参数
//0读者优先1写者优先2读写公平
#define STRATEGY 2
#define READERS_ALLOWED 3
#define B_SLEEP_TIME 1
#define C_SLEEP_TIME 2
#define D_SLEEP_TIME 1
#define E_SLEEP_TIME 1
#define F_SLEEP_TIME 2
#define B_PROC_TIME 2
#define C_PROC_TIME 3
#define D_PROC_TIME 3
#define E_PROC_TIME 3
#define F_PROC_TIME 4
//TODO 声明
//每个进程的状态需要进程维护由A来输出0表示正在使用1表示等待使用2表示休眠
#define TIME_SEG (500) //时间片
int proc_state[5]={1,1,1,1,1};
int thread_sleep_time[5];
int thread_proc_time[5];
//两个信号量
SEMAPHORE w_mutex;
SEMAPHORE r_mutex;
//额外的信号量
SEMAPHORE S;
int readers_cnt;//目前在线的读者数量
int writers_cnt;//目前在线的写者数量
int mode;//模式
/*======================================================================*
TODO clearScreen
*======================================================================*/
void clearScreen(){
u8 *base=(u8 *)0xB8000;
for(int i=0;i<0x8000;i+=2){
base[i]=' ';
}
disp_pos=0;
}
/*======================================================================*
TODO init_semaphore
*======================================================================*/
void init_semaphore(SEMAPHORE *semaphore, int value)
{
semaphore->s=0;
semaphore->e=0;
semaphore->value=value;
}
/*======================================================================*
kernel_main
*======================================================================*/
PUBLIC int kernel_main()
{
disp_str("-----\"kernel_main\" begins-----\n");
TASK* p_task = task_table;
PROCESS* p_proc = proc_table;
char* p_task_stack = task_stack + STACK_SIZE_TOTAL;
u16 selector_ldt = SELECTOR_LDT_FIRST;
int i;
for (i = 0; i < NR_TASKS; i++) {
strcpy(p_proc->p_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(DESCRIPTOR));
p_proc->ldts[0].attr1 = DA_C | PRIVILEGE_TASK << 5;
memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3],
sizeof(DESCRIPTOR));
p_proc->ldts[1].attr1 = DA_DRW | PRIVILEGE_TASK << 5;
p_proc->regs.cs = ((8 * 0) & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_TASK;
p_proc->regs.ds = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_TASK;
p_proc->regs.es = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_TASK;
p_proc->regs.fs = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_TASK;
p_proc->regs.ss = ((8 * 1) & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_TASK;
p_proc->regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK)
| RPL_TASK;
p_proc->regs.eip = (u32)p_task->initial_eip;
p_proc->regs.esp = (u32)p_task_stack;
p_proc->regs.eflags = 0x1202; /* IF=1, IOPL=1 */
p_task_stack -= p_task->stacksize;
p_proc++;
p_task++;
selector_ldt += 1 << 3;
}
proc_table[0].ticks = proc_table[0].priority = 15;
proc_table[1].ticks = proc_table[1].priority = 5;
proc_table[2].ticks = proc_table[2].priority = 3;
k_reenter = 0;
ticks = 0;
p_proc_ready = proc_table;
/* 初始化 8253 PIT */
out_byte(TIMER_MODE, RATE_GENERATOR);
out_byte(TIMER0, (u8) (TIMER_FREQ/HZ) );
out_byte(TIMER0, (u8) ((TIMER_FREQ/HZ) >> 8));
put_irq_handler(CLOCK_IRQ, clock_handler); /* 设定时钟中断处理程序 */
enable_irq(CLOCK_IRQ); /* 让8259A可以接收时钟中断 */
//TODO 初始化超参数
init_semaphore(&r_mutex, READERS_ALLOWED);
init_semaphore(&w_mutex, 1);
init_semaphore(&S, 1);
thread_sleep_time[0]=B_SLEEP_TIME*TIME_SEG;
thread_sleep_time[1]=C_SLEEP_TIME*TIME_SEG;
thread_sleep_time[2]=D_SLEEP_TIME*TIME_SEG;
thread_sleep_time[3]=E_SLEEP_TIME*TIME_SEG;
thread_sleep_time[4]=F_SLEEP_TIME*TIME_SEG;
thread_proc_time[0]=B_PROC_TIME*TIME_SEG;
thread_proc_time[1]=C_PROC_TIME*TIME_SEG;
thread_proc_time[2]=D_PROC_TIME*TIME_SEG;
thread_proc_time[3]=E_PROC_TIME*TIME_SEG;
thread_proc_time[4]=F_PROC_TIME*TIME_SEG;
readers_cnt=0;
writers_cnt=0;
mode=STRATEGY;
clearScreen();
restart();
while(1){}
}
/*======================================================================*
TestA
*======================================================================*/
// void TestA()
// {
// int i = 0;
// while (1) {
// disp_str("A.");
// milli_delay(10);
// }
// }
/*======================================================================*
TestB
*======================================================================*/
// void TestB()
// {
// int i = 0x1000;
// while(1){
// disp_str("B.");
// milli_delay(10);
// }
// }
/*======================================================================*
TestC
*======================================================================*/
// void TestC()
// {
// int i = 0x2000;
// while(1){
// disp_str("C.");
// milli_delay(10);
// }
// }
/*======================================================================*
A
*======================================================================*/
void A()
{
int i = 1;
while (1) {
if (i > 20) { while (1); }
char *str;
if (i < 10) {
str = " \0";
str[0] = (char) ('0' + i);
}
else {
str = " \0";
str[0] = (char) ('0' + i / 10);
str[1] = (char) ('0' + i % 10);
}
print_str(str);
for (int j = 0; j < 6; ++j) {
//每个进程的状态需要每个进程维护并且由A来输出 0-正在使用 1-等待使用 2-休眠
//int proc_state[5] = {-1, 0, 1, 2, -1};
if (j == 5) {
print_str("\n");
break;
}
if (proc_state[j] == -1) print_str("None ");
else if (proc_state[j] == 0) disp_color_str("O ", 0x0A);
else if (proc_state[j] == 1) disp_color_str("X ", 0x0C);
else if (proc_state[j] == 2) disp_color_str("Z ", 0x03);
}
i++;
milli_delay(TIME_SEG);
}
}
/*======================================================================*
READER_B
*======================================================================*/
void READER_B()
{
reader(0);
}
/*======================================================================*
READER_C
*======================================================================*/
void READER_C()
{
reader(1);
}
/*======================================================================*
READER_D
*======================================================================*/
void READER_D()
{
reader(2);
}
/*======================================================================*
WRITER_E
*======================================================================*/
void WRITER_E()
{
writer(3);
}
/*======================================================================*
WRITER_F
*======================================================================*/
void WRITER_F()
{
writer(4);
}
/*======================================================================*
writer
使w_mutexr_mutex
pv操作proc_state[]
pvproc_state[]
使w_mutexr_mutex
使w_mutexr_mutexS
pv操作
pv操作
*======================================================================*/
void writer(int i)
{
if(mode ==0){
while(1){
p(&w_mutex);
proc_state[i]=0;
milli_delay(thread_proc_time[i]);
v(&w_mutex);
proc_state[i]=2;
milli_delay(thread_sleep_time[i]);
proc_state[i]=1;
}
}
else if(mode == 1){
while(1){
if(writers_cnt==0) p(&S);
writers_cnt++;
p(&w_mutex);
proc_state[i]=0;
milli_delay(thread_proc_time[i]);
writers_cnt--;
v(&w_mutex);
if(writers_cnt==0) v(&S);
proc_state[i]=2;
milli_delay(thread_sleep_time[i]);
proc_state[i]=1;
}
}
else if(mode == 2){
while(1){
p(&S);
p(&w_mutex);
proc_state[i]=0;
milli_delay(thread_proc_time[i]);
v(&w_mutex);
v(&S);
proc_state[i]=2;
milli_delay(thread_sleep_time[i]);
proc_state[i]=1;
}
}
}
/*======================================================================*
reader
*======================================================================*/
void reader(int i)
{
if(mode ==0){
while(1){
if(readers_cnt==0) p(&w_mutex);
readers_cnt++;
p(&r_mutex);
proc_state[i]=0;
milli_delay(thread_proc_time[i]);
v(&r_mutex);
readers_cnt--;
if(readers_cnt==0) v(&w_mutex);
proc_state[i]=2;
milli_delay(thread_sleep_time[i]);
proc_state[i]=1;
}
}
else if(mode ==1){
while(1){
p(&r_mutex);
p(&S);
v(&S);
if(readers_cnt==0) p(&w_mutex);
readers_cnt++;
proc_state[i]=0;
milli_delay(thread_proc_time[i]);
readers_cnt--;
if(readers_cnt==0) v(&w_mutex);
v(&r_mutex);
proc_state[i]=2;
milli_delay(thread_sleep_time[i]);
proc_state[i]=1;
}
}
else if(mode ==2){
while(1){
p(&S);
p(&r_mutex);
if(readers_cnt==0) p(&w_mutex);
readers_cnt++;
proc_state[i]=0;
v(&S);
milli_delay(thread_proc_time[i]);
readers_cnt--;
if(readers_cnt==0) v(&w_mutex);
v(&r_mutex);
proc_state[i]=2;
milli_delay(thread_sleep_time[i]);
proc_state[i]=1;
}
}
}

80
kernel/proc.c Normal file
View File

@ -0,0 +1,80 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
proc.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "proto.h"
#include "string.h"
#include "global.h"
/*======================================================================*
schedule TODO
*======================================================================*/
PUBLIC void schedule()
{
PROCESS* p; // 修改此结构体
int current_tick = get_ticks();
while (1) {
p_proc_ready++;
if (p_proc_ready >= proc_table + NR_TASKS) {
p_proc_ready = proc_table;
}
if (p_proc_ready->waiting_semaphore == 0 &&
p_proc_ready->wake <= current_tick) {
break; // 寻找到进程
}
// if (p_proc_ready->wake_tick <= current_tick) {
// break; // 寻找到进程
// }
}
}
/*======================================================================*
sys_get_ticks
*======================================================================*/
PUBLIC int sys_get_ticks()
{
return ticks;
}
/*======================================================================*
TODO sys_ms_delay_proc
*======================================================================*/
PUBLIC void sys_ms_delay_proc(int milli_seconds)
{
p_proc_ready->wake = get_ticks() + (milli_seconds*(1000/HZ));
schedule();
}
/*======================================================================*
TODO sys_p_impl sys_v_impl
*======================================================================*/
PUBLIC void sys_p_impl(SEMAPHORE *semaphore)
{
semaphore->value--;
if(semaphore->value<0){
p_proc_ready->waiting_semaphore = semaphore;
semaphore->queue[semaphore->e] = p_proc_ready;
semaphore->e++;
semaphore->e%=1000;
schedule();
}
}
PUBLIC void sys_v_impl(SEMAPHORE *semaphore)
{
semaphore->value++;
if(semaphore->value<=0){
semaphore->queue[semaphore->s]->waiting_semaphore = 0;
semaphore->s++;
semaphore->s%=1000;
}
}

287
kernel/protect.c Normal file
View File

@ -0,0 +1,287 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
protect.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "proto.h"
#include "global.h"
/* 本文件内函数声明 */
PRIVATE void init_idt_desc(unsigned char vector, u8 desc_type, int_handler handler, unsigned char privilege);
PRIVATE void init_descriptor(DESCRIPTOR * p_desc, u32 base, u32 limit, u16 attribute);
/* 中断处理函数 */
void divide_error();
void single_step_exception();
void nmi();
void breakpoint_exception();
void overflow();
void bounds_check();
void inval_opcode();
void copr_not_available();
void double_fault();
void copr_seg_overrun();
void inval_tss();
void segment_not_present();
void stack_exception();
void general_protection();
void page_fault();
void copr_error();
void hwint00();
void hwint01();
void hwint02();
void hwint03();
void hwint04();
void hwint05();
void hwint06();
void hwint07();
void hwint08();
void hwint09();
void hwint10();
void hwint11();
void hwint12();
void hwint13();
void hwint14();
void hwint15();
/*======================================================================*
init_prot
*----------------------------------------------------------------------*
IDT
*======================================================================*/
PUBLIC void init_prot()
{
init_8259A();
// 全部初始化成中断门(没有陷阱门)
init_idt_desc(INT_VECTOR_DIVIDE, DA_386IGate,
divide_error, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_DEBUG, DA_386IGate,
single_step_exception, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_NMI, DA_386IGate,
nmi, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_BREAKPOINT, DA_386IGate,
breakpoint_exception, PRIVILEGE_USER);
init_idt_desc(INT_VECTOR_OVERFLOW, DA_386IGate,
overflow, PRIVILEGE_USER);
init_idt_desc(INT_VECTOR_BOUNDS, DA_386IGate,
bounds_check, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_INVAL_OP, DA_386IGate,
inval_opcode, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_COPROC_NOT, DA_386IGate,
copr_not_available, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_DOUBLE_FAULT, DA_386IGate,
double_fault, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_COPROC_SEG, DA_386IGate,
copr_seg_overrun, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_INVAL_TSS, DA_386IGate,
inval_tss, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_SEG_NOT, DA_386IGate,
segment_not_present, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_STACK_FAULT, DA_386IGate,
stack_exception, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_PROTECTION, DA_386IGate,
general_protection, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_PAGE_FAULT, DA_386IGate,
page_fault, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_COPROC_ERR, DA_386IGate,
copr_error, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ0 + 0, DA_386IGate,
hwint00, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ0 + 1, DA_386IGate,
hwint01, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ0 + 2, DA_386IGate,
hwint02, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ0 + 3, DA_386IGate,
hwint03, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ0 + 4, DA_386IGate,
hwint04, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ0 + 5, DA_386IGate,
hwint05, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ0 + 6, DA_386IGate,
hwint06, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ0 + 7, DA_386IGate,
hwint07, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 0, DA_386IGate,
hwint08, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 1, DA_386IGate,
hwint09, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 2, DA_386IGate,
hwint10, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 3, DA_386IGate,
hwint11, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 4, DA_386IGate,
hwint12, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 5, DA_386IGate,
hwint13, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 6, DA_386IGate,
hwint14, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_IRQ8 + 7, DA_386IGate,
hwint15, PRIVILEGE_KRNL);
init_idt_desc(INT_VECTOR_SYS_CALL, DA_386IGate,
sys_call, PRIVILEGE_USER);
/* 填充 GDT 中 TSS 这个描述符 */
memset(&tss, 0, sizeof(tss));
tss.ss0 = SELECTOR_KERNEL_DS;
init_descriptor(&gdt[INDEX_TSS],
vir2phys(seg2phys(SELECTOR_KERNEL_DS), &tss),
sizeof(tss) - 1,
DA_386TSS);
tss.iobase = sizeof(tss); /* 没有I/O许可位图 */
// 填充 GDT 中进程的 LDT 的描述符
int i;
PROCESS* p_proc = proc_table;
u16 selector_ldt = INDEX_LDT_FIRST << 3;
for(i=0;i<NR_TASKS;i++){
init_descriptor(&gdt[selector_ldt>>3],
vir2phys(seg2phys(SELECTOR_KERNEL_DS),
proc_table[i].ldts),
LDT_SIZE * sizeof(DESCRIPTOR) - 1,
DA_LDT);
p_proc++;
selector_ldt += 1 << 3;
}
}
/*======================================================================*
init_idt_desc
*----------------------------------------------------------------------*
386
*======================================================================*/
PUBLIC void init_idt_desc(unsigned char vector, u8 desc_type, int_handler handler, unsigned char privilege)
{
GATE * p_gate = &idt[vector];
u32 base = (u32)handler;
p_gate->offset_low = base & 0xFFFF;
p_gate->selector = SELECTOR_KERNEL_CS;
p_gate->dcount = 0;
p_gate->attr = desc_type | (privilege << 5);
p_gate->offset_high = (base >> 16) & 0xFFFF;
}
/*======================================================================*
seg2phys
*----------------------------------------------------------------------*
*======================================================================*/
PUBLIC u32 seg2phys(u16 seg)
{
DESCRIPTOR* p_dest = &gdt[seg >> 3];
return (p_dest->base_high << 24) | (p_dest->base_mid << 16) | (p_dest->base_low);
}
/*======================================================================*
init_descriptor
*----------------------------------------------------------------------*
*======================================================================*/
PRIVATE void init_descriptor(DESCRIPTOR * p_desc, u32 base, u32 limit, u16 attribute)
{
p_desc->limit_low = limit & 0x0FFFF; // 段界限 1 (2 字节)
p_desc->base_low = base & 0x0FFFF; // 段基址 1 (2 字节)
p_desc->base_mid = (base >> 16) & 0x0FF; // 段基址 2 (1 字节)
p_desc->attr1 = attribute & 0xFF; // 属性 1
p_desc->limit_high_attr2 = ((limit >> 16) & 0x0F) |
(attribute >> 8) & 0xF0;// 段界限 2 + 属性 2
p_desc->base_high = (base >> 24) & 0x0FF; // 段基址 3 (1 字节)
}
/*======================================================================*
exception_handler
*----------------------------------------------------------------------*
*======================================================================*/
PUBLIC void exception_handler(int vec_no, int err_code, int eip, int cs, int eflags)
{
int i;
int text_color = 0x74; /* 灰底红字 */
char err_description[][64] = { "#DE Divide Error",
"#DB RESERVED",
"— NMI Interrupt",
"#BP Breakpoint",
"#OF Overflow",
"#BR BOUND Range Exceeded",
"#UD Invalid Opcode (Undefined Opcode)",
"#NM Device Not Available (No Math Coprocessor)",
"#DF Double Fault",
" Coprocessor Segment Overrun (reserved)",
"#TS Invalid TSS",
"#NP Segment Not Present",
"#SS Stack-Segment Fault",
"#GP General Protection",
"#PF Page Fault",
"— (Intel reserved. Do not use.)",
"#MF x87 FPU Floating-Point Error (Math Fault)",
"#AC Alignment Check",
"#MC Machine Check",
"#XF SIMD Floating-Point Exception"
};
/* 通过打印空格的方式清空屏幕的前五行,并把 disp_pos 清零 */
disp_pos = 0;
for(i=0;i<80*5;i++){
disp_str(" ");
}
disp_pos = 0;
disp_color_str("Exception! --> ", text_color);
disp_color_str(err_description[vec_no], text_color);
disp_color_str("\n\n", text_color);
disp_color_str("EFLAGS:", text_color);
disp_int(eflags);
disp_color_str("CS:", text_color);
disp_int(cs);
disp_color_str("EIP:", text_color);
disp_int(eip);
if(err_code != 0xFFFFFFFF){
disp_color_str("Error code:", text_color);
disp_int(err_code);
}
}

43
kernel/start.c Normal file
View File

@ -0,0 +1,43 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
start.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "proto.h"
#include "string.h"
#include "global.h"
/*======================================================================*
cstart
*======================================================================*/
PUBLIC void cstart()
{
disp_str("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n-----\"cstart\" begins-----\n");
// 将 LOADER 中的 GDT 复制到新的 GDT 中
memcpy( &gdt, // New GDT
(void*)(*((u32*)(&gdt_ptr[2]))), // Base of Old GDT
*((u16*)(&gdt_ptr[0])) + 1 // Limit of Old GDT
);
// gdt_ptr[6] 共 6 个字节0~15:Limit 16~47:Base。用作 sgdt 以及 lgdt 的参数。
u16* p_gdt_limit = (u16*)(&gdt_ptr[0]);
u32* p_gdt_base = (u32*)(&gdt_ptr[2]);
*p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) - 1;
*p_gdt_base = (u32)&gdt;
// idt_ptr[6] 共 6 个字节0~15:Limit 16~47:Base。用作 sidt 以及 lidt 的参数。
u16* p_idt_limit = (u16*)(&idt_ptr[0]);
u32* p_idt_base = (u32*)(&idt_ptr[2]);
*p_idt_limit = IDT_SIZE * sizeof(GATE) - 1;
*p_idt_base = (u32)&idt;
init_prot();
disp_str("-----\"cstart\" finished-----\n");
}

133
kernel/syscall.asm Normal file
View File

@ -0,0 +1,133 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; syscall.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%include "sconst.inc"
; TODO 系统调用的设置
_NR_get_ticks equ 0 ; 要跟 global.c 中 sys_call_table 的定义相对应!
_NR_print_str equ 1 ; 要跟 global.c 中 sys_call_table 的定义相对应!
_NR_ms_delay equ 2 ; 要跟 global.c 中 sys_call_table 的定义相对应!
_NR_p equ 3 ; 要跟 global.c 中 sys_call_table 的定义相对应!
_NR_v equ 4 ; 要跟 global.c 中 sys_call_table 的定义相对应!
INT_VECTOR_SYS_CALL equ 0x90
; TODO 系统调用的导出符号
global get_ticks
global print_str
global sys_print_str
global ms_delay ;外部调用,准备现场,发出中断
global sys_ms_delay ;具体实现将ebx压入内核栈调用sys_ms_delay_proc(int)
global sys_p
global sys_v
global p
global v
; TODO 导入外部函数
extern sys_ms_delay_proc
extern disp_str
extern sys_p_impl
extern sys_v_impl
bits 32
[section .text]
; ====================================================================
; get_ticks
; ====================================================================
get_ticks:
mov eax, _NR_get_ticks
int INT_VECTOR_SYS_CALL
ret
; ====================================================================
; TODO 不分配时间片sys_ms_delay
; ====================================================================
ms_delay:
;准备现场,发出中断
mov eax, _NR_ms_delay
push ebx
mov ebx, [esp+8]
int INT_VECTOR_SYS_CALL
pop ebx
ret
sys_ms_delay:
;系统调用
pusha
push ebx
call sys_ms_delay_proc ;保存ebx后调用sys_ms_delay_proc
pop ebx
popa
ret
; ====================================================================
; TODO 打印字符串sys_print_str
; ====================================================================
print_str:
;准备现场,发出中断
mov eax, _NR_print_str
push ebx
mov ebx, [esp+8]
int INT_VECTOR_SYS_CALL
pop ebx
ret
sys_print_str:
;系统调用
pusha
push ebx
call disp_str
pop ebx
popa
ret
; ====================================================================
; TODO PV
; ====================================================================
p:
;准备现场,发出中断
mov eax, _NR_p
push ebx
mov ebx, [esp+8]
int INT_VECTOR_SYS_CALL
pop ebx
ret
v:
;准备现场,发出中断
mov eax, _NR_v
push ebx
mov ebx, [esp+8]
int INT_VECTOR_SYS_CALL
pop ebx
ret
sys_p:
;系统调用
pusha
push ebx
call sys_p_impl
pop ebx
popa
ret
sys_v:
;系统调用
pusha
push ebx
call sys_v_impl
pop ebx
popa
ret

74
lib/klib.c Normal file
View File

@ -0,0 +1,74 @@
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
klib.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "const.h"
#include "protect.h"
#include "proto.h"
#include "string.h"
#include "global.h"
/*======================================================================*
itoa
*======================================================================*/
PUBLIC char * itoa(char * str, int num)/* 数字前面的 0 不被显示出来, 比如 0000B800 被显示成 B800 */
{
char * p = str;
char ch;
int i;
int flag = FALSE;
*p++ = '0';
*p++ = 'x';
if(num == 0){
*p++ = '0';
}
else{
for(i=28;i>=0;i-=4){
ch = (num >> i) & 0xF;
if(flag || (ch > 0)){
flag = TRUE;
ch += '0';
if(ch > '9'){
ch += 7;
}
*p++ = ch;
}
}
}
*p = 0;
return str;
}
/*======================================================================*
disp_int
*======================================================================*/
PUBLIC void disp_int(int input)
{
char output[16];
itoa(output, input);
disp_str(output);
}
/*======================================================================*
delay
*======================================================================*/
PUBLIC void delay(int time)
{
int i, j, k;
for(k=0;k<time;k++){
/*for(i=0;i<10000;i++){ for Virtual PC */
for(i=0;i<10;i++){/* for Bochs */
for(j=0;j<10000;j++){}
}
}
}

198
lib/kliba.asm Normal file
View File

@ -0,0 +1,198 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; klib.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
%include "sconst.inc"
; 导入全局变量
extern disp_pos
[SECTION .text]
; 导出函数
global disp_str
global disp_color_str
global out_byte
global in_byte
global enable_irq
global disable_irq
; ========================================================================
; void disp_str(char * info);
; ========================================================================
disp_str:
push ebp
mov ebp, esp
mov esi, [ebp + 8] ; pszInfo
mov edi, [disp_pos]
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 [disp_pos], edi
pop ebp
ret
; ========================================================================
; void disp_color_str(char * info, int color);
; ========================================================================
disp_color_str:
push ebp
mov ebp, esp
mov esi, [ebp + 8] ; pszInfo
mov edi, [disp_pos]
mov ah, [ebp + 12] ; color
.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 [disp_pos], edi
pop ebp
ret
; ========================================================================
; void out_byte(u16 port, u8 value);
; ========================================================================
out_byte:
mov edx, [esp + 4] ; port
mov al, [esp + 4 + 4] ; value
out dx, al
nop ; 一点延迟
nop
ret
; ========================================================================
; u8 in_byte(u16 port);
; ========================================================================
in_byte:
mov edx, [esp + 4] ; port
xor eax, eax
in al, dx
nop ; 一点延迟
nop
ret
; ========================================================================
; void disable_irq(int irq);
; ========================================================================
; Disable an interrupt request line by setting an 8259 bit.
; Equivalent code:
; if(irq < 8){
; out_byte(INT_M_CTLMASK, in_byte(INT_M_CTLMASK) | (1 << irq));
; }
; else{
; out_byte(INT_S_CTLMASK, in_byte(INT_S_CTLMASK) | (1 << irq));
; }
disable_irq:
mov ecx, [esp + 4] ; irq
pushf
cli
mov ah, 1
rol ah, cl ; ah = (1 << (irq % 8))
cmp cl, 8
jae disable_8 ; disable irq >= 8 at the slave 8259
disable_0:
in al, INT_M_CTLMASK
test al, ah
jnz dis_already ; already disabled?
or al, ah
out INT_M_CTLMASK, al ; set bit at master 8259
popf
mov eax, 1 ; disabled by this function
ret
disable_8:
in al, INT_S_CTLMASK
test al, ah
jnz dis_already ; already disabled?
or al, ah
out INT_S_CTLMASK, al ; set bit at slave 8259
popf
mov eax, 1 ; disabled by this function
ret
dis_already:
popf
xor eax, eax ; already disabled
ret
; ========================================================================
; void enable_irq(int irq);
; ========================================================================
; Enable an interrupt request line by clearing an 8259 bit.
; Equivalent code:
; if(irq < 8){
; out_byte(INT_M_CTLMASK, in_byte(INT_M_CTLMASK) & ~(1 << irq));
; }
; else{
; out_byte(INT_S_CTLMASK, in_byte(INT_S_CTLMASK) & ~(1 << irq));
; }
;
enable_irq:
mov ecx, [esp + 4] ; irq
pushf
cli
mov ah, ~1
rol ah, cl ; ah = ~(1 << (irq % 8))
cmp cl, 8
jae enable_8 ; enable irq >= 8 at the slave 8259
enable_0:
in al, INT_M_CTLMASK
and al, ah
out INT_M_CTLMASK, al ; clear bit at master 8259
popf
ret
enable_8:
in al, INT_S_CTLMASK
and al, ah
out INT_S_CTLMASK, al ; clear bit at slave 8259
popf
ret

116
lib/string.asm Normal file
View File

@ -0,0 +1,116 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; string.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
[SECTION .text]
; 导出函数
global memcpy
global memset
global strcpy
; ------------------------------------------------------------------------
; void* memcpy(void* es:p_dst, void* ds:p_src, int size);
; ------------------------------------------------------------------------
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 结束-------------------------------------------------------------
; ------------------------------------------------------------------------
; void memset(void* p_dst, char ch, int size);
; ------------------------------------------------------------------------
memset:
push ebp
mov ebp, esp
push esi
push edi
push ecx
mov edi, [ebp + 8] ; Destination
mov edx, [ebp + 12] ; Char to be putted
mov ecx, [ebp + 16] ; Counter
.1:
cmp ecx, 0 ; 判断计数器
jz .2 ; 计数器为零时跳出
mov byte [edi], dl ; ┓
inc edi ; ┛
dec ecx ; 计数器减一
jmp .1 ; 循环
.2:
pop ecx
pop edi
pop esi
mov esp, ebp
pop ebp
ret ; 函数结束,返回
; ------------------------------------------------------------------------
; ------------------------------------------------------------------------
; char* strcpy(char* p_dst, char* p_src);
; ------------------------------------------------------------------------
strcpy:
push ebp
mov ebp, esp
mov esi, [ebp + 12] ; Source
mov edi, [ebp + 8] ; Destination
.1:
mov al, [esi] ; ┓
inc esi ; ┃
; ┣ 逐字节移动
mov byte [edi], al ; ┃
inc edi ; ┛
cmp al, 0 ; 是否遇到 '\0'
jnz .1 ; 没遇到就继续循环,遇到就结束
mov eax, [ebp + 8] ; 返回值
pop ebp
ret ; 函数结束,返回
; strcpy 结束-------------------------------------------------------------