├── .gitignore ├── Makefile ├── README.md ├── boot ├── bootsect.asm ├── floppy.asm └── gdt.asm ├── include ├── asm.h ├── debug.h ├── fault.h ├── gdt.h ├── idt.h ├── ipc.h ├── irq.h ├── isr.h ├── pmm.h ├── print.h ├── proc.h ├── string.h ├── syscall.h ├── sysproc.h ├── type.h ├── uvm.h ├── vga.h └── vmm.h ├── screenshot └── ipc.png ├── script ├── gdbinit ├── link.ld └── ulink.ld ├── src └── kernel │ ├── asm.c │ ├── debug.c │ ├── fault.c │ ├── gdt.c │ ├── idt.c │ ├── init.asm │ ├── ipc.c │ ├── irq.c │ ├── isr.c │ ├── loader.asm │ ├── main.c │ ├── pmm.c │ ├── print.c │ ├── proc.c │ ├── string.c │ ├── syscall.c │ ├── sysproc.c │ ├── uvm.c │ ├── vga.c │ └── vmm.c └── usr └── logo.txt /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | lst 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # makefile 2 | 3 | .PHONY: init run fs fsck clean 4 | .IGNORE: init 5 | 6 | MAKE = make -r 7 | AS = nasm 8 | CC = gcc 9 | DEL = rm -f 10 | QEMU = qemu 11 | LD = ld 12 | OBJCPY = objcopy 13 | GDB = cgdb 14 | IMG = qemu-img 15 | MKFS = mkfs.minix 16 | FSCK = fsck.minix 17 | CFLAGS = -c -O0 -Wall -Werror -nostdinc -fno-builtin -fno-stack-protector -funsigned-char \ 18 | -finline-functions -finline-small-functions -findirect-inlining \ 19 | -finline-functions-called-once -Iinclude -m32 -ggdb -gstabs+ -fdump-rtl-expand 20 | ROOTFS = bin/rootfs 21 | OBJS = bin/loader.o bin/main.o bin/asm.o bin/vga.o bin/string.o bin/print.o bin/debug.o \ 22 | bin/gdt.o bin/idt.o bin/isr.o bin/irq.o bin/fault.o \ 23 | bin/pmm.o bin/vmm.o bin/uvm.o bin/init.o bin/proc.o bin/syscall.o bin/sysproc.o bin/ipc.o 24 | 25 | # default task 26 | default: Makefile 27 | $(MAKE) bin/floppy.img 28 | 29 | # create a 1.44MB floppy include kernel and bootsector 30 | bin/floppy.img: boot/floppy.asm bin/bootsect.bin bin/kernel 31 | $(AS) -I ./bin/ -f bin -l lst/floppy.s $< -o $@ 32 | 33 | # bootsector 34 | bin/bootsect.bin: boot/bootsect.asm 35 | $(AS) -I ./boot/ -f bin -l lst/bootsect.s $< -o $@ 36 | 37 | bin/loader.o : src/kernel/loader.asm 38 | $(AS) -I ./boot/ -f elf32 -g -F stabs -l lst/loader.s $< -o $@ 39 | 40 | bin/init.o: src/kernel/init.asm 41 | $(AS) -I ./boot/ -f elf32 -g -F stabs -l lst/init.s $< -o $@ 42 | 43 | # link loader.o and c objfile 44 | # generate a symbol file(kernel.elf) and a flat binary kernel file(kernel) 45 | bin/kernel: script/link.ld $(OBJS) 46 | $(LD) -T$< -melf_i386 -static -o $@.elf $(OBJS) -M>lst/map.map 47 | $(OBJCPY) -O binary $@.elf $@ 48 | 49 | # compile c file in all directory 50 | bin/%.o: src/*/%.c 51 | $(CC) $(CFLAGS) -c $^ -o $@ 52 | 53 | #---------------------------------------- 54 | 55 | # init 56 | init: 57 | mkdir lst 58 | mkdir bin 59 | mkdir $(ROOTFS) 60 | 61 | # make a disk with minix v1 file system 62 | fs: 63 | $(DEL) bin/rootfs.img 64 | $(IMG) create -f raw bin/rootfs.img 10M 65 | $(MKFS) bin/rootfs.img -1 -n14 66 | sudo mount -o loop -t minix bin/rootfs.img $(ROOTFS) 67 | mkdir $(ROOTFS)/bin 68 | mkdir $(ROOTFS)/share 69 | sleep 1 70 | sudo umount $(ROOTFS) 71 | 72 | # check root file system 73 | fsck: 74 | $(FSCK) -fsl bin/rootfs.img 75 | 76 | # run with qemu 77 | run: 78 | $(QEMU) -S -s \ 79 | -drive file=bin/floppy.img,if=floppy,format=raw \ 80 | -drive file=bin/rootfs.img,if=ide,format=raw,cyls=18,heads=2,secs=80 \ 81 | -boot a -m 64 & 82 | sleep 1 83 | $(GDB) -x script/gdbinit 84 | 85 | # clean the binary file 86 | clean: 87 | $(DEL) bin/*.lst 88 | $(DEL) bin/*.o 89 | $(DEL) bin/*.bin 90 | $(DEL) bin/*.tmp 91 | $(DEL) bin/kernel 92 | $(DEL) bin/kernel.elf 93 | $(DEL) bin/floppy.img 94 | $(DEL) lst/* 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiniOS 2 | =============================== 3 | Simple x86-based OS. 4 | 5 | 6 | ## 开发阶段 7 | 8 | 1. **[0.2.0]** 从零开始,保留核心文件,慢慢添加功能,实现Hello world 9 | 2. **[0.2.1]** 添加GDT和IDT 10 | 3. **[0.2.2]** 添加ISR和IRQ 11 | 4. **[0.2.3]** 添加PMM 12 | 5. **[0.2.4]** 添加VMM 13 | 6. **[0.2.5]** 添加多进程机制,完善注释 14 | 7. **[0.2.6]** 实现进程间通信IPC 15 | 16 | ## 运行截图 17 | 18 | ### 进程间通信 19 | 20 | ![ipc](https://github.com/bajdcc/MiniOS/raw/master/screenshot/ipc.png) 21 | 22 | ## 构建工具 23 | **平台:Ubuntu 16.04.2 x86** 24 | * make 25 | * nasm 26 | * gcc 27 | * binutils 28 | * cgdb 29 | * qemu 30 | 31 | ```bash 32 | sudo apt install make nasm gcc binutils cgdb qemu 33 | sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu 34 | ``` 35 | 36 | ## 编译并运行 37 | ```shell 38 | make init # only for first time 39 | make fs # build root file system and user routines, root privilege required 40 | make # build kernel 41 | make run # run with qemu 42 | ``` 43 | 44 | 45 | ## References 46 | * [OS67](https://github.com/SilverRainZ/OS67) 47 | * [osdev](http://wiki.osdev.org/) 48 | * Orange'S:一个操作系统的实现 - 于渊 -------------------------------------------------------------------------------- /boot/bootsect.asm: -------------------------------------------------------------------------------- 1 | ; 引导扇区 FAT12 2 | 3 | %INCLUDE "gdt.asm" ; 段描述表定义 4 | 5 | [BITS 16] 6 | org 0x7c00 ; 加载地址偏移 参考http://blog.csdn.net/u011542994/article/details/46707815 7 | 8 | BS_jmpBoot jmp entry 9 | db 0x90 10 | BS_OEMName db "CCOSV587" ; OEM name / 8 B 11 | BPB_BytsPerSec dw 512 ; 一个扇区512字节 12 | BPB_SecPerClus db 1 ; 每个簇一个扇区 13 | BPB_RsvdSecCnt dw 1 ; 保留扇区数, 必须为1 14 | BPB_NumFATs db 2 ; FAT表份数 15 | BPB_RootEntCnt dw 224 ; 根目录项数 16 | BPB_TotSec16 dw 2880 ; RolSec16, 总扇区数 17 | BPB_Media db 0xf0 ; 介质种类: 移动介质 18 | BPB_FATSz16 dw 9 ; FATSz16 分区表占用扇区数 19 | BPB_SecPerTrk dw 18 ; SecPerTrk, 磁盘 20 | BPB_NumHeads dw 2 ; 磁头数 21 | BPB_HiddSec dd 0 ; HiddSec 22 | BPB_TotSec32 dd 2880 ; 卡容量 23 | BS_DrvNum db 0 ; DvcNum 24 | BS_Reserved1 db 0 ; NT保留 25 | BS_BootSig db 0x29 ; BootSig扩展引导标记 26 | BS_VolD dd 0xffffffff ; VolID 27 | BS_VolLab db "FLOPPYCDDS " ; 卷标 28 | BS_FileSysType db "FAT12 " ; FilesysType 29 | 30 | times 18 db 0 31 | 32 | _print16: 33 | loop: 34 | lodsb ; ds:si -> al 35 | or al,al 36 | jz done 37 | mov ah,0x0e 38 | mov bx,15 39 | int 0x10 ; 打印字符 40 | jmp loop 41 | done: 42 | ret 43 | 44 | ;============================================================ 45 | ; 入口 46 | entry: 47 | mov ax,0 48 | mov ss,ax 49 | mov sp,0x7c00 50 | mov ds,ax 51 | mov es,ax ; bios interrupt expects ds 52 | 53 | ; shift to text mode, 16 color 80*25 54 | ; 参考自http://blog.csdn.net/hua19880705/article/details/8125706 55 | ; http://www.cnblogs.com/magic-cube/archive/2011/10/19/2217676.html 56 | mov ah,0x0 57 | mov al,0x03 ; 设置模式 16色 80x25矩阵 58 | int 0x10 ; 设置颜色 59 | 60 | mov si, msg_boot 61 | call _print16 62 | 63 | ;============================================================ 64 | ; 获取内存布局 65 | ; http://blog.csdn.net/yes_life/article/details/6839453 66 | 67 | ARD_ENTRY equ 0x500 68 | ARD_COUNT equ 0x400 69 | 70 | get_mem_map: 71 | mov dword [ARD_COUNT], 0 72 | mov ax, 0 73 | mov es, ax 74 | xor ebx, ebx ; If this is the first call, EBX must contain zero. 75 | mov di, ARD_ENTRY ; ES:DI-> Buffer Pointer 76 | 77 | get_mem_map_loop: 78 | mov eax, 0xe820 ; code 79 | mov ecx, 20 ; Buffer Size, min = 20 80 | mov edx, 0x534D4150 ; Signature, edx = "SMAP" 81 | int 0x15 82 | jc get_mem_map_fail ; Non-Carry - indicates no error 83 | add di, 20 ; Buffer Pointer += 20(sizeof structure) 84 | inc dword [ARD_COUNT] ; Inc ADR count 85 | cmp ebx, 0 ; Anything else? 86 | jne get_mem_map_loop 87 | jmp get_mem_map_ok 88 | 89 | get_mem_map_fail: 90 | mov si, msg_get_mem_map_err 91 | call _print16 92 | hlt 93 | 94 | get_mem_map_ok: 95 | 96 | ;============================================================ 97 | ; 从软盘中读取内核代码 98 | ; 参考http://blog.chinaunix.net/uid-20496675-id-1664077.html 99 | ; http://chuanwang66.iteye.com/blog/1678952 100 | 101 | ; 读磁盘时,将读到的扇区放到[es:bx]开始的内存中 102 | ; 写磁盘时,将[es:bx]开始的一个扇区写到磁盘上 103 | ; 这两处,[es:bx]都称为 数据缓冲区 104 | 105 | ; read 20 sector (360 KB) form floppy 106 | loadloader: 107 | mov bx,0 108 | mov ax,0x0800 109 | mov es,ax ; [es:bx] buffer address point -> 0x8000 将读取数据存放至0x8000 110 | mov cl,2 ; 扇区 Sector 111 | mov ch,0 ; 磁道 Track 112 | mov dh,0 ; 盘面 Cylinder 113 | mov dl,0 ; 驱动器号 driver a: 114 | ; kernel locates after bootloader, which is the second sector 115 | 116 | readloop: 117 | mov si,0 ; 错误计数 err counter 118 | 119 | retry: 120 | mov ah,0x02 ; int 0x13 ah = 0x02 read sector form dirve 121 | mov al,1 ; read 1 sector 122 | int 0x13 ; 读取磁道1 123 | jnc next ; 没有错误则继续读取 124 | add si,1 125 | cmp si,5 ; 累计错误出现5次就报错 126 | jae error 127 | mov ah,0 128 | mov dl,0 ; driver a 129 | int 0x13 ; 复位 reset 130 | jmp next 131 | 132 | next: 133 | mov ax,es 134 | add ax,0x20 ; 一个扇区是512B=0x200,es是段,故再除以16,得到0x20 135 | mov es,ax 136 | 137 | add cl,1 ; 读下一个扇区 sector + 1 138 | cmp cl,18 ; 18 sector 如果读满了所有18个扇区,就 139 | jbe readloop 140 | 141 | mov cl,1 142 | add dh,1 ; 盘面 + 1 143 | cmp dh,1 144 | jbe readloop 145 | 146 | mov dh,0 147 | add ch,1 ; 磁道 + 1 148 | cmp ch,20 ; 只读取20个磁道共360KB 149 | jbe readloop 150 | jmp succ 151 | 152 | error: 153 | mov si,msg_err ; 报错 154 | call _print16 155 | jmp $ ; halt 156 | 157 | succ: 158 | mov si,msg_succ ; 读取成功 159 | call _print16 160 | 161 | ; fill and load GDTR 读取全局描述符表寄存器 162 | ; 参考http://x86.renejeschke.de/html/file_module_x86_id_156.html 163 | xor eax,eax 164 | mov ax,ds 165 | shl eax,4 166 | add eax,GDT ; eax <- gdt base 167 | mov dword [GdtPtr+2],eax ; [GdtPtr + 2] <- gdt base 168 | 169 | lgdt [GdtPtr] 170 | cli 171 | 172 | ; turn on A20 line 173 | ; 参考 http://blog.csdn.net/yunsongice/article/details/6110648 174 | in al,0x92 175 | or al,00000010b 176 | out 0x92,al 177 | 178 | ; 切换到保护模式 shift to protect mode 179 | mov eax,cr0 180 | or eax,1 181 | mov cr0,eax 182 | 183 | ; special, clear pipe-line and jump 184 | ; 前面读取软盘数据到0x8000处,现在跳转至0x8000 185 | jmp dword Selec_Code32_R0:0x8000 ; 自动切换段寄存器 186 | 187 | msg_boot: 188 | db "[Bootsector] loading...",13,10,0 ; 13 10(0x0D 0x0A)是'\r \n' 189 | msg_err: 190 | db "[Bootsector] error",13,10,0 191 | msg_succ: 192 | db "[Bootsector] ok",13,10,0 193 | msg_temp: 194 | db 0,0,0 195 | msg_get_mem_map_err: 196 | db "[Bootsector] failed",0 197 | 198 | GDT: ; 全局描述符表 199 | DESC_NULL: Descriptor 0, 0, 0 ; null 200 | DESC_CODE32_R0: Descriptor 0, 0xfffff - 1, DA_C+DA_32+DA_LIMIT_4K ; uncomfirm 201 | DESC_DATA_R0: Descriptor 0, 0xfffff - 1, DA_DRW+DA_32+DA_LIMIT_4K ; uncomfirm ; 4G seg 202 | DESC_VIDEO_R0: Descriptor 0xb8000, 0xffff, DA_DRW+DA_32 ; vram 203 | 204 | GdtLen equ $ - GDT ; GDT len 205 | GdtPtr dw GdtLen - 1 ; GDT limit 206 | dd 0 ; GDT Base 207 | 208 | ; GDT Selector 209 | Selec_Code32_R0 equ DESC_CODE32_R0 - DESC_NULL 210 | Selec_Data_R0 equ DESC_DATA_R0 - DESC_NULL 211 | Selec_Video_R0 equ DESC_VIDEO_R0 - DESC_NULL 212 | 213 | times 510 - ($-$$) db 0 ; 填充零 214 | db 0x55, 0xaa 215 | -------------------------------------------------------------------------------- /boot/floppy.asm: -------------------------------------------------------------------------------- 1 | ; 软盘 - 引导扇区 2 | INCBIN "bootsect.bin" ; 引导区代码 3 | INCBIN "../bin/kernel" ; 内核代码 4 | 5 | ; 其他空间填充零 6 | ; 软盘大小:80(磁道)x 18(扇区)x 512 bytes(扇区的大小) x 2(双面) 7 | ; = 1440 x 1024 bytes = 1440 KB = 1.44MB 8 | times 80*18*2*512 - ($-$$) db 0 ; 80 (Track / sectors) * 18 (Sector / head) * 2 Cylinder * 512 byte 9 | -------------------------------------------------------------------------------- /boot/gdt.asm: -------------------------------------------------------------------------------- 1 | ; 参考自 2 | 3 | ; 描述符类型 4 | DA_32 EQU 0x4000 ; 32 位段 5 | DA_LIMIT_4K EQU 0x8000 ; 段界限粒度为 4K 字节 6 | 7 | DA_DPL0 EQU 0x00 ; DPL = 0 8 | DA_DPL1 EQU 0x20 ; DPL = 1 9 | DA_DPL2 EQU 0x40 ; DPL = 2 10 | DA_DPL3 EQU 0x60 ; DPL = 3 11 | 12 | ; 存储段描述符类型 13 | DA_DR EQU 0x90 ; 存在的只读数据段类型值 14 | DA_DRW EQU 0x92 ; 存在的可读写数据段属性值 15 | DA_DRWA EQU 0x93 ; 存在的已访问可读写数据段类型值 16 | DA_C equ 0x98 ; 存在的只执行代码段属性值 17 | DA_CR equ 0x9a ; 存在的可执行可读代码段属性值 18 | DA_CCO equ 0x9c ; 存在的只执行一致代码段属性值 19 | DA_CCOR equ 0x9e ; 存在的可执行可读一致代码段属性值 20 | 21 | ; 系统段描述符类型 22 | DA_LDT EQU 0x82 ; 局部描述符表段类型值 23 | DA_TaskGate EQU 0x85 ; 任务门类型值 24 | DA_386TSS EQU 0x89 ; 可用 386 任务状态段类型值 25 | DA_386CGATE EQU 0x8c ; 386 调用门类型值 26 | DA_386IGATE EQU 0x8e ; 386 中断门类型值 27 | DA_386TGATE EQU 0x8f ; 386 陷阱门类型值 28 | 29 | 30 | ; Selector Attribute 31 | SA_RPL0 EQU 0 ; ┓ 32 | SA_RPL1 EQU 1 ; ┣ RPL(Request Privilege Level) 33 | SA_RPL2 EQU 2 ; ┃ 34 | SA_RPL3 EQU 3 ; ┛ 35 | 36 | SA_TIG EQU 0 ; ┓TI(Table Indicator) 37 | SA_TIL EQU 4 ; ┛ 38 | 39 | ; constant for Pageing 40 | PG_P EQU 1 ; 页存在属性位 41 | PG_RWR EQU 0 ; R/W 属性位值, 读/执行 42 | PG_RWW EQU 2 ; R/W 属性位值, 读/写/执行 43 | PG_USS EQU 0 ; U/S 属性位值, 系统级 44 | PG_USU EQU 4 ; U/S 属性位值, 用户级 45 | 46 | ; usage: Descriptor Base, Limit, Attr 47 | ; Base: dd 48 | ; Limit: dd (low 20 bits available) 49 | ; Attr: dw (lower 4 bits of higher byte are always 0) 50 | %macro Descriptor 3 51 | DW %2 & 0xffff ; 段界限1 52 | DW %1 & 0xffff ; 段基址1 53 | DB (%1 >> 16) & 0xff ; 段基址2 54 | DW ((%2 >> 8) & 0xf00) | (%3 & 0xf0ff) ; 属性1 + 段界限2 + 属性2 55 | DB (%1 >> 24) & 0xff ; 段基址3 56 | %endmacro ; 共 8 字节 57 | 58 | 59 | ; usage: Gate Selector, Offset, DCount, Attr 60 | ; Selector: dw 61 | ; Offset: dd 62 | ; DCount: db 63 | ; Attr: db 64 | %macro Gate 4 65 | DW (%2 & 0xffff) ; Offset1 偏移1 66 | DW %1 ; Selector 选择子 67 | DW (%3 & 0x1f) | ((%4 << 8) & 0xff00) ; Attr 属性 68 | DW ((%2 >> 16) & 0xffff) ; Offset2 偏移2 69 | %endmacro ; 共 8 字节 70 | 71 | ; usage segment reg, selector, gdt entry 72 | %macro FillDesc 3 73 | xor eax, eax 74 | mov ax, %1 75 | shl eax, 4 76 | add eax, %2 77 | mov word [%3 + 2], ax 78 | shr eax, 16 79 | mov byte [%3 + 4], al 80 | mov byte [%3 + 7], ah 81 | %endmacro 82 | -------------------------------------------------------------------------------- /include/asm.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASM_H 2 | #define __ASM_H 3 | 4 | #include 5 | 6 | uint8_t inb(uint16_t port); 7 | void outb(uint16_t port, uint8_t data); 8 | 9 | uint16_t inw(uint16_t port); 10 | void outw(uint16_t port, uint16_t data); 11 | 12 | void insl(uint32_t port, void *addr, uint32_t cnt); 13 | void outsl(uint32_t port, const void *addr, uint32_t cnt); 14 | 15 | void cli(); 16 | void sti(); 17 | void hlt(); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEBUG_H 2 | #define __DEBUG_H 3 | 4 | void panic(const char *msg, int ring); 5 | 6 | #define ASSERT 7 | #ifdef ASSERT 8 | void assertion_failure(char *exp, char *file, char *base_file, int line); 9 | #define assert(exp) if (exp) ; \ 10 | else assertion_failure(#exp, __FILE__, __BASE_FILE__, __LINE__) 11 | #else 12 | #define assert(exp) 13 | #endif 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/fault.h: -------------------------------------------------------------------------------- 1 | #ifndef __FAULT_H 2 | #define __FAULT_H 3 | 4 | // 异常处理 5 | 6 | #include 7 | 8 | void fault_init(); 9 | void fault_handler(struct interrupt_frame *r); 10 | 11 | #endif -------------------------------------------------------------------------------- /include/gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef __GDT_H 2 | #define __GDT_H 3 | 4 | #include 5 | 6 | /* 参考: http://wiki.osdev.org/GDT */ 7 | 8 | // 全局描述符表结构 http://www.cnblogs.com/hicjiajia/archive/2012/05/25/2518684.html 9 | // base: 基址 10 | // limit: 寻址最大范围 tells the maximum addressable unit 11 | // flags: 标志位 见上面的AC_AC等 12 | struct gdt_entry { 13 | uint16_t limit_low; 14 | uint16_t base_low; 15 | uint8_t base_middle; 16 | uint8_t access; 17 | unsigned limit_high: 4; 18 | unsigned flags: 4; 19 | uint8_t base_high; 20 | } __attribute__((packed)); 21 | 22 | // GDTR 23 | struct gdt_ptr { 24 | uint16_t limit; 25 | uint32_t base; 26 | } __attribute__((packed)); 27 | 28 | #define NGDT 256 // 全局描述符表大小 Global Descriptor Table 29 | 30 | #define AC_AC 0x1 // 可访问 access 31 | #define AC_RW 0x2 // [代码]可读;[数据]可写 readable for code selector & writeable for data selector 32 | #define AC_DC 0x4 // 方向位 direction 33 | #define AC_EX 0x8 // 可执行 executable, code segment 34 | #define AC_RE 0x10 // 保留位 reserve 35 | #define AC_PR 0x80 // 有效位 persent in memory 36 | 37 | // 特权位: 01100000b 38 | #define AC_DPL_KERN 0x00 // RING 0 kernel level 39 | #define AC_DPL_SYST 0x20 // RING 1 systask level 40 | #define AC_DPL_USER 0x60 // RING 3 user level 41 | 42 | #define GDT_GR 0x8 // 页面粒度 page granularity, limit in 4k blocks 43 | #define GDT_SZ 0x4 // 大小位 size bt, 32 bit protect mode 44 | 45 | // gdt selector 选择子 46 | #define SEL_KCODE 0x1 // 内核代码段 47 | #define SEL_KDATA 0x2 // 内核数据段 48 | #define SEL_UCODE 0x3 // 用户代码段 49 | #define SEL_UDATA 0x4 // 用户数据段 50 | #define SEL_SCODE 0x5 // 用户代码段 51 | #define SEL_SDATA 0x6 // 用户数据段 52 | #define SEL_TSS 0x7 // 任务状态段 task state segment http://wiki.osdev.org/TSS 53 | 54 | // RPL 请求特权等级 request privilege level 55 | #define RPL_KERN 0x0 56 | #define RPL_SYST 0x1 57 | #define RPL_USER 0x3 58 | 59 | // CPL 当前特权等级 current privilege level 60 | #define CPL_KERN 0x0 61 | #define CPL_SYST 0x1 62 | #define CPL_USER 0x3 63 | 64 | // 初始化GDT 65 | void gdt_init(); 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /include/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef __IDT_H 2 | #define __IDT_H 3 | 4 | #include 5 | #include 6 | 7 | /* 参考: http://wiki.osdev.org/IDT */ 8 | 9 | // 中断描述符表结构 http://blog.csdn.net/fwqcuc/article/details/5855460 10 | // base: 存储中断处理函数/中断向量的地址 11 | // selector: 中断向量选择子,其选择子的DPL必须是零(即最高级ring0) http://blog.csdn.net/better0332/article/details/3416749 12 | // gate: reference http://wiki.osdev.org/IDT 13 | // 0b0101 0x5 5 80386 32 bit task gate 14 | // 0b0110 0x6 6 80286 16-bit interrupt gate 15 | // 0b0111 0x7 7 80286 16-bit trap gate 16 | // 0b1110 0xE 14 80386 32-bit interrupt gate 17 | // 0b1111 0xF 15 80386 32-bit trap gate 18 | // flags: 19 | // Storage Segment(0) Set to 0 for interrupt gates (see above). 20 | // DPL(1-2) Gate call protection. 0-3的特权等级 21 | // P(3) Set to 0 for unused interrupts. 22 | struct idt_entry { 23 | uint16_t base_low; 24 | uint16_t selector; 25 | uint8_t always0; // must be 0 26 | unsigned gate_type: 4; // gate type 27 | unsigned flags: 4; // S(0) DPL(1-2) P(3) 28 | uint16_t base_high; 29 | } __attribute__((packed)); 30 | 31 | // IDTR 32 | struct idt_ptr { 33 | uint16_t limit; 34 | uint32_t base; 35 | } __attribute__((packed)); 36 | 37 | // 任务状态段 task state segment http://wiki.osdev.org/TSS 38 | // The only interesting fields are SS0 and ESP0. 39 | // SS0 gets the kernel datasegment descriptor (e.g. 0x10 if the third entry in your GDT describes your kernel's data) 40 | // ESP0 gets the value the stack-pointer shall get at a system call 41 | // IOPB may get the value sizeof(TSS) (which is 104) if you don't plan to use this io-bitmap further (according to mystran in http://forum.osdev.org/viewtopic.php?t=13678) 42 | 43 | // http://blog.csdn.net/huybin_wang/article/details/2161886 44 | // TSS的使用是为了解决调用门中特权级变换时堆栈发生的变化 45 | 46 | // http://www.kancloud.cn/wizardforcel/intel-80386-ref-manual/123838 47 | /* 48 | TSS 状态段由两部分组成: 49 | 1、 动态部分(处理器在每次任务切换时会设置这些字段值) 50 | 通用寄存器(EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI) 51 | 段寄存器(ES,CS,SS,DS,FS,GS) 52 | 状态寄存器(EFLAGS) 53 | 指令指针(EIP) 54 | 前一个执行的任务的TSS段的选择子(只有当要返回时才更新) 55 | 2、 静态字段(处理器读取,但从不更改) 56 | 任务的LDT选择子 57 | 页目录基址寄存器(PDBR)(当启用分页时,只读) 58 | 内层堆栈指针,特权级0-2 59 | T-位,指示了处理器在任务切换时是否引发一个调试异常 60 | I/O 位图基址 61 | */ 62 | struct tss_entry { 63 | uint32_t link; 64 | uint32_t esp0; 65 | uint32_t ss0; 66 | uint32_t esp1; 67 | uint32_t ss1; 68 | uint32_t esp2; 69 | uint32_t ss2; 70 | uint32_t cr3; 71 | uint32_t eip; 72 | uint32_t eflags; 73 | uint32_t eax; 74 | uint32_t ecx; 75 | uint32_t edx; 76 | uint32_t ebx; 77 | uint32_t esp; 78 | uint32_t ebp; 79 | uint32_t esi; 80 | uint32_t edi; 81 | uint32_t es; 82 | uint32_t cs; 83 | uint32_t ss; 84 | uint32_t ds; 85 | uint32_t fs; 86 | uint32_t gs; 87 | uint32_t ldtr; 88 | uint16_t padding1; 89 | uint16_t iopb_off; 90 | } __attribute__ ((packed)); 91 | 92 | #define NIDT 256 // 中断描述符表大小 Interrupt Descriptor Table 93 | 94 | /* 386 32-bit gata type */ 95 | #define GATE_TASK 0x5 96 | #define GATE_INT 0xe 97 | #define GATE_TRAP 0xf 98 | 99 | #define IDT_SS 0x1 // 存储段 store segment 100 | #define IDT_DPL_KERN 0x0 // 内核特权级 descriptor privilege level 101 | #define IDT_DPL_SYST 0x2 // 系统服务特权级 descriptor privilege level 102 | #define IDT_DPL_USER 0x6 // 用户特权级 descriptor privilege level 103 | #define IDT_PR 0x8 // 有效位 present in memory 104 | 105 | // 初始化中断向量表 106 | void idt_init(); 107 | 108 | // 设置中断向量 109 | void idt_install(uint8_t num, uint32_t base, uint16_t selector, uint8_t gate, uint8_t flags); 110 | 111 | // 设置任务状态段 112 | void tss_install(); 113 | 114 | // 设置TSS为当前进程堆栈 115 | extern void tss_reset(); 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /include/ipc.h: -------------------------------------------------------------------------------- 1 | #ifndef __IPC_H 2 | #define __IPC_H 3 | 4 | #include 5 | 6 | #define TASK_TTY 0 7 | #define TASK_SYS 1 8 | #define TASK_WINCH 2 9 | #define TASK_FS 3 10 | #define TASK_MM 4 11 | 12 | #define NTASK 16 13 | 14 | #define TASK_SYS 1 15 | 16 | #define TASK_ANY -1 17 | #define TASK_NONE -2 18 | 19 | // IPC消息模式 20 | #define SEND 1 21 | #define RECEIVE 2 22 | #define BOTH 3 /* BOTH = (SEND | RECEIVE) */ 23 | 24 | #define SENDING 0x02 /* set when proc trying to send */ 25 | #define RECEIVING 0x04 /* set when proc trying to recv */ 26 | #define INTERRUPT -10 27 | 28 | // IPC消息种类 29 | enum msg_type { 30 | /* 31 | * 当收到中断时,发送硬中断消息 32 | */ 33 | HARD_INT = 1, 34 | 35 | SYS_TICKS, 36 | }; 37 | 38 | #define RETVAL u.m3.m3i1 39 | 40 | void block(struct proc* p); 41 | void unblock(struct proc* p); 42 | int msg_send(struct proc* current, int dest, MESSAGE* m); 43 | int msg_receive(struct proc* current, int src, MESSAGE* m); 44 | int deadlock(int src, int dest); 45 | void reset_msg(MESSAGE* p); 46 | int send_recv(int function, int src_dest, MESSAGE* msg); 47 | 48 | int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p); 49 | 50 | #endif -------------------------------------------------------------------------------- /include/irq.h: -------------------------------------------------------------------------------- 1 | #ifndef __IRQ_H 2 | #define __IRQ_H 3 | 4 | #include 5 | 6 | /* 参考: http://wiki.osdev.org/IRQ */ 7 | 8 | #define IRQ_TIMER 0 9 | #define IRQ_KB 1 10 | #define IRQ_IDE 14 11 | 12 | // 中断处理 13 | void irq_handler(struct interrupt_frame *r); 14 | // 注册中断服务例程 15 | void irq_install(uint8_t irq, void (*handler)(struct interrupt_frame *r)); 16 | // 卸载中断服务例程 17 | void irq_uninstall(uint8_t irq); 18 | // 初始化中断服务 19 | void irq_init(); 20 | 21 | // 允许中断 22 | void irq_enable(uint8_t irq); 23 | // 屏蔽中断 24 | void irq_disable(uint8_t irq); 25 | 26 | // --------------------------- 27 | 28 | // 初始化时钟 29 | void irq_init_timer(void (*handler)(struct interrupt_frame *r)); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/isr.h: -------------------------------------------------------------------------------- 1 | #ifndef __ISR_H 2 | #define __ISR_H 3 | 4 | #include 5 | 6 | /* 参考: http://wiki.osdev.org/Interrupt_Service_Routines */ 7 | 8 | // IDT.h中只是设置中断向量,而这里是设置中断服务例程ISR,是处理中断的 9 | // 中断处理程序和中断服务例程不同 10 | // 所有中断由中断处理程序处理,再分派到各个中断服务例程中 11 | 12 | #define ISR_IRQ0 32 // 处理32-47的中断 13 | #define ISR_NIRQ 16 14 | #define ISR_SYSCALL 0x80 // 系统调用中断,目前还没用到它 15 | #define ISR_UNKNOWN 255 16 | 17 | // 中断时要保存的寄存器数据 18 | struct interrupt_frame { 19 | /* segment registers */ 20 | uint32_t gs; // 16 bits 21 | uint32_t fs; // 16 bits 22 | uint32_t es; // 16 bits 23 | uint32_t ds; // 16 bits 24 | 25 | /* registers save by pusha */ 26 | uint32_t edi; 27 | uint32_t esi; 28 | uint32_t ebp; 29 | uint32_t esp; 30 | uint32_t ebx; 31 | uint32_t edx; 32 | uint32_t ecx; 33 | uint32_t eax; 34 | 35 | uint32_t ret_addr; // 返回地址 36 | uint32_t int_no; 37 | 38 | /* save by `int` instruction */ 39 | uint32_t eip; 40 | uint32_t cs; // 16 bits 41 | uint32_t eflags; 42 | uint32_t user_esp; 43 | uint32_t ss; // 16 bits 44 | }; 45 | 46 | // 初始化中断服务例程 47 | void isr_init(); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /include/pmm.h: -------------------------------------------------------------------------------- 1 | #ifndef __PMM_H 2 | #define __PMM_H 3 | 4 | /* page memory management */ 5 | // 内存物理页框分配 6 | 7 | #define STACK_SIZE 8192 8 | extern uint32_t kern_stack_top; 9 | 10 | #define ADDR_RANGE_MEMORY 1 11 | #define ADDR_RANGE_RESERVED 2 12 | #define ADDR_RANGE_UNDEFINE 3 13 | 14 | #define PMM_MAX_SIZE 0x04000000 // 64M 15 | #define PMM_PAGE_SIZE 0x1000 16 | #define PAGE_MAX_SIZE (PMM_MAX_SIZE/PMM_PAGE_SIZE) 17 | #define PMM_PAGW_MASK 0xfffff000 18 | 19 | // ARD Address Range Descriptor 20 | // size = 20 21 | struct ard_entry { 22 | uint32_t base_addr_low; 23 | uint32_t base_addr_high; 24 | uint32_t len_low; 25 | uint32_t len_high; 26 | uint32_t type; 27 | }__attribute__((packed)); 28 | 29 | // 这里的页框是物理页框,对应的是物理地址,而不是虚拟地址 30 | 31 | // 页框初始化 32 | void pmm_init(); 33 | 34 | // 物理页表大小 35 | uint32_t pmm_size(); 36 | 37 | // 申请页框 38 | uint32_t pmm_alloc(); 39 | 40 | // 释放页框 41 | void pmm_free(uint32_t addr); 42 | 43 | #endif -------------------------------------------------------------------------------- /include/print.h: -------------------------------------------------------------------------------- 1 | #ifndef __PRINT_H 2 | #define __PRINT_H 3 | 4 | typedef char* va_list; 5 | 6 | #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) 7 | 8 | #define va_start(ap, v) (ap = (va_list)&v + _INTSIZEOF(v)) 9 | #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 10 | #define va_end(ap) (ap = (char*) 0) 11 | 12 | void printk(const char *fmt, ...); 13 | void vsprint(char *buf, const char *fmt, va_list args); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/proc.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROC_H 2 | #define __PROC_H 3 | 4 | #include 5 | #include 6 | 7 | // 进程名称最大长度 8 | #define PN_MAX_LEN 16 9 | 10 | // 最大进程数量 11 | #define NPROC 128 12 | 13 | /* 进程状态 */ 14 | #define P_UNUSED 0x0 // 未使用 15 | #define P_USED 0x1 // 使用中 16 | #define P_RUNABLE 0x2 // 可运行 17 | #define P_RUNNING 0x3 // 运行中 18 | #define P_SLEEPING 0x4 // 休眠中 19 | #define P_ZOMBIE 0x5 // 死亡 20 | 21 | // 优先级就是时间片分配 22 | #define PRIOR_SYST 0x20 // 系统服务优先级 23 | #define PRIOR_USER 0x10 // 用户进程优先级 24 | 25 | /** 26 | * MESSAGE 结构 / 借鉴自 MINIX 27 | */ 28 | struct msg1 { 29 | int m1i1; 30 | int m1i2; 31 | int m1i3; 32 | int m1i4; 33 | }; 34 | struct msg2 { 35 | void* m2p1; 36 | void* m2p2; 37 | void* m2p3; 38 | void* m2p4; 39 | }; 40 | struct msg3 { 41 | int m3i1; 42 | int m3i2; 43 | int m3i3; 44 | int m3i4; 45 | u64 m3l1; 46 | u64 m3l2; 47 | void* m3p1; 48 | void* m3p2; 49 | }; 50 | typedef struct { 51 | int source; 52 | int type; 53 | union { 54 | struct msg1 m1; 55 | struct msg2 m2; 56 | struct msg3 m3; 57 | } u; 58 | } MESSAGE; 59 | 60 | // PCB process control block 进程控制块 61 | // http://blog.csdn.net/wyzxg/article/details/4024340 62 | struct proc { 63 | /* KERNEL */ 64 | struct interrupt_frame *fi; // 中断现场 65 | volatile uint8_t pid; // 进程ID 66 | uint32_t size; // 用户空间大小 67 | uint8_t state; // 进程状态 68 | char name[PN_MAX_LEN]; // 进程名称 69 | pde_t *pgdir; // 虚页目录(一级页表) 70 | char *stack; // 进程内核堆栈 71 | struct proc *parent; // 父进程 72 | int8_t ticks; // 时间片 73 | int8_t priority; // 优先级 74 | /* IPC */ 75 | int p_flags; // 标识 76 | MESSAGE *p_msg; // 消息 77 | int p_recvfrom; // 接收消息的进程ID 78 | int p_sendto; // 发送消息的进程ID 79 | int has_int_msg; // nonzero if an INTERRUPT occurred when the task is not ready to deal with it. 80 | struct proc *q_sending; // queue of procs sending messages to this proc 81 | struct proc *next_sending; // next proc in the sending queue (q_sending) 82 | }; 83 | 84 | // 当前运行的进程 85 | extern struct proc *proc; 86 | 87 | // 记录中断重入次数 88 | extern int32_t k_reenter; 89 | 90 | // 时钟 91 | extern uint32_t tick; 92 | 93 | // 初始化进程管理 94 | void proc_init(); 95 | 96 | // 进程调度循环 97 | void schedule(); 98 | 99 | // 进程分叉 100 | int fork(); 101 | 102 | // 等待子进程 103 | int wait(); 104 | 105 | // 进程休眠 106 | void sleep(); 107 | 108 | // 进程唤醒 109 | void wakeup(uint8_t pid); 110 | 111 | // 杀死进程 112 | int kill(uint8_t pid); 113 | 114 | // 进程退出 115 | void exit(); 116 | 117 | // --------------------------------- 118 | 119 | #define proc2pid(x) ((x) -> pid) 120 | 121 | struct proc *nproc(int offset); 122 | struct proc *npid(int pid); 123 | int npoffset(int pid); 124 | 125 | void* va2la(int pid, void* va); 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /include/string.h: -------------------------------------------------------------------------------- 1 | #ifndef __STRING_H 2 | #define __STRING_H 3 | 4 | #include 5 | 6 | void *memcpy(void *dest, const void *src, uint32_t count); 7 | void *memset(void *dest, uint8_t val, uint32_t count); 8 | uint16_t *memsetw(uint16_t *dest, uint16_t val, uint32_t count); 9 | 10 | int strlen(const char *str); 11 | int strcmp(const char *str1, const char *str2); 12 | char *strcpy(char *dest,const char *src); 13 | char *strcat(char *dest,const char *src); 14 | char *strstr(const char *str, const char *search); 15 | 16 | int strncmp(const char *str1, const char *str2, uint32_t n); 17 | char *strncpy(char *dest, const char *src, uint32_t n); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef __SYSCALL_H 2 | #define __SYSCALL_H 3 | 4 | #include 5 | #include 6 | 7 | #define NSYSCALL 20 8 | 9 | // 调用号是自己设置的(在以后的微内核架构中,系统调用转化为信号) 10 | // 调用号: mov eax #n; int 0x80 11 | #define SYS_FORK 1 12 | #define SYS_EXIT 2 13 | #define SYS_EXEC 3 14 | #define SYS_SLEEP 4 15 | #define SYS_WAIT 5 16 | #define SYS_KILL 6 17 | #define SYS_IPC 7 // IPC实现微内核 18 | 19 | // 指定地址取整数 20 | int fetchint(uint32_t addr, int *ip); 21 | 22 | // 指定地址取字符串 23 | int fetchstr(uint32_t addr, char **pp); 24 | 25 | // 参数:取第n个整数 26 | int argint(int n, int *ip); 27 | 28 | // 参数:取第n个字符串,返回长度 29 | int argstr(int n, char **pp); 30 | 31 | // 参数:取第n个字符串地址 32 | int argptr(int n, char **pp, int size); 33 | 34 | // 初始化系统调用 35 | void sys_init(); 36 | 37 | // 系统调用 38 | void syscall(); 39 | 40 | // 用户调用 41 | int call(int no); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /include/sysproc.h: -------------------------------------------------------------------------------- 1 | #ifndef __SYSPROC_H 2 | #define __SYSPROC_H 3 | 4 | void irq_handler_clock(struct interrupt_frame *r); 5 | 6 | int sys_fork(); 7 | int sys_exit(); 8 | int sys_exec(); 9 | int sys_sleep(); 10 | int sys_wait(); 11 | int sys_kill(); 12 | int sys_ipc(); 13 | 14 | // ------------------------------- 15 | 16 | extern void user_main(); 17 | 18 | extern void sys_tasks0(); 19 | extern int sys_ticks(); 20 | 21 | #endif -------------------------------------------------------------------------------- /include/type.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONST_H 2 | #define __CONST_H 3 | 4 | #include 5 | 6 | typedef char bool; 7 | #define TRUE 1 8 | #define FALSE 0 9 | #define OK 0 10 | #define ERROR -1 11 | #define NULL (void *)0 12 | 13 | typedef unsigned long long uint64_t; 14 | typedef long long int64_t; 15 | typedef unsigned int uint32_t; 16 | typedef int int32_t; 17 | typedef unsigned short uint16_t; 18 | typedef short int16_t; 19 | typedef unsigned char uint8_t; 20 | typedef char int8_t; 21 | 22 | typedef uint64_t u64; 23 | typedef uint32_t u32; 24 | typedef uint16_t u16; 25 | typedef uint8_t u8; 26 | 27 | /* useful macro */ 28 | #define min(a, b) ((a)<(b)?(a):(b)) 29 | #define max(a, b) ((a)>(b)?(a):(b)) 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/uvm.h: -------------------------------------------------------------------------------- 1 | #ifndef __UVM_H 2 | #define __UVM_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // 内核空间初始化 9 | void kvm_init(pde_t *pgdir); 10 | 11 | // 用户空间初始化 12 | void uvm_init(pde_t *pgdir, char *init, uint32_t size); 13 | 14 | // 申请用户空间 15 | int uvm_alloc(pte_t *pgdir, uint32_t old_sz, uint32_t new_sz); 16 | 17 | // 用户页表切换 18 | void uvm_switch(struct proc *pp); 19 | 20 | // 释放用户空间 21 | void uvm_free(pte_t *pgdir); 22 | 23 | // 拷贝用户空间 24 | pde_t *uvm_copy(pte_t *pgdir, uint32_t size); 25 | 26 | #endif -------------------------------------------------------------------------------- /include/vga.h: -------------------------------------------------------------------------------- 1 | #ifndef __VGA_H 2 | #define __VAG_H 3 | 4 | #define VGA_COLOR_BLACK 0 5 | #define VGA_COLOR_BLUE 1 6 | #define VGA_COLOR_GREEN 2 7 | #define VGA_COLOR_CYAN 3 8 | #define VGA_COLOR_RED 4 9 | #define VGA_COLOR_MAGENTA 5 10 | #define VGA_COLOR_BROWN 6 11 | #define VGA_COLOR_LIGHTGREY 7 12 | #define VGA_COLOR_DARKGREY 8 13 | #define VGA_COLOR_LIGHTBLUE 9 14 | #define VGA_COLOR_LIGHTGREEN 10 15 | #define VGA_COLOR_LIGHTCYAN 11 16 | #define VGA_COLOR_LIGHTRED 12 17 | #define VGA_COLOR_LIGHTMAG 13 18 | #define VGA_COLOR_LIGHTBROWN 14 19 | #define VGA_COLOR_WHITE 15 20 | 21 | #define VGA_CRT_IC 0x3d4 // vga index register port 22 | #define VGA_CRT_DC 0x3d5 // vga data register port 23 | 24 | struct point { 25 | int x; 26 | int y; 27 | }; 28 | 29 | struct vga_char { 30 | char ch: 8; 31 | char fc: 4; // foreground color 32 | char bc: 4; // background color 33 | }; 34 | 35 | void vga_init(); 36 | void cls(); 37 | 38 | void putchar_at(int x, int y, char ch); 39 | void putchar(char ch); 40 | void puts(char *str); 41 | 42 | void vga_setcolor(char f_color, char b_color); 43 | struct point vga_getcur(); 44 | void vga_setcur(int x, int y); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/vmm.h: -------------------------------------------------------------------------------- 1 | #ifndef __VMM_H 2 | #define __VMM_H 3 | 4 | /* virtual memory management */ 5 | // 虚存分配(二级页表分配方式) 6 | 7 | // 参考:http://wiki.osdev.org/Paging 8 | 9 | // 对于一个32位虚拟地址(virtual address) 10 | // 32-22: 页目录号 | 21-12: 页表号 | 11-0: 页内偏移 11 | // http://www.360doc.com/content/11/0804/10/7204565_137844381.shtml 12 | 13 | /* 4k per page */ 14 | #define PAGE_SIZE 4096 15 | 16 | /* 页掩码,取高20位 */ 17 | #define PAGE_MASK 0xfffff000 18 | 19 | /* 地址对齐 */ 20 | #define PAGE_ALIGN_DOWN(x) ((x) & PAGE_MASK) 21 | #define PAGE_ALIGN_UP(x) (((x) + PAGE_SIZE - 1) & PAGE_MASK) 22 | 23 | /* 分析地址 */ 24 | #define PDE_INDEX(x) (((x) >> 22) & 0x3ff) // 获得地址对应的页目录号 25 | #define PTE_INDEX(x) (((x) >> 12) & 0x3ff) // 获得页表号 26 | #define OFFSET_INDEX(x) ((x) & 0xfff) // 获得页内偏移 27 | 28 | // 页目录项、页表项用uint32表示即可 29 | typedef uint32_t pde_t; 30 | typedef uint32_t pte_t; 31 | 32 | /* 页目录大小 1024 */ 33 | #define PDE_SIZE (PAGE_SIZE/sizeof(pte_t)) 34 | /* 页表大小 1024 */ 35 | #define PTE_SIZE (PAGE_SIZE/sizeof(pde_t)) 36 | /* 页表总数 1024*PTE_SIZE*PAGE_SIZE = 4G */ 37 | #define PTE_COUNT 1024 38 | 39 | /* CPU */ 40 | #define CR0_PG 0x80000000 41 | 42 | /* pde&pdt attribute */ 43 | #define PTE_P 0x1 // 有效位 Present 44 | #define PTE_R 0x2 // 读写位 Read/Write, can be read&write when set 45 | #define PTE_U 0x4 // 用户位 User / Kern 46 | #define PTE_K 0x0 // 内核位 User / Kern 47 | #define PTE_W 0x8 // 写回 Write through 48 | #define PTE_D 0x10 // 不缓存 Cache disable 49 | #define PTE_A 0x20 // 可访问 Accessed 50 | #define PTE_S 0x40 // Page size, 0 for 4kb pre page 51 | #define PTE_G 0x80 // Ignored 52 | 53 | /* 用户空间基址 */ 54 | #define USER_BASE 0xc0000000 55 | 56 | // 初始化页表 57 | void vmm_init(); 58 | 59 | // 启用分页 60 | void vmm_switch(uint32_t pgd); 61 | 62 | // 虚页映射 63 | void vmm_map(pde_t *pgdir, uint32_t va, uint32_t pa, uint32_t flags); 64 | 65 | // 解除映射 66 | void vmm_unmap(pde_t *pgdir, uint32_t va); 67 | 68 | // 查询分页情况 69 | int vmm_ismap(pde_t *pgdir, uint32_t va, uint32_t *pa); 70 | 71 | #endif -------------------------------------------------------------------------------- /screenshot/ipc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bajdcc/MiniOS/69d08fdf391d6f1fac1d6785abe6f94b8f22f28f/screenshot/ipc.png -------------------------------------------------------------------------------- /script/gdbinit: -------------------------------------------------------------------------------- 1 | target remote localhost:1234 2 | symbol-file bin/kernel.elf 3 | #b *0xc0000000 4 | c 5 | -------------------------------------------------------------------------------- /script/link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-i386") 2 | ENTRY(start) 3 | phys = 0x100000; 4 | SECTIONS 5 | { 6 | .text phys : AT(phys) 7 | { 8 | kernstart = .; 9 | code = .; 10 | *(.text) 11 | *(.rodata) 12 | . = ALIGN(4096); 13 | } 14 | .data : AT(phys + (data - code)) 15 | { 16 | data = .; 17 | *(.data) 18 | . = ALIGN(4096); 19 | } 20 | .stab : AT(phys + (stab - code)) 21 | { 22 | stab = .; 23 | *(.stab) 24 | . = ALIGN(4096); 25 | } 26 | .stabstr : AT(phys + (stabstr - code)) 27 | { 28 | stabstr = .; 29 | *(.stabstr) 30 | . = ALIGN(4096); 31 | } 32 | .bss : AT(phys + (bss - code)) 33 | { 34 | bss = .; 35 | *(.bss) 36 | . = ALIGN(4096); 37 | } 38 | kernend = .; 39 | 40 | /DISCARD/ : { *(.comment) *(.eh_frame) } 41 | } 42 | -------------------------------------------------------------------------------- /script/ulink.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-i386") 2 | ENTRY(main) 3 | va = 0xc0000000; 4 | SECTIONS 5 | { 6 | .text va : AT(va) 7 | { 8 | code = .; 9 | *(.text) 10 | *(.rodata) 11 | . = ALIGN(0x1000); 12 | } 13 | .data : AT(va + (data - code)) 14 | { 15 | data = .; 16 | *(.data) 17 | . = ALIGN(0x1000); 18 | } 19 | .stab : AT(va + (stab - code)) 20 | { 21 | stab = .; 22 | *(.stab) 23 | . = ALIGN(0x1000); 24 | } 25 | .stabstr : AT(va + (stabstr - code)) 26 | { 27 | stabstr = .; 28 | *(.stabstr) 29 | . = ALIGN(0x1000); 30 | } 31 | .bss : AT(va + (bss - code)) 32 | { 33 | bss = .; 34 | *(.bss) 35 | . = ALIGN(0x1000); 36 | } 37 | 38 | /DISCARD/ : { *(.comment) *(.eh_frame) } 39 | } 40 | -------------------------------------------------------------------------------- /src/kernel/asm.c: -------------------------------------------------------------------------------- 1 | /* asm.c 2 | * 汇编指令 3 | * some short, assembly function. 4 | */ 5 | 6 | /* http://www.cnblogs.com/taek/archive/2012/02/05/2338838.html */ 7 | 8 | #include 9 | #include 10 | 11 | /* read a byte from a port */ 12 | inline uint8_t inb (uint16_t port){ 13 | uint8_t rv; 14 | __asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (port)); 15 | return rv; 16 | } 17 | 18 | /* write a byte to a port */ 19 | inline void outb (uint16_t port, uint8_t data){ 20 | __asm__ __volatile__ ("outb %1, %0" : : "dN" (port), "a" (data)); 21 | } 22 | 23 | /* write a word to a port */ 24 | inline void outw (uint16_t port, uint16_t data){ 25 | __asm__ __volatile__ ("outw %1, %0" : : "dN" (port), "a" (data)); 26 | } 27 | 28 | /* read a word from a port */ 29 | inline uint16_t inw (uint16_t port){ 30 | uint16_t rv; 31 | __asm__ __volatile__ ("inw %1, %0" : "=a" (rv) : "dN" (port)); 32 | return rv; 33 | } 34 | 35 | 36 | /* read a string(start at addr, length cnt) from a port */ 37 | inline void insl(uint32_t port, void *addr, uint32_t cnt){ 38 | __asm__ __volatile__("cld; rep insl" 39 | : "=D" (addr), "=c" (cnt) 40 | : "d" (port), "0" (addr), "1" (cnt) 41 | : "memory", "cc" 42 | ); 43 | } 44 | 45 | /* write a string(start at addr, length cnt) to a port */ 46 | inline void outsl(uint32_t port, const void *addr, uint32_t cnt) { 47 | __asm__ __volatile__("cld; rep outsl" 48 | : "=S" (addr), "=c" (cnt) 49 | : "d" (port), "0" (addr), "1" (cnt) 50 | : "cc" 51 | ); 52 | } 53 | 54 | /* clear interrupt */ 55 | inline void cli(){ 56 | __asm__ __volatile__ ("cli"); 57 | } 58 | 59 | /* set interrupt */ 60 | inline void sti(){ 61 | __asm__ __volatile__ ("sti"); 62 | } 63 | 64 | /* enter halt state, until int comeup */ 65 | inline void hlt(){ 66 | __asm__ __volatile__ ("hlt"); 67 | } 68 | -------------------------------------------------------------------------------- /src/kernel/debug.c: -------------------------------------------------------------------------------- 1 | /* debug.c 2 | * some short, assembly function. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void print_status() { 12 | uint16_t reg1, reg2, reg3, reg4; 13 | __asm__ __volatile__( 14 | "mov %%cs, %0;" 15 | "mov %%ds, %1;" 16 | "mov %%es, %2;" 17 | "mov %%ss, %3;" 18 | : "=m"(reg1), "=m"(reg2), "=m"(reg3), "=m"(reg4)); 19 | 20 | printk("ring: %d\n", reg1 & 0x3); 21 | printk("cs: %x\n", reg1); 22 | printk("ds: %x\n", reg2); 23 | printk("es: %x\n", reg3); 24 | printk("ss: %x\n", reg4); 25 | } 26 | 27 | void panic(const char *msg, int ring) { 28 | cli(); 29 | 30 | vga_setcolor(VGA_COLOR_RED, VGA_COLOR_BLACK); 31 | 32 | printk("***** PANIC *****\n"); 33 | 34 | if (proc) { 35 | printk("Current proc: `%s`(PID: %d)\n", proc->name, proc->pid); 36 | } 37 | 38 | printk("Ring: %d\n", ring); 39 | printk("Message: %s\n", msg); 40 | printk("\n"); 41 | printk("Status:\n"); 42 | print_status(); 43 | 44 | for (;;) hlt(); 45 | } 46 | 47 | // 断言失败 48 | void assertion_failure(char *exp, char *file, char *base_file, int line) 49 | { 50 | printk("[ Assert(%s) failed: file: %s, base_file: %s, line: %d ]\n", 51 | exp, file, base_file, line); 52 | 53 | /** 54 | * If assertion fails in a TASK, the system will halt before 55 | * printl() returns. If it happens in a USER PROC, printl() will 56 | * return like a common routine and arrive here. 57 | * @see sys_printx() 58 | * 59 | * We use a forever loop to prevent the proc from going on: 60 | */ 61 | while (1); 62 | 63 | /* should never arrive here */ 64 | __asm__ __volatile__("ud2"); 65 | } 66 | -------------------------------------------------------------------------------- /src/kernel/fault.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* int 0-31 is used to service exceptions! */ 9 | extern void fault0(); 10 | extern void fault1(); 11 | extern void fault2(); 12 | extern void fault3(); 13 | extern void fault4(); 14 | extern void fault5(); 15 | extern void fault6(); 16 | extern void fault7(); 17 | extern void fault8(); 18 | extern void fault9(); 19 | extern void fault10(); 20 | extern void fault11(); 21 | extern void fault12(); 22 | extern void fault13(); 23 | extern void fault14(); 24 | extern void fault15(); 25 | extern void fault16(); 26 | extern void fault17(); 27 | extern void fault18(); 28 | extern void fault19(); 29 | extern void fault20(); 30 | extern void fault21(); 31 | extern void fault22(); 32 | extern void fault23(); 33 | extern void fault24(); 34 | extern void fault25(); 35 | extern void fault26(); 36 | extern void fault27(); 37 | extern void fault28(); 38 | extern void fault29(); 39 | extern void fault30(); 40 | extern void fault31(); 41 | 42 | static char *fault_msg[] = { 43 | "Division By Zero", 44 | "Debug", 45 | "Non Maskable Interrupt", 46 | "Breakpoint", 47 | "Into Detected Overflow", 48 | "Out of Bounds", 49 | "Invalid Opcode", 50 | "No Coprocessor", 51 | 52 | "Double Fault", 53 | "Coprocessor Segment Overrun", 54 | "Bad TSS", 55 | "Segment Not Present", 56 | "Stack Fault", 57 | "General Protection Fault", 58 | "Page Fault", 59 | "Unknown Interrupt", 60 | 61 | "Coprocessor Fault", 62 | "Alignment Check", 63 | "Machine Check", 64 | "Reserved", 65 | "Reserved", 66 | "Reserved", 67 | "Reserved", 68 | "Reserved", 69 | 70 | "Reserved", 71 | "Reserved", 72 | "Reserved", 73 | "Reserved", 74 | "Reserved", 75 | "Reserved", 76 | "Reserved", 77 | "Reserved" 78 | }; 79 | 80 | void fault_init() 81 | { 82 | idt_install(0, (uint32_t)fault0, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 83 | idt_install(1, (uint32_t)fault1, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 84 | idt_install(2, (uint32_t)fault2, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 85 | idt_install(3, (uint32_t)fault3, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 86 | idt_install(4, (uint32_t)fault4, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 87 | idt_install(5, (uint32_t)fault5, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 88 | idt_install(6, (uint32_t)fault6, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 89 | idt_install(7, (uint32_t)fault7, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 90 | 91 | idt_install(8, (uint32_t)fault8, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 92 | idt_install(9, (uint32_t)fault9, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 93 | idt_install(10, (uint32_t)fault10, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 94 | idt_install(11, (uint32_t)fault11, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 95 | idt_install(12, (uint32_t)fault12, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 96 | idt_install(13, (uint32_t)fault13, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 97 | idt_install(14, (uint32_t)fault14, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 98 | idt_install(15, (uint32_t)fault15, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 99 | 100 | idt_install(16, (uint32_t)fault16, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 101 | idt_install(17, (uint32_t)fault17, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 102 | idt_install(18, (uint32_t)fault18, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 103 | idt_install(19, (uint32_t)fault19, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 104 | idt_install(20, (uint32_t)fault20, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 105 | idt_install(21, (uint32_t)fault21, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 106 | idt_install(22, (uint32_t)fault22, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 107 | idt_install(23, (uint32_t)fault23, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 108 | 109 | idt_install(24, (uint32_t)fault24, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 110 | idt_install(25, (uint32_t)fault25, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 111 | idt_install(26, (uint32_t)fault26, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 112 | idt_install(27, (uint32_t)fault27, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 113 | idt_install(28, (uint32_t)fault28, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 114 | idt_install(29, (uint32_t)fault29, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 115 | idt_install(30, (uint32_t)fault30, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 116 | idt_install(31, (uint32_t)fault31, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 117 | 118 | } 119 | 120 | static void page_fault(struct interrupt_frame *r) { 121 | uint32_t cr2; 122 | 123 | __asm__ volatile ("mov %%cr2, %0":"=r"(cr2)); 124 | 125 | printk("Page fault! [addr: 0x%x]\n", cr2); 126 | panic(fault_msg[r->int_no], r->cs & 0x3); 127 | } 128 | 129 | void fault_handler(struct interrupt_frame *r) { 130 | 131 | // Page Fault 132 | if (r->int_no == 14) { // 页面错误 133 | page_fault(r); 134 | } 135 | 136 | if (r->int_no < 32) { 137 | printk("System fault!\n"); 138 | panic(fault_msg[r->int_no], r->cs & 0x3); 139 | } else { 140 | panic("Unknown Interrupt, System Halted!\n", r->cs & 0x3); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/kernel/gdt.c: -------------------------------------------------------------------------------- 1 | /* gdt.c 2 | * This file is modified form Bram's Kernel Development Tutorial 3 | * set the new gdt, the new gdt table has 256 entrys 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern struct tss_entry tss; 12 | 13 | static struct gdt_entry gdt[NGDT]; // 256 gdt entry 14 | struct gdt_ptr gp; // loader.asm中使用 15 | 16 | extern void gdt_flush(); // 在loader.asm中实现 extern func in loader.asm 17 | 18 | void gdt_install(uint8_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags) { 19 | 20 | /* Setup the descriptor base address */ 21 | gdt[num].base_low = (base & 0xffff); 22 | gdt[num].base_middle = (base >> 16) & 0xff; 23 | gdt[num].base_high = (base >> 24) & 0xff; 24 | 25 | /* Setup the descriptor limits */ 26 | gdt[num].limit_low = (limit & 0xffff); 27 | gdt[num].limit_high = ((limit >> 16) & 0x0f); 28 | 29 | /* Finally, set up the granularity and access flags */ 30 | gdt[num].flags = flags; 31 | 32 | access |= AC_RE; // 设置保留位为1 33 | gdt[num].access = access; 34 | } 35 | 36 | void tss_init() { 37 | gdt_install(SEL_TSS, (uint32_t)&tss, sizeof(tss), AC_PR|AC_AC|AC_EX, GDT_GR); 38 | /* for tss, access_reverse bit is 1 */ 39 | gdt[SEL_TSS].access &= ~AC_RE; 40 | } 41 | 42 | void gdt_init() { 43 | /* Setup the GDT pointer and limit */ 44 | gp.limit = (sizeof(struct gdt_entry) * NGDT) - 1; 45 | gp.base = (uint32_t)&gdt; 46 | 47 | // 注意:启用分页GDT_GR后,limit的单位是4KB,故0xfffff*4KB=4GB 48 | 49 | /* null descriptor */ 50 | gdt_install(0, 0, 0, 0, 0); 51 | /* kernel code segment type: code addr: 0 limit: 4G gran: 4KB sz: 32bit */ 52 | gdt_install(SEL_KCODE, 0, 0xfffff, AC_RW|AC_EX|AC_DPL_KERN|AC_PR, GDT_GR|GDT_SZ); 53 | /* kernel data segment type: data addr: 0 limit: 4G gran: 4KB sz: bit 32bit */ 54 | gdt_install(SEL_KDATA, 0, 0xfffff, AC_RW|AC_DPL_KERN|AC_PR, GDT_GR|GDT_SZ); 55 | /* user code segment type: code addr: 0 limit: 4G gran: 4KB sz: 32bit */ 56 | gdt_install(SEL_UCODE, 0, 0xfffff, AC_RW|AC_EX|AC_DPL_USER|AC_PR, GDT_GR|GDT_SZ); 57 | /* user code segment type: data addr: 0 limit: 4G gran: 4KB sz: 32bit */ 58 | gdt_install(SEL_UDATA, 0, 0xfffff, AC_RW|AC_DPL_USER|AC_PR, GDT_GR|GDT_SZ); 59 | /* systask code segment type: code addr: 0 limit: 4G gran: 4KB sz: 32bit */ 60 | gdt_install(SEL_SCODE, 0, 0xfffff, AC_RW|AC_EX|AC_DPL_SYST|AC_PR, GDT_GR|GDT_SZ); 61 | /* systask code segment type: data addr: 0 limit: 4G gran: 4KB sz: 32bit */ 62 | gdt_install(SEL_SDATA, 0, 0xfffff, AC_RW|AC_DPL_SYST|AC_PR, GDT_GR|GDT_SZ); 63 | 64 | tss_init(); 65 | gdt_flush(); 66 | tss_install(); 67 | } 68 | -------------------------------------------------------------------------------- /src/kernel/idt.c: -------------------------------------------------------------------------------- 1 | /* idt.c 2 | * This file is modified form Bram's Kernel Development Tutorial 3 | * install idt, 256 entrys table 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static struct idt_entry idt[NIDT]; 15 | struct tss_entry tss; 16 | struct idt_ptr idtp; // loader.asm中使用 17 | 18 | extern void idt_load(); // 在loader.asm中实现 19 | 20 | void idt_install(uint8_t num, uint32_t base, uint16_t selctor, uint8_t gate, uint8_t flags) { 21 | /* The interrupt routine's base address */ 22 | idt[num].base_low = (base & 0xffff); 23 | idt[num].base_high = (base >> 16) & 0xffff; 24 | 25 | /* The segment or 'selector' that this IDT entry will use 26 | * is set here, along with any access flags */ 27 | idt[num].selector = selctor; 28 | idt[num].always0 = 0; 29 | idt[num].gate_type = 0x0f & gate; 30 | idt[num].flags = 0x0f & flags; 31 | } 32 | 33 | void idt_init() { 34 | idtp.limit = (sizeof (struct idt_entry) * NIDT) - 1; 35 | idtp.base = (uint32_t)&idt; 36 | 37 | /* 清空IDT Clear out the entire IDT, initializing it to zeros */ 38 | memset(&idt, 0, sizeof(struct idt_entry) * NIDT); 39 | 40 | /* Add any new ISRs to the IDT here using idt_install */ 41 | idt_load(); 42 | } 43 | 44 | // 设置任务状态段 45 | void tss_install() { 46 | __asm__ volatile("ltr %%ax" : : "a"((SEL_TSS << 3))); 47 | } 48 | 49 | // 设置TSS 50 | void tss_set(uint16_t ss0, uint32_t esp0) { 51 | // 清空TSS 52 | memset((void *)&tss, 0, sizeof(tss)); 53 | tss.ss0 = ss0; 54 | tss.esp0 = esp0; 55 | tss.iopb_off = sizeof(tss); 56 | } 57 | 58 | // 重置当前进程的TSS 59 | void tss_reset() { 60 | // TSS用于当切换到ring0时设置堆栈 61 | // 每个进程有一个内核堆栈 62 | tss_set(SEL_KDATA << 3, (uint32_t)proc->stack + PAGE_SIZE); 63 | } 64 | -------------------------------------------------------------------------------- /src/kernel/init.asm: -------------------------------------------------------------------------------- 1 | ; init proc 2 | align 4 3 | 4 | [bits 32] 5 | [section .text] 6 | [global __init_start] 7 | [global __init_end] 8 | 9 | [extern user_main] 10 | 11 | __init_start: 12 | nop 13 | nop 14 | push user_main 15 | pop eax 16 | call eax 17 | jmp $ 18 | 19 | __init_end: 20 | -------------------------------------------------------------------------------- /src/kernel/ipc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern int sendrec(int function, int src_dest, MESSAGE* msg, int caller); 8 | 9 | static void ipc_dump(int function, int src_dest, MESSAGE* msg, int caller, const char *direction) { 10 | //const char *str; 11 | /*switch (function) { 12 | case SEND: 13 | str = "[ipc] proc#%d %s send | %d --> %d(%s)\n"; 14 | break; 15 | case RECEIVE: 16 | str = "[ipc] proc#%d %s recv | %d <-- %d(%s)\n"; 17 | break; 18 | default: 19 | str = "[ipc] proc#%d %s ???? | %d <-- %d(%s)\n"; 20 | break; 21 | }*/ 22 | //printk(str, proc2pid(proc), direction, caller, src_dest, (src_dest == TASK_ANY) ? "task_any" : "task_normal"); 23 | } 24 | 25 | int _sendrec(int function, int src_dest, MESSAGE* msg, int caller) { 26 | int ret; 27 | ipc_dump(function, src_dest, msg, caller, ">>"); 28 | ret = sendrec(function, src_dest, msg, caller); 29 | ipc_dump(function, src_dest, msg, caller, "<<"); 30 | return ret; 31 | } 32 | 33 | /***************************************************************************** 34 | * sys_sendrec 35 | *****************************************************************************/ 36 | /** 37 | * The core routine of system call `sendrec()'. 38 | * 39 | * @param function SEND or RECEIVE 40 | * @param src_dest To/From whom the message is transferred. 41 | * @param m Ptr to the MESSAGE body. 42 | * @param p The caller proc. 43 | * 44 | * @return 0 if success. 45 | *****************************************************************************/ 46 | int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p) 47 | { 48 | assert(k_reenter == 0); /* make sure we are not in ring0 */ 49 | assert((src_dest > 0) || 50 | src_dest == TASK_ANY || 51 | src_dest == INTERRUPT); 52 | 53 | int ret = 0; 54 | int caller = proc2pid(p); 55 | MESSAGE* mla = (MESSAGE*)va2la(caller, m); // 获取调用方消息 56 | mla->source = caller; 57 | 58 | assert(mla->source != src_dest); //确保不向自身发消息,防止死锁 59 | 60 | /** 61 | * Actually we have the third message type: BOTH. However, it is not 62 | * allowed to be passed to the kernel directly. Kernel doesn't know 63 | * it at all. It is transformed into a SEND followed by a RECEIVE 64 | * by `send_recv()'. 65 | */ 66 | if (function == SEND) { 67 | ret = msg_send(p, src_dest, m); // 发送消息 68 | } 69 | else if (function == RECEIVE) { 70 | ret = msg_receive(p, src_dest, m); // 接收消息 71 | } 72 | else { 73 | printk("invalid function: " 74 | "%d (SEND:%d, RECEIVE:%d).", function, SEND, RECEIVE); 75 | assert(!"sys_sendrec failed"); 76 | } 77 | 78 | return ret; 79 | } 80 | 81 | /***************************************************************************** 82 | * send_recv 83 | *****************************************************************************/ 84 | /** 85 | * IPC syscall. 86 | * 87 | * It is an encapsulation of `sendrec', 88 | * invoking `sendrec' directly should be avoided 89 | * 90 | * @param function SEND, RECEIVE or BOTH 91 | * @param src_dest The caller's proc_nr 92 | * @param msg Pointer to the MESSAGE struct 93 | * 94 | * @return always 0. 95 | *****************************************************************************/ 96 | int send_recv(int function, int src_dest, MESSAGE* msg) 97 | { 98 | int ret = 0, caller; 99 | 100 | caller = proc2pid(proc); 101 | 102 | if (function == RECEIVE) 103 | memset(msg, 0, sizeof(MESSAGE)); 104 | 105 | switch (function) { 106 | case BOTH: // 先发送再接收 107 | ret = _sendrec(SEND, src_dest, msg, caller); 108 | if (ret == 0) 109 | ret = _sendrec(RECEIVE, src_dest, msg, caller); 110 | break; 111 | case SEND: 112 | case RECEIVE: 113 | ret = _sendrec(function, src_dest, msg, caller); 114 | break; 115 | default: 116 | assert((function == BOTH) || 117 | (function == SEND) || (function == RECEIVE)); 118 | break; 119 | } 120 | 121 | return ret; 122 | } 123 | 124 | /***************************************************************************** 125 | * reset_msg 126 | *****************************************************************************/ 127 | /** 128 | * Clear up a MESSAGE by setting each byte to 0. 129 | * 130 | * @param p The message to be cleared. 131 | *****************************************************************************/ 132 | void reset_msg(MESSAGE* p) 133 | { 134 | memset(p, 0, sizeof(MESSAGE)); 135 | } 136 | 137 | /***************************************************************************** 138 | * block 139 | *****************************************************************************/ 140 | /** 141 | * This routine is called after `p_flags' has been set (!= 0), it 142 | * calls `schedule()' to choose another proc as the `proc_ready'. 143 | * 144 | * @attention This routine does not change `p_flags'. Make sure the `p_flags' 145 | * of the proc to be blocked has been set properly. 146 | * 147 | * @param p The proc to be blocked. 148 | *****************************************************************************/ 149 | void block(struct proc* p) 150 | { 151 | int pid; 152 | pid = proc2pid(p); 153 | assert(p->p_flags); 154 | __asm__("int $0x20"); // 强制切换 155 | assert(pid != proc2pid(proc)); // 确保切换成功 156 | } 157 | 158 | /***************************************************************************** 159 | * unblock 160 | *****************************************************************************/ 161 | /** 162 | * This is a dummy routine. It does nothing actually. When it is 163 | * called, the `p_flags' should have been cleared (== 0). 164 | * 165 | * @param p The unblocked proc. 166 | *****************************************************************************/ 167 | void unblock(struct proc* p) 168 | { 169 | assert(p->p_flags == 0); 170 | } 171 | 172 | /***************************************************************************** 173 | * deadlock 174 | *****************************************************************************/ 175 | /** 176 | * Check whether it is safe to send a message from src to dest. 177 | * The routine will detect if the messaging graph contains a cycle. For 178 | * instance, if we have procs trying to send messages like this: 179 | * A -> B -> C -> A, then a deadlock occurs, because all of them will 180 | * wait forever. If no cycles detected, it is considered as safe. 181 | * 182 | * @param src Who wants to send message. 183 | * @param dest To whom the message is sent. 184 | * 185 | * @return Zero if success. 186 | *****************************************************************************/ 187 | int deadlock(int src, int dest) 188 | { 189 | struct proc* p = npid(dest); 190 | while (1) { 191 | if (p->p_flags & SENDING) { 192 | if (p->p_sendto == src) { 193 | return 1; 194 | } 195 | p = npid(p->p_sendto); 196 | } 197 | else { 198 | break; 199 | } 200 | } 201 | return 0; 202 | } 203 | 204 | /***************************************************************************** 205 | * msg_send 206 | *****************************************************************************/ 207 | /** 208 | * Send a message to the dest proc. If dest is blocked waiting for 209 | * the message, copy the message to it and unblock dest. Otherwise the caller 210 | * will be blocked and appended to the dest's sending queue. 211 | * 212 | * @param current The caller, the sender. 213 | * @param dest To whom the message is sent. 214 | * @param m The message. 215 | * 216 | * @return Zero if success. 217 | *****************************************************************************/ 218 | int msg_send(struct proc* current, int dest, MESSAGE* m) 219 | { 220 | struct proc* sender = current; 221 | struct proc* p_dest = npid(dest); /* proc dest */ 222 | 223 | assert(proc2pid(sender) != proc2pid(p_dest)); 224 | 225 | /* check for deadlock here */ 226 | if (deadlock(proc2pid(sender), dest)) { 227 | printk("DEADLOCK! %d --> %d\n", sender->pid, p_dest->pid); 228 | assert(!"DEADLOCK"); 229 | } 230 | 231 | if ((p_dest->p_flags & RECEIVING) && /* dest is waiting for the msg */ 232 | (p_dest->p_recvfrom == proc2pid(sender) || 233 | p_dest->p_recvfrom == TASK_ANY)) { 234 | assert(p_dest->p_msg); 235 | assert(m); 236 | 237 | memcpy(va2la(dest, p_dest->p_msg), 238 | va2la(proc2pid(sender), m), 239 | sizeof(MESSAGE)); 240 | 241 | p_dest->p_msg = 0; 242 | p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */ 243 | p_dest->p_recvfrom = TASK_NONE; 244 | unblock(p_dest); 245 | 246 | assert(p_dest->p_flags == 0); 247 | assert(p_dest->p_msg == 0); 248 | assert(p_dest->p_recvfrom == TASK_NONE); 249 | assert(p_dest->p_sendto == TASK_NONE); 250 | assert(sender->p_flags == 0); 251 | assert(sender->p_msg == 0); 252 | assert(sender->p_recvfrom == TASK_NONE); 253 | assert(sender->p_sendto == TASK_NONE); 254 | } 255 | else { /* dest is not waiting for the msg */ 256 | sender->p_flags |= SENDING; 257 | assert(sender->p_flags == SENDING); 258 | sender->p_sendto = dest; 259 | sender->p_msg = m; 260 | 261 | /* append to the sending queue */ 262 | struct proc * p; 263 | if (p_dest->q_sending) { 264 | p = p_dest->q_sending; 265 | while (p->next_sending) 266 | p = p->next_sending; 267 | p->next_sending = sender; 268 | } 269 | else { 270 | p_dest->q_sending = sender; 271 | } 272 | sender->next_sending = 0; 273 | 274 | block(sender); 275 | 276 | assert(sender->p_flags == SENDING); 277 | assert(sender->p_msg != 0); 278 | assert(sender->p_recvfrom == TASK_NONE); 279 | assert(sender->p_sendto == dest); 280 | } 281 | 282 | return 0; 283 | } 284 | 285 | 286 | /***************************************************************************** 287 | * msg_receive 288 | *****************************************************************************/ 289 | /** 290 | * Try to get a message from the src proc. If src is blocked sending 291 | * the message, copy the message from it and unblock src. Otherwise the caller 292 | * will be blocked. 293 | * 294 | * @param current The caller, the proc who wanna receive. 295 | * @param src From whom the message will be received. 296 | * @param m The message ptr to accept the message. 297 | * 298 | * @return Zero if success. 299 | *****************************************************************************/ 300 | int msg_receive(struct proc* current, int src, MESSAGE* m) 301 | { 302 | struct proc* p_who_wanna_recv = current; 303 | struct proc* p_from = 0; /* from which the message will be fetched */ 304 | struct proc* prev = 0; 305 | int copyok = 0; 306 | 307 | assert(proc2pid(p_who_wanna_recv) != src); 308 | 309 | if ((p_who_wanna_recv->has_int_msg) && 310 | ((src == TASK_ANY) || (src == INTERRUPT))) { 311 | /* There is an interrupt needs p_who_wanna_recv's handling and 312 | * p_who_wanna_recv is ready to handle it. 313 | */ 314 | 315 | MESSAGE msg; 316 | reset_msg(&msg); 317 | msg.source = INTERRUPT; 318 | msg.type = HARD_INT; 319 | assert(m); 320 | memcpy(va2la(proc2pid(p_who_wanna_recv), m), &msg, 321 | sizeof(MESSAGE)); 322 | 323 | p_who_wanna_recv->has_int_msg = 0; 324 | 325 | assert(p_who_wanna_recv->p_flags == 0); 326 | assert(p_who_wanna_recv->p_msg == 0); 327 | assert(p_who_wanna_recv->p_sendto == TASK_NONE); 328 | assert(p_who_wanna_recv->has_int_msg == 0); 329 | 330 | return 0; 331 | } 332 | 333 | 334 | /* Arrives here if no interrupt for p_who_wanna_recv. */ 335 | if (src == TASK_ANY) { 336 | /* p_who_wanna_recv is ready to receive messages from 337 | * TASK_ANY proc, we'll check the sending queue and pick the 338 | * first proc in it. 339 | */ 340 | if (p_who_wanna_recv->q_sending) { 341 | p_from = p_who_wanna_recv->q_sending; 342 | copyok = 1; 343 | 344 | assert(p_who_wanna_recv->p_flags == 0); 345 | assert(p_who_wanna_recv->p_msg == 0); 346 | assert(p_who_wanna_recv->p_recvfrom == TASK_NONE); 347 | assert(p_who_wanna_recv->p_sendto == TASK_NONE); 348 | assert(p_who_wanna_recv->q_sending != 0); 349 | assert(p_from->p_flags == SENDING); 350 | assert(p_from->p_msg != 0); 351 | assert(p_from->p_recvfrom == TASK_NONE); 352 | assert(p_from->p_sendto == proc2pid(p_who_wanna_recv)); 353 | } 354 | } 355 | else { 356 | /* p_who_wanna_recv wants to receive a message from 357 | * a certain proc: src. 358 | */ 359 | p_from = npid(src); 360 | 361 | if ((p_from->p_flags & SENDING) && 362 | (p_from->p_sendto == proc2pid(p_who_wanna_recv))) { 363 | /* Perfect, src is sending a message to 364 | * p_who_wanna_recv. 365 | */ 366 | copyok = 1; 367 | 368 | struct proc* p = p_who_wanna_recv->q_sending; 369 | assert(p); /* p_from must have been appended to the 370 | * queue, so the queue must not be NULL 371 | */ 372 | while (p) { 373 | assert(p_from->p_flags & SENDING); 374 | if (proc2pid(p) == proc2pid(npid(src))) { /* if p is the one */ 375 | p_from = p; 376 | break; 377 | } 378 | prev = p; 379 | p = p->next_sending; 380 | } 381 | 382 | assert(p_who_wanna_recv->p_flags == 0); 383 | assert(p_who_wanna_recv->p_msg == 0); 384 | assert(p_who_wanna_recv->p_recvfrom == TASK_NONE); 385 | assert(p_who_wanna_recv->p_sendto == TASK_NONE); 386 | assert(p_who_wanna_recv->q_sending != 0); 387 | assert(p_from->p_flags == SENDING); 388 | assert(p_from->p_msg != 0); 389 | assert(p_from->p_recvfrom == TASK_NONE); 390 | assert(p_from->p_sendto == proc2pid(p_who_wanna_recv)); 391 | } 392 | } 393 | 394 | if (copyok) { 395 | /* It's determined from which proc the message will 396 | * be copied. Note that this proc must have been 397 | * waiting for this moment in the queue, so we should 398 | * remove it from the queue. 399 | */ 400 | if (p_from == p_who_wanna_recv->q_sending) { /* the 1st one */ 401 | assert(prev == 0); 402 | p_who_wanna_recv->q_sending = p_from->next_sending; 403 | p_from->next_sending = 0; 404 | } 405 | else { 406 | assert(prev); 407 | prev->next_sending = p_from->next_sending; 408 | p_from->next_sending = 0; 409 | } 410 | 411 | assert(m); 412 | assert(p_from->p_msg); 413 | /* copy the message */ 414 | memcpy(va2la(proc2pid(p_who_wanna_recv), m), 415 | va2la(proc2pid(p_from), p_from->p_msg), 416 | sizeof(MESSAGE)); 417 | 418 | p_from->p_msg = 0; 419 | p_from->p_sendto = TASK_NONE; 420 | p_from->p_flags &= ~SENDING; 421 | unblock(p_from); 422 | } 423 | else { /* nobody's sending TASK_ANY msg */ 424 | /* Set p_flags so that p_who_wanna_recv will not 425 | * be scheduled until it is unblocked. 426 | */ 427 | p_who_wanna_recv->p_flags |= RECEIVING; 428 | 429 | p_who_wanna_recv->p_msg = m; 430 | 431 | if (src == TASK_ANY) 432 | p_who_wanna_recv->p_recvfrom = TASK_ANY; 433 | else 434 | p_who_wanna_recv->p_recvfrom = proc2pid(p_from); 435 | 436 | block(p_who_wanna_recv); 437 | 438 | assert(p_who_wanna_recv->p_flags == RECEIVING); 439 | assert(p_who_wanna_recv->p_msg != 0); 440 | assert(p_who_wanna_recv->p_recvfrom != TASK_NONE); 441 | assert(p_who_wanna_recv->p_sendto == TASK_NONE); 442 | assert(p_who_wanna_recv->has_int_msg == 0); 443 | } 444 | 445 | return 0; 446 | } -------------------------------------------------------------------------------- /src/kernel/irq.c: -------------------------------------------------------------------------------- 1 | /* irq.c 2 | * This file is modified form Bram's Kernel Development Tutorial 3 | * Handle the Interrupt Requests(IRQs)h raised by hardware device 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* These are own ISRs that point to our special IRQ handler 13 | * instead of the regular 'fault_handler' function */ 14 | // 中断服务例程,默认是由fault_handler处理 15 | extern void irq0(); 16 | extern void irq1(); 17 | extern void irq2(); 18 | extern void irq3(); 19 | extern void irq4(); 20 | extern void irq5(); 21 | extern void irq6(); 22 | extern void irq7(); 23 | extern void irq8(); 24 | extern void irq9(); 25 | extern void irq10(); 26 | extern void irq11(); 27 | extern void irq12(); 28 | extern void irq13(); 29 | extern void irq14(); 30 | extern void irq15(); 31 | 32 | /* This array is actually an array of function pointers. We use 33 | * this to handle custom IRQ handlers for a given IRQ */ 34 | // 16级中断,初始化函数指针为空 35 | void *irq_routines[ISR_NIRQ] = { 36 | 0, 0, 0, 0, 0, 0, 0, 0, 37 | 0, 0, 0, 0, 0, 0, 0, 0 38 | }; 39 | 40 | /* This installs a custom IRQ handler for the given IRQ */ 41 | // 设置IRQ 42 | void irq_install(uint8_t irq, void (*handler)(struct interrupt_frame *r)) { 43 | irq_disable(irq); 44 | irq_routines[irq] = handler; 45 | irq_enable(irq); 46 | } 47 | 48 | /* This clears the handler for a given IRQ */ 49 | // 清空IRQ 50 | void irq_uninstall(uint8_t irq) { 51 | irq_disable(irq); 52 | irq_routines[irq] = 0; 53 | } 54 | 55 | /* in short: map irq 0-15 to int 32-47 */ 56 | // 8259A中断管理芯片 http://blog.csdn.net/github_30220885/article/details/47139671 57 | 58 | /* Chip IO port */ 59 | // Master PIC 60 | #define PIC1_CMD 0x20 61 | #define PIC1_DATA 0x21 62 | // Slave PIC 63 | #define PIC2_CMD 0xa0 64 | #define PIC2_DATA 0xa1 65 | 66 | // end of intrrupte 67 | #define PIC_EOI 0x20 68 | 69 | #define ICW1_ICW4 0x01 /* ICW4 (not) needed */ 70 | #define ICW1_SINGLE 0x02 /* Single (cascade) mode */ 71 | #define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ 72 | #define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ 73 | #define ICW1_INIT 0x10 /* Initialization - required! */ 74 | 75 | #define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ 76 | #define ICW4_AUTO 0x02 /* Auto (normal) EOI */ 77 | #define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ 78 | #define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ 79 | #define ICW4_SFNM 0x10 /* Special fully nested (not) */ 80 | 81 | void irq_remap() { 82 | 83 | /* 参考:http://wiki.osdev.org/8259_PIC */ 84 | 85 | outb(PIC1_CMD, ICW1_INIT + ICW1_ICW4); // starts the initialization sequence (in cascade mode) 86 | outb(PIC2_CMD, ICW1_INIT + ICW1_ICW4); 87 | 88 | outb(PIC1_DATA, 0x20); // ICW2: Master PIC vector offset 89 | outb(PIC2_DATA, 0x28); // ICW2: Slave PIC vector offset 90 | 91 | outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) 92 | outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) 93 | 94 | outb(PIC1_DATA, ICW4_8086); 95 | outb(PIC2_DATA, ICW4_8086); 96 | 97 | outb(PIC1_DATA, 0xFF); // disable all irq in Mister PIC 98 | outb(PIC2_DATA, 0xFF); // disable all irq in Slave PIC 99 | } 100 | 101 | /* We first remap the interrupt controllers, and then we install 102 | * the appropriate ISRs to the correct entries in the IDT. This 103 | * is just like installing the exception handlers */ 104 | void irq_init() { 105 | irq_remap(); 106 | 107 | idt_install(ISR_IRQ0 + 0, (uint32_t)irq0, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 108 | idt_install(ISR_IRQ0 + 1, (uint32_t)irq1, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 109 | idt_install(ISR_IRQ0 + 2, (uint32_t)irq2, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 110 | idt_install(ISR_IRQ0 + 3, (uint32_t)irq3, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 111 | idt_install(ISR_IRQ0 + 4, (uint32_t)irq4, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 112 | idt_install(ISR_IRQ0 + 5, (uint32_t)irq5, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 113 | idt_install(ISR_IRQ0 + 6, (uint32_t)irq6, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 114 | idt_install(ISR_IRQ0 + 7, (uint32_t)irq7, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 115 | idt_install(ISR_IRQ0 + 8, (uint32_t)irq8, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 116 | idt_install(ISR_IRQ0 + 9, (uint32_t)irq9, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 117 | idt_install(ISR_IRQ0 + 10, (uint32_t)irq10, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 118 | idt_install(ISR_IRQ0 + 11, (uint32_t)irq11, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 119 | idt_install(ISR_IRQ0 + 12, (uint32_t)irq12, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 120 | idt_install(ISR_IRQ0 + 13, (uint32_t)irq13, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 121 | idt_install(ISR_IRQ0 + 14, (uint32_t)irq14, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 122 | idt_install(ISR_IRQ0 + 15, (uint32_t)irq15, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 123 | } 124 | 125 | void irq_handler(struct interrupt_frame *r) { 126 | /* This is a blank function pointer */ 127 | void (*handler)(struct interrupt_frame *r); 128 | 129 | /* Find out if we have a custom handler to run for this 130 | * IRQ, and then finally, run it */ 131 | handler = irq_routines[r->int_no - ISR_IRQ0]; 132 | if (handler) { 133 | handler(r); 134 | } 135 | 136 | /* If the IDT entry that was invoked was greater than 40 137 | * (meaning IRQ8 - 15), then we need to send an EOI to 138 | * the slave controller */ 139 | if (r->int_no >= 40) { 140 | outb(PIC2_CMD, PIC_EOI); 141 | } 142 | /* In either case, we need to send an EOI to the master 143 | * interrupt controller too */ 144 | outb(PIC1_CMD, PIC_EOI); 145 | } 146 | 147 | // 允许中断 148 | void irq_enable(uint8_t irq) { 149 | if(irq < 8) { 150 | outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq)); 151 | } else { 152 | outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << irq)); 153 | } 154 | } 155 | 156 | // 屏蔽中断 157 | void irq_disable(uint8_t irq) { 158 | if(irq < 8) { 159 | outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq)); 160 | } else { 161 | outb(PIC2_DATA, inb(PIC2_DATA) | (1 << irq)); 162 | } 163 | } 164 | 165 | // 时钟中断 初始化 8253 PIT 166 | 167 | /* 8253/8254 PIT (Programmable Interval Timer) */ 168 | #define TIMER0 0x40 /* I/O port for timer channel 0 */ 169 | #define TIMER_MODE 0x43 /* I/O port for timer mode control */ 170 | #define RATE_GENERATOR 0x34 /* 00-11-010-0 : 171 | * Counter0 - LSB then MSB - rate generator - binary 172 | */ 173 | #define TIMER_FREQ 1193182L /* clock frequency for timer in PC and AT */ 174 | #define HZ 100 /* clock freq (software settable on IBM-PC) */ 175 | 176 | void irq_init_timer(void (*handler)(struct interrupt_frame *r)) { 177 | outb(TIMER_MODE, RATE_GENERATOR); 178 | outb(TIMER0, (uint8_t) (TIMER_FREQ/HZ) ); 179 | outb(TIMER0, (uint8_t) ((TIMER_FREQ/HZ) >> 8)); 180 | irq_install(IRQ_TIMER, handler); 181 | } -------------------------------------------------------------------------------- /src/kernel/isr.c: -------------------------------------------------------------------------------- 1 | /* isr.c 2 | * This file is modified form Bram's Kernel Development Tutorial 3 | * set up the Interrupt Service Routines, which handle excepitons generated by processor 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | extern void isr_unknown(); // see loader.asm 14 | 15 | void isr_init() { 16 | fault_init(); 17 | /* treat unkown interrupt as fault */ 18 | int i; 19 | // 初始化中断服务例程为isr_unknown 20 | for (i = ISR_IRQ0; i < NIDT; i++) { 21 | idt_install(i,(uint32_t)isr_unknown, SEL_KCODE << 3, GATE_INT, IDT_PR|IDT_DPL_KERN); 22 | } 23 | 24 | irq_init(); 25 | } 26 | 27 | void isr_stub(struct interrupt_frame *r) { 28 | if (r->int_no < ISR_IRQ0 || r->int_no == ISR_UNKNOWN) { 29 | fault_handler(r); // 默认中断处理 30 | } else if (r->int_no < ISR_IRQ0 + 16) { 31 | irq_handler(r); // 指定中断处理 32 | } else if (r->int_no == ISR_SYSCALL) { 33 | syscall(); // 系统调用 34 | } else { 35 | panic("isr_stub: wrong interrupt number", 0); // 中断号错误 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/kernel/loader.asm: -------------------------------------------------------------------------------- 1 | ; loader.asm 2 | ; jmp to C kernel, achieve some function in asm 3 | ; 4 | 5 | ; kernel code segment selector 6 | SEL_KERN_CODE EQU 0x8 7 | ; kernel data segment selector 8 | SEL_KERN_DATA EQU 0x10 9 | ; vedio memory 10 | SEL_KERN_VIDEO EQU 0x18 11 | ; 用户地址起始 12 | USER_BASE EQU 0xc0000000 13 | 14 | align 4 15 | 16 | [bits 32] 17 | [section .text] 18 | 19 | [extern os_main] 20 | [extern tss_reset] 21 | [extern isr_stub] 22 | [extern k_reenter] 23 | [global start] 24 | start: 25 | xor eax, eax 26 | mov ax, SEL_KERN_DATA 27 | mov ds, ax 28 | mov ax, SEL_KERN_DATA 29 | mov es, ax 30 | mov ax, SEL_KERN_VIDEO 31 | mov gs, ax 32 | mov ax, SEL_KERN_DATA 33 | mov ss, ax 34 | mov esp, 0x7c00 ; 联想到bootsect中的org 0x7c00 35 | 36 | ; mov the kernel to 0x100000 37 | [extern kernstart] 38 | [extern kernend] 39 | mov eax, kernend 40 | mov ecx, kernstart 41 | sub eax, ecx 42 | mov ecx, eax 43 | mov esi, 0x8000 44 | mov edi, 0x100000 45 | cld 46 | rep movsb 47 | jmp dword SEL_KERN_CODE:go 48 | 49 | go: 50 | mov edi, (160*3)+0 ; 160*50 line 3 column 1 51 | mov ah, 00001100b ; red color 52 | 53 | mov esi, msg 54 | call print 55 | 56 | push 0 57 | jmp os_main ; os entry 58 | 59 | jmp $ ; halt 60 | 61 | print: 62 | add edi, 160 63 | push edi 64 | cld 65 | 66 | loop: 67 | lodsb 68 | cmp al, 0 69 | je outloop 70 | mov [gs:edi], ax 71 | add edi, 2 72 | jmp loop 73 | 74 | outloop: 75 | pop edi 76 | ret 77 | 78 | msg: 79 | db "=== [ OS ENTRY ] ===", 0 80 | 81 | ; ####################### 内核专用 ####################### 82 | 83 | ; ********** gdt.c 84 | [global gdt_flush] 85 | [extern gp] 86 | 87 | gdt_flush: 88 | lgdt [gp] ; load gdt 89 | mov ax, SEL_KERN_DATA 90 | mov ds, ax 91 | mov es, ax 92 | mov fs, ax 93 | mov gs, ax 94 | mov ss, ax 95 | jmp SEL_KERN_CODE:flush2 96 | flush2: 97 | ret 98 | 99 | ; ********** idt.c 100 | [global idt_load] 101 | [extern idtp] 102 | 103 | idt_load: 104 | lidt [idtp] ; load idt 105 | ret 106 | 107 | ; ####################### 中断服务 ####################### 108 | 109 | ; ***** 0-31号中断 exception int 0 - int 31 110 | %macro m_fault 1 111 | [global fault%1] 112 | fault%1: 113 | cli 114 | ; int 8,10-14,17,30 have error code 115 | %if %1 != 17 && %1 != 30 && (%1 < 8 || %1 > 14) 116 | push 0 ; fake error code 117 | %endif 118 | pop eax 119 | push %1 120 | jmp _isr_stub ; 中断处理程序 121 | %endmacro 122 | 123 | %assign i 0 124 | %rep 32 125 | m_fault i 126 | %assign i i + 1 127 | %endrep 128 | 129 | ; ***** 32-47号中断 Interrupt Request int 32 - 47 130 | ; int 0-7 131 | %macro m_irq 1 132 | [global irq%1] 133 | irq%1: 134 | push %1+32 ; 参见于渊的minix代码 135 | call save ; 保存现场 136 | in al, 0x21 ; `. 137 | or al, (1 << %1) ; | 屏蔽当前中断 138 | out 0x21, al ; / 139 | mov al, 0x20 ; `. 置EOI位 140 | out 0x20, al ; / 141 | push %1+32 142 | mov eax, esi 143 | push eax 144 | mov eax, isr_stub 145 | sti 146 | call eax ; 中断处理 147 | cli 148 | pop ecx 149 | in al, 0x21 ; `. 150 | and al, ~(1 << %1) ; | 恢复接受当前中断 151 | out 0x21, al ; / 152 | add esp, 4 153 | ret 154 | %endmacro 155 | 156 | ; int 8-15 157 | %macro m_irq2 1 158 | [global irq%1] 159 | irq%1: 160 | cli 161 | push %1+32 162 | jmp _isr_stub ; 中断处理程序 163 | %endmacro 164 | 165 | %assign i 0 166 | %rep 8 167 | m_irq i 168 | %assign i i+1 169 | %endrep 170 | %rep 8 171 | m_irq2 i 172 | %assign i i+1 173 | %endrep 174 | 175 | ; ***** Unknown Interrupt 176 | ; 255 is a flag of unknown int 177 | ; so please note that we can't use it 178 | [global isr_unknown] 179 | isr_unknown: 180 | cli 181 | push 255 182 | jmp _isr_stub ; 中断处理程序 183 | 184 | 185 | ; ####################### 中断服务例程 ####################### 186 | 187 | [extern isr_stub] 188 | ; a common ISR func, sava the context of CPU 189 | ; call C func to process fault 190 | ; at last restore stack frame 191 | _isr_stub: 192 | push eax 193 | 194 | ; 保存现场 195 | pusha 196 | push ds 197 | push es 198 | push fs 199 | push gs 200 | 201 | mov ax, SEL_KERN_DATA ; 内核数据段 202 | mov ds, ax 203 | mov es, ax 204 | mov fs, ax 205 | mov gs, ax 206 | mov ss, ax 207 | 208 | mov eax, esp 209 | push eax 210 | 211 | mov eax, isr_stub 212 | call eax ; 中断处理 213 | pop eax 214 | 215 | _isr_stub_ret: 216 | ; 恢复现场 217 | pop gs 218 | pop fs 219 | pop es 220 | pop ds 221 | popa 222 | add esp, 8 223 | iret 224 | 225 | save: 226 | pushad ; `. 227 | push ds ; | 228 | push es ; | 保存原寄存器值 229 | push fs ; | 230 | push gs ; / 231 | mov dx, ss 232 | mov ds, dx 233 | mov es, dx 234 | 235 | mov esi, esp ; esi = 进程表起始地址 236 | 237 | inc dword [k_reenter] ; k_reenter++; 238 | cmp dword [k_reenter], 0 ; if (k_reenter == 0) 239 | jne .1 ; { 240 | mov ax, SEL_KERN_DATA ; 切换到内核数据段 241 | mov ds, ax 242 | mov es, ax 243 | mov fs, ax 244 | mov gs, ax 245 | mov ss, ax 246 | push restart ; push restart 247 | jmp [esi + 48] ; return; 248 | .1: ; } else { 已经在内核栈,不需要再切换 249 | push restart_reenter ; push restart_reenter 250 | jmp [esi + 48] ; return; 251 | ; } 252 | 253 | [extern proc] 254 | [global restart] 255 | restart: 256 | call tss_reset ; 修改tss 257 | mov eax, [proc] 258 | mov esp, [eax] ; esp = proc->fi 259 | restart_reenter: 260 | dec dword [k_reenter] 261 | pop gs 262 | pop fs 263 | pop es 264 | pop ds 265 | popad 266 | add esp, 8 267 | iretd ; 中断返回,弹出堆栈中的剩余信息,刷新cs和ip 268 | 269 | ; ####################### 系统调用 ####################### 270 | ; int 0x80 271 | [global _syscall] 272 | _syscall: 273 | push 0x80 274 | call save 275 | sti 276 | pop eax 277 | mov [esi + 48], eax ; 保存中断返回地址 278 | mov eax, isr_stub 279 | push esi 280 | call eax ; 中断处理 281 | mov [esi + 44], eax ; 系统调用返回值 282 | mov eax, [esi + 48] 283 | push eax ; 恢复刚刚pop的中断返回地址 284 | cli 285 | ret 286 | 287 | ; ####################### 进程间通信 ####################### 288 | ; IPC 289 | [global sendrec] 290 | sendrec: 291 | mov eax, 7 292 | int 0x80 293 | ret -------------------------------------------------------------------------------- /src/kernel/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | extern void restart(); 19 | 20 | void print_ok(void) 21 | { 22 | putchar('['); 23 | vga_setcolor(VGA_COLOR_GREEN, VGA_COLOR_BLACK); 24 | puts("OK"); 25 | vga_setcolor(VGA_COLOR_LIGHTGREY, VGA_COLOR_BLACK); 26 | putchar(']'); 27 | } 28 | 29 | void init(void) 30 | { 31 | vga_init(); 32 | 33 | print_ok(); 34 | puts(" init vga...\n"); 35 | 36 | gdt_init(); 37 | print_ok(); 38 | puts(" init gdt...\n"); 39 | 40 | idt_init(); 41 | print_ok(); 42 | puts(" init idt...\n"); 43 | 44 | isr_init(); 45 | print_ok(); 46 | puts(" init isr...\n"); 47 | 48 | pmm_init(); 49 | print_ok(); 50 | puts(" init pmm..."); 51 | 52 | printk(" size: 0x%x\n", pmm_size()); 53 | 54 | vmm_init(); 55 | print_ok(); 56 | puts(" init vmm...\n"); 57 | 58 | sys_init(); 59 | print_ok(); 60 | puts(" init syscall...\n"); 61 | 62 | proc_init(); 63 | print_ok(); 64 | puts(" init proc...\n"); 65 | } 66 | 67 | int os_main(void) 68 | { 69 | init(); 70 | 71 | vga_setcolor(VGA_COLOR_LIGHTBLUE, VGA_COLOR_BLACK); 72 | 73 | puts("\n"); 74 | puts("Hello world! --- OS by bajdcc \n"); 75 | puts("\n"); 76 | 77 | vga_setcolor(VGA_COLOR_WHITE, VGA_COLOR_BLACK); 78 | 79 | irq_init_timer(irq_handler_clock); 80 | 81 | uvm_switch(proc); 82 | restart(); // 完成中断的后半部分,从而进入init进程 83 | 84 | while (1); // handle interrupt 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /src/kernel/pmm.c: -------------------------------------------------------------------------------- 1 | /* pmm.h 2 | * 内存物理页分配 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | extern uint8_t kernstart; // 内核开始地址 10 | extern uint8_t code; 11 | extern uint8_t data; 12 | extern uint8_t bss; 13 | extern uint8_t kernend; // 内核结束地址 14 | 15 | static uint32_t pmm_stack[PAGE_MAX_SIZE + 1]; // 可用内存表 16 | static uint32_t pmm_stack_top = 0; // 栈顶 17 | static uint32_t pmm_count = 0; 18 | static uint32_t mem_size = 0; 19 | 20 | void pmm_init() { 21 | // ARD_count和ARD在bootsect.asm中已计算完毕 22 | uint32_t ARD_count = *(uint32_t *)0x400; 23 | struct ard_entry *ARD = (struct ard_entry *)0x500; 24 | uint32_t i = 0; 25 | 26 | for (i = 0; i < ARD_count; i++) { 27 | 28 | if (ARD[i].type == ADDR_RANGE_MEMORY && // 如果该内存段有效 29 | ARD[i].base_addr_low == 0x100000) { // 且基址是0x100000 30 | 31 | uint32_t addr = ((uint32_t)&kernend + PMM_PAGE_SIZE) & 0xfffff000; 32 | uint32_t limit = ARD[i].base_addr_low + ARD[i].len_low; 33 | 34 | while (addr < limit && addr <= PMM_MAX_SIZE) { 35 | mem_size += PMM_PAGE_SIZE; 36 | pmm_stack[pmm_stack_top++] = addr; // 将空闲内存映射到pmm_stack中 37 | addr += PMM_PAGE_SIZE; 38 | pmm_count++; 39 | } 40 | } 41 | } 42 | } 43 | 44 | uint32_t pmm_size() { 45 | return mem_size; 46 | } 47 | 48 | uint32_t pmm_alloc() { 49 | uint32_t addr = pmm_stack[--pmm_stack_top]; 50 | memset((void *)addr, 0, PMM_PAGE_SIZE); // 申请的内容清零 51 | return addr; 52 | } 53 | 54 | void pmm_free(uint32_t addr) { 55 | pmm_stack[pmm_stack_top++] = addr; 56 | memset((void *)addr, 1, PMM_PAGE_SIZE); 57 | } 58 | -------------------------------------------------------------------------------- /src/kernel/print.c: -------------------------------------------------------------------------------- 1 | /* print.c 2 | * this file include a useful funcion printk() 3 | * suppose %d %x %c %s now 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // 数字转字符串 12 | char* itoa( int value, char *str, int radix ) 13 | { 14 | char reverse[36]; 15 | char *p = reverse; 16 | bool sign = (value >= 0) ? TRUE : FALSE; 17 | 18 | value = (value >= 0) ? value : -value; 19 | *p++ = '\0'; 20 | while ( value >= 0 ) 21 | { 22 | *p++ = "0123456789abcdef"[value % radix]; 23 | value /= radix; 24 | if ( value == 0 ) 25 | break; 26 | } 27 | 28 | if ( !sign ) 29 | { 30 | *p = '-'; 31 | }else { 32 | p--; 33 | } 34 | 35 | while ( p >= reverse ) 36 | { 37 | *str++ = *p--; 38 | } 39 | 40 | return(str); 41 | } 42 | 43 | // 无符号数字转字符串 44 | char* uitoa( uint32_t value, char *str, int radix ) 45 | { 46 | char reverse[36]; 47 | char *p = reverse; 48 | 49 | *p++ = '\0'; 50 | while ( value != 0 ) 51 | { 52 | *p++ = "0123456789abcdef"[value % radix]; 53 | value /= radix; 54 | if ( value == 0 ) 55 | break; 56 | } 57 | p--; 58 | 59 | while ( p >= reverse ) 60 | { 61 | *str++ = *p--; 62 | } 63 | 64 | return(str); 65 | } 66 | 67 | // 字符串格式化 68 | void vsprint( char *buf, const char *fmt, va_list args ) 69 | { 70 | char *p; 71 | va_list p_next_arg = args; 72 | 73 | for ( p = buf; *fmt; fmt++ ) 74 | { 75 | if ( *fmt != '%' ) 76 | { 77 | *p++ = *fmt; 78 | continue; 79 | } 80 | fmt++; /* *fmt = '%' */ 81 | switch ( *fmt ) 82 | { 83 | case 'd': 84 | itoa( va_arg( p_next_arg, int ), p, 10 ); 85 | p += strlen( p ); 86 | break; 87 | case 'x': 88 | uitoa( va_arg( p_next_arg, unsigned int ), p, 16 ); 89 | p += strlen( p ); 90 | break; 91 | case 'c': 92 | *p++ = va_arg( p_next_arg, char ); 93 | break; 94 | case 's': 95 | *p = '\0'; 96 | strcat( p, va_arg( p_next_arg, char * ) ); 97 | p += strlen( p ); 98 | break; 99 | default: 100 | break; 101 | } 102 | } 103 | *p = '\0'; 104 | } 105 | 106 | // 内核打印 107 | void printk( const char *fmt, ... ) 108 | { 109 | char buf[256]; // 最多一次输出256B 110 | va_list args; 111 | 112 | memset( buf, 0, sizeof(buf) ); 113 | va_start( args, fmt ); 114 | vsprint( buf, fmt, args ); 115 | va_end( args ); 116 | puts( buf ); 117 | } 118 | -------------------------------------------------------------------------------- /src/kernel/proc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define PROC_IS_RUNABLE(pp) ((pp)->state == P_RUNABLE && (pp)->p_flags == 0) 16 | 17 | // 中断重入计数 18 | int32_t k_reenter = 0; 19 | 20 | // 进程ID(每次递增,用于赋值给新进程) 21 | static uint32_t cur_pid = 0; 22 | 23 | // 时钟 24 | uint32_t tick = 0; 25 | 26 | // 进程PCB数组 27 | static struct proc pcblist[NPROC]; 28 | 29 | // 当前进程 30 | struct proc *proc = NULL; 31 | 32 | // 开辟新进程 33 | static struct proc *proc_alloc() { 34 | struct proc *pp; 35 | char *p; 36 | 37 | // 查找空闲进程 38 | for (pp = &pcblist[0]; pp < &pcblist[NPROC]; pp++) { 39 | 40 | if (pp->state == P_UNUSED) { // 找到未使用的进程 41 | 42 | /* 43 | ------------------------------ 4KB 44 | interrupt_frame 45 | ... 46 | stack 47 | ------------------------------ 0KB 48 | */ 49 | 50 | pp->state = P_USED; // 占用它 51 | pp->pid = ++cur_pid; // 进程ID递增 52 | 53 | pp->stack = (char *)pmm_alloc(); // 申请物理页框 54 | 55 | // p指针指向页头 p = page_head 56 | p = pp->stack; 57 | 58 | // p = page_head + 页大小 - 中断现场结构大小 59 | p = p + PAGE_SIZE - sizeof(*pp->fi); 60 | 61 | // 进程的中断现场fi 位置 = page_head + 页大小 - 中断现场结构大小 62 | pp->fi = (struct interrupt_frame *)p; 63 | 64 | // 初始化优先级 65 | pp->priority = PRIOR_USER; 66 | 67 | // 重置时间片 68 | pp->ticks = 0; 69 | 70 | // init ipc data 71 | pp->p_flags = 0; 72 | pp->p_recvfrom = TASK_NONE; 73 | pp->p_sendto = TASK_NONE; 74 | pp->has_int_msg = 0; 75 | pp->q_sending = 0; 76 | pp->next_sending = 0; 77 | 78 | return pp; 79 | } 80 | } 81 | return 0; // 创建失败返回空 82 | } 83 | 84 | // 初始化进程管理 85 | void proc_init() { 86 | struct proc *pp; 87 | extern char __init_start; 88 | extern char __init_end; 89 | 90 | // PCB数组清零 91 | memset(pcblist, 0, sizeof(struct proc) * NPROC); 92 | 93 | // 初始化第一个进程 94 | pp = proc_alloc(); 95 | 96 | // 初始化一级页表 97 | pp->pgdir = (pde_t *)pmm_alloc(); 98 | 99 | // 初始化内核堆栈 100 | kvm_init(pp->pgdir); 101 | 102 | // 映射内核堆栈 103 | vmm_map(pp->pgdir, (uint32_t)pp->stack, (uint32_t)pp->stack, PTE_P | PTE_R | PTE_K); 104 | 105 | // 得到内核大小 106 | uint32_t size = &__init_end - &__init_start; 107 | 108 | // 用户空间大小是4KB 109 | pp->size = PAGE_SIZE; 110 | 111 | // 将内核数据拷贝至映射用户空间 112 | uvm_init(pp->pgdir, &__init_start, size); 113 | 114 | // 拷贝中断现场,在执行完_isr_stub_ret恢复 115 | pp->fi->cs = (SEL_SCODE << 3) | RPL_SYST; // 0x1 RPL=1 116 | pp->fi->ds = (SEL_SDATA << 3) | RPL_SYST; // SYS TASK 117 | pp->fi->es = pp->fi->ds; 118 | pp->fi->fs = pp->fi->ds; 119 | pp->fi->gs = pp->fi->ds; 120 | pp->fi->ss = pp->fi->ds; 121 | pp->fi->eflags = 0x202; 122 | pp->fi->user_esp = USER_BASE + PAGE_SIZE; // 堆栈顶部,因为是向下生长的 123 | pp->fi->eip = USER_BASE; // 从0xc0000000开始执行 124 | 125 | strcpy(pp->name, "init"); // 第一个进程名为init 126 | 127 | pp->state = P_RUNABLE; // 状态设置为可运行 128 | 129 | pp->priority = PRIOR_SYST; // 初始化优先级 130 | 131 | proc = pp; 132 | } 133 | 134 | // 进程切换 135 | static void proc_switch(struct proc *pp) { 136 | if (proc->state == P_RUNNING) { 137 | proc->state = P_RUNABLE; 138 | } 139 | pp->state = P_RUNABLE; 140 | proc = pp; // 切换当前活动进程 141 | } 142 | 143 | // 挑选可用进程 144 | static struct proc *proc_pick() { 145 | struct proc *pp, *pp_ready; 146 | int greatest_ticks = 0; 147 | 148 | // 查找可用进程 149 | for (pp = &pcblist[0]; pp < &pcblist[NPROC]; pp++) { 150 | 151 | if (PROC_IS_RUNABLE(pp)) { // 进程是活动的,且不堵塞 152 | 153 | if (pp->ticks > greatest_ticks) { 154 | greatest_ticks = pp->ticks; 155 | pp_ready = pp; 156 | } 157 | } 158 | } 159 | 160 | if (!greatest_ticks) 161 | return 0; 162 | return pp_ready; 163 | } 164 | 165 | // 重置时间片 166 | static void reset_time() { 167 | int i; 168 | struct proc *pp; 169 | i = 0; 170 | for (pp = &pcblist[0]; pp < &pcblist[NPROC]; pp++) { 171 | if (PROC_IS_RUNABLE(pp)) { 172 | i++; 173 | pp->ticks = pp->priority; 174 | } 175 | } 176 | if (i == 0) { 177 | assert(!"no runable process!"); 178 | } 179 | } 180 | 181 | // 进程调度 182 | void schedule() { 183 | struct proc *pp, *pp_ready; 184 | pp_ready = 0; 185 | 186 | while (!pp_ready) { 187 | 188 | pp_ready = proc_pick(); // 挑选进程 189 | 190 | if (!pp_ready) { // 所有进程时间片用完,重置 191 | reset_time(); 192 | } else { 193 | pp = pp_ready; 194 | 195 | // 关中断 196 | cli(); 197 | 198 | uvm_switch(pp); // 切换页表 199 | proc_switch(pp); // 切换进程 200 | 201 | // 开中断 202 | sti(); 203 | 204 | return; 205 | } 206 | } 207 | } 208 | 209 | // 进程休眠 210 | void sleep() { 211 | cli(); 212 | 213 | if (proc->state == P_RUNNING) { 214 | proc->state = P_SLEEPING; 215 | } 216 | 217 | sti(); 218 | } 219 | 220 | // 唤醒进程 221 | void wakeup(uint8_t pid) { 222 | struct proc* pp; 223 | 224 | cli(); 225 | 226 | for (pp = pcblist; pp < &pcblist[NPROC]; pp++) { 227 | if (pp->pid == pid && pp->state == P_SLEEPING) { 228 | pp->state = P_RUNABLE; 229 | return; 230 | } 231 | } 232 | 233 | sti(); 234 | } 235 | 236 | // 进程销毁 237 | static void proc_destroy(struct proc *pp) { 238 | 239 | pmm_free((uint32_t)pp->stack); // 释放堆栈内存 240 | pp->stack = 0; 241 | uvm_free(pp->pgdir); // 释放页表 242 | 243 | pp->state = P_UNUSED; 244 | pp->pid = 0; 245 | pp->parent = 0; 246 | pp->name[0] = 0; 247 | } 248 | 249 | // 等待子进程 250 | int wait() { 251 | uint8_t has_child, pid; 252 | struct proc* pp; 253 | 254 | cli(); 255 | 256 | has_child = 0; 257 | 258 | for (pp = pcblist; pp < &pcblist[NPROC]; pp++) { 259 | if (pp->parent != proc) { 260 | continue; // 没有子进程 261 | } 262 | 263 | has_child = 1; 264 | 265 | if (pp->state == P_ZOMBIE) { 266 | 267 | pid = pp->pid; 268 | proc_destroy(pp); 269 | 270 | sti(); 271 | 272 | return pid; // 返回刚刚退出的子进程ID 273 | } 274 | } 275 | 276 | if (!has_child || proc->state == P_ZOMBIE) { 277 | sti(); 278 | return -1; // 无需等待一个将被杀死的进程 279 | } 280 | 281 | sleep(); // 等待,这里不能阻塞,因为要防止中断重入 282 | 283 | sti(); 284 | 285 | return -1; 286 | } 287 | 288 | // 退出进程 289 | void exit() { 290 | struct proc *pp; 291 | 292 | cli(); 293 | 294 | if (!proc->parent) { // init 295 | sti(); 296 | return; 297 | } 298 | 299 | wakeup(proc->parent->pid); // 唤醒当前进程的父进程 300 | proc->state = P_ZOMBIE; // 标记为僵尸进程,等待父进程wait来销毁 301 | 302 | for (pp = pcblist; pp < &pcblist[NPROC]; pp++) { 303 | if (pp->parent == proc) { // 找到子进程 304 | pp->parent = proc->parent; // 将子进程的父进程置为当前进程的父进程(当前进程skip) 305 | if (pp->state == P_ZOMBIE) { // 若子进程为僵尸进程 306 | wakeup(proc->parent->pid); // 唤醒当前进程的父进程 307 | } 308 | } 309 | } 310 | 311 | sti(); 312 | } 313 | 314 | // 杀死进程 315 | int kill(uint8_t pid) { 316 | struct proc *pp; 317 | 318 | cli(); 319 | 320 | for (pp = pcblist; pp < &pcblist[NPROC]; pp++) { 321 | 322 | if (pp->pid == pid) { // 找到要杀死的进程 323 | pp->state = P_ZOMBIE; // 标记为僵尸进程 324 | sti(); 325 | return 0; 326 | } 327 | } 328 | 329 | sti(); 330 | 331 | return -1; 332 | } 333 | 334 | // 进程分叉 335 | int fork() { 336 | struct proc *child; // 子进程 337 | 338 | cli(); 339 | 340 | if ((child = proc_alloc()) == 0) { 341 | sti(); 342 | return -1; // 创建进程失败 343 | } 344 | 345 | // 将当前进程的用户空间拷贝至子进程 346 | child->pgdir = uvm_copy(proc->pgdir, proc->size); 347 | 348 | // 映射堆栈 349 | /* 这句的缺少才是导致bug的罪魁祸首 350 | * 具体原因是:未映射内存导致无法访问,进而导致系统reset 351 | */ 352 | vmm_map(child->pgdir, (uint32_t)child->stack, (uint32_t)child->stack, PTE_P | PTE_R | PTE_U); 353 | 354 | if (child->pgdir == 0) { // 拷贝失败 355 | panic("fork:", child->fi->cs & 0x3); 356 | pmm_free((uint32_t)child->stack); 357 | child->stack = 0; 358 | child->state = P_UNUSED; 359 | sti(); 360 | return -1; // 创建进程失败 361 | } 362 | 363 | child->size = proc->size; // 用户空间一致 364 | child->parent = proc; // 设置父进程为当前进程 365 | *child->fi = *proc->fi; // 拷贝中断现场 366 | 367 | child->fi->eax = 0; 368 | 369 | strncpy(child->name, proc->name, sizeof(proc->name)); // 拷贝进程名 370 | 371 | child->state = P_RUNABLE; // 置状态为可运行 372 | 373 | sti(); 374 | 375 | return child->pid; // 返回子进程PID 376 | } 377 | 378 | void irq_handler_clock(struct interrupt_frame *r) { 379 | 380 | tick++; 381 | 382 | if (!proc) 383 | return; 384 | 385 | if (PROC_IS_RUNABLE(proc)) { 386 | if (proc->ticks > 0) { 387 | proc->ticks--; 388 | return; 389 | } 390 | 391 | if (k_reenter != 0) { // 防止重入 392 | return; 393 | } 394 | } 395 | 396 | schedule(); 397 | } 398 | 399 | struct proc *nproc(int offset) { 400 | return &pcblist[offset]; 401 | } 402 | 403 | struct proc *npid(int pid) { 404 | struct proc* pp; 405 | 406 | for (pp = pcblist; pp < &pcblist[NPROC]; pp++) { 407 | if (pp->pid == pid) { 408 | return pp; 409 | } 410 | } 411 | return 0; 412 | } 413 | 414 | int npoffset(int pid) { 415 | int i; 416 | 417 | for (i = 0; i < NPROC; i++) { 418 | if (pcblist[i].pid == pid) { 419 | return i; 420 | } 421 | } 422 | return -1; 423 | } 424 | 425 | void* va2la(int pid, void* va) { 426 | struct proc* pp; 427 | 428 | for (pp = pcblist; pp < &pcblist[NPROC]; pp++) { 429 | if (pp->pid == pid) { 430 | 431 | uint32_t pa; 432 | 433 | if (vmm_ismap(pp->pgdir, (uint32_t)va, &pa)) { 434 | return (void*)((uint32_t)pa + OFFSET_INDEX((uint32_t)va)); 435 | } 436 | 437 | assert(!"vmm: va not mapped!"); 438 | 439 | return 0; 440 | } 441 | } 442 | 443 | return 0; 444 | } 445 | -------------------------------------------------------------------------------- /src/kernel/string.c: -------------------------------------------------------------------------------- 1 | /* memory.c 2 | * 字符串处理 3 | * this file includes some library functions to process string 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | void *memcpy( void *dest, const void *src, uint32_t count ) 10 | { 11 | const uint8_t *sp = (const uint8_t *) src; 12 | uint8_t *dp = (uint8_t *) dest; 13 | 14 | for (; count != 0; count-- ) 15 | *dp++ = *sp++; 16 | return(dest); 17 | } 18 | 19 | 20 | void *memset( void *dest, uint8_t val, uint32_t count ) 21 | { 22 | uint8_t *temp = (uint8_t *) dest; 23 | 24 | for (; count != 0; count-- ) 25 | *temp++ = val; 26 | return(dest); 27 | } 28 | 29 | 30 | uint16_t *memsetw( uint16_t *dest, uint16_t val, uint32_t count ) 31 | { 32 | uint16_t *temp = (uint16_t *) dest; 33 | 34 | for (; count != 0; count-- ) 35 | *temp++ = val; 36 | return(dest); 37 | } 38 | 39 | 40 | int strlen( const char *str ) 41 | { 42 | int len; 43 | 44 | for ( len = 0; *str; str++ ) 45 | len++; 46 | return(len); 47 | } 48 | 49 | 50 | int strcmp( const char *str1, const char *str2 ) 51 | { 52 | while ( *str1 || *str2 ) 53 | { 54 | if ( *str1 == *str2 ) 55 | { 56 | str1++; 57 | str2++; 58 | }else return(*str1 - *str2); 59 | } 60 | return(0); 61 | } 62 | 63 | 64 | char *strcpy( char *dest, const char *src ) 65 | { 66 | char *addr = dest; 67 | 68 | while ( *src ) 69 | { 70 | *dest++ = *src++; 71 | } 72 | *dest = '\0'; 73 | return(addr); 74 | } 75 | 76 | 77 | char *strcat( char *dest, const char *src ) 78 | { 79 | char *addr = dest; 80 | 81 | while ( *dest ) 82 | dest++; 83 | do 84 | *dest++ = *src++; 85 | while ( *src ); /* end by zero */ 86 | return(addr); 87 | } 88 | 89 | 90 | /* this function is case sensitive 91 | * return the substring, not index*/ 92 | char *strstr( const char *str, const char *search ) 93 | { 94 | char *p = (char *) search; 95 | 96 | while ( *str ) 97 | { 98 | while ( (!*p) || *str == *p ) 99 | { 100 | if ( !*p ) 101 | return( (char *) str); 102 | str++; 103 | p++; 104 | } 105 | p = (char *) search; 106 | str++; 107 | } 108 | return(NULL); 109 | } 110 | 111 | 112 | int strncmp( const char *str1, const char *str2, uint32_t n ) 113 | { 114 | while ( (*str1 || *str2) && n > 0 ) 115 | { 116 | if ( *str1 == *str2 ) 117 | { 118 | str1++; 119 | str2++; 120 | n--; 121 | }else return(*str1 - *str2); 122 | } 123 | return(0); 124 | } 125 | 126 | 127 | char *strncpy( char *dest, const char *src, uint32_t n ) 128 | { 129 | char *addr = dest; 130 | 131 | while ( *src && n > 0 ) 132 | { 133 | *dest++ = *src++; 134 | n--; 135 | } 136 | *dest = '\0'; 137 | return(addr); 138 | } 139 | -------------------------------------------------------------------------------- /src/kernel/syscall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | // 系统调用 14 | extern void _syscall(); 15 | 16 | // 默认调用 17 | int sys_none(); 18 | 19 | // 调用表 20 | static int (*sys_routines[])(void) = { 21 | sys_none, 22 | sys_fork, 23 | sys_exit, 24 | sys_exec, 25 | sys_sleep, 26 | sys_wait, 27 | sys_kill, 28 | sys_ipc, 29 | sys_none, 30 | sys_none, 31 | sys_none, 32 | sys_none, 33 | sys_none, 34 | sys_none, 35 | sys_none, 36 | sys_none, 37 | sys_none, 38 | sys_none, 39 | sys_none, 40 | sys_none, 41 | sys_none 42 | }; 43 | 44 | /* 调用名 */ 45 | char *sys_name[NSYSCALL + 1] = { 46 | "sys_none", 47 | "sys_fork", 48 | "sys_exit", 49 | "sys_exec", 50 | "sys_sleep", 51 | "sys_wait", 52 | "sys_kill", 53 | "sys_ipc", 54 | "sys_none", 55 | "sys_none", 56 | "sys_none", 57 | "sys_none", 58 | "sys_none", 59 | "sys_none", 60 | "sys_none", 61 | "sys_none", 62 | "sys_none", 63 | "sys_none", 64 | "sys_none", 65 | "sys_none", 66 | "sys_none" 67 | }; 68 | 69 | // 指定地址取整数 70 | int fetchint(uint32_t addr, int *ip) { 71 | if (addr < USER_BASE 72 | || addr > USER_BASE + proc->size 73 | || addr + 4 > USER_BASE + proc->size) { 74 | return -1; 75 | } 76 | *ip = *(int *)(addr); 77 | 78 | return 0; 79 | } 80 | 81 | // 指定地址取字符串 82 | int fetchstr(uint32_t addr, char **pp) { 83 | char *s, *ep; 84 | 85 | if (addr < USER_BASE || addr >= USER_BASE + proc->size) { 86 | return -1; 87 | } 88 | 89 | *pp = (char *)addr; 90 | ep = (char *)(USER_BASE + proc->size); 91 | 92 | for (s = *pp; s < ep; s++) { 93 | if (*s == '\0') { 94 | return s - *pp; 95 | } 96 | } 97 | 98 | return -1; 99 | } 100 | 101 | // 参数:取第n个整数 102 | int argint(int n, int *ip) { 103 | return fetchint(proc->fi->user_esp + 4*n + 4, ip); // "+4" for eip 104 | } 105 | 106 | // 参数:取第n个字符串,返回长度 107 | int argstr(int n, char **pp) { 108 | int addr; 109 | 110 | if (argint(n, &addr) < 0) { 111 | return -1; 112 | } 113 | 114 | return fetchstr((uint32_t)addr, pp); 115 | } 116 | 117 | // 参数:取第n个字符串地址 118 | int argptr(int n, char **pp, int size) { 119 | int addr; 120 | 121 | if(argint(n, &addr) < 0) { 122 | return -1; 123 | } 124 | 125 | if ((uint32_t)addr < USER_BASE 126 | || (uint32_t)addr >= USER_BASE + proc->size 127 | || (uint32_t)addr + size >= USER_BASE + proc->size) { 128 | return -1; 129 | } 130 | 131 | *pp = (char *)addr; 132 | 133 | return 0; 134 | } 135 | 136 | // 默认调用 137 | int sys_none() { 138 | return -1; 139 | } 140 | 141 | // 初始化系统调用 142 | void sys_init() { 143 | // 注册中断调用 144 | idt_install(ISR_SYSCALL, (uint32_t)_syscall, SEL_KCODE << 3, GATE_TRAP, IDT_PR | IDT_DPL_SYST); 145 | } 146 | 147 | // 系统调用 148 | void syscall() { 149 | int cn; 150 | 151 | if (!proc || proc->state == P_SLEEPING) { 152 | proc->fi->eax = -1; 153 | return; 154 | } 155 | 156 | cn = proc->fi->eax; // eax表示调用号 157 | 158 | //printk("proc#%d: %s\n", proc->pid, sys_name[cn]); 159 | 160 | if (cn > 0 && cn <= NSYSCALL && sys_routines[cn]) { 161 | proc->fi->eax = sys_routines[cn](); 162 | } else { 163 | proc->fi->eax = -1; 164 | } 165 | } 166 | 167 | // http://www.cnblogs.com/taek/archive/2012/02/05/2338838.html 168 | int call(int no) { 169 | volatile int ret = -1; 170 | __asm__ __volatile__("mov 0x8(%ebp), %eax\n\t" 171 | "int $0x80\n\t" 172 | "mov %eax, -0x4(%ebp)"); 173 | return ret; 174 | } 175 | -------------------------------------------------------------------------------- /src/kernel/sysproc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int sys_fork() { 9 | return fork(); 10 | } 11 | 12 | int sys_exit() { 13 | int i; 14 | i = proc->pid; 15 | exit(); 16 | printk("proc#%d exit\n", i); 17 | return 0; 18 | } 19 | 20 | int sys_exec() { 21 | printk("exec: pid=%d\n", proc->pid); 22 | return 0; 23 | } 24 | 25 | int sys_sleep() { 26 | sleep(); 27 | return 0; 28 | } 29 | 30 | int sys_wait() { 31 | return wait(); 32 | } 33 | 34 | int sys_kill() { 35 | int pid; 36 | 37 | if (argint(0, &pid) < 0) { 38 | return -1; 39 | } 40 | 41 | return kill(pid); 42 | } 43 | 44 | int sys_ipc() { 45 | int function, src_dest, caller; 46 | char *message; 47 | struct proc *pp; 48 | 49 | if (argint(0, &function) < 0) { 50 | return -1; 51 | } 52 | 53 | if (argint(1, &src_dest) < 0) { 54 | return -1; 55 | } 56 | 57 | if (argptr(2, &message, 0) < 0) { 58 | return -1; 59 | } 60 | 61 | if (argint(3, &caller) < 0) { 62 | return -1; 63 | } 64 | 65 | pp = npid(caller); 66 | 67 | return sys_sendrec(function, src_dest, (MESSAGE *)message, pp); 68 | } 69 | 70 | // --------------------------- 71 | 72 | static void halt() { 73 | while (1); 74 | } 75 | 76 | static int delay() { 77 | volatile int i; 78 | 79 | for (i = 0; i < 0x1000000; i++); 80 | return i; 81 | } 82 | 83 | void user_main() { 84 | 85 | int i; 86 | 87 | i = call(SYS_FORK); 88 | 89 | delay(); 90 | if (i != 0) { 91 | sys_tasks0(); 92 | delay(); 93 | call(SYS_WAIT); 94 | } else { 95 | while (1) { 96 | delay(); 97 | i = sys_ticks(); 98 | delay(); 99 | printk("!! proc#%d <-- tick '%d'\n", proc2pid(proc), i); 100 | delay(); 101 | delay(); 102 | delay(); 103 | delay(); 104 | } 105 | } 106 | 107 | printk("halt...\n"); 108 | 109 | halt(); 110 | } 111 | 112 | void sys_tasks0() { 113 | extern uint32_t tick; 114 | 115 | MESSAGE msg; 116 | while (1) { 117 | send_recv(RECEIVE, TASK_ANY, &msg); 118 | int src = msg.source; 119 | switch (msg.type) { 120 | case SYS_TICKS: 121 | msg.RETVAL = tick; 122 | printk("!! proc#%d --> tick '%d'\n", proc2pid(proc), tick); 123 | send_recv(SEND, src, &msg); 124 | break; 125 | default: 126 | assert(!"unknown msg type"); 127 | break; 128 | } 129 | } 130 | } 131 | 132 | static int sys_ipc_call(int type) { 133 | MESSAGE msg; 134 | reset_msg(&msg); 135 | msg.type = type; 136 | send_recv(BOTH, TASK_SYS, &msg); 137 | return msg.RETVAL; 138 | } 139 | 140 | int sys_ticks() { 141 | return sys_ipc_call(SYS_TICKS); 142 | } -------------------------------------------------------------------------------- /src/kernel/uvm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* 将 0 - USER_BASE(0xc0000000) 的所有内存映射到页表中 */ 9 | void kvm_init(pde_t *pgdir) { 10 | uint32_t addr; 11 | 12 | for (addr = 0; addr < pmm_size(); addr += PAGE_SIZE) { 13 | // http://www.cnblogs.com/Acg-Check/p/4268136.html 14 | vmm_map(pgdir, addr, addr, PTE_P | PTE_R | PTE_K); // 原本是内核空间,为实现微内核临时设为PTE_U 15 | } 16 | } 17 | 18 | /* 映射空间 */ 19 | void uvm_init(pde_t *pgdir, char *init, uint32_t size) { 20 | char *room; 21 | room = (char *)pmm_alloc(); // 申请页框 22 | memcpy(room, init, size); // 拷贝初值 23 | vmm_map(pgdir, USER_BASE, (uint32_t)room, PTE_U | PTE_P | PTE_R); // 用户空间基址存放初值 24 | } 25 | 26 | // 页表切换 27 | void uvm_switch(struct proc *pp) { 28 | /* 注:据Linux内核的实现,tss不参与进程切换 29 | * 然而tss的作用是特权级切换时,恢复ss0和esp0 30 | * 由于先前删除了这一句,故用户态int 0x80时直接跳转至 31 | * 0x00000000地址处,即BIOS代码处,导致bug 32 | * 33 | * 已查明BUG原因:下下一句页表设置有缺陷,即没有映射堆栈pp->stack 34 | * 已将TSS初始化语句放置在proc_init中 35 | */ 36 | // 切换到进程页表 37 | vmm_switch((uint32_t)pp->pgdir); 38 | } 39 | 40 | /* 释放空间 */ 41 | void uvm_free(pte_t *pgdir) { 42 | uint32_t i; 43 | 44 | for (i = PDE_INDEX(USER_BASE); i < PTE_COUNT; i++) { 45 | if (pgdir[i] & PTE_P) { // 有效 46 | pmm_free(pgdir[i] & PAGE_MASK); // 释放物理页 47 | } 48 | } 49 | 50 | pmm_free((uint32_t)pgdir); // 释放页表 51 | } 52 | 53 | // 拷贝空间 54 | pde_t *uvm_copy(pte_t *pgdir, uint32_t size) { 55 | pde_t *pgd; 56 | uint32_t i, pa, mem; 57 | 58 | pgd = (pde_t *)pmm_alloc(); // 申请物理页 59 | 60 | kvm_init(pgd); // 映射空间到物理页 61 | 62 | for (i = 0; i < size; i += PAGE_SIZE) { 63 | if (vmm_ismap(pgdir, USER_BASE + i, &pa)) { 64 | mem = pmm_alloc(); 65 | memcpy((void *)mem, (void *)pa, PAGE_SIZE); 66 | vmm_map(pgd, USER_BASE + i, mem, PTE_R | PTE_U | PTE_P); 67 | } 68 | } 69 | 70 | return pgd; 71 | } 72 | 73 | // 申请空间 74 | int uvm_alloc(pte_t *pgdir, uint32_t old_sz, uint32_t new_sz) { 75 | uint32_t mem; 76 | uint32_t start; 77 | 78 | if (new_sz < old_sz) { // 新空间更小,不用申请 79 | return old_sz; 80 | } 81 | 82 | // 对多余的空间进行映射 83 | for (start = PAGE_ALIGN_UP(old_sz); start < new_sz; start += PAGE_SIZE) { 84 | mem = pmm_alloc(); 85 | vmm_map(pgdir, start, mem, PTE_P | PTE_R | PTE_U); 86 | } 87 | 88 | return new_sz; 89 | } 90 | -------------------------------------------------------------------------------- /src/kernel/vga.c: -------------------------------------------------------------------------------- 1 | /* vga.c 2 | * control the display of char, 3 | * you must invoke init_vga before use other func 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | struct vga_char *vga_mem; /* vga[25][80] at 0xb8000 */ 13 | struct vga_char color; /* use vag_char structure to store color */ 14 | 15 | struct point cur; 16 | 17 | static void move_cur() 18 | { 19 | uint16_t tmp; 20 | 21 | tmp = cur.y * 80 + cur.x; 22 | 23 | /* cursor high port to vga index register */ 24 | outb( VGA_CRT_IC, 0xe ); 25 | outb( VGA_CRT_DC, tmp >> 8 ); 26 | /* cursor low port to vga index register */ 27 | outb( VGA_CRT_IC, 0xf ); 28 | outb( VGA_CRT_DC, tmp ); 29 | } 30 | 31 | 32 | static void scroll() 33 | { 34 | uint16_t blank = color.ch | ( (color.bc << 4) | color.fc) << 8; 35 | 36 | if ( cur.y >= 25 ) 37 | { 38 | memcpy( vga_mem, vga_mem + 80, 80 * 24 * 2 ); 39 | memsetw( (uint16_t *) vga_mem + 80 * 24, blank, 80 ); 40 | cur.y--; 41 | } 42 | } 43 | 44 | 45 | void cls() 46 | { 47 | int x, y; 48 | struct vga_char blank = { ' ', VGA_COLOR_LIGHTGREY, VGA_COLOR_BLACK }; 49 | 50 | for ( y = 0; y < 25; y++ ) 51 | { 52 | for ( x = 0; x < 80; x++ ) 53 | { 54 | vga_mem[y * 80 + x] = blank; 55 | } 56 | } 57 | cur.x = cur.y = 0; 58 | move_cur(); 59 | } 60 | 61 | 62 | void vga_init() 63 | { 64 | cur.x = 0; 65 | cur.y = 5; 66 | move_cur(); 67 | vga_mem = (struct vga_char *) 0xb8000; 68 | color.ch = ' '; 69 | color.fc = VGA_COLOR_LIGHTGREY; 70 | color.bc = VGA_COLOR_BLACK; 71 | } 72 | 73 | 74 | void putchar( char ch ) 75 | { 76 | switch ( ch ) 77 | { 78 | case '\r': cur.x = 0; break; 79 | case '\n': cur.y = cur.y + 1; cur.x = 0; break; 80 | case '\b': cur.y -= (cur.x == 0) ? 1 : 0; 81 | cur.x = (cur.x + 80 - 1) % 80; 82 | vga_mem[cur.y * 80 + cur.x].ch = ' '; 83 | vga_mem[cur.y * 80 + cur.x].fc = color.fc; 84 | vga_mem[cur.y * 80 + cur.x].bc = color.bc; 85 | break; 86 | case '\t': do 87 | putchar( ' ' ); 88 | while ( cur.x % 4 != 0 ); break; 89 | default: { 90 | vga_mem[cur.y * 80 + cur.x].ch = ch; 91 | vga_mem[cur.y * 80 + cur.x].fc = color.fc; 92 | vga_mem[cur.y * 80 + cur.x].bc = color.bc; 93 | cur.y += (cur.x + 1) / 80; 94 | cur.x = (cur.x + 1) % 80; 95 | } 96 | } 97 | scroll(); /* you should scroll before updata the cursor, or error */ 98 | move_cur(); 99 | } 100 | 101 | 102 | void puts( char *str ) 103 | { 104 | for (; *str != '\0'; str++ ) 105 | putchar( *str ); 106 | } 107 | 108 | 109 | void vga_setcolor( char fc, char bc ) 110 | { 111 | if ( fc < 0 || fc > 16 || bc < 0 || bc > 16 ) 112 | { 113 | return; 114 | } 115 | color.fc = fc; 116 | color.bc = bc; 117 | } 118 | 119 | 120 | struct point vga_getcur() 121 | { 122 | return(cur); 123 | } 124 | 125 | 126 | void vga_setcur( int x, int y ) 127 | { 128 | if ( x < 0 || x >= 80 || y < 0 || y >= 25 ) 129 | { 130 | return; 131 | } 132 | cur.x = x; 133 | cur.y = y; 134 | } 135 | -------------------------------------------------------------------------------- /src/kernel/vmm.c: -------------------------------------------------------------------------------- 1 | /* vmm.h 2 | * 虚拟内存分配 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern uint8_t kernstart; 11 | extern uint8_t kernend; 12 | 13 | /* 内核页表 = PTE_SIZE*PAGE_SIZE */ 14 | pde_t pgd_kern[PTE_SIZE] __attribute__((aligned(PAGE_SIZE))); 15 | /* 内核页表内容 = PTE_COUNT*PTE_SIZE*PAGE_SIZE */ 16 | pte_t pte_kern[PTE_COUNT][PTE_SIZE] __attribute__((aligned(PAGE_SIZE))); 17 | 18 | /* 修改CR3寄存器 */ 19 | inline void vmm_switch(uint32_t pde) { 20 | __asm__ volatile ("mov %0, %%cr3": :"r"(pde)); 21 | } 22 | 23 | /* 刷新TLB */ 24 | // http://blog.csdn.net/cinmyheart/article/details/39994769 25 | static inline void vmm_reflush(uint32_t va) { 26 | __asm__ volatile ("invlpg (%0)"::"a"(va)); 27 | } 28 | 29 | // 允许分页 30 | // http://blog.csdn.net/whatday/article/details/24851197 31 | static inline void vmm_enable() { 32 | uint32_t cr0; 33 | __asm__ volatile ("mov %%cr0, %0" : "=r" (cr0)); 34 | cr0 |= CR0_PG; 35 | __asm__ volatile ("mov %0, %%cr0" : : "r" (cr0)); 36 | } 37 | 38 | void vmm_init() { 39 | uint32_t i; 40 | 41 | // map 4G memory, physcial address = virtual address 42 | for (i = 0; i < PTE_SIZE; i++) { 43 | pgd_kern[i] = (uint32_t)pte_kern[i] | PTE_P | PTE_R | PTE_K; 44 | } 45 | 46 | uint32_t *pte = (uint32_t *)pte_kern; 47 | for (i = 1; i < PTE_COUNT*PTE_SIZE; i++) { 48 | pte[i] = (i << 12) | PTE_P | PTE_R | PTE_K; // i是页表号 49 | } 50 | 51 | vmm_switch((uint32_t)pgd_kern); 52 | vmm_enable(); 53 | } 54 | 55 | // 虚页映射 56 | // va = 虚拟地址 pa = 物理地址 57 | void vmm_map(pde_t *pgdir, uint32_t va, uint32_t pa, uint32_t flags) { 58 | uint32_t pde_idx = PDE_INDEX(va); // 页目录号 59 | uint32_t pte_idx = PTE_INDEX(va); // 页表号 60 | 61 | pte_t *pte = (pte_t *)(pgdir[pde_idx] & PAGE_MASK); // 页表 62 | 63 | if (!pte) { // 缺页 64 | if (va >= USER_BASE) { // 若是用户地址则转换 65 | pte = (pte_t *)pmm_alloc(); // 申请物理页框,用作新页表 66 | pgdir[pde_idx] = (uint32_t)pte | PTE_P | flags; // 设置页表 67 | pte[pte_idx] = (pa & PAGE_MASK) | PTE_P | flags; // 设置页表项 68 | } else { // 内核地址不转换 69 | pte = (pte_t *)(pgd_kern[pde_idx] & PAGE_MASK); // 取得内核页表 70 | pgdir[pde_idx] = (uint32_t)pte | PTE_P | flags; // 设置页表 71 | } 72 | } else { // pte存在 73 | pte[pte_idx] = (pa & PAGE_MASK) | PTE_P | flags; // 设置页表项 74 | } 75 | } 76 | 77 | // 释放虚页 78 | void vmm_unmap(pde_t *pde, uint32_t va) { 79 | uint32_t pde_idx = PDE_INDEX(va); 80 | uint32_t pte_idx = PTE_INDEX(va); 81 | 82 | pte_t *pte = (pte_t *)(pde[pde_idx] & PAGE_MASK); 83 | 84 | if (!pte) { 85 | return; 86 | } 87 | 88 | pte[pte_idx] = 0; // 清空页表项,此时有效位为零 89 | vmm_reflush(va); // 刷新TLB 90 | } 91 | 92 | // 是否已分页 93 | int vmm_ismap(pde_t *pgdir, uint32_t va, uint32_t *pa) { 94 | uint32_t pde_idx = PDE_INDEX(va); 95 | uint32_t pte_idx = PTE_INDEX(va); 96 | 97 | pte_t *pte = (pte_t *)(pgdir[pde_idx] & PAGE_MASK); 98 | if (!pte) { 99 | return 0; // 页表不存在 100 | } 101 | if (pte[pte_idx] != 0 && (pte[pte_idx] & PTE_P) && pa) { 102 | *pa = pte[pte_idx] & PAGE_MASK; // 计算物理页面 103 | return 1; // 页面存在 104 | } 105 | return 0; // 页表项不存在 106 | } -------------------------------------------------------------------------------- /usr/logo.txt: -------------------------------------------------------------------------------- 1 | ███╗ ███╗██╗███╗ ██╗██╗ ██████╗ ███████╗ 2 | ████╗ ████║██║████╗ ██║██║██╔═══██╗██╔════╝ 3 | ██╔████╔██║██║██╔██╗ ██║██║██║ ██║███████╗ 4 | ██║╚██╔╝██║██║██║╚██╗██║██║██║ ██║╚════██║ 5 | ██║ ╚═╝ ██║██║██║ ╚████║██║╚██████╔╝███████║ 6 | ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═════╝ ╚══════╝ 7 | 8 | --------------------------------------------------------------------------------