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 trap_dispatch(struct trap_frame *frame);
|
||||||
|
void handle_irq(struct trap_frame *frame);
|
||||||
void handle_page_fault(struct trap_frame *frame);
|
void handle_page_fault(struct trap_frame *frame);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -2,6 +2,7 @@ ARCH_KERNEL_SRCS := \
|
|||||||
$(ARCH_DIR)/kernel/early_log.c \
|
$(ARCH_DIR)/kernel/early_log.c \
|
||||||
$(ARCH_DIR)/kernel/gdt.c \
|
$(ARCH_DIR)/kernel/gdt.c \
|
||||||
$(ARCH_DIR)/kernel/idt.c \
|
$(ARCH_DIR)/kernel/idt.c \
|
||||||
|
$(ARCH_DIR)/kernel/irq.c \
|
||||||
$(ARCH_DIR)/kernel/traps.c
|
$(ARCH_DIR)/kernel/traps.c
|
||||||
|
|
||||||
ARCH_KERNEL_OBJS := \
|
ARCH_KERNEL_OBJS := \
|
||||||
|
|||||||
@ -45,5 +45,6 @@ void exception_28(void);
|
|||||||
void exception_29(void);
|
void exception_29(void);
|
||||||
void exception_30(void);
|
void exception_30(void);
|
||||||
void exception_31(void);
|
void exception_31(void);
|
||||||
|
void irq_32(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <arch/io.h>
|
||||||
|
|
||||||
#include <tianole/arch.h>
|
#include <tianole/arch.h>
|
||||||
|
|
||||||
#define X86_QEMU_DEBUG_PORT 0xe9
|
#define X86_QEMU_DEBUG_PORT 0xe9
|
||||||
@ -15,19 +18,6 @@
|
|||||||
#define X86_COM_LCR_8N1 0x03
|
#define X86_COM_LCR_8N1 0x03
|
||||||
#define X86_COM_LSR_THRE 0x20
|
#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)
|
static void debug_port_putc(char ch)
|
||||||
{
|
{
|
||||||
outb(X86_QEMU_DEBUG_PORT, (uint8_t)ch);
|
outb(X86_QEMU_DEBUG_PORT, (uint8_t)ch);
|
||||||
|
|||||||
@ -15,6 +15,15 @@ exception_\vector:
|
|||||||
jmp exception_common
|
jmp exception_common
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
.macro IRQ vector
|
||||||
|
.global irq_\vector
|
||||||
|
.type irq_\vector, @function
|
||||||
|
irq_\vector:
|
||||||
|
pushq $0
|
||||||
|
pushq $\vector
|
||||||
|
jmp exception_common
|
||||||
|
.endm
|
||||||
|
|
||||||
.section .text
|
.section .text
|
||||||
|
|
||||||
EXCEPTION_NO_ERROR 0
|
EXCEPTION_NO_ERROR 0
|
||||||
@ -50,6 +59,8 @@ EXCEPTION_ERROR 29
|
|||||||
EXCEPTION_ERROR 30
|
EXCEPTION_ERROR 30
|
||||||
EXCEPTION_NO_ERROR 31
|
EXCEPTION_NO_ERROR 31
|
||||||
|
|
||||||
|
IRQ 32
|
||||||
|
|
||||||
.type exception_common, @function
|
.type exception_common, @function
|
||||||
exception_common:
|
exception_common:
|
||||||
cld
|
cld
|
||||||
|
|||||||
@ -80,6 +80,7 @@ void idt_init(void)
|
|||||||
idt_set_gate(29, exception_29);
|
idt_set_gate(29, exception_29);
|
||||||
idt_set_gate(30, exception_30);
|
idt_set_gate(30, exception_30);
|
||||||
idt_set_gate(31, exception_31);
|
idt_set_gate(31, exception_31);
|
||||||
|
idt_set_gate(32, irq_32);
|
||||||
|
|
||||||
load_idt(&idtr);
|
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;
|
uint64_t vector = frame->vector;
|
||||||
const char *name = "unknown exception";
|
const char *name = "unknown exception";
|
||||||
|
|
||||||
|
if (vector >= 32 && vector < 48) {
|
||||||
|
handle_irq(frame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (vector < 32) {
|
if (vector < 32) {
|
||||||
name = exception_names[vector];
|
name = exception_names[vector];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,3 +41,26 @@
|
|||||||
- 线程可以 sleep 并被 timer 唤醒。
|
- 线程可以 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()`。 |
|
| `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 回归测试。 |
|
| `02-cpu-interrupts.md` | 基础完成 | 已有 GDT/TSS/IDT、exception vector 0-31、`trap_frame` 和 invalid opcode 回归测试。 |
|
||||||
| `03-memory.md` | 基础完成 | 已有最小物理页 allocator、页表 map/unmap/query、page fault 诊断和内核堆。 |
|
| `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` | 未开始 | 需要输入事件模型和键盘接入。 |
|
| `05-input-events.md` | 未开始 | 需要输入事件模型和键盘接入。 |
|
||||||
| `06-storage-vfs.md` | 未开始 | 需要块层、缓存、VFS 和基础文件系统。 |
|
| `06-storage-vfs.md` | 未开始 | 需要块层、缓存、VFS 和基础文件系统。 |
|
||||||
| `07-user-mode.md` | 未开始 | 需要 syscall、进程、用户地址空间和 ELF 加载。 |
|
| `07-user-mode.md` | 未开始 | 需要 syscall、进程、用户地址空间和 ELF 加载。 |
|
||||||
@ -36,7 +36,7 @@
|
|||||||
| `09-driver-expansion.md` | 未开始 | 需要设备模型、PCI/ACPI、存储、网络等驱动扩展。 |
|
| `09-driver-expansion.md` | 未开始 | 需要设备模型、PCI/ACPI、存储、网络等驱动扩展。 |
|
||||||
| `10-real-machine.md` | 未开始 | 需要 U 盘真机启动验证和硬件差异记录。 |
|
| `10-real-machine.md` | 未开始 | 需要 U 盘真机启动验证和硬件差异记录。 |
|
||||||
|
|
||||||
当前最合适的下一步是 `04-time-scheduler.md`:先建立 timer interrupt,再引入最小 kernel thread 和调度循环。
|
当前最合适的下一步仍在 `04-time-scheduler.md` 内:引入最小 kernel thread 和内核栈,再做调度循环。
|
||||||
|
|
||||||
## Linux 级能力缺口路由
|
## Linux 级能力缺口路由
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
- 页表验证:页表 map/unmap/query selftest 已接入启动检查。
|
- 页表验证:页表 map/unmap/query selftest 已接入启动检查。
|
||||||
- page fault 基础:已能输出 fault address、错误码、访问类型和权限来源。
|
- page fault 基础:已能输出 fault address、错误码、访问类型和权限来源。
|
||||||
- 内核堆基础:已提供 `kmalloc/kfree`,并接入启动 selftest。
|
- 内核堆基础:已提供 `kmalloc/kfree`,并接入启动 selftest。
|
||||||
|
- timer 基础:已接入 x86 PIC/PIT,timer IRQ 能进入通用 tick 入口。
|
||||||
- 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。
|
- 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。
|
||||||
- `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径,GitHub Actions 已接入。
|
- `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径,GitHub Actions 已接入。
|
||||||
- `.clang-format` 已用于强制当前 C 代码风格。
|
- `.clang-format` 已用于强制当前 C 代码风格。
|
||||||
@ -30,7 +31,7 @@
|
|||||||
后续未完成:
|
后续未完成:
|
||||||
|
|
||||||
- 完整日志体系:printk、log level、ring buffer、oops、符号化、crash dump。
|
- 完整日志体系:printk、log level、ring buffer、oops、符号化、crash dump。
|
||||||
- 完整中断体系:PIC/APIC、外部 IRQ、timer interrupt。
|
- 完整中断体系:IRQ handler 注册、APIC、外部设备 IRQ、优先级和屏蔽策略。
|
||||||
- 内存管理扩展:长期内存区域模型、page metadata、buddy/slab、后续完整缺页策略。
|
- 内存管理扩展:长期内存区域模型、page metadata、buddy/slab、后续完整缺页策略。
|
||||||
- 后续主线:调度、进程、文件系统、用户态和 shell。
|
- 后续主线:调度、进程、文件系统、用户态和 shell。
|
||||||
|
|
||||||
@ -42,7 +43,6 @@
|
|||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
- 建立 timer interrupt 基础。
|
|
||||||
- 建立最小 kernel thread 和调度循环。
|
- 建立最小 kernel thread 和调度循环。
|
||||||
|
|
||||||
## 任务路由
|
## 任务路由
|
||||||
|
|||||||
@ -8,5 +8,6 @@ void arch_early_log_putc(char ch);
|
|||||||
void arch_halt_forever(void) __attribute__((noreturn));
|
void arch_halt_forever(void) __attribute__((noreturn));
|
||||||
int arch_page_table_uses_page(uint64_t page);
|
int arch_page_table_uses_page(uint64_t page);
|
||||||
void arch_traps_init(void);
|
void arch_traps_init(void);
|
||||||
|
void arch_timer_init(void);
|
||||||
|
|
||||||
#endif
|
#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_SRCS := \
|
||||||
kernel/main.c \
|
kernel/main.c \
|
||||||
kernel/boot_report.c \
|
kernel/boot_report.c \
|
||||||
kernel/early_log.c
|
kernel/early_log.c \
|
||||||
|
kernel/time/timer.c
|
||||||
|
|
||||||
KERNEL_OBJS := \
|
KERNEL_OBJS := \
|
||||||
$(ARCH_KERNEL_OBJS) \
|
$(ARCH_KERNEL_OBJS) \
|
||||||
|
|||||||
@ -12,6 +12,7 @@ void kernel_main(const boot_info_t *boot_info)
|
|||||||
arch_traps_init();
|
arch_traps_init();
|
||||||
kernel_report_boot_state(boot_info);
|
kernel_report_boot_state(boot_info);
|
||||||
mm_init(boot_info);
|
mm_init(boot_info);
|
||||||
|
arch_timer_init();
|
||||||
|
|
||||||
#if KERNEL_TEST_TRAP
|
#if KERNEL_TEST_TRAP
|
||||||
__asm__ volatile("ud2");
|
__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" \
|
"kernel page table root active" \
|
||||||
"page table selftest ok" \
|
"page table selftest ok" \
|
||||||
"kernel heap initialized" \
|
"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 \
|
check_lines build/serial.log \
|
||||||
"kernel_main entered" \
|
"kernel_main entered" \
|
||||||
@ -36,6 +40,10 @@ check_lines build/serial.log \
|
|||||||
"kernel page table root active" \
|
"kernel page table root active" \
|
||||||
"page table selftest ok" \
|
"page table selftest ok" \
|
||||||
"kernel heap initialized" \
|
"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
|
cat build/debug.log
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user