diff --git a/docs/agents/code-style.md b/docs/agents/code-style.md index 12ea46a..c884380 100644 --- a/docs/agents/code-style.md +++ b/docs/agents/code-style.md @@ -44,6 +44,11 @@ - 不写重复描述简单代码行为的注释。 - 如果代码依赖硬件手册、启动协议、调用顺序或特殊寄存器状态,应写明约束。 - 如果一个实现是临时简化,应写清楚后续替换边界,而不是写成永久接口。 +- 公共头文件 `include/tianole/*.h` 中的函数声明必须使用 Linux kernel-doc 风格块注释。 +- 公共函数注释至少说明函数职责、关键参数、返回值或副作用;不要只复述函数名。 +- 公共函数声明和前一个声明/注释块之间必须留一个空行,避免 API 挤在一起。 +- 私有 `static` 小函数不要求每个都写函数头注释;只有逻辑、数据流、锁语义或硬件约束不明显时才写。 +- 多行注释要控制行宽,优先写职责、数据流和调用约束,而不是描述每一行代码。 ## C 格式 diff --git a/include/tianole/arch.h b/include/tianole/arch.h index 6263fdd..2968583 100644 --- a/include/tianole/arch.h +++ b/include/tianole/arch.h @@ -3,13 +3,63 @@ #include +/** + * arch_early_log_init() - Initialize architecture early log devices. + * + * Provides the backend setup used by the generic early log front end. + */ void arch_early_log_init(void); + +/** + * arch_early_log_putc() - Emit one character through arch early log devices. + * @ch: Character to write. + * + * This keeps port I/O and other architecture details out of generic logging. + */ void arch_early_log_putc(char ch); + +/** + * arch_halt_forever() - Stop execution permanently. + * + * Used by panic and unrecoverable paths after diagnostics have been emitted. + */ void arch_halt_forever(void) __attribute__((noreturn)); + +/** + * arch_irq_save() - Save interrupt state and disable maskable interrupts. + * + * Return: Architecture flags needed by arch_irq_restore(). + */ uint64_t arch_irq_save(void); + +/** + * arch_irq_restore() - Restore a previously saved interrupt state. + * @flags: Value returned by arch_irq_save(). + * + * This is the backend for interrupt-safe spinlocks on the current CPU. + */ void arch_irq_restore(uint64_t flags); + +/** + * arch_page_table_uses_page() - Check whether a page backs page tables. + * @page: Physical page base address. + * + * Return: Non-zero if the architecture page-table layer owns the page. + */ int arch_page_table_uses_page(uint64_t page); + +/** + * arch_traps_init() - Initialize architecture trap and IRQ entry tables. + * + * Sets up descriptor tables and entry points before devices enable IRQs. + */ void arch_traps_init(void); + +/** + * arch_timer_init() - Initialize the architecture timer backend. + * + * Connects the hardware timer to the generic timer and scheduler path. + */ void arch_timer_init(void); #endif diff --git a/include/tianole/early_log.h b/include/tianole/early_log.h index c16b137..03ac396 100644 --- a/include/tianole/early_log.h +++ b/include/tianole/early_log.h @@ -3,11 +3,53 @@ #include +/** + * early_log_init() - Initialize early logging backends. + * + * Sets up the architecture-provided early output path. This must be usable + * before heap, scheduler, VFS or normal drivers exist. + */ void early_log_init(void); + +/** + * early_log_putc() - Write one character to every early log backend. + * @ch: Character to emit. + * + * This is the byte-level output path used by the string and number helpers. + */ void early_log_putc(char ch); + +/** + * early_log_puts() - Write a NUL-terminated string to early logs. + * @text: String to emit. A NULL pointer is ignored by callers only if they + * explicitly check before calling. + * + * The function does not allocate memory and is safe for early boot diagnostics. + */ void early_log_puts(const char *text); + +/** + * early_log_u64_hex() - Write a 64-bit value in hexadecimal form. + * @value: Value to print. + * + * Used by trap, memory and boot diagnostics where libc formatting is absent. + */ void early_log_u64_hex(uint64_t value); + +/** + * early_log_u64_decimal() - Write a 64-bit value in decimal form. + * @value: Value to print. + * + * Used for counters and boot statistics without depending on printf support. + */ void early_log_u64_decimal(uint64_t value); + +/** + * panic() - Print a fatal error and stop the machine. + * @message: Panic reason to include in the early log. + * + * This path must work even when normal kernel services are not initialized. + */ void panic(const char *message) __attribute__((noreturn)); #endif diff --git a/include/tianole/irq.h b/include/tianole/irq.h index f90271c..57d7b60 100644 --- a/include/tianole/irq.h +++ b/include/tianole/irq.h @@ -5,6 +5,14 @@ typedef void (*irq_handler_t)(uint8_t irq, void *data); +/** + * irq_register() - Register a handler for an external IRQ line. + * @irq: IRQ line number in the generic IRQ namespace. + * @handler: Function called when the IRQ is dispatched. + * @data: Opaque handler data passed back during dispatch. + * + * Return: 0 on success, negative value on failure. + */ int irq_register(uint8_t irq, irq_handler_t handler, void *data); #endif diff --git a/include/tianole/kernel_init.h b/include/tianole/kernel_init.h index a6326b5..71ffd43 100644 --- a/include/tianole/kernel_init.h +++ b/include/tianole/kernel_init.h @@ -3,6 +3,12 @@ #include +/** + * kernel_report_boot_state() - Log the boot handoff state. + * @boot_info: Firmware-independent boot data passed to the kernel. + * + * Prints early diagnostics used to verify the bootloader-to-kernel contract. + */ void kernel_report_boot_state(const boot_info_t *boot_info); #endif diff --git a/include/tianole/mm.h b/include/tianole/mm.h index d649e54..ec11a48 100644 --- a/include/tianole/mm.h +++ b/include/tianole/mm.h @@ -15,15 +15,84 @@ typedef uint64_t virt_addr_t; #define PAGE_WRITABLE (1ull << 1) #define PAGE_NO_EXECUTE (1ull << 63) +/** + * mm_init() - Initialize the generic memory manager. + * @boot_info: Bootloader-provided memory map and handoff data. + * + * Converts boot memory information into Tianole-owned page allocator state. + */ void mm_init(const boot_info_t *boot_info); + +/** + * alloc_page() - Allocate one physical page. + * + * Return: Physical page base address, or 0 when no page is available. + */ phys_addr_t alloc_page(void); + +/** + * free_page() - Return one physical page to the allocator. + * @page: Physical page base address previously returned by alloc_page(). + * + * The page must not still be mapped or owned by another subsystem. + */ void free_page(phys_addr_t page); + +/** + * kmalloc() - Allocate small kernel heap memory. + * @size: Number of bytes requested. + * + * Return: Kernel virtual pointer, or NULL when allocation fails. + */ void *kmalloc(size_t size); + +/** + * kfree() - Free memory allocated by kmalloc(). + * @ptr: Pointer returned by kmalloc(), or NULL. + * + * Releases the allocation back to the kernel heap. + */ void kfree(void *ptr); + +/** + * map_page() - Map one virtual page to one physical page. + * @virt: Virtual page address. + * @phys: Physical page address. + * @flags: Generic page flags such as PAGE_WRITABLE. + * + * Return: 0 on success, negative value on failure. + */ int map_page(virt_addr_t virt, phys_addr_t phys, uint64_t flags); + +/** + * unmap_page() - Remove one virtual page mapping. + * @virt: Virtual page address to unmap. + * + * Return: 0 on success, negative value on failure. + */ int unmap_page(virt_addr_t virt); + +/** + * virt_to_phys() - Resolve a virtual address to a physical address. + * @virt: Virtual address to query. + * @phys: Output storage for the resolved physical address. + * + * Return: 0 when a mapping exists, negative value otherwise. + */ int virt_to_phys(virt_addr_t virt, phys_addr_t *phys); + +/** + * heap_init() - Initialize the kernel heap. + * + * Sets up kmalloc()/kfree() after page allocation and page tables are ready. + */ void heap_init(void); + +/** + * page_table_selftest() - Run boot-time page table checks. + * + * Verifies map, unmap and address translation behavior during early boot. + */ void page_table_selftest(void); #endif diff --git a/include/tianole/sched.h b/include/tianole/sched.h index 5352312..4359acc 100644 --- a/include/tianole/sched.h +++ b/include/tianole/sched.h @@ -38,24 +38,127 @@ struct thread { char name[32]; }; +/** + * sched_init() - Initialize scheduler core state. + * + * Creates scheduler data structures and runs scheduler selftests before + * threads are started. + */ void sched_init(void); + +/** + * kernel_thread_create() - Create a kernel thread. + * @name: Human-readable thread name used by diagnostics. + * @entry: Thread entry point. + * @arg: Opaque argument passed to @entry. + * + * Return: Thread object on success, or NULL when allocation fails. + */ struct thread *kernel_thread_create( const char *name, kernel_thread_entry_t entry, void *arg); + +/** + * kernel_thread_exit() - Terminate the current kernel thread. + * + * Marks the current thread dead and yields forever until the scheduler + * switches away and later reclaims it from a safe context. + */ void kernel_thread_exit(void) __attribute__((noreturn)); + +/** + * sched_start() - Start scheduler demo threads and enter scheduling. + * + * Creates the idle thread and current boot-stage scheduler demonstration. + */ void sched_start(void) __attribute__((noreturn)); + +/** + * sched_tick() - Notify the scheduler about a timer tick. + * @tick: Current generic timer tick. + * + * Wakes sleeping threads whose deadlines expired and requests rescheduling. + */ void sched_tick(uint64_t tick); + +/** + * sched_irq_exit() - Consume pending reschedule work after IRQ handling. + * + * Keeps timer IRQ handling short by moving the actual scheduling decision to + * a common interrupt-exit boundary. + */ void sched_irq_exit(void); + +/** + * sched_yield() - Yield the CPU to another runnable kernel thread. + * + * Performs cooperative scheduling and context switching when another runnable + * thread is available. + */ void sched_yield(void); + +/** + * sched_sleep() - Sleep the current thread for a number of ticks. + * @ticks: Number of timer ticks to sleep. + * + * Marks the current thread sleeping and yields until the timer path wakes it. + */ void sched_sleep(uint64_t ticks); + +/** + * wait_queue_init() - Initialize a wait queue. + * @queue: Wait queue storage owned by the caller. + * + * Resets queue links and the internal interrupt-safe lock. + */ void wait_queue_init(struct wait_queue *queue); + +/** + * wait_queue_sleep() - Sleep until another thread wakes the queue. + * @queue: Queue to sleep on. + * + * Enqueues the current thread, marks it waiting and yields until wakeup. + */ void wait_queue_sleep(struct wait_queue *queue); + +/** + * wait_queue_wait() - Sleep until a condition becomes true. + * @queue: Queue used for wakeups. + * @condition: Predicate checked before and after sleeping. + * @arg: Opaque predicate argument. + * + * Return: 0 when the condition is true, negative value on invalid input. + */ int wait_queue_wait( struct wait_queue *queue, wait_condition_t condition, void *arg); + +/** + * wait_queue_wait_timeout() - Sleep until a condition or timeout. + * @queue: Queue used for wakeups. + * @condition: Predicate checked before and after sleeping. + * @arg: Opaque predicate argument. + * @ticks: Maximum number of timer ticks to wait. + * + * Return: 0 when the condition is true, negative value on timeout or error. + */ int wait_queue_wait_timeout(struct wait_queue *queue, wait_condition_t condition, void *arg, uint64_t ticks); + +/** + * wait_queue_wake_one() - Wake one thread from a wait queue. + * @queue: Queue containing waiting threads. + * + * Removes the oldest waiter and marks it ready if it is still waitable. + */ void wait_queue_wake_one(struct wait_queue *queue); + +/** + * wait_queue_wake_all() - Wake every thread from a wait queue. + * @queue: Queue containing waiting threads. + * + * Drains the queue and marks every waitable thread ready. + */ void wait_queue_wake_all(struct wait_queue *queue); #endif diff --git a/include/tianole/spinlock.h b/include/tianole/spinlock.h index 15a647e..ffceb0a 100644 --- a/include/tianole/spinlock.h +++ b/include/tianole/spinlock.h @@ -12,7 +12,23 @@ struct spinlock { 0 \ } +/** + * spin_lock_irqsave() - Acquire a single-CPU interrupt-safe spinlock. + * @lock: Lock to acquire. + * @flags: Storage for the previous interrupt state. + * + * Disables interrupts before taking the lock so IRQ and thread context can + * share early kernel data structures. + */ void spin_lock_irqsave(struct spinlock *lock, uint64_t *flags); + +/** + * spin_unlock_irqrestore() - Release an interrupt-safe spinlock. + * @lock: Lock to release. + * @flags: Interrupt state returned by spin_lock_irqsave(). + * + * Restores the caller's original interrupt state after releasing the lock. + */ void spin_unlock_irqrestore(struct spinlock *lock, uint64_t flags); #endif diff --git a/include/tianole/timer.h b/include/tianole/timer.h index ab3c4b2..85ac41e 100644 --- a/include/tianole/timer.h +++ b/include/tianole/timer.h @@ -3,7 +3,18 @@ #include +/** + * timer_tick() - Advance generic timer state by one hardware tick. + * + * Called by the architecture timer IRQ handler before scheduler tick handling. + */ void timer_tick(void); + +/** + * timer_ticks() - Read the generic monotonic tick counter. + * + * Return: Number of timer ticks observed since timer initialization. + */ uint64_t timer_ticks(void); #endif diff --git a/scripts/tools/check_structure.py b/scripts/tools/check_structure.py index 71b794d..0ca6d0e 100644 --- a/scripts/tools/check_structure.py +++ b/scripts/tools/check_structure.py @@ -72,6 +72,16 @@ def source_files(files: set[Path]) -> list[Path]: return sorted(path for path in files if path.suffix in (".c", ".h")) +def public_headers(files: set[Path]) -> list[Path]: + return sorted( + path + for path in files + if path.suffix == ".h" + and len(path.parts) == 3 + and path.parts[:2] == ("include", "tianole") + ) + + def c_files(files: set[Path]) -> list[Path]: return sorted( path @@ -99,6 +109,108 @@ def check_no_relative_parent_includes(root: Path, files: list[Path]) -> list[str return errors +def is_function_declaration_start(line: str) -> bool: + stripped = line.strip() + + if stripped == "" or stripped.startswith("#"): + return False + + if stripped.startswith(("typedef ", "struct ", "enum ", "union ")): + return False + + if "(" not in stripped or stripped.startswith("*"): + return False + + if "(*" in stripped or stripped.endswith("{"): + return False + + return True + + +def collect_function_declarations(lines: list[str]) -> list[tuple[int, int, str]]: + decls = [] + index = 0 + + while index < len(lines): + if not is_function_declaration_start(lines[index]): + index += 1 + continue + + start = index + text = lines[index].strip() + while ";" not in lines[index]: + index += 1 + if index >= len(lines): + break + text += " " + lines[index].strip() + + if index < len(lines): + decls.append((start, index, text)) + + index += 1 + + return decls + + +def has_kernel_doc_before(lines: list[str], start: int) -> bool: + index = start - 1 + + while index >= 0 and lines[index].strip() == "": + index -= 1 + + if index < 0 or lines[index].strip() != "*/": + return False + + while index >= 0: + if lines[index].strip().startswith("/**"): + return True + index -= 1 + + return False + + +def has_blank_line_before_doc(lines: list[str], start: int) -> bool: + index = start - 1 + + while index >= 0 and lines[index].strip() == "": + index -= 1 + + if index < 0 or lines[index].strip() != "*/": + return True + + while index >= 0 and not lines[index].strip().startswith("/**"): + index -= 1 + + if index <= 0: + return True + + return lines[index - 1].strip() == "" + + +def check_public_header_function_docs(root: Path, files: list[Path]) -> list[str]: + errors = [] + + for path in files: + lines = read_text(root, path).splitlines() + for start, _end, text in collect_function_declarations(lines): + line_no = start + 1 + name = text.split("(", 1)[0].split()[-1].lstrip("*") + if not has_kernel_doc_before(lines, start): + errors.append( + f"{path}:{line_no}: public function '{name}' needs " + "a kernel-doc comment immediately before it" + ) + continue + + if not has_blank_line_before_doc(lines, start): + errors.append( + f"{path}:{line_no}: public function '{name}' " + "comment block must be separated by a blank line" + ) + + return errors + + def check_selftests_are_centralized(files: list[Path]) -> list[str]: errors = [] @@ -201,6 +313,7 @@ def main() -> int: errors = [] errors.extend(check_no_relative_parent_includes(root, source_files(files))) + errors.extend(check_public_header_function_docs(root, public_headers(files))) errors.extend(check_selftests_are_centralized(c_files(files))) errors.extend(check_makefile_source_lists(root, files, all_mode))