diff --git a/arch/x86/include/arch/io.h b/arch/x86/include/arch/io.h new file mode 100644 index 0000000..327c324 --- /dev/null +++ b/arch/x86/include/arch/io.h @@ -0,0 +1,24 @@ +#ifndef ARCH_X86_IO_H +#define ARCH_X86_IO_H + +#include + +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 value; + + __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline void io_wait(void) +{ + outb(0x80, 0); +} + +#endif diff --git a/arch/x86/include/arch/traps.h b/arch/x86/include/arch/traps.h index 76f8736..18909f6 100644 --- a/arch/x86/include/arch/traps.h +++ b/arch/x86/include/arch/traps.h @@ -27,6 +27,7 @@ struct trap_frame { }; void trap_dispatch(struct trap_frame *frame); +void handle_irq(struct trap_frame *frame); void handle_page_fault(struct trap_frame *frame); #endif diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index c616105..d63b019 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -2,6 +2,7 @@ ARCH_KERNEL_SRCS := \ $(ARCH_DIR)/kernel/early_log.c \ $(ARCH_DIR)/kernel/gdt.c \ $(ARCH_DIR)/kernel/idt.c \ + $(ARCH_DIR)/kernel/irq.c \ $(ARCH_DIR)/kernel/traps.c ARCH_KERNEL_OBJS := \ diff --git a/arch/x86/kernel/cpu.h b/arch/x86/kernel/cpu.h index b92d837..0d27db7 100644 --- a/arch/x86/kernel/cpu.h +++ b/arch/x86/kernel/cpu.h @@ -45,5 +45,6 @@ void exception_28(void); void exception_29(void); void exception_30(void); void exception_31(void); +void irq_32(void); #endif diff --git a/arch/x86/kernel/early_log.c b/arch/x86/kernel/early_log.c index a86edc3..8698d89 100644 --- a/arch/x86/kernel/early_log.c +++ b/arch/x86/kernel/early_log.c @@ -1,4 +1,7 @@ #include + +#include + #include #define X86_QEMU_DEBUG_PORT 0xe9 @@ -15,19 +18,6 @@ #define X86_COM_LCR_8N1 0x03 #define X86_COM_LSR_THRE 0x20 -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 value; - - __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); - return value; -} - static void debug_port_putc(char ch) { outb(X86_QEMU_DEBUG_PORT, (uint8_t)ch); diff --git a/arch/x86/kernel/exception_entry.S b/arch/x86/kernel/exception_entry.S index 5005e29..f30e2e3 100644 --- a/arch/x86/kernel/exception_entry.S +++ b/arch/x86/kernel/exception_entry.S @@ -15,6 +15,15 @@ exception_\vector: jmp exception_common .endm +.macro IRQ vector +.global irq_\vector +.type irq_\vector, @function +irq_\vector: + pushq $0 + pushq $\vector + jmp exception_common +.endm + .section .text EXCEPTION_NO_ERROR 0 @@ -50,6 +59,8 @@ EXCEPTION_ERROR 29 EXCEPTION_ERROR 30 EXCEPTION_NO_ERROR 31 +IRQ 32 + .type exception_common, @function exception_common: cld diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c index 1ce10f4..4f944cc 100644 --- a/arch/x86/kernel/idt.c +++ b/arch/x86/kernel/idt.c @@ -80,6 +80,7 @@ void idt_init(void) idt_set_gate(29, exception_29); idt_set_gate(30, exception_30); idt_set_gate(31, exception_31); + idt_set_gate(32, irq_32); load_idt(&idtr); } diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c new file mode 100644 index 0000000..c01903c --- /dev/null +++ b/arch/x86/kernel/irq.c @@ -0,0 +1,108 @@ +#include + +#include +#include + +#include +#include + +#define PIC1_COMMAND 0x20 +#define PIC1_DATA 0x21 +#define PIC2_COMMAND 0xa0 +#define PIC2_DATA 0xa1 +#define PIC_EOI 0x20 + +#define ICW1_INIT 0x10 +#define ICW1_ICW4 0x01 +#define ICW4_8086 0x01 + +#define PIT_FREQUENCY 1193182u +#define PIT_TARGET_HZ 100u +#define PIT_COMMAND 0x43 +#define PIT_CHANNEL0 0x40 +#define PIT_MODE_RATE_GENERATOR 0x36 + +#define IRQ_BASE 32u +#define IRQ_TIMER 0u + +static void enable_interrupts(void) +{ + __asm__ volatile("sti"); +} + +static void pic_remap(void) +{ + outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + + outb(PIC1_DATA, IRQ_BASE); + io_wait(); + outb(PIC2_DATA, IRQ_BASE + 8); + io_wait(); + + outb(PIC1_DATA, 4); + io_wait(); + outb(PIC2_DATA, 2); + io_wait(); + + outb(PIC1_DATA, ICW4_8086); + io_wait(); + outb(PIC2_DATA, ICW4_8086); + io_wait(); +} + +static void pic_mask_all_except_timer(void) +{ + outb(PIC1_DATA, 0xfe); + outb(PIC2_DATA, 0xff); +} + +static void pic_send_eoi(uint8_t irq) +{ + if (irq >= 8) { + outb(PIC2_COMMAND, PIC_EOI); + } + + outb(PIC1_COMMAND, PIC_EOI); +} + +static void pit_init(void) +{ + uint16_t divisor = (uint16_t)(PIT_FREQUENCY / PIT_TARGET_HZ); + + outb(PIT_COMMAND, PIT_MODE_RATE_GENERATOR); + outb(PIT_CHANNEL0, (uint8_t)(divisor & 0xff)); + outb(PIT_CHANNEL0, (uint8_t)(divisor >> 8)); +} + +static void handle_timer_irq(void) +{ + timer_tick(); + pic_send_eoi(IRQ_TIMER); +} + +void handle_irq(struct trap_frame *frame) +{ + uint64_t irq = frame->vector - IRQ_BASE; + + if (irq == IRQ_TIMER) { + handle_timer_irq(); + return; + } + + early_log_puts("unexpected irq="); + early_log_u64_decimal(irq); + early_log_puts("\n"); + pic_send_eoi((uint8_t)irq); +} + +void arch_timer_init(void) +{ + pic_remap(); + pic_mask_all_except_timer(); + pit_init(); + early_log_puts("timer initialized\n"); + enable_interrupts(); +} diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index e102b3e..4cb6dbe 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -57,6 +57,11 @@ void trap_dispatch(struct trap_frame *frame) uint64_t vector = frame->vector; const char *name = "unknown exception"; + if (vector >= 32 && vector < 48) { + handle_irq(frame); + return; + } + if (vector < 32) { name = exception_names[vector]; } diff --git a/docs/agents/tasks/04-time-scheduler.md b/docs/agents/tasks/04-time-scheduler.md index 8fc141d..26743a4 100644 --- a/docs/agents/tasks/04-time-scheduler.md +++ b/docs/agents/tasks/04-time-scheduler.md @@ -41,3 +41,26 @@ - 线程可以 sleep 并被 timer 唤醒。 - 调度现场保存和恢复稳定。 - 基础对象生命周期规则有文档和调用约束。 + +## 当前状态 + +基础完成: + +- 已建立 x86 PIC remap,外部 IRQ 使用 vector 32-47,不再和 CPU exception 混用。 +- 已接入 PIT periodic timer,当前频率为 100Hz。 +- 已建立通用 `timer_tick()` 入口和 `timer_ticks()` 计数接口。 +- 已在 trap dispatch 中区分 CPU exception 和外部 IRQ。 +- 已对 timer IRQ0 发送 EOI,避免中断只触发一次。 +- `scripts/check.sh` 已验证 `timer initialized` 和 `timer tick=1/2/3`。 + +后续扩展: + +- 把 IRQ 分发扩展为可注册 handler 的表,而不是只处理 timer。 +- 建立 kernel thread 对象和内核栈。 +- 建立上下文切换入口。 +- 建立 run queue 和最小 round-robin scheduler。 +- 建立 `yield()`、`sleep()`、timer wakeup 和 wait queue。 + +下一阶段: + +- 继续在 `04-time-scheduler.md` 内推进最小 kernel thread。 diff --git a/docs/agents/tasks/README.md b/docs/agents/tasks/README.md index a89a836..fdaa4f0 100644 --- a/docs/agents/tasks/README.md +++ b/docs/agents/tasks/README.md @@ -28,7 +28,7 @@ | `01-early-debug.md` | 基础完成 | 已有 early log 前端、QEMU debug port、COM1 串口和最小 `panic()`。 | | `02-cpu-interrupts.md` | 基础完成 | 已有 GDT/TSS/IDT、exception vector 0-31、`trap_frame` 和 invalid opcode 回归测试。 | | `03-memory.md` | 基础完成 | 已有最小物理页 allocator、页表 map/unmap/query、page fault 诊断和内核堆。 | -| `04-time-scheduler.md` | 未开始 | 需要 timer、kernel thread、调度和等待队列。 | +| `04-time-scheduler.md` | 进行中 | 已有 PIT timer interrupt 和通用 tick 计数;kernel thread、调度和等待队列未完成。 | | `05-input-events.md` | 未开始 | 需要输入事件模型和键盘接入。 | | `06-storage-vfs.md` | 未开始 | 需要块层、缓存、VFS 和基础文件系统。 | | `07-user-mode.md` | 未开始 | 需要 syscall、进程、用户地址空间和 ELF 加载。 | @@ -36,7 +36,7 @@ | `09-driver-expansion.md` | 未开始 | 需要设备模型、PCI/ACPI、存储、网络等驱动扩展。 | | `10-real-machine.md` | 未开始 | 需要 U 盘真机启动验证和硬件差异记录。 | -当前最合适的下一步是 `04-time-scheduler.md`:先建立 timer interrupt,再引入最小 kernel thread 和调度循环。 +当前最合适的下一步仍在 `04-time-scheduler.md` 内:引入最小 kernel thread 和内核栈,再做调度循环。 ## Linux 级能力缺口路由 diff --git a/docs/roadmap.md b/docs/roadmap.md index 77b4d4c..09a4033 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -23,6 +23,7 @@ - 页表验证:页表 map/unmap/query selftest 已接入启动检查。 - page fault 基础:已能输出 fault address、错误码、访问类型和权限来源。 - 内核堆基础:已提供 `kmalloc/kfree`,并接入启动 selftest。 +- timer 基础:已接入 x86 PIC/PIT,timer IRQ 能进入通用 tick 入口。 - 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。 - `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径,GitHub Actions 已接入。 - `.clang-format` 已用于强制当前 C 代码风格。 @@ -30,7 +31,7 @@ 后续未完成: - 完整日志体系:printk、log level、ring buffer、oops、符号化、crash dump。 -- 完整中断体系:PIC/APIC、外部 IRQ、timer interrupt。 +- 完整中断体系:IRQ handler 注册、APIC、外部设备 IRQ、优先级和屏蔽策略。 - 内存管理扩展:长期内存区域模型、page metadata、buddy/slab、后续完整缺页策略。 - 后续主线:调度、进程、文件系统、用户态和 shell。 @@ -42,7 +43,6 @@ 目标: -- 建立 timer interrupt 基础。 - 建立最小 kernel thread 和调度循环。 ## 任务路由 diff --git a/include/tianole/arch.h b/include/tianole/arch.h index 2009320..5d77103 100644 --- a/include/tianole/arch.h +++ b/include/tianole/arch.h @@ -8,5 +8,6 @@ void arch_early_log_putc(char ch); void arch_halt_forever(void) __attribute__((noreturn)); int arch_page_table_uses_page(uint64_t page); void arch_traps_init(void); +void arch_timer_init(void); #endif diff --git a/include/tianole/timer.h b/include/tianole/timer.h new file mode 100644 index 0000000..ab3c4b2 --- /dev/null +++ b/include/tianole/timer.h @@ -0,0 +1,9 @@ +#ifndef TIANOLE_TIMER_H +#define TIANOLE_TIMER_H + +#include + +void timer_tick(void); +uint64_t timer_ticks(void); + +#endif diff --git a/kernel/Makefile b/kernel/Makefile index 615e89e..8ee9d58 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,7 +1,8 @@ KERNEL_SRCS := \ kernel/main.c \ kernel/boot_report.c \ - kernel/early_log.c + kernel/early_log.c \ + kernel/time/timer.c KERNEL_OBJS := \ $(ARCH_KERNEL_OBJS) \ diff --git a/kernel/main.c b/kernel/main.c index f1745f8..105022a 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -12,6 +12,7 @@ void kernel_main(const boot_info_t *boot_info) arch_traps_init(); kernel_report_boot_state(boot_info); mm_init(boot_info); + arch_timer_init(); #if KERNEL_TEST_TRAP __asm__ volatile("ud2"); diff --git a/kernel/time/timer.c b/kernel/time/timer.c new file mode 100644 index 0000000..6d21360 --- /dev/null +++ b/kernel/time/timer.c @@ -0,0 +1,22 @@ +#include + +#include +#include + +static uint64_t tick_count; + +void timer_tick(void) +{ + tick_count++; + + if (tick_count <= 3) { + early_log_puts("timer tick="); + early_log_u64_decimal(tick_count); + early_log_puts("\n"); + } +} + +uint64_t timer_ticks(void) +{ + return tick_count; +} diff --git a/scripts/checks/boot.sh b/scripts/checks/boot.sh index f520245..45dbde6 100755 --- a/scripts/checks/boot.sh +++ b/scripts/checks/boot.sh @@ -22,7 +22,11 @@ check_lines build/debug.log \ "kernel page table root active" \ "page table selftest ok" \ "kernel heap initialized" \ - "kernel heap selftest ok" + "kernel heap selftest ok" \ + "timer initialized" \ + "timer tick=1" \ + "timer tick=2" \ + "timer tick=3" check_lines build/serial.log \ "kernel_main entered" \ @@ -36,6 +40,10 @@ check_lines build/serial.log \ "kernel page table root active" \ "page table selftest ok" \ "kernel heap initialized" \ - "kernel heap selftest ok" + "kernel heap selftest ok" \ + "timer initialized" \ + "timer tick=1" \ + "timer tick=2" \ + "timer tick=3" cat build/debug.log