├── floppy.img ├── test.jpg ├── tools ├── gdbinit └── kernel.ld ├── .gitignore ├── kern ├── driver │ ├── clock.h │ ├── tty.h │ ├── tty.c │ ├── clock.c │ ├── console.h │ ├── console.c │ ├── keymap.h │ ├── keyboard.c │ └── keyboard.h ├── mm │ ├── heap.h │ ├── gdtasm.S │ ├── pmm.h │ ├── vmm.h │ ├── gdt.h │ ├── pmm.c │ ├── gdt.c │ ├── vmm.c │ └── heap.c ├── schedule │ ├── sched.h │ ├── switchasm.S │ └── sched.c ├── debug │ ├── debug.h │ ├── debug.c │ └── printk.c ├── process │ ├── proc.h │ └── proc.c ├── trap │ ├── idtasm.S │ ├── idt.h │ └── idt.c └── init │ └── entry.c ├── libs ├── vargs.h ├── common.h ├── string.h ├── common.c ├── types.h ├── elf.h ├── string.c ├── elf.c └── multiboot.h ├── README.md ├── boot └── bootasm.S └── Makefile /floppy.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hijkzzz/mini-os-kernel/HEAD/floppy.img -------------------------------------------------------------------------------- /test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hijkzzz/mini-os-kernel/HEAD/test.jpg -------------------------------------------------------------------------------- /tools/gdbinit: -------------------------------------------------------------------------------- 1 | file kernel 2 | target remote:1234 3 | break kern_init 4 | continue 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 忽略所有 .o 结尾的文件 2 | *.o 3 | 4 | # 忽略内核文件 5 | kernel 6 | 7 | # 忽略 tags 8 | tags 9 | 10 | # 系统产生的文件 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /kern/driver/clock.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_TIMER_H 2 | #define INCLUDE_TIMER_H 3 | 4 | #include "types.h" 5 | 6 | void init_timer(uint32_t frequency); 7 | 8 | #endif // INCLUDE_TIMER_H 9 | -------------------------------------------------------------------------------- /kern/driver/tty.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_TTY_H 2 | #define INCLUDE_TTY_H 3 | 4 | // tty 进程 5 | int task_tty(void *args); 6 | // 打印字符 7 | void in_process(uint32_t key); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /libs/vargs.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_VARGS_H 2 | #define INCLUDE_VARGS_H 3 | 4 | typedef __builtin_va_list va_list; 5 | 6 | #define va_start(ap, last) (__builtin_va_start(ap, last)) 7 | #define va_arg(ap, type) (__builtin_va_arg(ap, type)) 8 | #define va_end(ap) 9 | 10 | #endif // INCLUDE_VARGS_H 11 | -------------------------------------------------------------------------------- /kern/driver/tty.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "keyboard.h" 3 | #include "debug.h" 4 | 5 | int task_tty(void *args) 6 | { 7 | while (1) { 8 | keyboard_read(); 9 | } 10 | return 0; 11 | } 12 | 13 | void in_process(uint32_t key) 14 | { 15 | char output[2] = {'\0', '\0'}; 16 | 17 | if (!(key & FLAG_EXT)) { 18 | output[0] = key & 0xFF; 19 | printk("%s", output); 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /libs/common.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_COMMON_H 2 | #define INCLUDE_COMMON_H 3 | 4 | #include "types.h" 5 | 6 | // 输出字节 7 | void outb(uint16_t port, uint8_t value); 8 | // 输入字节 9 | uint8_t inb(uint16_t port); 10 | // 输入字 11 | uint16_t inw(uint16_t port); 12 | // 开中断 13 | void sti(void); 14 | // 关中断 15 | void cli(void); 16 | // 读 eflags 17 | uint32_t read_eflags(void); 18 | // 写 eflags 19 | void write_eflags(uint32_t eflags); 20 | // 刷新页表 21 | void invlpg(void *addr); 22 | 23 | #endif // INCLUDE_COMMON_H 24 | -------------------------------------------------------------------------------- /libs/string.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_STRING_H 2 | #define INCLUDE_STRING_H 3 | 4 | #include "types.h" 5 | 6 | void memcpy(uint8_t *dest, const uint8_t *src, uint32_t len); 7 | 8 | void memset(void *dest, uint8_t val, uint32_t len); 9 | 10 | void bzero(void *dest, uint32_t len); 11 | 12 | int strcmp(const char *str1, const char *str2); 13 | 14 | char *strcpy(char *dest, const char *src); 15 | 16 | char *strcat(char *dest, const char *src); 17 | 18 | int strlen(const char *src); 19 | 20 | #endif // INCLUDE_STRING_H 21 | -------------------------------------------------------------------------------- /kern/mm/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_HEAP_H 2 | #define INCLUDE_HEAP_H 3 | 4 | #include "types.h" 5 | 6 | // 堆起始地址,内核页表映射区域为 0xC0000000 ~ 0xDFFFFFFF 7 | #define HEAP_START 0xE0000000 8 | 9 | // 内存块管理结构 10 | typedef 11 | struct header { 12 | struct header *prev; 13 | struct header *next; 14 | uint32_t allocated : 1; // 内存是否被申请 15 | uint32_t length : 31; 16 | } header_t; 17 | 18 | // 初始化堆 19 | void init_heap(); 20 | 21 | void *kmalloc(uint32_t len); 22 | 23 | void kfree(void *p); 24 | 25 | #endif // INCLUDE_HEAP_H 26 | -------------------------------------------------------------------------------- /kern/schedule/sched.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_SCHEDULE_H 2 | #define INCLUDE_SCHEDULE_H 3 | 4 | #include "proc.h" 5 | 6 | // 运行队列, 等待队列 7 | extern proc_struct_t *running_proc_head; 8 | extern proc_struct_t *wait_proc_head; 9 | extern proc_struct_t *current; 10 | 11 | // 初始化进程调度 12 | void init_sched(); 13 | 14 | // 进程调度 15 | void schedule(); 16 | 17 | // 切换 PCB 18 | void change_task_to(proc_struct_t *next); 19 | 20 | // 切换上下文 21 | void switch_to(context_t *prev, context_t *next); 22 | 23 | // read eip 24 | uint32_t read_eip(); 25 | 26 | #endif // INCLUDE_SCHEDULE_H 27 | -------------------------------------------------------------------------------- /kern/driver/clock.c: -------------------------------------------------------------------------------- 1 | #include "clock.h" 2 | #include "idt.h" 3 | #include "common.h" 4 | #include "debug.h" 5 | #include "sched.h" 6 | 7 | // 时间片轮转调度 8 | void timer_callback(pt_regs_t *regs) 9 | { 10 | schedule(); 11 | } 12 | 13 | void init_timer(uint32_t frequency) 14 | { 15 | register_interrupt_handler(32, timer_callback); 16 | 17 | // 8253/8254 端口地址是 40h~43h 18 | uint32_t divisor = (1193182 + frequency / 2) / frequency; 19 | 20 | // 初始化 8253/8254,方式2,0 0 | 1 1 | 0 1 1 | 0 21 | outb(0x43, 0x36); 22 | // 先写入低字节,在写入高字节 23 | outb(0x40, (uint8_t)(divisor % 256)); 24 | outb(0x40, (uint8_t)(divisor / 256)); 25 | } 26 | -------------------------------------------------------------------------------- /kern/schedule/switchasm.S: -------------------------------------------------------------------------------- 1 | [global switch_to] 2 | 3 | switch_to: 4 | ; 保存现场 5 | mov eax, [esp + 4] 6 | 7 | mov [eax + 0], esp 8 | mov [eax + 4], ebp 9 | mov [eax + 8], ebx 10 | mov [eax + 12], esi 11 | mov [eax + 16], edi 12 | ; 保存标志寄存器 13 | pushf 14 | pop ecx 15 | mov [eax + 20], ecx 16 | 17 | ; 加载新环境 18 | mov eax, [esp + 8] 19 | 20 | mov esp, [eax + 0] 21 | mov ebp, [eax + 4] 22 | mov ebx, [eax + 8] 23 | mov esi, [eax + 12] 24 | mov edi, [eax + 16] 25 | mov eax, [eax + 20] 26 | push eax 27 | popf 28 | 29 | ret 30 | 31 | [global read_eip] 32 | read_eip: 33 | pop eax 34 | jmp eax 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # os-9527 2 | 3 | Mini Operating System Kernel 4 | 5 | ## Modules 6 | - BootLoader 7 | - VGA Driver 8 | - Interrupt Handlers 9 | - Keyboard Driver 10 | - Physical Memory Management 11 | - Virtual Memory Management 12 | - Kernel Process 13 | - Process Scheduling 14 | 15 | ## Requirements 16 | - Linux 17 | - GCC 18 | - NASM 19 | - QEMU 20 | 21 | ## Build 22 | ``` 23 | sudo apt-get install build-essential nasm gdb qemu 24 | sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu 25 | sudo mkdir /mnt/floppy 26 | make 27 | ``` 28 | 29 | ## Usage 30 | ``` 31 | make qemu 32 | ``` 33 | 34 | ## Screenshot 35 | ![](https://github.com/hijkzzz/os-9527/blob/master/test.jpg?raw=true) 36 | 37 | ## References 38 | - JamesM's kernel development tutorials 39 | - uCore OS 40 | -------------------------------------------------------------------------------- /kern/debug/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_DEBUG_H 2 | #define INCLUDE_DEBUG_H 3 | 4 | #include "console.h" 5 | #include "vargs.h" 6 | #include "elf.h" 7 | 8 | #define assert(x, info) \ 9 | do { \ 10 | if (!(x)) { \ 11 | panic(info); \ 12 | } \ 13 | } while (0) 14 | 15 | // 编译期间静态检测 16 | #define static_assert(x) \ 17 | switch (x) { case 0: case (x): ; } 18 | 19 | // 初始化 Debug 信息 20 | void init_debug(); 21 | 22 | // 打印当前的函数调用栈信息 23 | void panic(const char *msg); 24 | 25 | // 打印当前的段存器值 26 | void print_cur_status(); 27 | 28 | // 内核的打印函数 29 | void printk(const char *format, ...); 30 | 31 | // 内核的打印函数 带颜色 32 | void printk_color(real_color_t back, real_color_t fore, const char *format, ...); 33 | 34 | #endif //INCLUDE_DEBUG_H 35 | -------------------------------------------------------------------------------- /kern/mm/gdtasm.S: -------------------------------------------------------------------------------- 1 | [GLOBAL gdt_flush] 2 | 3 | gdt_flush: 4 | mov eax, [esp + 4] 5 | lgdt [eax] 6 | 7 | ; 更新数据段 8 | ; 内核数据段,索引号为 2 9 | mov ax, 0x10 10 | mov ds, ax 11 | mov ss, ax 12 | mov es, ax 13 | mov fs, ax 14 | mov gs, ax 15 | 16 | ; 更新代码段 17 | ; 内核代码段,索引号为 1 18 | jmp 0x08:.flush 19 | 20 | .flush 21 | ret 22 | 23 | [GLOBAL tss_flush] ; Allows our C code to call tss_flush(). 24 | tss_flush: 25 | mov ax, 0x2B ; Load the index of our TSS structure - The index is 26 | ; 0x28, as it is the 5th selector and each is 8 bytes 27 | ; long, but we set the bottom two bits (making 0x2B) 28 | ; so that it has an RPL of 3, not zero. 29 | ltr ax ; Load 0x2B into the task state register. 30 | ret 31 | -------------------------------------------------------------------------------- /kern/mm/pmm.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_PMM_H 2 | #define INCLUDE_PMM_H 3 | 4 | #include "multiboot.h" 5 | 6 | // 内核在内存中的起始和结束地址 7 | // 由链接脚本定义 8 | // 只是个单纯的符号,用下面的形式声明 9 | extern uint8_t kern_start[]; 10 | extern uint8_t kern_end[]; 11 | 12 | #define STACK_SIZE 8192 13 | #define KERN_STACK_SIZE 2048 // 内核栈大小 14 | #define PMM_MAX_SIZE 0x20000000 // 可用内存 512MB 15 | #define PMM_PAGE_SIZE 0x1000 // 页面大小 4KB 16 | #define PAGE_MAX_SIZE (PMM_MAX_SIZE / PMM_PAGE_SIZE) 17 | 18 | // 动态分配物理页面数 19 | extern uint32_t phy_page_count; 20 | 21 | // 内核栈 22 | extern char kern_stack[]; 23 | extern uint32_t kern_stack_top; 24 | 25 | // 初始化内存管理 26 | void init_pmm(); 27 | 28 | // 分配页面 29 | uint32_t pmm_alloc_page(); 30 | 31 | // 释放页面 32 | void pmm_free_page(uint32_t p); 33 | 34 | // 内存分布状态 35 | void show_memory_map(); 36 | 37 | #endif // INCLUDE_PMM_H 38 | -------------------------------------------------------------------------------- /kern/driver/console.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_CONSOLE_H 2 | #define INCLUDE_CONSOLE_H 3 | 4 | #include "types.h" 5 | // 颜色定义 6 | typedef 7 | enum real_color{ 8 | rc_black = 0, 9 | rc_blue = 1, 10 | rc_green = 2, 11 | rc_cyan = 3, 12 | rc_red = 4, 13 | rc_magenta = 5, 14 | rc_brown = 6, 15 | rc_light_grey = 7, 16 | rc_dark_grey = 8, 17 | rc_light_blue = 9, 18 | rc_light_green = 10, 19 | rc_light_cyan = 11, 20 | rc_light_red = 12, 21 | rc_light_magenta = 13, 22 | rc_light_brown = 14, 23 | rc_white = 15 24 | } real_color_t; 25 | 26 | // 输出清空 27 | void console_clear(); 28 | 29 | // 输出字符 30 | void console_putc_color(char c, real_color_t back, real_color_t fore); 31 | 32 | // 输出字符串 33 | void console_write(char *cstr); 34 | 35 | // 输出带颜色的字符串 36 | void console_write_color(char *cstr, real_color_t back, real_color_t fore); 37 | 38 | #endif // INCLUDE_CONSOLE_H 39 | -------------------------------------------------------------------------------- /libs/common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | inline void outb(uint16_t port, uint8_t value) 4 | { 5 | asm volatile("outb %1, %0" : : "dN"(port), "a"(value)); 6 | } 7 | 8 | inline uint8_t inb(uint16_t port) 9 | { 10 | uint8_t ret; 11 | asm volatile("inb %1, %0" : "=a"(ret) : "dN"(port)); 12 | return ret; 13 | } 14 | 15 | inline uint16_t inw(uint16_t port) 16 | { 17 | uint16_t ret; 18 | asm volatile("inw %1, %0" : "=a"(ret) : "dN"(port)); 19 | return ret; 20 | } 21 | 22 | inline void sti(void) 23 | { 24 | asm volatile ("sti"); 25 | } 26 | 27 | inline void cli(void) 28 | { 29 | asm volatile ("cli" ::: "memory"); 30 | } 31 | 32 | inline uint32_t read_eflags(void) 33 | { 34 | uint32_t eflags; 35 | asm volatile ("pushfl; popl %0" : "=r" (eflags)); 36 | return eflags; 37 | } 38 | 39 | inline void write_eflags(uint32_t eflags) 40 | { 41 | asm volatile ("pushl %0; popfl" :: "r" (eflags)); 42 | } 43 | 44 | inline void 45 | invlpg(void *addr) { 46 | asm volatile ("invlpg (%0)" :: "r" (addr) : "memory"); 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /libs/types.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_TYPES_H 2 | #define INCLUDE_TYPES_H 3 | 4 | #ifndef NULL 5 | #define NULL 0 6 | #endif 7 | 8 | #ifndef TRUE 9 | #define TRUE 1 10 | #define FALSE 0 11 | #endif 12 | 13 | typedef unsigned int uint32_t; 14 | typedef int int32_t; 15 | typedef unsigned short uint16_t; 16 | typedef short int16_t; 17 | typedef unsigned char uint8_t; 18 | typedef char int8_t; 19 | 20 | typedef char bool; 21 | typedef uint32_t size_t; 22 | typedef uint32_t pid_t; 23 | 24 | // 向下取整 25 | #define ROUNDDOWN(a, n) ({ \ 26 | size_t __a = (size_t)(a); \ 27 | (typeof(a))(__a - __a % (n)); \ 28 | }) 29 | 30 | #define ROUNDUP(a, n) ({ \ 31 | size_t __n = (size_t)(n); \ 32 | (typeof(a))(ROUNDDOWN((size_t)(a) + __n - 1, __n)); \ 33 | }) 34 | 35 | #endif // INCLUDE_TYPES_H 36 | -------------------------------------------------------------------------------- /tools/kernel.ld: -------------------------------------------------------------------------------- 1 | ENTRY(start) 2 | SECTIONS 3 | { 4 | PROVIDE( kern_start = 0xC0100000 ); 5 | . = 0x100000; 6 | .init.text : 7 | { 8 | *(.init.text) 9 | . = ALIGN(4096); 10 | } 11 | .init.data : 12 | { 13 | *(.init.data) 14 | . = ALIGN(4096); 15 | } 16 | /* 内核虚拟地址偏移 0xC0000000 */ 17 | . += 0xC0000000; 18 | .text : AT(ADDR(.text) - 0xC0000000) 19 | { 20 | *(.text) 21 | . = ALIGN(4096); 22 | } 23 | .data : AT(ADDR(.data) - 0xC0000000) 24 | { 25 | *(.data) 26 | *(.rodata) 27 | . = ALIGN(4096); 28 | } 29 | .bss : AT(ADDR(.bss) - 0xC0000000) 30 | { 31 | *(.bss) 32 | . = ALIGN(4096); 33 | } 34 | .stab : AT(ADDR(.stab) - 0xC0000000) 35 | { 36 | *(.stab) 37 | . = ALIGN(4096); 38 | } 39 | .stabstr : AT(ADDR(.stabstr) - 0xC0000000) 40 | { 41 | *(.stabstr) 42 | . = ALIGN(4096); 43 | } 44 | PROVIDE( kern_end = . ); 45 | /DISCARD/ : { *(.comment) *(.eh_frame) } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /libs/elf.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_ELF_H 2 | #define INCLUDE_ELF_H 3 | 4 | #include "types.h" 5 | #include "multiboot.h" 6 | 7 | #define ELF32_ST_TYPE(i) ((i)&0xf) 8 | 9 | // ELF 段头 10 | typedef 11 | struct elf_section_header_t { 12 | uint32_t name; 13 | uint32_t type; 14 | uint32_t flags; 15 | uint32_t addr; 16 | uint32_t offset; 17 | uint32_t size; 18 | uint32_t link; 19 | uint32_t info; 20 | uint32_t addralign; 21 | uint32_t entsize; 22 | } __attribute__((packed)) elf_section_header_t; 23 | 24 | // ELF 符号表项 25 | typedef 26 | struct elf_symbol_t { 27 | uint32_t name; 28 | uint32_t value; 29 | uint32_t size; 30 | uint8_t info; 31 | uint8_t other; 32 | uint16_t shndx; 33 | } __attribute__((packed)) elf_symbol_t; 34 | 35 | // ELF 信息,即符号表和字符串表 36 | typedef 37 | struct elf_t { 38 | elf_symbol_t *symtab; 39 | uint32_t symtabsz; 40 | const char *strtab; 41 | uint32_t strtabsz; 42 | } elf_t; 43 | 44 | // 从 multiboot_t 结构获取 ELF 信息 45 | elf_t elf_from_multiboot(multiboot_t *mb); 46 | 47 | // 查看 ELF 的符号信息 48 | const char *elf_lookup_symbol(uint32_t addr, elf_t *elf); 49 | 50 | #endif // INCLUDE_ELF_H 51 | -------------------------------------------------------------------------------- /libs/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | inline void memcpy(uint8_t *dest, const uint8_t *src, uint32_t len) 4 | { 5 | for (; len != 0; --len) { 6 | *dest++ = *src++; 7 | } 8 | } 9 | 10 | inline void memset(void *dest, uint8_t val, uint32_t len) 11 | { 12 | uint8_t *dst = (uint8_t *)dest; 13 | 14 | for ( ; len != 0; --len) { 15 | *dst++ = val; 16 | } 17 | } 18 | 19 | inline void bzero(void *dest, uint32_t len) 20 | { 21 | memset(dest, 0, len); 22 | } 23 | 24 | inline int strcmp(const char *str1, const char *str2) 25 | { 26 | //找到第一个不相同的字符 27 | while (*str1 != '\0' && *str1 == *str2) { 28 | ++str1; 29 | ++str2; 30 | } 31 | return (uint8_t)*str1 - (uint8_t)*str2; 32 | } 33 | 34 | inline char *strcpy(char *dest, const char *src) 35 | { 36 | char *p = dest; 37 | while ((*p++ = *src++) != '\0'); 38 | return dest; 39 | } 40 | 41 | inline char *strcat(char *dest, const char *src) 42 | { 43 | return strcpy(dest + strlen(dest), src); 44 | } 45 | 46 | inline int strlen(const char *s) { 47 | int cnt = 0; 48 | while (*s++ != '\0') { 49 | ++cnt; 50 | } 51 | return cnt; 52 | } 53 | -------------------------------------------------------------------------------- /boot/bootasm.S: -------------------------------------------------------------------------------- 1 | ; 1. CS 指向基地址为 0x00000000, 限长为 4G – 1 的代码段描述符 2 | ; 2. DS ,SS ,ES ,FS ,GS 指向基地址为0x00000000, 限长为 4G – 1 的数据段描述符 3 | ; 3. A20 地址线已经打开 4 | ; 4. 页机制被禁止 5 | ; 5. 中断被禁止 6 | ; 6. EAX = 0x2BADB002 MBOOT_HEADER_MAGIC equ 0x1BADB002 7 | ; 7. 系统信息和启动信息块的线性地址保存在 EBX中 8 | 9 | MBOOT_HEADER_MAGIC equ 0x1BADB002 10 | MBOOT_PAGE_ALIGN equ 1 << 0 11 | MBOOT_MEM_INFO equ 1 << 1 12 | MBOOT_HEADER_FLAGS equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO 13 | MBOOT_CHEKSUM equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS) 14 | 15 | [BITS 32] 16 | section .init.text 17 | 18 | ; 在代码段的起始位置设置符合 Multiboot 规范的标记 19 | dd MBOOT_HEADER_MAGIC; 20 | dd MBOOT_HEADER_FLAGS; 21 | dd MBOOT_CHEKSUM; 22 | 23 | [GLOBAL start] 24 | [GLOBAL tmp_mboot_ptr] ; 声明临时 multiboot 结构体指针 25 | [EXTERN kern_entry] ; C 代码的入口函数 26 | 27 | start: 28 | cli 29 | mov esp, STACK_TOP 30 | mov ebp, 0 31 | and esp, 0FFFFFFF0H ; 栈顶对齐 16 字节,提升储存效率 32 | mov [tmp_mboot_ptr], ebx ; 保存 multiboot 结构体指针 33 | call kern_entry 34 | 35 | ; 开启分页前的临时数据段 36 | section .init.data 37 | stack: 38 | times 1024 db 0 ; 临时内核栈大小 1024B 39 | STACK_TOP equ $ - stack - 1 ; 临时内核栈栈顶 40 | 41 | tmp_mboot_ptr: 42 | dd 0 43 | -------------------------------------------------------------------------------- /kern/schedule/sched.c: -------------------------------------------------------------------------------- 1 | #include "sched.h" 2 | #include "common.h" 3 | #include "proc.h" 4 | #include "pmm.h" 5 | #include "heap.h" 6 | #include "debug.h" 7 | #include "gdt.h" 8 | 9 | proc_struct_t *running_proc_head = NULL; 10 | proc_struct_t *wait_proc_head = NULL; 11 | proc_struct_t *current = NULL; 12 | 13 | // 初始化进程调度 14 | void init_sched() 15 | { 16 | // 初始化 idleproc 进程 17 | current = (proc_struct_t *)(kern_stack_top - STACK_SIZE); 18 | current->state = TASK_RUNNABLE; 19 | current->pid = now_pid++; 20 | set_proc_name(current, "idleproc"); 21 | current->cr3 = (uint32_t)pgd_kern - PAGE_OFFSET; 22 | current->kstack = (uint32_t)kern_stack + STACK_SIZE; 23 | 24 | // 双向循环链表 25 | current->next = current; 26 | current->prev = current; 27 | running_proc_head = current; 28 | } 29 | 30 | // 进程调度 31 | void schedule() 32 | { 33 | if (current) { 34 | change_task_to(current->next); 35 | } 36 | } 37 | 38 | // 切换 PCB 39 | void change_task_to(proc_struct_t *next) 40 | { 41 | if (current != next) { 42 | proc_struct_t *prev = current; 43 | current = next; 44 | // 切换内核堆和页表 45 | set_kernel_stack(next->kstack); 46 | switch_pgd(next->cr3); 47 | switch_to(&(prev->context), &(current->context)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kern/debug/debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | 3 | static void print_stack_trace(); 4 | 5 | static elf_t kernel_elf; 6 | 7 | void init_debug() 8 | { 9 | // 获取 ELF 信息 10 | kernel_elf = elf_from_multiboot(glb_mboot_ptr); 11 | } 12 | 13 | void print_cur_status() 14 | { 15 | static int round = 0; 16 | uint16_t reg1, reg2, reg3, reg4; 17 | 18 | asm volatile ( "mov %%cs, %0;" 19 | "mov %%ds, %1;" 20 | "mov %%es, %2;" 21 | "mov %%ss, %3;" 22 | : "=m"(reg1), "=m"(reg2), "=m"(reg3), "=m"(reg4)); 23 | 24 | // 打印当前的运行级别 25 | printk("%d: @ring %d\n", round, reg1 & 0x3); 26 | printk("%d: cs = %x\n", round, reg1); 27 | printk("%d: ds = %x\n", round, reg2); 28 | printk("%d: es = %x\n", round, reg3); 29 | printk("%d: ss = %x\n", round, reg4); 30 | ++round; 31 | } 32 | 33 | void panic(const char *msg) 34 | { 35 | printk("*** System panic: %s\n", msg); 36 | print_stack_trace(); 37 | printk("***\n"); 38 | 39 | // 致命错误发生后打印栈信息后停止在这里 40 | while(1); 41 | } 42 | 43 | void print_stack_trace() 44 | { 45 | uint32_t *ebp, *eip; 46 | 47 | asm volatile ("mov %%ebp, %0" : "=r" (ebp)); 48 | // ebp 的初始值为 0 49 | while (ebp) { 50 | eip = ebp + 1; 51 | // 打印函数名 52 | printk(" [0x%x] %s\n", *eip, elf_lookup_symbol(*eip, &kernel_elf)); 53 | ebp = (uint32_t*)*ebp; 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /libs/elf.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "string.h" 3 | #include "elf.h" 4 | #include "vmm.h" 5 | 6 | // 从 multiboot_t 结构获取 ELF 符号 7 | elf_t elf_from_multiboot(multiboot_t *mb) 8 | { 9 | elf_t elf; 10 | elf_section_header_t *sh = (elf_section_header_t*)mb->addr; 11 | // 段字符串表地址 12 | uint32_t shstrtab = sh[mb->shndx].addr; 13 | 14 | // 寻找 字符串表,符号表 存入 elf_t 15 | for (int i = 0; i < mb->num; i++) { 16 | // sh[i].name 是一个偏移值,加上内核偏移地址 17 | const char *name = (const char *)(shstrtab + sh[i].name) + PAGE_OFFSET; 18 | if (strcmp(name, ".strtab") == 0) { 19 | elf.strtab = (const char *)sh[i].addr + PAGE_OFFSET; 20 | elf.strtabsz = sh[i].size; 21 | } 22 | if (strcmp(name, ".symtab") == 0) { 23 | elf.symtab = (elf_symbol_t*)sh[i].addr + PAGE_OFFSET; 24 | elf.symtabsz = sh[i].size; 25 | } 26 | } 27 | 28 | return elf; 29 | } 30 | 31 | // 查看 ELF 的符号信息 32 | const char *elf_lookup_symbol(uint32_t addr, elf_t *elf) 33 | { 34 | for (int i = 0; i < (elf->symtabsz / sizeof(elf_symbol_t)); i++) { 35 | // 判断是否为函数符号 36 | if (ELF32_ST_TYPE(elf->symtab[i].info) != 0x2) { 37 | continue; 38 | } 39 | // 通过函数调用地址查到函数的名字,符号表保存的是虚拟地址 40 | if ( (addr >= elf->symtab[i].value) && (addr < (elf->symtab[i].value + elf->symtab[i].size)) ) { 41 | return (const char *)((uint32_t)elf->strtab + elf->symtab[i].name); 42 | } 43 | } 44 | 45 | return NULL; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | C_SOURCES = $(shell find . -name "*.c") 2 | C_OBJECTS = $(patsubst %.c, %.o, $(C_SOURCES)) 3 | S_SOURCES = $(shell find . -name "*.S") 4 | S_OBJECTS = $(patsubst %.S, %.o, $(S_SOURCES)) 5 | 6 | CC = gcc 7 | LD = ld 8 | ASM = nasm 9 | 10 | C_FLAGS = -c -Wall -m32 -ggdb -gstabs+ -nostdinc -fno-builtin -fno-stack-protector 11 | LD_FLAGS = -T tools/kernel.ld -m elf_i386 -nostdlib 12 | ASM_FLAGS = -f elf -g -F stabs 13 | 14 | C_INCLUDE = libs/ \ 15 | kern/debug/ \ 16 | kern/driver/ \ 17 | kern/mm/ \ 18 | kern/trap/ \ 19 | kern/process \ 20 | kern/schedule \ 21 | 22 | C_FLAGS += $(addprefix -I,$(C_INCLUDE)) 23 | 24 | all: $(S_OBJECTS) $(C_OBJECTS) link update_image 25 | 26 | .c.o: 27 | @echo gcc $< 28 | $(CC) $(C_FLAGS) $< -o $@ 29 | 30 | .S.o: 31 | @echo nasm $< 32 | $(ASM) $(ASM_FLAGS) $< 33 | 34 | link: 35 | @echo ld 36 | $(LD) $(LD_FLAGS) $(S_OBJECTS) $(C_OBJECTS) -o kernel 37 | 38 | .PHONY:clean 39 | clean: 40 | $(RM) $(S_OBJECTS) $(C_OBJECTS) kernel 41 | 42 | .PHONY:update_image 43 | update_image: 44 | sudo mount floppy.img /mnt/floppy 45 | sudo cp kernel /mnt/floppy 46 | sleep 1 47 | sudo umount /mnt/floppy 48 | 49 | .PHONY:mount_image 50 | mount_image: 51 | sudo mount floppy.img /mnt/floppy 52 | 53 | .PHONY:umount_image 54 | umount_image: 55 | sudo umount /mnt/floppy 56 | 57 | .PHONY:qemu 58 | qemu: 59 | qemu -fda floppy.img -boot a -m 256 60 | 61 | .PHONY:debug 62 | debug: 63 | qemu -S -s -fda floppy.img -boot a -m 256 & 64 | sleep 1 65 | cgdb -x tools/gdbinit 66 | 67 | -------------------------------------------------------------------------------- /kern/process/proc.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_PROC_H 2 | #define INCLUDE_PROC_H 3 | 4 | #include "types.h" 5 | #include "vmm.h" 6 | 7 | #define PROC_NAME_LEN 15 8 | #define MAX_PROCESS 4096 9 | #define MAX_PID (MAX_PROCESS * 2) 10 | 11 | // 进程状态 12 | typedef 13 | enum task_state { 14 | TASK_UNINIT = 0, 15 | TASK_SLEEPING = 1, 16 | TASK_RUNNABLE = 2, 17 | TASK_ZOMBIE = 3, 18 | } task_state_t; 19 | 20 | typedef 21 | struct context { 22 | uint32_t esp; 23 | uint32_t ebp; 24 | uint32_t ebx; 25 | uint32_t esi; 26 | uint32_t edi; 27 | uint32_t eflags; 28 | } context_t; 29 | 30 | // 进程控制块 31 | typedef 32 | struct proc_struct { 33 | volatile task_state_t state; // 运行状态 34 | volatile bool need_resched; // 进程需要被调度 35 | context_t context; // 上下文 36 | uint32_t kstack; // 内核栈 37 | uint32_t cr3; // 页目录 38 | pid_t pid; // 进程号 39 | char name[PROC_NAME_LEN + 1]; // 进程名 40 | uint32_t flags; // 进程标志 41 | struct proc_struct *parent; // 父进程 42 | 43 | struct proc_struct *prev; // 双向循环链表 44 | struct proc_struct *next; 45 | } proc_struct_t; 46 | 47 | 48 | extern pid_t now_pid; 49 | 50 | // 内核线程创建 51 | int32_t kernel_thread(int (*fn)(void *), void *arg); 52 | 53 | // 内核线程退出 54 | void kthread_exit(); 55 | 56 | // 设置进程名 57 | char *set_proc_name(struct proc_struct *proc, const char *name); 58 | 59 | // 获取进程 id 60 | int32_t getpid(); 61 | 62 | #endif // INCLUDE_PROC_H 63 | -------------------------------------------------------------------------------- /libs/multiboot.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_MULTIBOOT_H 2 | #define INCLUDE_MULTIBOOT_H 3 | 4 | #include "types.h" 5 | 6 | typedef 7 | struct multiboot_t { 8 | uint32_t flags; // Multiboot 的版本信息 9 | uint32_t mem_lower; // 从 BIOS 获知的可用内存 10 | uint32_t mem_upper; 11 | 12 | uint32_t boot_device; // 指出引导程序从哪个BIOS磁盘设备载入的OS映像 13 | uint32_t cmdline; // 内核命令行 14 | uint32_t mods_count; // boot 模块列表 15 | uint32_t mods_addr; 16 | 17 | uint32_t num; // ELF 格式内核映像的 section 头表 18 | uint32_t size; 19 | uint32_t addr; 20 | uint32_t shndx; // 段字符串表索引 21 | 22 | // 内存分布的缓冲区的地址和长度 23 | uint32_t mmap_length; 24 | uint32_t mmap_addr; 25 | 26 | uint32_t drives_length; // 指出第一个驱动器结构的物理地址 27 | uint32_t drives_addr; // 指出第一个驱动器这个结构的大小 28 | uint32_t config_table; // ROM 配置表 29 | uint32_t boot_loader_name; // boot loader 的名字 30 | uint32_t apm_table; // APM 表 31 | uint32_t vbe_control_info; 32 | uint32_t vbe_mode_info; 33 | uint32_t vbe_mode; 34 | uint32_t vbe_interface_seg; 35 | uint32_t vbe_interface_off; 36 | uint32_t vbe_interface_len; 37 | } __attribute__((packed)) multiboot_t; 38 | 39 | typedef 40 | struct mmap_entry_t { 41 | uint32_t size; // size 是不含 size 自身变量的大小 42 | uint32_t base_addr_low; 43 | uint32_t base_addr_high; 44 | uint32_t length_low; 45 | uint32_t length_high; 46 | uint32_t type; 47 | } __attribute__((packed)) mmap_entry_t; 48 | 49 | // 临时 multiboot 结构体指针 50 | extern multiboot_t *tmp_mboot_ptr; 51 | // 建立页表后的 multiboot 结构体指针 52 | extern multiboot_t *glb_mboot_ptr; 53 | 54 | #endif // INCLUDE_MULTIBOOT_H 55 | -------------------------------------------------------------------------------- /kern/process/proc.c: -------------------------------------------------------------------------------- 1 | #include "proc.h" 2 | #include "sched.h" 3 | #include "pmm.h" 4 | #include "vmm.h" 5 | #include "gdt.h" 6 | #include "heap.h" 7 | #include "debug.h" 8 | #include "string.h" 9 | #include "common.h" 10 | 11 | pid_t now_pid = 0; 12 | 13 | int32_t kernel_thread(int (*fn)(void *), void *arg) 14 | { 15 | proc_struct_t *new_proc = (proc_struct_t *)kmalloc(STACK_SIZE); 16 | assert(new_proc != NULL, "kernel_thread : kmalloc error\n"); 17 | 18 | bzero(new_proc, sizeof(proc_struct_t)); 19 | 20 | // 内核线程共享内核页表 21 | new_proc->cr3 = (uint32_t)pgd_kern - PAGE_OFFSET; 22 | new_proc->state = TASK_RUNNABLE; 23 | new_proc->pid = now_pid++; 24 | set_proc_name(new_proc, ""); 25 | new_proc->kstack = (uint32_t)new_proc + STACK_SIZE; 26 | 27 | // 填写函数调用栈 28 | uint32_t *stack_top = (uint32_t *)((uint32_t)new_proc + STACK_SIZE); 29 | *(--stack_top) = (uint32_t)arg; 30 | *(--stack_top) = (uint32_t)kthread_exit; 31 | *(--stack_top) = (uint32_t)fn; 32 | 33 | new_proc->context.esp = (uint32_t)new_proc + STACK_SIZE - 3 * sizeof(uint32_t); 34 | // 开中断 35 | new_proc->context.eflags = 0x200; 36 | 37 | // 插入调度链表 38 | new_proc->next = running_proc_head; 39 | new_proc->prev = running_proc_head->prev; 40 | running_proc_head->prev->next = new_proc; 41 | running_proc_head->prev = new_proc; 42 | 43 | return new_proc->pid; 44 | } 45 | 46 | void kthread_exit() 47 | { 48 | // 暂时没有释放内存 49 | register uint32_t val asm("eax"); 50 | printk("Thread exited with value %d", val); 51 | } 52 | 53 | char * set_proc_name(struct proc_struct *proc, const char *name) { 54 | bzero(proc->name, sizeof(proc->name)); 55 | return strcpy(proc->name, name); 56 | } 57 | 58 | int32_t getpid() 59 | { 60 | return current->pid; 61 | } 62 | -------------------------------------------------------------------------------- /kern/mm/vmm.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_VMM_H 2 | #define INCLUDE_VMM_H 3 | 4 | #include "types.h" 5 | #include "idt.h" 6 | 7 | // 偏移地址 8 | #define PAGE_OFFSET 0xC0000000 9 | 10 | // 页表项 [31~12 页帧地址, 3 AVL, 00, D, A, 00, U/S, R/W, P] 11 | // A 访问位,D 修改位,P 存在位 12 | #define PAGE_PRESENT 0x1 13 | #define PAGE_WRITE 0x2 14 | #define PAGE_USER 0x4 15 | 16 | // 页帧大小 17 | #define PAGE_SIZE 4096 18 | 19 | // 地址转换为目录项索引,3FF == 1111111111 20 | #define PGD_INDEX(x) (((x) >> 22) & 0x3FF) 21 | // 地址转换为页表项索引 22 | #define PTE_INDEX(x) (((x) >> 12) & 0x3FF) 23 | 24 | // 获取页内偏移 25 | #define OFFSET_INDEX(x) ((x) & 0xFFF) 26 | 27 | // 页目录项、页表项 类型 28 | typedef uint32_t pgd_t; 29 | typedef uint32_t pte_t; 30 | 31 | typedef struct page_helper 32 | { 33 | uint32_t present : 1; // Page present in memory 34 | uint32_t rw : 1; // Read-only if clear, readwrite if set 35 | uint32_t user : 1; // Supervisor level only if clear 36 | uint32_t accessed : 1; // Has the page been accessed since last refresh? 37 | uint32_t dirty : 1; // Has the page been written to since last refresh? 38 | uint32_t unused : 7; // Amalgamation of unused and reserved bits 39 | uint32_t frame : 20; // Frame address (shifted right 12 bits) 40 | } page_helper_t; 41 | 42 | // 页表成员数 43 | #define PGD_SIZE (PAGE_SIZE / sizeof(pgd_t)) 44 | #define PTE_SIZE (PAGE_SIZE / sizeof(pte_t)) 45 | 46 | // 页表数,512 MB / 4MB 47 | #define PTE_COUNT (512 / 4) 48 | 49 | // 页目录区域 50 | extern pgd_t pgd_kern[PGD_SIZE]; 51 | 52 | // 初始化页表 53 | void init_vmm(); 54 | 55 | // 更换当前页目录 56 | void switch_pgd(uint32_t pd); 57 | 58 | // 映射虚拟地址到物理地址,flags 为页权限 59 | void map(pgd_t *pgd_now, uint32_t va, uint32_t pa, uint32_t flags); 60 | 61 | // 取消映射 62 | void unmap(pgd_t *pgd_now, uint32_t va); 63 | 64 | // 获取映射的物理地址 65 | uint32_t get_mapping(pgd_t *pgd_now, uint32_t va, uint32_t *pa); 66 | 67 | // 缺页异常处理 68 | void page_fault(pt_regs_t *regs); 69 | 70 | #endif // INCLUDE_VMM_H 71 | -------------------------------------------------------------------------------- /kern/mm/gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_GDT_H 2 | #define INCLUDE_GDT_H 3 | 4 | #include "types.h" 5 | 6 | typedef 7 | struct gdt_entry_t { 8 | uint16_t limit_low; 9 | uint16_t base_low; 10 | uint8_t base_middle; 11 | uint8_t access; //1 P, 2 DPL, 1, 4 Type 12 | uint8_t granularity; 13 | uint8_t base_high; 14 | } __attribute__((packed)) gdt_entry_t; 15 | 16 | typedef 17 | struct gdt_ptr_t { 18 | uint16_t limit; 19 | uint32_t base; 20 | 21 | } __attribute__((packed)) gdt_ptr_t; 22 | 23 | // A struct describing a Task State Segment. 24 | typedef 25 | struct tss_entry 26 | { 27 | uint32_t prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list. 28 | uint32_t esp0; // The stack pointer to load when we change to kernel mode. 29 | uint32_t ss0; // The stack segment to load when we change to kernel mode. 30 | uint32_t esp1; // Unused... 31 | uint32_t ss1; 32 | uint32_t esp2; 33 | uint32_t ss2; 34 | uint32_t cr3; 35 | uint32_t eip; 36 | uint32_t eflags; 37 | uint32_t eax; 38 | uint32_t ecx; 39 | uint32_t edx; 40 | uint32_t ebx; 41 | uint32_t esp; 42 | uint32_t ebp; 43 | uint32_t esi; 44 | uint32_t edi; 45 | uint32_t es; // The value to load into ES when we change to kernel mode. 46 | uint32_t cs; // The value to load into CS when we change to kernel mode. 47 | uint32_t ss; // The value to load into SS when we change to kernel mode. 48 | uint32_t ds; // The value to load into DS when we change to kernel mode. 49 | uint32_t fs; // The value to load into FS when we change to kernel mode. 50 | uint32_t gs; // The value to load into GS when we change to kernel mode. 51 | uint32_t ldt; // Unused... 52 | uint16_t trap; 53 | uint16_t iomap_base; 54 | } __attribute__((packed)) tss_entry_t; 55 | 56 | // 初始化 57 | void init_gdt(); 58 | 59 | extern void gdt_flush(uint32_t); 60 | extern void tss_flush(); 61 | 62 | // 设置内核栈 63 | void set_kernel_stack(uint32_t stack); 64 | 65 | #endif // INCLUDE_GDT_H 66 | -------------------------------------------------------------------------------- /kern/mm/pmm.c: -------------------------------------------------------------------------------- 1 | #include "pmm.h" 2 | #include "debug.h" 3 | #include "vmm.h" 4 | 5 | // 物理内存页面管理栈 6 | static uint32_t pmm_stack[PAGE_MAX_SIZE + 1]; 7 | static uint32_t pmm_stack_top; 8 | 9 | uint32_t phy_page_count; 10 | 11 | void init_pmm() 12 | { 13 | // GRUB 提供的内存分布信息 14 | uint32_t mmap_addr = glb_mboot_ptr->mmap_addr; 15 | uint32_t mmap_length = glb_mboot_ptr->mmap_length; 16 | 17 | uint32_t phy_kern_end = (uint32_t)kern_end - PAGE_OFFSET; 18 | for (mmap_entry_t *mmap = (mmap_entry_t *)mmap_addr; 19 | (uint32_t)mmap < mmap_addr + mmap_length; ++mmap) { 20 | // 如果是有效物理内存 21 | if (mmap->type == 1) { 22 | uint32_t begin = mmap->base_addr_low < (uint32_t)phy_kern_end ? (uint32_t)phy_kern_end : mmap->base_addr_low; 23 | uint32_t end = mmap->base_addr_low + mmap->length_low; 24 | 25 | begin = ROUNDUP(begin, PMM_PAGE_SIZE); 26 | end = ROUNDDOWN(end, PMM_PAGE_SIZE); 27 | 28 | // 检查是否和内核空间重叠 29 | if (end < (uint32_t)phy_kern_end) { 30 | continue; 31 | } 32 | 33 | while (begin < end) { 34 | pmm_free_page(begin); 35 | begin += PMM_PAGE_SIZE; 36 | phy_page_count++; 37 | } 38 | } 39 | } 40 | } 41 | 42 | uint32_t pmm_alloc_page() 43 | { 44 | assert(pmm_stack_top != 0, "out of memory"); 45 | return pmm_stack[pmm_stack_top--]; 46 | } 47 | 48 | void pmm_free_page(uint32_t p) 49 | { 50 | assert(pmm_stack_top != PAGE_MAX_SIZE, "out of pmm_stack stack"); 51 | pmm_stack[++pmm_stack_top] = p; 52 | } 53 | 54 | void show_memory_map() 55 | { 56 | // GRUB 提供的内存信息 57 | uint32_t mmap_addr = glb_mboot_ptr->mmap_addr; 58 | uint32_t mmap_length = glb_mboot_ptr->mmap_length; 59 | 60 | printk("\nMemory map:\n"); 61 | 62 | for (mmap_entry_t *mmap = (mmap_entry_t *)(mmap_addr + PAGE_OFFSET); 63 | (uint32_t)mmap < (mmap_addr + mmap_length + PAGE_OFFSET); ++mmap) { 64 | printk("base_addr = 0x%08X, length = 0x%08X, type = 0x%X\n", 65 | (uint32_t)mmap->base_addr_low, (uint32_t)mmap->length_low, 66 | (uint32_t)mmap->type); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /kern/driver/console.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "console.h" 3 | #include "vmm.h" 4 | 5 | // 显存地址,加上内核偏移地址 6 | // BUG小记,地址要先相加,再转换成指针 7 | static uint16_t *video_memory = (uint16_t *)(0xB8000 + PAGE_OFFSET); 8 | 9 | // 光标位置 10 | static uint8_t cursor_x = 0; 11 | static uint8_t cursor_y = 0; 12 | 13 | static void move_cursor() 14 | { 15 | uint16_t cursor_location = cursor_y * 80 + cursor_x; 16 | 17 | // 高8位 18 | outb(0x3D4, 14); 19 | outb(0x3D5, cursor_location >> 8); 20 | // 低8位 21 | outb(0x3D4, 15); 22 | outb(0x3D5, cursor_location); 23 | } 24 | 25 | static void scroll() 26 | { 27 | uint8_t attribute_byte = (0 << 4) | (15 & 0x0F); 28 | uint16_t blank = 0x20 | (attribute_byte << 8); 29 | 30 | if (cursor_y >= 25) { 31 | int i; 32 | for (i = 0 * 80; i < 24 * 80; i++) { 33 | video_memory[i] = video_memory[i + 80]; 34 | } 35 | for (; i < 25 * 80; i++){ 36 | video_memory[i] = blank; 37 | } 38 | 39 | cursor_y = 24; 40 | } 41 | 42 | } 43 | 44 | void console_clear() 45 | { 46 | // 高位存颜色 47 | uint8_t attribute_byte = (0 << 4) | (15 & 0x0F); 48 | uint16_t blank = 0x20 | (attribute_byte << 8); 49 | 50 | for (int i = 0; i < 80 * 25; i++) { 51 | video_memory[i] = blank; 52 | } 53 | 54 | cursor_x = 0; 55 | cursor_y = 0; 56 | move_cursor(); 57 | } 58 | 59 | void console_putc_color(char c, real_color_t back, real_color_t fore) 60 | { 61 | uint8_t attribute_byte = ((uint8_t)back << 4) | ((uint8_t)fore & 0x0F); 62 | uint16_t attribute = attribute_byte << 8; 63 | 64 | // 退格键 65 | if (c == 0x08 && cursor_x) { 66 | cursor_x--; 67 | // TAB键 68 | } else if (c == 0x09) { 69 | // &~(8 - 1) 取整对齐 70 | cursor_x = (cursor_x + 8) & ~(8 - 1); 71 | } else if (c == '\r') { 72 | cursor_x = 0; 73 | } else if (c == '\n') { 74 | cursor_x = 0; 75 | cursor_y++; 76 | } else if (c >= ' ') { 77 | video_memory[cursor_y * 80 + cursor_x] = c | attribute; 78 | cursor_x++; 79 | } 80 | 81 | // 换行 82 | if(cursor_x >= 80) { 83 | cursor_x = 0; 84 | cursor_y++; 85 | } 86 | 87 | // 滚动屏幕,移动光标 88 | scroll(); 89 | move_cursor(); 90 | } 91 | 92 | void console_write(char* cstr) 93 | { 94 | while (*cstr) { 95 | console_putc_color(*cstr++, rc_black, rc_white); 96 | } 97 | } 98 | 99 | void console_write_color(char *cstr, real_color_t back, real_color_t fore) 100 | { 101 | while (*cstr) { 102 | console_putc_color(*cstr++, back, fore); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /kern/mm/gdt.c: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "string.h" 3 | 4 | #define GDT_LENGTH 6 5 | 6 | gdt_entry_t gdt_entries[GDT_LENGTH]; 7 | tss_entry_t tss_entry; 8 | 9 | // GDTR 10 | gdt_ptr_t gdt_ptr; 11 | 12 | static void gdt_set_gate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran); 13 | static void write_tss(uint32_t num, uint16_t ss0, uint32_t esp0); 14 | 15 | void init_gdt() 16 | { 17 | gdt_ptr.limit = sizeof(gdt_entry_t) * GDT_LENGTH - 1; 18 | gdt_ptr.base = (uint32_t)&gdt_entries; 19 | 20 | // 第一个段全部为0 21 | gdt_set_gate(0, 0, 0, 0, 0); 22 | gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // 内核代码段 23 | gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // 内核数据段 24 | // 修改了 DPL 位 以及 Type 位 25 | gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // 用户代码段 26 | gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // 用户数据段 27 | // TSS段 28 | write_tss(5, 0x10, 0x0); 29 | 30 | gdt_flush((uint32_t)&gdt_ptr); 31 | tss_flush(); 32 | } 33 | 34 | static void gdt_set_gate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) 35 | { 36 | gdt_entries[num].base_low = (base & 0xFFFF); 37 | gdt_entries[num].base_middle = (base >> 16) & 0xFF; 38 | gdt_entries[num].base_high = (base >> 24) & 0xFF; 39 | 40 | gdt_entries[num].limit_low = (limit & 0xFFFF); 41 | gdt_entries[num].granularity = (limit >> 16) & 0x0F; 42 | 43 | gdt_entries[num].granularity |= gran & 0xF0; 44 | gdt_entries[num].access = access; 45 | } 46 | 47 | static void write_tss(uint32_t num, uint16_t ss0, uint32_t esp0) 48 | { 49 | // Firstly, let's compute the base and limit of our entry into the GDT. 50 | uint32_t base = (uint32_t) &tss_entry; 51 | uint32_t limit = base + sizeof(tss_entry); 52 | 53 | // Now, add our TSS descriptor's address to the GDT. 54 | gdt_set_gate(num, base, limit, 0xE9, 0x00); 55 | 56 | // Ensure the descriptor is initially zero. 57 | bzero(&tss_entry, sizeof(tss_entry)); 58 | 59 | tss_entry.ss0 = ss0; // Set the kernel stack segment. 60 | tss_entry.esp0 = esp0; // Set the kernel stack pointer. 61 | 62 | // Here we set the cs, ss, ds, es, fs and gs entries in the TSS. These specify what 63 | // segments should be loaded when the processor switches to kernel mode. Therefore 64 | // they are just our normal kernel code/data segments - 0x08 and 0x10 respectively, 65 | // but with the last two bits set, making 0x0b and 0x13. The setting of these bits 66 | // sets the RPL (requested privilege level) to 3, meaning that this TSS can be used 67 | // to switch to kernel mode from ring 3. 68 | tss_entry.cs = 0x0b; 69 | tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x13; 70 | } 71 | 72 | void set_kernel_stack(uint32_t stack) 73 | { 74 | tss_entry.esp0 = stack; 75 | } 76 | 77 | -------------------------------------------------------------------------------- /kern/trap/idtasm.S: -------------------------------------------------------------------------------- 1 | ; 中断处理函数生成宏 2 | %macro ISR_NOERRCODE 1 3 | [GLOBAL isr%1] 4 | isr%1: 5 | cli 6 | push 0 ; 无效错误代码 7 | push %1 ; 中断号 8 | jmp isr_common_stub 9 | %endmacro 10 | 11 | %macro ISR_ERRCODE 1 12 | [GLOBAL isr%1] 13 | isr%1: 14 | cli 15 | push %1 16 | jmp isr_common_stub 17 | %endmacro 18 | 19 | ; 定义中断处理函数 20 | ISR_NOERRCODE 0 ; 0 #DE 除 0 异常 21 | ISR_NOERRCODE 1 ; 1 #DB 调试异常 22 | ISR_NOERRCODE 2 ; 2 NMI 23 | ISR_NOERRCODE 3 ; 3 BP 断点异常 24 | ISR_NOERRCODE 4 ; 4 #OF 溢出 25 | ISR_NOERRCODE 5 ; 5 #BR 对数组的引用超出边界 26 | ISR_NOERRCODE 6 ; 6 #UD 无效或未定义的操作码 27 | ISR_NOERRCODE 7 ; 7 #NM 设备不可用(无数学协处理器) 28 | ISR_ERRCODE 8 ; 8 #DF 双重故障(有错误代码) 29 | ISR_NOERRCODE 9 ; 9 协处理器跨段操作 30 | ISR_ERRCODE 10 ; 10 #TS 无效TSS(有错误代码) 31 | ISR_ERRCODE 11 ; 11 #NP 段不存在(有错误代码) 32 | ISR_ERRCODE 12 ; 12 #SS 栈错误(有错误代码) 33 | ISR_ERRCODE 13 ; 13 #GP 常规保护(有错误代码) 34 | ISR_ERRCODE 14 ; 14 #PF 页故障(有错误代码) 35 | ISR_NOERRCODE 15 ; 15 CPU 保留 36 | ISR_NOERRCODE 16 ; 16 #MF 浮点处理单元错误 37 | ISR_ERRCODE 17 ; 17 #AC 对齐检查 38 | ISR_NOERRCODE 18 ; 18 #MC 机器检查 39 | ISR_NOERRCODE 19 ; 19 #XM SIMD(单指令多数据)浮点异常 40 | 41 | ; 20~31 Intel 保留 42 | ISR_NOERRCODE 20 43 | ISR_NOERRCODE 21 44 | ISR_NOERRCODE 22 45 | ISR_NOERRCODE 23 46 | ISR_NOERRCODE 24 47 | ISR_NOERRCODE 25 48 | ISR_NOERRCODE 26 49 | ISR_NOERRCODE 27 50 | ISR_NOERRCODE 28 51 | ISR_NOERRCODE 29 52 | ISR_NOERRCODE 30 53 | ISR_NOERRCODE 31 54 | 55 | ; 32~255 用户自定义,硬件中断 56 | ISR_NOERRCODE 32 ; 0 电脑系统计时器 57 | ISR_NOERRCODE 33 ; 1 键盘 58 | ISR_NOERRCODE 34 ; 2 与 IRQ9 相接,MPU-401 MD 使用 59 | ISR_NOERRCODE 35 ; 3 串口设备 60 | ISR_NOERRCODE 36 ; 4 串口设备 61 | ISR_NOERRCODE 37 ; 5 建议声卡使用 62 | ISR_NOERRCODE 38 ; 6 软驱传输控制使用 63 | ISR_NOERRCODE 39 ; 7 打印机传输控制使用 64 | ISR_NOERRCODE 40 ; 8 即时时钟 65 | ISR_NOERRCODE 41 ; 9 与 IRQ2 相接,可设定给其他硬件 66 | ISR_NOERRCODE 42 ; 10 建议网卡使用 67 | ISR_NOERRCODE 43 ; 11 建议 AGP 显卡使用 68 | ISR_NOERRCODE 44 ; 12 接 PS/2 鼠标,也可设定给其他硬件 69 | ISR_NOERRCODE 45 ; 13 协处理器使用 70 | ISR_NOERRCODE 46 ; 14 IDE0 传输控制使用 71 | ISR_NOERRCODE 47 ; 15 IDE1 传输控制使用 72 | 73 | ISR_NOERRCODE 80 ; 系统调用 74 | 75 | ; 保存,恢复现场 76 | [EXTERN isr_handler] 77 | [GLOBAL isr_common_stub]c 78 | isr_common_stub: 79 | pusha ; 保存通用寄存器 80 | mov ax, ds 81 | push eax ; 保存数据段选择子 82 | 83 | mov ax, 0x10 ; 加载内核数据段选择子 84 | mov ds, ax 85 | mov es, ax 86 | mov fs, ax 87 | mov gs, ax 88 | mov ss, ax 89 | 90 | push esp ; 传入 pt_regs 结构体 91 | call isr_handler ; 调用中断服务例程 92 | add esp, 4 ; 清除传入参数 93 | 94 | pop ebx ; 恢复原来的数据段选择子 95 | mov ds, bx 96 | mov es, bx 97 | mov fs, bx 98 | mov gs, bx 99 | mov ss, bx 100 | 101 | popa ; 通用寄存器 出栈 102 | add esp, 8 ; 中断号, Error Code 出栈 103 | iret ; EIP,CS,EFLAGS,ESP,SS 出栈 104 | .end 105 | 106 | ; 加载中断描述符表 107 | [GLOBAL idt_flush] 108 | idt_flush: 109 | mov eax, [esp + 4] 110 | lidt [eax] 111 | ret ; EIP 出栈 112 | .end 113 | -------------------------------------------------------------------------------- /kern/trap/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_IDT_H 2 | #define INCLUDE_IDT_H 3 | 4 | #include "types.h" 5 | 6 | typedef 7 | struct idt_entry_t { 8 | uint16_t base_low; 9 | uint16_t sel; // 目标代码的段选择子 10 | uint8_t always0; 11 | uint8_t flags; 12 | uint16_t base_high; 13 | }__attribute__((packed)) idt_entry_t; 14 | 15 | typedef 16 | struct idt_ptr_t { 17 | uint16_t limit; 18 | uint32_t base; 19 | }__attribute__((packed)) idt_ptr_t; 20 | 21 | typedef 22 | struct pt_regs_t { 23 | uint32_t ds; 24 | uint32_t edi; 25 | uint32_t esi; 26 | uint32_t ebp; 27 | uint32_t esp; 28 | uint32_t ebx; 29 | uint32_t edx; 30 | uint32_t ecx; 31 | uint32_t eax; 32 | uint32_t int_no; //中断号 33 | uint32_t err_code; 34 | uint32_t eip; 35 | uint32_t cs; 36 | uint32_t eflags; 37 | uint32_t useresp; 38 | uint32_t ss; 39 | } pt_regs_t; 40 | 41 | // 中断初始化 42 | void init_idt(); 43 | 44 | // 中断处理函数指针 45 | typedef void (*interrupt_handler_t)(pt_regs_t *); 46 | 47 | // 注册中断处理函数 48 | void register_interrupt_handler(uint8_t n, interrupt_handler_t h); 49 | 50 | // 调用中断处理 51 | void isr_handler(pt_regs_t *regs); 52 | 53 | // 开启指定中断 54 | void enable_irq(int32_t irq); 55 | 56 | // 关闭指定中断 57 | void disable_irq(int32_t irq); 58 | 59 | // 声明中断处理函数 60 | // 0-19 属于 CPU 的异常中断 61 | void isr0(); // 0 #DE 除 0 异常 62 | void isr1(); // 1 #DB 调试异常 63 | void isr2(); // 2 NMI 64 | void isr3(); // 3 BP 断点异常 65 | void isr4(); // 4 #OF 溢出 66 | void isr5(); // 5 #BR 对数组的引用超出边界 67 | void isr6(); // 6 #UD 无效或未定义的操作码 68 | void isr7(); // 7 #NM 设备不可用(无数学协处理器) 69 | void isr8(); // 8 #DF 双重故障(有错误代码) 70 | void isr9(); // 9 协处理器跨段操作 71 | void isr10(); // 10 #TS 无效TSS(有错误代码) 72 | void isr11(); // 11 #NP 段不存在(有错误代码) 73 | void isr12(); // 12 #SS 栈错误(有错误代码) 74 | void isr13(); // 13 #GP 常规保护(有错误代码) 75 | void isr14(); // 14 #PF 页故障(有错误代码) 76 | void isr15(); // 15 CPU 保留 77 | void isr16(); // 16 #MF 浮点处理单元错误 78 | void isr17(); // 17 #AC 对齐检查 79 | void isr18(); // 18 #MC 机器检查 80 | void isr19(); // 19 #XM SIMD(单指令多数据)浮点异常 81 | 82 | // 20-31 Intel 保留 83 | void isr20(); 84 | void isr21(); 85 | void isr22(); 86 | void isr23(); 87 | void isr24(); 88 | void isr25(); 89 | void isr26(); 90 | void isr27(); 91 | void isr28(); 92 | void isr29(); 93 | void isr30(); 94 | void isr31(); 95 | 96 | // 32~255 用户自定义异常 97 | void isr32(); // 0 电脑系统计时器 98 | void isr33(); // 1 键盘 99 | void isr34(); // 2 与 IRQ9 相接,MPU-401 MD 使用 100 | void isr35(); // 3 串口设备 101 | void isr36(); // 4 串口设备 102 | void isr37(); // 5 建议声卡使用 103 | void isr38(); // 6 软驱传输控制使用 104 | void isr39(); // 7 打印机传输控制使用 105 | void isr40(); // 8 即时时钟 106 | void isr41(); // 9 与 IRQ2 相接,可设定给其他硬件 107 | void isr42(); // 10 建议网卡使用 108 | void isr43(); // 11 建议 AGP 显卡使用 109 | void isr44(); // 12 接 PS/2 鼠标,也可设定给其他硬件 110 | void isr45(); // 13 协处理器使用 111 | void isr46(); // 14 IDE0 传输控制使用 112 | void isr47(); // 15 IDE1 传控制使用 113 | 114 | void isr80(); // 系统调用 115 | 116 | #endif // INCLUDE_IDT_H 117 | -------------------------------------------------------------------------------- /kern/init/entry.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "debug.h" 3 | #include "gdt.h" 4 | #include "idt.h" 5 | #include "clock.h" 6 | #include "pmm.h" 7 | #include "vmm.h" 8 | #include "heap.h" 9 | #include "sched.h" 10 | #include "keyboard.h" 11 | #include "tty.h" 12 | 13 | // 内核初始化 14 | void kern_init(); 15 | 16 | // 开启分页后的 multiboot 结构体指针 17 | multiboot_t *glb_mboot_ptr; 18 | 19 | // 开启分页后的 内核栈 20 | char kern_stack[STACK_SIZE]; 21 | uint32_t kern_stack_top; 22 | 23 | // 临时页表,使用 0 - 640 KB 空闲内存 24 | __attribute__((section(".init.data"))) pgd_t *pgd_tmp = (pgd_t *)0x1000; 25 | __attribute__((section(".init.data"))) pgd_t *pte_low = (pgd_t *)0x2000; 26 | __attribute__((section(".init.data"))) pgd_t *pte_hign = (pgd_t *)0x3000; 27 | 28 | // 内核入口,建立临时页表,调用内核初始化函数 29 | __attribute__((section(".init.text"))) void kern_entry() 30 | { 31 | pgd_tmp[0] = (uint32_t)pte_low | PAGE_PRESENT | PAGE_WRITE; 32 | pgd_tmp[PGD_INDEX(PAGE_OFFSET)] = (uint32_t)pte_hign | PAGE_PRESENT | PAGE_WRITE; 33 | 34 | // 映射内核虚拟地址 4MB 到物理地址的前 4MB, 保证入口函数正常运行 35 | for (int i = 0; i < 1024; i++) { 36 | pte_low[i] = (i << 12) | PAGE_PRESENT | PAGE_WRITE; 37 | } 38 | 39 | // 映射 0xC0000000 后面的 4MB 到物理地址前 4MB 40 | for (int i = 0; i < 1024; i++) { 41 | pte_hign[i] = (i << 12) | PAGE_PRESENT | PAGE_WRITE; 42 | } 43 | 44 | // 设置临时页表 45 | asm volatile ("mov %0, %%cr3" : : "r" (pgd_tmp)); 46 | 47 | uint32_t cr0; 48 | 49 | // 启用分页,将 cr0 寄存器的 31位 置 1 50 | asm volatile ("mov %%cr0, %0" : "=r" (cr0)); 51 | cr0 |= 0x80000000; 52 | asm volatile ("mov %0, %%cr0" : : "r" (cr0)); 53 | 54 | // 更新内核栈地址 55 | kern_stack_top = ((uint32_t)kern_stack + STACK_SIZE) & 0xFFFFFFF0; 56 | asm volatile ("mov %0, %%esp\n\t" 57 | "xor %%ebp, %%ebp" : : "r" (kern_stack_top)); 58 | 59 | // 更新全局 multiboot_t 指针 60 | glb_mboot_ptr = (multiboot_t *)((uint32_t)tmp_mboot_ptr + PAGE_OFFSET); 61 | 62 | // 内核初始化 63 | kern_init(); 64 | } 65 | 66 | // 内核初始化 67 | void kern_init() 68 | { 69 | // 初始化字符显示 70 | init_debug(); 71 | console_clear(); 72 | // 初始化全局描述符表 73 | init_gdt(); 74 | // 初始化中断描述符表、可编程中断控制器 75 | init_idt(); 76 | // 初始化时钟中断 77 | init_timer(100); 78 | // 初始化物理内存管理 79 | init_pmm(); 80 | // 初始化页表 81 | init_vmm(); 82 | // 初始化内核堆 83 | init_heap(); 84 | // 初始化进程调度 85 | init_sched(); 86 | // 初始化键盘 87 | init_keyboard(); 88 | 89 | // 显示可用内存 90 | printk_color(rc_black, rc_red, "\nfree physical memory: %u MB\n", phy_page_count * 4 / 1024); 91 | 92 | // 测试堆内存管理 93 | printk_color(rc_black, rc_magenta, "\ntest kmalloc() && kfree() now ...\n\n"); 94 | 95 | void *addr2 = kmalloc(500); 96 | printk("kmalloc 500 byte in 0x%X\n", addr2); 97 | void *addr3 = kmalloc(5000); 98 | printk("kmalloc 5000 byte in 0x%X\n", addr3); 99 | void *addr4 = kmalloc(50000); 100 | printk("kmalloc 50000 byte in 0x%X\n\n", addr4); 101 | 102 | printk("free mem in 0x%X\n", addr2); 103 | kfree(addr2); 104 | printk("free mem in 0x%X\n", addr3); 105 | kfree(addr3); 106 | printk("free mem in 0x%X\n\n", addr4); 107 | kfree(addr4); 108 | 109 | // 初始化 TTY 进程 110 | kernel_thread(task_tty, NULL); 111 | 112 | // 开启中断 113 | sti(); 114 | while(1) { 115 | schedule(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /kern/mm/vmm.c: -------------------------------------------------------------------------------- 1 | #include "vmm.h" 2 | #include "debug.h" 3 | #include "pmm.h" 4 | #include "string.h" 5 | #include "heap.h" 6 | 7 | // 内核页目录区域,对齐到 4KB 8 | pgd_t pgd_kern[PGD_SIZE] __attribute__ ((aligned(PAGE_SIZE))); 9 | 10 | // 内核页表区域,对齐到 4KB 11 | static pte_t pte_kern[PTE_COUNT][PTE_SIZE] __attribute__ ((aligned(PAGE_SIZE))); 12 | 13 | // 初始化页表 14 | void init_vmm() 15 | { 16 | uint32_t kern_pte_first_idx = PGD_INDEX(PAGE_OFFSET); 17 | 18 | // 填写页目录,映射 VM 0xC0000000 ~ 0xC0000000 + 512 MB => PM 0 ~ 512MB 19 | for (uint32_t i = kern_pte_first_idx, j = 0; j < PTE_COUNT; i++, j++) { 20 | // MMU 需要的是物理地址,所以减去内核偏移地址 21 | pgd_kern[i] = ((uint32_t)pte_kern[j] - PAGE_OFFSET) | PAGE_PRESENT | PAGE_WRITE; 22 | } 23 | 24 | pte_t* pte = (pte_t *)pte_kern; 25 | // 填写页表,页 0 是无效页 26 | for (uint32_t i = 1; i < PTE_COUNT * PTE_SIZE; i++) { 27 | pte[i] = (i << 12) | PAGE_PRESENT | PAGE_WRITE; 28 | } 29 | 30 | // 设置缺页处理函数 31 | register_interrupt_handler(14, page_fault); 32 | 33 | // 加载页表需要物理地址 34 | uint32_t pgd_kern_phy_addr = (uint32_t)pgd_kern - PAGE_OFFSET; 35 | switch_pgd(pgd_kern_phy_addr); 36 | } 37 | 38 | // 更换当前页目录 39 | void switch_pgd(uint32_t pd) 40 | { 41 | // 页目录地址写入 cr3 42 | asm volatile ("mov %0, %%cr3" : : "r"(pd)); 43 | } 44 | 45 | // 缺页异常处理 46 | void page_fault(pt_regs_t *regs) 47 | { 48 | // cr2 保存当前访问地址 49 | uint32_t cr2; 50 | asm volatile ("mov %%cr2, %0" : "=r"(cr2)); 51 | printk("Page fault at 0x%x, virtual address 0x%x\n", regs->eip, cr2); 52 | printk("Error code : %x\n", regs->err_code); 53 | 54 | // bit 0 为 0 表示页不存在 55 | if (!(regs->err_code & 0x1)) { 56 | printk_color(rc_black, rc_red, "The page wasn't present.\n"); 57 | } 58 | 59 | // bit 1 为 0 表示读错误,为 1 为写错误 60 | if (regs->err_code & 0x2) { 61 | printk_color(rc_black, rc_red, "Write error.\n"); 62 | } else { 63 | printk_color(rc_black, rc_red, "Read error.\n"); 64 | } 65 | 66 | // bit 2 为 1 表示在用户模式打断的,为 0 是在内核模式打断的 67 | if (regs->err_code & 0x4) { 68 | printk_color(rc_black, rc_red, "User mode.\n"); 69 | } else { 70 | printk_color(rc_black, rc_red, "Kernel mode.\n"); 71 | } 72 | 73 | // bit 3 为 1 表示错误是由保留位覆盖造成的 74 | if (regs->err_code & 0x8) { 75 | printk_color(rc_black, rc_red, "Reserved bits being overwritten.\n"); 76 | } 77 | 78 | // bit 4 为 1 表示错误发生在取指令的时候 79 | if (regs->err_code & 0x10) { 80 | printk_color(rc_black, rc_red, "The fault occurred during an instruction fetch.\n"); 81 | } 82 | 83 | while (1); 84 | } 85 | 86 | // 映射虚拟地址到物理地址,flags 为页帧权限 87 | void map(pgd_t *pgd_now, uint32_t va, uint32_t pa, uint32_t flags) 88 | { 89 | uint32_t pgd_idx = PGD_INDEX(va); 90 | uint32_t pte_idx = PTE_INDEX(va); 91 | 92 | // 从页目录获取页表 93 | pte_t *pte = (pte_t *)(ROUNDDOWN(pgd_now[pgd_idx], PAGE_SIZE)); 94 | if (!pte) { 95 | // 分配新页表 96 | pte = (pte_t *)pmm_alloc_page(); 97 | pgd_now[pgd_idx] = (uint32_t)pte | PAGE_PRESENT | PAGE_WRITE; 98 | 99 | // 页表清空 100 | pte = (pte_t *)((uint32_t)pte + PAGE_OFFSET); 101 | bzero(pte, PAGE_SIZE); 102 | } else { 103 | // 加上内核偏移地址 104 | pte = (pte_t *)((uint32_t)pte + PAGE_OFFSET); 105 | } 106 | 107 | // 填写页表项 108 | pte[pte_idx] = (ROUNDDOWN(pa, PAGE_SIZE) | flags); 109 | // 更新快表 110 | asm volatile ("invlpg (%0)" : : "a"(va)); 111 | } 112 | 113 | // 取消映射 114 | void unmap(pgd_t *pgd_now, uint32_t va) 115 | { 116 | uint32_t pgd_idx = PGD_INDEX(va); 117 | uint32_t pte_idx = PTE_INDEX(va); 118 | 119 | // 从页目录获取页表 120 | pte_t *pte = (pte_t *)(ROUNDDOWN(pgd_now[pgd_idx], PAGE_SIZE)); 121 | if (!pte) { 122 | return; 123 | } 124 | 125 | // 加上内核偏移地址 126 | pte = (pte_t *)((uint32_t)pte + PAGE_OFFSET); 127 | 128 | // 清空页表项 129 | pte[pte_idx] = 0; 130 | // 更新快表 131 | asm volatile ("invlpg (%0)" : : "a"(va)); 132 | } 133 | 134 | // 获取虚拟地址对应的物理地址,失败返回 0 135 | uint32_t get_mapping(pgd_t *pgd_now, uint32_t va, uint32_t *pa) 136 | { 137 | uint32_t pgd_idx = PGD_INDEX(va); 138 | uint32_t pte_idx = PTE_INDEX(va); 139 | 140 | // 从页目录获取页表 141 | pte_t *pte = (pte_t *)(ROUNDDOWN(pgd_now[pgd_idx], PAGE_SIZE)); 142 | if (!pte) { 143 | return 0; 144 | } 145 | 146 | // 加上内核偏移地址 147 | pte = (pte_t *)((uint32_t)pte + PAGE_OFFSET); 148 | 149 | if (pte[pte_idx] != 0 && pa) { 150 | *pa = ROUNDDOWN(pte[pte_idx], PAGE_SIZE); 151 | return 1; 152 | } 153 | 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /kern/driver/keymap.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_KEYMAP_H 2 | #define INCLUDE_KEYMAP_H 3 | 4 | #include "types.h" 5 | #include "keyboard.h" 6 | 7 | /* Keymap for US MF-2 keyboard. */ 8 | 9 | uint32_t keymap[NR_SCAN_CODES * MAP_COLS] = { 10 | 11 | /* scan-code !Shift Shift E0 XX */ 12 | /* ==================================================================== */ 13 | /* 0x00 - none */ 0, 0, 0, 14 | /* 0x01 - ESC */ ESC, ESC, 0, 15 | /* 0x02 - '1' */ '1', '!', 0, 16 | /* 0x03 - '2' */ '2', '@', 0, 17 | /* 0x04 - '3' */ '3', '#', 0, 18 | /* 0x05 - '4' */ '4', '$', 0, 19 | /* 0x06 - '5' */ '5', '%', 0, 20 | /* 0x07 - '6' */ '6', '^', 0, 21 | /* 0x08 - '7' */ '7', '&', 0, 22 | /* 0x09 - '8' */ '8', '*', 0, 23 | /* 0x0A - '9' */ '9', '(', 0, 24 | /* 0x0B - '0' */ '0', ')', 0, 25 | /* 0x0C - '-' */ '-', '_', 0, 26 | /* 0x0D - '=' */ '=', '+', 0, 27 | /* 0x0E - BS */ BACKSPACE, BACKSPACE, 0, 28 | /* 0x0F - TAB */ TAB, TAB, 0, 29 | /* 0x10 - 'q' */ 'q', 'Q', 0, 30 | /* 0x11 - 'w' */ 'w', 'W', 0, 31 | /* 0x12 - 'e' */ 'e', 'E', 0, 32 | /* 0x13 - 'r' */ 'r', 'R', 0, 33 | /* 0x14 - 't' */ 't', 'T', 0, 34 | /* 0x15 - 'y' */ 'y', 'Y', 0, 35 | /* 0x16 - 'u' */ 'u', 'U', 0, 36 | /* 0x17 - 'i' */ 'i', 'I', 0, 37 | /* 0x18 - 'o' */ 'o', 'O', 0, 38 | /* 0x19 - 'p' */ 'p', 'P', 0, 39 | /* 0x1A - '[' */ '[', '{', 0, 40 | /* 0x1B - ']' */ ']', '}', 0, 41 | /* 0x1C - CR/LF */ ENTER, ENTER, PAD_ENTER, 42 | /* 0x1D - l. Ctrl */ CTRL_L, CTRL_L, CTRL_R, 43 | /* 0x1E - 'a' */ 'a', 'A', 0, 44 | /* 0x1F - 's' */ 's', 'S', 0, 45 | /* 0x20 - 'd' */ 'd', 'D', 0, 46 | /* 0x21 - 'f' */ 'f', 'F', 0, 47 | /* 0x22 - 'g' */ 'g', 'G', 0, 48 | /* 0x23 - 'h' */ 'h', 'H', 0, 49 | /* 0x24 - 'j' */ 'j', 'J', 0, 50 | /* 0x25 - 'k' */ 'k', 'K', 0, 51 | /* 0x26 - 'l' */ 'l', 'L', 0, 52 | /* 0x27 - ';' */ ';', ':', 0, 53 | /* 0x28 - '\'' */ '\'', '"', 0, 54 | /* 0x29 - '`' */ '`', '~', 0, 55 | /* 0x2A - l. SHIFT */ SHIFT_L, SHIFT_L, 0, 56 | /* 0x2B - '\' */ '\\', '|', 0, 57 | /* 0x2C - 'z' */ 'z', 'Z', 0, 58 | /* 0x2D - 'x' */ 'x', 'X', 0, 59 | /* 0x2E - 'c' */ 'c', 'C', 0, 60 | /* 0x2F - 'v' */ 'v', 'V', 0, 61 | /* 0x30 - 'b' */ 'b', 'B', 0, 62 | /* 0x31 - 'n' */ 'n', 'N', 0, 63 | /* 0x32 - 'm' */ 'm', 'M', 0, 64 | /* 0x33 - ',' */ ',', '<', 0, 65 | /* 0x34 - '.' */ '.', '>', 0, 66 | /* 0x35 - '/' */ '/', '?', PAD_SLASH, 67 | /* 0x36 - r. SHIFT */ SHIFT_R, SHIFT_R, 0, 68 | /* 0x37 - '*' */ '*', '*', 0, 69 | /* 0x38 - ALT */ ALT_L, ALT_L, ALT_R, 70 | /* 0x39 - ' ' */ ' ', ' ', 0, 71 | /* 0x3A - CapsLock */ CAPS_LOCK, CAPS_LOCK, 0, 72 | /* 0x3B - F1 */ F1, F1, 0, 73 | /* 0x3C - F2 */ F2, F2, 0, 74 | /* 0x3D - F3 */ F3, F3, 0, 75 | /* 0x3E - F4 */ F4, F4, 0, 76 | /* 0x3F - F5 */ F5, F5, 0, 77 | /* 0x40 - F6 */ F6, F6, 0, 78 | /* 0x41 - F7 */ F7, F7, 0, 79 | /* 0x42 - F8 */ F8, F8, 0, 80 | /* 0x43 - F9 */ F9, F9, 0, 81 | /* 0x44 - F10 */ F10, F10, 0, 82 | /* 0x45 - NumLock */ NUM_LOCK, NUM_LOCK, 0, 83 | /* 0x46 - ScrLock */ SCROLL_LOCK, SCROLL_LOCK, 0, 84 | /* 0x47 - Home */ PAD_HOME, '7', HOME, 85 | /* 0x48 - CurUp */ PAD_UP, '8', UP, 86 | /* 0x49 - PgUp */ PAD_PAGEUP, '9', PAGEUP, 87 | /* 0x4A - '-' */ PAD_MINUS, '-', 0, 88 | /* 0x4B - Left */ PAD_LEFT, '4', LEFT, 89 | /* 0x4C - MID */ PAD_MID, '5', 0, 90 | /* 0x4D - Right */ PAD_RIGHT, '6', RIGHT, 91 | /* 0x4E - '+' */ PAD_PLUS, '+', 0, 92 | /* 0x4F - End */ PAD_END, '1', END, 93 | /* 0x50 - Down */ PAD_DOWN, '2', DOWN, 94 | /* 0x51 - PgDown */ PAD_PAGEDOWN, '3', PAGEDOWN, 95 | /* 0x52 - Insert */ PAD_INS, '0', INSERT, 96 | /* 0x53 - Delete */ PAD_DOT, '.', DELETE, 97 | /* 0x54 - Enter */ 0, 0, 0, 98 | /* 0x55 - ??? */ 0, 0, 0, 99 | /* 0x56 - ??? */ 0, 0, 0, 100 | /* 0x57 - F11 */ F11, F11, 0, 101 | /* 0x58 - F12 */ F12, F12, 0, 102 | /* 0x59 - ??? */ 0, 0, 0, 103 | /* 0x5A - ??? */ 0, 0, 0, 104 | /* 0x5B - ??? */ 0, 0, GUI_L, 105 | /* 0x5C - ??? */ 0, 0, GUI_R, 106 | /* 0x5D - ??? */ 0, 0, APPS, 107 | /* 0x5E - ??? */ 0, 0, 0, 108 | /* 0x5F - ??? */ 0, 0, 0, 109 | /* 0x60 - ??? */ 0, 0, 0, 110 | /* 0x61 - ??? */ 0, 0, 0, 111 | /* 0x62 - ??? */ 0, 0, 0, 112 | /* 0x63 - ??? */ 0, 0, 0, 113 | /* 0x64 - ??? */ 0, 0, 0, 114 | /* 0x65 - ??? */ 0, 0, 0, 115 | /* 0x66 - ??? */ 0, 0, 0, 116 | /* 0x67 - ??? */ 0, 0, 0, 117 | /* 0x68 - ??? */ 0, 0, 0, 118 | /* 0x69 - ??? */ 0, 0, 0, 119 | /* 0x6A - ??? */ 0, 0, 0, 120 | /* 0x6B - ??? */ 0, 0, 0, 121 | /* 0x6C - ??? */ 0, 0, 0, 122 | /* 0x6D - ??? */ 0, 0, 0, 123 | /* 0x6E - ??? */ 0, 0, 0, 124 | /* 0x6F - ??? */ 0, 0, 0, 125 | /* 0x70 - ??? */ 0, 0, 0, 126 | /* 0x71 - ??? */ 0, 0, 0, 127 | /* 0x72 - ??? */ 0, 0, 0, 128 | /* 0x73 - ??? */ 0, 0, 0, 129 | /* 0x74 - ??? */ 0, 0, 0, 130 | /* 0x75 - ??? */ 0, 0, 0, 131 | /* 0x76 - ??? */ 0, 0, 0, 132 | /* 0x77 - ??? */ 0, 0, 0, 133 | /* 0x78 - ??? */ 0, 0, 0, 134 | /* 0x78 - ??? */ 0, 0, 0, 135 | /* 0x7A - ??? */ 0, 0, 0, 136 | /* 0x7B - ??? */ 0, 0, 0, 137 | /* 0x7C - ??? */ 0, 0, 0, 138 | /* 0x7D - ??? */ 0, 0, 0, 139 | /* 0x7E - ??? */ 0, 0, 0, 140 | /* 0x7F - ??? */ 0, 0, 0 141 | }; 142 | #endif 143 | -------------------------------------------------------------------------------- /kern/trap/idt.c: -------------------------------------------------------------------------------- 1 | #include "idt.h" 2 | #include "string.h" 3 | #include "debug.h" 4 | #include "common.h" 5 | 6 | idt_entry_t idt_entries[256]; 7 | 8 | // IDTR 9 | idt_ptr_t idt_ptr; 10 | 11 | // 中断处理函数数组 12 | interrupt_handler_t interrupt_handlers[256]; 13 | 14 | static void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags); 15 | 16 | static void init_8259a(); 17 | 18 | extern void idt_flush(uint32_t); 19 | 20 | void init_idt() 21 | { 22 | // 初始化 8259A 23 | init_8259a(); 24 | 25 | // 中断处理函数表、中断描述符表初始化 26 | bzero((uint8_t *)&interrupt_handlers, sizeof(interrupt_handlers)); 27 | bzero((uint8_t *)&idt_entries, sizeof(idt_entries)); 28 | 29 | // 填写中断描述符表 30 | // 0-32: 用于 CPU 的中断 31 | // 0x08 内核代码段选择子, 0x8E P = 1, DPL = 00 32 | idt_set_gate( 0, (uint32_t)isr0, 0x08, 0x8E); 33 | idt_set_gate( 1, (uint32_t)isr1, 0x08, 0x8E); 34 | idt_set_gate( 2, (uint32_t)isr2, 0x08, 0x8E); 35 | idt_set_gate( 3, (uint32_t)isr3, 0x08, 0x8E); 36 | idt_set_gate( 4, (uint32_t)isr4, 0x08, 0x8E); 37 | idt_set_gate( 5, (uint32_t)isr5, 0x08, 0x8E); 38 | idt_set_gate( 6, (uint32_t)isr6, 0x08, 0x8E); 39 | idt_set_gate( 7, (uint32_t)isr7, 0x08, 0x8E); 40 | idt_set_gate( 8, (uint32_t)isr8, 0x08, 0x8E); 41 | idt_set_gate( 9, (uint32_t)isr9, 0x08, 0x8E); 42 | idt_set_gate(10, (uint32_t)isr10, 0x08, 0x8E); 43 | idt_set_gate(11, (uint32_t)isr11, 0x08, 0x8E); 44 | idt_set_gate(12, (uint32_t)isr12, 0x08, 0x8E); 45 | idt_set_gate(13, (uint32_t)isr13, 0x08, 0x8E); 46 | idt_set_gate(14, (uint32_t)isr14, 0x08, 0x8E); 47 | idt_set_gate(15, (uint32_t)isr15, 0x08, 0x8E); 48 | idt_set_gate(16, (uint32_t)isr16, 0x08, 0x8E); 49 | idt_set_gate(17, (uint32_t)isr17, 0x08, 0x8E); 50 | idt_set_gate(18, (uint32_t)isr18, 0x08, 0x8E); 51 | idt_set_gate(19, (uint32_t)isr19, 0x08, 0x8E); 52 | idt_set_gate(20, (uint32_t)isr20, 0x08, 0x8E); 53 | idt_set_gate(21, (uint32_t)isr21, 0x08, 0x8E); 54 | idt_set_gate(22, (uint32_t)isr22, 0x08, 0x8E); 55 | idt_set_gate(23, (uint32_t)isr23, 0x08, 0x8E); 56 | idt_set_gate(24, (uint32_t)isr24, 0x08, 0x8E); 57 | idt_set_gate(25, (uint32_t)isr25, 0x08, 0x8E); 58 | idt_set_gate(26, (uint32_t)isr26, 0x08, 0x8E); 59 | idt_set_gate(27, (uint32_t)isr27, 0x08, 0x8E); 60 | idt_set_gate(28, (uint32_t)isr28, 0x08, 0x8E); 61 | idt_set_gate(29, (uint32_t)isr29, 0x08, 0x8E); 62 | idt_set_gate(30, (uint32_t)isr30, 0x08, 0x8E); 63 | idt_set_gate(31, (uint32_t)isr31, 0x08, 0x8E); 64 | 65 | // 32-47,自定义硬件中断 66 | idt_set_gate(32, (uint32_t)isr32, 0x08, 0x8E); 67 | idt_set_gate(33, (uint32_t)isr33, 0x08, 0x8E); 68 | idt_set_gate(34, (uint32_t)isr34, 0x08, 0x8E); 69 | idt_set_gate(35, (uint32_t)isr35, 0x08, 0x8E); 70 | idt_set_gate(36, (uint32_t)isr36, 0x08, 0x8E); 71 | idt_set_gate(37, (uint32_t)isr37, 0x08, 0x8E); 72 | idt_set_gate(38, (uint32_t)isr38, 0x08, 0x8E); 73 | idt_set_gate(39, (uint32_t)isr39, 0x08, 0x8E); 74 | idt_set_gate(40, (uint32_t)isr40, 0x08, 0x8E); 75 | idt_set_gate(41, (uint32_t)isr41, 0x08, 0x8E); 76 | idt_set_gate(42, (uint32_t)isr42, 0x08, 0x8E); 77 | idt_set_gate(43, (uint32_t)isr43, 0x08, 0x8E); 78 | idt_set_gate(44, (uint32_t)isr44, 0x08, 0x8E); 79 | idt_set_gate(45, (uint32_t)isr45, 0x08, 0x8E); 80 | idt_set_gate(46, (uint32_t)isr46, 0x08, 0x8E); 81 | idt_set_gate(47, (uint32_t)isr47, 0x08, 0x8E); 82 | 83 | // 80 用于实现系统调用 84 | idt_set_gate(80, (uint32_t)isr80, 0x08, 0xEE); 85 | 86 | idt_ptr.base = (uint32_t)&idt_entries; 87 | idt_ptr.limit = sizeof(idt_entries) - 1; 88 | idt_flush((uint32_t)&idt_ptr); 89 | } 90 | 91 | static void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) 92 | { 93 | idt_entries[num].base_low = base & 0xFFFF; 94 | idt_entries[num].base_high = (base >> 16) & 0xFFFF; 95 | 96 | idt_entries[num].sel = sel; 97 | idt_entries[num].always0 = 0; 98 | 99 | idt_entries[num].flags = flags; 100 | } 101 | 102 | void isr_handler(pt_regs_t *regs) 103 | { 104 | // 如果是硬件中断 105 | if (regs->int_no >= 32 && regs->int_no <= 47) { 106 | // 发送中断结束信号给 8259A 107 | // 单片 8259A 芯片只能处理 8 级中断 108 | // 大于等于 40 的中断号是由从片处理的 109 | if (regs->int_no >= 40) { 110 | // 从片 EOI 111 | outb(0xA0, 0x20); 112 | } 113 | // 主片 EOI 114 | outb(0x20, 0x20); 115 | } 116 | 117 | if (interrupt_handlers[regs->int_no]) { 118 | interrupt_handlers[regs->int_no](regs); 119 | } else { 120 | printk_color (rc_black, rc_blue, "Unhandled interrupt : %d\n", regs->int_no); 121 | } 122 | } 123 | 124 | 125 | void register_interrupt_handler(uint8_t n, interrupt_handler_t h) 126 | { 127 | interrupt_handlers[n] = h; 128 | } 129 | 130 | static void init_8259a() 131 | { 132 | // 两片级联的 8259A 芯片 133 | // 主片端口 0x20 0x21 134 | // 从片端口 0xA0 0xA1 135 | 136 | // 初始化主片、从片 137 | // 0001 0001,ICW1,电平触发,级联,要 ICW4 138 | outb(0x20, 0x11); 139 | outb(0xA0, 0x11); 140 | 141 | // ICW2,设置主片 IRQ 从 0x20(32) 号中断开始 142 | outb(0x21, 0x20); 143 | 144 | // ICW2,设置从片 IRQ 从 0x28(40) 号中断开始 145 | outb(0xA1, 0x28); 146 | 147 | // ICW3,设置主片 IR2 引脚连接从片 148 | outb(0x21, 0x04); 149 | 150 | // ICW3,设置从片输出引脚和主片 IR2 号相连 151 | outb(0xA1, 0x02); 152 | 153 | // ICW4,设置主片和从片按照 8086 的方式工作 154 | outb(0x21, 0x01); 155 | outb(0xA1, 0x01); 156 | 157 | // OCW1,设置允许中断 158 | outb(0x21, 0x0); 159 | outb(0xA1, 0x0); 160 | } 161 | -------------------------------------------------------------------------------- /kern/mm/heap.c: -------------------------------------------------------------------------------- 1 | /* [ Allocation ] 2 | * Search the index table to find the smallest hole that will fit the requested size. As the table is ordered, this just entails iterating through until we find a hole which will fit. 3 | * If we didn't find a hole large enough, then: 4 | * Expand the heap. 5 | * If the index table is empty (no holes have been recorded) then add a new entry to it. 6 | * Else, adjust the last header's size member and rewrite the footer. 7 | * To ease the number of control-flow statements, we can just recurse and call the allocation function again, trusting that this time there will be a hole large enough. 8 | * Decide if the hole should be split into two parts. This will normally be the case - we usually will want much less space than is available in the hole. The only time this will not happen is if there is less free space after allocating the block than the header/footer takes up. In this case we can just increase the block size and reclaim it all afterwards. 9 | * If the block should be page-aligned, we must alter the block starting address so that it is and create a new hole in the new unused area. 10 | * If it is not, we can just delete the hole from the index. 11 | * Write the new block's header and footer. 12 | * If the hole was to be split into two parts, do it now and write a new hole into the index. 13 | * Return the address of the block + sizeof(header_t) to the user. 14 | */ 15 | 16 | #include "heap.h" 17 | #include "vmm.h" 18 | #include "pmm.h" 19 | 20 | // 申请内存块 21 | static void alloc_chunk(uint32_t start, uint32_t len); 22 | 23 | // 释放内存块 24 | static void free_chunk(header_t *chunk); 25 | 26 | // 切分内存块 27 | static void split_chunk(header_t *chunk, uint32_t len); 28 | 29 | // 合并内存块 30 | static void glue_chunk(header_t *chunk); 31 | 32 | static uint32_t heap_max = HEAP_START; 33 | 34 | // 内存块管理头指针 35 | static header_t *heap_first; 36 | 37 | void init_heap() 38 | { 39 | heap_first = 0; 40 | } 41 | 42 | void *kmalloc(uint32_t len) 43 | { 44 | // 加上头大小 45 | len += sizeof(header_t); 46 | 47 | header_t *cur_header = heap_first; 48 | header_t *prev_header = 0; 49 | 50 | // 遍历 heap 链表 51 | while (cur_header) { 52 | // 如果大小合适 53 | if (cur_header->allocated == 0 && cur_header->length >= len) { 54 | split_chunk(cur_header, len); 55 | cur_header->allocated = 1; 56 | return (void *)((uint32_t)cur_header + sizeof(header_t)); 57 | } 58 | 59 | prev_header = cur_header; 60 | cur_header = cur_header->next; 61 | } 62 | 63 | // 没找到合适的内存块 64 | uint32_t chunk_start; 65 | if (prev_header) { 66 | chunk_start = (uint32_t)prev_header + prev_header->length; 67 | // 第一次分配 68 | } else { 69 | chunk_start = HEAP_START; 70 | heap_first = (header_t *) chunk_start; 71 | } 72 | 73 | // 扩展堆内存区域 74 | alloc_chunk(chunk_start, len); 75 | 76 | cur_header = (header_t *)chunk_start; 77 | cur_header->allocated = 1; 78 | cur_header->length = len; 79 | 80 | if (prev_header) prev_header->next = cur_header; 81 | cur_header->prev = prev_header; 82 | cur_header->next = 0; 83 | 84 | return (void *)(chunk_start + sizeof(header_t)); 85 | } 86 | 87 | void kfree(void *p) 88 | { 89 | header_t *header = (header_t *)((uint32_t)p - sizeof(header_t)); 90 | header->allocated = 0; 91 | 92 | // 粘合内存 93 | glue_chunk(header); 94 | } 95 | 96 | // 扩展堆内存 97 | void alloc_chunk(uint32_t start, uint32_t len) 98 | { 99 | // 循环申请页面 100 | while (start + len > heap_max) { 101 | uint32_t page = pmm_alloc_page(); 102 | // 修改页表 103 | map(pgd_kern, heap_max, page, PAGE_PRESENT | PAGE_WRITE); 104 | heap_max += PAGE_SIZE; 105 | } 106 | } 107 | 108 | void free_chunk(header_t *chunk) 109 | { 110 | // 修改指针 111 | if (chunk->prev == 0) { 112 | heap_first = 0; 113 | } else { 114 | chunk->prev->next = 0; 115 | } 116 | 117 | // 空闲的内存超过 1 页就释放掉 118 | while ((heap_max - PAGE_SIZE) > (uint32_t)chunk) { 119 | heap_max -= PAGE_SIZE; 120 | 121 | uint32_t page; 122 | get_mapping(pgd_kern, heap_max, &page); 123 | unmap(pgd_kern, heap_max); 124 | pmm_free_page(page); 125 | } 126 | } 127 | 128 | void split_chunk(header_t *chunk, uint32_t len) 129 | { 130 | // 剩余空间要能存下一个 header 131 | if (chunk->length - len > sizeof(header_t)) { 132 | header_t *new_chunk = (header_t *)((uint32_t)chunk + len); 133 | 134 | // 新 chunk 插入 heap 链表 135 | new_chunk->prev = chunk; 136 | new_chunk->next = chunk->next; 137 | if (chunk->next) chunk->next->prev = new_chunk; 138 | chunk->next = new_chunk; 139 | 140 | // 长度修改 141 | new_chunk->allocated = 0; 142 | new_chunk->length = chunk->length - len; 143 | chunk->length = len; 144 | } 145 | } 146 | 147 | void glue_chunk(header_t *chunk) 148 | { 149 | // 合并后面的 150 | if (chunk->next && chunk->next->allocated == 0) { 151 | chunk->length += chunk->next->length; 152 | 153 | chunk->next = chunk->next->next; 154 | if(chunk->next) chunk->next->prev = chunk; 155 | } 156 | 157 | // 合并前面的 158 | if (chunk->prev && chunk->prev->allocated == 0) { 159 | chunk->prev->length += chunk->length; 160 | 161 | chunk->prev->next = chunk->next; 162 | if(chunk->next) chunk->next->prev = chunk->prev; 163 | chunk = chunk->prev; 164 | } 165 | 166 | // 如果后面没有内存块了直接释放 167 | if (chunk->next == 0) { 168 | free_chunk(chunk); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /kern/driver/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "keyboard.h" 2 | #include "keymap.h" 3 | #include "tty.h" 4 | #include "common.h" 5 | #include "idt.h" 6 | #include "debug.h" 7 | #include "string.h" 8 | 9 | // 输入缓冲区 10 | kb_input_t kb_in; 11 | 12 | static int code_with_E0; 13 | static int shift_l; /* l shift state */ 14 | static int shift_r; /* r shift state */ 15 | static int alt_l; /* l alt state */ 16 | static int alt_r; /* r left state */ 17 | static int ctrl_l; /* l ctrl state */ 18 | static int ctrl_r; /* l ctrl state */ 19 | static int caps_lock; /* Caps Lock */ 20 | static int num_lock; /* Num Lock */ 21 | static int scroll_lock; /* Scroll Lock*/ 22 | static int column; 23 | 24 | static uint8_t get_byte_from_kbuf(); 25 | 26 | // 键盘事件处理 27 | void keyboard_handler(pt_regs_t *regs) 28 | { 29 | // 从8042读取数据 30 | uint8_t scan_code = inb(0x60); 31 | if (kb_in.count < KB_IN_BYTES) { 32 | *(kb_in.p_tail) = scan_code; 33 | ++kb_in.p_tail; 34 | if (kb_in.p_tail == kb_in.buff + KB_IN_BYTES) { 35 | kb_in.p_tail = kb_in.buff; 36 | } 37 | ++kb_in.count; 38 | } 39 | // 缓冲区满了直接丢弃 40 | } 41 | 42 | // 初始化键盘 43 | void init_keyboard() 44 | { 45 | //初始化缓冲区 46 | kb_in.count = 0; 47 | kb_in.p_head = kb_in.p_tail = kb_in.buff; 48 | 49 | // 初始化标志 50 | shift_l = shift_r = 0; 51 | alt_l = alt_r = 0; 52 | ctrl_l = ctrl_r = 0; 53 | 54 | // 注册中断处理函数 55 | register_interrupt_handler(33, keyboard_handler); 56 | } 57 | 58 | // 从缓冲区读取 59 | void keyboard_read() 60 | { 61 | uint8_t scan_code; 62 | char output[2]; 63 | int make; // TRUE, make code / FALSE, break code 64 | uint32_t key = 0; // 表示一个按键 65 | uint32_t *keyrow; // 指向 keymap 的某一行 66 | 67 | bzero(output, sizeof(output)); 68 | 69 | if (kb_in.count > 0) { 70 | code_with_E0 = 0; 71 | scan_code = get_byte_from_kbuf(); 72 | 73 | /* 下面开始解析扫描码 */ 74 | if (scan_code == 0xE1) { 75 | int i; 76 | uint8_t pausebrk_scode[] = {0xE1, 0x1D, 0x45, 77 | 0xE1, 0x9D, 0xC5}; 78 | int is_pausebreak = 1; 79 | for(i = 1; i < 6; i++){ 80 | if (get_byte_from_kbuf() != pausebrk_scode[i]) { 81 | is_pausebreak = 0; 82 | break; 83 | } 84 | } 85 | if (is_pausebreak) { 86 | key = PAUSEBREAK; 87 | } 88 | } 89 | else if (scan_code == 0xE0) { 90 | scan_code = get_byte_from_kbuf(); 91 | 92 | /* PrintScreen 被按下 */ 93 | if (scan_code == 0x2A) { 94 | if (get_byte_from_kbuf() == 0xE0) { 95 | if (get_byte_from_kbuf() == 0x37) { 96 | key = PRINTSCREEN; 97 | make = 1; 98 | } 99 | } 100 | } 101 | /* PrintScreen 被释放 */ 102 | if (scan_code == 0xB7) { 103 | if (get_byte_from_kbuf() == 0xE0) { 104 | if (get_byte_from_kbuf() == 0xAA) { 105 | key = PRINTSCREEN; 106 | make = 0; 107 | } 108 | } 109 | } 110 | /* 不是 PrintScreen, 此时scan_code为 0xE0 紧跟的那个值. */ 111 | if (key == 0) { 112 | code_with_E0 = 1; 113 | } 114 | } 115 | 116 | if ((key != PAUSEBREAK) && (key != PRINTSCREEN)) { 117 | /* 首先判断Make Code 还是 Break Code */ 118 | make = (scan_code & FLAG_BREAK ? 0 : 1); 119 | 120 | /* 先定位到 keymap 中的行 */ 121 | keyrow = &keymap[(scan_code & 0x7F) * MAP_COLS]; 122 | 123 | column = 0; 124 | if (shift_l || shift_r) { 125 | column = 1; 126 | } 127 | if (code_with_E0) { 128 | column = 2; 129 | code_with_E0 = 0; 130 | } 131 | 132 | key = keyrow[column]; 133 | 134 | switch(key) { 135 | case SHIFT_L: 136 | shift_l = make; 137 | break; 138 | case SHIFT_R: 139 | shift_r = make; 140 | break; 141 | case CTRL_L: 142 | ctrl_l = make; 143 | break; 144 | case CTRL_R: 145 | ctrl_r = make; 146 | break; 147 | case ALT_L: 148 | alt_l = make; 149 | break; 150 | case ALT_R: 151 | alt_l = make; 152 | break; 153 | default: 154 | break; 155 | } 156 | 157 | if (make) { /* 忽略 Break Code */ 158 | key |= shift_l ? FLAG_SHIFT_L : 0; 159 | key |= shift_r ? FLAG_SHIFT_R : 0; 160 | key |= ctrl_l ? FLAG_CTRL_L : 0; 161 | key |= ctrl_r ? FLAG_CTRL_R : 0; 162 | key |= alt_l ? FLAG_ALT_L : 0; 163 | key |= alt_r ? FLAG_ALT_R : 0; 164 | 165 | in_process(key); 166 | } 167 | } 168 | } 169 | } 170 | 171 | // 从缓冲区读取 172 | uint8_t get_byte_from_kbuf() 173 | { 174 | uint8_t scan_code; 175 | 176 | while (kb_in.count <= 0) {} /* 等待下一个字节到来 */ 177 | 178 | // 进入临界区 179 | cli(); 180 | scan_code = *(kb_in.p_head); 181 | kb_in.p_head++; 182 | if (kb_in.p_head == kb_in.buff + KB_IN_BYTES) { 183 | kb_in.p_head = kb_in.buff; 184 | } 185 | kb_in.count--; 186 | sti(); 187 | 188 | return scan_code; 189 | } 190 | 191 | -------------------------------------------------------------------------------- /kern/driver/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_KEYBOARD_H 2 | #define INCLUDE_KEYBOARD_H 3 | 4 | #include "types.h" 5 | 6 | // 宏定义 7 | #define KB_IN_BYTES 32 /* size of keyboard input buffer */ 8 | #define MAP_COLS 3 /* Number of columns in keymap */ 9 | #define NR_SCAN_CODES 0x80 /* Number of scan codes (rows in keymap) */ 10 | 11 | #define FLAG_BREAK 0x0080 /* Break Code */ 12 | #define FLAG_EXT 0x0100 /* Normal function keys */ 13 | #define FLAG_SHIFT_L 0x0200 /* Shift key */ 14 | #define FLAG_SHIFT_R 0x0400 /* Shift key */ 15 | #define FLAG_CTRL_L 0x0800 /* Control key */ 16 | #define FLAG_CTRL_R 0x1000 /* Control key */ 17 | #define FLAG_ALT_L 0x2000 /* Alternate key */ 18 | #define FLAG_ALT_R 0x4000 /* Alternate key */ 19 | #define FLAG_PAD 0x8000 /* keys in num pad */ 20 | 21 | #define MASK_RAW 0x01FF /* raw key value = code passed to tty & MASK_RAW 22 | the value can be found either in the keymap column 0 23 | or in the list below */ 24 | 25 | /* Special keys */ 26 | #define ESC (0x01 + FLAG_EXT) /* Esc */ 27 | #define TAB (0x02 + FLAG_EXT) /* Tab */ 28 | #define ENTER (0x03 + FLAG_EXT) /* Enter */ 29 | #define BACKSPACE (0x04 + FLAG_EXT) /* BackSpace */ 30 | 31 | #define GUI_L (0x05 + FLAG_EXT) /* L GUI */ 32 | #define GUI_R (0x06 + FLAG_EXT) /* R GUI */ 33 | #define APPS (0x07 + FLAG_EXT) /* APPS */ 34 | 35 | /* Shift, Ctrl, Alt */ 36 | #define SHIFT_L (0x08 + FLAG_EXT) /* L Shift */ 37 | #define SHIFT_R (0x09 + FLAG_EXT) /* R Shift */ 38 | #define CTRL_L (0x0A + FLAG_EXT) /* L Ctrl */ 39 | #define CTRL_R (0x0B + FLAG_EXT) /* R Ctrl */ 40 | #define ALT_L (0x0C + FLAG_EXT) /* L Alt */ 41 | #define ALT_R (0x0D + FLAG_EXT) /* R Alt */ 42 | 43 | /* Lock keys */ 44 | #define CAPS_LOCK (0x0E + FLAG_EXT) /* Caps Lock */ 45 | #define NUM_LOCK (0x0F + FLAG_EXT) /* Number Lock */ 46 | #define SCROLL_LOCK (0x10 + FLAG_EXT) /* Scroll Lock */ 47 | 48 | /* Function keys */ 49 | #define F1 (0x11 + FLAG_EXT) /* F1 */ 50 | #define F2 (0x12 + FLAG_EXT) /* F2 */ 51 | #define F3 (0x13 + FLAG_EXT) /* F3 */ 52 | #define F4 (0x14 + FLAG_EXT) /* F4 */ 53 | #define F5 (0x15 + FLAG_EXT) /* F5 */ 54 | #define F6 (0x16 + FLAG_EXT) /* F6 */ 55 | #define F7 (0x17 + FLAG_EXT) /* F7 */ 56 | #define F8 (0x18 + FLAG_EXT) /* F8 */ 57 | #define F9 (0x19 + FLAG_EXT) /* F9 */ 58 | #define F10 (0x1A + FLAG_EXT) /* F10 */ 59 | #define F11 (0x1B + FLAG_EXT) /* F11 */ 60 | #define F12 (0x1C + FLAG_EXT) /* F12 */ 61 | 62 | /* Control Pad */ 63 | #define PRINTSCREEN (0x1D + FLAG_EXT) /* Print Screen */ 64 | #define PAUSEBREAK (0x1E + FLAG_EXT) /* Pause/Break */ 65 | #define INSERT (0x1F + FLAG_EXT) /* Insert */ 66 | #define DELETE (0x20 + FLAG_EXT) /* Delete */ 67 | #define HOME (0x21 + FLAG_EXT) /* Home */ 68 | #define END (0x22 + FLAG_EXT) /* End */ 69 | #define PAGEUP (0x23 + FLAG_EXT) /* Page Up */ 70 | #define PAGEDOWN (0x24 + FLAG_EXT) /* Page Down */ 71 | #define UP (0x25 + FLAG_EXT) /* Up */ 72 | #define DOWN (0x26 + FLAG_EXT) /* Down */ 73 | #define LEFT (0x27 + FLAG_EXT) /* Left */ 74 | #define RIGHT (0x28 + FLAG_EXT) /* Right */ 75 | 76 | /* ACPI keys */ 77 | #define POWER (0x29 + FLAG_EXT) /* Power */ 78 | #define SLEEP (0x2A + FLAG_EXT) /* Sleep */ 79 | #define WAKE (0x2B + FLAG_EXT) /* Wake Up */ 80 | 81 | /* Num Pad */ 82 | #define PAD_SLASH (0x2C + FLAG_EXT) /* / */ 83 | #define PAD_STAR (0x2D + FLAG_EXT) /* * */ 84 | #define PAD_MINUS (0x2E + FLAG_EXT) /* - */ 85 | #define PAD_PLUS (0x2F + FLAG_EXT) /* + */ 86 | #define PAD_ENTER (0x30 + FLAG_EXT) /* Enter */ 87 | #define PAD_DOT (0x31 + FLAG_EXT) /* . */ 88 | #define PAD_0 (0x32 + FLAG_EXT) /* 0 */ 89 | #define PAD_1 (0x33 + FLAG_EXT) /* 1 */ 90 | #define PAD_2 (0x34 + FLAG_EXT) /* 2 */ 91 | #define PAD_3 (0x35 + FLAG_EXT) /* 3 */ 92 | #define PAD_4 (0x36 + FLAG_EXT) /* 4 */ 93 | #define PAD_5 (0x37 + FLAG_EXT) /* 5 */ 94 | #define PAD_6 (0x38 + FLAG_EXT) /* 6 */ 95 | #define PAD_7 (0x39 + FLAG_EXT) /* 7 */ 96 | #define PAD_8 (0x3A + FLAG_EXT) /* 8 */ 97 | #define PAD_9 (0x3B + FLAG_EXT) /* 9 */ 98 | #define PAD_UP PAD_8 /* Up */ 99 | #define PAD_DOWN PAD_2 /* Down */ 100 | #define PAD_LEFT PAD_4 /* Left */ 101 | #define PAD_RIGHT PAD_6 /* Right */ 102 | #define PAD_HOME PAD_7 /* Home */ 103 | #define PAD_END PAD_1 /* End */ 104 | #define PAD_PAGEUP PAD_9 /* Page Up */ 105 | #define PAD_PAGEDOWN PAD_3 /* Page Down */ 106 | #define PAD_INS PAD_0 /* Ins */ 107 | #define PAD_MID PAD_5 /* Middle key */ 108 | #define PAD_DEL PAD_DOT /* Del */ 109 | 110 | // 输入缓冲 111 | typedef struct kb_input { 112 | char* p_head; /* 队列首部 */ 113 | char* p_tail; /* 队列尾部,指向下一个位置 */ 114 | int count; /* 缓冲区中共有多少字节 */ 115 | char buff[KB_IN_BYTES]; /* 缓冲区 */ 116 | } kb_input_t; 117 | 118 | // 键盘初始化 119 | void init_keyboard(); 120 | 121 | // 从缓冲区读取 122 | void keyboard_read(); 123 | 124 | #endif // INCLUDE_KEYBOARD_H 125 | -------------------------------------------------------------------------------- /kern/debug/printk.c: -------------------------------------------------------------------------------- 1 | // printk 可以在 Linux 内核源码中找到 2 | #include "string.h" 3 | #include "debug.h" 4 | 5 | static int vsprintf(char *buff, const char *format, va_list args); 6 | 7 | void printk(const char *format, ...) 8 | { 9 | static char buff[1024]; 10 | va_list args; 11 | int i; 12 | 13 | va_start(args, format); 14 | i = vsprintf(buff, format, args); 15 | va_end(args); 16 | 17 | buff[i] = '\0'; 18 | 19 | console_write(buff); 20 | } 21 | 22 | void printk_color(real_color_t back, real_color_t fore, const char *format, ...) 23 | { 24 | static char buff[1024]; 25 | va_list args; 26 | int i; 27 | 28 | va_start(args, format); 29 | i = vsprintf(buff, format, args); 30 | va_end(args); 31 | 32 | buff[i] = '\0'; 33 | 34 | console_write_color(buff, back, fore); 35 | } 36 | 37 | #define is_digit(c) ((c) >= '0' && (c) <= '9') 38 | 39 | static int skip_atoi(const char **s) 40 | { 41 | int i = 0; 42 | 43 | while (is_digit(**s)) { 44 | i = i * 10 + *((*s)++) - '0'; 45 | } 46 | 47 | return i; 48 | } 49 | 50 | #define ZEROPAD 1 // pad with zero 51 | #define SIGN 2 // unsigned/signed long 52 | #define PLUS 4 // show plus 53 | #define SPACE 8 // space if plus 54 | #define LEFT 16 // left justified 55 | #define SPECIAL 32 // 0x 56 | #define SMALL 64 // use 'abcdef' instead of 'ABCDEF' 57 | 58 | #define do_div(n,base) ({ \ 59 | int __res; \ 60 | __asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \ 61 | __res; }) 62 | 63 | static char *number(char *str, int num, int base, int size, int precision, int type) 64 | { 65 | char c, sign, tmp[36]; 66 | const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 67 | int i; 68 | 69 | if (type & SMALL) { 70 | digits ="0123456789abcdefghijklmnopqrstuvwxyz"; 71 | } 72 | if (type & LEFT) { 73 | type &= ~ZEROPAD; 74 | } 75 | if (base < 2 || base > 36) { 76 | return 0; 77 | } 78 | 79 | c = (type & ZEROPAD) ? '0' : ' ' ; 80 | 81 | if (type & SIGN && num < 0) { 82 | sign = '-'; 83 | num = -num; 84 | } else { 85 | sign = (type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0); 86 | } 87 | 88 | if (sign) { 89 | size--; 90 | } 91 | if (type & SPECIAL) { 92 | if (base == 16) { 93 | size -= 2; 94 | } else if (base == 8) { 95 | size--; 96 | } 97 | } 98 | i = 0; 99 | if (num == 0) { 100 | tmp[i++] = '0'; 101 | } else { 102 | while (num != 0) { 103 | tmp[i++] = digits[do_div(num,base)]; 104 | } 105 | } 106 | 107 | if (i > precision) { 108 | precision = i; 109 | } 110 | size -= precision; 111 | 112 | if (!(type&(ZEROPAD+LEFT))) { 113 | while (size-- > 0) { 114 | *str++ = ' '; 115 | } 116 | } 117 | if (sign) { 118 | *str++ = sign; 119 | } 120 | if (type & SPECIAL) { 121 | if (base == 8) { 122 | *str++ = '0'; 123 | } else if (base == 16) { 124 | *str++ = '0'; 125 | *str++ = digits[33]; 126 | } 127 | } 128 | if (!(type&LEFT)) { 129 | while (size-- > 0) { 130 | *str++ = c; 131 | } 132 | } 133 | while (i < precision--) { 134 | *str++ = '0'; 135 | } 136 | while (i-- > 0) { 137 | *str++ = tmp[i]; 138 | } 139 | while (size-- > 0) { 140 | *str++ = ' '; 141 | } 142 | 143 | return str; 144 | } 145 | 146 | static int vsprintf(char *buff, const char *format, va_list args) 147 | { 148 | int len; 149 | int i; 150 | char *str; 151 | char *s; 152 | int *ip; 153 | 154 | int flags; // flags to number() 155 | 156 | int field_width; // width of output field 157 | int precision; // min. # of digits for integers; max number of chars for from string 158 | 159 | for (str = buff ; *format ; ++format) { 160 | if (*format != '%') { 161 | *str++ = *format; 162 | continue; 163 | } 164 | 165 | flags = 0; 166 | repeat: 167 | ++format; // this also skips first '%' 168 | switch (*format) { 169 | case '-': flags |= LEFT; 170 | goto repeat; 171 | case '+': flags |= PLUS; 172 | goto repeat; 173 | case ' ': flags |= SPACE; 174 | goto repeat; 175 | case '#': flags |= SPECIAL; 176 | goto repeat; 177 | case '0': flags |= ZEROPAD; 178 | goto repeat; 179 | } 180 | 181 | // get field width 182 | field_width = -1; 183 | if (is_digit(*format)) { 184 | field_width = skip_atoi(&format); 185 | } else if (*format == '*') { 186 | // it's the next argument 187 | field_width = va_arg(args, int); 188 | if (field_width < 0) { 189 | field_width = -field_width; 190 | flags |= LEFT; 191 | } 192 | } 193 | 194 | // get the precision 195 | precision = -1; 196 | if (*format == '.') { 197 | ++format; 198 | if (is_digit(*format)) { 199 | precision = skip_atoi(&format); 200 | } else if (*format == '*') { 201 | // it's the next argument 202 | precision = va_arg(args, int); 203 | } 204 | if (precision < 0) { 205 | precision = 0; 206 | } 207 | } 208 | 209 | // get the conversion qualifier 210 | //int qualifier = -1; // 'h', 'l', or 'L' for integer fields 211 | if (*format == 'h' || *format == 'l' || *format == 'L') { 212 | //qualifier = *format; 213 | ++format; 214 | } 215 | 216 | switch (*format) { 217 | case 'c': 218 | if (!(flags & LEFT)) { 219 | while (--field_width > 0) { 220 | *str++ = ' '; 221 | } 222 | } 223 | *str++ = (unsigned char) va_arg(args, int); 224 | while (--field_width > 0) { 225 | *str++ = ' '; 226 | } 227 | break; 228 | 229 | case 's': 230 | s = va_arg(args, char *); 231 | len = strlen(s); 232 | if (precision < 0) { 233 | precision = len; 234 | } else if (len > precision) { 235 | len = precision; 236 | } 237 | 238 | if (!(flags & LEFT)) { 239 | while (len < field_width--) { 240 | *str++ = ' '; 241 | } 242 | } 243 | for (i = 0; i < len; ++i) { 244 | *str++ = *s++; 245 | } 246 | while (len < field_width--) { 247 | *str++ = ' '; 248 | } 249 | break; 250 | 251 | case 'o': 252 | str = number(str, va_arg(args, unsigned long), 8, 253 | field_width, precision, flags); 254 | break; 255 | 256 | case 'p': 257 | if (field_width == -1) { 258 | field_width = 8; 259 | flags |= ZEROPAD; 260 | } 261 | str = number(str, (unsigned long) va_arg(args, void *), 16, 262 | field_width, precision, flags); 263 | break; 264 | 265 | case 'x': 266 | flags |= SMALL; 267 | case 'X': 268 | str = number(str, va_arg(args, unsigned long), 16, 269 | field_width, precision, flags); 270 | break; 271 | 272 | case 'd': 273 | case 'i': 274 | flags |= SIGN; 275 | case 'u': 276 | str = number(str, va_arg(args, unsigned long), 10, 277 | field_width, precision, flags); 278 | break; 279 | case 'b': 280 | str = number(str, va_arg(args, unsigned long), 2, 281 | field_width, precision, flags); 282 | break; 283 | 284 | case 'n': 285 | ip = va_arg(args, int *); 286 | *ip = (str - buff); 287 | break; 288 | 289 | default: 290 | if (*format != '%') 291 | *str++ = '%'; 292 | if (*format) { 293 | *str++ = *format; 294 | } else { 295 | --format; 296 | } 297 | break; 298 | } 299 | } 300 | *str = '\0'; 301 | 302 | return (str -buff); 303 | } 304 | 305 | --------------------------------------------------------------------------------