feat(kernel): add PIT timer interrupt foundation
This commit is contained in:
parent
a2e6889932
commit
9e610bdbaa
24
arch/x86/include/arch/io.h
Normal file
24
arch/x86/include/arch/io.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef ARCH_X86_IO_H
|
||||
#define ARCH_X86_IO_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 value;
|
||||
|
||||
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void io_wait(void)
|
||||
{
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -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
|
||||
|
||||
@ -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 := \
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <arch/io.h>
|
||||
|
||||
#include <tianole/arch.h>
|
||||
|
||||
#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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
108
arch/x86/kernel/irq.c
Normal file
108
arch/x86/kernel/irq.c
Normal file
@ -0,0 +1,108 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <arch/io.h>
|
||||
#include <arch/traps.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/timer.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
@ -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];
|
||||
}
|
||||
|
||||
@ -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。
|
||||
|
||||
@ -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 级能力缺口路由
|
||||
|
||||
|
||||
@ -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 和调度循环。
|
||||
|
||||
## 任务路由
|
||||
|
||||
@ -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
|
||||
|
||||
9
include/tianole/timer.h
Normal file
9
include/tianole/timer.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef TIANOLE_TIMER_H
|
||||
#define TIANOLE_TIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void timer_tick(void);
|
||||
uint64_t timer_ticks(void);
|
||||
|
||||
#endif
|
||||
@ -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) \
|
||||
|
||||
@ -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");
|
||||
|
||||
22
kernel/time/timer.c
Normal file
22
kernel/time/timer.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/timer.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user