Compare commits

...

26 Commits
ch1 ... master

Author SHA1 Message Date
ridethepig
ce1293fd48 firework and report 2022-11-30 10:21:29 +08:00
ridethepig
f886476139 forkbomb over 10W, god knows still some conflicts? 2022-11-23 22:54:45 +08:00
ridethepig
2f336e536b forkbomb 2W 2022-11-23 22:15:57 +08:00
ridethepig
9f77409a4f testwait.bin ok 2022-11-23 17:09:34 +08:00
ridethepig
359c4ab5e2 fork.c fin 2022-11-23 10:34:00 +08:00
ridethepig
52a5d090ea get familiar with new code and babystep in fork 2022-11-22 00:54:03 +08:00
ridethepig
a9e2c29c26 before lab6 2022-11-21 23:07:57 +08:00
ridethepig
890505eecb lab5 update report 2022-11-04 13:59:53 +08:00
ridethepig
32b816a4e1 lab5 extra task ok 2022-11-04 13:57:40 +08:00
ridethepig
f13d09f336 lab5 report 2022-11-04 12:33:43 +08:00
ridethepig
9c17cfaecf delay syscall ok 2022-10-30 01:53:43 +08:00
ridethepig
6c1c2e05ad load program ok 2022-10-30 00:18:22 +08:00
ridethepig
dd540f15d0 before lab5 2022-10-29 19:48:58 +08:00
ridethepig
a71d15ad67 lab4 report 2022-10-29 19:46:57 +08:00
ridethepig
acc9cdbf63 lab4 ok, music 2022-10-19 21:37:15 +08:00
ridethepig
4b58f8019d before lab4 2022-10-18 18:23:01 +08:00
ridethepig
f8828d6c4e update report 2022-10-07 23:31:12 +08:00
ridethepig
16798301ec add lab report 2022-10-02 22:35:55 +08:00
ridethepig
5b04836546 task3 2022-10-02 21:35:23 +08:00
ridethepig
b758be84a8 task1 2022-10-02 15:07:40 +08:00
ridethepig
9b1f505537 before ch3 2022-10-02 12:54:35 +08:00
ridethepig
48dd43409d update report 2022-09-22 11:47:05 +08:00
ridethepig
274d517426 lab2 ok 2022-09-21 22:15:05 +08:00
ridethepig
a0dacc4b9e print all cluster numbers ok 2022-09-21 14:25:18 +08:00
ridethepig
308d1caf1e first cluster find ok 2022-09-21 13:42:12 +08:00
ridethepig
b8277f454f before ch2 2022-09-20 20:02:21 +08:00
85 changed files with 7128 additions and 241 deletions

3
.gitignore vendored
View File

@ -1,2 +1 @@
*.bin obj
*.img

14
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/inc"
],
"defines": [],
"cStandard": "gnu99",
"compilerArgs": ["-m32"]
}
],
"version": 4
}

22
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,22 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "kernel-debug",
"type": "cppdbg",
"request": "launch",
"miDebuggerServerAddress": "127.0.0.1:1234",
"program": "${workspaceFolder}/obj/kern/kernel.dbg",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"logging": {
"engineLogging": false
},
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
}
]
}

11
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"editor.detectIndentation": false,
"editor.tabSize": 4,
"editor.insertSpaces": false,
"files.associations": {
"*.h": "c",
"*.c": "c",
"*.asm": "nasm",
"Makefrag": "makefile"
}
}

Binary file not shown.

143
Makefile
View File

@ -1,23 +1,136 @@
CC = gcc #
ASM = nasm # make的主文件
QEMU = qemu #
IMG = boot.bin
boot.bin: boot.asm # 文件夹
$(ASM) $< -o $@ # OBJ用于存放编译出来的可重定位文件
OBJDIR := obj
# INC用于存放各种头文件(*.h)
INCDIR := inc
compile: boot.bin # 编译以及日常工具
CC := gcc
# 汇编器
AS := nasm
# 静态库编辑器
AR := ar
# 链接器
LD := ld
# 复制文件
OBJCOPY := objcopy
# 反编译
OBJDUMP := objdump
# 查询可重定位文件符号表
NM := nm
.DEFAULT_GOAL=compile DEFS :=
run: compile # gcc的相关命令参数
$(QEMU) -boot order=c -drive file=$(IMG),format=raw # $(DEFS) 定义一些可能的参数
# -O0 0优化保证程序按照代码语义走而不被优化方便调试
# -fno-builtin 静止使用gcc内置函数具体查手册
CFLAGS := $(CFLAGS) $(DEFS) -O0 -fno-builtin
# -I 编译时去指定文件夹查找头文件
# -MD 一个黑科技暂时可以不需要了解总之是在头文件依赖变动的时候能够及时更新target
CFLAGS += -I $(INCDIR) -MD
# -fno-stack-protector 禁止栈保护(金丝雀保护机制,内核代码扛不住)
CFLAGS += -fno-stack-protector
# -std=gnu99 规定编译的语言规范为gnu99
CFLAGS += -std=gnu99
# -fno-pie 不创建动态链接库
CFLAGS += -fno-pie
# -static 编译静态程序
# -m32 编译32位程序
CFLAGS += -static -m32
# -g 打开gdb调试信息能够允许gdb的时候调试
CFLAGS += -g
# 一车的warning在编译的时候可能会很有用
CFLAGS += -Wall -Wno-format -Wno-unused -Werror
# ld链接器的相关命令参数
# -m elf_i386 链接的格式为i386
LDFLAGS := -m elf_i386
# -nostdlib 不链接gcc的标准库用库只能用命令行的
LDFLAGS += -nostdlib
# 获取gcc的库文件除法取模会用到
GCC_LIB := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
# 记录每个OBJDIR里存放的每个子文件夹
# 对于这个系统来说,最后的值为./obj/kern和./obj/boot
OBJDIRS :=
# 保证all是第一个target这样make的时候会先执行all
# all的依赖会在kern/Makefrag中填充
all:
# xv6黑科技获取编译命令如果命令较新则重新编译所有文件
.PRECIOUS: $(OBJDIR)/.vars.% \
$(OBJDIR)/kern/%.o $(OBJDIR)/kern/%.d \
$(OBJDIR)/user/%.o $(OBJDIR)/user/%.d \
$(OBJDIR)/lib/%.o $(OBJDIR)/lib/kern/%.o $(OBJDIR)/lib/user/%.o \
$(OBJDIR)/lib/%.d $(OBJDIR)/lib/kern/%.d $(OBJDIR)/lib/user/%.d
$(OBJDIR)/.vars.%: FORCE
@echo "$($*)" | cmp -s $@ || echo "$($*)" > $@
.PHONY: FORCE
# 导入两个文件两个文件分别编写方便管理也让主makefile更加舒服
include boot/Makefrag
include lib/Makefrag
include kern/Makefrag
include user/Makefrag
# FAT32镜像文件
IMAGE = $(OBJDIR)/a.img
$(IMAGE): $(OBJDIR)/boot/boot.bin $(OBJDIR)/boot/loader.bin $(OBJDIR)/kern/kernel.bin $(USER_BINS)
@stat $@ 1>/dev/null 2>/dev/null || dd if=/dev/zero of=$@ bs=512 count=102400
@mkfs.vfat -F 32 -s8 $@
@dd if=$< of=$@ bs=1 count=422 seek=90 conv=notrunc
@sudo mount -o loop $@ /mnt
@sudo cp $(OBJDIR)/boot/loader.bin /mnt -v
@sudo cp $(OBJDIR)/kern/kernel.bin /mnt -v
@sudo cp $(USER_BINS) /mnt
@sudo umount /mnt
all: $(IMAGE)
clean: clean:
rm boot.bin @rm -rf $(OBJDIR)
debug: compile run: $(IMAGE)
$(QEMU) -boot order=c -drive file=$(IMG),format=raw -S -s & @qemu-system-i386 \
gdb -ex 'set tdesc filename target.xml' -ex 'target remote localhost:1234' -boot order=a \
-drive file=$(IMAGE),format=raw \
.PHONY: compile clean run debug gdb: $(IMAGE)
@qemu-system-i386 \
-boot order=a \
-drive file=$(IMAGE),format=raw \
-s -S \
gdb-no-graphic: $(IMAGE)
@qemu-system-i386 \
-nographic \
-boot order=a \
-drive file=$(IMAGE),format=raw \
-s -S \
# 调试的内核代码elf
KERNDBG := $(OBJDIR)/kern/kernel.dbg
monitor: $(IMAGE)
@gdb \
-ex 'set confirm off' \
-ex 'target remote localhost:1234' \
-ex 'file $(KERNDBG)'
# 黑科技时间,获取每个.c对应的头文件依赖
# 挺难整明白的不建议一开始整明白反正从xv6上抄的不明觉厉
$(OBJDIR)/.deps: $(foreach dir, $(OBJDIRS), $(wildcard $(OBJDIR)/$(dir)/*.d))
@mkdir -p $(@D)
@perl mergedep.pl $@ $^
-include $(OBJDIR)/.deps
.PHONY: all clean run gdb gdb-no-graphic monitor

View File

@ -1,31 +0,0 @@
org 0x7c00 ; 告诉编译器程序加载到7c00处
mov ax, cs
mov ds, ax
mov es, ax
call DispStr ; 调用显示字符串例程
jmp $ ; 无限循环
DispStr:
mov ax, 0600h
mov bx, 0h
mov cx, 0h
mov dh, 25-1
mov dl, 80-1 ; vga text-mode: 80x25
int 10h ; clear screen
mov ax, 0200h
mov bx, 0h
mov dx, 1326h ; 19=13H, 38=26H
int 10h ; set cursor position
mov ax, NWPU
mov bp, ax ; ES:BP = 串地址, es has been set to the same seg
mov cx, 4 ; CX = 串长度
mov ax, 01300h ; AH = 13, AL = 01h
mov bx, 00f9h ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
int 10h ; 10h 号中断
mov ax, 0200h
mov bx, 0h
mov dx, 0h
int 10H ; reset cursor to 0,0
ret
NWPU: db "NWPU"
times 510-($-$$) db 0 ; 填充剩下的空间使生成的二进制代码恰好为512字节
dw 0xaa55 ; 结束标志

28
boot/Makefrag Normal file
View File

@ -0,0 +1,28 @@
#
# makefile的boot部分
# 将会导入到根目录的makefile文件
#
OBJDIRS += boot
LOADER_OBJS := $(OBJDIR)/boot/loader.o $(OBJDIR)/boot/loadkernel.o
LOADER_LINKER := boot/linker.ld
$(OBJDIR)/boot/%.o: boot/%.c $(OBJDIR)/.vars.CFLAGS
@echo + cc $<
@mkdir -p $(@D)
@$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR)/boot/%.o: boot/%.asm
@echo + as obj $<
@mkdir -p $(@D)
@$(AS) -f elf -o $@ $<
$(OBJDIR)/boot/boot.bin: boot/boot.asm
@echo + as bin $<
@mkdir -p $(@D)
@$(AS) -o $@ $<
$(OBJDIR)/boot/loader.bin: $(LOADER_OBJS) $(LOADER_LINKER) $(OBJDIR)/.vars.LDFLAGS
@echo + ld $@
@$(LD) $(LDFLAGS) -s -T $(LOADER_LINKER) --oformat binary -o $@ $(LOADER_OBJS)

215
boot/boot.asm Normal file
View File

@ -0,0 +1,215 @@
;==============================================================================================================================
BaseOfStack equ 0x07c00 ; Boot状态下堆栈基地址
STACK_ADDR equ BaseOfStack-28 ; 堆栈栈顶
BaseOfBoot equ 1000h ; added by mingxuan 2020-9-12
OffsetOfBoot equ 7c00h ; load Boot sector to BaseOfBoot:OffsetOfBoot
OffsetOfActiPartStartSec equ 7e00h ; 活动分区的起始扇区号相对于BaseOfBoot的偏移量 ;added by mingxuan 2020-9-12
; 该变量来自分区表保存在该内存地址用于在os_boot和loader中查找FAT32文件
BOOT_FAT32_INFO equ 0x5A ; 位于boot中的FAT32配置信息的长度
OSLOADER_SEG equ 0x1000 ; 段地址
OSLOADER_OFF equ 0x5000 ; 段偏移
OSLOADER_ADDR equ 0x15000 ; loader地址
BUF_OFF equ 0x0000 ; 加载FAT表的临时存储
BUF_ADDR equ 0x10000 ; 加载FAT表的临时存储
DIR_PER_SECTOR equ 0x10 ; 每个扇区所容纳的目录 BYTE
; 扩展磁盘服务所使用的地址包
DAP_SECTOR equ 8 ; 起始扇区号
DAP_BUFFER_SEG equ 10 ; 缓冲区地址
DAP_BUFFER_OFF equ 12 ; 缓冲区地址
DAP_READ_SECTORS equ 14 ; 要处理的扇区数
DAP_PACKET_SIZE equ 16 ; 包的大小为24字节
CURRENT_CLUSTER equ 20 ; 当前正在处理的簇号 DWORD
FAT_START_SECTOR equ 24 ; FAT表的起始扇区号 ;added by mingxuan 2020-9-17
DATA_START_SECTOR equ 28 ; 数据区起始扇区号 ;added by mingxuan 2020-9-17
; 目录项结构
OFF_START_CLUSTER_HIGH equ 20 ; 起始簇号高位 WORD
OFF_START_CLUSTER_LOW equ 26 ; 起始簇号低位 WORD
; 相关常量
DIR_NAME_FREE equ 0x00 ; 该项是空闲的
DIR_ENTRY_SIZE equ 32 ; 每个目录项的尺寸
; 簇属性
CLUSTER_MASK equ 0FFFFFFFH ; 簇号掩码
CLUSTER_LAST equ 0FFFFFF8H ;0xFFFFFFF8-0xFFFFFFFF表示文件的最后一个簇
; added by mingxuan 2020-9-12
BPB_BytesPerSec equ (OffsetOfBoot + 0xb) ;每扇区字节数
BPB_SecPerClu equ (OffsetOfBoot + 0xd) ;每簇扇区数
BPB_RsvdSecCnt equ (OffsetOfBoot + 0xe) ;保留扇区数
BPB_NumFATs equ (OffsetOfBoot + 0x10) ;FAT表数
BPB_RootEntCnt equ (OffsetOfBoot + 0x11) ;FAT32不使用
BPB_TotSec16 equ (OffsetOfBoot + 0x13) ;扇区总数
BPB_Media equ (OffsetOfBoot + 0x15) ;介质描述符
BPB_FATSz16 equ (OffsetOfBoot + 0x16) ;每个FAT表的大小扇区数(FAT32不使用)
BPB_SecPerTrk equ (OffsetOfBoot + 0x18) ;每磁道扇区数
BPB_NumHeads equ (OffsetOfBoot + 0x1a) ;磁头数
BPB_HiddSec equ (OffsetOfBoot + 0x1c) ;分区已使用扇区数
BPB_TotSec32 equ (OffsetOfBoot + 0x20) ;文件系统大小扇区数
BPB_FATSz32 equ (OffsetOfBoot + 0x24) ;每个FAT表大小扇区数
BPB_ExtFlags equ (OffsetOfBoot + 0x28) ;标记
BPB_FSVer equ (OffsetOfBoot + 0x2a) ;版本号
BPB_RootClus equ (OffsetOfBoot + 0x2c) ;根目录簇号
BPB_FSInfo equ (OffsetOfBoot + 0x30) ;FSINFO扇区号
BS_BackBootSec equ (OffsetOfBoot + 0x32) ;备份引导扇区位置
BPB_Reserved equ (OffsetOfBoot + 0x34) ;未使用
BS_DrvNum equ (OffsetOfBoot + 0x40) ;设备号
BS_Reserved1 equ (OffsetOfBoot + 0x41) ;未使用
BS_BootSig equ (OffsetOfBoot + 0x42) ;扩展引导标志
BS_VolID equ (OffsetOfBoot + 0x43) ;卷序列号
BS_VolLab equ (OffsetOfBoot + 0x47) ;卷标
BS_FilSysType equ (OffsetOfBoot + 0x52) ;文件系统类型
;==============================================================================================================================
org (07c00h + BOOT_FAT32_INFO) ;FAT322规范规定第90~512个字节(共423个字节)是引导程序
;modified by mingxuan 2020-9-16
START:
cld
mov ax, cs
mov ds, ax
mov ss, ax
mov ax, OSLOADER_SEG
mov es, ax ;deleted by mingxuan 2020-9-13
mov bp, BaseOfStack
mov sp, STACK_ADDR
movzx eax, word [BPB_RsvdSecCnt]
mov [bp - FAT_START_SECTOR], eax
; 计算数据区起始扇区号 ; added by mingxuan 2020-9-17
movzx cx, byte [BPB_NumFATs]
_CALC_DATA_START:
add eax, [BPB_FATSz32]
loop _CALC_DATA_START
mov [bp - DATA_START_SECTOR], eax
mov word [bp - DAP_PACKET_SIZE], 10h
mov word [bp - DAP_BUFFER_SEG], OSLOADER_SEG
jmp _SEARCH_LOADER
LoaderName db "LOADER BIN" ; 第二阶段启动程序 FDOSLDR.BIN
ReadSector:
pusha
mov ah, 42h ;ah是功能号扩展读对应0x42
lea si, [bp - DAP_PACKET_SIZE] ;使用扩展int13时DAP结构体首地址传给si
mov dl, [BS_DrvNum] ;modified by mingxuan 2020-9-17
int 13h
jc $
popa
ret
InlineReadDataSector:
sub eax, 2
movzx edx, byte [BPB_SecPerClu]
mul edx
add eax, [bp - DATA_START_SECTOR]
mov [bp - DAP_SECTOR], eax
movzx edx, byte [BPB_SecPerClu]
mov [bp - DAP_READ_SECTORS], dx
mov [bp - DAP_BUFFER_OFF], bx
call ReadSector
ret
; 根据簇号计算扇区号
; Comments, added by mingxuan 2020-9-10
;====================================================================
; 检查是否还有下一个簇(读取FAT表的相关信息)
; N = 数据簇号
; FAT_BYTES(在FAT表中的偏移) = N*4 (FAT32)
; FAT_SECTOR = FAT_BYTES / BPB_BytesPerSec
; FAT_OFFSET = FAT_BYTES % BPB_BytesPerSec
;====================================================================
NextCluster: ;added by yangxiaofeng 2021-12-1
pushad
mov eax, [bp - CURRENT_CLUSTER]
mov ebx, 4
mul ebx
movzx ebx, word [BPB_BytesPerSec]
div ebx
add eax, [bp - FAT_START_SECTOR]
mov [bp - DAP_SECTOR], eax
mov word [bp - DAP_READ_SECTORS], 1
mov word [bp - DAP_BUFFER_OFF], BUF_OFF
call ReadSector
mov bx, dx
mov eax, [es:bx + BUF_OFF]
mov [bp - DATA_START_SECTOR - 6], eax
popad
ret
_SEARCH_LOADER:
mov eax, [BPB_RootClus]
_NEXT_ROOT_CLUSTER: ;这个时候eax拿着的是当前的簇号
mov [bp - CURRENT_CLUSTER], eax
mov bx, BUF_OFF
call InlineReadDataSector
mov di, BUF_OFF
; 这里dl拿着的是BPB_SecPerClu
_NEXT_ROOT_SECTOR:
mov bl, DIR_PER_SECTOR
_NEXT_ROOT_ENTRY:
mov si, LoaderName
mov cx, 11
repe cmpsb
and di, DIR_ENTRY_SIZE
jcxz _FOUND_LOADER
add di, DIR_ENTRY_SIZE
dec bl
jnz _NEXT_ROOT_ENTRY
dec dl
jz _CHECK_NEXT_ROOT_CLUSTER
jmp _NEXT_ROOT_SECTOR
_CHECK_NEXT_ROOT_CLUSTER: ; 检查是否还有下一个簇
call NextCluster ;added by yangxiaofeng 2021-12-1
cmp eax, CLUSTER_LAST ;CX >= 0FFFFFF8H则意味着没有更多的簇了
jb _NEXT_ROOT_CLUSTER
jmp $
_FOUND_LOADER:
movzx eax, word [es:di + OFF_START_CLUSTER_HIGH]; 起始簇号高32位
shl eax, 16
mov ax, [es:di + OFF_START_CLUSTER_LOW] ; 起始簇号低32位
mov bx, OSLOADER_OFF
;这个时候eax拿着的是当前的簇号ebx存放加载的地址
_LOAD_LOADER:
mov dword [bp - CURRENT_CLUSTER], eax
call InlineReadDataSector
movzx eax, byte [BPB_SecPerClu]
mov cx, [BPB_BytesPerSec]
mul cx
add ebx, eax
call NextCluster
cmp eax, CLUSTER_LAST ;CX >= 0FFFFFF8H则意味着没有更多的簇了
jb _LOAD_LOADER
_RUN_LOADER:
jmp OSLOADER_SEG : OSLOADER_OFF
times 510 - BOOT_FAT32_INFO - ($-$$) db 0 ; 填充剩下的空间使生成的二进制代码恰好为512字节
dw 0xaa55 ; 结束标志

324
boot/inc/pm.inc Normal file
View File

@ -0,0 +1,324 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; 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 字节
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

30
boot/linker.ld Normal file
View File

@ -0,0 +1,30 @@
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
BASE_ADDRESS = 0x5000;
LOADER_SEG_PHYADDR = 0x10000;
SECTIONS
{
. = BASE_ADDRESS;
.text.s16 : {
obj/boot/loader.o(.text.s16)
}
. = ALIGN(32);
. += LOADER_SEG_PHYADDR;
text_lma = . - LOADER_SEG_PHYADDR;
.text ALIGN(32): AT(text_lma) {
*(.text .stub .text.* .gnu.linkonce.t.* .rodata .rodata.*)
}
. = ALIGN(32);
data_lma = . - LOADER_SEG_PHYADDR;
.data ALIGN(32): AT(data_lma) {
*(*)
}
/DISCARD/ : {
*(.eh_frame .note.GNU-stack)
}
}

240
boot/loader.asm Normal file
View File

@ -0,0 +1,240 @@
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; loader.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
BaseOfLoader equ 01000h ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader equ 05000h ; LOADER.BIN 被加载到的位置 ---- 偏移地址
LoaderSegPhyAddr equ 10000h ; LOADER.BIN 被加载到的位置 ---- 物理地址 (= Base * 10h)
BaseOfStack equ OffsetOfLoader
[SECTION .text.s16]
ALIGN 16
[BITS 16]
global _start
_start:
jmp Main ; Start
%include "./boot/inc/pm.inc"
ALIGN 4
; 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 ; 显存首地址
; 准备lgdt必要的数据结构 --------------------------------------------------------------------------------------------------------------------------------------------
GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1 ; gdt界限
dd LoaderSegPhyAddr + LABEL_GDT ; 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
Main: ; <--- 从这里开始 *************
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
mov cx, 02000h
mov ah, 01h
int 10h
; 下面准备跳入保护模式 -------------------------------------------
; 加载 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:LABEL_PM_START
; 从此以后的代码在保护模式下执行 ----------------------------------------------------
; 32 位代码段. 由实模式跳入 ---------------------------------------------------------
[SECTION .text]
ALIGN 32
[BITS 32]
extern load_kernel
LABEL_PM_START:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorFlatRW
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov esp, TopOfStack
call SetupPaging; 启动分页机制,不过在这个实验大家可以不用管,就当是一个能够扩展访存范围的牛逼玩意
jmp SelectorFlatC:load_kernel
;***************************************************************
; 内存看上去是这样的:
; ┃ ┃
; ┃ . ┃
; ┃ . ┃
; ┃ . ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■┃
; ┃■■■■■■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。
;
; 启动分页机制 --------------------------------------------------------------
PageDirBase equ 100000h ; 页目录开始地址: 1M
PageTblBase equ 101000h ; 页表开始地址: 1M + 4K
VirtAddrBase1 equ 0h ; 线性地址的0~128MB映射到0~128MB的物理地址
VirtAddrBase2 equ 0c0000000h; 线性地址的3GB~3GB+128MB映射到0~128MB的物理地址
SetupPaging:
; 根据内存大小计算应初始化多少PDE以及多少页表
mov eax, 8000000h ; qemu虚拟机默认内存128MB这里简化实现省去了实模式代码里中断向BIOS询问可用内存大小的步骤
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 ; 暂存页表个数
; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.
InitDirPages1:
; 初始化0~128MB的根目录页表
mov edi, PageDirBase ; 此段首地址为 PageDirBase
mov eax, PageTblBase | PG_P | PG_USU | PG_RWW
.1:
stosd
add eax, 4096 ; 为了简化, 所有页表在内存中是连续的.
loop .1
InitDirPages2:
; 初始化3GB + 0 ~ 3GB + 128MB的根目录页表
mov ecx, [esp]
mov edi, PageDirBase + 0c00h
.1:
stosd
add eax, 4096
loop .1
InitTblPages1:
; 再初始化所有0~128MB页表
pop eax ; 页表个数
mov ebx, 1024 ; 每个页表 1024 个 PTE
mul ebx
mov ecx, eax ; PTE个数 = 页表个数 * 1024
push ecx
mov edi, PageTblBase ; 此段首地址为 PageTblBase
mov eax, PG_P | PG_USU | PG_RWW
.1:
stosd
add eax, 4096 ; 每一页指向 4K 的空间
loop .1
InitTblPages2:
pop ecx
mov eax, PG_P | PG_USU | PG_RWW
.1:
stosd
add eax, 4096 ; 每一页指向 4K 的空间
loop .1
mov eax, PageDirBase
mov cr3, eax
mov eax, cr0
or eax, 80000000h
mov cr0, eax
jmp short .3
.3:
nop
ret
; 分页机制启动完毕 ----------------------------------------------------------
; SECTION .data 之开始 ---------------------------------------------------------------------------------------------
[SECTION .data]
ALIGN 32
[BITS 32]
; 堆栈就在数据段的末尾
StackSpace: times 1000h db 0
TopOfStack equ $ ; 栈顶

228
boot/loadkernel.c Normal file
View File

@ -0,0 +1,228 @@
#include <type.h>
#include <elf.h>
#include <fat32.h>
#include <x86.h>
/*
*
*/
#define TERMINAL_COLUMN 80
#define TERMINAL_ROW 25
#define TERMINAL_POS(row, column) ((u16)(row) * TERMINAL_COLUMN + (column))
/*
*
*/
#define DEFAULT_COLOR 0x0f00
/*
* content数据2disp_pos个字符
* 0001018010
* mov word [gs:disp_pos * 2], content
*/
inline static void
write_to_terminal(u16 disp_pos, u16 content)
{
asm(
"mov %1, %%gs:(%0)" ::"r"(disp_pos * 2), "r"(content)
: "memory");
}
/*
*
*/
void
clear_screen()
{
for (int i = 0; i < TERMINAL_ROW; i++)
for (int j = 0; j < TERMINAL_COLUMN; j++)
write_to_terminal(TERMINAL_POS(i, j),
DEFAULT_COLOR | ' ');
}
void *
memset(void *v, int c, size_t n)
{
char *p;
int m;
p = v;
m = n;
while (--m >= 0)
*p++ = c;
return v;
}
int
strncmp(const char *p, const char *q, size_t n)
{
while (n > 0 && *p && *p == *q)
n--, p++, q++;
if (n == 0)
return 0;
else
return (int) ((unsigned char) *p - (unsigned char) *q);
}
#define SECTSIZE 512
#define BUF_ADDR 0x30000
#define ELF_ADDR 0x40000
void
waitdisk(void)
{
// wait for disk reaady
while ((inb(0x1F7) & 0xC0) != 0x40)
/* do nothing */;
}
void
readsect(void *dst, u32 offset)
{
// wait for disk to be ready
waitdisk();
outb(0x1F2, 1); // count = 1
outb(0x1F3, offset);
outb(0x1F4, offset >> 8);
outb(0x1F5, offset >> 16);
outb(0x1F6, (offset >> 24) | 0xE0);
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
// wait for disk to be ready
waitdisk();
// read a sector
insl(0x1F0, dst, SECTSIZE/4);
}
u32 fat_start_sec;
u32 data_start_sec;
u32 elf_clus;
u32 elf_off;
u32 fat_now_sec;
struct BPB bpb;
u32
get_next_clus(u32 current_clus)
{
u32 sec = current_clus * 4 / SECTSIZE;
u32 off = current_clus * 4 % SECTSIZE;
if (fat_now_sec != fat_start_sec + sec) {
readsect((void *)BUF_ADDR, fat_start_sec + sec);
fat_now_sec = fat_start_sec + sec;
}
return *(u32 *)(BUF_ADDR + off);
}
/*
*
*/
void *
read_data_sec(void *dst, u32 current_clus)
{
current_clus -= 2;
current_clus *= bpb.BPB_SecPerClus;
current_clus += data_start_sec;
for (int i = 0 ; i < bpb.BPB_SecPerClus ; i++, dst += SECTSIZE)
readsect(dst, current_clus + i);
return dst;
}
/*
* kernel.bin是正经ld链接的
* 4096
* elf_clus推出到需要读取的簇号则直接默认为读取失败
*/
void
readseg(u32 va, u32 count, u32 offset)
{
u32 end_va;
end_va = va + count;
if ((offset & (8 * SECTSIZE - 1)) != 0)
goto bad;
if (offset < elf_off)
goto bad;
while (va < end_va) {
if (elf_off == offset) {
read_data_sec((void *)va, elf_clus);
va += 8 * SECTSIZE;
offset += 8 * SECTSIZE;
}
elf_off += 8 * SECTSIZE;
elf_clus = get_next_clus(elf_clus);
}
return;
bad:
for (char *s = "----fail to kernel elf----", *st = s; *s; s++)
write_to_terminal(TERMINAL_POS(1, s - st), DEFAULT_COLOR | *s);
while (1)
;// do nothing
}
/*
* kernel.bin的elf文件并跳过去
*/
void
load_kernel()
{
clear_screen();
for (char *s = "----start loading kernel elf----", *st = s; *s; s++)
write_to_terminal(TERMINAL_POS(0, s - st), DEFAULT_COLOR | *s);
readsect((void *)&bpb, 0);
if (bpb.BPB_SecPerClus != 8 || bpb.BPB_BytsPerSec != SECTSIZE)
goto bad;
fat_start_sec = bpb.BPB_RsvdSecCnt;
data_start_sec = fat_start_sec + bpb.BPB_FATSz32 * bpb.BPB_NumFATs;
u32 root_clus = bpb.BPB_RootClus;
while (root_clus < 0x0FFFFFF8) {
void *read_end = read_data_sec((void *)BUF_ADDR, root_clus);
for (struct Directory_Entry *p = (void *)BUF_ADDR
; (void *)p < read_end ; p++) {
if (strncmp(p->DIR_Name, "KERNEL BIN", 11) == 0) {
elf_clus = (u32)p->DIR_FstClusHI << 16 |
p->DIR_FstClusLO;
break;
}
}
if (elf_clus != 0)
break;
root_clus = get_next_clus(root_clus);
}
if (elf_clus == 0)
goto bad;
read_data_sec((void *)ELF_ADDR, elf_clus);
struct Elf *eh = (void *)ELF_ADDR;
struct Proghdr *ph = (void *)eh + eh->e_phoff;
for (int i = 0 ; i < eh->e_phnum ; i++, ph++) {
if (ph->p_type != PT_LOAD)
continue;
readseg(ph->p_va, ph->p_filesz, ph->p_offset);
memset(
(void *)ph->p_va + ph->p_filesz,
0,
ph->p_memsz - ph->p_filesz);
}
for (char *s = "----finish loading kernel elf----", *st = s; *s; s++)
write_to_terminal(TERMINAL_POS(1, s - st), DEFAULT_COLOR | *s);
((void (*)(void))(eh->e_entry))();
bad:
for (char *s = "----fail to load kernel elf----", *st = s; *s; s++)
write_to_terminal(TERMINAL_POS(1, s - st), DEFAULT_COLOR | *s);
while (1)
;// do nothing
}

View File

@ -1,192 +0,0 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2010-2017 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!-- I386 with SSE -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.core">
<flags id="i386_eflags" size="4">
<field name="" start="22" end="31"/>
<field name="ID" start="21" end="21"/>
<field name="VIP" start="20" end="20"/>
<field name="VIF" start="19" end="19"/>
<field name="AC" start="18" end="18"/>
<field name="VM" start="17" end="17"/>
<field name="RF" start="16" end="16"/>
<field name="" start="15" end="15"/>
<field name="NT" start="14" end="14"/>
<field name="IOPL" start="12" end="13"/>
<field name="OF" start="11" end="11"/>
<field name="DF" start="10" end="10"/>
<field name="IF" start="9" end="9"/>
<field name="TF" start="8" end="8"/>
<field name="SF" start="7" end="7"/>
<field name="ZF" start="6" end="6"/>
<field name="" start="5" end="5"/>
<field name="AF" start="4" end="4"/>
<field name="" start="3" end="3"/>
<field name="PF" start="2" end="2"/>
<field name="" start="1" end="1"/>
<field name="CF" start="0" end="0"/>
</flags>
<reg name="eax" bitsize="32" type="int32" regnum="0"/>
<reg name="ecx" bitsize="32" type="int32"/>
<reg name="edx" bitsize="32" type="int32"/>
<reg name="ebx" bitsize="32" type="int32"/>
<reg name="esp" bitsize="32" type="data_ptr"/>
<reg name="ebp" bitsize="32" type="data_ptr"/>
<reg name="esi" bitsize="32" type="int32"/>
<reg name="edi" bitsize="32" type="int32"/>
<reg name="eip" bitsize="32" type="code_ptr"/>
<reg name="eflags" bitsize="32" type="i386_eflags"/>
<reg name="cs" bitsize="32" type="int32"/>
<reg name="ss" bitsize="32" type="int32"/>
<reg name="ds" bitsize="32" type="int32"/>
<reg name="es" bitsize="32" type="int32"/>
<reg name="fs" bitsize="32" type="int32"/>
<reg name="gs" bitsize="32" type="int32"/>
<!-- Segment descriptor caches and TLS base MSRs -->
<!--reg name="cs_base" bitsize="32" type="int32"/>
<reg name="ss_base" bitsize="32" type="int32"/>
<reg name="ds_base" bitsize="32" type="int32"/>
<reg name="es_base" bitsize="32" type="int32"/-->
<reg name="fs_base" bitsize="32" type="int32"/>
<reg name="gs_base" bitsize="32" type="int32"/>
<reg name="k_gs_base" bitsize="32" type="int32"/>
<flags id="i386_cr0" size="4">
<field name="PG" start="31" end="31"/>
<field name="CD" start="30" end="30"/>
<field name="NW" start="29" end="29"/>
<field name="AM" start="18" end="18"/>
<field name="WP" start="16" end="16"/>
<field name="NE" start="5" end="5"/>
<field name="ET" start="4" end="4"/>
<field name="TS" start="3" end="3"/>
<field name="EM" start="2" end="2"/>
<field name="MP" start="1" end="1"/>
<field name="PE" start="0" end="0"/>
</flags>
<flags id="i386_cr3" size="4">
<field name="PDBR" start="12" end="31"/>
<!--field name="" start="3" end="11"/>
<field name="WT" start="2" end="2"/>
<field name="CD" start="1" end="1"/>
<field name="" start="0" end="0"/-->
<field name="PCID" start="0" end="11"/>
</flags>
<flags id="i386_cr4" size="4">
<field name="VME" start="0" end="0"/>
<field name="PVI" start="1" end="1"/>
<field name="TSD" start="2" end="2"/>
<field name="DE" start="3" end="3"/>
<field name="PSE" start="4" end="4"/>
<field name="PAE" start="5" end="5"/>
<field name="MCE" start="6" end="6"/>
<field name="PGE" start="7" end="7"/>
<field name="PCE" start="8" end="8"/>
<field name="OSFXSR" start="9" end="9"/>
<field name="OSXMMEXCPT" start="10" end="10"/>
<field name="UMIP" start="11" end="11"/>
<field name="LA57" start="12" end="12"/>
<field name="VMXE" start="13" end="13"/>
<field name="SMXE" start="14" end="14"/>
<field name="FSGSBASE" start="16" end="16"/>
<field name="PCIDE" start="17" end="17"/>
<field name="OSXSAVE" start="18" end="18"/>
<field name="SMEP" start="20" end="20"/>
<field name="SMAP" start="21" end="21"/>
<field name="PKE" start="22" end="22"/>
</flags>
<flags id="i386_efer" size="8">
<field name="TCE" start="15" end="15"/>
<field name="FFXSR" start="14" end="14"/>
<field name="LMSLE" start="13" end="13"/>
<field name="SVME" start="12" end="12"/>
<field name="NXE" start="11" end="11"/>
<field name="LMA" start="10" end="10"/>
<field name="LME" start="8" end="8"/>
<field name="SCE" start="0" end="0"/>
</flags>
<reg name="cr0" bitsize="32" type="i386_cr0"/>
<reg name="cr2" bitsize="32" type="int32"/>
<reg name="cr3" bitsize="32" type="i386_cr3"/>
<reg name="cr4" bitsize="32" type="i386_cr4"/>
<reg name="cr8" bitsize="32" type="int32"/>
<reg name="efer" bitsize="32" type="i386_efer"/>
<reg name="st0" bitsize="80" type="i387_ext"/>
<reg name="st1" bitsize="80" type="i387_ext"/>
<reg name="st2" bitsize="80" type="i387_ext"/>
<reg name="st3" bitsize="80" type="i387_ext"/>
<reg name="st4" bitsize="80" type="i387_ext"/>
<reg name="st5" bitsize="80" type="i387_ext"/>
<reg name="st6" bitsize="80" type="i387_ext"/>
<reg name="st7" bitsize="80" type="i387_ext"/>
<reg name="fctrl" bitsize="32" type="int" group="float"/>
<reg name="fstat" bitsize="32" type="int" group="float"/>
<reg name="ftag" bitsize="32" type="int" group="float"/>
<reg name="fiseg" bitsize="32" type="int" group="float"/>
<reg name="fioff" bitsize="32" type="int" group="float"/>
<reg name="foseg" bitsize="32" type="int" group="float"/>
<reg name="fooff" bitsize="32" type="int" group="float"/>
<reg name="fop" bitsize="32" type="int" group="float"/>
<!--/feature>
<feature name="org.gnu.gdb.i386.32bit.sse"-->
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v16i8" type="int8" count="16"/>
<vector id="v8i16" type="int16" count="8"/>
<vector id="v4i32" type="int32" count="4"/>
<vector id="v2i64" type="int64" count="2"/>
<union id="vec128">
<field name="v4_float" type="v4f"/>
<field name="v2_double" type="v2d"/>
<field name="v16_int8" type="v16i8"/>
<field name="v8_int16" type="v8i16"/>
<field name="v4_int32" type="v4i32"/>
<field name="v2_int64" type="v2i64"/>
<field name="uint128" type="uint128"/>
</union>
<flags id="i386_mxcsr" size="4">
<field name="IE" start="0" end="0"/>
<field name="DE" start="1" end="1"/>
<field name="ZE" start="2" end="2"/>
<field name="OE" start="3" end="3"/>
<field name="UE" start="4" end="4"/>
<field name="PE" start="5" end="5"/>
<field name="DAZ" start="6" end="6"/>
<field name="IM" start="7" end="7"/>
<field name="DM" start="8" end="8"/>
<field name="ZM" start="9" end="9"/>
<field name="OM" start="10" end="10"/>
<field name="UM" start="11" end="11"/>
<field name="PM" start="12" end="12"/>
<field name="FZ" start="15" end="15"/>
</flags>
<reg name="xmm0" bitsize="128" type="vec128"/>
<reg name="xmm1" bitsize="128" type="vec128"/>
<reg name="xmm2" bitsize="128" type="vec128"/>
<reg name="xmm3" bitsize="128" type="vec128"/>
<reg name="xmm4" bitsize="128" type="vec128"/>
<reg name="xmm5" bitsize="128" type="vec128"/>
<reg name="xmm6" bitsize="128" type="vec128"/>
<reg name="xmm7" bitsize="128" type="vec128"/>
<reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
</feature>

16
inc/assert.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef MINIOS_ASSERT_H
#define MINIOS_ASSERT_H
void _warn(const char*, int, const char*, ...);
void _panic(const char*, int, const char*, ...) __attribute__((noreturn));
#define warn(...) _warn(__FILE__, __LINE__, __VA_ARGS__)
#define panic(...) _panic(__FILE__, __LINE__, __VA_ARGS__)
#define assert(x) \
do { if (!(x)) panic("assertion failed: %s", #x); } while (0)
// 静态assert如果不符合条件就会直接在编译期报错
#define static_assert(x) switch (x) case 0: case (x):;
#endif /* MINIOS_ASSERT_H */

67
inc/elf.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef MINIOS_ELF_H
#define MINIOS_ELF_H
#include <type.h>
#define ELF_MAGIC 0x464C457FU /* "\x7FELF" in little endian */
struct Elf {
u32 e_magic; // must equal ELF_MAGIC
u8 e_elf[12];
u16 e_type;
u16 e_machine;
u32 e_version;
u32 e_entry;
u32 e_phoff;
u32 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
};
struct Proghdr {
u32 p_type;
u32 p_offset;
u32 p_va;
u32 p_pa;
u32 p_filesz;
u32 p_memsz;
u32 p_flags;
u32 p_align;
};
struct Secthdr {
u32 sh_name;
u32 sh_type;
u32 sh_flags;
u32 sh_addr;
u32 sh_offset;
u32 sh_size;
u32 sh_link;
u32 sh_info;
u32 sh_addralign;
u32 sh_entsize;
};
// Values for Proghdr::p_type
#define PT_LOAD 1
// Flag bits for Proghdr::p_flags
#define ELF_PROG_FLAG_EXEC 1
#define ELF_PROG_FLAG_WRITE 2
#define ELF_PROG_FLAG_READ 4
// Values for Secthdr::sh_type
#define ELF_SHT_NULL 0
#define ELF_SHT_PROGBITS 1
#define ELF_SHT_SYMTAB 2
#define ELF_SHT_STRTAB 3
// Values for Secthdr::sh_name
#define ELF_SHN_UNDEF 0
#endif /* MINIOS_ELF_H */

139
inc/errno.h Normal file
View File

@ -0,0 +1,139 @@
#ifndef MINIOS_ERRNO_H
#define MINIOS_ERRNO_H
#define EPERM 1
#define ENOENT 2
#define ESRCH 3
#define EINTR 4
#define EIO 5
#define ENXIO 6
#define E2BIG 7
#define ENOEXEC 8
#define EBADF 9
#define ECHILD 10
#define EAGAIN 11
#define ENOMEM 12
#define EACCES 13
#define EFAULT 14
#define ENOTBLK 15
#define EBUSY 16
#define EEXIST 17
#define EXDEV 18
#define ENODEV 19
#define ENOTDIR 20
#define EISDIR 21
#define EINVAL 22
#define ENFILE 23
#define EMFILE 24
#define ENOTTY 25
#define ETXTBSY 26
#define EFBIG 27
#define ENOSPC 28
#define ESPIPE 29
#define EROFS 30
#define EMLINK 31
#define EPIPE 32
#define EDOM 33
#define ERANGE 34
#define EDEADLK 35
#define ENAMETOOLONG 36
#define ENOLCK 37
#define ENOSYS 38
#define ENOTEMPTY 39
#define ELOOP 40
#define EWOULDBLOCK EAGAIN
#define ENOMSG 42
#define EIDRM 43
#define ECHRNG 44
#define EL2NSYNC 45
#define EL3HLT 46
#define EL3RST 47
#define ELNRNG 48
#define EUNATCH 49
#define ENOCSI 50
#define EL2HLT 51
#define EBADE 52
#define EBADR 53
#define EXFULL 54
#define ENOANO 55
#define EBADRQC 56
#define EBADSLT 57
#define EDEADLOCK EDEADLK
#define EBFONT 59
#define ENOSTR 60
#define ENODATA 61
#define ETIME 62
#define ENOSR 63
#define ENONET 64
#define ENOPKG 65
#define EREMOTE 66
#define ENOLINK 67
#define EADV 68
#define ESRMNT 69
#define ECOMM 70
#define EPROTO 71
#define EMULTIHOP 72
#define EDOTDOT 73
#define EBADMSG 74
#define EOVERFLOW 75
#define ENOTUNIQ 76
#define EBADFD 77
#define EREMCHG 78
#define ELIBACC 79
#define ELIBBAD 80
#define ELIBSCN 81
#define ELIBMAX 82
#define ELIBEXEC 83
#define EILSEQ 84
#define ERESTART 85
#define ESTRPIPE 86
#define EUSERS 87
#define ENOTSOCK 88
#define EDESTADDRREQ 89
#define EMSGSIZE 90
#define EPROTOTYPE 91
#define ENOPROTOOPT 92
#define EPROTONOSUPPORT 93
#define ESOCKTNOSUPPORT 94
#define EOPNOTSUPP 95
#define ENOTSUP EOPNOTSUPP
#define EPFNOSUPPORT 96
#define EAFNOSUPPORT 97
#define EADDRINUSE 98
#define EADDRNOTAVAIL 99
#define ENETDOWN 100
#define ENETUNREACH 101
#define ENETRESET 102
#define ECONNABORTED 103
#define ECONNRESET 104
#define ENOBUFS 105
#define EISCONN 106
#define ENOTCONN 107
#define ESHUTDOWN 108
#define ETOOMANYREFS 109
#define ETIMEDOUT 110
#define ECONNREFUSED 111
#define EHOSTDOWN 112
#define EHOSTUNREACH 113
#define EALREADY 114
#define EINPROGRESS 115
#define ESTALE 116
#define EUCLEAN 117
#define ENOTNAM 118
#define ENAVAIL 119
#define EISNAM 120
#define EREMOTEIO 121
#define EDQUOT 122
#define ENOMEDIUM 123
#define EMEDIUMTYPE 124
#define ECANCELED 125
#define ENOKEY 126
#define EKEYEXPIRED 127
#define EKEYREVOKED 128
#define EKEYREJECTED 129
#define EOWNERDEAD 130
#define ENOTRECOVERABLE 131
#define ERFKILL 132
#define EHWPOISON 133
#endif

55
inc/fat32.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef MINIOS_FAT32_H
#define MINIOS_FAT32_H
#include <type.h>
struct BPB {
u8 BS_jmpBoot[3];
u8 BS_OEMName[8];
u16 BPB_BytsPerSec;
u8 BPB_SecPerClus;
u16 BPB_RsvdSecCnt;
u8 BPB_NumFATs;
u16 BPB_RootEntCnt;
u16 BPB_TotSec16;
u8 BPB_Media;
u16 BPB_FATSz16;
u16 BPB_SecPerTrk;
u16 BPB_NumHeads;
u32 BPB_HiddSec;
u32 BPB_TotSec32;
u32 BPB_FATSz32;
u16 BPB_ExtFlags;
u16 BPB_FSVer;
u32 BPB_RootClus;
u16 BPB_FSInfo;
u16 BPB_BkBootSec;
u8 BPB_Reserved[12];
u8 BS_DrvNum;
u8 BS_Reserved1;
u8 BS_BootSig;
u32 BS_VolID;
u8 BS_VolLabp[11];
u8 BS_FilSysType[8];
u8 zero[420];
u16 Signature_word;
} __attribute__((packed));
struct Directory_Entry {
char DIR_Name[11];
u8 DIR_Attr;
u8 DIR_NTRes;
u8 DIR_CrtTimeTenth;
u16 DIR_CrtTime;
u16 DIR_CrtDate;
u16 DIR_LstAccDate;
u16 DIR_FstClusHI;
u16 DIR_WrtTime;
u16 DIR_WrtDate;
u16 DIR_FstClusLO;
u32 DIR_FileSize;
} __attribute__((packed));
#endif /* MINIOS_FAT32_H */

8
inc/kern/exec.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef MINIOS_KERN_EXEC_H
#define MINIOS_KERN_EXEC_H
#include <kern/process.h>
ssize_t kern_exec(PROCESS_0 *p_proc, const char *pathname);
#endif /* MINIOS_KERN_EXEC_H */

8
inc/kern/exit.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef MINIOS_KERN_EXIT_H
#define MINIOS_KERN_EXIT_H
#include <kern/process.h>
ssize_t kern_exit(PROCESS_0 *p_proc, int exit_code);
#endif /* MINIOS_KERN_EXIT_H */

9
inc/kern/fork.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef MINIOS_KERN_FORK_H
#define MINIOS_KERN_FORK_H
#include <type.h>
#include <kern/process.h>
ssize_t kern_fork(PROCESS_0 *p_father);
#endif /* MINIOS_KERN_FORK_H */

11
inc/kern/fs.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef MINIOS_KERN_FS_H
#define MINIOS_KERN_FS_H
#include <type.h>
ssize_t kern_read(int fd, void *buf, size_t count);
ssize_t kern_write(int fd, const void *buf, size_t count);
ssize_t read_file(const char *filename, void *dst);
#endif /* MINIOS_KERN_FS_H */

9
inc/kern/keyboard.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef MINIOS_KERN_KEYBOARD_H
#define MINIOS_KERN_KEYBOARD_H
#include <type.h>
void add_keyboard_buf(u8 ch);
u8 read_keyboard_buf(void);
#endif /* MINIOS_KERN_KEYBOARD_H */

323
inc/kern/keymap.h Normal file
View File

@ -0,0 +1,323 @@
#ifndef MINIOS_KERN_KEYMAP_H
#define MINIOS_KERN_KEYMAP_H
#include <type.h>
#define NR_SCAN_CODES 128
#define FLAG_BREAK 0x0080 /* Break Code */
#define FLAG_EXT 0x0100 /* Normal function keys */
#define FLAG_SHIFT_L 0x0200 /* Shift key */
#define FLAG_SHIFT_R 0x0400 /* Shift key */
#define FLAG_CTRL_L 0x0800 /* Control key */
#define FLAG_CTRL_R 0x1000 /* Control key */
#define FLAG_ALT_L 0x2000 /* Alternate key */
#define FLAG_ALT_R 0x4000 /* Alternate key */
#define FLAG_PAD 0x8000 /* keys in num pad */
#define MASK_RAW 0x01FF /* raw key value = code_passed_to_tty & MASK_RAW
* the value can be found either in the keymap
* column 0 or in the list below
*/
/* Special keys */
#define ESC (0x01 + FLAG_EXT) /* Esc */
#define TAB (0x02 + FLAG_EXT) /* Tab */
#define ENTER (0x03 + FLAG_EXT) /* Enter */
#define BACKSPACE (0x04 + FLAG_EXT) /* BackSpace */
#define GUI_L (0x05 + FLAG_EXT) /* L GUI */
#define GUI_R (0x06 + FLAG_EXT) /* R GUI */
#define APPS (0x07 + FLAG_EXT) /* APPS */
/* Shift, Ctrl, Alt */
#define SHIFT_L (0x08 + FLAG_EXT) /* L Shift */
#define SHIFT_R (0x09 + FLAG_EXT) /* R Shift */
#define CTRL_L (0x0A + FLAG_EXT) /* L Ctrl */
#define CTRL_R (0x0B + FLAG_EXT) /* R Ctrl */
#define ALT_L (0x0C + FLAG_EXT) /* L Alt */
#define ALT_R (0x0D + FLAG_EXT) /* R Alt */
/* Lock keys */
#define CAPS_LOCK (0x0E + FLAG_EXT) /* Caps Lock */
#define NUM_LOCK (0x0F + FLAG_EXT) /* Number Lock */
#define SCROLL_LOCK (0x10 + FLAG_EXT) /* Scroll Lock */
/* Function keys */
#define F1 (0x11 + FLAG_EXT) /* F1 */
#define F2 (0x12 + FLAG_EXT) /* F2 */
#define F3 (0x13 + FLAG_EXT) /* F3 */
#define F4 (0x14 + FLAG_EXT) /* F4 */
#define F5 (0x15 + FLAG_EXT) /* F5 */
#define F6 (0x16 + FLAG_EXT) /* F6 */
#define F7 (0x17 + FLAG_EXT) /* F7 */
#define F8 (0x18 + FLAG_EXT) /* F8 */
#define F9 (0x19 + FLAG_EXT) /* F9 */
#define F10 (0x1A + FLAG_EXT) /* F10 */
#define F11 (0x1B + FLAG_EXT) /* F11 */
#define F12 (0x1C + FLAG_EXT) /* F12 */
/* Control Pad */
#define PRINTSCREEN (0x1D + FLAG_EXT) /* Print Screen */
#define PAUSEBREAK (0x1E + FLAG_EXT) /* Pause/Break */
#define INSERT (0x1F + FLAG_EXT) /* Insert */
#define DELETE (0x20 + FLAG_EXT) /* Delete */
#define HOME (0x21 + FLAG_EXT) /* Home */
#define END (0x22 + FLAG_EXT) /* End */
#define PAGEUP (0x23 + FLAG_EXT) /* Page Up */
#define PAGEDOWN (0x24 + FLAG_EXT) /* Page Down */
#define UP (0x25 + FLAG_EXT) /* Up */
#define DOWN (0x26 + FLAG_EXT) /* Down */
#define LEFT (0x27 + FLAG_EXT) /* Left */
#define RIGHT (0x28 + FLAG_EXT) /* Right */
/* ACPI keys */
// #define POWER (0x29 + FLAG_EXT) /* Power */
// #define SLEEP (0x2A + FLAG_EXT) /* Sleep */
// #define WAKE (0x2B + FLAG_EXT) /* Wake Up */
/* Num Pad */
#define PAD_SLASH (0x2C + FLAG_EXT) /* / */
#define PAD_STAR (0x2D + FLAG_EXT) /* * */
#define PAD_MINUS (0x2E + FLAG_EXT) /* - */
#define PAD_PLUS (0x2F + FLAG_EXT) /* + */
#define PAD_ENTER (0x30 + FLAG_EXT) /* Enter */
#define PAD_DOT (0x31 + FLAG_EXT) /* . */
#define PAD_0 (0x32 + FLAG_EXT) /* 0 */
#define PAD_1 (0x33 + FLAG_EXT) /* 1 */
#define PAD_2 (0x34 + FLAG_EXT) /* 2 */
#define PAD_3 (0x35 + FLAG_EXT) /* 3 */
#define PAD_4 (0x36 + FLAG_EXT) /* 4 */
#define PAD_5 (0x37 + FLAG_EXT) /* 5 */
#define PAD_6 (0x38 + FLAG_EXT) /* 6 */
#define PAD_7 (0x39 + FLAG_EXT) /* 7 */
#define PAD_8 (0x3A + FLAG_EXT) /* 8 */
#define PAD_9 (0x3B + FLAG_EXT) /* 9 */
#define PAD_UP PAD_8 /* Up */
#define PAD_DOWN PAD_2 /* Down */
#define PAD_LEFT PAD_4 /* Left */
#define PAD_RIGHT PAD_6 /* Right */
#define PAD_HOME PAD_7 /* Home */
#define PAD_END PAD_1 /* End */
#define PAD_PAGEUP PAD_9 /* Page Up */
#define PAD_PAGEDOWN PAD_3 /* Page Down */
#define PAD_INS PAD_0 /* Ins */
#define PAD_MID PAD_5 /* Middle key */
#define PAD_DEL PAD_DOT /* Del */
/* Keymap for US MF-2 keyboard. */
const static u32 keymap[NR_SCAN_CODES] = {
/* scan-code !Shift */
/* =============================================*/
/* 0x00 - none */ 0,
/* 0x01 - ESC */ ESC,
/* 0x02 - '1' */ '1',
/* 0x03 - '2' */ '2',
/* 0x04 - '3' */ '3',
/* 0x05 - '4' */ '4',
/* 0x06 - '5' */ '5',
/* 0x07 - '6' */ '6',
/* 0x08 - '7' */ '7',
/* 0x09 - '8' */ '8',
/* 0x0A - '9' */ '9',
/* 0x0B - '0' */ '0',
/* 0x0C - '-' */ '-',
/* 0x0D - '=' */ '=',
/* 0x0E - BS */ BACKSPACE,
/* 0x0F - TAB */ TAB,
/* 0x10 - 'q' */ 'q',
/* 0x11 - 'w' */ 'w',
/* 0x12 - 'e' */ 'e',
/* 0x13 - 'r' */ 'r',
/* 0x14 - 't' */ 't',
/* 0x15 - 'y' */ 'y',
/* 0x16 - 'u' */ 'u',
/* 0x17 - 'i' */ 'i',
/* 0x18 - 'o' */ 'o',
/* 0x19 - 'p' */ 'p',
/* 0x1A - '[' */ '[',
/* 0x1B - ']' */ ']',
/* 0x1C - CR/LF */ ENTER,
/* 0x1D - l. Ctrl */ CTRL_L,
/* 0x1E - 'a' */ 'a',
/* 0x1F - 's' */ 's',
/* 0x20 - 'd' */ 'd',
/* 0x21 - 'f' */ 'f',
/* 0x22 - 'g' */ 'g',
/* 0x23 - 'h' */ 'h',
/* 0x24 - 'j' */ 'j',
/* 0x25 - 'k' */ 'k',
/* 0x26 - 'l' */ 'l',
/* 0x27 - ';' */ ';',
/* 0x28 - '\'' */ '\'',
/* 0x29 - '`' */ '`',
/* 0x2A - l. SHIFT */ SHIFT_L,
/* 0x2B - '\' */ '\\',
/* 0x2C - 'z' */ 'z',
/* 0x2D - 'x' */ 'x',
/* 0x2E - 'c' */ 'c',
/* 0x2F - 'v' */ 'v',
/* 0x30 - 'b' */ 'b',
/* 0x31 - 'n' */ 'n',
/* 0x32 - 'm' */ 'm',
/* 0x33 - ',' */ ',',
/* 0x34 - '.' */ '.',
/* 0x35 - '/' */ '/',
/* 0x36 - r. SHIFT */ SHIFT_R,
/* 0x37 - '*' */ '*',
/* 0x38 - ALT */ ALT_L,
/* 0x39 - ' ' */ ' ',
/* 0x3A - CapsLock */ CAPS_LOCK,
/* 0x3B - F1 */ F1,
/* 0x3C - F2 */ F2,
/* 0x3D - F3 */ F3,
/* 0x3E - F4 */ F4,
/* 0x3F - F5 */ F5,
/* 0x40 - F6 */ F6,
/* 0x41 - F7 */ F7,
/* 0x42 - F8 */ F8,
/* 0x43 - F9 */ F9,
/* 0x44 - F10 */ F10,
/* 0x45 - NumLock */ NUM_LOCK,
/* 0x46 - ScrLock */ SCROLL_LOCK,
/* 0x47 - Home */ PAD_HOME,
/* 0x48 - CurUp */ PAD_UP,
/* 0x49 - PgUp */ PAD_PAGEUP,
/* 0x4A - '-' */ PAD_MINUS,
/* 0x4B - Left */ PAD_LEFT,
/* 0x4C - MID */ PAD_MID,
/* 0x4D - Right */ PAD_RIGHT,
/* 0x4E - '+' */ PAD_PLUS,
/* 0x4F - End */ PAD_END,
/* 0x50 - Down */ PAD_DOWN,
/* 0x51 - PgDown */ PAD_PAGEDOWN,
/* 0x52 - Insert */ PAD_INS,
/* 0x53 - Delete */ PAD_DOT,
/* 0x54 - Enter */ 0,
/* 0x55 - ??? */ 0,
/* 0x56 - ??? */ 0,
/* 0x57 - F11 */ F11,
/* 0x58 - F12 */ F12,
/* 0x59 - ??? */ 0,
/* 0x5A - ??? */ 0,
/* 0x5B - ??? */ 0,
/* 0x5C - ??? */ 0,
/* 0x5D - ??? */ 0,
/* 0x5E - ??? */ 0,
/* 0x5F - ??? */ 0,
/* 0x60 - ??? */ 0,
/* 0x61 - ??? */ 0,
/* 0x62 - ??? */ 0,
/* 0x63 - ??? */ 0,
/* 0x64 - ??? */ 0,
/* 0x65 - ??? */ 0,
/* 0x66 - ??? */ 0,
/* 0x67 - ??? */ 0,
/* 0x68 - ??? */ 0,
/* 0x69 - ??? */ 0,
/* 0x6A - ??? */ 0,
/* 0x6B - ??? */ 0,
/* 0x6C - ??? */ 0,
/* 0x6D - ??? */ 0,
/* 0x6E - ??? */ 0,
/* 0x6F - ??? */ 0,
/* 0x70 - ??? */ 0,
/* 0x71 - ??? */ 0,
/* 0x72 - ??? */ 0,
/* 0x73 - ??? */ 0,
/* 0x74 - ??? */ 0,
/* 0x75 - ??? */ 0,
/* 0x76 - ??? */ 0,
/* 0x77 - ??? */ 0,
/* 0x78 - ??? */ 0,
/* 0x78 - ??? */ 0,
/* 0x7A - ??? */ 0,
/* 0x7B - ??? */ 0,
/* 0x7C - ??? */ 0,
/* 0x7D - ??? */ 0,
/* 0x7E - ??? */ 0,
/* 0x7F - ??? */ 0,
};
/*====================================================================
Appendix: Scan code set 1
*====================================================================
KEY MAKE BREAK| KEY MAKE BREAK | KEY MAKE BREAK
---------------------|------------------------|-----------------------
A 1E 9E | 9 0A 8A | [ 1A 9A
B 30 B0 | ` 29 89 | INSERT E0,52 E0,D2
C 2E AE | - 0C 8C | HOME E0,47 E0,C7
D 20 A0 | = 0D 8D | PG UP E0,49 E0,C9
E 12 92 | \ 2B AB | DELETE E0,53 E0,D3
F 21 A1 | BKSP 0E 8E | END E0,4F E0,CF
G 22 A2 | SPACE 39 B9 | PG DN E0,51 E0,D1
H 23 A3 | TAB 0F 8F | U ARROW E0,48 E0,C8
I 17 97 | CAPS 3A BA | L ARROW E0,4B E0,CB
J 24 A4 | L SHFT 2A AA | D ARROW E0,50 E0,D0
K 25 A5 | L CTRL 1D 9D | R ARROW E0,4D E0,CD
L 26 A6 | L GUI E0,5B E0,DB | NUM 45 C5
M 32 B2 | L ALT 38 B8 | KP / E0,35 E0,B5
N 31 B1 | R SHFT 36 B6 | KP * 37 B7
O 18 98 | R CTRL E0,1D E0,9D | KP - 4A CA
P 19 99 | R GUI E0,5C E0,DC | KP + 4E CE
Q 10 19 | R ALT E0,38 E0,B8 | KP EN E0,1C E0,9C
R 13 93 | APPS E0,5D E0,DD | KP . 53 D3
S 1F 9F | ENTER 1C 9C | KP 0 52 D2
T 14 94 | ESC 01 81 | KP 1 4F CF
U 16 96 | F1 3B BB | KP 2 50 D0
V 2F AF | F2 3C BC | KP 3 51 D1
W 11 91 | F3 3D BD | KP 4 4B CB
X 2D AD | F4 3E BE | KP 5 4C CC
Y 15 95 | F5 3F BF | KP 6 4D CD
Z 2C AC | F6 40 C0 | KP 7 47 C7
0 0B 8B | F7 41 C1 | KP 8 48 C8
1 02 82 | F8 42 C2 | KP 9 49 C9
2 03 83 | F9 43 C3 | ] 1B 9B
3 04 84 | F10 44 C4 | ; 27 A7
4 05 85 | F11 57 D7 | ' 28 A8
5 06 86 | F12 58 D8 | , 33 B3
| |
6 07 87 | PRTSCRN E0,2A E0,B7 | . 34 B4
| E0,37 E0,AA |
| |
7 08 88 | SCROLL 46 C6 | / 35 B5
| |
8 09 89 | PAUSE E1,1D |
| 45,E1, -NONE-|
| 9D,C5 |
----------------------------------------------------------------------
-----------------
ACPI Scan Codes:
-------------------------------------------
Key Make Code Break Code
-------------------------------------------
Power E0, 5E E0, DE
Sleep E0, 5F E0, DF
Wake E0, 63 E0, E3
-------------------------------
Windows Multimedia Scan Codes:
-------------------------------------------
Key Make Code Break Code
-------------------------------------------
Next Track E0, 19 E0, 99
Previous Track E0, 10 E0, 90
Stop E0, 24 E0, A4
Play/Pause E0, 22 E0, A2
Mute E0, 20 E0, A0
Volume Up E0, 30 E0, B0
Volume Down E0, 2E E0, AE
Media Select E0, 6D E0, ED
E-Mail E0, 6C E0, EC
Calculator E0, 21 E0, A1
My Computer E0, 6B E0, EB
WWW Search E0, 65 E0, E5
WWW Home E0, 32 E0, B2
WWW Back E0, 6A E0, EA
WWW Forward E0, 69 E0, E9
WWW Stop E0, 68 E0, E8
WWW Refresh E0, 67 E0, E7
WWW Favorites E0, 66 E0, E6
*=====================================================================================*/
#endif /* MINIOS_KERN_KEYMAP_H */

13
inc/kern/kmalloc.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef MINIOS_KERN_KMALLOC_H
#define MINIOS_KERN_KMALLOC_H
#include <type.h>
void phy_free_4k(phyaddr_t v);
phyaddr_t phy_malloc_4k(void);
void phy_init_4k();
void kfree(void *v);
void * kmalloc(size_t n);
#endif /* MINIOS_KERN_KMALLOC_H */

12
inc/kern/pmap.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef MINIOS_KERN_PMAP_H
#define MINIOS_KERN_PMAP_H
#include <type.h>
#include <kern/process.h>
void map_kern(u32 cr3, struct page_node **page_list);
void map_elf(PROCESS_0 *p_proc, void *elf_addr);
void map_stack(PROCESS_0 *p_proc);
void recycle_pages(struct page_node *page_list);
#endif /* MINIOS_KERN_PMAP_H */

109
inc/kern/process.h Normal file
View File

@ -0,0 +1,109 @@
#ifndef MINIOS_KERN_PROCESS_H
#define MINIOS_KERN_PROCESS_H
#include <type.h>
#include <mmu.h>
/* pcb中存储用户态进程的寄存器信息 */
struct user_context { /* proc_ptr points here ↑ Low */
u32 gs; /* ┓ │ */
u32 fs; /* ┃ │ */
u32 es; /* ┃ │ */
u32 ds; /* ┃ │ */
u32 edi; /* ┃ │ */
u32 esi; /* ┣ pushed by save() │ */
u32 ebp; /* ┃ │ */
u32 kernel_esp; /* <- 'popad' will ignore it │ */
u32 ebx; /* ┃ ↑栈从高地址往低地址增长 */
u32 edx; /* ┃ │ */
u32 ecx; /* ┃ │ */
u32 eax; /* ┛ │ */
u32 retaddr; /* return address for assembly code save() │ */
u32 eip; /* ┓ │ */
u32 cs; /* ┃ │ */
u32 eflags; /* ┣ these are pushed by CPU during interrupt │ */
u32 esp; /* ┃ │ */
u32 ss; /* ┛ ┷High */
};
struct kern_context {
u32 eflags;
u32 edi;
u32 esi;
u32 ebp;
u32 ebx;
u32 edx;
u32 ecx;
u32 eax;
u32 esp;
};
enum proc_statu {IDLE, READY, SLEEP, ZOMBIE, INITING};
typedef struct s_proc PROCESS_0;
/*
*
* cr3laddr被置为-1
* 线
*/
struct page_node {
struct page_node *nxt;
phyaddr_t paddr;
uintptr_t laddr;
};
/*
*
*/
struct son_node {
struct son_node *pre;
struct son_node *nxt;
PROCESS_0 *p_son;
};
struct tree_node {
PROCESS_0 *p_fa;
struct son_node *sons;
};
/* pcb */
struct s_proc {
struct user_context user_regs;
struct kern_context kern_regs;
u32 lock;
enum proc_statu statu;
u32 pid;
phyaddr_t cr3;
struct page_node *page_list;
int exit_code;
int priority;
int ticks;
struct tree_node fork_tree;
};
#define KERN_STACKSIZE (8 * KB)
typedef union u_proc {
PROCESS_0 pcb; // 内核pcb
char _pad[KERN_STACKSIZE]; // pcb往上剩下的空间用于内核栈
}PROCESS; // 通过union控制每个进程占8kb空间
/* 指向当前进程pcb的指针 */
// kern/main.c
extern PROCESS *p_proc_ready;
/* pcb表 */
#define PCB_SIZE 20
// kern/main.c
extern PROCESS proc_table[];
// 内核栈切换上下文函数(汇编接口)
void switch_kern_context(
struct kern_context *current_context,
struct kern_context *next_context
);
// 处理函数
u32 kern_get_pid(PROCESS_0 *p_proc);
#endif /* MINIOS_KERN_PROCESS_H */

181
inc/kern/protect.h Normal file
View File

@ -0,0 +1,181 @@
#ifndef MINIOS_KERN_PROTECT_H
#define MINIOS_KERN_PROTECT_H
#include <type.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;
/* 描述符选择子 */
#define SELECTOR_DUMMY 0x00 // ┓
#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 0x28
/* 描述符类型值说明 */
#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_SYSCALL 0x80
/* 权限 */
#define PRIVILEGE_KRNL 0
#define PRIVILEGE_TASK 1
#define PRIVILEGE_USER 3
#define RPL_KRNL SA_RPL0
#define RPL_TASK SA_RPL1
#define RPL_USER SA_RPL3
/* 初始化描述符函数 */
static void
init_segment(DESCRIPTOR *p_desc, u32 base, u32 limit, u16 attribute)
{
p_desc->limit_low = limit & 0x0FFFF; // 段界限1
p_desc->base_low = base & 0x0FFFF; // 段基址1
p_desc->base_mid = (base >> 16) & 0x0FF; // 段基址2
p_desc->attr1 = attribute & 0xFF; // 属性1
p_desc->limit_high_attr2 = ((limit >> 16) & 0x0F) | // 段界限2
((attribute >> 8) & 0xF0); // 属性2
p_desc->base_high = (base >> 24) & 0x0FF; // 段基址3
}
static void
init_gate(GATE *p_gate, u8 desc_type, void *handler, u8 privilage)
{
p_gate->offset_low = (u32)handler & 0xffff; //中断处理地址的低16位
p_gate->selector = SELECTOR_FLAT_C; //内核代码段选择子
p_gate->dcount = 0; //不需要复制参数
p_gate->attr = desc_type | (privilage << 5); //属性
p_gate->offset_high = ((u32)handler >> 16) & 0xffff;//中断处理地址的高16位
}
/* GDT 、 LDT 和 IDT 中描述符的个数 */
#define GDT_SIZE 128
#define LDT_SIZE GDT_SIZE
#define IDT_SIZE 256
// kern/start.c
extern DESCRIPTOR gdt[GDT_SIZE];
extern DESCRIPTOR ldt[LDT_SIZE];
extern GATE idt[IDT_SIZE];
/* TSS(Taskstate) */
typedef struct s_tss {
u32 backlink;
u32 esp0; /* 当发生中断的时候esp就会变成esp0 */
u32 ss0; /* 当发生中断的时候ss就会变成ss0由于ss0存储的是内核态权限段于是顺利进入内核态 */
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;
/* 全局的tss数据结构 */
// kern/start.c
extern TSS tss;
/* 描述符表 */
struct Pseudodesc {
u16 pd_lim; // Limit
u32 pd_base; // Base address
} __attribute__ ((packed));
// kern/start.c
extern struct Pseudodesc gdt_ptr, idt_ptr;
#endif /* MINIOS_KERN_PROTECT_H */

6
inc/kern/sche.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef MINIOS_KERN_SCHE_H
#define MINIOS_KERN_SCHE_H
void schedule(void);
#endif

10
inc/kern/stdio.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef MINIOS_KERN_STDIO_H
#define MINIOS_KERN_STDIO_H
#include <stdio.h>
// lib/kern/terminal.c
int kprintf(const char *fmt, ...);
int vkprintf(const char *fmt, va_list);
#endif /* MINIOS_KERN_STDIO_H */

35
inc/kern/syscall.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef MINIOS_KERN_SYSCALL_H
#define MINIOS_KERN_SYSCALL_H
#include <syscall.h>
#include <type.h>
// 由于会出现系统底层函数复用的情况
// miniOS 系统调用分成三层函数调用
// sys_* 外层函数,放进系统调用表中
// do_* 中间函数,留着用于参数处理
// kern_* 底层函数,实际处理函数
// 系统调用处理函数表
// kern/syscall.c
extern ssize_t (*syscall_table[])(void);
// kern/time.c
ssize_t do_get_ticks(void);
// kern/process.c
ssize_t do_get_pid(void);
// kern/exec.c
ssize_t do_exec(const char *pathname);
// kern/exit.c
ssize_t do_exit(int status);
// kern/fork.c
ssize_t do_fork(void);
// kern/wait.c
ssize_t do_wait(int *wstatus);
// kern/fs.c
ssize_t do_read(int fd, void *buf, size_t count);
ssize_t do_write(int fd, const void *buf, size_t count);
// 不告诉你这个实现在哪
ssize_t do_fork_ack(void);
#endif /* MINIOS_KERN_SYSCALL_H */

9
inc/kern/time.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef MINIOS_KERN_TIME_H
#define MINIOS_KERN_TIME_H
#include <type.h>
void timecounter_inc(void);
size_t kern_get_ticks(void);
#endif /* MINIOS_KERN_TIME_H */

109
inc/kern/trap.h Normal file
View File

@ -0,0 +1,109 @@
#ifndef MINIOS_KERN_TRAP_H
#define MINIOS_KERN_TRAP_H
#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> */
/* 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 */
/* 执行用户进程的入口(汇编接口) */
void restart();
/* 导入中断处理函数(汇编接口) */
// 系统调用
void int_syscall();
// 异常
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();
/* 中断开关 */
void enable_irq(int irq);
void disable_irq(int irq);
// kern/main.c
/* 标志着内核是否初始化完成 */
extern bool init_kernel;
static inline void
disable_int()
{
asm volatile("cli");
}
static inline void
enable_int()
{
asm volatile("sti");
}
#define DISABLE_INT() \
{ \
u32 IF_BIT = read_eflags() & FL_IF; \
if (IF_BIT != 0) \
disable_int();
#define ENABLE_INT() \
if (IF_BIT != 0) \
enable_int(); \
}
/* 系统调用实际处理函数(C接口) */
void syscall_handler(void);
/* 异常中断实际处理函数(C接口) */
void exception_handler(int vec_no, int err_code, int eip,
int cs, int eflags);
/* 外设中断实际处理函数(C接口) */
void default_interrupt_handler(int irq);
void clock_interrupt_handler(int irq);
void kb_interrupt_handler(int irq);
/* 外设中断实际处理函数表 */
// kern/trap.c
extern void (*irq_table[])(int);
#endif /* MINIOS_KERN_TRAP_H */

8
inc/kern/wait.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef MINIOS_KERN_WAIT_H
#define MINIOS_KERN_WAIT_H
#include <type.h>
ssize_t kern_wait(int *wstatus);
#endif /* MINIOS_KERN_WAIT_H */

124
inc/mmu.h Normal file
View File

@ -0,0 +1,124 @@
#ifndef MINIOS_MMU_H
#define MINIOS_MMU_H
#include <type.h>
// 内核并不是恒等映射
// 3GB~3GB+128MB的线性地址被映射到0~128MB的物理地址上
// kernel程序的运行在3GB~3GB+128MB的线性地址上
// 低3GB线性地址用于存放用户程序
#define KERNBASE 0xC0000000
#define K_PHY2LIN(x) ((x)+KERNBASE)
#define K_LIN2PHY(x) ((x)-KERNBASE)
#define KB 1024u
#define MB (1024u * KB)
#define GB (1024u * MB)
// A linear address 'la' has a three-part structure as follows:
//
// +--------10------+-------10-------+---------12----------+
// | Page Directory | Page Table | Offset within Page |
// | Index | Index | |
// +----------------+----------------+---------------------+
// \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/
// \---------- PGNUM(la) ----------/
//
// The PDX, PTX, PGOFF, and PGNUM macros decompose linear addresses as shown.
// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la),
// use PGADDR(PDX(la), PTX(la), PGOFF(la)).
// page number field of address
#define PGNUM(la) (((uintptr_t) (la)) >> PTXSHIFT)
// page directory index
#define PDX(la) ((((uintptr_t) (la)) >> PDXSHIFT) & 0x3FF)
// page table index
#define PTX(la) ((((uintptr_t) (la)) >> PTXSHIFT) & 0x3FF)
// offset in page
#define PGOFF(la) (((uintptr_t) (la)) & 0xFFF)
// construct linear address from indexes and offset
#define PGADDR(d, t, o) ((void*) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
// Page directory and page table constants.
#define NPDENTRIES 1024 // page directory entries per page directory
#define NPTENTRIES 1024 // page table entries per page table
#define PGSIZE 4096 // bytes mapped by a page
#define PGSHIFT 12 // log2(PGSIZE)
#define PTSIZE (PGSIZE*NPTENTRIES) // bytes mapped by a page directory entry
#define PTSHIFT 22 // log2(PTSIZE)
#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address
// Page table/directory entry flags.
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PWT 0x008 // Write-Through
#define PTE_PCD 0x010 // Cache-Disable
#define PTE_A 0x020 // Accessed
#define PTE_D 0x040 // Dirty
#define PTE_PS 0x080 // Page Size
#define PTE_G 0x100 // Global
// Address in page table or page directory entry
#define PTE_ADDR(pte) ((phyaddr_t) (pte) & ~0xFFF)
#define PTE_FLAG(pte) ((size_t) (pte) & 0xFFF)
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_MP 0x00000002 // Monitor coProcessor
#define CR0_EM 0x00000004 // Emulation
#define CR0_TS 0x00000008 // Task Switched
#define CR0_ET 0x00000010 // Extension Type
#define CR0_NE 0x00000020 // Numeric Errror
#define CR0_WP 0x00010000 // Write Protect
#define CR0_AM 0x00040000 // Alignment Mask
#define CR0_NW 0x20000000 // Not Writethrough
#define CR0_CD 0x40000000 // Cache Disable
#define CR0_PG 0x80000000 // Paging
#define CR4_PCE 0x00000100 // Performance counter enable
#define CR4_MCE 0x00000040 // Machine Check Enable
#define CR4_PSE 0x00000010 // Page Size Extensions
#define CR4_DE 0x00000008 // Debugging Extensions
#define CR4_TSD 0x00000004 // Time Stamp Disable
#define CR4_PVI 0x00000002 // Protected-Mode Virtual Interrupts
#define CR4_VME 0x00000001 // V86 Mode Extensions
// Eflags register
#define FL_CF 0x00000001 // Carry Flag
#define FL_PF 0x00000004 // Parity Flag
#define FL_AF 0x00000010 // Auxiliary carry Flag
#define FL_ZF 0x00000040 // Zero Flag
#define FL_SF 0x00000080 // Sign Flag
#define FL_TF 0x00000100 // Trap Flag
#define FL_IF 0x00000200 // Interrupt Flag
#define FL_DF 0x00000400 // Direction Flag
#define FL_OF 0x00000800 // Overflow Flag
#define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask
#define FL_IOPL_0 0x00000000 // IOPL == 0
#define FL_IOPL_1 0x00001000 // IOPL == 1
#define FL_IOPL_2 0x00002000 // IOPL == 2
#define FL_IOPL_3 0x00003000 // IOPL == 3
#define FL_NT 0x00004000 // Nested Task
#define FL_RF 0x00010000 // Resume Flag
#define FL_VM 0x00020000 // Virtual 8086 mode
#define FL_AC 0x00040000 // Alignment Check
#define FL_VIF 0x00080000 // Virtual Interrupt Flag
#define FL_VIP 0x00100000 // Virtual Interrupt Pending
#define FL_ID 0x00200000 // ID flag
// Page fault error codes
#define FEC_PR 0x1 // Page fault caused by protection violation
#define FEC_WR 0x2 // Page fault caused by a write
#define FEC_U 0x4 // Page fault occured while in user mode
#endif /* MINIOS_MMU_H */

12
inc/stdarg.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef MINIOS_STDARG_H
#define MINIOS_STDARG_H
typedef __builtin_va_list va_list;
#define va_start(ap, last) __builtin_va_start(ap, last)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
#define va_end(ap) __builtin_va_end(ap)
#endif /* MINIOS_STDARG_H */

17
inc/stdio.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef MINIOS_STDIO_H
#define MINIOS_STDIO_H
#include <type.h>
#include <stdarg.h>
#ifndef NULL
#define NULL ((void *) 0)
#endif /* NULL */
// lib/kern/printfmt.c
void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);
void vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list);
int snprintf(char *str, int size, const char *fmt, ...);
int vsnprintf(char *str, int size, const char *fmt, va_list);
#endif /* MINIOS_STDIO_H */

17
inc/string.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef MINIOS_STRING_H
#define MINIOS_STRING_H
#include <type.h>
int strlen(const char *s);
int strnlen(const char *s, size_t size);
char * strcpy(char *dst, const char *src);
char * strncpy(char *dst, const char *src, size_t size);
char * strcat(char *dst, const char *src);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t size);
void * memset(void *v, int c, size_t n);
void * memcpy(void *dst, const void *src, size_t n);
#endif /* MINIOS_STRING_H */

15
inc/syscall.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef MINIOS_SYSCALL_H
#define MINIOS_SYSCALL_H
// 系统调用号
#define _NR_get_ticks 0
#define _NR_get_pid 1
#define _NR_read 2
#define _NR_write 3
#define _NR_exec 4
#define _NR_fork 5
#define _NR_wait 6
#define _NR_exit 7
#define _NR_fork_ack 8
#endif /* MINIOS_SYSCALL_H */

43
inc/terminal.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef MINIOS_TERMINAL_H
#define MINIOS_TERMINAL_H
#include <type.h>
/*
*
*/
#define TERMINAL_ADDR 0xC0B8000
#define TERMINAL_COLUMN 80
#define TERMINAL_ROW 25
#define TERMINAL_SIZE ((TERMINAL_ROW) * (TERMINAL_COLUMN))
#define TERMINAL_POS(row, column) ((u16)((row) - 1) * TERMINAL_COLUMN \
+ (column) - 1)
/*
*
*/
#define BLACK 0x0
#define BLUE 0x1
#define GREEN 0x2
#define CYAN 0x3
#define RED 0x4
#define FUCHUSIA 0x5
#define BROWN 0x6
#define SILVER 0x7
#define GRAY 0x8
#define LIGHT_BLUE 0x9
#define LIGHT_GREEN 0xa
#define LIGHT_CYAN 0xb
#define LIGHT_RED 0xc
#define LIGHT_FUCHSIA 0xd
#define YELLOW 0xe
#define WHITE 0xf
#define FOREGROUND(color) ((u16)(color & 0xf) << 8)
#define BACKGROUND(color) ((u16)(color & 0xf) << 12)
/*
*
*/
#define DEFAULT_COLOR FOREGROUND(WHITE) | BACKGROUND(BLACK)
#endif /* MINIOS_TERMINAL_H */

63
inc/type.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef MINIOS_TYPE_H
#define MINIOS_TYPE_H
#ifndef NULL
#define NULL ((void*) 0)
#endif
typedef _Bool bool;
enum { false, true };
typedef long long i64;
typedef unsigned long long u64;
typedef int i32;
typedef unsigned int u32;
typedef short i16;
typedef unsigned short u16;
typedef char i8;
typedef unsigned char u8;
typedef i32 intptr_t;
typedef u32 uintptr_t;
// 通常描述一个对象的大小,会根据机器的型号变化类型
typedef u32 size_t;
// signed size_t 通常描述系统调用返回值,会根据机器的型号变化类型
typedef i32 ssize_t;
// 通常描述偏移量,会根据机器的型号变化类型
typedef i32 off_t;
// 通常描述物理地址
typedef u32 phyaddr_t;
#define MIN(_a, _b) \
({ \
typeof(_a) __a = (_a); \
typeof(_b) __b = (_b); \
__a <= __b ? __a : __b; \
})
#define MAX(_a, _b) \
({ \
typeof(_a) __a = (_a); \
typeof(_b) __b = (_b); \
__a >= __b ? __a : __b; \
})
#define ROUNDDOWN(a, n) \
({ \
u32 __a = (u32) (a); \
(typeof(a)) (__a - __a % n); \
})
#define ROUNDUP(a, n) \
({ \
u32 __n = (u32) (n); \
(typeof(a)) (ROUNDDOWN((u32) (a) + __n - 1, __n)); \
})
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
// 求出在结构体中的某个成员的偏移量
#define offsetof(type, member) ((size_t) (&((type*)0)->member))
#endif /* MINIOS_TYPE_H */

18
inc/user/stdio.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef MINIOS_USER_STDIO_H
#define MINIOS_USER_STDIO_H
#include <stdio.h>
#define STDIN 0
#define STDOUT 1
// lib/user/stdio.c
int printf(const char *fmt, ...);
int vprintf(const char *fmt, va_list);
u8 getch(void);
u8 getchar(void);
void fflush(void);
#endif /* MINIOS_USER_STDIO_H */

24
inc/user/syscall.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef MINIOS_USER_SYSCALL_H
#define MINIOS_USER_SYSCALL_H
#include <type.h>
#include <syscall.h>
ssize_t syscall0(size_t NR_syscall);
ssize_t syscall1(size_t NR_syscall, size_t p1);
ssize_t syscall2(size_t NR_syscall, size_t p1, size_t p2);
ssize_t syscall3(size_t NR_syscall, size_t p1, size_t p2, size_t p3);
ssize_t syscall4(size_t NR_syscall, size_t p1, size_t p2, size_t p3, size_t p4);
ssize_t syscall5(size_t NR_syscall, size_t p1, size_t p2, size_t p3, size_t p4, size_t p5);
ssize_t get_ticks(void);
ssize_t get_pid(void);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
ssize_t exec(const char *pathname);
ssize_t fork(void);
ssize_t exit(int status);
ssize_t wait(int *wstatus);
ssize_t fork_ack(void);
#endif

14
inc/user/wait.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef MINIOS_USER_WAIT_H
#define MINIOS_USER_WAIT_H
#define WEXITSTATUS(s) (((s) & 0xff00) >> 8)
#define WTERMSIG(s) ((s) & 0x7f)
#define WSTOPSIG(s) WEXITSTATUS(s)
#define WCOREDUMP(s) ((s) & 0x80)
#define WIFEXITED(s) (!WTERMSIG(s))
#define WIFSTOPPED(s) ((short)((((s)&0xffff)*0x10001)>>8) > 0x7f00)
#define WIFSIGNALED(s) (((s)&0xffff)-1U < 0xffu)
#define WIFCONTINUED(s) ((s) == 0xffff)
#endif /* MINIOS_USER_WAIT_H */

211
inc/x86.h Normal file
View File

@ -0,0 +1,211 @@
#ifndef MINIOS_X86_H
#define MINIOS_X86_H
#include <type.h>
static inline u8
inb(int port)
{
u8 data;
asm volatile("inb %w1,%0" : "=a" (data) : "d" (port));
return data;
}
static inline void
insb(int port, void *addr, int cnt)
{
asm volatile("cld\n\trepne\n\tinsb"
: "=D" (addr), "=c" (cnt)
: "d" (port), "0" (addr), "1" (cnt)
: "memory", "cc");
}
static inline u16
inw(int port)
{
u16 data;
asm volatile("inw %w1,%0" : "=a" (data) : "d" (port));
return data;
}
static inline void
insw(int port, void *addr, int cnt)
{
asm volatile("cld\n\trepne\n\tinsw"
: "=D" (addr), "=c" (cnt)
: "d" (port), "0" (addr), "1" (cnt)
: "memory", "cc");
}
static inline u32
inl(int port)
{
u32 data;
asm volatile("inl %w1,%0" : "=a" (data) : "d" (port));
return data;
}
static inline void
insl(int port, void *addr, int cnt)
{
asm volatile("cld\n\trepne\n\tinsl"
: "=D" (addr), "=c" (cnt)
: "d" (port), "0" (addr), "1" (cnt)
: "memory", "cc");
}
static inline void
outb(int port, u8 data)
{
asm volatile("outb %0,%w1" : : "a" (data), "d" (port));
}
static inline void
outsb(int port, const void *addr, int cnt)
{
asm volatile("cld\n\trepne\n\toutsb"
: "=S" (addr), "=c" (cnt)
: "d" (port), "0" (addr), "1" (cnt)
: "cc");
}
static inline void
outw(int port, u16 data)
{
asm volatile("outw %0,%w1" : : "a" (data), "d" (port));
}
static inline void
outsw(int port, const void *addr, int cnt)
{
asm volatile("cld\n\trepne\n\toutsw"
: "=S" (addr), "=c" (cnt)
: "d" (port), "0" (addr), "1" (cnt)
: "cc");
}
static inline void
outsl(int port, const void *addr, int cnt)
{
asm volatile("cld\n\trepne\n\toutsl"
: "=S" (addr), "=c" (cnt)
: "d" (port), "0" (addr), "1" (cnt)
: "cc");
}
static inline void
outl(int port, u32 data)
{
asm volatile("outl %0,%w1" : : "a" (data), "d" (port));
}
static inline void
lcr0(u32 val)
{
asm volatile("movl %0,%%cr0" : : "r" (val));
}
static inline u32
rcr0(void)
{
u32 val;
asm volatile("movl %%cr0,%0" : "=r" (val));
return val;
}
static inline u32
rcr2(void)
{
u32 val;
asm volatile("movl %%cr2,%0" : "=r" (val));
return val;
}
static inline void
lcr3(u32 val)
{
asm volatile("movl %0,%%cr3" : : "r" (val));
}
static inline u32
rcr3(void)
{
u32 val;
asm volatile("movl %%cr3,%0" : "=r" (val));
return val;
}
static inline void
lcr4(u32 val)
{
asm volatile("movl %0,%%cr4" : : "r" (val));
}
static inline u32
rcr4(void)
{
u32 cr4;
asm volatile("movl %%cr4,%0" : "=r" (cr4));
return cr4;
}
static inline void
tlbflush(void)
{
u32 cr3;
asm volatile("movl %%cr3,%0" : "=r" (cr3));
asm volatile("movl %0,%%cr3" : : "r" (cr3));
}
static inline u32
read_eflags(void)
{
u32 eflags;
asm volatile("pushfl; popl %0" : "=r" (eflags));
return eflags;
}
static inline void
write_eflags(u32 eflags)
{
asm volatile("pushl %0; popfl" : : "r" (eflags));
}
static inline u32
read_ebp(void)
{
u32 ebp;
asm volatile("movl %%ebp,%0" : "=r" (ebp));
return ebp;
}
static inline u32
read_esp(void)
{
u32 esp;
asm volatile("movl %%esp,%0" : "=r" (esp));
return esp;
}
static inline u64
read_tsc(void)
{
u64 tsc;
asm volatile("rdtsc" : "=A" (tsc));
return tsc;
}
static inline u32
xchg(volatile u32 *addr, u32 newval)
{
u32 result;
// The + in "+m" denotes a read-modify-write operand.
asm volatile("lock; xchgl %0, %1"
: "+m" (*addr), "=a" (result)
: "1" (newval)
: "cc");
return result;
}
#endif /* MINIOS_X86_H */

53
kern/Makefrag Normal file
View File

@ -0,0 +1,53 @@
#
# makefile的kernel部分
# 将会导入到根目录的makefile文件
#
OBJDIRS += kern
KERN_ENTRY_ADDR := 0xC0200000
KERN_SRCFILES:= kern/astart.asm \
kern/atrap.asm \
kern/aswitch.asm \
kern/exec.c \
kern/exit.c \
kern/fork.c \
kern/fork_ack.c \
kern/fs.c \
kern/main.c \
kern/pmap.c \
kern/process.c \
kern/sche.c \
kern/start.c \
kern/syscall.c \
kern/time.c \
kern/trap.c \
kern/wait.c \
lib/kern/terminal.c \
lib/kern/keyboard.c \
lib/kern/kmalloc.c \
lib/printfmt.c \
lib/string.c
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
KERN_OBJFILES := $(patsubst %.asm, $(OBJDIR)/%.o, $(KERN_OBJFILES))
$(OBJDIR)/kern/fork_ack.o: lib/kern/fork_ack.o
@echo + cp $<
@mkdir -p $(@D)
@cp $< $(@D)
$(OBJDIR)/kern/%.o: kern/%.c $(OBJDIR)/.vars.CFLAGS
@echo + cc $<
@mkdir -p $(@D)
@$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR)/kern/%.o: kern/%.asm $(OBJDIR)/.vars.CFLAGS
@echo + as obj $<
@mkdir -p $(@D)
@$(AS) -f elf -o $@ $<
$(OBJDIR)/kern/kernel.bin: $(KERN_OBJFILES) $(OBJDIR)/.vars.LDFLAGS
@echo + ld $@
@$(LD) $(LDFLAGS) -s -Ttext $(KERN_ENTRY_ADDR) -o $@ $(KERN_OBJFILES) $(GCC_LIB)
@$(LD) $(LDFLAGS) -Ttext $(KERN_ENTRY_ADDR) -o $(OBJDIR)/kern/kernel.dbg $(KERN_OBJFILES) $(GCC_LIB)

82
kern/astart.asm Normal file
View File

@ -0,0 +1,82 @@
; 此时内存看上去是这样的(更详细的内存情况在 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.
;
;
SELECTOR_KERNEL_CS equ 008h
SELECTOR_KERNEL_DS equ 010h
SELECTOR_KERNEL_GS equ 018h + 3
SELECTOR_TSS equ 020h
SELECTOR_LDT equ 028h
; 导入函数
; kern/start.c
extern cstart
; kern/main.c
extern kernel_main
; 导入全局变量
; kern/start.c
extern gdt_ptr
extern idt_ptr
[SECTION .bss]
StackSpace resb 4096
global StackTop
StackTop: ; 栈顶
[section .text] ; 代码在此
global _start ; 导出 _start
_start:
; 把 esp 从 LOADER 挪到 KERNEL
mov esp, StackTop ; 堆栈在 bss 段中
call cstart ; 在此函数中改变了gdt_ptr让它指向新的GDT
lgdt [gdt_ptr] ; 装载GDT
mov ax, SELECTOR_KERNEL_GS
mov gs, ax ; 刷新gs缓存因为在cstart中发生了更新
mov ax, SELECTOR_LDT; 装载LDT
lldt ax
mov ax, SELECTOR_TSS; 装载TSS
ltr ax
lidt [idt_ptr]
jmp SELECTOR_KERNEL_CS:kernel_main

59
kern/aswitch.asm Normal file
View File

@ -0,0 +1,59 @@
[BITS 32]
[section .text]
P_STACKBASE equ 0
EFLAGSREG equ P_STACKBASE
EDIREG equ EFLAGSREG + 4
ESIREG equ EDIREG + 4
EBPREG equ ESIREG + 4
EBXREG equ EBPREG + 4
EDXREG equ EBXREG + 4
ECXREG equ EDXREG + 4
EAXREG equ ECXREG + 4
ESPREG equ EAXREG + 4
global switch_kern_context
;void switch_kern_context(
; struct kern_context *current_context,
; struct kern_context *next_context
;)
; 程序语义在kern/process.c的schedule函数有讲
switch_kern_context:
push ebp
mov ebp, esp
push eax
push ebx
mov eax, [ebp + 8]
mov ebx, [ebp + 12]
call .inner_switch
pop ebx
pop eax
pop ebp
ret
.inner_switch:
mov [eax + ESPREG], esp
lea esp, [eax + ESPREG]
push eax
push ecx
push edx
push ebx
push ebp
push esi
push edi
pushf
mov esp, ebx
popf
pop edi
pop esi
pop ebp
pop ebx
pop edx
pop ecx
pop eax
pop esp
ret

311
kern/atrap.asm Normal file
View File

@ -0,0 +1,311 @@
[section .text]
; 导入
extern StackTop ; kern/astart.asm
extern tss ; inc/kern/protect.h
extern exception_handler ; inc/kern/trap.h
extern irq_table ; inc/kern/trap.h
extern syscall_handler ; inc/kern/trap.h
extern p_proc_ready ; inc/kern/process.h
; 具体看inc/kern/process.h pcb维护的寄存器表
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
extern to_kern_stack ; kern/process.c
save:
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx
mov eax, esp
test dword [eax + CSREG - P_STACKBASE], 3
jz .1 ; 根据段寄存器的状态信息判断是否发生内核重入
mov esp, StackTop ; 如果没有发生内核重入就意味着要到用户栈了先将esp移入临时内核栈
push eax ; 传入进程表首地址信息
call to_kern_stack ; 这个函数之后esp变为进程内核栈
pop eax ; 恢复进程表首地址信息这个是restart第一句话要用的
.1:
push eax ; 将restart要用的如果在内核栈重入pop esp不会有任何变化
push restart ; 否则eax在进程表首地址pop esp会使esp移动到用户栈栈底
jmp [eax + RETADR - P_STACKBASE]
restart:
pop esp ; 获悉当前的esp该到哪不用管现在是否要回用户态语义在save中有解释
pop gs
pop fs
pop es
pop ds
popad
add esp, 4
iretd
; 系统调用
int_syscall:
call save
sti
call syscall_handler
cli
ret
EOI equ 0x20
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>
; 中断和异常 -- 硬件中断
; ---------------------------------
%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).
call save
mov al, EOI
out INT_M_CTL, al
push 0
call [irq_table + 0]
pop ecx
ret
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
hlt ; 后面的8个外设中断暂时不需要先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 eax
mov eax, [esp + 4]
mov [StackTop - 1024], eax
pop eax
pushad ; `.
push ds ; |
push es ; | 保存原寄存器值
push fs ; |
push gs ; /
mov dx, ss
mov ds, dx
mov es, dx
mov eax, esp
test dword [eax + CSREG - P_STACKBASE], 3
jz .1 ; 根据段寄存器的状态信息判断是否发生内核重入
mov esp, StackTop ; 如果没有发生内核重入就意味着要到用户栈了先将esp移入临时内核栈
push eax ; 传入进程表首地址信息
call to_kern_stack ; 这个函数之后esp变为进程内核栈
pop eax ; 恢复进程表首地址信息这个是restart第一句话要用的
.1:
mov ecx, [eax + EFLAGSREG - P_STACKBASE]
push ecx
mov ecx, [eax + CSREG - P_STACKBASE]
push ecx
mov ecx, [eax + EIPREG - P_STACKBASE]
push ecx
mov ebx, [StackTop - 1024]
push ebx
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
; 一堆符号导出,没别的
global restart
; 系统调用
global int_syscall
; 异常处理
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

150
kern/exec.c Normal file
View File

@ -0,0 +1,150 @@
#include <assert.h>
#include <errno.h>
#include <elf.h>
#include <string.h>
#include <x86.h>
#include <kern/fs.h>
#include <kern/exec.h>
#include <kern/kmalloc.h>
#include <kern/pmap.h>
#include <kern/protect.h>
#include <kern/sche.h>
#include <kern/syscall.h>
#include <kern/trap.h>
static inline void
init_segment_regs(PROCESS_0 *p_proc)
{
p_proc->user_regs.cs = (SELECTOR_FLAT_C & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_USER;
p_proc->user_regs.ds = (SELECTOR_FLAT_RW & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_USER;
p_proc->user_regs.es = (SELECTOR_FLAT_RW & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_USER;
p_proc->user_regs.fs = (SELECTOR_FLAT_RW & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_USER;
p_proc->user_regs.ss = (SELECTOR_FLAT_RW & SA_RPL_MASK & SA_TI_MASK)
| SA_TIL | RPL_USER;
p_proc->user_regs.gs = (SELECTOR_VIDEO & SA_RPL_MASK & SA_TI_MASK)
| RPL_USER;
}
static inline void
init_pagetbl(PROCESS_0 *p_proc)
{
phyaddr_t new_cr3 = phy_malloc_4k();
memset((void *)K_PHY2LIN(new_cr3), 0, PGSIZE);
struct page_node *new_page_list = kmalloc(sizeof(struct page_node));
new_page_list->nxt = NULL;
new_page_list->paddr = new_cr3;
new_page_list->laddr = -1;
map_kern(new_cr3, &new_page_list);
// 这里需要特别注意的是,替换用户页表这种危险行为
// 无论如何都是要关中断的,不允许中间有任何调度
// 否则很有可能换到一半,啪,一个中断进来调度了
// 调度回来时要加载cr3然后惊喜地发现page fault了
struct page_node *old_page_list;
DISABLE_INT();
old_page_list = p_proc->page_list;
p_proc->cr3 = new_cr3;
p_proc->page_list = new_page_list;
lcr3(p_proc->cr3);
ENABLE_INT();
// 最后记得回收进程页面资源
recycle_pages(old_page_list);
}
ssize_t
kern_exec(PROCESS_0 *p_proc, const char *pathname)
{
ssize_t ret = 0;
// 路径名的地址必须在内核中的地址
// 因为exec会回收用户程序的页表
// 这会导致程序中的地址发生缺页触发page fault
assert((uintptr_t)pathname >= K_PHY2LIN(0));
// 进来先给自己上个锁
while (xchg(&p_proc->lock, 1) == 1)
schedule();
if ((ret = read_file(pathname, (void *)K_PHY2LIN(48 * MB))) < 0)
goto free;
// 这里有一个特判如果不是elf文件会返回ENOEXEC
if (*(uintptr_t *)K_PHY2LIN(48 * MB) != ELF_MAGIC) {
ret = -ENOEXEC;
goto free;
}
assert(p_proc->statu == READY);
// 初始化用户寄存器
memset(&p_proc->user_regs, 0, sizeof(p_proc->user_regs));
init_segment_regs(p_proc);
p_proc->user_regs.eflags = 0x1202; /* IF=1, IOPL=1 */
// 初始化页表
init_pagetbl(p_proc);
// 将elf加载到文件指定的地址
map_elf(p_proc, (void *)K_PHY2LIN(48 * MB));
// 初始化用户栈空间
map_stack(p_proc);
free:
// 最后记得释放锁
xchg(&p_proc->lock, 0);
return ret;
}
/*
* src路径名转换为段目录项名到dst
* -ENOENT
*/
static inline ssize_t
translate_pathname(char *dst, const char *src)
{
assert(strlen(dst) == 11);
char *st = (char *)src;
char *ed = st + strlen(st);
char *dot = ed;
for (char *c = st ; *c ; c++) {
if (*c == '.')
dot = c;
}
if (dot - st > 8)
return -ENOENT;
memcpy(dst, st, dot - st);
if (ed - dot - 1 > 3)
return -ENOENT;
memcpy(dst + 8, dot + 1, ed == dot ? 0 : ed - dot - 1);
for (char *c = dst ; *c ; c++) {
if ('a' <= *c && *c <= 'z')
*c += 'A' - 'a';
}
return 0;
}
ssize_t
do_exec(const char *pathname)
{
ssize_t ret = 0;
char *name_cpy;
// 原来路径名转短目录项名的工作可以交给do_exec来做
name_cpy = kmalloc(12);
memset(name_cpy, ' ', 11);
name_cpy[11] = '\0';
// 当然如果转文件名失败了会返回-ENOENT
if ((ret = translate_pathname(name_cpy, pathname)) < 0)
goto free;
ret = kern_exec(&p_proc_ready->pcb, name_cpy);
free:
if (name_cpy)
kfree(name_cpy);
return ret;
}

117
kern/exit.c Normal file
View File

@ -0,0 +1,117 @@
#include <assert.h>
#include <x86.h>
#include <kern/exit.h>
#include <kern/kmalloc.h>
#include <kern/pmap.h>
#include <kern/sche.h>
#include <kern/syscall.h>
#include <kern/trap.h>
static void
awake_father_and_become_zombie(PROCESS_0 *p_proc)
{
PROCESS_0 *p_fa;
// 由于我们已经协商了上锁的顺序,上锁顺序是从父亲到儿子
// 但是这里我们必须锁了自己才能知道父进程是谁
// 所以这里我们换了一种做法,不再一个劲的等锁
// 如果父进程上锁失败,就直接将自己的锁释放拱手让人
// 这样做有个好处,要么两个锁同时被上掉,要么两个锁同时被释放
// 这也是一个非常有趣的实现方法
// 而真实情况是将大锁拆小锁不可能一个pcb就一个大锁保着这样又浪费效率又难写
while (1) {
if (xchg(&p_proc->lock, 1) == 1)
goto loop;
p_fa = p_proc->fork_tree.p_fa;
if (xchg(&p_fa->lock, 1) == 1)
goto free;
break;
free:
xchg(&p_proc->lock, 0);
loop:
schedule();
}
// 这两句assert防止其他奇奇怪怪的状态出现
assert(p_proc->statu == READY);
assert(p_fa->statu == READY || p_fa->statu == SLEEP);
p_proc->statu = ZOMBIE;
p_fa->statu = READY;
xchg(&p_fa->lock, 0);
xchg(&p_proc->lock, 0);
}
static void
transfer_orphans(PROCESS_0 *p_proc)
{
PROCESS_0 *p_init = &proc_table[0].pcb;
// 上锁顺序为:初始进程->当前进程->子进程
while (xchg(&p_init->lock, 1) == 1)
schedule();
while (xchg(&p_proc->lock, 1) == 1)
schedule();
for (struct son_node *p = p_proc->fork_tree.sons ; p ;) {
PROCESS_0 *p_son = p->p_son;
struct son_node *p_nxt = p->nxt;
// 上子进程的锁,因为需要修改子进程的父进程信息(移到初始进程下)
while (xchg(&p_son->lock, 1) == 1)
schedule();
// 将子进程的进程树信息做修改
// 将节点node移到初始进程的链表头处
p_son->fork_tree.p_fa = p_init;
// 确保这个是双向链表头
assert(p->pre == NULL);
// 接下来就是一坨又臭又长的链表操作部分
if (p->nxt != NULL)
p->nxt->pre = p->pre;
p->nxt = p_init->fork_tree.sons;
if (p->nxt != NULL)
p->nxt->pre = p;
p_init->fork_tree.sons = p;
// 最后释放子进程的锁
xchg(&p_son->lock, 0);
p = p_nxt;
}
// 在移交完后当前进程的子进程信息会被清空
p_proc->fork_tree.sons = NULL;
// 初始进程可能在休眠,而且子进程可能是僵尸进程,需要将初始进程唤醒
// 初始进程会一直调用wait系统调用回收僵尸子进程
assert(p_init->statu == READY || p_init->statu == SLEEP);
p_init->statu = READY;
free:
xchg(&p_proc->lock, 0);
xchg(&p_init->lock, 0);
}
ssize_t
kern_exit(PROCESS_0 *p_proc, int exit_code)
{
// 托孤,将所有子进程转移到初始进程下
transfer_orphans(p_proc);
// 上锁修改exit code
while (xchg(&p_proc->lock, 1) == 1)
schedule();
p_proc->exit_code = exit_code;
xchg(&p_proc->lock, 0);
// 下面两个操作会修改进程的状态,
// 这是非常危险的,最好用开关中断保护上
DISABLE_INT();
// 这个函数干了两件事,唤醒父进程,将自己状态置为僵尸进程
// 关中断就相当于两件事同时干了
awake_father_and_become_zombie(p_proc);
// 在触发了调度之后这个进程在被回收之前永远无法被调度到
schedule();
ENABLE_INT();
panic("exit failed!");
}
ssize_t
do_exit(int status)
{
// 为什么这个参数这么奇怪?你可能需要读读手册
return kern_exit(&p_proc_ready->pcb, (status & 0xFF) << 8);
}

217
kern/fork.c Normal file
View File

@ -0,0 +1,217 @@
#include <assert.h>
#include <x86.h>
#include <errno.h>
#include <string.h>
#include <kern/fork.h>
#include <kern/syscall.h>
#include <kern/trap.h>
#include <kern/pmap.h>
#include <kern/kmalloc.h>
#include <kern/sche.h>
#include <kern/stdio.h>
// get the flag of a lin addr in page table
static inline u32 get_flag(uintptr_t laddr, u32 cr3) {
assert(PGOFF(laddr) == 0);
uintptr_t *pde_ptr = (uintptr_t *)K_PHY2LIN(cr3);
assert((pde_ptr[PDX(laddr)] & PTE_P) != 0);
phyaddr_t pte_phy = PTE_ADDR(pde_ptr[PDX(laddr)]);
uintptr_t *pte_ptr = (uintptr_t *)K_PHY2LIN(pte_phy);
assert((pte_ptr[PTX(laddr)] & PTE_P) != 0);
return PTE_FLAG(pte_ptr[PTX(laddr)]);
}
static phyaddr_t
alloc_phy_page(struct page_node **page_list)
{
phyaddr_t paddr = phy_malloc_4k();
struct page_node *new_node = kmalloc(sizeof(struct page_node));
new_node->nxt = *page_list;
new_node->paddr = paddr;
new_node->laddr = -1;
*page_list = new_node;
return paddr;
}
static phyaddr_t
lin_mapping_phy(u32 cr3,
struct page_node **page_list,
uintptr_t laddr,
u32 pte_flag)
{
assert(PGOFF(laddr) == 0);
uintptr_t *pde_ptr = (uintptr_t *)K_PHY2LIN(cr3);
if ((pde_ptr[PDX(laddr)] & PTE_P) == 0) {
phyaddr_t pte_phy = alloc_phy_page(page_list);
memset((void *)K_PHY2LIN(pte_phy), 0, PGSIZE);
pde_ptr[PDX(laddr)] = pte_phy | PTE_P | PTE_W | PTE_U;
}
phyaddr_t pte_phy = PTE_ADDR(pde_ptr[PDX(laddr)]);
uintptr_t *pte_ptr = (uintptr_t *)K_PHY2LIN(pte_phy);
phyaddr_t page_phy;
if ((pte_ptr[PTX(laddr)] & PTE_P) != 0)
warn("fork alloc: this page was mapped before, laddr: %x", laddr);
page_phy = alloc_phy_page(page_list);
(*page_list)->laddr = laddr;
pte_ptr[PTX(laddr)] = page_phy | pte_flag;
return page_phy;
}
// modified from pmap.c::map_elf
// first alloc a new page list, together with a new cr3(with page directory page)
// second map kern
// third iterate over parent pages,
// for each parent page(except page table pages, child has its own) , map a page in child proc
// then copy content of this page to child
// finally, set child->page_list to this brand new one and set cr3
// SET: child's page list and cr3
static inline void copy_parent_pages(PROCESS_0 *child, PROCESS_0 *parent){
phyaddr_t new_cr3 = phy_malloc_4k();
memset((void *)K_PHY2LIN(new_cr3), 0, PGSIZE);
struct page_node *new_page_list = kmalloc(sizeof(struct page_node));
new_page_list->nxt = NULL;
new_page_list->paddr = new_cr3;
new_page_list->laddr = -1;
// maybe redundant, let it be for now
map_kern(new_cr3, &new_page_list);
// iterate over parent pages
for (struct page_node* pa_page = parent->page_list; pa_page != NULL; pa_page = pa_page->nxt) {
// no need to take care of non-physical page, we have our own page table and cr3
if (pa_page->laddr == -1) continue;
// first alloc a new page for the same laddr, and set flag same as parent
phyaddr_t new_paddr = lin_mapping_phy(new_cr3, &new_page_list, pa_page->laddr, get_flag(pa_page->laddr, parent->cr3));
// copy parent page to child page using paddr
memcpy((void*)K_PHY2LIN(new_paddr), (const void*)K_PHY2LIN(pa_page->paddr), PGSIZE);
// kprintf("%x -> %x\n", K_PHY2LIN(pa_page->paddr), K_PHY2LIN(new_paddr));
}
child->page_list = new_page_list;
child->cr3 = new_cr3;
}
ssize_t
kern_fork(PROCESS_0 *p_fa)
{
// 这可能是你第一次实现一个比较完整的功能,你可能会比较畏惧
// 但是放心,别怕,先别想自己要实现一个这么大的东西而毫无思路
// 这样你在焦虑的同时也在浪费时间,就跟你在实验五中被页表折磨一样
// 人在触碰到未知的时候总是害怕的,这是天性,所以请你先冷静下来
// fork系统调用会一步步引导你写出来不会让你本科造火箭的
// panic("Unimplement! CALM DOWN!");
// 推荐是边写边想,而不是想一车然后写,这样非常容易计划赶不上变化
//? before all, lock father
while (xchg(&p_fa->lock, 1) == 1)
schedule();
// fork的第一步你需要找一个空闲IDLE的进程作为你要fork的子进程
PROCESS *proc_child = NULL;
int i = 0;
//? maybe cli will be better when preserving a proc resource
DISABLE_INT();
for (i = 1; i < PCB_SIZE; ++ i) {
if (proc_table[i].pcb.statu == IDLE) {
proc_table[i].pcb.statu = INITING;
proc_child = &proc_table[i];
break;
}
}
ENABLE_INT();
if (proc_child == NULL) {
// NO hell. no free proc_table found.
xchg(&p_fa->lock, 0);
return -EAGAIN;
}
assert(proc_child->pcb.lock == 0);
while (xchg(&proc_child->pcb.lock, 1) == 1)
schedule();
PROCESS_0 *p_child = &proc_child->pcb;
// 再之后你需要做的是好好阅读一下pcb的数据结构搞明白结构体中每个成员的语义
// 别光扫一遍,要搞明白这个成员到底在哪里被用到了,具体是怎么用的
// 可能exec和exit系统调用的代码能够帮助你对pcb的理解不先理解好pcb你fork是无从下手的
// panic("Unimplement! read pcb");
// 在阅读完pcb之后终于可以开始fork工作了
// 本质上相当于将父进程的pcb内容复制到子进程pcb中
// 但是你需要想清楚,哪些应该复制到子进程,哪些不应该复制,哪些应该子进程自己初始化
// 其中有三个难点
// 1. 子进程"fork"的返回值怎么处理?(需要你对系统调用整个过程都掌握比较清楚,如果非常清晰这个问题不会很大)
// 2. 子进程内存如何复制别傻乎乎地复制父进程的cr3本质上相当于与父进程共享同一块内存
// 而共享内存肯定不符合fork的语义这样一个进程写内存某块地方会影响到另一个进程这个东西需要你自己思考如何复制父进程的内存
// 3. 在fork结束后肯定会调度到子进程那么你怎么保证子进程能够正常进入用户态
// (你肯定会觉得这个问题问的莫名其妙的,只能说你如果遇到那一块问题了就会体会到这个问题的重要性,
// 这需要你对调度整个过程都掌握比较清楚)
//? Start to COPY
//? 1. COPY PCB
// p_fa shares the same base addr as its padding
// anyways, copy the whole proc_table item(8K?)
DISABLE_INT(); // make sure this process is never interrupted
// memset(proc_child, 0, sizeof(PROCESS)); // clear child's kernel stack //? seem to be useless
// the commented fields below will be set later
// p_child->cr3
p_child->exit_code = p_fa->exit_code;
// p_child->fork_tree
// p_child->kern_regs
// p_child->lock
// p_child->page_list
p_child->pid = i;
p_child->priority = p_fa->priority;
p_child->statu = INITING; //! important!!! if you copied this from parent, haha, waste one hour
p_child->ticks = p_fa->ticks;
p_child->user_regs = p_fa->user_regs;
ENABLE_INT(); // TODO: may be a useless atomic block, try to remove and test again
//? 2. ALLOC PAGES AND COPY PHYSICAL MEMORY
copy_parent_pages(p_child, p_fa);
// panic("Unimplement! soul torture1");
//? 3. SET restart point
// //! maybe, kern stack different, use offset relevant to stack base addr to set child's kern esp
// u32 esp_off = p_fa->kern_regs.esp - (u32)p_fa;
// p_child->kern_regs.esp = (u32)p_child + esp_off;
// u32 ebp_off = p_fa->kern_regs.ebp - (u32)p_fa;
// p_child->kern_regs.ebp = (u32)p_child + ebp_off;
// kprintf("%x %x\n", p_child->kern_regs.esp, p_fa->kern_regs.esp);
// kprintf("%x %x\n", p_child->user_regs.kernel_esp, p_fa->user_regs.kernel_esp);
//! kern_ctx is not part of our FSM model of user process, never copy it from parent
//! or 1 another hour will be wasted
// just set it as a new process from scratch, directly jump to restart
p_child->kern_regs.esp = (u32)(proc_child + 1) - 8;
*(u32 *)(p_child->kern_regs.esp + 0) = (u32)restart;
*(u32 *)(p_child->kern_regs.esp + 4) = (u32)p_child;
p_child->user_regs.eax = 0; // eax as the retval
// 别忘了维护进程树,将这对父子进程关系添加进去
// ? maintain process tree
p_child->fork_tree.p_fa = p_fa;
// malloc a son_node
struct son_node* p_son = (struct son_node*)kmalloc(sizeof(struct son_node));
p_son->p_son = p_child;
// head insert into parent's son list
p_son->pre = NULL;
p_son->nxt = p_fa->fork_tree.sons;
if (p_fa->fork_tree.sons != NULL) {
p_son->nxt->pre = p_son;
}
p_fa->fork_tree.sons = p_son;
// 最后你需要将子进程的状态置为READY说明fork已经好了子进程准备就绪了
p_child->statu = READY;
xchg(&p_child->lock, 0);
xchg(&p_fa->lock, 0);
// 在你写完fork代码时先别急着运行跑先要对自己来个灵魂拷问
// 1. 上锁上了吗?所有临界情况都考虑到了吗?(永远要相信有各种奇奇怪怪的并发问题)
// 2. 所有错误情况都判断到了吗错误情况怎么处理RTFM->`man 2 fork`
// 3. 你写的代码真的符合fork语义吗
// panic("Unimplement! soul torture");
// ? return to parent proc by the normal syscall return machanism
return p_child->pid;
}
ssize_t
do_fork(void)
{
return kern_fork(&p_proc_ready->pcb);
}

182
kern/fs.c Normal file
View File

@ -0,0 +1,182 @@
#include <assert.h>
#include <errno.h>
#include <fat32.h>
#include <mmu.h>
#include <string.h>
#include <x86.h>
#include <kern/keyboard.h>
#include <kern/fs.h>
#include <kern/stdio.h>
#include <kern/syscall.h>
/*
*
* assert让fd为00
*
*/
ssize_t
kern_read(int fd, void *buf, size_t count)
{
assert(fd == 0);
char *s = buf;
for (size_t i = 0 ; i < count ; i++) {
char c = read_keyboard_buf();
if (c == -1)
return i;
*s++ = c;
}
return count;
}
ssize_t
do_read(int fd, void *buf, size_t count)
{
assert(buf < (void *)KERNBASE);
assert(buf + count < (void *)KERNBASE);
return kern_read(fd, buf, count);
}
/*
*
* assert让fd为11
*
*/
ssize_t
kern_write(int fd, const void *buf, size_t count)
{
assert(fd == 1);
const char *s = buf;
for (size_t i = 0 ; i < count ; i++)
kprintf("%c", *s++);
return count;
}
ssize_t
do_write(int fd, const void *buf, size_t count)
{
assert(buf < (void *)KERNBASE);
assert(buf + count < (void *)KERNBASE);
return kern_write(fd, buf, count);
}
#define SECTSIZE 512
static void
waitdisk(void)
{
// wait for disk reaady
while ((inb(0x1F7) & 0xC0) != 0x40)
/* do nothing */;
}
static void
readsect(void *dst, u32 offset)
{
// wait for disk to be ready
waitdisk();
outb(0x1F2, 1); // count = 1
outb(0x1F3, offset);
outb(0x1F4, offset >> 8);
outb(0x1F5, offset >> 16);
outb(0x1F6, (offset >> 24) | 0xE0);
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
// wait for disk to be ready
waitdisk();
// read a sector
insl(0x1F0, dst, SECTSIZE/4);
}
u32 fat_start_sec;
u32 data_start_sec;
u32 fat_now_sec;
struct BPB bpb;
/*
*
*/
static u32
get_next_clus(u32 current_clus)
{
u32 sec = current_clus * 4 / SECTSIZE;
u32 off = current_clus * 4 % SECTSIZE;
static u32 buf[SECTSIZE / 4];
if (fat_now_sec != fat_start_sec + sec) {
readsect(buf, fat_start_sec + sec);
fat_now_sec = fat_start_sec + sec;
}
return buf[off / 4];
}
/*
*
* dst开头的位置
*/
static void *
read_data_sec(void *dst, u32 current_clus)
{
current_clus -= 2;
current_clus *= bpb.BPB_SecPerClus;
current_clus += data_start_sec;
for (int i = 0 ; i < bpb.BPB_SecPerClus ; i++, dst += SECTSIZE)
readsect(dst, current_clus + i);
return dst;
}
/*
* filename读取文件到dst处
* filename要求是短目录项名11
* dst推荐是3GB + 48MB
*/
ssize_t
read_file(const char *filename, void *dst)
{
assert(strlen(filename) == 11);
readsect(&bpb, 0);
fat_start_sec = bpb.BPB_RsvdSecCnt;
data_start_sec = fat_start_sec + bpb.BPB_FATSz32 * bpb.BPB_NumFATs;
u32 root_clus = bpb.BPB_RootClus;
u32 file_clus = 0;
assert(bpb.BPB_BytsPerSec == SECTSIZE && bpb.BPB_SecPerClus == 8);
static char buf[SECTSIZE * 8];
// 遍历目录项获取文件起始簇号
while (root_clus < 0x0FFFFFF8) {
void *read_end = read_data_sec((void *)buf, root_clus);
for (struct Directory_Entry *p = (void *)buf
; (void *)p < read_end ; p++) {
if (strncmp(p->DIR_Name, filename, 11) == 0) {
assert(p->DIR_FileSize <= 16 * MB);
file_clus = (u32)p->DIR_FstClusHI << 16 |
p->DIR_FstClusLO;
break;
}
}
if (file_clus != 0)
break;
root_clus = get_next_clus(root_clus);
}
if (file_clus == 0)
return -ENOENT;
// 读入文件
while (file_clus < 0x0FFFFFF8) {
dst = read_data_sec(dst, file_clus);
file_clus = get_next_clus(file_clus);
}
return 0;
}

56
kern/main.c Normal file
View File

@ -0,0 +1,56 @@
#include <assert.h>
#include <x86.h>
#include <kern/stdio.h>
#include <kern/syscall.h>
#include <kern/protect.h>
#include <kern/process.h>
#include <kern/trap.h>
#include <kern/kmalloc.h>
// 标志着内核是否处理完成
bool init_kernel;
// 指向当前进程pcb的指针
PROCESS *p_proc_ready;
// pcb表
PROCESS proc_table[PCB_SIZE];
/*
* main函数
*
*/
void kernel_main(void)
{
kprintf("---start kernel main---\n");
phy_init_4k(); // init phy_malloc/free_4k management
p_proc_ready = proc_table;
PROCESS_0 *p_proc = &p_proc_ready->pcb;
p_proc->statu = READY;
// kern regs
p_proc->kern_regs.esp = (u32)(p_proc_ready + 1) - 8;
// 保证切换内核栈后执行流进入的是restart函数。
*(u32 *)(p_proc->kern_regs.esp + 0) = (u32)restart;
// 这里是因为restart要用`pop esp`确认esp该往哪里跳。
*(u32 *)(p_proc->kern_regs.esp + 4) = (u32)p_proc;
// 初始化进程树
p_proc->fork_tree.p_fa = NULL;
p_proc->fork_tree.sons = NULL;
// 初始化ticks
p_proc->priority = p_proc->ticks = 1;
// 在实现了exec系统调用后main函数的负担就少了
// 只需要调用一个函数接口就能实现进程的加载
if (do_exec("initproc.bin") < 0)
panic("init process failed");
// 切换tss
tss.esp0 = (u32)(&p_proc->user_regs + 1);
lcr3(p_proc->cr3);
init_kernel = true;
// 开个无用的kern_context存当前执行流的寄存器上下文之后就没用了直接放在临时变量中
struct kern_context idle;
switch_kern_context(&idle, &p_proc->kern_regs);
assert(0);
}

154
kern/pmap.c Normal file
View File

@ -0,0 +1,154 @@
#include <assert.h>
#include <elf.h>
#include <string.h>
#include <mmu.h>
#include <kern/kmalloc.h>
#include <kern/pmap.h>
/*
* page_list页面信息
*
*/
static phyaddr_t
alloc_phy_page(struct page_node **page_list)
{
phyaddr_t paddr = phy_malloc_4k();
struct page_node *new_node = kmalloc(sizeof(struct page_node));
new_node->nxt = *page_list;
new_node->paddr = paddr;
new_node->laddr = -1;
*page_list = new_node;
return paddr;
}
/*
* MINIOS中比较通用的页表映射函数
* laddr处的虚拟页面映射到物理地址为paddrpaddr为-1
* pte_flag置位到页表项PTE_P | PTE_W | PTE_U
* page_list这个链表中
*/
static void
lin_mapping_phy(u32 cr3,
struct page_node **page_list,
uintptr_t laddr,
phyaddr_t paddr,
u32 pte_flag)
{
assert(PGOFF(laddr) == 0);
uintptr_t *pde_ptr = (uintptr_t *)K_PHY2LIN(cr3);
if ((pde_ptr[PDX(laddr)] & PTE_P) == 0) {
phyaddr_t pte_phy = alloc_phy_page(page_list);
memset((void *)K_PHY2LIN(pte_phy), 0, PGSIZE);
pde_ptr[PDX(laddr)] = pte_phy | PTE_P | PTE_W | PTE_U;
}
phyaddr_t pte_phy = PTE_ADDR(pde_ptr[PDX(laddr)]);
uintptr_t *pte_ptr = (uintptr_t *)K_PHY2LIN(pte_phy);
phyaddr_t page_phy;
if (paddr == (phyaddr_t)-1) {
if ((pte_ptr[PTX(laddr)] & PTE_P) != 0)
return;
page_phy = alloc_phy_page(page_list);
(*page_list)->laddr = laddr;
} else {
if ((pte_ptr[PTX(laddr)] & PTE_P) != 0)
warn("this page was mapped before, laddr: %x", laddr);
assert(PGOFF(paddr) == 0);
page_phy = paddr;
}
pte_ptr[PTX(laddr)] = page_phy | pte_flag;
}
/*
*
* 3GB ~ 3GB + 128MB的线性地址映射到0 ~ 128MB的物理地址
*/
void
map_kern(u32 cr3, struct page_node **page_list)
{
for (phyaddr_t paddr = 0 ; paddr < 128 * MB ; paddr += PGSIZE) {
lin_mapping_phy(cr3,
page_list,
K_PHY2LIN(paddr),
paddr,
PTE_P | PTE_W | PTE_U);
}
}
/*
* elf文件信息将数据搬迁到指定位置
* eip的置位
*
*/
void
map_elf(PROCESS_0 *p_proc, void *elf_addr)
{
assert(p_proc->lock != 0);
struct Elf *eh = (struct Elf *)elf_addr;
struct Proghdr *ph = (struct Proghdr *)(elf_addr + eh->e_phoff);
for (int i = 0 ; i < eh->e_phnum ; i++, ph++) {
if (ph->p_type != PT_LOAD)
continue;
uintptr_t st = ROUNDDOWN(ph->p_va, PGSIZE);
uintptr_t en = ROUNDUP(st + ph->p_memsz, PGSIZE);
for (uintptr_t laddr = st ; laddr < en ; laddr += PGSIZE) {
u32 pte_flag = PTE_P | PTE_U;
if ((ph->p_flags & ELF_PROG_FLAG_WRITE) != 0)
pte_flag |= PTE_W;
lin_mapping_phy(p_proc->cr3,
&p_proc->page_list,
laddr,
(phyaddr_t)-1,
pte_flag);
}
memcpy( (void *)ph->p_va,
(const void *)eh + ph->p_offset,
ph->p_filesz);
memset( (void *)ph->p_va + ph->p_filesz,
0,
ph->p_memsz - ph->p_filesz);
}
p_proc->user_regs.eip = eh->e_entry;
}
/*
* 访
* (0xbffff000~0xc0000000)
* esp寄存器放置好
*/
void
map_stack(PROCESS_0 *p_proc)
{
assert(p_proc->lock != 0);
lin_mapping_phy(p_proc->cr3,
&p_proc->page_list,
K_PHY2LIN(-PGSIZE),
(phyaddr_t)-1,
PTE_P | PTE_W | PTE_U);
p_proc->user_regs.esp = K_PHY2LIN(0);
}
/*
* page_list回收所有的页面
*/
void
recycle_pages(struct page_node *page_list)
{
for (struct page_node *prevp, *p = page_list ; p ;) {
phy_free_4k(p->paddr);
prevp = p, p = p->nxt;
kfree(prevp);
}
}

47
kern/process.c Normal file
View File

@ -0,0 +1,47 @@
#include <x86.h>
#include <kern/process.h>
#include <kern/sche.h>
#include <kern/syscall.h>
/*
*
* save函数保存完用户寄存器上下文后
* minios的进程栈是与pcb是绑定的
* pcb
* 使8KB7KB多
*
* C语言这知道
* C语言里面写一个内联汇编实现便
*/
void
to_kern_stack(u32 ret_esp)
{
asm volatile (
"add %1, %0\n\t"
"mov %0, %%esp\n\t"
"push %2\n\t"
"jmp *4(%%ebp)\n\t":
: "a"((u32)p_proc_ready->_pad)
, "b"((u32)sizeof(p_proc_ready->_pad))
, "c"(ret_esp)
);
}
u32
kern_get_pid(PROCESS_0 *p_proc)
{
u32 ret = 0;
while (xchg(&p_proc->lock, 1) == 1)
schedule();
ret = p_proc->pid;
free:
xchg(&p_proc->lock, 0);
return ret;
}
ssize_t
do_get_pid(void)
{
return (ssize_t)kern_get_pid(&p_proc_ready->pcb);
}

48
kern/sche.c Normal file
View File

@ -0,0 +1,48 @@
#include <x86.h>
#include <kern/process.h>
#include <kern/protect.h>
#include <kern/trap.h>
/*
*
*/
void
schedule(void)
{
// 如果中断没关,一定!一定!一定!要关中断保证当前执行流操作的原子性
DISABLE_INT();
PROCESS *p_cur_proc = p_proc_ready;
PROCESS *p_next_proc = p_proc_ready;
asm volatile ("":::"memory");
static bool in_sche;
if (in_sche)
goto free;
in_sche = true;
do {
if (++p_next_proc >= proc_table + PCB_SIZE)
p_next_proc = proc_table;
if (p_cur_proc == p_next_proc &&
p_next_proc->pcb.statu != READY) {
asm volatile("sti\n\thlt\n\tcli":::"memory");
}
} while (p_next_proc->pcb.statu != READY);
p_proc_ready = p_next_proc;
// 切换进程页表和tss
lcr3(p_proc_ready->pcb.cr3);
tss.esp0 = (u32)(&p_proc_ready->pcb.user_regs + 1);
in_sche = false;
switch_kern_context(
&p_cur_proc->pcb.kern_regs,
&p_next_proc->pcb.kern_regs);
asm volatile ("":::"memory");
free:
ENABLE_INT();
}

237
kern/start.c Normal file
View File

@ -0,0 +1,237 @@
#include <assert.h>
#include <mmu.h>
#include <x86.h>
#include <kern/protect.h>
#include <kern/stdio.h>
#include <kern/trap.h>
struct Pseudodesc gdt_ptr, idt_ptr;
DESCRIPTOR gdt[GDT_SIZE];
DESCRIPTOR ldt[LDT_SIZE];
GATE idt[IDT_SIZE];
TSS tss;
/*
* 使CPU核休眠
*/
void
_panic(const char *file, int line, const char *fmt,...)
{
va_list ap;
// 确保CPU核不受外界中断的影响
asm volatile("cli");
asm volatile("cld");
va_start(ap, fmt);
kprintf("\x1b[0m\x1b[91mkernel panic at %s:%d: ", file, line);
vkprintf(fmt, ap);
kprintf("\n\x1b[0m");
va_end(ap);
// 休眠CPU核直接罢工
while(1)
asm volatile("hlt");
}
/*
* panicCPU核
*/
void
_warn(const char *file, int line, const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
kprintf("\x1b[0m\x1b[93mkernel warning at %s:%d: ", file, line);
vkprintf(fmt, ap);
kprintf("\n\x1b[0m");
va_end(ap);
}
/*
* gdt表loader中的gdt表不同的是
* tss和ldt的两个的全局描述符
*/
static void
init_gdt(void)
{
init_segment(&gdt[0], 0, 0, 0);
// 代码段(cs)
init_segment(&gdt[1], 0, 0xfffff, DA_CR | DA_32 | DA_LIMIT_4K);
// 数据段(ds,es,fs,ss)
init_segment(&gdt[2], 0, 0xfffff, DA_DRW | DA_32 | DA_LIMIT_4K);
// 显存段(gs)
init_segment(&gdt[3], K_PHY2LIN(0xb8000), 0x8, DA_DRW | DA_DPL3 |
DA_32 | DA_LIMIT_4K);
tss.ss0 = SELECTOR_FLAT_RW;
tss.iobase = sizeof(tss); /* 没有I/O许可位图 */
// tss段
init_segment(&gdt[4], (u32)&tss, sizeof(tss) - 1, DA_386TSS);
// ldt段
init_segment(&gdt[5], (u32)ldt, sizeof(ldt) - 1, DA_LDT);
gdt_ptr.pd_base = (u32)gdt;
gdt_ptr.pd_lim = sizeof(gdt) - 1;
}
/*
* ldt表ldt表用于用户态gdt
* 使ldt
*/
static void
init_ldt(void)
{
init_segment(&ldt[0], 0, 0, 0);
// 代码段(cs)
init_segment(&ldt[1], 0, 0xfffff,
DA_CR | DA_32 | DA_LIMIT_4K | DA_DPL3);
// 数据段(ds,es,fs,ss)
init_segment(&ldt[2], 0, 0xfffff,
DA_DRW | DA_32 | DA_LIMIT_4K | DA_DPL3);
// 用户态的gs继承自gdt的显存段
}
/*
* idt表
*/
static void
init_idt(void)
{
// 异常处理
init_gate(idt + INT_VECTOR_DIVIDE, DA_386IGate,
divide_error, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_DEBUG, DA_386IGate,
single_step_exception, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_NMI, DA_386IGate,
nmi, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_BREAKPOINT, DA_386IGate,
breakpoint_exception, PRIVILEGE_USER);
init_gate(idt + INT_VECTOR_OVERFLOW, DA_386IGate,
overflow, PRIVILEGE_USER);
init_gate(idt + INT_VECTOR_BOUNDS, DA_386IGate,
bounds_check, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_INVAL_OP, DA_386IGate,
inval_opcode, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_COPROC_NOT, DA_386IGate,
copr_not_available, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_DOUBLE_FAULT,DA_386IGate,
double_fault, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_COPROC_SEG, DA_386IGate,
copr_seg_overrun, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_INVAL_TSS, DA_386IGate,
inval_tss, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_SEG_NOT, DA_386IGate,
segment_not_present, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_STACK_FAULT, DA_386IGate,
stack_exception, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_PROTECTION, DA_386IGate,
general_protection, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_PAGE_FAULT, DA_386IGate,
page_fault, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_COPROC_ERR, DA_386IGate,
copr_error, PRIVILEGE_KRNL);
// 外设中断
init_gate(idt + INT_VECTOR_IRQ0 + 0, DA_386IGate,
hwint00, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 1, DA_386IGate,
hwint01, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 2, DA_386IGate,
hwint02, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 3, DA_386IGate,
hwint03, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 4, DA_386IGate,
hwint04, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 5, DA_386IGate,
hwint05, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 6, DA_386IGate,
hwint06, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ0 + 7, DA_386IGate,
hwint07, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 0, DA_386IGate,
hwint08, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 1, DA_386IGate,
hwint09, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 2, DA_386IGate,
hwint10, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 3, DA_386IGate,
hwint11, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 4, DA_386IGate,
hwint12, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 5, DA_386IGate,
hwint13, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 6, DA_386IGate,
hwint14, PRIVILEGE_KRNL);
init_gate(idt + INT_VECTOR_IRQ8 + 7, DA_386IGate,
hwint15, PRIVILEGE_KRNL);
// 系统调用
init_gate(idt + INT_VECTOR_SYSCALL, DA_386IGate,
int_syscall, PRIVILEGE_USER);
idt_ptr.pd_base = (u32)idt;
idt_ptr.pd_lim = sizeof(idt) - 1;
}
/*
* 8259A
*/
static void
init_8259A(void)
{
outb(INT_M_CTL, 0x11); // Master 8259, ICW1.
outb(INT_S_CTL, 0x11); // Slave 8259, ICW1.
// Master 8259, ICW2. 设置 '主8259' 的中断入口地址为 0x20.
outb(INT_M_CTLMASK, INT_VECTOR_IRQ0);
// Slave 8259, ICW2. 设置 '从8259' 的中断入口地址为 0x28
outb(INT_S_CTLMASK, INT_VECTOR_IRQ8);
// Master 8259, ICW3. IR2 对应 '从8259'.
outb(INT_M_CTLMASK, 0x4);
// Slave 8259, ICW3. 对应 '主8259' 的 IR2.
outb(INT_S_CTLMASK, 0x2);
outb(INT_M_CTLMASK, 0x1); // Master 8259, ICW4.
outb(INT_S_CTLMASK, 0x1); // Slave 8259, ICW4.
outb(INT_M_CTLMASK, 0xFF); // Master 8259, OCW1.
outb(INT_S_CTLMASK, 0xFF); // Slave 8259, OCW1.
}
static void
init_irq(void)
{
// 开启时钟中断
enable_irq(CLOCK_IRQ);
// 调整时钟频率至1000Hz
outb(0x43, 0x34);
outb(0x40, 1193&0xff);
outb(0x40, 1193>>8);
// 开启键盘中断
enable_irq(KEYBOARD_IRQ);
}
/*
*
*/
void cstart(void)
{
// 清屏并将光标置为开头
// ANSI转义序列由\x1b(ascii码27为Esc)开头的序列
// 能够控制光标控制终端linux上也在用这个控制终端
// 目前支持的功能在inc/terminal.c中的kprintf函数有写
// \x1b[2J 清屏
// \x1b[H 将光标放置到左上角
kprintf("\x1b[2J\x1b[H");
kprintf("---loading gdt ");
init_gdt();
kprintf("ldt ");
init_ldt();
kprintf("8259A ");
init_8259A();
kprintf("idt ");
init_idt();
kprintf("irq");
init_irq();
kprintf("---\n");
}

147
kern/syscall.c Normal file
View File

@ -0,0 +1,147 @@
#include <assert.h>
#include <kern/fs.h>
#include <kern/process.h>
#include <kern/syscall.h>
#include <kern/time.h>
static ssize_t sys_get_ticks(void);
static ssize_t sys_get_pid(void);
static ssize_t sys_read(void);
static ssize_t sys_write(void);
static ssize_t sys_exec(void);
static ssize_t sys_fork(void);
static ssize_t sys_wait(void);
static ssize_t sys_exit(void);
static ssize_t sys_fork_ack(void);
ssize_t (*syscall_table[])(void) = {
[_NR_get_ticks] sys_get_ticks,
[_NR_get_pid] sys_get_pid,
[_NR_read] sys_read,
[_NR_write] sys_write,
[_NR_exec] sys_exec,
[_NR_fork] sys_fork,
[_NR_wait] sys_wait,
[_NR_exit] sys_exit,
[_NR_fork_ack] sys_fork_ack,
};
/*
*
* 使6
* 0 1 2 3 4 5
* ebx ecx edx esi edi ebp
*/
static u32
get_arg(int order)
{
switch (order) {
case 0:
return p_proc_ready->pcb.user_regs.ebx;
case 1:
return p_proc_ready->pcb.user_regs.ecx;
case 2:
return p_proc_ready->pcb.user_regs.edx;
case 3:
return p_proc_ready->pcb.user_regs.esi;
case 4:
return p_proc_ready->pcb.user_regs.edi;
case 5:
return p_proc_ready->pcb.user_regs.ebp;
default:
panic("invalid order! order: %d", order);
}
}
/*
* ssize_t get_ticks(void)
*
*/
static ssize_t
sys_get_ticks(void)
{
return do_get_ticks();
}
/*
* int get_pid(void)
*
*/
static ssize_t
sys_get_pid(void)
{
return do_get_pid();
}
/*
* ssize_t read(int fd, void *buf, size_t count)
*
* buf为开头的长度为count字节的缓冲区
*/
static ssize_t
sys_read(void)
{
return do_read(get_arg(0), (void *)get_arg(1), get_arg(2));
}
/*
* ssize_t write(int fd, const void *buf, size_t count)
*
* buf为开头的长度为count字节的缓冲区
*/
static ssize_t
sys_write(void)
{
return do_write(get_arg(0), (const void *)get_arg(1), get_arg(2));
}
/*
* ssize_t exec(const char *pathname)
* pathname对应的ELF文件
*/
static ssize_t
sys_exec(void)
{
return do_exec((const void *)get_arg(0));
}
/*
* ssize_t fork(void)
*
*/
static ssize_t
sys_fork(void)
{
return do_fork();
}
/*
* ssize_t wait(int *wstatus)
* exitwstatus接收子进程的返回码
*/
static ssize_t
sys_wait(void)
{
return do_wait((int *)get_arg(0));
}
/*
* ssize_t exit(int status)
* 退status作为返回码传递给父进程
*/
static ssize_t
sys_exit(void)
{
return do_exit(get_arg(0));
}
/*
* ssize_t fork_ack(void)
* forkbomb.c所用fork的次数
*
*/
static ssize_t
sys_fork_ack(void)
{
return do_fork_ack();
}

30
kern/time.c Normal file
View File

@ -0,0 +1,30 @@
#include <type.h>
#include <kern/syscall.h>
#include <kern/time.h>
static size_t timecounter;
/*
*
*/
void
timecounter_inc(void)
{
timecounter++;
}
/*
*
*/
size_t
kern_get_ticks(void)
{
return timecounter;
}
ssize_t
do_get_ticks(void)
{
return (ssize_t)kern_get_ticks();
}

186
kern/trap.c Normal file
View File

@ -0,0 +1,186 @@
#include <assert.h>
#include <x86.h>
#include <kern/keyboard.h>
#include <kern/keymap.h>
#include <kern/process.h>
#include <kern/protect.h>
#include <kern/sche.h>
#include <kern/stdio.h>
#include <kern/syscall.h>
#include <kern/time.h>
#include <kern/trap.h>
/*
*
*/
int k_reenter;
void (*irq_table[16])(int) = {
clock_interrupt_handler,
kb_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
default_interrupt_handler,
};
/*
* 0
*/
void
enable_irq(int irq)
{
u8 mask = 1 << (irq % 8);
if (irq < 8)
outb(INT_M_CTLMASK, inb(INT_M_CTLMASK) & ~mask);
else
outb(INT_S_CTLMASK, inb(INT_S_CTLMASK) & ~mask);
}
/*
* 1
*/
void
disable_irq(int irq)
{
u8 mask = 1 << (irq % 8);
if (irq < 8)
outb(INT_M_CTLMASK, inb(INT_M_CTLMASK) | mask);
else
outb(INT_S_CTLMASK, inb(INT_S_CTLMASK) | mask);
}
/*
*
* warning
*/
void
default_interrupt_handler(int irq)
{
warn("unsupport interrupt! irq = %d", irq);
}
/*
*
* panic
*/
void
exception_handler(int vec_no, int err_code, int eip, int cs, int eflags)
{
static 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"
};
#define PANIC_STR_SIZE 256
static char fmtstr[PANIC_STR_SIZE];
if (vec_no == INT_VECTOR_PAGE_FAULT) {
char *p_str = fmtstr;
p_str += snprintf(
p_str, PANIC_STR_SIZE - (p_str - fmtstr) - 1,
"\x1b[H\x1b[2JOh, you receive a page fault!\n");
p_str += snprintf(
p_str, PANIC_STR_SIZE - (p_str - fmtstr) - 1,
"CS: %%x EIP: %%x You tried to access the address: %%x\n");
if ((err_code & FEC_PR) == 0) {
p_str += snprintf(
p_str, PANIC_STR_SIZE - (p_str - fmtstr) - 1,
"You tried to access a nonexistent page!\n");
}
if ((err_code & FEC_WR) != 0) {
p_str += snprintf(
p_str, PANIC_STR_SIZE - (p_str - fmtstr) - 1,
"You tried to write in this page!\n");
} else {
p_str += snprintf(
p_str, PANIC_STR_SIZE - (p_str - fmtstr) - 1,
"You tried to read in this page!\n");
}
if ((err_code & FEC_U) != 0) {
p_str += snprintf(
p_str, PANIC_STR_SIZE - (p_str - fmtstr) - 1,
"You tried to access a page in user mode!\n");
}
panic(fmtstr, cs, eip, rcr2());
} else {
snprintf(fmtstr, PANIC_STR_SIZE - 1,
"\x1b[H\x1b[2JException! --> "
"%%s\nEFLAGS: %%x CS: %%x EIP: %%x\nError code: %%x");
panic(fmtstr, err_description[vec_no],
eflags, cs, eip, err_code);
}
}
/*
*
*
* !!!
* !!!
* !!!
*
*/
void
clock_interrupt_handler(int irq)
{
timecounter_inc();
// 当一个函数的时间片全用完时
if (--p_proc_ready->pcb.ticks == 0) {
p_proc_ready->pcb.ticks = p_proc_ready->pcb.priority;
schedule();
}
}
/*
*
*/
void
kb_interrupt_handler(int irq)
{
u32 ch = (u32)inb(0x60);
if ((ch & 0x80) != 0)
return;
ch = keymap[ch];
if (0x20 <= ch && ch <= 0x7e)
add_keyboard_buf(ch);
if (ch == ENTER)
add_keyboard_buf('\n');
}
/*
*
*/
void
syscall_handler(void)
{
int syscall_id = p_proc_ready->pcb.user_regs.eax;
ssize_t ret = (*syscall_table[syscall_id])();
p_proc_ready->pcb.user_regs.eax = ret;
}

101
kern/wait.c Normal file
View File

@ -0,0 +1,101 @@
#include <assert.h>
#include <mmu.h>
#include <errno.h>
#include <x86.h>
#include <kern/syscall.h>
#include <kern/wait.h>
#include <kern/process.h>
#include <kern/pmap.h>
#include <kern/sche.h>
#include <kern/kmalloc.h>
/*
The wait() system call suspends execution of the calling thread
until one of its children terminates.
RETURN VALUE
on success, returns the process ID of the terminated child;
on failure, -1 is returned.
! however the testwait.bin expects a valued return other than simply -1
*/
ssize_t
kern_wait(int *wstatus)
{
again:
while (xchg(&p_proc_ready->pcb.lock, 1) == 1)
schedule();
PROCESS_0 *p_fa = &p_proc_ready->pcb;
int child_pid = -1;
struct son_node* p_son = p_fa->fork_tree.sons;
if (p_son == NULL) {
xchg(&p_fa->lock, 0);
return -ECHILD;
}
assert(p_son->pre == NULL); // make sure it is the head of the list
/*
struct s_proc {
user_regs; // let it be
kern_regs; // let it be
lock; // get and release before kfree
statu; // set to IDLE
pid; // keep it util return
cr3; // let it be
page_list; // recycle it
exit_code; // set to wstatus
priority; // let it be
ticks; // let it be
fork_tree; // already cleared
};
*/
while (p_son != NULL) {
if (p_son->p_son->statu == ZOMBIE) {
while (xchg(&p_son->p_son->lock, 1) == 1)
schedule();
// just use this value, which is '(status & 0xFF) << 8' in exit.c::do_exit
if (wstatus != NULL)
*wstatus = p_son->p_son->exit_code;
// recycle_pages in pmap.c
recycle_pages(p_son->p_son->page_list);
// remove p_son from p_fa's son list
if (p_son->pre != NULL) {
p_son->pre->nxt = p_son->nxt;
}
else {
p_fa->fork_tree.sons = p_son->nxt;
}
if (p_son->nxt != NULL) {
p_son->nxt->pre = p_son->pre;
}
// keep p_son's pid for retval, 'cause the pointer will get freed before return
child_pid = p_son->p_son->pid;
kfree(p_son);
// free it in proc table, 'cause in fork I judge free by statu
xchg(&proc_table[child_pid].pcb.lock, 0);
proc_table[child_pid].pcb.statu = IDLE; // ! set status be the last state, or die
goto done;
}
p_son = p_son->nxt;
}
xchg(&p_fa->lock, 0);
p_fa->statu = SLEEP; //! sleep after release lock, or it will deadlock
schedule();
goto again;
// 接下来就是你自己的实现了,我们在设计的时候这段代码不会有太大问题
// 在实现完后你任然要对自己来个灵魂拷问
// 1. 上锁上了吗?所有临界情况都考虑到了吗?(永远要相信有各种奇奇怪怪的并发问题)
// 2. 所有错误情况都判断到了吗错误情况怎么处理RTFM->`man 2 wait`
// 3. 是否所有的资源都正确回收了?
// 4. 你写的代码真的符合wait语义吗
done:
xchg(&p_fa->lock, 0);
// panic("Unimplement! soul torture");
return child_pid;
}
ssize_t
do_wait(int *wstatus)
{
assert((uintptr_t)wstatus < KERNBASE);
assert((uintptr_t)wstatus + sizeof(wstatus) < KERNBASE);
return kern_wait(wstatus);
}

BIN
lab6-finish.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

31
lib/Makefrag Normal file
View File

@ -0,0 +1,31 @@
OBJDIRS += lib
$(OBJDIR)/lib/%.o: lib/%.c $(OBJDIR)/.vars.CFLAGS
@echo + cc $<
@mkdir -p $(@D)
@$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR)/lib/kern/%.o: lib/kern/%.c $(OBJDIR)/.vars.CFLAGS
@echo + cc $<
@mkdir -p $(@D)
@$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR)/lib/user/%.o: lib/user/%.c $(OBJDIR)/.vars.CFLAGS
@echo + cc $<
@mkdir -p $(@D)
@$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR)/lib/%.o: lib/%.asm
@echo + as obj $<
@mkdir -p $(@D)
@$(AS) -f elf -o $@ $<
$(OBJDIR)/lib/kern/%.o: lib/kern/%.asm
@echo + as obj $<
@mkdir -p $(@D)
@$(AS) -f elf -o $@ $<
$(OBJDIR)/lib/user/%.o: lib/user/%.asm
@echo + as obj $<
@mkdir -p $(@D)
@$(AS) -f elf -o $@ $<

BIN
lib/kern/fork_ack.o Normal file

Binary file not shown.

71
lib/kern/keyboard.c Normal file
View File

@ -0,0 +1,71 @@
#include <type.h>
#include <x86.h>
#include <kern/keymap.h>
#include <kern/trap.h>
#include <kern/sche.h>
#define KB_INBUF_SIZE 1024
typedef struct kb_inbuf {
u32 lock;
u8* p_head;
u8* p_tail;
int count;
u8 buf[KB_INBUF_SIZE];
} KB_INPUT;
static KB_INPUT kb_input = {
.p_head = kb_input.buf,
.p_tail = kb_input.buf,
};
/*
* ch这个字符放进内核的字符缓冲区
*/
void
add_keyboard_buf(u8 ch)
{
// 上个自旋锁,保证线程安全(不建议用中断)计组课上讲过,相信大家的记忆力
while (xchg(&kb_input.lock, 1) == 1)
schedule();
if (kb_input.count == KB_INBUF_SIZE)
goto free;
*kb_input.p_tail++ = ch;
if (kb_input.p_tail == kb_input.buf + KB_INBUF_SIZE)
kb_input.p_tail = kb_input.buf;
kb_input.count++;
free:
xchg(&kb_input.lock, 0);
}
/*
* -1
*
*/
u8
read_keyboard_buf(void)
{
u8 res;
// 上个自旋锁,保证线程安全(不建议用中断)计组课上讲过,相信大家的记忆力
while (xchg(&kb_input.lock, 1) == 1)
schedule();
if (kb_input.count == 0) {
res = -1;
goto free;
}
res = *kb_input.p_head++;
if (kb_input.p_head == kb_input.buf + KB_INBUF_SIZE)
kb_input.p_head = kb_input.buf;
kb_input.count--;
free:
xchg(&kb_input.lock, 0);
return res;
}

159
lib/kern/kmalloc.c Normal file
View File

@ -0,0 +1,159 @@
#include <assert.h>
#include <mmu.h>
#include <x86.h>
#include <kern/stdio.h>
#include <kern/kmalloc.h>
#include <kern/sche.h>
#include <kern/trap.h>
static u32 phy_malloc_4k_lock;
static phyaddr_t phy_malloc_4k_p = 96 * MB;
// 96MB~128MB = 0x0600_0000 ~ 0x0800_0000
// 32MB = 32 * 1024 / 4 = 8192 PAGES
#define PHY_START (96 * MB)
#define PHY_END (128 * MB)
#define PHY_PAGENUM ((PHY_END - PHY_START) / PGSIZE)
// map them to 8192 slots
#define PHY_INDEX(paddr) ((paddr - PHY_START) >> 12)
#define PHY_PADDR(idx) ((idx << 12) + PHY_START)
// we have a head index point to the lowest free slot
// if slot[i] is free, slot[i] = the index of the next free slot
// else, slot[i] = -1
// when initialize, slots are initialized to {1, 2, 3, ...}
// when alloc, head mov to slot[i], then set slot[i] = 0
// when free, set slot[i]=head, head = i
static int phy_free_head;
static int phy_free_table[PHY_PAGENUM];
void phy_init_4k() {
for (int i = 0; i < PHY_PAGENUM - 1; ++ i) {
phy_free_table[i] = i + 1;
}
phy_free_table[PHY_PAGENUM-1] = -1;
phy_free_head = 0;
}
/*
* free_4k的代码
*
*
*/
void
phy_free_4k(phyaddr_t paddr)
{
assert(paddr % PGSIZE == 0);
assert(96 * MB <= paddr && paddr < 128 * MB);
while(xchg(&phy_malloc_4k_lock, 1) == 1)
schedule();
phy_free_table[PHY_INDEX(paddr)] = phy_free_head;
phy_free_head = PHY_INDEX(paddr);
xchg(&phy_malloc_4k_lock, 0);
}
/*
* 4kb
* 96MB~128MB
*/
phyaddr_t
phy_malloc_4k(void)
{
while(xchg(&phy_malloc_4k_lock, 1) == 1)
schedule();
assert(0 <= phy_free_head && phy_free_head < PHY_PAGENUM);
phyaddr_t paddr = PHY_PADDR(phy_free_head);
phy_free_head = phy_free_table[phy_free_head];
phy_free_table[PHY_INDEX(paddr)] = -1;
// assert(phy_malloc_4k_p < 128 * MB);
// phyaddr_t paddr = phy_malloc_4k_p;
// phy_malloc_4k_p += PGSIZE;
free:
xchg(&phy_malloc_4k_lock, 0);
return paddr;
}
struct kmalloc_header {
struct kmalloc_header *ptr;
size_t size;
};
static struct kmalloc_header *freePtr;
static u32 malloc_lock;
/*
* kmalloc申请的内存
*/
void
kfree(void *v)
{
struct kmalloc_header *bp, *p;
bp = (struct kmalloc_header*)v - 1;
while(xchg(&malloc_lock, 1) == 1)
schedule();
for (p = freePtr; !(bp > p && bp < p->ptr); p = p->ptr) {
if (p >= p->ptr && (bp > p || bp < p->ptr)) {
break;
}
}
if (bp + bp->size == p->ptr) {
bp->size += p->ptr->size;
bp->ptr = p->ptr->ptr;
} else {
bp->ptr = p->ptr;
}
if (p + p->size == bp) {
p->size += bp->size;
p->ptr = bp->ptr;
} else {
p->ptr = bp;
}
freePtr = p;
free:
xchg(&malloc_lock, 0);
}
/*
* n字节
* 64MB~96MB
*/
void *
kmalloc(size_t n)
{
struct kmalloc_header *p, *prevP;
size_t nUnits;
void *ret;
while(xchg(&malloc_lock, 1) == 1)
schedule();
nUnits = (n + sizeof(struct kmalloc_header) - 1)
/ sizeof(struct kmalloc_header) + 1;
if ((prevP = freePtr) == 0) {
freePtr = prevP = (void *)K_PHY2LIN(64 * MB);
freePtr->ptr = freePtr;
freePtr->size = (32 * MB) / sizeof(struct kmalloc_header);
}
for (p = prevP->ptr ; ; prevP = p, p = p->ptr) {
if (p->size >= nUnits) {
if (p->size == nUnits) {
prevP->ptr = p->ptr;
} else {
p->size -= nUnits;
p += p->size;
p->size = nUnits;
}
freePtr = prevP;
ret = (void*)(p + 1);
goto free;
}
if (p == freePtr) {
panic("malloc failed!");
}
}
free:
xchg(&malloc_lock, 0);
return ret;
}

428
lib/kern/terminal.c Normal file
View File

@ -0,0 +1,428 @@
#include <assert.h>
#include <mmu.h>
#include <string.h>
#include <stdarg.h>
#include <type.h>
#include <x86.h>
#include <kern/stdio.h>
#include <kern/trap.h>
#include <terminal.h>
inline static void
write_to_terminal(u16 disp_pos, u16 content)
{
asm(
"mov %1, %%gs:(%0)" ::"r"(disp_pos * 2), "r"(content)
: "memory");
}
struct kprintfbuf {
// 通用
u16 color;
i16 cursor_row;
i16 cursor_col;
int cnt;
// 控制字符
int CSI;
i16 param1;
i16 param2;
};
#define CSI_ESC 0
#define CSI_BRACKET 1
#define CSI_PARAM1 2
#define CSI_PARAM2 3
static struct kprintfbuf TTY = {
.color = DEFAULT_COLOR,
.cursor_row = 1,
.cursor_col = 1,
};
inline static u16
cursor_pos(struct kprintfbuf *b)
{
return TERMINAL_POS(b->cursor_row, b->cursor_col);
}
static void
cursor_move(i16 move_row, i16 move_col, struct kprintfbuf *b)
{
b->cursor_row += move_row;
if (b->cursor_row < 1)
b->cursor_row = 1;
if (b->cursor_row > TERMINAL_ROW)
b->cursor_row = TERMINAL_ROW;
b->cursor_col += move_col;
if (b->cursor_col < 1)
b->cursor_col = 1;
if (b->cursor_col > TERMINAL_COLUMN)
b->cursor_col = TERMINAL_COLUMN;
}
static void
cursor_move_pre(struct kprintfbuf *b)
{
if (b->cursor_col == 1) {
if (b->cursor_row > 1) {
b->cursor_row--;
b->cursor_col = TERMINAL_COLUMN;
}
} else {
b->cursor_col--;
}
}
static void
cursor_move_nxt(struct kprintfbuf *b)
{
if (b->cursor_col == TERMINAL_COLUMN) {
if (b->cursor_row < TERMINAL_ROW) {
b->cursor_row++;
b->cursor_col = 1;
}
} else {
b->cursor_col++;
}
}
#define CLEAR_CURSOR2END 0
#define CLEAR_CURSOR2BEGIN 1
#define CLEAR_ENTIRE 2
static void
clear_screen(struct kprintfbuf *b)
{
u16 content = DEFAULT_COLOR | ' ';
if (b->param1 == CLEAR_CURSOR2END) {
u16 disp_pos = cursor_pos(b);
while (disp_pos < TERMINAL_SIZE)
write_to_terminal(disp_pos++, content);
} else if (b->param1 == CLEAR_CURSOR2BEGIN) {
u16 disp_pos = cursor_pos(b);
while (disp_pos > 0)
write_to_terminal(disp_pos--, content);
write_to_terminal(disp_pos, content);
} else if (b->param1 == CLEAR_ENTIRE) {
u16 disp_pos = 0;
while (disp_pos < TERMINAL_SIZE)
write_to_terminal(disp_pos++, content);
}
}
static void
clear_line(struct kprintfbuf *b)
{
u16 content = DEFAULT_COLOR | ' ';
if (b->param1 == CLEAR_CURSOR2END) {
u16 disp_pos = cursor_pos(b);
while (disp_pos % TERMINAL_COLUMN != 0)
write_to_terminal(disp_pos++, content);
} else if (b->param1 == CLEAR_CURSOR2BEGIN) {
u16 disp_pos = cursor_pos(b);
while (disp_pos % TERMINAL_COLUMN != 0)
write_to_terminal(disp_pos--, content);
write_to_terminal(disp_pos, content);
} else if (b->param1 == CLEAR_ENTIRE) {
u16 disp_pos = TERMINAL_POS(b->cursor_row, 1);
do {
write_to_terminal(disp_pos++, content);
} while (disp_pos % TERMINAL_COLUMN != 0);
}
}
static void
scroll(i16 scroll_up_num)
{
scroll_up_num = MIN(scroll_up_num, +TERMINAL_ROW);
scroll_up_num = MAX(scroll_up_num, -TERMINAL_ROW);
if (scroll_up_num > 0) {
void *dst = TERMINAL_POS(1, 1) * 2;
dst = dst + TERMINAL_ADDR;
void *src = (void*)(TERMINAL_POS(1 + scroll_up_num, 1) * 2);
src = src + TERMINAL_ADDR;
size_t clear_size = scroll_up_num * TERMINAL_COLUMN * 2;
size_t copy_size = TERMINAL_SIZE * 2 - clear_size;
memcpy(dst, src, copy_size);
void *v = dst + copy_size;
memset(v, 0, clear_size);
} else if (scroll_up_num < 0) {
i16 scroll_down_num = -scroll_up_num;
void *dst = (void*)(TERMINAL_POS(1 + scroll_down_num, 1) * 2);
dst = dst + TERMINAL_ADDR;
void *src = TERMINAL_POS(1, 1) * 2;
src = src + TERMINAL_ADDR;
size_t clear_size = scroll_down_num * TERMINAL_COLUMN * 2;
size_t copy_size = TERMINAL_SIZE * 2 - clear_size;
memcpy(dst, src, copy_size);
void *v = src;
memset(v, 0, clear_size);
}
}
inline static void
param12vga_color(struct kprintfbuf *b)
{
u8 tmp = b->param1 & 1;
b->param1 &= 0b0110;
b->param1 |= b->param1 >> 2;
b->param1 &= 0b0011;
b->param1 |= tmp << 2;
}
static void
set_color(struct kprintfbuf *b)
{
if (b->param1 == 0) {
b->color = DEFAULT_COLOR;
} else if (b->param1 == 1) {
b->color |= 0x8800;
} else if (b->param1 == 2) {
b->color &= 0x7700;
} else if (30 <= b->param1 && b->param1 <= 37) {
b->param1 -= 30;
param12vga_color(b);
b->color = (b->color & 0xf8ff) | FOREGROUND(b->param1);
} else if (40 <= b->param1 && b->param1 <= 47) {
b->param1 -= 40;
param12vga_color(b);
b->color = (b->color & 0x8fff) | BACKGROUND(b->param1);
} else if (90 <= b->param1 && b->param1 <= 97) {
b->param1 -= 90;
param12vga_color(b);
b->param1 |= 0x8;
b->color = (b->color & 0xf0ff) | FOREGROUND(b->param1);
} else if (100 <= b->param1 && b->param1 <= 107) {
b->param1 -= 100;
param12vga_color(b);
b->param1 |= 0x8;
b->color = (b->color & 0x0fff) | BACKGROUND(b->param1);
} else {
warn("unsupport CSI: %dm", b->param1);
}
}
static void
CSI_handler(u8 terminator, struct kprintfbuf *b)
{
b->CSI = CSI_ESC;
switch (terminator) {
case 'A':
if (b->param1 == 0)
b->param1 = 1;
cursor_move(-b->param1, 0, b);
break;
case 'B':
if (b->param1 == 0)
b->param1 = 1;
cursor_move(+b->param1, 0, b);
break;
case 'C':
if (b->param1 == 0)
b->param1 = 1;
cursor_move(0, +b->param1, b);
break;
case 'D':
if (b->param1 == 0)
b->param1 = 1;
cursor_move(0, -b->param1, b);
break;
case 'E':
if (b->param1 == 0)
b->param1 = 1;
cursor_move(+b->param1, 1 - b->cursor_col, b);
break;
case 'F':
if (b->param1 == 0)
b->param1 = 1;
cursor_move(-b->param1, 1 - b->cursor_col, b);
break;
case 'H':
if (b->param1 == 0)
b->param1 = 1;
if (b->param2 == 0)
b->param2 = 1;
cursor_move(b->param1 - b->cursor_row,
b->param2 - b->cursor_col, b);
break;
case 'J':
clear_screen(b);
break;
case 'K':
clear_line(b);
break;
case 'S':
if (b->param1 == 0)
b->param1 = 1;
scroll(+b->param1);
break;
case 'T':
if (b->param1 == 0)
b->param1 = 1;
scroll(-b->param1);
break;
case 'm':
set_color(b);
break;
default:
warn("unsupport CSI: %c", terminator);
break;
}
}
static void
kprintfputch(int ch, struct kprintfbuf *b)
{
ch = ch & 0xff;
b->cnt++;
reswitch:
if (b->CSI == CSI_ESC) {
switch (ch) {
case '\b':
cursor_move_pre(b);
break;
case '\t':
if (cursor_pos(b) == TERMINAL_SIZE - 1)
break;
while (b->cursor_col % 4 != 1)
cursor_move_nxt(b);
break;
case '\n':
cursor_move(+1, 1 - b->cursor_col, b);
break;
case '\x1b':
b->CSI++;
break;
default:
{
u16 disp_pos = TERMINAL_POS(b->cursor_row, b->cursor_col);
u16 content = b->color | ch;
write_to_terminal(disp_pos, content);
cursor_move_nxt(b);
break;
}
}
} else if (b->CSI == CSI_BRACKET) {
switch (ch) {
case '[':
b->CSI++;
b->param1 = b->param2 = 0;
break;
default:
b->CSI = CSI_ESC;
break;
}
} else {
switch (ch) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (b->CSI == CSI_PARAM1)
b->param1 = b->param1 * 10 + ch - '0';
else if (b->CSI == CSI_PARAM2)
b->param2 = b->param2 * 10 + ch - '0';
else
;//do nothing
break;
case ';':
b->CSI++;
break;
default:
if (!(0x20 <= ch && ch <= 0x7e))
b->CSI = CSI_ESC;
if (0x40 <= ch && ch <= 0x7e)
CSI_handler(ch, b);
break;
}
}
}
int
vkprintf(const char *fmt, va_list ap)
{
int rc;
DISABLE_INT();
TTY.cnt = 0;
vprintfmt((void*)kprintfputch, &TTY, fmt, ap);
rc = TTY.cnt;
ENABLE_INT();
return rc;
}
/*
* %d,%s等格式化输出外
* ANSI转义序列中的CSI sequence("ESC(十六进制字符为\x1b)[")
* (en.wikipedia.org/wiki/ANSI_escape_code)
*
* \x1b[nA n(\x1b[A等价于\x1b[1A)
* \x1b[nB n(\x1b[B等价于\x1b[1B)
* \x1b[nC n(\x1b[C等价于\x1b[1C)
* \x1b[nD n(\x1b[D等价于\x1b[1D)
* \x1b[nE n(\x1b[E等价于\x1b[1E)
* \x1b[nF n(\x1b[F等价于\x1b[1F)
* \x1b[n;mH n行m列11nm为空时默认为1
* ";"
* \x1b[;5H等价于\x1b[1;5H
* \x1b[5H等价于\x1b[5;H等价于\x1b[5;1H
* \x1b[H等价于\x1b[1;1H
* \x1b[nJ (\x1b[J等价于\x1b[0J)
* n为0时
* n为1时
* n为2时
* \x1b[nK (\x1b[J等价于\x1b[0J)
* n为0时
* n为1时
* n为2时
* \x1b[nS (\x1b[S等价于\x1b[1S)
* \x1b[nT (\x1b[T等价于\x1b[1T)
* \x1b[nm (\x1b[m等价于\x1b[0m)
* n为0时
* n为1时
* n为2时
* n为30/40/
* n为31/41/
* n为32/42/绿
* n为33/43/
* n为34/44/
* n为35/45/
* n为36/46/
* n为37/47/
* n为90/100/
* n为91/101/
* n为92/102/绿
* n为93/103/
* n为94/104/
* n为95/105/
* n为96/106/
* n为97/107/
*/
int
kprintf(const char *fmt, ...)
{
va_list ap;
int rc;
va_start(ap, fmt);
rc = vkprintf(fmt, ap);
va_end(ap);
return rc;
}

267
lib/printfmt.c Normal file
View File

@ -0,0 +1,267 @@
#include <type.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
/*
* xv6中抄过来的printf
* %d,%c,%f,%s,%u,%p
*
*/
/*
* Print a number (base <= 16) in reverse order,
* using specified putch function and associated pointer putdat.
*/
static void
printnum(void (*putch)(int, void*), void *putdat,
unsigned long long num, unsigned base, int width, int padc)
{
// first recursively print all preceding (more significant) digits
if (num >= base) {
printnum(putch, putdat, num / base, base, width - 1, padc);
} else {
// print any needed pad characters before first digit
while (--width > 0)
putch(padc, putdat);
}
// then print this (the least significant) digit
putch("0123456789abcdef"[num % base], putdat);
}
// Get an unsigned int of various possible sizes from a varargs list,
// depending on the lflag parameter.
static unsigned long long
getuint(va_list *ap, int lflag)
{
if (lflag >= 2)
return va_arg(*ap, unsigned long long);
else if (lflag)
return va_arg(*ap, unsigned long);
else
return va_arg(*ap, unsigned int);
}
// Same as getuint but signed - can't use getuint
// because of sign extension
static long long
getint(va_list *ap, int lflag)
{
if (lflag >= 2)
return va_arg(*ap, long long);
else if (lflag)
return va_arg(*ap, long);
else
return va_arg(*ap, int);
}
// Main function to format and print a string.
void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...);
void
vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
{
register const char *p;
register int ch, err;
unsigned long long num;
int base, lflag, width, precision, altflag;
char padc;
while (1) {
while ((ch = *(unsigned char *) fmt++) != '%') {
if (ch == '\0')
return;
putch(ch, putdat);
}
// Process a %-escape sequence
padc = ' ';
width = -1;
precision = -1;
lflag = 0;
altflag = 0;
reswitch:
switch (ch = *(unsigned char *) fmt++) {
// flag to pad on the right
case '-':
padc = '-';
goto reswitch;
// flag to pad with 0's instead of spaces
case '0':
padc = '0';
goto reswitch;
// width field
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
for (precision = 0; ; ++fmt) {
precision = precision * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
goto process_precision;
case '*':
precision = va_arg(ap, int);
goto process_precision;
case '.':
if (width < 0)
width = 0;
goto reswitch;
case '#':
altflag = 1;
goto reswitch;
process_precision:
if (width < 0)
width = precision, precision = -1;
goto reswitch;
// long flag (doubled for long long)
case 'l':
lflag++;
goto reswitch;
// character
case 'c':
putch(va_arg(ap, int), putdat);
break;
// string
case 's':
if ((p = va_arg(ap, char *)) == NULL)
p = "(null)";
if (width > 0 && padc != '-')
for (width -= strnlen(p, precision); width > 0; width--)
putch(padc, putdat);
for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--)
if (altflag && (ch < ' ' || ch > '~'))
putch('?', putdat);
else
putch(ch, putdat);
for (; width > 0; width--)
putch(' ', putdat);
break;
// (signed) decimal
case 'd':
num = getint(&ap, lflag);
if ((long long) num < 0) {
putch('-', putdat);
num = -(long long) num;
}
base = 10;
goto number;
// unsigned decimal
case 'u':
num = getuint(&ap, lflag);
base = 10;
goto number;
// (unsigned) octal
case 'o':
// Replace this with your code.
putch('X', putdat);
putch('X', putdat);
putch('X', putdat);
break;
// pointer
case 'p':
putch('0', putdat);
putch('x', putdat);
num = (unsigned long long)
(uintptr_t) va_arg(ap, void *);
base = 16;
goto number;
// (unsigned) hexadecimal
case 'x':
num = getuint(&ap, lflag);
base = 16;
number:
printnum(putch, putdat, num, base, width, padc);
break;
// escaped '%' character
case '%':
putch(ch, putdat);
break;
// unrecognized escape sequence - just print it literally
default:
putch('%', putdat);
for (fmt--; fmt[-1] != '%'; fmt--)
/* do nothing */;
break;
}
}
}
void
printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintfmt(putch, putdat, fmt, ap);
va_end(ap);
}
struct sprintbuf {
char *buf;
char *ebuf;
int cnt;
};
static void
sprintputch(int ch, struct sprintbuf *b)
{
b->cnt++;
if (b->buf < b->ebuf)
*b->buf++ = ch;
}
int
vsnprintf(char *buf, int n, const char *fmt, va_list ap)
{
struct sprintbuf b = {buf, buf+n-1, 0};
if (buf == NULL || n < 1)
return -1;
// print the string to the buffer
vprintfmt((void*)sprintputch, &b, fmt, ap);
// null terminate the buffer
*b.buf = '\0';
return b.cnt;
}
int
snprintf(char *buf, int n, const char *fmt, ...)
{
va_list ap;
int rc;
va_start(ap, fmt);
rc = vsnprintf(buf, n, fmt, ap);
va_end(ap);
return rc;
}

110
lib/string.c Normal file
View File

@ -0,0 +1,110 @@
#include "string.h"
int
strlen(const char *s)
{
int n;
for (n = 0; *s != '\0'; s++)
n++;
return n;
}
int
strnlen(const char *s, size_t size)
{
int n;
for (n = 0; size > 0 && *s != '\0'; s++, size--)
n++;
return n;
}
char *
strcpy(char *dst, const char *src)
{
char *ret;
ret = dst;
while ((*dst++ = *src++) != '\0')
/* do nothing */;
return ret;
}
char *
strcat(char *dst, const char *src)
{
int len = strlen(dst);
strcpy(dst + len, src);
return dst;
}
char *
strncpy(char *dst, const char *src, size_t size) {
size_t i;
char *ret;
ret = dst;
for (i = 0; i < size; i++) {
*dst++ = *src;
// If strlen(src) < size, null-pad 'dst' out to 'size' chars
if (*src != '\0')
src++;
}
return ret;
}
int
strcmp(const char *p, const char *q)
{
while (*p && *p == *q)
p++, q++;
return (int) ((unsigned char) *p - (unsigned char) *q);
}
int
strncmp(const char *p, const char *q, size_t n)
{
while (n > 0 && *p && *p == *q)
n--, p++, q++;
if (n == 0)
return 0;
else
return (int) ((unsigned char) *p - (unsigned char) *q);
}
void *
memset(void *v, int c, size_t n)
{
char *p;
int m;
p = v;
m = n;
while (--m >= 0)
*p++ = c;
return v;
}
void *
memcpy(void *dst, const void *src, size_t n)
{
const char *s;
char *d;
s = src;
d = dst;
if (s < d && s + n > d) {
s += n;
d += n;
while (n-- > 0)
*--d = *--s;
} else {
while (n-- > 0)
*d++ = *s++;
}
return dst;
}

40
lib/user/assert.c Normal file
View File

@ -0,0 +1,40 @@
#include <assert.h>
#include <user/stdio.h>
/*
* 使
*/
void
_panic(const char *file, int line, const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
printf("\x1b[0m\x1b[91muser panic at %s:%d: ", file, line);
vprintf(fmt, ap);
printf("\n\x1b[0m");
va_end(ap);
fflush();
// 休眠CPU核直接罢工
while(1)
/* do nothing */;
}
/*
* panic
*/
void
_warn(const char *file, int line, const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
printf("\x1b[0m\x1b[93muser warning at %s:%d: ", file, line);
vprintf(fmt, ap);
printf("\n\x1b[0m");
va_end(ap);
fflush();
}

10
lib/user/astart.asm Normal file
View File

@ -0,0 +1,10 @@
[section .text]
extern main
extern exit
global _start
_start: ; 第一条指令
call main
push eax
call exit

BIN
lib/user/firework.bin Normal file

Binary file not shown.

115
lib/user/stdio.c Normal file
View File

@ -0,0 +1,115 @@
#include <x86.h>
#include <user/stdio.h>
#include <user/syscall.h>
#define PRINTFBUF_SIZE 4096
struct printfbuf {
u32 lock;
char buf[PRINTFBUF_SIZE];
char *buf_p;
int cnt;
};
static struct printfbuf printfb = {
.buf_p = printfb.buf
};
static void
printfputch(int ch, struct printfbuf *b)
{
b->cnt++;
*b->buf_p++ = (char)ch;
if (ch == '\n' || b->buf_p == b->buf + PRINTFBUF_SIZE) {
write(STDOUT, b->buf, b->buf_p - b->buf);
b->buf_p = b->buf;
}
}
int
vprintf(const char *fmt, va_list ap)
{
struct printfbuf *b = &printfb;
// 上个自旋锁,保证线程安全(不建议用中断)计组课上讲过,相信大家的记忆力
while (xchg(&b->lock, 1) == 1);
b->cnt = 0;
vprintfmt((void *)printfputch, b, fmt, ap);
int rc = b->cnt;
xchg(&b->lock, 0);
return rc;
}
int
printf(const char *fmt, ...)
{
va_list ap;
int rc;
va_start(ap, fmt);
rc = vprintf(fmt, ap);
va_end(ap);
return rc;
}
/*
* printf的缓冲区全写出去
*/
void
fflush(void)
{
struct printfbuf *b = &printfb;
while (xchg(&b->lock, 1) == 1);
write(STDOUT, b->buf, b->buf_p - b->buf);
b->buf_p = b->buf;
xchg(&b->lock, 0);
}
#define GETCHBUG_SIZE 1024
struct getchbuf {
u32 lock;
u8 buf[GETCHBUG_SIZE];
u8 *st, *en;
};
static struct getchbuf getchb = {
.st = getchb.buf,
.en = getchb.buf,
};
u8
getch(void)
{
struct getchbuf *b = &getchb;
// 上个自旋锁,保证线程安全(不建议用中断)计组课上讲过,相信大家的记忆力
while (xchg(&b->lock, 1) == 1);
if (b->st == b->en) {
b->st = b->en = b->buf;
b->en += read(STDIN, b->buf, sizeof(b->buf));
}
u8 rc = b->st == b->en ? -1 : *b->st++;
xchg(&b->lock, 0);
return rc;
}
u8
getchar(void)
{
u8 rc = -1;
while ((rc = getch()) == (u8)-1);
return rc;
}

134
lib/user/syscall.c Normal file
View File

@ -0,0 +1,134 @@
#include <user/syscall.h>
ssize_t
syscall0(size_t NR_syscall)
{
ssize_t ret;
asm volatile("int $0x80"
: "=a"(ret)
: "a" (NR_syscall)
: "cc", "memory");
return ret;
}
ssize_t
syscall1(size_t NR_syscall, size_t p1)
{
ssize_t ret;
asm volatile("int $0x80"
: "=a"(ret)
: "a" (NR_syscall),
"b" (p1)
: "cc", "memory");
return ret;
}
ssize_t
syscall2(size_t NR_syscall, size_t p1, size_t p2)
{
ssize_t ret;
asm volatile("int $0x80"
: "=a"(ret)
: "a" (NR_syscall),
"b" (p1),
"c" (p2)
: "cc", "memory");
return ret;
}
ssize_t
syscall3(size_t NR_syscall, size_t p1, size_t p2, size_t p3)
{
ssize_t ret;
asm volatile("int $0x80"
: "=a"(ret)
: "a" (NR_syscall),
"b" (p1),
"c" (p2),
"d" (p3)
: "cc", "memory");
return ret;
}
ssize_t
syscall4(size_t NR_syscall, size_t p1, size_t p2,
size_t p3, size_t p4)
{
ssize_t ret;
asm volatile("int $0x80"
: "=a"(ret)
: "a" (NR_syscall),
"b" (p1),
"c" (p2),
"d" (p3),
"S" (p4)
: "cc", "memory");
return ret;
}
ssize_t
syscall5(size_t NR_syscall, size_t p1, size_t p2,
size_t p3, size_t p4, size_t p5)
{
ssize_t ret;
asm volatile("int $0x80"
: "=a"(ret)
: "a" (NR_syscall),
"b" (p1),
"c" (p2),
"d" (p3),
"S" (p4),
"D" (p5)
: "cc", "memory");
return ret;
}
ssize_t
get_ticks(void)
{
return syscall0(_NR_get_ticks);
}
ssize_t
get_pid(void)
{
return syscall0(_NR_get_pid);
}
ssize_t
read(int fd, void *buf, size_t count)
{
return syscall3(_NR_read, fd, (size_t)buf, count);
}
ssize_t
write(int fd, const void *buf, size_t count)
{
return syscall3(_NR_write, fd, (size_t)buf, count);
}
ssize_t
exec(const char *pathname)
{
return syscall1(_NR_exec, (size_t)pathname);
}
ssize_t
fork(void)
{
return syscall0(_NR_fork);
}
ssize_t
exit(int status)
{
return syscall1(_NR_exit, status);
}
ssize_t
wait(int *wstatus)
{
return syscall1(_NR_wait, (size_t)wstatus);
}
ssize_t
fork_ack(void)
{
return syscall0(_NR_fork_ack);
}

86
mergedep.pl Normal file
View File

@ -0,0 +1,86 @@
#!/usr/bin/perl
# Copyright 2003 Bryan Ford
# Distributed under the GNU General Public License.
#
# Usage: mergedep <main-depfile> [<new-depfiles> ...]
#
# This script merges the contents of all <new-depfiles> specified
# on the command line into the single file <main-depfile>,
# which may or may not previously exist.
# Dependencies in the <new-depfiles> will override
# any existing dependencies for the same targets in <main-depfile>.
# The <new-depfiles> are deleted after <main-depfile> is updated.
#
# The <new-depfiles> are typically generated by GCC with the -MD option,
# and the <main-depfile> is typically included from a Makefile,
# as shown here for GNU 'make':
#
# .deps: $(wildcard *.d)
# perl mergedep $@ $^
# -include .deps
#
# This script properly handles multiple dependencies per <new-depfile>,
# including dependencies having no target,
# so it is compatible with GCC3's -MP option.
#
sub readdeps {
my $filename = shift;
open(DEPFILE, $filename) or return 0;
while (<DEPFILE>) {
if (/([^:]*):([^\\:]*)([\\]?)$/) {
my $target = $1;
my $deplines = $2;
my $slash = $3;
while ($slash ne '') {
$_ = <DEPFILE>;
defined($_) or die
"Unterminated dependency in $filename";
/(^[ \t][^\\]*)([\\]?)$/ or die
"Bad continuation line in $filename";
$deplines = "$deplines\\\n$1";
$slash = $2;
}
#print "DEPENDENCY [[$target]]: [[$deplines]]\n";
$dephash{$target} = $deplines;
} elsif (/^[#]?[ \t]*$/) {
# ignore blank lines and comments
} else {
die "Bad dependency line in $filename: $_";
}
}
close DEPFILE;
return 1;
}
if ($#ARGV < 0) {
print "Usage: mergedep <main-depfile> [<new-depfiles> ..]\n";
exit(1);
}
%dephash = ();
# Read the main dependency file
$maindeps = $ARGV[0];
readdeps($maindeps);
# Read and merge in the new dependency files
foreach $i (1 .. $#ARGV) {
readdeps($ARGV[$i]) or die "Can't open $ARGV[$i]";
}
# Update the main dependency file
open(DEPFILE, ">$maindeps.tmp") or die "Can't open output file $maindeps.tmp";
foreach $target (keys %dephash) {
print DEPFILE "$target:$dephash{$target}";
}
close DEPFILE;
rename("$maindeps.tmp", "$maindeps") or die "Can't overwrite $maindeps";
# Finally, delete the new dependency files
foreach $i (1 .. $#ARGV) {
unlink($ARGV[$i]) or print "Error removing $ARGV[$i]\n";
}

View File

@ -1 +0,0 @@
<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><architecture>i8086</architecture><xi:include href="i386-32bit.xml"/></target>

36
user/Makefrag Normal file
View File

@ -0,0 +1,36 @@
OBJDIRS += user
USER_LIBSRCS:= lib/printfmt.c \
lib/string.c \
lib/user/assert.c \
lib/user/astart.asm \
lib/user/stdio.c \
lib/user/syscall.c \
USER_LIBOBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(USER_LIBSRCS))
USER_LIBOBJS := $(patsubst %.asm, $(OBJDIR)/%.o, $(USER_LIBOBJS))
USER_SRCS := user/testfork.c \
user/testwait.c \
user/shell.c \
user/initproc.c \
user/forkbomb.c \
user/firework.bin \
USER_OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(USER_SRCS))
USER_BINS := $(patsubst %.bin, $(OBJDIR)/%.bin, $(USER_SRCS))
USER_BINS := $(patsubst %.c, $(OBJDIR)/%.bin, $(USER_BINS))
$(OBJDIR)/user/%.o: user/%.c $(OBJDIR)/.vars.CFLAGS
@echo + cc $<
@mkdir -p $(@D)
@$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR)/user/firework.bin: lib/user/firework.bin
@mkdir -p $(@D)
@cp $< $(@D)
$(OBJDIR)/user/%.bin: $(OBJDIR)/user/%.o $(USER_LIBOBJS) $(OBJDIR)/.vars.LDFLAGS
@echo + ld $@
@$(LD) $(LDFLAGS) -o $@ $< $(USER_LIBOBJS) $(GCC_LIB)

47
user/forkbomb.c Normal file
View File

@ -0,0 +1,47 @@
#include <user/stdio.h>
#include <user/syscall.h>
static size_t random_seed;
static size_t rand() { return (((random_seed = random_seed * 214013L + 2531011L) >> 16) & 0x7fff);}
void bomb_salve(void)
{
fork_ack();
random_seed = get_ticks() & 0x7fff;
for (int i = 0 ; i < get_pid() ; i++)
rand();
for (int i = 0 ; i < (rand() & 0xf) ; i++) {
int pid = fork();
if (pid == 0)
bomb_salve();
for (int i = 0 ; i < rand() ; i++)
; // do nothing
}
if (rand() % 2 == 1) {
while (wait(NULL) >= 0);
}
exit(0);
}
void bomb_master(void)
{
random_seed = get_ticks() & 0x7fff;
while (1) {
int pid = fork();
if (pid == 0)
bomb_salve();
for (int i = 0 ; i < rand() ; i++)
; // do nothing
while (wait(NULL) >= 0);
}
}
int main()
{
bomb_master();
}

22
user/initproc.c Normal file
View File

@ -0,0 +1,22 @@
#include <assert.h>
#include <user/stdio.h>
#include <user/syscall.h>
int main()
{
ssize_t pid = fork();
if (pid == 0) {
int ret = exec("shell.bin");
panic("exec failed! errno: %d", ret);
}
if (pid < 0)
panic("init process fork failed errno: %d", pid);
int wstatus;
while (1) {
pid = wait(&wstatus);
}
}

52
user/shell.c Normal file
View File

@ -0,0 +1,52 @@
#include <assert.h>
#include <string.h>
#include <user/stdio.h>
#include <user/wait.h>
#include <user/syscall.h>
int main() {
int childStatus = 0;
while (1) {
printf("\x1b[32mMINIOS\x1b[0m$ ");
fflush();
int cntInput = 0;
char input[512] = {0};
while (1) {
char c = getchar();
printf("%c",c);
fflush();
if (c != '\n') {
input[cntInput++] = c;
} else {
input[cntInput++] = '\0';
break;
}
}
if (strcmp(input, "?") == 0) {
printf("%d\n",WEXITSTATUS(childStatus));
continue;
}
int ForkRetVal = fork();
if (ForkRetVal < 0) {
printf("fork failed!\n");
continue;
}
if (ForkRetVal == 0) {
exec(input);
printf("exec failed!\n");
exit(1);
}
int wait_pid;
wait_pid = wait(&childStatus);
if ((wait_pid = wait(&childStatus)) >= 0) {
// do nothing
}
}
return 0;
}

22
user/testfork.c Normal file
View File

@ -0,0 +1,22 @@
#include <user/stdio.h>
#include <user/syscall.h>
int main()
{
int pid = fork();
if (pid) {
while (1) {
printf("\x1b[93mI'm fa, son pid = %d\x1b[0m", pid);
fflush();
for (int i = 0 ; i < (int)1e8 ; i++)
;//do nothing
}
} else {
while (1) {
printf("I'm son");
fflush();
for (int i = 0 ; i < (int)1e8 ; i++)
;//do nothing
}
}
}

115
user/testwait.c Normal file
View File

@ -0,0 +1,115 @@
#include <assert.h>
#include <errno.h>
#include <user/stdio.h>
#include <user/syscall.h>
#include <user/wait.h>
void test_fork_wait1(void)
{
ssize_t pid = fork();
assert(pid >= 0);
if (pid == 0)
exit(114);
int wstatus;
assert(pid == wait(&wstatus));
assert(WEXITSTATUS(wstatus) == 114);
printf("\x1b[92mtest_fork_wait1 passed!\x1b[0m\n");
}
void test_fork_wait2(void)
{
ssize_t pid = fork();
assert(pid >= 0);
if (pid == 0)
exit(514);
assert(pid == wait(NULL));
printf("\x1b[92mtest_fork_wait2 passed!\x1b[0m\n");
}
void test_empty_wait(void)
{
assert(wait(NULL) == -ECHILD);
printf("\x1b[92mtest_empty_wait passed!\x1b[0m\n");
}
void test_fork_limit(void)
{
int xor_sum = 0;
for (int i = 1 ; i <= 17 ; i++) {
ssize_t pid = fork();
assert(pid >= 0);
if (pid == 0)
exit(0);
xor_sum ^= pid;
}
assert(fork() == -EAGAIN);
int wait_cnt = 0, wait_pid;
while ((wait_pid = wait(NULL)) >= 0)
wait_cnt++, xor_sum ^= wait_pid;
assert(wait_cnt == 17);
assert(xor_sum == 0);
printf("\x1b[92mtest_fork_limit passed!\x1b[0m\n");
}
void test_wait_is_sleeping(void)
{
ssize_t rt_pid = get_pid();
for (int i = 1 ; i <= 17 ; i++) {
ssize_t pid = fork();
assert(pid >= 0);
if (pid > 0) {
int wstatus;
assert(pid == wait(&wstatus));
assert(WEXITSTATUS(wstatus) == 42);
if (get_pid() != rt_pid)
exit(42);
break;
}
}
if (get_pid() != rt_pid) {
ssize_t la_ticks = get_ticks();
for (int i = 1 ; i <= (int)5e6 ; i++) {
ssize_t now_ticks = get_ticks();
assert(now_ticks - la_ticks <= 2);
la_ticks = now_ticks;
}
exit(42);
}
printf("\x1b[92mtest_wait_is_sleeping passed!\x1b[0m\n");
}
int main()
{
test_fork_wait1();
test_fork_wait2();
test_empty_wait();
test_fork_limit();
test_wait_is_sleeping();
printf("\x1b[92mall tests passed!\x1b[0m\n");
}

270
实验六-v0.1.md Normal file
View File

@ -0,0 +1,270 @@
<div align='center'>
<font size='6'>实验六 fork和进程树</font>
</div>
<div align='center'>
<font size='4'>谷建华</font>
</div>
<div align='center'>
<font size='4'>2022-11-14 v0.1</font>
</div>
#### 实验目的
1. 学习fork进程复制的原理和实现细节.
2. 学习进程树以理解进程间父子关系.
3. 学习简单的并发控制,内存资源管理.
#### 实验预习内容
1. fork、wait、exit系统调用基本语义和相互依赖关系.
2. 进程树结构.
3. 并发控制,理解互斥锁,关中断对内核执行流的影响.
#### 实验内容
1. 编写fork系统调用.
(1) 编写fork系统调用,使调用者能够复制进程.
(2) 在fork基本语义的实现的同时,需要考虑一种边界情况,当内核pcb资源满了这次实验内核pcb资源只有20个无法再分配时如何处理这个需要你自行阅读手册`man 2 fork`这个2什么意思可以看手册的手册`man man`,它会告诉你答案)了解怎么处理这个细节.
(3) exec系统调用已经由我们实现好了,修改`main.c`中的`do_exec`函数调用参数变为`"testfork.bin"`,测试fork是否如预期复制了进程,复制出的两个进程并发着运行.
2. 编写wait系统调用.
(1) 编写wait系统调用,使调用者在调用时会遍历它的所有子进程,如果有僵尸子进程则将其回收,将子进程状态返回,否则该进程变成睡眠状态直到有子进程exit将其唤醒.
(2) 你在写wait基本语义的同时,需要考虑一种边界情况,当调用者进程没有子进程时如何处理?这个需要你自行阅读手册`man 2 wait`了解怎么处理这个细节.
(3) exit系统调用已经由我们实现好了,修改`main.c`中的`do_exec`函数调用参数变为`"initproc.bin"`,这是OS的初始进程,它会fork出一个新进程并exec成`"shell.bin"`执行shell程序后永远不停地执行wait系统,shell大家做课内实验的时候已经实现了,现在你需要以内核的视角实现以支持shell的系统调用.
(4) `testwait.bin`是一个用于检测你实现的fork、wait的功能正确性的用户程序,你可以通过shell调用它,如果不爆panic则你实现的功能全通过.
3. fork炸弹测试
(1) 如果光实现功能,那还算简单,但是实际上操作系统很大的难点不在于实现**出**功能,而是实现**对**功能,能够经受系统中运行的各种复杂环境的考验,并发控制和资源管理对一个操作系统尤为重要.
(2) `forkbomb.bin`是一个用于测试fork、wait和exit的程序,里面会不断随机调用fork、wait和exit系统调用达成fork炸弹的目的,你需要成功运行fork系统调用20000两万内核pcb的数量**必须**为20个,内核中会有一段专门的程序检测fork的次数.测试通过的效果图如下:
![](lab6-finish.png)
(3) (自我提高内容,自己想做就做,不用写进报告,**禁止内卷**)上面的两万次还算小打小闹,检验内核程序基本的并发控制和资源管理,而这次需要你成功运行fork系统调用2000000两百万次,预计耗时若干小时,这基本上能将你实现的系统调用的并发问题都暴露出来,虽然关中断很有效,但是希望你在做这个实验的时候能尽量少关,尽可能将你的并发问题暴露出来,如果你通过了这个测试就能看到我们准备的小彩蛋.
#### 实验总结
1. 进程树算是相当经典的描述进程关系的框架,但是如果不允许用进程树这个框架,意味着没有父子进程关系,你会怎么去设计进程的创建与销毁?(开放性答案,不要觉得自己做法低级,放心大胆往上写,任何言之有理的答案都行)
2. 在这次实验的时候你遇到了什么离谱的并发bug可以写没有,如果没有,那么恭喜你,你是一个严谨的人,你考虑问题非常周全)
#### 实验指导
##### fork-状态机模型
经过前几个实验的实践,我们从一个内核的视角看向用户程序可以逐渐隐约感觉到用户程序是一个状态机.具体什么意思,可以看回pcb中存储的用户寄存器.
```c
struct user_context {
u32 gs;
u32 fs;
u32 es;
u32 ds;
u32 edi;
u32 esi;
u32 ebp;
u32 kernel_esp;
u32 ebx;
u32 edx;
u32 ecx;
u32 eax;
u32 retaddr;
u32 eip;
u32 cs;
u32 eflags;
u32 esp;
u32 ss;
};
```
`cs,ds,es,fs,gs,ss,eax,ebx,ecx,edx,esi,edi,ebp,esi,eip,eflags`这些用户寄存器就可以描述用户当前的运行状态,再结合用户程序的页表pcb中的cr3变量,也就是用户程序能够访问的内存.这些对于一个用户程序来说就是它能够**直接**控制的所有信息.其余信息需要通过系统调用向内核申请服务获悉.
用户程序在执行的时候pc执行的汇编命令可以先简单抽象为三种形式
1. 修改寄存器
2. 修改内存
3. 系统调用等其余指令
也就是说如果不考虑第三种形式(下面都暂时不考虑),仅考虑用户程序由前两种形式的汇编指令组成,给用户进程指定的用户寄存器信息和内存,这个用户进程的执行过程可以**唯一确定**.
举个简单的例子,假设一个人要涂一面墙(内存),人手里有把刷子(寄存器),人可以进行下面两个操作之一(汇编指令):
1. 将刷子的颜色调成指定颜色
2. 往墙上某个区域用刷子涂上颜色,墙上原有的部分的颜色会被覆盖
假设给人固定好初始的刷子颜色、墙面颜色(颜色可能五花八门)和操作序列,那么每一次操作后墙面颜色和刷子颜色是可以**唯一确定**的.可以通过数学归纳法证明经过若干次操作后墙面颜色和刷子颜色我们也是可以**唯一确定**的.
所以对于一个用户进程,可以将它的寄存器信息和内存信息抽象成用户程序的<u>状态</u>,而汇编指令就是一个映射方法,它将用户程序从一个<u>状态</u>映射到另一个<u>状态</u>.只要给定初始寄存器信息和内存信息,并确定执行流(汇编指令序列),那么执行流中任意一环的程序<u>状态</u>我们都是可以**唯一确定**的,这就是**状态机模型**.
接下来回到第三种形式的汇编指令`系统调用等其余指令`,第三种形式指令本质上也是对用户寄存器和内存修改的指令,系统调用根据pcb中的信息反馈给用户,由于pcb对用户是不可见的,所以在用户视角下这个指令是不可预测的,意味着在用户视角下这条指令会发生什么是不可唯一确定的.而当我们将视角从用户移到内核,甚至硬件(某些指令是跟硬件交互的),当我们知道的信息越多,用户指令执行后的状态总是可以**唯一确定**的.
##### fork-状态机复制
在讲完状态机模型后fork就比较好理解了,我们根据状态机模型可知,如果给定确定的寄存器信息和内存信息,用户程序的执行状态是可以唯一确定的(假设没有系统调用这种对用户程序来说**不可唯一确定**的指令).
fork本质上就是对**状态机的复制**,当用户进程fork的时候,内核会新创建一个pcb,将当前进程的所有状态机信息原封不动地复制一份,也就是将当前进程的用户寄存器和内存原封不动复制一份给新的进程.
在复制完状态机信息后产生了两个进程,这两个进程的内存信息和寄存器信息**完全一样**,所以之后的执行流,用户程序状态机也会**完全一致**(直到第一次遇到系统调用这种指令),这样就通过一个进程fork出两个进程.
##### fork-进程树
fork能够一模一样复制一份进程的状态机,但是实际上这终究是两个独立的进程,调用fork系统调用的进程被称为父进程,在fork中新创建的进程称为子进程.这样除了初始进程pid号为0,每个进程都有它的父进程,从数据结构的视角下进程之间的父子关系能够构成一个以0号进程为根的进程树.
这样进程每次执行fork后,该进程在进程树上会新增一个儿子节点,这个儿子节点指向的进程是fork新被创建的进程.
##### fork-父子进程返回值
这属于是大家在做上课就已经需要搞明白的东西,为了能够区分fork完后进程是父进程还是子进程,从它的返回值判断,如果返回值为0,则执行流获悉它是子进程,否则返回值为子进程的pid号,执行流获悉它是父进程.
##### exit
上面提到的进程树不只是用于描述进程之间的关系,与之关系密切的是exit和wait系统调用,相信大家在课内实验写简易shell的时候用到了fork,exec,wait这三个系统调用,然后还有一个系统调用exit被隐式调用了,被隐藏在了main函数中的返回值中.
##### exit-用户程序执行的终点
我们先随便编写一个`hello_world`程序,然后通过`strace`命令查看执行所用到的系统调用.
```
$ ls
hello_world hello_world.c
$ cat ./hello_world.c
#include <stdio.h>
int main()
{
puts("hello world!");
return 114;
}
$ strace ./hello_world
... # 太长了就不展示上面的了
write(1, "hello world!\n", 13hello world!
) = 13
exit_group(114) = ?
+++ exited with 114 +++
```
可以看到用户程序在执行完exitexit_group系统调用与线程相关,但这次实验没有线程的概念,所以可以简单理解为exit后用户程序就停止运行了,而main中的返回值114也写入到exit系统调用的参数中了.
##### exit-程序的第一条指令和最后一条指令
在我们初学编程的时候肯定会有一个潜意识就是程序的第一条指令在main函数,这对初学者来说肯定是这样的,也很符合我们的直觉,觉得第一条指令在main函数.
不过这只是编译器给我们的错觉,实际上在进入main函数之前和结束main函数跳出之后程序实际上还是做了很多事的,在进入main函数之前会初始化很多东西,在结束了main函数跳出之后程序需要将很多在缓冲区的数据及时推送到内核,最后调用exit系统调用结束程序的执行.
在MINIOS中,用户程序的开始并不在main函数,由于`ld`工具默认`_start`是elf文件运行的程序入口,所以在`lib/user/astart.asm`存放着真正的用户程序入口,就是简单的调用了main,等main函数退出后eax寄存器存放着的是main函数的返回值,将eax作为参数调用exit函数结束程序的运行.虽然这个`_start`函数非常简单,但是还是能够作为一个例子证明main函数并不是程序运行的入口.
```nasm
[section .text]
extern main
extern exit
global _start
_start: ; 第一条指令
call main
push eax
call exit
```
##### exit-具体语义
通过实例可以发现exit系统调用实际上是一个进程的终止,通过翻阅linux手册`man 2 _exit`这个2什么意思可以看手册的手册`man man`,它会告诉你答案).里面有三段话能够帮助我们理解exit系统调用的具体语义.
```
The function _exit() terminates the calling process "immediately". Any
open file descriptors belonging to the process are closed. Any chil
dren of the process are inherited by init(1) (or by the nearest "sub
reaper" process as defined through the use of the prctl(2)
PR_SET_CHILD_SUBREAPER operation). The process's parent is sent a
SIGCHLD signal.
The value status & 0xFF is returned to the parent process as the
process's exit status, and can be collected using one of the wait(2)
family of calls.
These functions do not return.
```
从这段话中可以知道实现exit系统调用需要注意的事情
+ exit没有返回值,执行完后进程结束它的生命周期.
+ 如果有子进程还未被回收,这些子进程全部会被转移到初始进程下成为初始进程的子进程.(否则这个进程退出后这些子进程的父进程就不见了,这个肯定是不允许的,而初始进程肯定是活着的,交给它刚刚好)
+ 父进程会收到一个SIGCHLD信号信号这个实验不谈,可以简单理解为唤醒父进程).
+ exit中的参数会取其低8位这是很多博客不会讲到的,只有查手册才能搞明白具体的语义)作为返回参数传递给父进程.
+ 父进程需要调用wait这类的系统调用获取该进程的exit参数.
通过这些信息可以提炼出exit这个系统调用需要做的事:
+ 变成僵尸进程,状态置为ZOMBIE进程的生命周期已经结束了,它已经是个将死之"人"了,但是没有被父进程回收.所以就成了僵尸,不能运行但是就占着一部分资源).
+ 将所有子进程的父进程变成初始进程(学术名词叫托孤,还是挺有诗意的).
+ 处理exit参数.
+ 将部分资源回收(某些资源回收需要父进程完成).
+ 唤醒父进程.
##### wait
理解wait最重要的是需要先理解exit,wait这个系统调用的语义就如它的英文语义一样,就一个字:“等”.这个系统调用实际上就是在等待任意子进程exit,如果有子进程状态是ZOMBIE就回收子进程资源返回子进程状态.如果当前没有子进程可以回收,就进入SLEEP状态等待子进程exit唤醒该进程.
如果你顺着上面的指导和exit的语义思考可以得出对wait系统调用的实现的一个模糊的映像,可以知道它大概需要做什么,但是可能一些实现细节不是很清楚.不过在这次不能一步步全部引导你什么地方要做什么,这次需要你自己去查手册`man 2 wait`去仔细阅读理解wait这个系统调用的具体语义设计出一个符合语义的系统调用程序,要学会查手册,不是全部读一遍跟背诵默写一样,而是挑你需要的看,大概需要看50~60行英文.
##### 并发
指导的最后一个部分交给并发部分,并发这玩意在上课的前几节讲概念的时候你们会感觉还好,并发就是一段时间内同时做几件事.但是真正遇到并发问题的时候你们就不会这么想了,并发能够带来非常多的问题,可以说是很多是让你摸不着头脑的问题.
因为MINIOS内核态允许中断,所以很多时候在执行内核程序时会发生调度,这会导致多个内核进程在一个时间段同时执行的并发场景,这会导致一些非常诡异的并发问题.
讲一个实验设计中的例子,还记得实验五中`phy_malloc_4k`这个函数吗?在设计初期为了防止多个内核进程同时访问这个函数上了一个锁保证分配不会因为并发问题打架:
```c
phyaddr_t
phy_malloc_4k(void)
{
assert(phy_malloc_4k_p < 128 * MB);
while(xchg(&phy_malloc_4k_lock, 1) == 1)
schedule();
phyaddr_t addr = phy_malloc_4k_p;
phy_malloc_4k_p += PGSIZE;
free:
xchg(&phy_malloc_4k_lock, 0);
return addr;
}
```
但是在测试的时候发生了一件诡异的事,这个函数分配了一个128MB~128MB+4KB的物理页面出去然后内核在用的时候触发了page fault.在设计的时候挠了半天头,想不明白怎么能分配这么一个离谱的页面.
在仔细想了想之后搞明白了出问题的原因,第一个进程在争取到锁之后在`phy_malloc_4k_p += PGSIZE;`这句执行之前触发了调度,然后第二个进程在调用这个函数的时候成功躲过了assert语句的判断.调度回第一个进程把`phy_malloc_4k_p`更新,相当于第二个进程在执行assert语句的时候拿的是变量的旧值,没有拿新值判断,这个时候`phy_malloc_4k_p`的值可能是128MB,所以导致能够分配128MB~128MB+4KB这么一个离谱的物理页面.
所以正确的做法是将这个assert函数移到锁的临界区内,保证在临界区内值的判断是正确的.
```c
phyaddr_t
phy_malloc_4k(void)
{
while(xchg(&phy_malloc_4k_lock, 1) == 1)
schedule();
assert(phy_malloc_4k_p < 128 * MB);
phyaddr_t addr = phy_malloc_4k_p;
phy_malloc_4k_p += PGSIZE;
free:
xchg(&phy_malloc_4k_lock, 0);
return addr;
}
```
##### 并发-一把大锁保平安
在并发面前,很多事情非常难以预料,我们人脑计算能力是非常渺小的,无法考虑那么全面.能够做的只能一把大锁保平安,简单来说,如果一个执行流需要访问/修改某一公共资源时,就需要给公共资源加上一个锁使用`xchg`原子指令进行上互斥锁进入临界区,防止公共资源同时被其他执行流访问/修改的情况.
##### 并发-多把大锁保平安?
在实际实践的时候,执行流可能需要访问多个公共资源,比如exit就需要访问三个pcb的锁,fork和wait需要访问两个pcb的锁,这就意味我们可能要上多把锁.
如果上一把互斥锁那还好处理,执行流要么进入临界区要么没进去,如果上多把锁就又涉及到上锁这个行为本身的并发问题,考虑一个情况:
两个执行流,第一个要先给a上锁再给b上锁,第二个要先给b上锁再给a上锁,然后一个可能的情况就是第一个执行流给a上完锁之后马上发生了调度,然后调度到第二个执行流,第二个执行流给b上完锁了发现a被上锁了于是就在等,然后调度回第一个执行流,第一个执行流发现b被上了锁,于是也在等.于是这两个执行流等到天荒地老也没等到对面释放锁.
对于这个问题的解决有一种办法,就是协商好上锁顺序,如果两个执行流都按照ab或ba的上锁顺序就能保证至少一个执行流能够运作,当一个执行流执行完释放完锁后另一个执行流再上锁.
##### 并发-实验六的上锁顺序
对于此次实验来说,需要上多把锁的场景有fork,exit和wait,这些场景都需要对进程pcb上锁,所以这里需要协商规范一下上锁顺序.
上锁规范是以进程树的0号进程为根,上锁顺序从根到叶子,就是说对于一个进程如果它,它祖宗,它儿子都需要被上锁,那么上锁顺序必须为祖宗->该节点->儿子.这种上锁方式能够保证上锁的节点在进程树中是深度递增的,无论什么情况都能保证至少一个执行流能够正常运行,不过这里暂且不证明.