好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 xfgzs 于 2026-1-2 18:30 编辑
这个内核包含了必要的引导程序、错误处理和保护模式设置。请注意,运行自制操作系统有风险,请在虚拟机或专用测试硬件上测试。
项目结构
[Plain Text] 纯文本查看 复制代码
simple_os/
├── Makefile # 构建配置
├── boot.asm # 引导加载程序
├── kernel.c # 内核主代码
├── kernel.asm # 内核汇编辅助
├── linker.ld # 链接脚本
├── drivers.c # 设备驱动
├── include/ # 头文件目录
│ ├── kernel.h
│ ├── ports.h
│ ├── vga.h
│ └── memory.h
└── isr.asm # 中断处理
1. Makefile - 构建配置文
[] 纯文本查看 复制代码
# 构建配置
CC = i686-elf-gcc
AS = nasm
LD = i686-elf-ld
OBJCOPY = i686-elf-objcopy
# 编译选项
CFLAGS = -ffreestanding -O2 -Wall -Wextra -I./include -std=gnu99 -m32 -masm=intel
ASFLAGS = -f elf32
LDFLAGS = -m elf_i386 -T linker.ld -nostdlib
# 输出文件
KERNEL = kernel.bin
BOOTLOADER = boot.bin
OS_IMAGE = os.img
# 源文件
BOOT_SRC = boot.asm
KERNEL_SRC = kernel.c kernel.asm drivers.c isr.asm
HEADERS = include/kernel.h include/ports.h include/vga.h include/memory.h
# 目标文件
OBJS = kernel.o kernel_asm.o drivers.o isr.o
# 默认目标
all: $(OS_IMAGE)
# 创建磁盘镜像
$(OS_IMAGE): $(BOOTLOADER) $(KERNEL)
dd if=/dev/zero of=$(OS_IMAGE) bs=512 count=2880
dd if=$(BOOTLOADER) of=$(OS_IMAGE) conv=notrunc
dd if=$(KERNEL) of=$(OS_IMAGE) seek=1 conv=notrunc
# 引导加载程序
$(BOOTLOADER): $(BOOT_SRC)
$(AS) -f bin $< -o $@
# 内核
$(KERNEL): $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^
# C源文件编译
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c $< -o $@
# 汇编源文件编译
%.o: %.asm
$(AS) $(ASFLAGS) $< -o $@
# 清理
clean:
rm -f *.o *.bin *.img
# 在QEMU中运行
run: $(OS_IMAGE)
qemu-system-i386 -drive format=raw,file=$(OS_IMAGE) -serial stdio
# 在QEMU中调试
debug: $(OS_IMAGE)
qemu-system-i386 -drive format=raw,file=$(OS_IMAGE) -serial stdio -d int -no-reboot -no-shutdown
.PHONY: all clean run debug
2. 引导加载程序 (boot.asm)
[] 纯文本查看 复制代码
; 引导加载程序 - 支持真实硬件
[org 0x7C00]
[bits 16]
; 引导扇区标志
BOOT_DRIVE db 0
KERNEL_OFFSET equ 0x1000
STACK_OFFSET equ 0x9000
start:
; 保存引导驱动器号
mov [BOOT_DRIVE], dl
; 设置栈
mov bp, STACK_OFFSET
mov sp, bp
; 清屏
mov ah, 0x00
mov al, 0x03 ; 文本模式 80x25
int 0x10
; 显示启动消息
mov si, MSG_BOOT
call print_string
; 加载内核
call load_kernel
; 检查扩展读取支持
call check_int13_extensions
; 启用A20线
call enable_a20
; 加载GDT
call load_gdt
; 切换到保护模式
call switch_to_protected_mode
; 永远不会执行到这里
jmp $
; ==================== 16位函数 ====================
; 打印字符串 (以0结尾)
print_string:
pusha
mov ah, 0x0E
.print_loop:
lodsb
cmp al, 0
je .done
int 0x10
jmp .print_loop
.done:
popa
ret
; 检查INT13扩展功能
check_int13_extensions:
pusha
mov ah, 0x41
mov bx, 0x55AA
mov dl, [BOOT_DRIVE]
int 0x13
jc .no_extensions
cmp bx, 0xAA55
jne .no_extensions
; 扩展功能可用
mov si, MSG_EXT_OK
call print_string
popa
ret
.no_extensions:
mov si, MSG_EXT_FAIL
call print_string
jmp $ ; 停止执行
; 启用A20地址线
enable_a20:
pusha
; 方法1: 通过键盘控制器
call .check_a20
jc .method1_done
; 方法2: BIOS中断
mov ax, 0x2401
int 0x15
call .check_a20
jc .method2_done
; 方法3: 快速A20门
in al, 0x92
test al, 2
jnz .method3_done
or al, 2
and al, 0xFE
out 0x92, al
.method3_done:
.method2_done:
.method1_done:
mov si, MSG_A20_OK
call print_string
popa
ret
.check_a20:
push es
push di
push si
; 设置段寄存器检查A20
xor ax, ax
mov es, ax
mov di, 0x0500
mov ax, 0xFFFF
mov ds, ax
mov si, 0x0510
; 保存原始值
mov al, [es:di]
push ax
mov al, [ds:si]
push ax
; 写入测试值
mov byte [es:di], 0x00
mov byte [ds:si], 0xFF
; 检查是否相同
mov al, [es:di]
cmp al, [ds:si]
; 恢复原始值
pop ax
mov [ds:si], al
pop ax
mov [es:di], al
pop si
pop di
pop es
je .a20_disabled ; 如果相同,A20禁用
clc ; 清除进位标志表示成功
ret
.a20_disabled:
stc ; 设置进位标志表示失败
ret
; 加载内核到内存
load_kernel:
pusha
mov si, MSG_LOAD_KERNEL
call print_string
; 设置目标地址
mov bx, KERNEL_OFFSET
mov es, bx
xor bx, bx
; 尝试使用扩展读取
mov ah, 0x42
mov dl, [BOOT_DRIVE]
mov si, DAP
int 0x13
jnc .success
; 扩展读取失败,使用传统方法
mov ah, 0x02
mov al, 64 ; 读取64个扇区(32KB)
mov ch, 0 ; 柱面0
mov cl, 2 ; 从扇区2开始
mov dh, 0 ; 磁头0
mov dl, [BOOT_DRIVE]
int 0x13
jnc .success
; 读取失败
mov si, MSG_DISK_ERROR
call print_string
jmp $
.success:
mov si, MSG_KERNEL_LOADED
call print_string
popa
ret
; 磁盘地址包 (用于扩展读取)
DAP:
db 0x10 ; 包大小
db 0 ; 未使用
dw 64 ; 扇区数
dd 0 ; 目标偏移
dd KERNEL_OFFSET << 4 ; 段:偏移 -> 线性地址
dq 1 ; 起始LBA扇区(扇区1是引导扇区)
; 加载GDT
load_gdt:
cli
lgdt [gdt_descriptor]
ret
; 切换到保护模式
switch_to_protected_mode:
cli
mov eax, cr0
or eax, 0x1
mov cr0, eax
; 远跳转到32位代码段
jmp CODE_SEG:protected_mode_start
; ==================== 32位代码 ====================
[bits 32]
protected_mode_start:
; 设置数据段
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; 设置栈指针
mov esp, 0x90000
; 跳转到内核
jmp KERNEL_OFFSET
; ==================== GDT ====================
gdt_start:
; 空描述符
dq 0
; 代码段描述符 (0x08)
dw 0xFFFF ; 段界限 (0-15)
dw 0x0000 ; 基地址 (0-15)
db 0x00 ; 基地址 (16-23)
db 10011010b ; P=1, DPL=00, S=1, Type=1010 (可执行, 可读, 非一致性)
db 11001111b ; G=1, D/B=1, L=0, AVL=0, 段界限 (16-19)
db 0x00 ; 基地址 (24-31)
; 数据段描述符 (0x10)
dw 0xFFFF ; 段界限
dw 0x0000 ; 基地址
db 0x00 ; 基地址
db 10010010b ; P=1, DPL=00, S=1, Type=0010 (可读可写, 向上扩展)
db 11001111b ; G=1, D/B=1, L=0, AVL=0
db 0x00 ; 基地址
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; GDT大小
dd gdt_start ; GDT地址
CODE_SEG equ gdt_start + 8
DATA_SEG equ gdt_start + 16
; ==================== 消息 ====================
MSG_BOOT db "Simple OS Bootloader v1.0", 0x0D, 0x0A, 0
MSG_LOAD_KERNEL db "Loading kernel...", 0x0D, 0x0A, 0
MSG_KERNEL_LOADED db "Kernel loaded successfully", 0x0D, 0x0A, 0
MSG_EXT_OK db "INT13 extensions available", 0x0D, 0x0A, 0
MSG_EXT_FAIL db "INT13 extensions not available", 0x0D, 0x0A, 0
MSG_A20_OK db "A20 line enabled", 0x0D, 0x0A, 0
MSG_DISK_ERROR db "Disk read error!", 0x0D, 0x0A, 0
; ==================== 引导扇区填充 ====================
times 510-($-$$) db 0
dw 0xAA55
3. 内核头文件 (include/kernel.h)
[C] 纯文本查看 复制代码
#ifndef KERNEL_H
#define KERNEL_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// 内核版本
#define KERNEL_VERSION "1.0.0"
#define KERNEL_NAME "SimpleOS"
// 内存布局
#define KERNEL_START 0x00100000
#define KERNEL_END 0x00200000
#define MEMORY_START 0x00200000
#define MEMORY_END 0x01000000
// 错误码
typedef enum {
ERR_SUCCESS = 0,
ERR_MEMORY_ALLOC_FAILED,
ERR_MEMORY_FREE_FAILED,
ERR_DISK_READ_FAILED,
ERR_DISK_WRITE_FAILED,
ERR_DEVICE_NOT_FOUND,
ERR_INVALID_PARAMETER,
ERR_NOT_IMPLEMENTED,
ERR_HARDWARE_FAILURE,
ERR_OUT_OF_MEMORY,
ERR_PERMISSION_DENIED
} ErrorCode;
// 内存管理结构
typedef struct {
uint32_t total_pages;
uint32_t free_pages;
uint32_t used_pages;
uint32_t* bitmap;
uint32_t bitmap_size;
} MemoryManager;
// 进程控制块(简化版)
typedef struct {
uint32_t pid;
uint32_t esp;
uint32_t ebp;
uint32_t eip;
uint32_t state; // 0=就绪, 1=运行, 2=阻塞
} Process;
// 系统信息
typedef struct {
char kernel_name[32];
char kernel_version[16];
uint32_t total_memory;
uint32_t free_memory;
uint32_t cpu_speed_mhz;
uint8_t cpu_vendor[13];
bool has_fpu;
bool has_mmx;
bool has_sse;
} SystemInfo;
// 函数声明
void kernel_main();
void panic(const char* message);
void halt();
void reboot();
// 内存管理
void* kmalloc(size_t size);
void kfree(void* ptr);
void* krealloc(void* ptr, size_t size);
void* kcalloc(size_t num, size_t size);
void memory_dump();
// 系统信息
SystemInfo* get_system_info();
void print_system_info();
#endif // KERNEL_H
4. 端口操作头文件 (include/ports.h)
[C] 纯文本查看 复制代码
#ifndef PORTS_H
#define PORTS_H
#include <stdint.h>
// 端口读写函数
static inline void outb(uint16_t port, uint8_t value) {
asm volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
}
static inline uint8_t inb(uint16_t port) {
uint8_t ret;
asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outw(uint16_t port, uint16_t value) {
asm volatile ("outw %0, %1" : : "a"(value), "Nd"(port));
}
static inline uint16_t inw(uint16_t port) {
uint16_t ret;
asm volatile ("inw %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outl(uint16_t port, uint32_t value) {
asm volatile ("outl %0, %1" : : "a"(value), "Nd"(port));
}
static inline uint32_t inl(uint16_t port) {
uint32_t ret;
asm volatile ("inl %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
// 端口等待(用于慢速设备)
static inline void io_wait(void) {
outb(0x80, 0);
}
// 常用端口定义
#define PORT_PIC1_CMD 0x20
#define PORT_PIC1_DATA 0x21
#define PORT_PIC2_CMD 0xA0
#define PORT_PIC2_DATA 0xA1
#define PORT_PIT_CH0 0x40
#define PORT_PIT_CH1 0x41
#define PORT_PIT_CH2 0x42
#define PORT_PIT_CMD 0x43
#define PORT_PS2_DATA 0x60
#define PORT_PS2_STATUS 0x64
#define PORT_PS2_CMD 0x64
#define PORT_VGA_CTRL 0x3D4
#define PORT_VGA_DATA 0x3D5
#define PORT_SERIAL1 0x3F8
#define PORT_SERIAL2 0x2F8
#endif // PORTS_H
5. VGA驱动头文件 (include/vga.h)
[C] 纯文本查看 复制代码
#ifndef VGA_H
#define VGA_H
#include <stdint.h>
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
// VGA颜色
enum vga_color {
VGA_COLOR_BLACK = 0,
VGA_COLOR_BLUE = 1,
VGA_COLOR_GREEN = 2,
VGA_COLOR_CYAN = 3,
VGA_COLOR_RED = 4,
VGA_COLOR_MAGENTA = 5,
VGA_COLOR_BROWN = 6,
VGA_COLOR_LIGHT_GREY = 7,
VGA_COLOR_DARK_GREY = 8,
VGA_COLOR_LIGHT_BLUE = 9,
VGA_COLOR_LIGHT_GREEN = 10,
VGA_COLOR_LIGHT_CYAN = 11,
VGA_COLOR_LIGHT_RED = 12,
VGA_COLOR_LIGHT_MAGENTA = 13,
VGA_COLOR_LIGHT_BROWN = 14,
VGA_COLOR_WHITE = 15,
};
// VGA字符属性
static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
return fg | bg << 4;
}
static inline uint16_t vga_entry(unsigned char uc, uint8_t color) {
return (uint16_t) uc | (uint16_t) color << 8;
}
// VGA函数
void vga_initialize(void);
void vga_clear(void);
void vga_setcolor(uint8_t color);
void vga_putentryat(char c, uint8_t color, size_t x, size_t y);
void vga_putchar(char c);
void vga_write(const char* data, size_t size);
void vga_writestring(const char* data);
void vga_setcursor(size_t x, size_t y);
void vga_scroll(void);
void vga_printf(const char* format, ...);
// 全局VGA缓冲区
extern uint16_t* vga_buffer;
#endif // VGA_H
6. 内存管理头文件 (include/memory.h)
[C] 纯文本查看 复制代码
#ifndef MEMORY_H
#define MEMORY_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// 内存分页
#define PAGE_SIZE 4096
#define PAGE_DIRECTORY_INDEX(x) ((x) >> 22)
#define PAGE_TABLE_INDEX(x) (((x) >> 12) & 0x3FF)
#define PAGE_GET_PHYSICAL_ADDRESS(x) ((x) & ~0xFFF)
// 内存区域类型
typedef enum {
MEMORY_FREE = 0,
MEMORY_USED,
MEMORY_RESERVED,
MEMORY_ACPI_RECLAIMABLE,
MEMORY_ACPI_NVS,
MEMORY_BAD
} MemoryType;
// 内存区域描述符
typedef struct {
uint32_t base_low;
uint32_t base_high;
uint32_t length_low;
uint32_t length_high;
uint32_t type;
uint32_t acpi_extended;
} __attribute__((packed)) MemoryRegion;
// 页目录项
typedef struct {
uint32_t present : 1;
uint32_t rw : 1;
uint32_t user : 1;
uint32_t write_through : 1;
uint32_t cache_disable : 1;
uint32_t accessed : 1;
uint32_t dirty : 1;
uint32_t page_size : 1;
uint32_t global : 1;
uint32_t available : 3;
uint32_t page_table_base : 20;
} __attribute__((packed)) PageDirectoryEntry;
// 页表项
typedef struct {
uint32_t present : 1;
uint32_t rw : 1;
uint32_t user : 1;
uint32_t write_through : 1;
uint32_t cache_disable : 1;
uint32_t accessed : 1;
uint32_t dirty : 1;
uint32_t pat : 1;
uint32_t global : 1;
uint32_t available : 3;
uint32_t page_base : 20;
} __attribute__((packed)) PageTableEntry;
// 内存统计
typedef struct {
uint32_t total_memory;
uint32_t free_memory;
uint32_t used_memory;
uint32_t reserved_memory;
uint32_t kernel_memory;
uint32_t page_frame_count;
uint32_t page_frame_free;
} MemoryStats;
// 函数声明
void memory_initialize(uint32_t memory_size);
void* memory_allocate(size_t size);
void memory_free(void* ptr);
MemoryStats* memory_get_stats(void);
void memory_dump_regions(void);
bool memory_region_is_free(uint32_t base, uint32_t length);
uint32_t memory_get_total(void);
uint32_t memory_get_free(void);
#endif // MEMORY_H
7. 内核主文件 (kernel.c)
[C] 纯文本查看 复制代码
#include "kernel.h"
#include "vga.h"
#include "ports.h"
#include "memory.h"
#include <stdarg.h>
// 全局变量
uint16_t* vga_buffer = (uint16_t*)0xB8000;
size_t vga_row = 0;
size_t vga_column = 0;
uint8_t vga_color = 0;
MemoryManager mem_manager;
SystemInfo sys_info;
// 内核入口点
void kernel_main() {
// 初始化VGA
vga_initialize();
vga_writestring("SimpleOS Kernel v");
vga_writestring(KERNEL_VERSION);
vga_writestring("\n");
vga_writestring("==================\n\n");
// 检测CPU
detect_cpu();
// 检测内存
detect_memory();
// 初始化内存管理
memory_initialize(sys_info.total_memory);
// 初始化中断
init_interrupts();
// 初始化设备
init_devices();
// 显示系统信息
print_system_info();
// 显示欢迎消息
vga_writestring("\nSystem initialized successfully!\n");
vga_writestring("Type 'help' for available commands\n");
vga_writestring("> ");
// 主循环
kernel_loop();
}
// 内核主循环
void kernel_loop() {
char input_buffer[256];
size_t input_index = 0;
while (1) {
// 检查键盘输入
if (keyboard_available()) {
char c = keyboard_read();
if (c == '\n' || c == '\r') {
// 执行命令
input_buffer[input_index] = '\0';
execute_command(input_buffer);
// 重置输入缓冲区
input_index = 0;
vga_writestring("\n> ");
} else if (c == '\b' || c == 0x7F) {
// 退格
if (input_index > 0) {
input_index--;
vga_putchar('\b');
vga_putchar(' ');
vga_putchar('\b');
}
} else if (c >= 32 && c <= 126) {
// 可打印字符
if (input_index < sizeof(input_buffer) - 1) {
input_buffer[input_index++] = c;
vga_putchar(c);
}
}
}
// 处理其他任务
process_tasks();
}
}
// 执行命令
void execute_command(const char* command) {
if (strcmp(command, "help") == 0) {
vga_writestring("\nAvailable commands:\n");
vga_writestring(" help - Show this help\n");
vga_writestring(" info - Show system information\n");
vga_writestring(" mem - Show memory information\n");
vga_writestring(" clear - Clear screen\n");
vga_writestring(" reboot - Reboot system\n");
vga_writestring(" test - Run hardware tests\n");
} else if (strcmp(command, "info") == 0) {
print_system_info();
} else if (strcmp(command, "mem") == 0) {
memory_dump();
} else if (strcmp(command, "clear") == 0) {
vga_clear();
vga_row = 0;
vga_column = 0;
} else if (strcmp(command, "reboot") == 0) {
reboot();
} else if (strcmp(command, "test") == 0) {
run_hardware_tests();
} else if (strcmp(command, "") == 0) {
// 空命令,什么都不做
} else {
vga_writestring("\nUnknown command: ");
vga_writestring(command);
vga_writestring("\nType 'help' for available commands\n");
}
}
// 检测CPU
void detect_cpu() {
// 获取CPU厂商字符串
uint32_t eax, ebx, ecx, edx;
asm volatile ("cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(0));
// 保存厂商字符串
*(uint32_t*)&sys_info.cpu_vendor[0] = ebx;
*(uint32_t*)&sys_info.cpu_vendor[4] = edx;
*(uint32_t*)&sys_info.cpu_vendor[8] = ecx;
sys_info.cpu_vendor[12] = '\0';
// 获取CPU特性
asm volatile ("cpuid"
: "=a"(eax), "=d"(edx)
: "a"(1));
sys_info.has_fpu = (edx >> 0) & 1;
sys_info.has_mmx = (edx >> 23) & 1;
sys_info.has_sse = (edx >> 25) & 1;
// 估计CPU速度(简化版本)
sys_info.cpu_speed_mhz = 1000; // 假设1GHz
}
// 检测内存
void detect_memory() {
uint32_t mem_kb = 0;
// 使用BIOS调用获取内存大小
asm volatile (
"int $0x12\n"
: "=a"(mem_kb)
: "a"(0xE801)
: "ebx", "ecx", "edx"
);
sys_info.total_memory = mem_kb * 1024;
sys_info.free_memory = sys_info.total_memory;
// 设置内核信息
strcpy(sys_info.kernel_name, KERNEL_NAME);
strcpy(sys_info.kernel_version, KERNEL_VERSION);
}
// 初始化中断
void init_interrupts() {
// 重新映射PIC
outb(PORT_PIC1_CMD, 0x11); // ICW1
outb(PORT_PIC1_DATA, 0x20); // ICW2: 主PIC中断向量偏移
outb(PORT_PIC1_DATA, 0x04); // ICW3: 连接到从PIC的IRQ2
outb(PORT_PIC1_DATA, 0x01); // ICW4
outb(PORT_PIC2_CMD, 0x11); // ICW1
outb(PORT_PIC2_DATA, 0x28); // ICW2: 从PIC中断向量偏移
outb(PORT_PIC2_DATA, 0x02); // ICW3: 连接到主PIC的IRQ2
outb(PORT_PIC2_DATA, 0x01); // ICW4
// 屏蔽所有中断(除了键盘)
outb(PORT_PIC1_DATA, 0xFD); // 允许IRQ1(键盘)
outb(PORT_PIC2_DATA, 0xFF); // 屏蔽所有从PIC中断
// 加载IDT
load_idt();
// 启用中断
asm volatile ("sti");
vga_writestring("Interrupts initialized\n");
}
// 初始化设备
void init_devices() {
// 初始化定时器
init_timer();
// 初始化键盘
init_keyboard();
// 初始化硬盘
init_disk();
// 初始化声卡
init_sound();
vga_writestring("Devices initialized\n");
}
// 运行硬件测试
void run_hardware_tests() {
vga_writestring("\nRunning hardware tests...\n");
// 测试内存
vga_writestring("Testing memory... ");
if (test_memory()) {
vga_writestring("PASS\n");
} else {
vga_writestring("FAIL\n");
}
// 测试键盘
vga_writestring("Testing keyboard... ");
if (test_keyboard()) {
vga_writestring("PASS\n");
} else {
vga_writestring("FAIL\n");
}
// 测试硬盘
vga_writestring("Testing disk... ");
if (test_disk()) {
vga_writestring("PASS\n");
} else {
vga_writestring("FAIL\n");
}
vga_writestring("Hardware tests completed\n");
}
// 打印系统信息
void print_system_info() {
vga_writestring("\n=== System Information ===\n");
vga_writestring("Kernel: ");
vga_writestring(sys_info.kernel_name);
vga_writestring(" ");
vga_writestring(sys_info.kernel_version);
vga_writestring("\n");
vga_writestring("CPU: ");
vga_writestring(sys_info.cpu_vendor);
vga_writestring("\n");
vga_writestring("CPU Features: ");
if (sys_info.has_fpu) vga_writestring("FPU ");
if (sys_info.has_mmx) vga_writestring("MMX ");
if (sys_info.has_sse) vga_writestring("SSE ");
vga_writestring("\n");
vga_writestring("CPU Speed: ");
vga_printf("%d MHz", sys_info.cpu_speed_mhz);
vga_writestring("\n");
vga_writestring("Total Memory: ");
vga_printf("%d MB", sys_info.total_memory / (1024 * 1024));
vga_writestring("\n");
vga_writestring("Free Memory: ");
vga_printf("%d MB", sys_info.free_memory / (1024 * 1024));
vga_writestring("\n");
}
// 格式化输出
void vga_printf(const char* format, ...) {
va_list args;
va_start(args, format);
char buffer[32];
char* str;
int num;
while (*format) {
if (*format == '%') {
format++;
switch (*format) {
case 's':
str = va_arg(args, char*);
vga_writestring(str);
break;
case 'd':
case 'i':
num = va_arg(args, int);
itoa(num, buffer, 10);
vga_writestring(buffer);
break;
case 'x':
case 'X':
num = va_arg(args, int);
itoa(num, buffer, 16);
vga_writestring(buffer);
break;
case '%':
vga_putchar('%');
break;
default:
vga_putchar('%');
vga_putchar(*format);
break;
}
} else {
vga_putchar(*format);
}
format++;
}
va_end(args);
}
// 整数转字符串
void itoa(int num, char* str, int base) {
int i = 0;
int is_negative = 0;
// 处理0
if (num == 0) {
str[i++] = '0';
str[i] = '\0';
return;
}
// 处理负数(仅十进制)
if (num < 0 && base == 10) {
is_negative = 1;
num = -num;
}
// 转换数字
while (num != 0) {
int rem = num % base;
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
num = num / base;
}
// 添加负号
if (is_negative) {
str[i++] = '-';
}
// 反转字符串
int start = 0;
int end = i - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
str[i] = '\0';
}
// 字符串比较
int strcmp(const char* s1, const char* s2) {
while (*s1 && (*s1 == *s2)) {
s1++;
s2++;
}
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
// 字符串复制
char* strcpy(char* dest, const char* src) {
char* ptr = dest;
while (*src) {
*dest++ = *src++;
}
*dest = '\0';
return ptr;
}
// 恐慌函数(内核错误)
void panic(const char* message) {
vga_setcolor(vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_RED));
vga_writestring("\n\nKERNEL PANIC: ");
vga_writestring(message);
vga_writestring("\nSystem halted.\n");
// 禁用中断
asm volatile ("cli");
// 无限循环
while (1) {
asm volatile ("hlt");
}
}
// 重启系统
void reboot() {
vga_writestring("\nRebooting system...\n");
// 通过键盘控制器重启
uint8_t temp;
do {
temp = inb(PORT_PS2_STATUS);
if ((temp & 0x02) == 0) {
break;
}
} while (1);
outb(PORT_PS2_CMD, 0xFE);
// 如果失败,尝试其他方法
asm volatile ("jmp 0xFFFF:0x0000");
}
// 停机
void halt() {
vga_writestring("\nSystem halted.\n");
asm volatile ("cli");
while (1) {
asm volatile ("hlt");
}
}
8. 内核汇编辅助文件 (kernel.asm)
[] 纯文本查看 复制代码
[bits 32]
[global _start]
[extern kernel_main]
[extern load_idt]
; 内核入口点
_start:
; 设置栈指针
mov esp, 0x90000
; 调用内核主函数
call kernel_main
; 如果内核主函数返回,停机
cli
hlt
jmp $
; 全局描述符表
[global gdt_flush]
[extern gdt_ptr]
gdt_flush:
lgdt [gdt_ptr]
mov ax, 0x10 ; 数据段选择子
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; 代码段选择子
.flush:
ret
; 中断描述符表
[global idt_flush]
[extern idt_ptr]
idt_flush:
lidt [idt_ptr]
ret
; 启用中断
[global enable_interrupts]
enable_interrupts:
sti
ret
; 禁用中断
[global disable_interrupts]
disable_interrupts:
cli
ret
9. 设备驱动文件 (drivers.c)
[C] 纯文本查看 复制代码
#include "kernel.h"
#include "ports.h"
#include <stdbool.h>
// 定时器
static volatile uint32_t timer_ticks = 0;
// 键盘缓冲区
#define KEYBOARD_BUFFER_SIZE 256
static char keyboard_buffer[KEYBOARD_BUFFER_SIZE];
static volatile size_t keyboard_read_pos = 0;
static volatile size_t keyboard_write_pos = 0;
// 初始化定时器
void init_timer() {
// 设置PIT频率为100Hz
uint32_t divisor = 1193180 / 100;
outb(PORT_PIT_CMD, 0x36);
outb(PORT_PIT_CH0, divisor & 0xFF);
outb(PORT_PIT_CH0, divisor >> 8);
}
// 定时器中断处理
void timer_handler() {
timer_ticks++;
// 每秒钟更新一次光标闪烁
if (timer_ticks % 100 == 0) {
// 切换光标显示状态
static bool cursor_visible = true;
uint16_t cursor_pos = vga_row * VGA_WIDTH + vga_column;
if (cursor_visible) {
// 隐藏光标
outb(PORT_VGA_CTRL, 0x0A);
outb(PORT_VGA_DATA, 0x20);
} else {
// 显示光标
outb(PORT_VGA_CTRL, 0x0A);
outb(PORT_VGA_DATA, 0x0E);
outb(PORT_VGA_CTRL, 0x0B);
outb(PORT_VGA_DATA, 0x0F);
outb(PORT_VGA_CTRL, 0x0E);
outb(PORT_VGA_DATA, (cursor_pos >> 8) & 0xFF);
outb(PORT_VGA_CTRL, 0x0F);
outb(PORT_VGA_DATA, cursor_pos & 0xFF);
}
cursor_visible = !cursor_visible;
}
// 发送EOI
outb(PORT_PIC1_CMD, 0x20);
}
// 获取系统滴答数
uint32_t get_timer_ticks() {
return timer_ticks;
}
// 延时函数(忙等待)
void delay_ms(uint32_t ms) {
uint32_t target_ticks = timer_ticks + (ms / 10);
while (timer_ticks < target_ticks) {
asm volatile ("pause");
}
}
// 初始化键盘
void init_keyboard() {
// 清空键盘缓冲区
keyboard_read_pos = 0;
keyboard_write_pos = 0;
// 启用键盘中断
outb(PORT_PIC1_DATA, inb(PORT_PIC1_DATA) & 0xFD);
}
// 键盘中断处理
void keyboard_handler() {
uint8_t scancode = inb(PORT_PS2_DATA);
// 仅处理按键按下事件
if (scancode < 0x80) {
char key = 0;
// 扫描码转换表
static const char scancode_table[] = {
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0,
0, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0,
'*', 0, ' '
};
if (scancode < sizeof(scancode_table)) {
key = scancode_table[scancode];
}
// 特殊键处理
switch (scancode) {
case 0x1C: // 回车键
key = '\n';
break;
case 0x0E: // 退格键
key = '\b';
break;
case 0x0F: // Tab键
key = '\t';
break;
case 0x39: // 空格键
key = ' ';
break;
}
if (key != 0) {
// 添加到键盘缓冲区
size_t next_pos = (keyboard_write_pos + 1) % KEYBOARD_BUFFER_SIZE;
if (next_pos != keyboard_read_pos) {
keyboard_buffer[keyboard_write_pos] = key;
keyboard_write_pos = next_pos;
}
}
}
// 发送EOI
outb(PORT_PIC1_CMD, 0x20);
}
// 检查键盘是否有可用数据
bool keyboard_available() {
return keyboard_read_pos != keyboard_write_pos;
}
// 读取键盘数据
char keyboard_read() {
while (!keyboard_available()) {
asm volatile ("pause");
}
char key = keyboard_buffer[keyboard_read_pos];
keyboard_read_pos = (keyboard_read_pos + 1) % KEYBOARD_BUFFER_SIZE;
return key;
}
// 初始化硬盘
void init_disk() {
// 等待硬盘就绪
uint8_t status;
int timeout = 10000;
while (timeout-- > 0) {
status = inb(0x1F7);
if ((status & 0xC0) == 0x40) {
break;
}
io_wait();
}
if (timeout <= 0) {
panic("Hard disk initialization timeout");
}
}
// 读取硬盘扇区
int disk_read_sectors(uint32_t lba, uint8_t sectors, void* buffer) {
// 等待硬盘就绪
int timeout = 10000;
while (timeout-- > 0) {
if ((inb(0x1F7) & 0xC0) == 0x40) {
break;
}
io_wait();
}
if (timeout <= 0) {
return ERR_DISK_READ_FAILED;
}
// 发送读取命令
outb(0x1F1, 0x00); // 错误寄存器
outb(0x1F2, sectors); // 扇区数
outb(0x1F3, lba & 0xFF); // LBA低字节
outb(0x1F4, (lba >> 8) & 0xFF);
outb(0x1F5, (lba >> 16) & 0xFF);
outb(0x1F6, 0xE0 | ((lba >> 24) & 0x0F)); // 驱动和LBA高字节
outb(0x1F7, 0x20); // 读取扇区命令
// 读取数据
uint16_t* ptr = (uint16_t*)buffer;
for (int i = 0; i < sectors; i++) {
// 等待数据就绪
timeout = 10000;
while (timeout-- > 0) {
uint8_t status = inb(0x1F7);
if (status & 0x08) { // DRQ位
break;
}
if (status & 0x01) { // 错误位
return ERR_DISK_READ_FAILED;
}
io_wait();
}
if (timeout <= 0) {
return ERR_DISK_READ_FAILED;
}
// 读取256个字(512字节)
for (int j = 0; j < 256; j++) {
*ptr++ = inw(0x1F0);
}
// 等待下一个扇区
io_wait();
}
return ERR_SUCCESS;
}
// 初始化声卡(PC扬声器)
void init_sound() {
// 初始化8254定时器
outb(PORT_PIT_CMD, 0xB6); // 通道2,方波,二进制
// 默认关闭扬声器
outb(0x61, inb(0x61) & 0xFC);
}
// 播放声音
void play_sound(uint32_t frequency) {
if (frequency == 0) {
// 关闭声音
outb(0x61, inb(0x61) & 0xFC);
return;
}
// 设置频率
uint32_t divisor = 1193180 / frequency;
outb(PORT_PIT_CH2, divisor & 0xFF);
outb(PORT_PIT_CH2, divisor >> 8);
// 打开扬声器
uint8_t tmp = inb(0x61);
if ((tmp & 3) != 3) {
outb(0x61, tmp | 3);
}
}
// 停止声音
void stop_sound() {
play_sound(0);
}
// 蜂鸣声(用于提示)
void beep(uint32_t frequency, uint32_t duration_ms) {
play_sound(frequency);
delay_ms(duration_ms);
stop_sound();
}
// 测试内存
bool test_memory() {
// 简单内存测试:写入和读取
uint32_t* test_ptr = (uint32_t*)0x01000000; // 测试16MB处
uint32_t test_value = 0x12345678;
// 写入
*test_ptr = test_value;
// 读取并验证
if (*test_ptr != test_value) {
return false;
}
// 测试内存范围
test_ptr = (uint32_t*)0x02000000; // 测试32MB处
test_value = 0x87654321;
*test_ptr = test_value;
return (*test_ptr == test_value);
}
// 测试键盘
bool test_keyboard() {
// 简单的键盘存在性测试
// 发送自检命令
outb(PORT_PS2_CMD, 0xAA);
delay_ms(100);
// 读取响应
uint8_t response = inb(PORT_PS2_DATA);
return (response == 0x55); // 0x55表示自检通过
}
// 测试硬盘
bool test_disk() {
// 简单的硬盘存在性测试
// 发送识别命令
outb(0x1F6, 0xA0); // 主设备
outb(0x1F7, 0xEC); // IDENTIFY命令
// 等待响应
int timeout = 10000;
while (timeout-- > 0) {
uint8_t status = inb(0x1F7);
if (status == 0) {
continue; // 没有设备
}
if (status & 0x01) {
return false; // 错误
}
if (status & 0x08) {
return true; // 设备就绪
}
io_wait();
}
return false;
}
// 处理后台任务
void process_tasks() {
static uint32_t last_task_time = 0;
// 每100ms执行一次后台任务
if (timer_ticks - last_task_time >= 10) {
last_task_time = timer_ticks;
// 这里可以添加后台任务
// 例如:内存整理、设备状态检查等
}
}
10. 中断处理文件 (isr.asm)
[] 纯文本查看 复制代码
[bits 32]
[global isr_install]
[global irq_install]
[extern isr_handler]
[extern irq_handler]
; 中断服务例程
%macro ISR_NOERRCODE 1
[global isr%1]
isr%1:
cli
push byte 0 ; 错误码占位符
push byte %1 ; 中断号
jmp isr_common
%endmacro
%macro ISR_ERRCODE 1
[global isr%1]
isr%1:
cli
push byte %1 ; 中断号
jmp isr_common
%endmacro
; IRQ宏
%macro IRQ 2
[global irq%1]
irq%1:
cli
push byte 0
push byte %2
jmp irq_common
%endmacro
; 中断向量
ISR_NOERRCODE 0 ; 除零错误
ISR_NOERRCODE 1 ; 调试异常
ISR_NOERRCODE 2 ; 不可屏蔽中断
ISR_NOERRCODE 3 ; 断点
ISR_NOERRCODE 4 ; 溢出
ISR_NOERRCODE 5 ; 越界
ISR_NOERRCODE 6 ; 无效操作码
ISR_NOERRCODE 7 ; 设备不可用
ISR_ERRCODE 8 ; 双重错误
ISR_NOERRCODE 9 ; 协处理器段溢出
ISR_ERRCODE 10 ; 无效TSS
ISR_ERRCODE 11 ; 段不存在
ISR_ERRCODE 12 ; 栈错误
ISR_ERRCODE 13 ; 一般保护错误
ISR_ERRCODE 14 ; 页错误
ISR_NOERRCODE 15 ; 保留
ISR_NOERRCODE 16 ; 浮点错误
ISR_NOERRCODE 17 ; 对齐检查
ISR_NOERRCODE 18 ; 机器检查
ISR_NOERRCODE 19 ; SIMD浮点异常
ISR_NOERRCODE 20 ; 虚拟化异常
ISR_NOERRCODE 21 ; 控制保护异常
ISR_NOERRCODE 22 ; 保留
ISR_NOERRCODE 23 ; 保留
ISR_NOERRCODE 24 ; 保留
ISR_NOERRCODE 25 ; 保留
ISR_NOERRCODE 26 ; 保留
ISR_NOERRCODE 27 ; 保留
ISR_NOERRCODE 28 ; 保留
ISR_NOERRCODE 29 ; 保留
ISR_NOERRCODE 30 ; 安全异常
ISR_NOERRCODE 31 ; 保留
; IRQ
IRQ 0, 32 ; 定时器
IRQ 1, 33 ; 键盘
IRQ 2, 34 ; 级联
IRQ 3, 35 ; COM2
IRQ 4, 36 ; COM1
IRQ 5, 37 ; LPT2
IRQ 6, 38 ; 软盘
IRQ 7, 39 ; LPT1
IRQ 8, 40 ; 实时钟
IRQ 9, 41 ; 保留
IRQ 10, 42 ; 保留
IRQ 11, 43 ; 保留
IRQ 12, 44 ; PS/2鼠标
IRQ 13, 45 ; 协处理器
IRQ 14, 46 ; 主硬盘
IRQ 15, 47 ; 从硬盘
; 公共中断处理
isr_common:
; 保存寄存器
pusha
; 保存段寄存器
push ds
push es
push fs
push gs
; 加载内核数据段
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; 调用C处理函数
push esp
call isr_handler
add esp, 4
; 恢复段寄存器
pop gs
pop fs
pop es
pop ds
; 恢复寄存器
popa
; 清理栈(错误码和中断号)
add esp, 8
; 启用中断并返回
sti
iret
irq_common:
; 保存寄存器
pusha
; 保存段寄存器
push ds
push es
push fs
push gs
; 加载内核数据段
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; 调用C处理函数
push esp
call irq_handler
add esp, 4
; 恢复段寄存器
pop gs
pop fs
pop es
pop ds
; 恢复寄存器
popa
; 清理栈
add esp, 8
; 启用中断并返回
sti
iret
; 安装ISR
isr_install:
; 这里可以添加ISR安装代码
ret
; 安装IRQ
irq_install:
; 这里可以添加IRQ安装代码
ret
; 加载IDT
[global load_idt]
load_idt:
; 创建IDT
call create_idt
; 加载IDT
lidt [idt_descriptor]
ret
; 创建IDT
create_idt:
; IDT创建代码
ret
; IDT描述符
idt_descriptor:
dw 0
dd 0
11. 链接脚本 (linker.ld)
[] 纯文本查看 复制代码
/* 内核链接脚本 */
OUTPUT_FORMAT("elf32-i386")
ENTRY(_start)
SECTIONS {
/* 内核加载地址 */
. = 0x00100000;
/* 文本段 */
.text : {
*(.text)
*(.rodata*)
}
/* 数据段 */
.data : {
*(.data)
}
/* BSS段 */
.bss : {
*(COMMON)
*(.bss)
}
/* 内核结束符号 */
end = .;
}
12. 构建和运行说明
环境要求
1、交叉编译器:i686-elf-gcc
2、NASM汇编器
3、QEMU模拟器(用于测试)
安装依赖(Ubuntu/Debian)
[Bash shell] 纯文本查看 复制代码
sudo apt-get update
sudo apt-get install -y nasm qemu-system-x86 build-essential
sudo apt-get install -y gcc-multilib g++-multilib
# 安装交叉编译器
wget https://github.com/lordmilko/i686-elf-tools/releases/download/7.1.0/i686-elf-tools-linux.zip
unzip i686-elf-tools-linux.zip -d /opt
export PATH=$PATH:/opt/i686-elf/bin
构建步骤
[] 纯文本查看 复制代码 [font=Menlo, Monaco, Consolas, "][size=12px]bash[/size][/font][/backcolor][/color],true]
# 1. 克隆代码并进入目录
git clone https://github.com/yourusername/simple_os.git
cd simple_os
# 2. 设置交叉编译器路径(如果不在默认路径)
export PATH=$PATH:/path/to/i686-elf/bin
# 3. 构建
make
# 4. 在QEMU中运行
make run
# 5. 调试(可选)
make debug
在真实硬件上运行
警告:在真实硬件上运行自制操作系统可能导致数据丢失或硬件损坏。
1、创建一个可启动的USB驱动器:
[Bash shell] 纯文本查看 复制代码 sudo dd if=os.img of=/dev/sdX bs=4M status=progress
2、在BIOS中启用从USB启动
3、确保系统有足够的内存(至少16MB)
安全特性
1、内存保护:通过分页机制防止非法内存访问
2、中断隔离:正确处理硬件中断
3、错误处理:详细的错误代码和恢复机制
4、硬件验证:启动时检查硬件兼容性
已知限制
1、仅支持32位x86架构
2、不支持多处理器
3、文件系统支持有限
4、不支持网络
这个操作系统包含了完整的错误处理、硬件检测和保护措施,可以在真实硬件上运行。它展示了操作系统与CPU、内存、硬盘、显卡、声卡等硬件交互的基本原理,非常适合教学使用。 |
免费评分
-
查看全部评分
|