Compare commits
No commits in common. "11a1a8234be8cc54da35943466dda83261b7383d" and "7292b9eb56c6f00696be0ac61e3e61430611c008" have entirely different histories.
11a1a8234b
...
7292b9eb56
4
Makefile
4
Makefile
@ -66,14 +66,14 @@ $(KERNEL_ELF): $(KERNEL_OBJS) $(ARCH_DIR)/kernel/linker.ld | dirs
|
||||
$(OVMF_VARS): | dirs
|
||||
cp $(OVMF_VARS_TEMPLATE) $@
|
||||
|
||||
run: $(BOOT_EFI) $(KERNEL_ELF) $(OVMF_VARS)
|
||||
run: $(BOOT_EFI) $(OVMF_VARS)
|
||||
qemu-system-x86_64 \
|
||||
-drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \
|
||||
-drive if=pflash,format=raw,file=$(OVMF_VARS) \
|
||||
-drive format=raw,file=fat:rw:$(BUILD_DIR)/image \
|
||||
-serial stdio
|
||||
|
||||
run-headless: $(BOOT_EFI) $(KERNEL_ELF) $(OVMF_VARS)
|
||||
run-headless: $(BOOT_EFI) $(OVMF_VARS)
|
||||
rm -f $(DEBUG_LOG) $(SERIAL_LOG)
|
||||
qemu-system-x86_64 \
|
||||
-display none \
|
||||
|
||||
@ -2,7 +2,6 @@ BOOT_SRCS := \
|
||||
$(ARCH_DIR)/boot/main.c \
|
||||
$(ARCH_DIR)/boot/file.c \
|
||||
$(ARCH_DIR)/boot/elf_loader.c \
|
||||
$(ARCH_DIR)/boot/framebuffer.c \
|
||||
$(ARCH_DIR)/boot/memory_map.c \
|
||||
$(ARCH_DIR)/boot/support.c
|
||||
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
#include "efi.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
efi_status boot_capture_framebuffer(
|
||||
efi_system_table_t *system_table, boot_info_t *boot_info)
|
||||
{
|
||||
efi_graphics_output_protocol_t *gop;
|
||||
efi_graphics_output_mode_information_t *info;
|
||||
efi_guid_t gop_guid = efi_graphics_output_protocol_guid();
|
||||
efi_status status;
|
||||
|
||||
if (system_table == 0 || boot_info == 0) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
status = system_table->boot_services->locate_protocol(
|
||||
&gop_guid, 0, (void **)&gop);
|
||||
if (status != EFI_SUCCESS || gop == 0 || gop->mode == 0 ||
|
||||
gop->mode->info == 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
info = gop->mode->info;
|
||||
boot_info->framebuffer_base = gop->mode->frame_buffer_base;
|
||||
boot_info->framebuffer_size = gop->mode->frame_buffer_size;
|
||||
boot_info->framebuffer_width = info->horizontal_resolution;
|
||||
boot_info->framebuffer_height = info->vertical_resolution;
|
||||
boot_info->framebuffer_pixels_per_scan_line =
|
||||
info->pixels_per_scan_line;
|
||||
boot_info->framebuffer_pixel_format = info->pixel_format;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
#ifndef X86_BOOT_FRAMEBUFFER_H
|
||||
#define X86_BOOT_FRAMEBUFFER_H
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
#include "efi.h"
|
||||
|
||||
/**
|
||||
* boot_capture_framebuffer() - Copy GOP mode data into boot_info.
|
||||
* @system_table: UEFI system table used to locate GOP.
|
||||
* @boot_info: Kernel handoff structure updated on success.
|
||||
*
|
||||
* Failure is non-fatal for boot. The kernel keeps serial/debug-port logging if
|
||||
* the firmware exposes no supported graphics output protocol.
|
||||
*/
|
||||
efi_status boot_capture_framebuffer(
|
||||
efi_system_table_t *system_table, boot_info_t *boot_info);
|
||||
|
||||
#endif
|
||||
@ -4,7 +4,6 @@
|
||||
#include "efi.h"
|
||||
#include "elf_loader.h"
|
||||
#include "file.h"
|
||||
#include "framebuffer.h"
|
||||
#include "memory_map.h"
|
||||
|
||||
static efi_char16_t boot_banner_text[] = u"Tianole x86 bootloader.\r\n";
|
||||
@ -25,7 +24,6 @@ efi_status EFIAPI efi_main(
|
||||
system_table->con_out->output_string(
|
||||
system_table->con_out, boot_banner_text);
|
||||
boot_debug_log_puts("Tianole x86 bootloader loaded.\n");
|
||||
(void)boot_capture_framebuffer(system_table, &boot_info);
|
||||
|
||||
status = boot_read_file(image_handle,
|
||||
system_table,
|
||||
|
||||
@ -29,9 +29,6 @@ typedef uint64_t efi_physical_address_t;
|
||||
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_A 0x5b1b31a1
|
||||
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_B 0x9562
|
||||
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_C 0x11d2
|
||||
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_A 0x9042a9de
|
||||
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_B 0x23dc
|
||||
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_C 0x4a38
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define EFIAPI __attribute__((ms_abi))
|
||||
@ -61,7 +58,6 @@ typedef struct efi_simple_file_system_protocol
|
||||
efi_simple_file_system_protocol_t;
|
||||
typedef struct efi_file_protocol efi_file_protocol_t;
|
||||
typedef struct efi_loaded_image_protocol efi_loaded_image_protocol_t;
|
||||
typedef struct efi_graphics_output_protocol efi_graphics_output_protocol_t;
|
||||
typedef struct efi_system_table efi_system_table_t;
|
||||
|
||||
struct efi_simple_text_output_protocol {
|
||||
@ -126,31 +122,6 @@ struct efi_loaded_image_protocol {
|
||||
void *unload;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint32_t horizontal_resolution;
|
||||
uint32_t vertical_resolution;
|
||||
uint32_t pixel_format;
|
||||
uint32_t pixel_information[4];
|
||||
uint32_t pixels_per_scan_line;
|
||||
} efi_graphics_output_mode_information_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t max_mode;
|
||||
uint32_t mode;
|
||||
efi_graphics_output_mode_information_t *info;
|
||||
efi_uintn_t size_of_info;
|
||||
efi_physical_address_t frame_buffer_base;
|
||||
efi_uintn_t frame_buffer_size;
|
||||
} efi_graphics_output_protocol_mode_t;
|
||||
|
||||
struct efi_graphics_output_protocol {
|
||||
void *query_mode;
|
||||
void *set_mode;
|
||||
void *blt;
|
||||
efi_graphics_output_protocol_mode_t *mode;
|
||||
};
|
||||
|
||||
struct efi_boot_services {
|
||||
efi_table_header_t hdr;
|
||||
void *raise_tpl;
|
||||
@ -193,15 +164,6 @@ struct efi_boot_services {
|
||||
void *get_next_monotonic_count;
|
||||
void(EFIAPI *stall)(efi_uintn_t microseconds);
|
||||
void *set_watchdog_timer;
|
||||
void *connect_controller;
|
||||
void *disconnect_controller;
|
||||
void *open_protocol;
|
||||
void *close_protocol;
|
||||
void *open_protocol_information;
|
||||
void *protocols_per_handle;
|
||||
void *locate_handle_buffer;
|
||||
efi_status(EFIAPI *locate_protocol)(
|
||||
efi_guid_t *protocol, void *registration, void **interface);
|
||||
};
|
||||
|
||||
struct efi_system_table {
|
||||
@ -250,14 +212,4 @@ static inline efi_guid_t efi_loaded_image_protocol_guid(void)
|
||||
};
|
||||
}
|
||||
|
||||
static inline efi_guid_t efi_graphics_output_protocol_guid(void)
|
||||
{
|
||||
return (efi_guid_t){
|
||||
.data1 = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_A,
|
||||
.data2 = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_B,
|
||||
.data3 = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_C,
|
||||
.data4 = {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a},
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -3,8 +3,6 @@ ARCH_KERNEL_SRCS := \
|
||||
$(ARCH_DIR)/kernel/gdt.c \
|
||||
$(ARCH_DIR)/kernel/idt.c \
|
||||
$(ARCH_DIR)/kernel/irq.c \
|
||||
$(ARCH_DIR)/kernel/screen.c \
|
||||
$(ARCH_DIR)/kernel/screen_font.c \
|
||||
$(ARCH_DIR)/kernel/traps.c
|
||||
|
||||
ARCH_KERNEL_OBJS := \
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
|
||||
#include <tianole/arch.h>
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
#define X86_QEMU_DEBUG_PORT 0xe9
|
||||
|
||||
#define X86_COM1_BASE 0x3f8
|
||||
@ -39,7 +37,7 @@ static void serial_putc(char ch)
|
||||
outb(X86_COM1_BASE + X86_COM_DATA, (uint8_t)ch);
|
||||
}
|
||||
|
||||
void arch_early_log_init(const boot_info_t *boot_info)
|
||||
void arch_early_log_init(void)
|
||||
{
|
||||
outb(X86_COM1_BASE + X86_COM_INTERRUPT_ENABLE, 0x00);
|
||||
outb(X86_COM1_BASE + X86_COM_LINE_CONTROL, X86_COM_LCR_DLAB);
|
||||
@ -48,14 +46,12 @@ void arch_early_log_init(const boot_info_t *boot_info)
|
||||
outb(X86_COM1_BASE + X86_COM_LINE_CONTROL, X86_COM_LCR_8N1);
|
||||
outb(X86_COM1_BASE + X86_COM_FIFO_CONTROL, 0xc7);
|
||||
outb(X86_COM1_BASE + X86_COM_MODEM_CONTROL, 0x0b);
|
||||
screen_console_init(boot_info);
|
||||
}
|
||||
|
||||
void arch_early_log_putc(char ch)
|
||||
{
|
||||
debug_port_putc(ch);
|
||||
serial_putc(ch);
|
||||
screen_console_putc(ch);
|
||||
}
|
||||
|
||||
void arch_halt_forever(void)
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
|
||||
#include <tianole/arch.h>
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/errno.h>
|
||||
#include <tianole/irq.h>
|
||||
#include <tianole/timer.h>
|
||||
|
||||
@ -103,14 +102,14 @@ int irq_register(uint8_t irq, irq_handler_t handler, void *data)
|
||||
uint64_t flags;
|
||||
|
||||
if (irq >= IRQ_COUNT || handler == 0) {
|
||||
return -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
flags = arch_irq_save();
|
||||
|
||||
if (irq_actions[irq].handler != 0) {
|
||||
arch_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
irq_actions[irq].handler = handler;
|
||||
|
||||
@ -1,210 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
#include "screen.h"
|
||||
#include "screen_font.h"
|
||||
|
||||
#define GLYPH_SCALE 1u
|
||||
#define CELL_WIDTH 6u
|
||||
#define CELL_HEIGHT 9u
|
||||
|
||||
#define PIXEL_FORMAT_RGB 0u
|
||||
#define PIXEL_FORMAT_BGR 1u
|
||||
|
||||
#define ASCII_CR 13
|
||||
#define ASCII_LF 10
|
||||
|
||||
static volatile uint32_t *framebuffer;
|
||||
static uint32_t screen_width;
|
||||
static uint32_t screen_height;
|
||||
static uint32_t pixels_per_scan_line;
|
||||
static uint32_t pixel_format;
|
||||
static uint32_t cursor_x;
|
||||
static uint32_t cursor_y;
|
||||
static uint32_t columns;
|
||||
static uint32_t rows;
|
||||
|
||||
static uint32_t pixel_color(uint8_t red, uint8_t green, uint8_t blue)
|
||||
{
|
||||
if (pixel_format == PIXEL_FORMAT_RGB) {
|
||||
return red | ((uint32_t)green << 8) | ((uint32_t)blue << 16);
|
||||
}
|
||||
|
||||
return blue | ((uint32_t)green << 8) | ((uint32_t)red << 16);
|
||||
}
|
||||
|
||||
static void draw_pixel(uint32_t x, uint32_t y, uint32_t color)
|
||||
{
|
||||
if (x >= screen_width || y >= screen_height) {
|
||||
return;
|
||||
}
|
||||
|
||||
framebuffer[y * pixels_per_scan_line + x] = color;
|
||||
}
|
||||
|
||||
static void clear_cell(uint32_t column, uint32_t row)
|
||||
{
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint32_t start_x = column * CELL_WIDTH;
|
||||
uint32_t start_y = row * CELL_HEIGHT;
|
||||
uint32_t background = pixel_color(0, 0, 0);
|
||||
|
||||
for (y = 0; y < CELL_HEIGHT; y++) {
|
||||
for (x = 0; x < CELL_WIDTH; x++) {
|
||||
draw_pixel(start_x + x, start_y + y, background);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_char(char ch, uint32_t column, uint32_t row)
|
||||
{
|
||||
const uint8_t *glyph = screen_font_glyph(ch);
|
||||
uint32_t foreground = pixel_color(220, 220, 220);
|
||||
uint32_t glyph_row;
|
||||
uint32_t glyph_col;
|
||||
uint32_t scale_x;
|
||||
uint32_t scale_y;
|
||||
uint32_t start_x = column * CELL_WIDTH;
|
||||
uint32_t start_y = row * CELL_HEIGHT + 1;
|
||||
|
||||
clear_cell(column, row);
|
||||
|
||||
for (glyph_row = 0; glyph_row < SCREEN_FONT_HEIGHT; glyph_row++) {
|
||||
for (glyph_col = 0; glyph_col < SCREEN_FONT_WIDTH;
|
||||
glyph_col++) {
|
||||
if ((glyph[glyph_row] &
|
||||
(1u << (SCREEN_FONT_WIDTH - 1 -
|
||||
glyph_col))) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (scale_y = 0; scale_y < GLYPH_SCALE; scale_y++) {
|
||||
for (scale_x = 0; scale_x < GLYPH_SCALE;
|
||||
scale_x++) {
|
||||
draw_pixel(start_x +
|
||||
glyph_col *
|
||||
GLYPH_SCALE +
|
||||
scale_x,
|
||||
start_y +
|
||||
glyph_row *
|
||||
GLYPH_SCALE +
|
||||
scale_y,
|
||||
foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_screen(void)
|
||||
{
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint32_t background = pixel_color(0, 0, 0);
|
||||
|
||||
for (y = 0; y < screen_height; y++) {
|
||||
for (x = 0; x < screen_width; x++) {
|
||||
draw_pixel(x, y, background);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_text_row(uint32_t row)
|
||||
{
|
||||
uint32_t column;
|
||||
|
||||
for (column = 0; column < columns; column++) {
|
||||
clear_cell(column, row);
|
||||
}
|
||||
}
|
||||
|
||||
static void scroll_up(void)
|
||||
{
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
|
||||
for (y = CELL_HEIGHT; y < rows * CELL_HEIGHT; y++) {
|
||||
for (x = 0; x < columns * CELL_WIDTH; x++) {
|
||||
framebuffer[(y - CELL_HEIGHT) * pixels_per_scan_line +
|
||||
x] = framebuffer[y * pixels_per_scan_line + x];
|
||||
}
|
||||
}
|
||||
|
||||
clear_text_row(rows - 1);
|
||||
}
|
||||
|
||||
static void newline(void)
|
||||
{
|
||||
cursor_x = 0;
|
||||
if (cursor_y + 1 < rows) {
|
||||
cursor_y++;
|
||||
return;
|
||||
}
|
||||
|
||||
scroll_up();
|
||||
cursor_y = rows - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* screen_console_init() - Attach early logging to the GOP framebuffer.
|
||||
* @boot_info: Boot handoff data captured before ExitBootServices().
|
||||
*
|
||||
* The console is intentionally minimal. It validates that the bootloader found
|
||||
* a linear 32-bit RGB/BGR framebuffer, records the current mode, and clears the
|
||||
* visible surface so subsequent early_log output is readable in the QEMU
|
||||
* window.
|
||||
*/
|
||||
void screen_console_init(const boot_info_t *boot_info)
|
||||
{
|
||||
if (boot_info == 0 || boot_info->framebuffer_base == 0 ||
|
||||
boot_info->framebuffer_width == 0 ||
|
||||
boot_info->framebuffer_height == 0 ||
|
||||
boot_info->framebuffer_pixels_per_scan_line == 0 ||
|
||||
boot_info->framebuffer_pixel_format > PIXEL_FORMAT_BGR) {
|
||||
return;
|
||||
}
|
||||
|
||||
framebuffer =
|
||||
(volatile uint32_t *)(uintptr_t)boot_info->framebuffer_base;
|
||||
screen_width = boot_info->framebuffer_width;
|
||||
screen_height = boot_info->framebuffer_height;
|
||||
pixels_per_scan_line = boot_info->framebuffer_pixels_per_scan_line;
|
||||
pixel_format = boot_info->framebuffer_pixel_format;
|
||||
columns = screen_width / CELL_WIDTH;
|
||||
rows = screen_height / CELL_HEIGHT;
|
||||
cursor_x = 0;
|
||||
cursor_y = 0;
|
||||
clear_screen();
|
||||
}
|
||||
|
||||
/**
|
||||
* screen_console_putc() - Mirror a log byte to the early framebuffer console.
|
||||
* @ch: Byte emitted by early_log.
|
||||
*
|
||||
* Characters are drawn into fixed-size cells. Newlines advance the cursor; when
|
||||
* the last row is exhausted the framebuffer text area scrolls up one row so
|
||||
* real boot smoke tests keep useful context on screen.
|
||||
*/
|
||||
void screen_console_putc(char ch)
|
||||
{
|
||||
if (framebuffer == 0 || rows == 0 || columns == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch == ASCII_CR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch == ASCII_LF) {
|
||||
newline();
|
||||
return;
|
||||
}
|
||||
|
||||
draw_char(ch, cursor_x, cursor_y);
|
||||
cursor_x++;
|
||||
if (cursor_x >= columns) {
|
||||
newline();
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
#ifndef X86_KERNEL_SCREEN_H
|
||||
#define X86_KERNEL_SCREEN_H
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
/**
|
||||
* screen_console_init() - Bind early log output to a boot framebuffer.
|
||||
* @boot_info: Boot handoff data with framebuffer mode information.
|
||||
*/
|
||||
void screen_console_init(const boot_info_t *boot_info);
|
||||
|
||||
/**
|
||||
* screen_console_putc() - Draw one early log byte on the boot framebuffer.
|
||||
* @ch: Character emitted by early_log.
|
||||
*/
|
||||
void screen_console_putc(char ch);
|
||||
|
||||
#endif
|
||||
@ -1,81 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "screen_font.h"
|
||||
|
||||
struct glyph {
|
||||
char ch;
|
||||
uint8_t rows[SCREEN_FONT_HEIGHT];
|
||||
};
|
||||
|
||||
static const struct glyph font[] = {
|
||||
{' ', {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
|
||||
{'!', {0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x04}},
|
||||
{'.', {0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c}},
|
||||
{',', {0x00, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x08}},
|
||||
{':', {0x00, 0x0c, 0x0c, 0x00, 0x0c, 0x0c, 0x00}},
|
||||
{'-', {0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00}},
|
||||
{'_', {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f}},
|
||||
{'=', {0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00}},
|
||||
{'/', {0x01, 0x02, 0x04, 0x04, 0x08, 0x10, 0x10}},
|
||||
{'0', {0x0e, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0e}},
|
||||
{'1', {0x04, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x0e}},
|
||||
{'2', {0x0e, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1f}},
|
||||
{'3', {0x1e, 0x01, 0x01, 0x0e, 0x01, 0x01, 0x1e}},
|
||||
{'4', {0x02, 0x06, 0x0a, 0x12, 0x1f, 0x02, 0x02}},
|
||||
{'5', {0x1f, 0x10, 0x10, 0x1e, 0x01, 0x01, 0x1e}},
|
||||
{'6', {0x0e, 0x10, 0x10, 0x1e, 0x11, 0x11, 0x0e}},
|
||||
{'7', {0x1f, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08}},
|
||||
{'8', {0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e}},
|
||||
{'9', {0x0e, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x0e}},
|
||||
{'A', {0x0e, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11}},
|
||||
{'B', {0x1e, 0x11, 0x11, 0x1e, 0x11, 0x11, 0x1e}},
|
||||
{'C', {0x0e, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0e}},
|
||||
{'D', {0x1e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1e}},
|
||||
{'E', {0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x1f}},
|
||||
{'F', {0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x10}},
|
||||
{'G', {0x0e, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0f}},
|
||||
{'H', {0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11}},
|
||||
{'I', {0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e}},
|
||||
{'J', {0x07, 0x02, 0x02, 0x02, 0x12, 0x12, 0x0c}},
|
||||
{'K', {0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11}},
|
||||
{'L', {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1f}},
|
||||
{'M', {0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11}},
|
||||
{'N', {0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11}},
|
||||
{'O', {0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e}},
|
||||
{'P', {0x1e, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10}},
|
||||
{'Q', {0x0e, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0d}},
|
||||
{'R', {0x1e, 0x11, 0x11, 0x1e, 0x14, 0x12, 0x11}},
|
||||
{'S', {0x0f, 0x10, 0x10, 0x0e, 0x01, 0x01, 0x1e}},
|
||||
{'T', {0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}},
|
||||
{'U', {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e}},
|
||||
{'V', {0x11, 0x11, 0x11, 0x11, 0x0a, 0x0a, 0x04}},
|
||||
{'W', {0x11, 0x11, 0x11, 0x15, 0x15, 0x1b, 0x11}},
|
||||
{'X', {0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11}},
|
||||
{'Y', {0x11, 0x11, 0x0a, 0x04, 0x04, 0x04, 0x04}},
|
||||
{'Z', {0x1f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1f}},
|
||||
{'?', {0x0e, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04}},
|
||||
};
|
||||
|
||||
/**
|
||||
* screen_font_glyph() - Return a tiny bitmap glyph for early console text.
|
||||
* @ch: ASCII character requested by the early framebuffer console.
|
||||
*
|
||||
* The boot console keeps the font deliberately small: lowercase letters are
|
||||
* folded to uppercase, and unsupported characters fall back to question mark.
|
||||
*/
|
||||
const uint8_t *screen_font_glyph(char ch)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
if (ch >= 97 && ch <= 122) {
|
||||
ch = (char)(ch - 97 + 65);
|
||||
}
|
||||
|
||||
for (index = 0; index < sizeof(font) / sizeof(font[0]); index++) {
|
||||
if (font[index].ch == ch) {
|
||||
return font[index].rows;
|
||||
}
|
||||
}
|
||||
|
||||
return font[sizeof(font) / sizeof(font[0]) - 1].rows;
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
#ifndef X86_KERNEL_SCREEN_FONT_H
|
||||
#define X86_KERNEL_SCREEN_FONT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SCREEN_FONT_WIDTH 5u
|
||||
#define SCREEN_FONT_HEIGHT 7u
|
||||
|
||||
const uint8_t *screen_font_glyph(char ch);
|
||||
|
||||
#endif
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
#include <tianole/arch.h>
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/errno.h>
|
||||
#include <tianole/mm.h>
|
||||
|
||||
#include "page_table.h"
|
||||
@ -173,7 +172,7 @@ static int ensure_next_table(uint64_t *table, uint64_t index, uint64_t **next)
|
||||
|
||||
page = alloc_page();
|
||||
if (page == 0) {
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
clear_page(page);
|
||||
@ -194,34 +193,24 @@ static int page_entry(virt_addr_t virt, int create, uint64_t **entry)
|
||||
uint64_t pt_index = table_index(virt, 12);
|
||||
|
||||
if (create != 0) {
|
||||
int ret = ensure_next_table(pml4, pml4_index, &pdpt);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ensure_next_table(pdpt, pdpt_index, &pd);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ensure_next_table(pd, pd_index, &pt);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
if (ensure_next_table(pml4, pml4_index, &pdpt) != 0 ||
|
||||
ensure_next_table(pdpt, pdpt_index, &pd) != 0 ||
|
||||
ensure_next_table(pd, pd_index, &pt) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if ((pml4[pml4_index] & PAGE_PRESENT) == 0) {
|
||||
return -ENOENT;
|
||||
return -1;
|
||||
}
|
||||
pdpt = entry_table(pml4[pml4_index]);
|
||||
|
||||
if ((pdpt[pdpt_index] & PAGE_PRESENT) == 0) {
|
||||
return -ENOENT;
|
||||
return -1;
|
||||
}
|
||||
pd = entry_table(pdpt[pdpt_index]);
|
||||
|
||||
if ((pd[pd_index] & PAGE_PRESENT) == 0) {
|
||||
return -ENOENT;
|
||||
return -1;
|
||||
}
|
||||
pt = entry_table(pd[pd_index]);
|
||||
}
|
||||
@ -233,19 +222,17 @@ static int page_entry(virt_addr_t virt, int create, uint64_t **entry)
|
||||
int map_page(virt_addr_t virt, phys_addr_t phys, uint64_t flags)
|
||||
{
|
||||
uint64_t *entry;
|
||||
int ret;
|
||||
|
||||
if ((virt & (PAGE_SIZE - 1)) != 0 || (phys & (PAGE_SIZE - 1)) != 0) {
|
||||
return -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = page_entry(virt, 1, &entry);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
if (page_entry(virt, 1, &entry) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((*entry & PAGE_PRESENT) != 0) {
|
||||
return -EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*entry = (phys & PAGE_MASK) | flags | PAGE_PRESENT;
|
||||
@ -256,15 +243,13 @@ int map_page(virt_addr_t virt, phys_addr_t phys, uint64_t flags)
|
||||
int unmap_page(virt_addr_t virt)
|
||||
{
|
||||
uint64_t *entry;
|
||||
int ret;
|
||||
|
||||
if ((virt & (PAGE_SIZE - 1)) != 0) {
|
||||
return -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = page_entry(virt, 0, &entry);
|
||||
if (ret != 0 || (*entry & PAGE_PRESENT) == 0) {
|
||||
return -ENOENT;
|
||||
if (page_entry(virt, 0, &entry) != 0 || (*entry & PAGE_PRESENT) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*entry = 0;
|
||||
@ -275,15 +260,10 @@ int unmap_page(virt_addr_t virt)
|
||||
int virt_to_phys(virt_addr_t virt, phys_addr_t *phys)
|
||||
{
|
||||
uint64_t *entry;
|
||||
int ret;
|
||||
|
||||
if (phys == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = page_entry(virt, 0, &entry);
|
||||
if (ret != 0 || (*entry & PAGE_PRESENT) == 0) {
|
||||
return -ENOENT;
|
||||
if (phys == 0 || page_entry(virt, 0, &entry) != 0 ||
|
||||
(*entry & PAGE_PRESENT) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*phys = (*entry & PAGE_MASK) | (virt & (PAGE_SIZE - 1));
|
||||
|
||||
@ -44,14 +44,6 @@
|
||||
- 不写重复描述简单代码行为的注释。
|
||||
- 如果代码依赖硬件手册、启动协议、调用顺序或特殊寄存器状态,应写明约束。
|
||||
- 如果一个实现是临时简化,应写清楚后续替换边界,而不是写成永久接口。
|
||||
- 公共头文件 `include/tianole/*.h` 中的函数声明必须使用 Linux kernel-doc 风格块注释。
|
||||
- 公共结构体、枚举、typedef 和长期宏常量也必须使用 kernel-doc 风格块注释。
|
||||
- 公共函数注释至少说明函数职责、关键参数、返回值或副作用;不要只复述函数名。
|
||||
- 公共类型注释要说明字段含义、所有权、生命周期或并发约束。
|
||||
- 公共宏注释要说明它代表的协议常量、标志位或长期 ABI 含义。
|
||||
- 公共函数声明和前一个声明/注释块之间必须留一个空行,避免 API 挤在一起。
|
||||
- 私有 `static` 小函数不要求每个都写函数头注释;只有逻辑、数据流、锁语义或硬件约束不明显时才写。
|
||||
- 多行注释要控制行宽,优先写职责、数据流和调用约束,而不是描述每一行代码。
|
||||
|
||||
## C 格式
|
||||
|
||||
@ -75,10 +67,7 @@
|
||||
- 可独立执行的检查放在 `scripts/checks/`。
|
||||
- 多个检查共享的函数放在 `scripts/lib/`。
|
||||
- 不把所有检查逻辑持续堆进 `scripts/check.sh`。
|
||||
- 项目结构规则由 `scripts/tools/check_structure.py` 编排,具体规则拆到
|
||||
`scripts/tools/structure_checks/`;不要把 Python 工具堆成单个超大文件。
|
||||
- 优先用宿主 Linux/LLVM 工具实现底层检查,把 Tianole 自己的目录和 include
|
||||
约束固化在轻量 Python 工具里。
|
||||
- 项目结构规则放在 `scripts/tools/check_structure.py`,由 `scripts/checks/structure.sh` 调用;优先用宿主 Linux/LLVM 工具实现底层检查,把 Tianole 自己的目录和 include 约束固化在轻量 Python 工具里。
|
||||
- 启动阶段内核自测集中放在 `kernel/selftest/`,不要散落在具体实现目录里。
|
||||
|
||||
## 验证
|
||||
@ -86,20 +75,3 @@
|
||||
- 新增或大改 C/H 文件后运行 `clang-format`。
|
||||
- 提交前至少运行 `scripts/check.sh`。
|
||||
- 如果只是文档修改,至少运行 `git diff --check`。
|
||||
|
||||
## 错误码
|
||||
|
||||
- 可恢复的内核内部错误优先返回 Linux 风格的负 errno,例如
|
||||
`-EINVAL`、`-ENOMEM`、`-ENOENT`、`-EBUSY`、`-EEXIST`、`-ETIMEDOUT`。
|
||||
- 不要用裸 `-1` 表达多个失败原因;调用者需要能区分输入错误、资源耗尽、
|
||||
对象已存在和超时。
|
||||
- 不要直接返回 `-22` 这类负数字面量;使用 `-EINVAL` 这类符号化 errno,
|
||||
结构检查会拒绝裸负数返回。
|
||||
- errno 常量放在 `include/tianole/errno.h`,只增加当前内核实际使用的值。
|
||||
|
||||
## 调度状态
|
||||
|
||||
- 线程状态转换必须通过 `kernel/sched/sched.h` 中的 helper 完成。
|
||||
- 不要在调度实现或其他子系统里直接写 `thread->state = ...`;结构检查会拒绝
|
||||
这种写法。
|
||||
- 新增状态或转换规则时,必须同步更新状态转换 helper 和 scheduler selftest。
|
||||
|
||||
@ -79,7 +79,6 @@
|
||||
|
||||
- kernel early log 已拆成通用前端和 x86 backend。
|
||||
- x86 backend 已同时写 QEMU debug port 和 COM1。
|
||||
- x86 backend 已可在 UEFI GOP framebuffer 上显示 early log,QEMU 图形窗口可直接观察启动日志。
|
||||
- `panic()` 已接入 early log 和 `arch_halt_forever()`。
|
||||
- `scripts/check.sh` 已验证 `build/debug.log` 和 `build/serial.log` 中的关键启动行。
|
||||
|
||||
|
||||
@ -152,11 +152,9 @@
|
||||
- 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。
|
||||
- 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。
|
||||
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
||||
- 已建立统一 `kernel_thread_exit()`/`sched_thread_exit()`,线程入口返回和显式退出都会进入明确退出路径,再由调度安全边界回收非当前 DEAD 线程。
|
||||
- 已在调度私有头中加入 thread state helper,调度核心、线程退出和 wait queue 路径不再直接散写主要状态转换。
|
||||
- 已提供 `wait_queue_lock_irqsave()` / `wait_queue_unlock_irqrestore()` 和 locked wakeup 接口,条件修改与 wakeup 可以收敛在同一 wait queue 锁边界内。
|
||||
- 已把调度代码按职责拆分为 `core.c`、`thread.c`、`wait.c`、`idle.c` 和私有 `sched.h`,并把当前阶段自测/演示线程移到 `kernel/selftest/sched.c`。
|
||||
- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized`、`kernel thread selftest ok`、timer 驱动线程轮转、`sched_sleep()`、wait queue wakeup、条件等待、超时等待、线程返回退出、显式退出和 DEAD 线程回收。
|
||||
- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized`、`kernel thread selftest ok`、timer 驱动线程轮转、`sched_sleep()`、wait queue wakeup、条件等待和超时等待。
|
||||
|
||||
后续扩展:
|
||||
|
||||
@ -168,8 +166,8 @@
|
||||
|
||||
### B. wait queue 锁语义
|
||||
|
||||
- 已为 wait queue 增加内部锁,并提供显式 locked wakeup 接口;后续继续扩大调用方按条件锁规则更新条件的覆盖面。
|
||||
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队、wakeup 队列修改,以及 demo 中的条件修改 + wakeup。
|
||||
- 为 wait queue 增加内部锁或要求调用方持有指定锁,并在接口命名中体现约束。
|
||||
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队和 wakeup 队列修改。
|
||||
- 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。
|
||||
- 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。
|
||||
|
||||
@ -183,7 +181,7 @@
|
||||
### D. thread lifecycle
|
||||
|
||||
- 为线程退出增加更完整的生命周期状态、引用规则和最终释放约束。
|
||||
- 继续补充“当前线程不能释放自身内核栈”的更严格断言和未来 join/wait 语义。
|
||||
- 补充“当前线程不能释放自身内核栈”的文档和自测。
|
||||
- 为未来 `kthread_stop()`、join/wait 和进程退出保留接口空间。
|
||||
- 回收路径需要覆盖等待队列残留、run queue 残留和 timer sleep 残留。
|
||||
|
||||
|
||||
@ -3,66 +3,13 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
/**
|
||||
* arch_early_log_init() - Initialize architecture early log devices.
|
||||
* @boot_info: Bootloader handoff data, or NULL for backend-only setup.
|
||||
*
|
||||
* Provides the backend setup used by the generic early log front end.
|
||||
*/
|
||||
void arch_early_log_init(const boot_info_t *boot_info);
|
||||
|
||||
/**
|
||||
* 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_init(void);
|
||||
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
|
||||
|
||||
@ -3,26 +3,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* BOOT_MEMORY_TYPE_CONVENTIONAL - Firmware memory type for usable RAM.
|
||||
*
|
||||
* Mirrors the UEFI conventional memory type used by the bootloader handoff.
|
||||
* The kernel converts this into its own long-term memory model during MM init.
|
||||
*/
|
||||
#define BOOT_MEMORY_TYPE_CONVENTIONAL 7u
|
||||
|
||||
/**
|
||||
* typedef boot_memory_descriptor_t - Boot memory map descriptor.
|
||||
* @type: Firmware memory type.
|
||||
* @pad: Reserved padding used to keep the handoff layout stable.
|
||||
* @physical_start: Physical base address of this region.
|
||||
* @virtual_start: Firmware virtual address field, currently preserved only.
|
||||
* @number_of_pages: Region size in 4 KiB pages.
|
||||
* @attribute: Firmware attributes for the region.
|
||||
*
|
||||
* Describes one memory map entry copied from firmware-owned data into the
|
||||
* Tianole boot handoff format.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t type;
|
||||
uint32_t pad;
|
||||
@ -32,25 +14,10 @@ typedef struct {
|
||||
uint64_t attribute;
|
||||
} boot_memory_descriptor_t;
|
||||
|
||||
/**
|
||||
* typedef boot_info_t - Bootloader-to-kernel handoff data.
|
||||
* @version: Handoff structure version.
|
||||
* @boot_flags: Flags describing the firmware handoff state.
|
||||
* @memory_map: Physical address of boot_memory_descriptor_t entries.
|
||||
* @memory_map_size: Total memory map size in bytes.
|
||||
* @memory_map_key: Firmware key captured before ExitBootServices().
|
||||
* @memory_descriptor_size: Size of each memory descriptor entry.
|
||||
* @memory_descriptor_version: Firmware descriptor version.
|
||||
* @reserved0: Reserved field for alignment and future expansion.
|
||||
* @framebuffer_base: Physical base address of the boot framebuffer.
|
||||
* @framebuffer_size: Framebuffer size in bytes.
|
||||
* @framebuffer_width: Visible framebuffer width in pixels.
|
||||
* @framebuffer_height: Visible framebuffer height in pixels.
|
||||
* @framebuffer_pixels_per_scan_line: Physical pixels per scan line.
|
||||
* @framebuffer_pixel_format: Bootloader-provided pixel format identifier.
|
||||
*
|
||||
* Boot-time handoff data owned by Tianole rather than by a specific firmware
|
||||
* or architecture API. Fields can grow while the kernel entry stays stable.
|
||||
/*
|
||||
* Boot-time handoff data owned by Tianole rather than by a specific
|
||||
* firmware or architecture API. Fields can grow over time while the
|
||||
* kernel entry stays stable.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
@ -61,22 +28,9 @@ typedef struct {
|
||||
uint64_t memory_descriptor_size;
|
||||
uint32_t memory_descriptor_version;
|
||||
uint32_t reserved0;
|
||||
uint64_t framebuffer_base;
|
||||
uint64_t framebuffer_size;
|
||||
uint32_t framebuffer_width;
|
||||
uint32_t framebuffer_height;
|
||||
uint32_t framebuffer_pixels_per_scan_line;
|
||||
uint32_t framebuffer_pixel_format;
|
||||
} boot_info_t;
|
||||
|
||||
/**
|
||||
* BOOT_INFO_VERSION - Current boot_info_t layout version.
|
||||
*/
|
||||
#define BOOT_INFO_VERSION 2u
|
||||
|
||||
/**
|
||||
* BOOT_FLAG_SERVICES_ACTIVE - Firmware boot services were active at handoff.
|
||||
*/
|
||||
#define BOOT_INFO_VERSION 1u
|
||||
#define BOOT_FLAG_SERVICES_ACTIVE (1u << 0)
|
||||
|
||||
#endif
|
||||
|
||||
@ -3,56 +3,11 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
/**
|
||||
* early_log_init() - Initialize early logging backends.
|
||||
* @boot_info: Bootloader handoff data used by optional display 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(const boot_info_t *boot_info);
|
||||
|
||||
/**
|
||||
* 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_init(void);
|
||||
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
|
||||
|
||||
@ -3,61 +3,16 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* ELF_MAGIC - ELF file magic value in little-endian word form.
|
||||
*/
|
||||
#define ELF_MAGIC 0x464c457fu
|
||||
|
||||
/**
|
||||
* ELFCLASS64 - ELF identification value for 64-bit objects.
|
||||
*/
|
||||
#define ELFCLASS64 2
|
||||
|
||||
/**
|
||||
* ELFDATA2LSB - ELF identification value for little-endian objects.
|
||||
*/
|
||||
#define ELFDATA2LSB 1
|
||||
|
||||
/**
|
||||
* EV_CURRENT - Current ELF object format version.
|
||||
*/
|
||||
#define EV_CURRENT 1
|
||||
|
||||
/**
|
||||
* ET_EXEC - ELF object type for executable files.
|
||||
*/
|
||||
#define ET_EXEC 2
|
||||
|
||||
/**
|
||||
* EM_X86_64 - ELF machine type for x86_64.
|
||||
*/
|
||||
#define EM_X86_64 62
|
||||
|
||||
/**
|
||||
* PT_LOAD - ELF program header type for loadable segments.
|
||||
*/
|
||||
#define PT_LOAD 1
|
||||
|
||||
/**
|
||||
* typedef elf64_ehdr_t - ELF64 file header.
|
||||
* @ident: ELF identification bytes.
|
||||
* @type: Object file type.
|
||||
* @machine: Target machine architecture.
|
||||
* @version: ELF object version.
|
||||
* @entry: Entry point virtual address.
|
||||
* @phoff: Program header table file offset.
|
||||
* @shoff: Section header table file offset.
|
||||
* @flags: Processor-specific flags.
|
||||
* @ehsize: ELF header size.
|
||||
* @phentsize: Size of one program header entry.
|
||||
* @phnum: Number of program header entries.
|
||||
* @shentsize: Size of one section header entry.
|
||||
* @shnum: Number of section header entries.
|
||||
* @shstrndx: Section name string table index.
|
||||
*
|
||||
* Used by the bootloader and future ELF loader to validate and locate image
|
||||
* segments without depending on host libc headers.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char ident[16];
|
||||
uint16_t type;
|
||||
@ -75,20 +30,6 @@ typedef struct {
|
||||
uint16_t shstrndx;
|
||||
} elf64_ehdr_t;
|
||||
|
||||
/**
|
||||
* typedef elf64_phdr_t - ELF64 program header.
|
||||
* @type: Segment type.
|
||||
* @flags: Segment permissions.
|
||||
* @offset: Segment file offset.
|
||||
* @vaddr: Segment virtual address.
|
||||
* @paddr: Segment physical address, if meaningful for the image.
|
||||
* @filesz: Number of bytes present in the file.
|
||||
* @memsz: Number of bytes required in memory.
|
||||
* @align: Segment alignment requirement.
|
||||
*
|
||||
* Describes one loadable or metadata segment consumed by boot and future
|
||||
* user-mode ELF loading paths.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t type;
|
||||
uint32_t flags;
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
#ifndef TIANOLE_ERRNO_H
|
||||
#define TIANOLE_ERRNO_H
|
||||
|
||||
/**
|
||||
* EINVAL - Invalid argument.
|
||||
*/
|
||||
#define EINVAL 22
|
||||
|
||||
/**
|
||||
* ENOENT - No such entry or mapping.
|
||||
*/
|
||||
#define ENOENT 2
|
||||
|
||||
/**
|
||||
* ENOMEM - Out of memory.
|
||||
*/
|
||||
#define ENOMEM 12
|
||||
|
||||
/**
|
||||
* EBUSY - Resource is already busy.
|
||||
*/
|
||||
#define EBUSY 16
|
||||
|
||||
/**
|
||||
* EEXIST - Object already exists.
|
||||
*/
|
||||
#define EEXIST 17
|
||||
|
||||
/**
|
||||
* ETIMEDOUT - Operation timed out.
|
||||
*/
|
||||
#define ETIMEDOUT 110
|
||||
|
||||
#endif
|
||||
@ -3,21 +3,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* typedef irq_handler_t - External IRQ dispatch callback.
|
||||
* @irq: IRQ line that triggered the callback.
|
||||
* @data: Opaque pointer supplied at registration time.
|
||||
*/
|
||||
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, -EINVAL for bad input or -EBUSY if occupied.
|
||||
*/
|
||||
int irq_register(uint8_t irq, irq_handler_t handler, void *data);
|
||||
|
||||
#endif
|
||||
|
||||
@ -3,12 +3,6 @@
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -6,118 +6,24 @@
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
/**
|
||||
* PAGE_SIZE - Base page size used by the current memory manager.
|
||||
*/
|
||||
#define PAGE_SIZE 4096u
|
||||
|
||||
/**
|
||||
* typedef phys_addr_t - Physical address value.
|
||||
*
|
||||
* Represents addresses in physical memory, not directly dereferenceable by C.
|
||||
*/
|
||||
typedef uint64_t phys_addr_t;
|
||||
|
||||
/**
|
||||
* typedef virt_addr_t - Virtual address value.
|
||||
*
|
||||
* Represents addresses in the active virtual address space.
|
||||
*/
|
||||
typedef uint64_t virt_addr_t;
|
||||
|
||||
/**
|
||||
* PAGE_PRESENT - Mapping flag that marks a page table entry present.
|
||||
*/
|
||||
#define PAGE_PRESENT (1ull << 0)
|
||||
|
||||
/**
|
||||
* PAGE_WRITABLE - Mapping flag that allows writes through a page mapping.
|
||||
*/
|
||||
#define PAGE_WRITABLE (1ull << 1)
|
||||
|
||||
/**
|
||||
* PAGE_NO_EXECUTE - Mapping flag that blocks instruction fetches.
|
||||
*/
|
||||
#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, -EINVAL, -ENOMEM or -EEXIST 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, -EINVAL or -ENOENT 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, -EINVAL or -ENOENT 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
|
||||
|
||||
@ -6,31 +6,9 @@
|
||||
|
||||
#include <tianole/spinlock.h>
|
||||
|
||||
/**
|
||||
* typedef kernel_thread_entry_t - Kernel thread entry function.
|
||||
* @arg: Opaque argument supplied at thread creation.
|
||||
*
|
||||
* The function runs in kernel thread context. Returning from it is equivalent
|
||||
* to calling kernel_thread_exit().
|
||||
*/
|
||||
typedef void (*kernel_thread_entry_t)(void *arg);
|
||||
|
||||
/**
|
||||
* typedef wait_condition_t - Wait queue condition predicate.
|
||||
* @arg: Opaque predicate argument supplied by the caller.
|
||||
*
|
||||
* Return: Non-zero when the condition is satisfied, zero to keep waiting.
|
||||
*/
|
||||
typedef int (*wait_condition_t)(void *arg);
|
||||
|
||||
/**
|
||||
* enum thread_state - Scheduler-visible thread lifecycle state.
|
||||
* @THREAD_READY: Thread is runnable and may be selected by the scheduler.
|
||||
* @THREAD_RUNNING: Thread is currently executing on the CPU.
|
||||
* @THREAD_SLEEPING: Thread is blocked until a timer deadline.
|
||||
* @THREAD_WAITING: Thread is blocked on a wait queue.
|
||||
* @THREAD_DEAD: Thread has exited and is waiting for safe reclamation.
|
||||
*/
|
||||
enum thread_state {
|
||||
THREAD_READY,
|
||||
THREAD_RUNNING,
|
||||
@ -39,41 +17,12 @@ enum thread_state {
|
||||
THREAD_DEAD,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct wait_queue - Queue of threads blocked on an event or condition.
|
||||
* @lock: Interrupt-safe lock protecting queue links and wakeup state.
|
||||
* @head: Oldest waiting thread.
|
||||
* @tail: Newest waiting thread.
|
||||
*
|
||||
* The queue owns only wait links. A condition protected by this queue must be
|
||||
* updated under wait_queue_lock_irqsave(), followed by a locked wakeup before
|
||||
* releasing the lock; otherwise the caller must document its own lock rule.
|
||||
*/
|
||||
struct wait_queue {
|
||||
struct spinlock lock;
|
||||
struct thread *head;
|
||||
struct thread *tail;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct thread - Kernel scheduler thread object.
|
||||
* @id: Scheduler-assigned thread identifier.
|
||||
* @state: Current scheduler state.
|
||||
* @stack_pointer: Saved context stack pointer for context switching.
|
||||
* @entry: Thread entry function.
|
||||
* @arg: Entry function argument.
|
||||
* @stack_base: Allocated kernel stack base.
|
||||
* @stack_top: Aligned initial stack top.
|
||||
* @stack_size: Kernel stack size in bytes.
|
||||
* @wake_tick: Timer tick deadline for sleeping threads.
|
||||
* @next: Run queue link owned by the scheduler.
|
||||
* @wait_next: Wait queue link owned by wait queue code.
|
||||
* @wait_queue: Wait queue currently owning @wait_next, or NULL.
|
||||
* @name: Diagnostic thread name.
|
||||
*
|
||||
* This structure is public only as an early-stage compromise. Long term, most
|
||||
* fields should move behind scheduler-private helpers or an opaque handle.
|
||||
*/
|
||||
struct thread {
|
||||
uint64_t id;
|
||||
enum thread_state state;
|
||||
@ -86,172 +35,26 @@ struct thread {
|
||||
uint64_t wake_tick;
|
||||
struct thread *next;
|
||||
struct thread *wait_next;
|
||||
struct wait_queue *wait_queue;
|
||||
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_lock_irqsave() - Lock a wait queue and save interrupt state.
|
||||
* @queue: Wait queue whose condition or waiter links will be updated.
|
||||
* @flags: Storage for the previous interrupt state.
|
||||
*
|
||||
* Callers may use this lock to protect the condition paired with
|
||||
* wait_queue_wait(). In that model, update the condition and call a locked
|
||||
* wakeup helper before unlocking to avoid lost wakeups.
|
||||
*/
|
||||
void wait_queue_lock_irqsave(struct wait_queue *queue, uint64_t *flags);
|
||||
|
||||
/**
|
||||
* wait_queue_unlock_irqrestore() - Unlock a wait queue and restore interrupts.
|
||||
* @queue: Wait queue previously locked by wait_queue_lock_irqsave().
|
||||
* @flags: Interrupt state returned by wait_queue_lock_irqsave().
|
||||
*/
|
||||
void wait_queue_unlock_irqrestore(struct wait_queue *queue, uint64_t flags);
|
||||
|
||||
/**
|
||||
* wait_queue_wake_one_locked() - Wake one waiter while holding queue lock.
|
||||
* @queue: Locked wait queue containing waiting threads.
|
||||
*
|
||||
* Use when the wakeup condition is modified under the wait queue lock.
|
||||
*/
|
||||
void wait_queue_wake_one_locked(struct wait_queue *queue);
|
||||
|
||||
/**
|
||||
* wait_queue_wake_all_locked() - Wake all waiters while holding queue lock.
|
||||
* @queue: Locked wait queue containing waiting threads.
|
||||
*
|
||||
* Use when the wakeup condition is modified under the wait queue lock.
|
||||
*/
|
||||
void wait_queue_wake_all_locked(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.
|
||||
*
|
||||
* The predicate is evaluated while the queue lock is held. If the waited-on
|
||||
* condition uses this queue for synchronization, writers must hold the same
|
||||
* lock, update the condition, and then call wait_queue_wake_*_locked().
|
||||
*
|
||||
* Return: 0 when the condition is true, -EINVAL 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.
|
||||
*
|
||||
* The predicate is evaluated while the queue lock is held. The same condition
|
||||
* locking rule as wait_queue_wait() applies.
|
||||
*
|
||||
* Return: 0 when the condition is true, -EINVAL or -ETIMEDOUT otherwise.
|
||||
*/
|
||||
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
|
||||
|
||||
@ -3,42 +3,16 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* struct spinlock - Early interrupt-safe lock.
|
||||
* @locked: Non-zero while the lock is held.
|
||||
*
|
||||
* Current implementation is single-CPU and panics on contention. The API is
|
||||
* shaped so it can later grow toward a real SMP spinlock.
|
||||
*/
|
||||
struct spinlock {
|
||||
int locked;
|
||||
};
|
||||
|
||||
/**
|
||||
* SPINLOCK_INITIALIZER - Static initializer for struct spinlock.
|
||||
*/
|
||||
#define SPINLOCK_INITIALIZER \
|
||||
{ \
|
||||
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
|
||||
|
||||
@ -3,18 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -5,20 +5,20 @@
|
||||
|
||||
static int early_log_ready;
|
||||
|
||||
void early_log_init(const boot_info_t *boot_info)
|
||||
void early_log_init(void)
|
||||
{
|
||||
if (early_log_ready != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
arch_early_log_init(boot_info);
|
||||
arch_early_log_init();
|
||||
early_log_ready = 1;
|
||||
}
|
||||
|
||||
void early_log_putc(char ch)
|
||||
{
|
||||
if (early_log_ready == 0) {
|
||||
early_log_init(0);
|
||||
early_log_init();
|
||||
}
|
||||
|
||||
arch_early_log_putc(ch);
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
void kernel_main(const boot_info_t *boot_info)
|
||||
{
|
||||
early_log_init(boot_info);
|
||||
early_log_init();
|
||||
early_log_puts("kernel_main entered\n");
|
||||
arch_traps_init();
|
||||
kernel_report_boot_state(boot_info);
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
#include <tianole/errno.h>
|
||||
#include <tianole/sched.h>
|
||||
|
||||
#include "sched.h"
|
||||
@ -16,7 +15,7 @@ int sched_idle_create(void)
|
||||
{
|
||||
idle_thread = kernel_thread_create("idle", idle_thread_entry, 0);
|
||||
if (idle_thread == 0) {
|
||||
return -ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/sched.h>
|
||||
#include <tianole/spinlock.h>
|
||||
|
||||
@ -43,88 +42,38 @@ static inline int thread_is_dead(const struct thread *thread)
|
||||
return thread != 0 && thread->state == THREAD_DEAD;
|
||||
}
|
||||
|
||||
static inline int thread_state_transition_is_valid(
|
||||
enum thread_state from, enum thread_state to)
|
||||
{
|
||||
if (from == to) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (from) {
|
||||
case THREAD_READY:
|
||||
return to == THREAD_RUNNING;
|
||||
case THREAD_RUNNING:
|
||||
return to == THREAD_READY || to == THREAD_SLEEPING ||
|
||||
to == THREAD_WAITING || to == THREAD_DEAD;
|
||||
case THREAD_SLEEPING:
|
||||
case THREAD_WAITING:
|
||||
return to == THREAD_READY || to == THREAD_DEAD;
|
||||
case THREAD_DEAD:
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void thread_validate_state_transition(
|
||||
struct thread *thread, enum thread_state state)
|
||||
{
|
||||
if (thread == 0) {
|
||||
panic("null thread state transition");
|
||||
}
|
||||
|
||||
if (!thread_state_transition_is_valid(thread->state, state)) {
|
||||
panic("invalid thread state transition");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void thread_set_state(
|
||||
struct thread *thread, enum thread_state state)
|
||||
{
|
||||
thread_validate_state_transition(thread, state);
|
||||
thread->state = state;
|
||||
}
|
||||
|
||||
static inline void thread_init_ready(struct thread *thread)
|
||||
static inline void thread_set_ready(struct thread *thread)
|
||||
{
|
||||
thread->wake_tick = 0;
|
||||
thread->state = THREAD_READY;
|
||||
}
|
||||
|
||||
static inline void thread_set_ready(struct thread *thread)
|
||||
{
|
||||
thread_set_state(thread, THREAD_READY);
|
||||
thread->wake_tick = 0;
|
||||
}
|
||||
|
||||
static inline void thread_set_running(struct thread *thread)
|
||||
{
|
||||
thread_set_state(thread, THREAD_RUNNING);
|
||||
thread->state = THREAD_RUNNING;
|
||||
}
|
||||
|
||||
static inline void thread_set_sleeping(
|
||||
struct thread *thread, uint64_t wake_tick)
|
||||
{
|
||||
thread_validate_state_transition(thread, THREAD_SLEEPING);
|
||||
thread->wake_tick = wake_tick;
|
||||
thread->state = THREAD_SLEEPING;
|
||||
}
|
||||
|
||||
static inline void thread_set_waiting(struct thread *thread)
|
||||
{
|
||||
thread_set_state(thread, THREAD_WAITING);
|
||||
thread->wake_tick = 0;
|
||||
thread->state = THREAD_WAITING;
|
||||
}
|
||||
|
||||
static inline void thread_set_dead(struct thread *thread)
|
||||
{
|
||||
thread_set_state(thread, THREAD_DEAD);
|
||||
thread->wake_tick = 0;
|
||||
thread->state = THREAD_DEAD;
|
||||
}
|
||||
|
||||
void enqueue_thread(struct thread *thread);
|
||||
void sched_reap_dead_threads(void);
|
||||
void sched_thread_exit(void) __attribute__((noreturn));
|
||||
void sched_selftest(void);
|
||||
int sched_idle_create(void);
|
||||
void sched_demo_start(void) __attribute__((noreturn));
|
||||
|
||||
@ -77,7 +77,7 @@ struct thread *kernel_thread_create(
|
||||
|
||||
stack_top = (uintptr_t)thread->stack_base + KERNEL_STACK_SIZE;
|
||||
|
||||
thread_init_ready(thread);
|
||||
thread_set_ready(thread);
|
||||
thread->entry = entry;
|
||||
thread->arg = arg;
|
||||
thread->stack_top = align_down_uintptr(stack_top, STACK_ALIGNMENT);
|
||||
@ -86,7 +86,6 @@ struct thread *kernel_thread_create(
|
||||
thread->wake_tick = 0;
|
||||
thread->next = 0;
|
||||
thread->wait_next = 0;
|
||||
thread->wait_queue = 0;
|
||||
copy_thread_name(thread->name, sizeof(thread->name), name);
|
||||
|
||||
spin_lock_irqsave(&scheduler_lock, &flags);
|
||||
@ -99,13 +98,6 @@ struct thread *kernel_thread_create(
|
||||
|
||||
static void release_thread(struct thread *thread)
|
||||
{
|
||||
if (thread->wait_queue != 0) {
|
||||
panic("reaping thread still on wait queue");
|
||||
}
|
||||
|
||||
early_log_puts("thread reaped ");
|
||||
early_log_puts(thread->name);
|
||||
early_log_puts("\n");
|
||||
kfree(thread->stack_base);
|
||||
kfree(thread);
|
||||
}
|
||||
@ -114,10 +106,7 @@ void sched_reap_dead_threads(void)
|
||||
{
|
||||
struct thread *prev = 0;
|
||||
struct thread *thread = run_queue_head;
|
||||
struct thread *reap_list = 0;
|
||||
uint64_t flags;
|
||||
|
||||
spin_lock_irqsave(&scheduler_lock, &flags);
|
||||
while (thread != 0) {
|
||||
struct thread *next = thread->next;
|
||||
|
||||
@ -132,41 +121,13 @@ void sched_reap_dead_threads(void)
|
||||
run_queue_tail = prev;
|
||||
}
|
||||
|
||||
thread->next = reap_list;
|
||||
reap_list = thread;
|
||||
release_thread(thread);
|
||||
} else {
|
||||
prev = thread;
|
||||
}
|
||||
|
||||
thread = next;
|
||||
}
|
||||
spin_unlock_irqrestore(&scheduler_lock, flags);
|
||||
|
||||
while (reap_list != 0) {
|
||||
struct thread *next = reap_list->next;
|
||||
|
||||
reap_list->next = 0;
|
||||
release_thread(reap_list);
|
||||
reap_list = next;
|
||||
}
|
||||
}
|
||||
|
||||
void sched_thread_exit(void)
|
||||
{
|
||||
if (current_thread == 0) {
|
||||
panic("thread exit without current thread");
|
||||
}
|
||||
|
||||
thread_set_dead(current_thread);
|
||||
|
||||
for (;;) {
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void kernel_thread_exit(void)
|
||||
{
|
||||
sched_thread_exit();
|
||||
}
|
||||
|
||||
static void thread_trampoline(void)
|
||||
@ -178,5 +139,9 @@ static void thread_trampoline(void)
|
||||
}
|
||||
|
||||
thread->entry(thread->arg);
|
||||
kernel_thread_exit();
|
||||
thread_set_dead(thread);
|
||||
|
||||
for (;;) {
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
#include <tianole/errno.h>
|
||||
#include <tianole/sched.h>
|
||||
#include <tianole/timer.h>
|
||||
|
||||
@ -18,12 +17,7 @@ void wait_queue_init(struct wait_queue *queue)
|
||||
static void wait_queue_enqueue_locked(
|
||||
struct wait_queue *queue, struct thread *thread)
|
||||
{
|
||||
if (thread->wait_queue != 0) {
|
||||
panic("thread already queued on wait queue");
|
||||
}
|
||||
|
||||
thread->wait_next = 0;
|
||||
thread->wait_queue = queue;
|
||||
|
||||
if (queue->tail != 0) {
|
||||
queue->tail->wait_next = thread;
|
||||
@ -34,36 +28,14 @@ static void wait_queue_enqueue_locked(
|
||||
queue->tail = thread;
|
||||
}
|
||||
|
||||
static struct thread *wait_queue_remove_head_locked(struct wait_queue *queue)
|
||||
{
|
||||
struct thread *thread = queue->head;
|
||||
|
||||
if (thread == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
queue->head = thread->wait_next;
|
||||
if (queue->head == 0) {
|
||||
queue->tail = 0;
|
||||
}
|
||||
|
||||
thread->wait_next = 0;
|
||||
thread->wait_queue = 0;
|
||||
return thread;
|
||||
}
|
||||
|
||||
static int wait_queue_remove_locked(
|
||||
static void wait_queue_remove_locked(
|
||||
struct wait_queue *queue, struct thread *target)
|
||||
{
|
||||
struct thread *prev = 0;
|
||||
struct thread *thread;
|
||||
|
||||
if (queue == 0 || target == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (target->wait_queue != queue) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (thread = queue->head; thread != 0; thread = thread->wait_next) {
|
||||
@ -79,70 +51,17 @@ static int wait_queue_remove_locked(
|
||||
}
|
||||
|
||||
thread->wait_next = 0;
|
||||
thread->wait_queue = 0;
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
|
||||
prev = thread;
|
||||
}
|
||||
|
||||
panic("wait queue membership is inconsistent");
|
||||
}
|
||||
|
||||
static void wait_queue_mark_ready_locked(struct thread *thread)
|
||||
{
|
||||
if (thread == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!thread_is_waiting(thread) && !thread_is_sleeping(thread)) {
|
||||
panic("wait queue wakeup found non-waiting thread");
|
||||
}
|
||||
|
||||
thread_set_ready(thread);
|
||||
}
|
||||
|
||||
void wait_queue_lock_irqsave(struct wait_queue *queue, uint64_t *flags)
|
||||
{
|
||||
if (queue == 0 || flags == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&queue->lock, flags);
|
||||
}
|
||||
|
||||
void wait_queue_unlock_irqrestore(struct wait_queue *queue, uint64_t flags)
|
||||
{
|
||||
if (queue == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
}
|
||||
|
||||
void wait_queue_wake_one_locked(struct wait_queue *queue)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
if (queue == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
thread = wait_queue_remove_head_locked(queue);
|
||||
wait_queue_mark_ready_locked(thread);
|
||||
}
|
||||
|
||||
void wait_queue_wake_all_locked(struct wait_queue *queue)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
if (queue == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (queue->head != 0) {
|
||||
thread = wait_queue_remove_head_locked(queue);
|
||||
wait_queue_mark_ready_locked(thread);
|
||||
if (thread_is_waiting(thread) || thread_is_sleeping(thread)) {
|
||||
thread_set_ready(thread);
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,7 +96,7 @@ int wait_queue_wait(
|
||||
uint64_t flags;
|
||||
|
||||
if (queue == 0 || condition == 0 || current_thread == 0) {
|
||||
return -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
@ -208,7 +127,7 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
|
||||
uint64_t flags;
|
||||
|
||||
if (queue == 0 || condition == 0 || current_thread == 0) {
|
||||
return -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (condition(arg) != 0) {
|
||||
@ -216,7 +135,7 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
|
||||
}
|
||||
|
||||
if (ticks == 0) {
|
||||
return -ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
deadline = timer_ticks() + ticks;
|
||||
@ -233,7 +152,7 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
|
||||
if (now >= deadline) {
|
||||
current_thread->wake_tick = 0;
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
return -ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
thread_set_sleeping(current_thread, deadline);
|
||||
@ -250,6 +169,7 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
|
||||
|
||||
void wait_queue_wake_one(struct wait_queue *queue)
|
||||
{
|
||||
struct thread *thread;
|
||||
uint64_t flags;
|
||||
|
||||
if (queue == 0) {
|
||||
@ -257,12 +177,25 @@ void wait_queue_wake_one(struct wait_queue *queue)
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&queue->lock, &flags);
|
||||
wait_queue_wake_one_locked(queue);
|
||||
if (queue->head == 0) {
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
thread = queue->head;
|
||||
queue->head = thread->wait_next;
|
||||
if (queue->head == 0) {
|
||||
queue->tail = 0;
|
||||
}
|
||||
|
||||
thread->wait_next = 0;
|
||||
wait_queue_mark_ready_locked(thread);
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
}
|
||||
|
||||
void wait_queue_wake_all(struct wait_queue *queue)
|
||||
{
|
||||
struct thread *thread;
|
||||
uint64_t flags;
|
||||
|
||||
if (queue == 0) {
|
||||
@ -270,6 +203,15 @@ void wait_queue_wake_all(struct wait_queue *queue)
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&queue->lock, &flags);
|
||||
wait_queue_wake_all_locked(queue);
|
||||
while (queue->head != 0) {
|
||||
thread = queue->head;
|
||||
queue->head = thread->wait_next;
|
||||
if (queue->head == 0) {
|
||||
queue->tail = 0;
|
||||
}
|
||||
|
||||
thread->wait_next = 0;
|
||||
wait_queue_mark_ready_locked(thread);
|
||||
}
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/errno.h>
|
||||
#include <tianole/mm.h>
|
||||
|
||||
#include "arch/x86/mm/page_table.h"
|
||||
@ -21,26 +20,10 @@ void page_table_selftest(void)
|
||||
|
||||
page_tables_init();
|
||||
|
||||
if (map_page(TEST_VIRTUAL_PAGE + 1, page, PAGE_WRITABLE) != -EINVAL) {
|
||||
panic("page table selftest unaligned map errno failed");
|
||||
}
|
||||
|
||||
if (virt_to_phys(TEST_VIRTUAL_PAGE, 0) != -EINVAL) {
|
||||
panic("page table selftest null output errno failed");
|
||||
}
|
||||
|
||||
if (virt_to_phys(TEST_VIRTUAL_PAGE, &resolved) != -ENOENT) {
|
||||
panic("page table selftest missing resolve errno failed");
|
||||
}
|
||||
|
||||
if (map_page(TEST_VIRTUAL_PAGE, page, PAGE_WRITABLE) != 0) {
|
||||
panic("page table selftest map failed");
|
||||
}
|
||||
|
||||
if (map_page(TEST_VIRTUAL_PAGE, page, PAGE_WRITABLE) != -EEXIST) {
|
||||
panic("page table selftest duplicate map errno failed");
|
||||
}
|
||||
|
||||
if (virt_to_phys(TEST_VIRTUAL_PAGE, &resolved) != 0 ||
|
||||
resolved != page) {
|
||||
panic("page table selftest resolve failed");
|
||||
@ -55,10 +38,6 @@ void page_table_selftest(void)
|
||||
panic("page table selftest unmap failed");
|
||||
}
|
||||
|
||||
if (unmap_page(TEST_VIRTUAL_PAGE) != -ENOENT) {
|
||||
panic("page table selftest missing unmap errno failed");
|
||||
}
|
||||
|
||||
free_page(page);
|
||||
early_log_puts("page table selftest ok\n");
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/errno.h>
|
||||
#include <tianole/sched.h>
|
||||
#include <tianole/spinlock.h>
|
||||
|
||||
@ -14,53 +13,15 @@ static void thread_selftest_entry(void *arg)
|
||||
(void)arg;
|
||||
}
|
||||
|
||||
static int selftest_condition_false(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void assert_thread_transition(
|
||||
enum thread_state from, enum thread_state to, int expected)
|
||||
{
|
||||
if (thread_state_transition_is_valid(from, to) != expected) {
|
||||
panic("thread state transition selftest failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void sched_state_machine_selftest(void)
|
||||
{
|
||||
assert_thread_transition(THREAD_READY, THREAD_RUNNING, 1);
|
||||
assert_thread_transition(THREAD_RUNNING, THREAD_READY, 1);
|
||||
assert_thread_transition(THREAD_RUNNING, THREAD_SLEEPING, 1);
|
||||
assert_thread_transition(THREAD_RUNNING, THREAD_WAITING, 1);
|
||||
assert_thread_transition(THREAD_RUNNING, THREAD_DEAD, 1);
|
||||
assert_thread_transition(THREAD_SLEEPING, THREAD_READY, 1);
|
||||
assert_thread_transition(THREAD_WAITING, THREAD_READY, 1);
|
||||
assert_thread_transition(THREAD_SLEEPING, THREAD_DEAD, 1);
|
||||
assert_thread_transition(THREAD_WAITING, THREAD_DEAD, 1);
|
||||
|
||||
assert_thread_transition(THREAD_READY, THREAD_SLEEPING, 0);
|
||||
assert_thread_transition(THREAD_READY, THREAD_WAITING, 0);
|
||||
assert_thread_transition(THREAD_READY, THREAD_DEAD, 0);
|
||||
assert_thread_transition(THREAD_SLEEPING, THREAD_RUNNING, 0);
|
||||
assert_thread_transition(THREAD_WAITING, THREAD_RUNNING, 0);
|
||||
assert_thread_transition(THREAD_DEAD, THREAD_READY, 0);
|
||||
assert_thread_transition(THREAD_DEAD, THREAD_RUNNING, 0);
|
||||
}
|
||||
|
||||
void sched_selftest(void)
|
||||
{
|
||||
struct spinlock test_lock;
|
||||
struct wait_queue test_wait_queue;
|
||||
struct thread *first =
|
||||
kernel_thread_create("worker-a", thread_selftest_entry, 0);
|
||||
struct thread *second =
|
||||
kernel_thread_create("worker-b", thread_selftest_entry, 0);
|
||||
uint64_t flags;
|
||||
|
||||
sched_state_machine_selftest();
|
||||
|
||||
test_lock.locked = 0;
|
||||
|
||||
if (first == 0 || second == 0 || first == second) {
|
||||
@ -72,10 +33,6 @@ void sched_selftest(void)
|
||||
panic("kernel thread selftest state failed");
|
||||
}
|
||||
|
||||
if (first->wait_queue != 0 || second->wait_queue != 0) {
|
||||
panic("kernel thread selftest wait queue ownership failed");
|
||||
}
|
||||
|
||||
if ((first->stack_top & (STACK_ALIGNMENT - 1)) != 0 ||
|
||||
(second->stack_top & (STACK_ALIGNMENT - 1)) != 0) {
|
||||
panic("kernel thread selftest stack alignment failed");
|
||||
@ -89,15 +46,6 @@ void sched_selftest(void)
|
||||
spin_lock_irqsave(&test_lock, &flags);
|
||||
spin_unlock_irqrestore(&test_lock, flags);
|
||||
|
||||
wait_queue_init(&test_wait_queue);
|
||||
if (wait_queue_wait(0, selftest_condition_false, 0) != -EINVAL) {
|
||||
panic("wait queue selftest null queue errno failed");
|
||||
}
|
||||
|
||||
if (wait_queue_wait(&test_wait_queue, 0, 0) != -EINVAL) {
|
||||
panic("wait queue selftest null condition errno failed");
|
||||
}
|
||||
|
||||
early_log_puts("kernel thread selftest ok\n");
|
||||
}
|
||||
|
||||
@ -162,17 +110,13 @@ static void condition_wait_demo_waiter(void *arg)
|
||||
|
||||
static void condition_wait_demo_waker(void *arg)
|
||||
{
|
||||
uint64_t flags;
|
||||
|
||||
(void)arg;
|
||||
|
||||
early_log_puts("condition waker sleeping\n");
|
||||
sched_sleep(5);
|
||||
wait_queue_lock_irqsave(&condition_wait_queue, &flags);
|
||||
condition_ready = 1;
|
||||
early_log_puts("condition waker wake_all\n");
|
||||
wait_queue_wake_all_locked(&condition_wait_queue);
|
||||
wait_queue_unlock_irqrestore(&condition_wait_queue, flags);
|
||||
wait_queue_wake_all(&condition_wait_queue);
|
||||
}
|
||||
|
||||
static void timeout_wait_demo_waiter(void *arg)
|
||||
@ -182,75 +126,41 @@ static void timeout_wait_demo_waiter(void *arg)
|
||||
(void)arg;
|
||||
|
||||
early_log_puts("timeout waiter sleeping\n");
|
||||
if (wait_queue_wait_timeout(
|
||||
&timeout_wait_queue, condition_is_ready, &never_ready, 0) !=
|
||||
-ETIMEDOUT) {
|
||||
panic("timeout wait zero tick errno failed");
|
||||
}
|
||||
|
||||
if (wait_queue_wait_timeout(
|
||||
&timeout_wait_queue, condition_is_ready, &never_ready, 3) !=
|
||||
-ETIMEDOUT) {
|
||||
-1) {
|
||||
panic("timeout wait did not time out");
|
||||
}
|
||||
early_log_puts("timeout waiter timed out\n");
|
||||
}
|
||||
|
||||
static void return_exit_demo_thread(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
early_log_puts("return exit thread returning\n");
|
||||
}
|
||||
|
||||
static void explicit_exit_demo_thread(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
early_log_puts("explicit exit thread exiting\n");
|
||||
kernel_thread_exit();
|
||||
}
|
||||
|
||||
void sched_demo_start(void)
|
||||
{
|
||||
struct thread *first;
|
||||
struct thread *second;
|
||||
struct thread *waiter;
|
||||
struct thread *waker;
|
||||
struct thread *condition_waiter;
|
||||
struct thread *condition_waker;
|
||||
struct thread *timeout_waiter;
|
||||
struct thread *return_exit;
|
||||
struct thread *explicit_exit;
|
||||
struct thread *first = kernel_thread_create(
|
||||
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
|
||||
struct thread *second = kernel_thread_create(
|
||||
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
|
||||
struct thread *waiter =
|
||||
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
||||
struct thread *waker =
|
||||
kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
||||
struct thread *condition_waiter = kernel_thread_create(
|
||||
"condition-waiter", condition_wait_demo_waiter, 0);
|
||||
struct thread *condition_waker = kernel_thread_create(
|
||||
"condition-waker", condition_wait_demo_waker, 0);
|
||||
struct thread *timeout_waiter = kernel_thread_create(
|
||||
"timeout-waiter", timeout_wait_demo_waiter, 0);
|
||||
|
||||
if (first == 0 || second == 0 || waiter == 0 || waker == 0 ||
|
||||
condition_waiter == 0 || condition_waker == 0 ||
|
||||
timeout_waiter == 0) {
|
||||
panic("scheduler demo thread creation failed");
|
||||
}
|
||||
|
||||
wait_queue_init(&demo_wait_queue);
|
||||
wait_queue_init(&condition_wait_queue);
|
||||
wait_queue_init(&timeout_wait_queue);
|
||||
condition_ready = 0;
|
||||
|
||||
first = kernel_thread_create(
|
||||
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
|
||||
second = kernel_thread_create(
|
||||
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
|
||||
waiter = kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
||||
waker = kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
||||
condition_waiter = kernel_thread_create(
|
||||
"condition-waiter", condition_wait_demo_waiter, 0);
|
||||
condition_waker = kernel_thread_create(
|
||||
"condition-waker", condition_wait_demo_waker, 0);
|
||||
timeout_waiter = kernel_thread_create(
|
||||
"timeout-waiter", timeout_wait_demo_waiter, 0);
|
||||
return_exit =
|
||||
kernel_thread_create("return-exit", return_exit_demo_thread, 0);
|
||||
explicit_exit = kernel_thread_create(
|
||||
"explicit-exit", explicit_exit_demo_thread, 0);
|
||||
|
||||
if (first == 0 || second == 0 || waiter == 0 || waker == 0 ||
|
||||
condition_waiter == 0 || condition_waker == 0 ||
|
||||
timeout_waiter == 0 || return_exit == 0 || explicit_exit == 0) {
|
||||
panic("scheduler demo thread creation failed");
|
||||
}
|
||||
|
||||
early_log_puts("scheduler starting\n");
|
||||
sched_yield();
|
||||
|
||||
|
||||
21
mm/heap.c
21
mm/heap.c
@ -2,7 +2,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/errno.h>
|
||||
#include <tianole/mm.h>
|
||||
|
||||
#define HEAP_BASE 0xffffff2000000000ull
|
||||
@ -86,16 +85,12 @@ static int map_heap_range(virt_addr_t start, size_t bytes)
|
||||
|
||||
for (current = start; current < end; current += PAGE_SIZE) {
|
||||
phys_addr_t page = alloc_page();
|
||||
int ret;
|
||||
|
||||
if (page == 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = map_page(current, page, PAGE_WRITABLE | PAGE_NO_EXECUTE);
|
||||
if (ret != 0) {
|
||||
free_page(page);
|
||||
return ret;
|
||||
if (page == 0 ||
|
||||
map_page(current,
|
||||
page,
|
||||
PAGE_WRITABLE | PAGE_NO_EXECUTE) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,11 +102,9 @@ static int heap_extend(size_t min_size)
|
||||
size_t bytes =
|
||||
align_up_size(min_size + sizeof(struct heap_block), PAGE_SIZE);
|
||||
struct heap_block *block = (struct heap_block *)(uintptr_t)heap_end;
|
||||
int ret;
|
||||
|
||||
ret = map_heap_range(heap_end, bytes);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
if (map_heap_range(heap_end, bytes) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
heap_end += bytes;
|
||||
|
||||
@ -34,12 +34,6 @@ check_lines build/debug.log \
|
||||
"condition waiter sleeping" \
|
||||
"condition waker sleeping" \
|
||||
"timeout waiter sleeping" \
|
||||
"return exit thread returning" \
|
||||
"explicit exit thread exiting" \
|
||||
"thread reaped worker-a" \
|
||||
"thread reaped worker-b" \
|
||||
"thread reaped return-exit" \
|
||||
"thread reaped explicit-exit" \
|
||||
"preempt thread 1 step=2" \
|
||||
"preempt thread 2 step=2" \
|
||||
"timeout waiter timed out" \
|
||||
@ -77,12 +71,6 @@ check_lines build/serial.log \
|
||||
"condition waiter sleeping" \
|
||||
"condition waker sleeping" \
|
||||
"timeout waiter sleeping" \
|
||||
"return exit thread returning" \
|
||||
"explicit exit thread exiting" \
|
||||
"thread reaped worker-a" \
|
||||
"thread reaped worker-b" \
|
||||
"thread reaped return-exit" \
|
||||
"thread reaped explicit-exit" \
|
||||
"preempt thread 1 step=2" \
|
||||
"preempt thread 2 step=2" \
|
||||
"timeout waiter timed out" \
|
||||
|
||||
2
scripts/tools/.gitignore
vendored
2
scripts/tools/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
@ -1,47 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from structure_checks.common import (
|
||||
all_files,
|
||||
c_files,
|
||||
changed_files,
|
||||
public_headers,
|
||||
repo_root,
|
||||
source_files,
|
||||
)
|
||||
from structure_checks.makefiles import check_makefile_source_lists
|
||||
from structure_checks.public_headers import check_public_header_docs
|
||||
from structure_checks.source_rules import (
|
||||
check_no_bare_negative_errno,
|
||||
check_no_relative_parent_includes,
|
||||
check_scheduler_state_writes,
|
||||
check_selftests_are_centralized,
|
||||
|
||||
SOURCE_ROOTS = ("arch", "include", "kernel", "mm")
|
||||
C_SOURCE_ROOTS = ("arch", "kernel", "mm")
|
||||
MAKEFILE_SOURCE_VARS = (
|
||||
"ARCH_KERNEL_SRCS",
|
||||
"ARCH_MM_SRCS",
|
||||
"BOOT_SRCS",
|
||||
"KERNEL_SRCS",
|
||||
"MM_SRCS",
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
def repo_root() -> Path:
|
||||
return Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def run_git(root: Path, args: list[str]) -> list[str]:
|
||||
result = subprocess.run(
|
||||
["git", *args],
|
||||
cwd=root,
|
||||
check=True,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
return [line for line in result.stdout.splitlines() if line]
|
||||
|
||||
|
||||
def changed_files(root: Path) -> set[Path]:
|
||||
files = set()
|
||||
|
||||
for path in run_git(root, ["diff", "--name-only", "--diff-filter=ACMR"]):
|
||||
files.add(Path(path))
|
||||
|
||||
for path in run_git(
|
||||
root, ["diff", "--cached", "--name-only", "--diff-filter=ACMR"]
|
||||
):
|
||||
files.add(Path(path))
|
||||
|
||||
for path in run_git(root, ["ls-files", "--others", "--exclude-standard"]):
|
||||
files.add(Path(path))
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def all_files(root: Path) -> set[Path]:
|
||||
files = set()
|
||||
|
||||
for base in SOURCE_ROOTS:
|
||||
root_dir = root / base
|
||||
if not root_dir.exists():
|
||||
continue
|
||||
|
||||
for path in root_dir.rglob("*"):
|
||||
if path.is_file():
|
||||
files.add(path.relative_to(root))
|
||||
|
||||
for path in (root / "scripts").rglob("*"):
|
||||
if path.is_file():
|
||||
files.add(path.relative_to(root))
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def source_files(files: set[Path]) -> list[Path]:
|
||||
return sorted(path for path in files if path.suffix in (".c", ".h"))
|
||||
|
||||
|
||||
def c_files(files: set[Path]) -> list[Path]:
|
||||
return sorted(
|
||||
path
|
||||
for path in files
|
||||
if path.suffix == ".c" and path.parts and path.parts[0] in C_SOURCE_ROOTS
|
||||
)
|
||||
|
||||
|
||||
def read_text(root: Path, path: Path) -> str:
|
||||
return (root / path).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def check_no_relative_parent_includes(root: Path, files: list[Path]) -> list[str]:
|
||||
errors = []
|
||||
include_re = re.compile(r'^\s*#include\s+[<"]\.\./')
|
||||
|
||||
for path in files:
|
||||
for line_no, line in enumerate(read_text(root, path).splitlines(), 1):
|
||||
if include_re.search(line):
|
||||
errors.append(
|
||||
f"{path}:{line_no}: do not use ../ includes; "
|
||||
"promote shared headers or add a private include root"
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def check_selftests_are_centralized(files: list[Path]) -> list[str]:
|
||||
errors = []
|
||||
|
||||
for path in files:
|
||||
if "selftest" not in path.name:
|
||||
continue
|
||||
|
||||
if len(path.parts) < 2 or path.parts[:2] != ("kernel", "selftest"):
|
||||
errors.append(f"{path}: kernel selftests belong under kernel/selftest/")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def expand_make_var_token(token: str, makefile: Path) -> str:
|
||||
return token.replace("$(ARCH_DIR)", "arch/x86").replace(
|
||||
"$(BUILD_DIR)", "build"
|
||||
)
|
||||
|
||||
|
||||
def parse_makefile_sources(root: Path, makefile: Path) -> set[Path]:
|
||||
text = read_text(root, makefile)
|
||||
lines = text.splitlines()
|
||||
sources = set()
|
||||
index = 0
|
||||
|
||||
while index < len(lines):
|
||||
line = lines[index]
|
||||
match = re.match(r"^([A-Z0-9_]+)\s*:=", line)
|
||||
if match == None or match.group(1) not in MAKEFILE_SOURCE_VARS:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
remainder = line.split(":=", 1)[1].strip()
|
||||
while True:
|
||||
continued = remainder.endswith("\\")
|
||||
remainder = remainder[:-1].strip() if continued else remainder
|
||||
if remainder:
|
||||
for token in remainder.split():
|
||||
if token.endswith(".c"):
|
||||
sources.add(Path(expand_make_var_token(token, makefile)))
|
||||
|
||||
if not continued:
|
||||
break
|
||||
|
||||
index += 1
|
||||
if index >= len(lines):
|
||||
break
|
||||
remainder = lines[index].strip()
|
||||
|
||||
index += 1
|
||||
|
||||
return sources
|
||||
|
||||
|
||||
def makefile_sources(root: Path) -> set[Path]:
|
||||
sources = set()
|
||||
|
||||
for makefile in sorted(root.rglob("Makefile")):
|
||||
if "build" in makefile.parts:
|
||||
continue
|
||||
sources.update(parse_makefile_sources(root, makefile.relative_to(root)))
|
||||
|
||||
return sources
|
||||
|
||||
|
||||
def check_makefile_source_lists(root: Path, files: set[Path], all_mode: bool) -> list[str]:
|
||||
errors = []
|
||||
listed = makefile_sources(root)
|
||||
existing = set(c_files(all_files(root)))
|
||||
|
||||
for path in sorted(listed):
|
||||
if not (root / path).exists():
|
||||
errors.append(f"{path}: listed in Makefile but file does not exist")
|
||||
|
||||
if all_mode:
|
||||
for path in sorted(existing - listed):
|
||||
errors.append(f"{path}: C source is not listed in a Makefile source list")
|
||||
else:
|
||||
for path in sorted(c_files(files)):
|
||||
if path not in listed:
|
||||
errors.append(
|
||||
f"{path}: changed C source is not listed in a Makefile source list"
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Check Tianole structure rules")
|
||||
mode = parser.add_mutually_exclusive_group()
|
||||
mode.add_argument("--all", action="store_true", help="check the whole tree")
|
||||
mode.add_argument(
|
||||
"--changed", action="store_true", help="check changed and untracked files"
|
||||
)
|
||||
return parser.parse_args()
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
root = repo_root()
|
||||
all_mode = not args.changed
|
||||
files = all_files(root) if all_mode else changed_files(root)
|
||||
|
||||
errors = []
|
||||
sources = source_files(files)
|
||||
errors.extend(check_no_relative_parent_includes(root, sources))
|
||||
errors.extend(check_no_bare_negative_errno(root, sources))
|
||||
errors.extend(check_scheduler_state_writes(root, sources))
|
||||
errors.extend(check_public_header_docs(root, public_headers(files)))
|
||||
errors.extend(check_no_relative_parent_includes(root, source_files(files)))
|
||||
errors.extend(check_selftests_are_centralized(c_files(files)))
|
||||
errors.extend(check_makefile_source_lists(root, files, all_mode))
|
||||
|
||||
|
||||
@ -1 +0,0 @@
|
||||
"""Structure checks for Tianole source layout."""
|
||||
@ -1,83 +0,0 @@
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SOURCE_ROOTS = ("arch", "include", "kernel", "mm")
|
||||
C_SOURCE_ROOTS = ("arch", "kernel", "mm")
|
||||
|
||||
|
||||
def repo_root() -> Path:
|
||||
return Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
def run_git(root: Path, args: list[str]) -> list[str]:
|
||||
result = subprocess.run(
|
||||
["git", *args],
|
||||
cwd=root,
|
||||
check=True,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
return [line for line in result.stdout.splitlines() if line]
|
||||
|
||||
|
||||
def changed_files(root: Path) -> set[Path]:
|
||||
files = set()
|
||||
|
||||
for path in run_git(root, ["diff", "--name-only", "--diff-filter=ACMR"]):
|
||||
files.add(Path(path))
|
||||
|
||||
for path in run_git(
|
||||
root, ["diff", "--cached", "--name-only", "--diff-filter=ACMR"]
|
||||
):
|
||||
files.add(Path(path))
|
||||
|
||||
for path in run_git(root, ["ls-files", "--others", "--exclude-standard"]):
|
||||
files.add(Path(path))
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def all_files(root: Path) -> set[Path]:
|
||||
files = set()
|
||||
|
||||
for base in SOURCE_ROOTS:
|
||||
root_dir = root / base
|
||||
if not root_dir.exists():
|
||||
continue
|
||||
|
||||
for path in root_dir.rglob("*"):
|
||||
if path.is_file():
|
||||
files.add(path.relative_to(root))
|
||||
|
||||
for path in (root / "scripts").rglob("*"):
|
||||
if path.is_file():
|
||||
files.add(path.relative_to(root))
|
||||
|
||||
return files
|
||||
|
||||
|
||||
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
|
||||
for path in files
|
||||
if path.suffix == ".c" and path.parts and path.parts[0] in C_SOURCE_ROOTS
|
||||
)
|
||||
|
||||
|
||||
def read_text(root: Path, path: Path) -> str:
|
||||
return (root / path).read_text(encoding="utf-8")
|
||||
@ -1,87 +0,0 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from .common import all_files, c_files, read_text
|
||||
|
||||
|
||||
MAKEFILE_SOURCE_VARS = (
|
||||
"ARCH_KERNEL_SRCS",
|
||||
"ARCH_MM_SRCS",
|
||||
"BOOT_SRCS",
|
||||
"KERNEL_SRCS",
|
||||
"MM_SRCS",
|
||||
)
|
||||
|
||||
|
||||
def expand_make_var_token(token: str, makefile: Path) -> str:
|
||||
return token.replace("$(ARCH_DIR)", "arch/x86").replace(
|
||||
"$(BUILD_DIR)", "build"
|
||||
)
|
||||
|
||||
|
||||
def parse_makefile_sources(root: Path, makefile: Path) -> set[Path]:
|
||||
text = read_text(root, makefile)
|
||||
lines = text.splitlines()
|
||||
sources = set()
|
||||
index = 0
|
||||
|
||||
while index < len(lines):
|
||||
line = lines[index]
|
||||
match = re.match(r"^([A-Z0-9_]+)\s*:=", line)
|
||||
if match == None or match.group(1) not in MAKEFILE_SOURCE_VARS:
|
||||
index += 1
|
||||
continue
|
||||
|
||||
remainder = line.split(":=", 1)[1].strip()
|
||||
while True:
|
||||
continued = remainder.endswith("\\")
|
||||
remainder = remainder[:-1].strip() if continued else remainder
|
||||
if remainder:
|
||||
for token in remainder.split():
|
||||
if token.endswith(".c"):
|
||||
sources.add(Path(expand_make_var_token(token, makefile)))
|
||||
|
||||
if not continued:
|
||||
break
|
||||
|
||||
index += 1
|
||||
if index >= len(lines):
|
||||
break
|
||||
remainder = lines[index].strip()
|
||||
|
||||
index += 1
|
||||
|
||||
return sources
|
||||
|
||||
|
||||
def makefile_sources(root: Path) -> set[Path]:
|
||||
sources = set()
|
||||
|
||||
for makefile in sorted(root.rglob("Makefile")):
|
||||
if "build" in makefile.parts:
|
||||
continue
|
||||
sources.update(parse_makefile_sources(root, makefile.relative_to(root)))
|
||||
|
||||
return sources
|
||||
|
||||
|
||||
def check_makefile_source_lists(root: Path, files: set[Path], all_mode: bool) -> list[str]:
|
||||
errors = []
|
||||
listed = makefile_sources(root)
|
||||
existing = set(c_files(all_files(root)))
|
||||
|
||||
for path in sorted(listed):
|
||||
if not (root / path).exists():
|
||||
errors.append(f"{path}: listed in Makefile but file does not exist")
|
||||
|
||||
if all_mode:
|
||||
for path in sorted(existing - listed):
|
||||
errors.append(f"{path}: C source is not listed in a Makefile source list")
|
||||
else:
|
||||
for path in sorted(c_files(files)):
|
||||
if path not in listed:
|
||||
errors.append(
|
||||
f"{path}: changed C source is not listed in a Makefile source list"
|
||||
)
|
||||
|
||||
return errors
|
||||
@ -1,89 +0,0 @@
|
||||
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 is_public_type_start(line: str) -> bool:
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.startswith(("struct ", "enum ", "union ")):
|
||||
return stripped.endswith("{") or "{" in stripped
|
||||
|
||||
if not stripped.startswith("typedef "):
|
||||
return False
|
||||
|
||||
return "(" not in stripped
|
||||
|
||||
|
||||
def collect_public_type_declarations(lines: list[str]) -> list[tuple[int, int, str]]:
|
||||
decls = []
|
||||
index = 0
|
||||
|
||||
while index < len(lines):
|
||||
if not is_public_type_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 symbol_name_from_type_declaration(text: str) -> str:
|
||||
if text.startswith("typedef "):
|
||||
return text.rsplit("}", 1)[-1].strip().rstrip(";").split()[-1]
|
||||
|
||||
parts = text.split()
|
||||
if len(parts) >= 2:
|
||||
return parts[1]
|
||||
|
||||
return "<unknown>"
|
||||
@ -1,114 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
from .common import read_text
|
||||
from .public_header_decls import (
|
||||
collect_function_declarations,
|
||||
collect_public_type_declarations,
|
||||
symbol_name_from_type_declaration,
|
||||
)
|
||||
|
||||
|
||||
def is_documented_public_define(lines: list[str], index: int, guard: str | None) -> bool:
|
||||
stripped = lines[index].strip()
|
||||
|
||||
if not stripped.startswith("#define "):
|
||||
return False
|
||||
|
||||
parts = stripped.split()
|
||||
if len(parts) < 2:
|
||||
return False
|
||||
|
||||
name = parts[1].split("(", 1)[0]
|
||||
if name == guard:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def header_guard_name(lines: list[str]) -> str | None:
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("#define "):
|
||||
parts = stripped.split()
|
||||
if len(parts) >= 2:
|
||||
return parts[1]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
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_kernel_doc_block(
|
||||
errors: list[str], path: Path, lines: list[str], start: int, kind: str, name: str
|
||||
) -> None:
|
||||
line_no = start + 1
|
||||
|
||||
if not has_kernel_doc_before(lines, start):
|
||||
errors.append(
|
||||
f"{path}:{line_no}: public {kind} '{name}' needs "
|
||||
"a kernel-doc comment immediately before it"
|
||||
)
|
||||
return
|
||||
|
||||
if not has_blank_line_before_doc(lines, start):
|
||||
errors.append(
|
||||
f"{path}:{line_no}: public {kind} '{name}' "
|
||||
"comment block must be separated by a blank line"
|
||||
)
|
||||
|
||||
|
||||
def check_public_header_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):
|
||||
name = text.split("(", 1)[0].split()[-1].lstrip("*")
|
||||
check_kernel_doc_block(errors, path, lines, start, "function", name)
|
||||
|
||||
for start, _end, text in collect_public_type_declarations(lines):
|
||||
name = symbol_name_from_type_declaration(text)
|
||||
check_kernel_doc_block(errors, path, lines, start, "type", name)
|
||||
|
||||
guard = header_guard_name(lines)
|
||||
for index, line in enumerate(lines):
|
||||
if not is_documented_public_define(lines, index, guard):
|
||||
continue
|
||||
|
||||
name = line.strip().split()[1].split("(", 1)[0]
|
||||
check_kernel_doc_block(errors, path, lines, index, "macro", name)
|
||||
|
||||
return errors
|
||||
@ -1,66 +0,0 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from .common import read_text
|
||||
|
||||
|
||||
def check_no_relative_parent_includes(root: Path, files: list[Path]) -> list[str]:
|
||||
errors = []
|
||||
include_re = re.compile(r'^\s*#include\s+[<"]\.\./')
|
||||
|
||||
for path in files:
|
||||
for line_no, line in enumerate(read_text(root, path).splitlines(), 1):
|
||||
if include_re.search(line):
|
||||
errors.append(
|
||||
f"{path}:{line_no}: do not use ../ includes; "
|
||||
"promote shared headers or add a private include root"
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def check_no_bare_negative_errno(root: Path, files: list[Path]) -> list[str]:
|
||||
errors = []
|
||||
return_re = re.compile(r"\breturn\s+-[0-9]+\s*;")
|
||||
|
||||
for path in files:
|
||||
for line_no, line in enumerate(read_text(root, path).splitlines(), 1):
|
||||
if return_re.search(line):
|
||||
errors.append(
|
||||
f"{path}:{line_no}: return a symbolic negative errno "
|
||||
"such as -EINVAL instead of a bare negative number"
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def check_scheduler_state_writes(root: Path, files: list[Path]) -> list[str]:
|
||||
errors = []
|
||||
state_write_re = re.compile(r"->state\s*=")
|
||||
allowed_path = Path("kernel/sched/sched.h")
|
||||
|
||||
for path in files:
|
||||
if path == allowed_path:
|
||||
continue
|
||||
|
||||
for line_no, line in enumerate(read_text(root, path).splitlines(), 1):
|
||||
if state_write_re.search(line):
|
||||
errors.append(
|
||||
f"{path}:{line_no}: thread state writes must use "
|
||||
"kernel/sched/sched.h helpers"
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def check_selftests_are_centralized(files: list[Path]) -> list[str]:
|
||||
errors = []
|
||||
|
||||
for path in files:
|
||||
if "selftest" not in path.name:
|
||||
continue
|
||||
|
||||
if len(path.parts) < 2 or path.parts[:2] != ("kernel", "selftest"):
|
||||
errors.append(f"{path}: kernel selftests belong under kernel/selftest/")
|
||||
|
||||
return errors
|
||||
Loading…
Reference in New Issue
Block a user