Compare commits
No commits in common. "master" and "ch1" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
@ -1 +1,2 @@
|
||||
obj
|
||||
*.bin
|
||||
*.img
|
||||
|
||||
14
.vscode/c_cpp_properties.json
vendored
14
.vscode/c_cpp_properties.json
vendored
@ -1,14 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/inc"
|
||||
],
|
||||
"defines": [],
|
||||
"cStandard": "gnu99",
|
||||
"compilerArgs": ["-m32"]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
22
.vscode/launch.json
vendored
22
.vscode/launch.json
vendored
@ -1,22 +0,0 @@
|
||||
{
|
||||
"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
11
.vscode/settings.json
vendored
@ -1,11 +0,0 @@
|
||||
{
|
||||
"editor.detectIndentation": false,
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": false,
|
||||
"files.associations": {
|
||||
"*.h": "c",
|
||||
"*.c": "c",
|
||||
"*.asm": "nasm",
|
||||
"Makefrag": "makefile"
|
||||
}
|
||||
}
|
||||
BIN
2020301918-李懋良-试点班第1个报告.docx
Normal file
BIN
2020301918-李懋良-试点班第1个报告.docx
Normal file
Binary file not shown.
Binary file not shown.
143
Makefile
143
Makefile
@ -1,136 +1,23 @@
|
||||
#
|
||||
# make的主文件
|
||||
#
|
||||
CC = gcc
|
||||
ASM = nasm
|
||||
QEMU = qemu
|
||||
IMG = boot.bin
|
||||
|
||||
# 文件夹
|
||||
# OBJ用于存放编译出来的可重定位文件
|
||||
OBJDIR := obj
|
||||
# INC用于存放各种头文件(*.h)
|
||||
INCDIR := inc
|
||||
boot.bin: boot.asm
|
||||
$(ASM) $< -o $@
|
||||
|
||||
# 编译以及日常工具
|
||||
CC := gcc
|
||||
# 汇编器
|
||||
AS := nasm
|
||||
# 静态库编辑器
|
||||
AR := ar
|
||||
# 链接器
|
||||
LD := ld
|
||||
# 复制文件
|
||||
OBJCOPY := objcopy
|
||||
# 反编译
|
||||
OBJDUMP := objdump
|
||||
# 查询可重定位文件符号表
|
||||
NM := nm
|
||||
compile: boot.bin
|
||||
|
||||
DEFS :=
|
||||
.DEFAULT_GOAL=compile
|
||||
|
||||
# gcc的相关命令参数
|
||||
# $(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)
|
||||
run: compile
|
||||
$(QEMU) -boot order=c -drive file=$(IMG),format=raw
|
||||
|
||||
clean:
|
||||
@rm -rf $(OBJDIR)
|
||||
rm boot.bin
|
||||
|
||||
run: $(IMAGE)
|
||||
@qemu-system-i386 \
|
||||
-boot order=a \
|
||||
-drive file=$(IMAGE),format=raw \
|
||||
debug: compile
|
||||
$(QEMU) -boot order=c -drive file=$(IMG),format=raw -S -s &
|
||||
gdb -ex 'set tdesc filename target.xml' -ex 'target remote localhost:1234'
|
||||
|
||||
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
|
||||
.PHONY: compile clean run debug
|
||||
31
boot.asm
Normal file
31
boot.asm
Normal file
@ -0,0 +1,31 @@
|
||||
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 ; 结束标志
|
||||
@ -1,28 +0,0 @@
|
||||
#
|
||||
# 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
215
boot/boot.asm
@ -1,215 +0,0 @@
|
||||
;==============================================================================================================================
|
||||
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
324
boot/inc/pm.inc
@ -1,324 +0,0 @@
|
||||
|
||||
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
; 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 字节
|
||||
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -1,30 +0,0 @@
|
||||
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
240
boot/loader.asm
@ -1,240 +0,0 @@
|
||||
|
||||
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
; 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 $ ; 栈顶
|
||||
@ -1,228 +0,0 @@
|
||||
#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数据(2字节)写到终端第disp_pos个字符
|
||||
* 第0个字符在0行0列,第1个字符在0行1列,第80个字符在1行0列,以此类推
|
||||
* 用的是内联汇编,等价于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
|
||||
}
|
||||
192
i386-32bit.xml
Normal file
192
i386-32bit.xml
Normal file
@ -0,0 +1,192 @@
|
||||
<?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
16
inc/assert.h
@ -1,16 +0,0 @@
|
||||
#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
67
inc/elf.h
@ -1,67 +0,0 @@
|
||||
#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
139
inc/errno.h
@ -1,139 +0,0 @@
|
||||
#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
55
inc/fat32.h
@ -1,55 +0,0 @@
|
||||
#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 */
|
||||
@ -1,8 +0,0 @@
|
||||
#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 */
|
||||
@ -1,8 +0,0 @@
|
||||
#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 */
|
||||
@ -1,9 +0,0 @@
|
||||
#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 */
|
||||
@ -1,11 +0,0 @@
|
||||
#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 */
|
||||
@ -1,9 +0,0 @@
|
||||
#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 */
|
||||
@ -1,323 +0,0 @@
|
||||
#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 */
|
||||
@ -1,13 +0,0 @@
|
||||
#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 */
|
||||
@ -1,12 +0,0 @@
|
||||
#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 */
|
||||
@ -1,109 +0,0 @@
|
||||
#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;
|
||||
|
||||
/*
|
||||
* 记录进程所申请的所有物理页面,用单向链表维护
|
||||
* 对于非物理页面(页表,页目录表cr3)的那些页面,其laddr被置为-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 */
|
||||
@ -1,181 +0,0 @@
|
||||
#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 */
|
||||
@ -1,6 +0,0 @@
|
||||
#ifndef MINIOS_KERN_SCHE_H
|
||||
#define MINIOS_KERN_SCHE_H
|
||||
|
||||
void schedule(void);
|
||||
|
||||
#endif
|
||||
@ -1,10 +0,0 @@
|
||||
#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 */
|
||||
@ -1,35 +0,0 @@
|
||||
#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 */
|
||||
@ -1,9 +0,0 @@
|
||||
#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
109
inc/kern/trap.h
@ -1,109 +0,0 @@
|
||||
#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 */
|
||||
@ -1,8 +0,0 @@
|
||||
#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
124
inc/mmu.h
@ -1,124 +0,0 @@
|
||||
#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
12
inc/stdarg.h
@ -1,12 +0,0 @@
|
||||
#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
17
inc/stdio.h
@ -1,17 +0,0 @@
|
||||
#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
17
inc/string.h
@ -1,17 +0,0 @@
|
||||
#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 */
|
||||
@ -1,15 +0,0 @@
|
||||
#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 */
|
||||
@ -1,43 +0,0 @@
|
||||
#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
63
inc/type.h
@ -1,63 +0,0 @@
|
||||
#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 */
|
||||
@ -1,18 +0,0 @@
|
||||
#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 */
|
||||
@ -1,24 +0,0 @@
|
||||
#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
|
||||
@ -1,14 +0,0 @@
|
||||
#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
211
inc/x86.h
@ -1,211 +0,0 @@
|
||||
#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 */
|
||||
@ -1,53 +0,0 @@
|
||||
#
|
||||
# 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)
|
||||
@ -1,82 +0,0 @@
|
||||
; 此时内存看上去是这样的(更详细的内存情况在 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 (0~4G) ┃ 8h = cs
|
||||
; ┣━━━━━━━━━━━━━━━━━━┫
|
||||
; ┃ DESC_FLAT_RW (0~4G) ┃ 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
|
||||
@ -1,59 +0,0 @@
|
||||
[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
311
kern/atrap.asm
@ -1,311 +0,0 @@
|
||||
[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
150
kern/exec.c
@ -1,150 +0,0 @@
|
||||
#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
117
kern/exit.c
@ -1,117 +0,0 @@
|
||||
#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
217
kern/fork.c
@ -1,217 +0,0 @@
|
||||
#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
182
kern/fs.c
@ -1,182 +0,0 @@
|
||||
#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为0,平时的读入流也默认是0的
|
||||
* 现在只有从键盘缓冲区获取字符的能力
|
||||
*/
|
||||
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为1,平时的输出流也默认是1的
|
||||
* 现在只有输出字符到终端的能力
|
||||
*/
|
||||
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
56
kern/main.c
@ -1,56 +0,0 @@
|
||||
#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
154
kern/pmap.c
@ -1,154 +0,0 @@
|
||||
#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处的虚拟页面映射到物理地址为paddr(如果paddr为-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);
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
#include <x86.h>
|
||||
|
||||
#include <kern/process.h>
|
||||
#include <kern/sche.h>
|
||||
#include <kern/syscall.h>
|
||||
|
||||
/*
|
||||
* 这个函数是写给汇编函数用,
|
||||
* 在汇编函数调用save函数保存完用户寄存器上下文后,
|
||||
* 需要切到进程内核栈中,minios的进程栈是与pcb是绑定的。
|
||||
* 相当于每个进程分配了一个大空间,前面一小部分给了pcb,
|
||||
* 后面的全可以给进程栈使用,目前进程占8KB,进程栈占7KB多。
|
||||
*
|
||||
* 这个函数的目的就是切换到进程栈处,由于每个进程的空间只有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
48
kern/sche.c
@ -1,48 +0,0 @@
|
||||
#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
237
kern/start.c
@ -1,237 +0,0 @@
|
||||
#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");
|
||||
}
|
||||
|
||||
/*
|
||||
* 很像panic,但是不会休眠CPU核,就是正常打印信息
|
||||
*/
|
||||
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
147
kern/syscall.c
@ -1,147 +0,0 @@
|
||||
#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)
|
||||
* 等待子进程exit,回收子进程资源,wstatus接收子进程的返回码
|
||||
*/
|
||||
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
30
kern/time.c
@ -1,30 +0,0 @@
|
||||
#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
186
kern/trap.c
@ -1,186 +0,0 @@
|
||||
#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
101
kern/wait.c
@ -1,101 +0,0 @@
|
||||
#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
BIN
lab6-finish.png
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
31
lib/Makefrag
31
lib/Makefrag
@ -1,31 +0,0 @@
|
||||
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 $@ $<
|
||||
Binary file not shown.
@ -1,71 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@ -1,428 +0,0 @@
|
||||
#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列(1行1列为终端左上角),n、m为空时默认为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
267
lib/printfmt.c
@ -1,267 +0,0 @@
|
||||
#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
110
lib/string.c
@ -1,110 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
[section .text]
|
||||
|
||||
extern main
|
||||
extern exit
|
||||
global _start
|
||||
|
||||
_start: ; 第一条指令
|
||||
call main
|
||||
push eax
|
||||
call exit
|
||||
Binary file not shown.
115
lib/user/stdio.c
115
lib/user/stdio.c
@ -1,115 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@ -1,134 +0,0 @@
|
||||
#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
86
mergedep.pl
@ -1,86 +0,0 @@
|
||||
#!/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";
|
||||
}
|
||||
|
||||
1
target.xml
Normal file
1
target.xml
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><architecture>i8086</architecture><xi:include href="i386-32bit.xml"/></target>
|
||||
@ -1,36 +0,0 @@
|
||||
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)
|
||||
@ -1,47 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
#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
52
user/shell.c
@ -1,52 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
#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
115
user/testwait.c
@ -1,115 +0,0 @@
|
||||
#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
270
实验六-v0.1.md
@ -1,270 +0,0 @@
|
||||
<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的次数.测试通过的效果图如下:
|
||||

|
||||
(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 +++
|
||||
```
|
||||
|
||||
可以看到用户程序在执行完exit(exit_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号进程为根,上锁顺序从根到叶子,就是说对于一个进程如果它,它祖宗,它儿子都需要被上锁,那么上锁顺序必须为祖宗->该节点->儿子.这种上锁方式能够保证上锁的节点在进程树中是深度递增的,无论什么情况都能保证至少一个执行流能够正常运行,不过这里暂且不证明.
|
||||
Loading…
Reference in New Issue
Block a user