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
|
$(OVMF_VARS): | dirs
|
||||||
cp $(OVMF_VARS_TEMPLATE) $@
|
cp $(OVMF_VARS_TEMPLATE) $@
|
||||||
|
|
||||||
run: $(BOOT_EFI) $(KERNEL_ELF) $(OVMF_VARS)
|
run: $(BOOT_EFI) $(OVMF_VARS)
|
||||||
qemu-system-x86_64 \
|
qemu-system-x86_64 \
|
||||||
-drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \
|
-drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \
|
||||||
-drive if=pflash,format=raw,file=$(OVMF_VARS) \
|
-drive if=pflash,format=raw,file=$(OVMF_VARS) \
|
||||||
-drive format=raw,file=fat:rw:$(BUILD_DIR)/image \
|
-drive format=raw,file=fat:rw:$(BUILD_DIR)/image \
|
||||||
-serial stdio
|
-serial stdio
|
||||||
|
|
||||||
run-headless: $(BOOT_EFI) $(KERNEL_ELF) $(OVMF_VARS)
|
run-headless: $(BOOT_EFI) $(OVMF_VARS)
|
||||||
rm -f $(DEBUG_LOG) $(SERIAL_LOG)
|
rm -f $(DEBUG_LOG) $(SERIAL_LOG)
|
||||||
qemu-system-x86_64 \
|
qemu-system-x86_64 \
|
||||||
-display none \
|
-display none \
|
||||||
|
|||||||
@ -2,7 +2,6 @@ BOOT_SRCS := \
|
|||||||
$(ARCH_DIR)/boot/main.c \
|
$(ARCH_DIR)/boot/main.c \
|
||||||
$(ARCH_DIR)/boot/file.c \
|
$(ARCH_DIR)/boot/file.c \
|
||||||
$(ARCH_DIR)/boot/elf_loader.c \
|
$(ARCH_DIR)/boot/elf_loader.c \
|
||||||
$(ARCH_DIR)/boot/framebuffer.c \
|
|
||||||
$(ARCH_DIR)/boot/memory_map.c \
|
$(ARCH_DIR)/boot/memory_map.c \
|
||||||
$(ARCH_DIR)/boot/support.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 "efi.h"
|
||||||
#include "elf_loader.h"
|
#include "elf_loader.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "framebuffer.h"
|
|
||||||
#include "memory_map.h"
|
#include "memory_map.h"
|
||||||
|
|
||||||
static efi_char16_t boot_banner_text[] = u"Tianole x86 bootloader.\r\n";
|
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->output_string(
|
||||||
system_table->con_out, boot_banner_text);
|
system_table->con_out, boot_banner_text);
|
||||||
boot_debug_log_puts("Tianole x86 bootloader loaded.\n");
|
boot_debug_log_puts("Tianole x86 bootloader loaded.\n");
|
||||||
(void)boot_capture_framebuffer(system_table, &boot_info);
|
|
||||||
|
|
||||||
status = boot_read_file(image_handle,
|
status = boot_read_file(image_handle,
|
||||||
system_table,
|
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_A 0x5b1b31a1
|
||||||
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_B 0x9562
|
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_B 0x9562
|
||||||
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_C 0x11d2
|
#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__)
|
#if defined(__x86_64__)
|
||||||
#define EFIAPI __attribute__((ms_abi))
|
#define EFIAPI __attribute__((ms_abi))
|
||||||
@ -61,7 +58,6 @@ typedef struct efi_simple_file_system_protocol
|
|||||||
efi_simple_file_system_protocol_t;
|
efi_simple_file_system_protocol_t;
|
||||||
typedef struct efi_file_protocol efi_file_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_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;
|
typedef struct efi_system_table efi_system_table_t;
|
||||||
|
|
||||||
struct efi_simple_text_output_protocol {
|
struct efi_simple_text_output_protocol {
|
||||||
@ -126,31 +122,6 @@ struct efi_loaded_image_protocol {
|
|||||||
void *unload;
|
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 {
|
struct efi_boot_services {
|
||||||
efi_table_header_t hdr;
|
efi_table_header_t hdr;
|
||||||
void *raise_tpl;
|
void *raise_tpl;
|
||||||
@ -193,15 +164,6 @@ struct efi_boot_services {
|
|||||||
void *get_next_monotonic_count;
|
void *get_next_monotonic_count;
|
||||||
void(EFIAPI *stall)(efi_uintn_t microseconds);
|
void(EFIAPI *stall)(efi_uintn_t microseconds);
|
||||||
void *set_watchdog_timer;
|
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 {
|
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
|
#endif
|
||||||
|
|||||||
@ -3,8 +3,6 @@ ARCH_KERNEL_SRCS := \
|
|||||||
$(ARCH_DIR)/kernel/gdt.c \
|
$(ARCH_DIR)/kernel/gdt.c \
|
||||||
$(ARCH_DIR)/kernel/idt.c \
|
$(ARCH_DIR)/kernel/idt.c \
|
||||||
$(ARCH_DIR)/kernel/irq.c \
|
$(ARCH_DIR)/kernel/irq.c \
|
||||||
$(ARCH_DIR)/kernel/screen.c \
|
|
||||||
$(ARCH_DIR)/kernel/screen_font.c \
|
|
||||||
$(ARCH_DIR)/kernel/traps.c
|
$(ARCH_DIR)/kernel/traps.c
|
||||||
|
|
||||||
ARCH_KERNEL_OBJS := \
|
ARCH_KERNEL_OBJS := \
|
||||||
|
|||||||
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
#include <tianole/arch.h>
|
#include <tianole/arch.h>
|
||||||
|
|
||||||
#include "screen.h"
|
|
||||||
|
|
||||||
#define X86_QEMU_DEBUG_PORT 0xe9
|
#define X86_QEMU_DEBUG_PORT 0xe9
|
||||||
|
|
||||||
#define X86_COM1_BASE 0x3f8
|
#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);
|
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_INTERRUPT_ENABLE, 0x00);
|
||||||
outb(X86_COM1_BASE + X86_COM_LINE_CONTROL, X86_COM_LCR_DLAB);
|
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_LINE_CONTROL, X86_COM_LCR_8N1);
|
||||||
outb(X86_COM1_BASE + X86_COM_FIFO_CONTROL, 0xc7);
|
outb(X86_COM1_BASE + X86_COM_FIFO_CONTROL, 0xc7);
|
||||||
outb(X86_COM1_BASE + X86_COM_MODEM_CONTROL, 0x0b);
|
outb(X86_COM1_BASE + X86_COM_MODEM_CONTROL, 0x0b);
|
||||||
screen_console_init(boot_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_early_log_putc(char ch)
|
void arch_early_log_putc(char ch)
|
||||||
{
|
{
|
||||||
debug_port_putc(ch);
|
debug_port_putc(ch);
|
||||||
serial_putc(ch);
|
serial_putc(ch);
|
||||||
screen_console_putc(ch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_halt_forever(void)
|
void arch_halt_forever(void)
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include <tianole/arch.h>
|
#include <tianole/arch.h>
|
||||||
#include <tianole/early_log.h>
|
#include <tianole/early_log.h>
|
||||||
#include <tianole/errno.h>
|
|
||||||
#include <tianole/irq.h>
|
#include <tianole/irq.h>
|
||||||
#include <tianole/timer.h>
|
#include <tianole/timer.h>
|
||||||
|
|
||||||
@ -103,14 +102,14 @@ int irq_register(uint8_t irq, irq_handler_t handler, void *data)
|
|||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
if (irq >= IRQ_COUNT || handler == 0) {
|
if (irq >= IRQ_COUNT || handler == 0) {
|
||||||
return -EINVAL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
flags = arch_irq_save();
|
flags = arch_irq_save();
|
||||||
|
|
||||||
if (irq_actions[irq].handler != 0) {
|
if (irq_actions[irq].handler != 0) {
|
||||||
arch_irq_restore(flags);
|
arch_irq_restore(flags);
|
||||||
return -EBUSY;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq_actions[irq].handler = handler;
|
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/arch.h>
|
||||||
#include <tianole/early_log.h>
|
#include <tianole/early_log.h>
|
||||||
#include <tianole/errno.h>
|
|
||||||
#include <tianole/mm.h>
|
#include <tianole/mm.h>
|
||||||
|
|
||||||
#include "page_table.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();
|
page = alloc_page();
|
||||||
if (page == 0) {
|
if (page == 0) {
|
||||||
return -ENOMEM;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_page(page);
|
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);
|
uint64_t pt_index = table_index(virt, 12);
|
||||||
|
|
||||||
if (create != 0) {
|
if (create != 0) {
|
||||||
int ret = ensure_next_table(pml4, pml4_index, &pdpt);
|
if (ensure_next_table(pml4, pml4_index, &pdpt) != 0 ||
|
||||||
|
ensure_next_table(pdpt, pdpt_index, &pd) != 0 ||
|
||||||
if (ret != 0) {
|
ensure_next_table(pd, pd_index, &pt) != 0) {
|
||||||
return ret;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((pml4[pml4_index] & PAGE_PRESENT) == 0) {
|
if ((pml4[pml4_index] & PAGE_PRESENT) == 0) {
|
||||||
return -ENOENT;
|
return -1;
|
||||||
}
|
}
|
||||||
pdpt = entry_table(pml4[pml4_index]);
|
pdpt = entry_table(pml4[pml4_index]);
|
||||||
|
|
||||||
if ((pdpt[pdpt_index] & PAGE_PRESENT) == 0) {
|
if ((pdpt[pdpt_index] & PAGE_PRESENT) == 0) {
|
||||||
return -ENOENT;
|
return -1;
|
||||||
}
|
}
|
||||||
pd = entry_table(pdpt[pdpt_index]);
|
pd = entry_table(pdpt[pdpt_index]);
|
||||||
|
|
||||||
if ((pd[pd_index] & PAGE_PRESENT) == 0) {
|
if ((pd[pd_index] & PAGE_PRESENT) == 0) {
|
||||||
return -ENOENT;
|
return -1;
|
||||||
}
|
}
|
||||||
pt = entry_table(pd[pd_index]);
|
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)
|
int map_page(virt_addr_t virt, phys_addr_t phys, uint64_t flags)
|
||||||
{
|
{
|
||||||
uint64_t *entry;
|
uint64_t *entry;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if ((virt & (PAGE_SIZE - 1)) != 0 || (phys & (PAGE_SIZE - 1)) != 0) {
|
if ((virt & (PAGE_SIZE - 1)) != 0 || (phys & (PAGE_SIZE - 1)) != 0) {
|
||||||
return -EINVAL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = page_entry(virt, 1, &entry);
|
if (page_entry(virt, 1, &entry) != 0) {
|
||||||
if (ret != 0) {
|
return -1;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*entry & PAGE_PRESENT) != 0) {
|
if ((*entry & PAGE_PRESENT) != 0) {
|
||||||
return -EEXIST;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*entry = (phys & PAGE_MASK) | flags | PAGE_PRESENT;
|
*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)
|
int unmap_page(virt_addr_t virt)
|
||||||
{
|
{
|
||||||
uint64_t *entry;
|
uint64_t *entry;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if ((virt & (PAGE_SIZE - 1)) != 0) {
|
if ((virt & (PAGE_SIZE - 1)) != 0) {
|
||||||
return -EINVAL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = page_entry(virt, 0, &entry);
|
if (page_entry(virt, 0, &entry) != 0 || (*entry & PAGE_PRESENT) == 0) {
|
||||||
if (ret != 0 || (*entry & PAGE_PRESENT) == 0) {
|
return -1;
|
||||||
return -ENOENT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*entry = 0;
|
*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)
|
int virt_to_phys(virt_addr_t virt, phys_addr_t *phys)
|
||||||
{
|
{
|
||||||
uint64_t *entry;
|
uint64_t *entry;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (phys == 0) {
|
if (phys == 0 || page_entry(virt, 0, &entry) != 0 ||
|
||||||
return -EINVAL;
|
(*entry & PAGE_PRESENT) == 0) {
|
||||||
}
|
return -1;
|
||||||
|
|
||||||
ret = page_entry(virt, 0, &entry);
|
|
||||||
if (ret != 0 || (*entry & PAGE_PRESENT) == 0) {
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*phys = (*entry & PAGE_MASK) | (virt & (PAGE_SIZE - 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 格式
|
## C 格式
|
||||||
|
|
||||||
@ -75,10 +67,7 @@
|
|||||||
- 可独立执行的检查放在 `scripts/checks/`。
|
- 可独立执行的检查放在 `scripts/checks/`。
|
||||||
- 多个检查共享的函数放在 `scripts/lib/`。
|
- 多个检查共享的函数放在 `scripts/lib/`。
|
||||||
- 不把所有检查逻辑持续堆进 `scripts/check.sh`。
|
- 不把所有检查逻辑持续堆进 `scripts/check.sh`。
|
||||||
- 项目结构规则由 `scripts/tools/check_structure.py` 编排,具体规则拆到
|
- 项目结构规则放在 `scripts/tools/check_structure.py`,由 `scripts/checks/structure.sh` 调用;优先用宿主 Linux/LLVM 工具实现底层检查,把 Tianole 自己的目录和 include 约束固化在轻量 Python 工具里。
|
||||||
`scripts/tools/structure_checks/`;不要把 Python 工具堆成单个超大文件。
|
|
||||||
- 优先用宿主 Linux/LLVM 工具实现底层检查,把 Tianole 自己的目录和 include
|
|
||||||
约束固化在轻量 Python 工具里。
|
|
||||||
- 启动阶段内核自测集中放在 `kernel/selftest/`,不要散落在具体实现目录里。
|
- 启动阶段内核自测集中放在 `kernel/selftest/`,不要散落在具体实现目录里。
|
||||||
|
|
||||||
## 验证
|
## 验证
|
||||||
@ -86,20 +75,3 @@
|
|||||||
- 新增或大改 C/H 文件后运行 `clang-format`。
|
- 新增或大改 C/H 文件后运行 `clang-format`。
|
||||||
- 提交前至少运行 `scripts/check.sh`。
|
- 提交前至少运行 `scripts/check.sh`。
|
||||||
- 如果只是文档修改,至少运行 `git diff --check`。
|
- 如果只是文档修改,至少运行 `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。
|
- kernel early log 已拆成通用前端和 x86 backend。
|
||||||
- x86 backend 已同时写 QEMU debug port 和 COM1。
|
- x86 backend 已同时写 QEMU debug port 和 COM1。
|
||||||
- x86 backend 已可在 UEFI GOP framebuffer 上显示 early log,QEMU 图形窗口可直接观察启动日志。
|
|
||||||
- `panic()` 已接入 early log 和 `arch_halt_forever()`。
|
- `panic()` 已接入 early log 和 `arch_halt_forever()`。
|
||||||
- `scripts/check.sh` 已验证 `build/debug.log` 和 `build/serial.log` 中的关键启动行。
|
- `scripts/check.sh` 已验证 `build/debug.log` 和 `build/serial.log` 中的关键启动行。
|
||||||
|
|
||||||
|
|||||||
@ -152,11 +152,9 @@
|
|||||||
- 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。
|
- 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。
|
||||||
- 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。
|
- 已建立 `sched_irq_exit()`,timer IRQ 只设置 `need_resched`,trap 的 IRQ 返回边界统一消费调度请求。
|
||||||
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
||||||
- 已建立统一 `kernel_thread_exit()`/`sched_thread_exit()`,线程入口返回和显式退出都会进入明确退出路径,再由调度安全边界回收非当前 DEAD 线程。
|
|
||||||
- 已在调度私有头中加入 thread state helper,调度核心、线程退出和 wait queue 路径不再直接散写主要状态转换。
|
- 已在调度私有头中加入 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`。
|
- 已把调度代码按职责拆分为 `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 锁语义
|
### B. wait queue 锁语义
|
||||||
|
|
||||||
- 已为 wait queue 增加内部锁,并提供显式 locked wakeup 接口;后续继续扩大调用方按条件锁规则更新条件的覆盖面。
|
- 为 wait queue 增加内部锁或要求调用方持有指定锁,并在接口命名中体现约束。
|
||||||
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队、wakeup 队列修改,以及 demo 中的条件修改 + wakeup。
|
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队和 wakeup 队列修改。
|
||||||
- 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。
|
- 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。
|
||||||
- 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。
|
- 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。
|
||||||
|
|
||||||
@ -183,7 +181,7 @@
|
|||||||
### D. thread lifecycle
|
### D. thread lifecycle
|
||||||
|
|
||||||
- 为线程退出增加更完整的生命周期状态、引用规则和最终释放约束。
|
- 为线程退出增加更完整的生命周期状态、引用规则和最终释放约束。
|
||||||
- 继续补充“当前线程不能释放自身内核栈”的更严格断言和未来 join/wait 语义。
|
- 补充“当前线程不能释放自身内核栈”的文档和自测。
|
||||||
- 为未来 `kthread_stop()`、join/wait 和进程退出保留接口空间。
|
- 为未来 `kthread_stop()`、join/wait 和进程退出保留接口空间。
|
||||||
- 回收路径需要覆盖等待队列残留、run queue 残留和 timer sleep 残留。
|
- 回收路径需要覆盖等待队列残留、run queue 残留和 timer sleep 残留。
|
||||||
|
|
||||||
|
|||||||
@ -3,66 +3,13 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <tianole/boot_info.h>
|
void arch_early_log_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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_putc(char ch);
|
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));
|
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);
|
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);
|
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);
|
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);
|
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);
|
void arch_timer_init(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,26 +3,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#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
|
#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 {
|
typedef struct {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t pad;
|
uint32_t pad;
|
||||||
@ -32,25 +14,10 @@ typedef struct {
|
|||||||
uint64_t attribute;
|
uint64_t attribute;
|
||||||
} boot_memory_descriptor_t;
|
} boot_memory_descriptor_t;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* typedef boot_info_t - Bootloader-to-kernel handoff data.
|
* Boot-time handoff data owned by Tianole rather than by a specific
|
||||||
* @version: Handoff structure version.
|
* firmware or architecture API. Fields can grow over time while the
|
||||||
* @boot_flags: Flags describing the firmware handoff state.
|
* kernel entry stays stable.
|
||||||
* @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.
|
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
@ -61,22 +28,9 @@ typedef struct {
|
|||||||
uint64_t memory_descriptor_size;
|
uint64_t memory_descriptor_size;
|
||||||
uint32_t memory_descriptor_version;
|
uint32_t memory_descriptor_version;
|
||||||
uint32_t reserved0;
|
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_t;
|
||||||
|
|
||||||
/**
|
#define BOOT_INFO_VERSION 1u
|
||||||
* 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_FLAG_SERVICES_ACTIVE (1u << 0)
|
#define BOOT_FLAG_SERVICES_ACTIVE (1u << 0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,56 +3,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <tianole/boot_info.h>
|
void early_log_init(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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_putc(char ch);
|
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);
|
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);
|
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);
|
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));
|
void panic(const char *message) __attribute__((noreturn));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,61 +3,16 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/**
|
|
||||||
* ELF_MAGIC - ELF file magic value in little-endian word form.
|
|
||||||
*/
|
|
||||||
#define ELF_MAGIC 0x464c457fu
|
#define ELF_MAGIC 0x464c457fu
|
||||||
|
|
||||||
/**
|
|
||||||
* ELFCLASS64 - ELF identification value for 64-bit objects.
|
|
||||||
*/
|
|
||||||
#define ELFCLASS64 2
|
#define ELFCLASS64 2
|
||||||
|
|
||||||
/**
|
|
||||||
* ELFDATA2LSB - ELF identification value for little-endian objects.
|
|
||||||
*/
|
|
||||||
#define ELFDATA2LSB 1
|
#define ELFDATA2LSB 1
|
||||||
|
|
||||||
/**
|
|
||||||
* EV_CURRENT - Current ELF object format version.
|
|
||||||
*/
|
|
||||||
#define EV_CURRENT 1
|
#define EV_CURRENT 1
|
||||||
|
|
||||||
/**
|
|
||||||
* ET_EXEC - ELF object type for executable files.
|
|
||||||
*/
|
|
||||||
#define ET_EXEC 2
|
#define ET_EXEC 2
|
||||||
|
|
||||||
/**
|
|
||||||
* EM_X86_64 - ELF machine type for x86_64.
|
|
||||||
*/
|
|
||||||
#define EM_X86_64 62
|
#define EM_X86_64 62
|
||||||
|
|
||||||
/**
|
|
||||||
* PT_LOAD - ELF program header type for loadable segments.
|
|
||||||
*/
|
|
||||||
#define PT_LOAD 1
|
#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 {
|
typedef struct {
|
||||||
unsigned char ident[16];
|
unsigned char ident[16];
|
||||||
uint16_t type;
|
uint16_t type;
|
||||||
@ -75,20 +30,6 @@ typedef struct {
|
|||||||
uint16_t shstrndx;
|
uint16_t shstrndx;
|
||||||
} elf64_ehdr_t;
|
} 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 {
|
typedef struct {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t flags;
|
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>
|
#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);
|
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);
|
int irq_register(uint8_t irq, irq_handler_t handler, void *data);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,12 +3,6 @@
|
|||||||
|
|
||||||
#include <tianole/boot_info.h>
|
#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);
|
void kernel_report_boot_state(const boot_info_t *boot_info);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -6,118 +6,24 @@
|
|||||||
|
|
||||||
#include <tianole/boot_info.h>
|
#include <tianole/boot_info.h>
|
||||||
|
|
||||||
/**
|
|
||||||
* PAGE_SIZE - Base page size used by the current memory manager.
|
|
||||||
*/
|
|
||||||
#define PAGE_SIZE 4096u
|
#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 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;
|
typedef uint64_t virt_addr_t;
|
||||||
|
|
||||||
/**
|
|
||||||
* PAGE_PRESENT - Mapping flag that marks a page table entry present.
|
|
||||||
*/
|
|
||||||
#define PAGE_PRESENT (1ull << 0)
|
#define PAGE_PRESENT (1ull << 0)
|
||||||
|
|
||||||
/**
|
|
||||||
* PAGE_WRITABLE - Mapping flag that allows writes through a page mapping.
|
|
||||||
*/
|
|
||||||
#define PAGE_WRITABLE (1ull << 1)
|
#define PAGE_WRITABLE (1ull << 1)
|
||||||
|
|
||||||
/**
|
|
||||||
* PAGE_NO_EXECUTE - Mapping flag that blocks instruction fetches.
|
|
||||||
*/
|
|
||||||
#define PAGE_NO_EXECUTE (1ull << 63)
|
#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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
void page_table_selftest(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -6,31 +6,9 @@
|
|||||||
|
|
||||||
#include <tianole/spinlock.h>
|
#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 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);
|
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 {
|
enum thread_state {
|
||||||
THREAD_READY,
|
THREAD_READY,
|
||||||
THREAD_RUNNING,
|
THREAD_RUNNING,
|
||||||
@ -39,41 +17,12 @@ enum thread_state {
|
|||||||
THREAD_DEAD,
|
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 wait_queue {
|
||||||
struct spinlock lock;
|
struct spinlock lock;
|
||||||
struct thread *head;
|
struct thread *head;
|
||||||
struct thread *tail;
|
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 {
|
struct thread {
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
enum thread_state state;
|
enum thread_state state;
|
||||||
@ -86,172 +35,26 @@ struct thread {
|
|||||||
uint64_t wake_tick;
|
uint64_t wake_tick;
|
||||||
struct thread *next;
|
struct thread *next;
|
||||||
struct thread *wait_next;
|
struct thread *wait_next;
|
||||||
struct wait_queue *wait_queue;
|
|
||||||
char name[32];
|
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);
|
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(
|
struct thread *kernel_thread_create(
|
||||||
const char *name, kernel_thread_entry_t entry, void *arg);
|
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));
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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(
|
int wait_queue_wait(
|
||||||
struct wait_queue *queue, wait_condition_t condition, void *arg);
|
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,
|
int wait_queue_wait_timeout(struct wait_queue *queue,
|
||||||
wait_condition_t condition,
|
wait_condition_t condition,
|
||||||
void *arg,
|
void *arg,
|
||||||
uint64_t ticks);
|
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);
|
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);
|
void wait_queue_wake_all(struct wait_queue *queue);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,42 +3,16 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#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 {
|
struct spinlock {
|
||||||
int locked;
|
int locked;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* SPINLOCK_INITIALIZER - Static initializer for struct spinlock.
|
|
||||||
*/
|
|
||||||
#define SPINLOCK_INITIALIZER \
|
#define SPINLOCK_INITIALIZER \
|
||||||
{ \
|
{ \
|
||||||
0 \
|
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);
|
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);
|
void spin_unlock_irqrestore(struct spinlock *lock, uint64_t flags);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,18 +3,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#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);
|
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);
|
uint64_t timer_ticks(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -5,20 +5,20 @@
|
|||||||
|
|
||||||
static int early_log_ready;
|
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) {
|
if (early_log_ready != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
arch_early_log_init(boot_info);
|
arch_early_log_init();
|
||||||
early_log_ready = 1;
|
early_log_ready = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void early_log_putc(char ch)
|
void early_log_putc(char ch)
|
||||||
{
|
{
|
||||||
if (early_log_ready == 0) {
|
if (early_log_ready == 0) {
|
||||||
early_log_init(0);
|
early_log_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
arch_early_log_putc(ch);
|
arch_early_log_putc(ch);
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
void kernel_main(const boot_info_t *boot_info)
|
void kernel_main(const boot_info_t *boot_info)
|
||||||
{
|
{
|
||||||
early_log_init(boot_info);
|
early_log_init();
|
||||||
early_log_puts("kernel_main entered\n");
|
early_log_puts("kernel_main entered\n");
|
||||||
arch_traps_init();
|
arch_traps_init();
|
||||||
kernel_report_boot_state(boot_info);
|
kernel_report_boot_state(boot_info);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#include <tianole/errno.h>
|
|
||||||
#include <tianole/sched.h>
|
#include <tianole/sched.h>
|
||||||
|
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
@ -16,7 +15,7 @@ int sched_idle_create(void)
|
|||||||
{
|
{
|
||||||
idle_thread = kernel_thread_create("idle", idle_thread_entry, 0);
|
idle_thread = kernel_thread_create("idle", idle_thread_entry, 0);
|
||||||
if (idle_thread == 0) {
|
if (idle_thread == 0) {
|
||||||
return -ENOMEM;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <tianole/early_log.h>
|
|
||||||
#include <tianole/sched.h>
|
#include <tianole/sched.h>
|
||||||
#include <tianole/spinlock.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;
|
return thread != 0 && thread->state == THREAD_DEAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int thread_state_transition_is_valid(
|
static inline void thread_set_ready(struct thread *thread)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
thread->wake_tick = 0;
|
thread->wake_tick = 0;
|
||||||
thread->state = THREAD_READY;
|
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)
|
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(
|
static inline void thread_set_sleeping(
|
||||||
struct thread *thread, uint64_t wake_tick)
|
struct thread *thread, uint64_t wake_tick)
|
||||||
{
|
{
|
||||||
thread_validate_state_transition(thread, THREAD_SLEEPING);
|
|
||||||
thread->wake_tick = wake_tick;
|
thread->wake_tick = wake_tick;
|
||||||
thread->state = THREAD_SLEEPING;
|
thread->state = THREAD_SLEEPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void thread_set_waiting(struct thread *thread)
|
static inline void thread_set_waiting(struct thread *thread)
|
||||||
{
|
{
|
||||||
thread_set_state(thread, THREAD_WAITING);
|
|
||||||
thread->wake_tick = 0;
|
thread->wake_tick = 0;
|
||||||
|
thread->state = THREAD_WAITING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void thread_set_dead(struct thread *thread)
|
static inline void thread_set_dead(struct thread *thread)
|
||||||
{
|
{
|
||||||
thread_set_state(thread, THREAD_DEAD);
|
|
||||||
thread->wake_tick = 0;
|
thread->wake_tick = 0;
|
||||||
|
thread->state = THREAD_DEAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
void enqueue_thread(struct thread *thread);
|
void enqueue_thread(struct thread *thread);
|
||||||
void sched_reap_dead_threads(void);
|
void sched_reap_dead_threads(void);
|
||||||
void sched_thread_exit(void) __attribute__((noreturn));
|
|
||||||
void sched_selftest(void);
|
void sched_selftest(void);
|
||||||
int sched_idle_create(void);
|
int sched_idle_create(void);
|
||||||
void sched_demo_start(void) __attribute__((noreturn));
|
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;
|
stack_top = (uintptr_t)thread->stack_base + KERNEL_STACK_SIZE;
|
||||||
|
|
||||||
thread_init_ready(thread);
|
thread_set_ready(thread);
|
||||||
thread->entry = entry;
|
thread->entry = entry;
|
||||||
thread->arg = arg;
|
thread->arg = arg;
|
||||||
thread->stack_top = align_down_uintptr(stack_top, STACK_ALIGNMENT);
|
thread->stack_top = align_down_uintptr(stack_top, STACK_ALIGNMENT);
|
||||||
@ -86,7 +86,6 @@ struct thread *kernel_thread_create(
|
|||||||
thread->wake_tick = 0;
|
thread->wake_tick = 0;
|
||||||
thread->next = 0;
|
thread->next = 0;
|
||||||
thread->wait_next = 0;
|
thread->wait_next = 0;
|
||||||
thread->wait_queue = 0;
|
|
||||||
copy_thread_name(thread->name, sizeof(thread->name), name);
|
copy_thread_name(thread->name, sizeof(thread->name), name);
|
||||||
|
|
||||||
spin_lock_irqsave(&scheduler_lock, &flags);
|
spin_lock_irqsave(&scheduler_lock, &flags);
|
||||||
@ -99,13 +98,6 @@ struct thread *kernel_thread_create(
|
|||||||
|
|
||||||
static void release_thread(struct thread *thread)
|
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->stack_base);
|
||||||
kfree(thread);
|
kfree(thread);
|
||||||
}
|
}
|
||||||
@ -114,10 +106,7 @@ void sched_reap_dead_threads(void)
|
|||||||
{
|
{
|
||||||
struct thread *prev = 0;
|
struct thread *prev = 0;
|
||||||
struct thread *thread = run_queue_head;
|
struct thread *thread = run_queue_head;
|
||||||
struct thread *reap_list = 0;
|
|
||||||
uint64_t flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&scheduler_lock, &flags);
|
|
||||||
while (thread != 0) {
|
while (thread != 0) {
|
||||||
struct thread *next = thread->next;
|
struct thread *next = thread->next;
|
||||||
|
|
||||||
@ -132,41 +121,13 @@ void sched_reap_dead_threads(void)
|
|||||||
run_queue_tail = prev;
|
run_queue_tail = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->next = reap_list;
|
release_thread(thread);
|
||||||
reap_list = thread;
|
|
||||||
} else {
|
} else {
|
||||||
prev = thread;
|
prev = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread = next;
|
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)
|
static void thread_trampoline(void)
|
||||||
@ -178,5 +139,9 @@ static void thread_trampoline(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread->entry(thread->arg);
|
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/sched.h>
|
||||||
#include <tianole/timer.h>
|
#include <tianole/timer.h>
|
||||||
|
|
||||||
@ -18,12 +17,7 @@ void wait_queue_init(struct wait_queue *queue)
|
|||||||
static void wait_queue_enqueue_locked(
|
static void wait_queue_enqueue_locked(
|
||||||
struct wait_queue *queue, struct thread *thread)
|
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_next = 0;
|
||||||
thread->wait_queue = queue;
|
|
||||||
|
|
||||||
if (queue->tail != 0) {
|
if (queue->tail != 0) {
|
||||||
queue->tail->wait_next = thread;
|
queue->tail->wait_next = thread;
|
||||||
@ -34,36 +28,14 @@ static void wait_queue_enqueue_locked(
|
|||||||
queue->tail = thread;
|
queue->tail = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct thread *wait_queue_remove_head_locked(struct wait_queue *queue)
|
static void wait_queue_remove_locked(
|
||||||
{
|
|
||||||
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(
|
|
||||||
struct wait_queue *queue, struct thread *target)
|
struct wait_queue *queue, struct thread *target)
|
||||||
{
|
{
|
||||||
struct thread *prev = 0;
|
struct thread *prev = 0;
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
|
|
||||||
if (queue == 0 || target == 0) {
|
if (queue == 0 || target == 0) {
|
||||||
return 0;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (target->wait_queue != queue) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (thread = queue->head; thread != 0; thread = thread->wait_next) {
|
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_next = 0;
|
||||||
thread->wait_queue = 0;
|
return;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prev = thread;
|
prev = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("wait queue membership is inconsistent");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wait_queue_mark_ready_locked(struct thread *thread)
|
static void wait_queue_mark_ready_locked(struct thread *thread)
|
||||||
{
|
{
|
||||||
if (thread == 0) {
|
if (thread_is_waiting(thread) || thread_is_sleeping(thread)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!thread_is_waiting(thread) && !thread_is_sleeping(thread)) {
|
|
||||||
panic("wait queue wakeup found non-waiting thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_set_ready(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +96,7 @@ int wait_queue_wait(
|
|||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
if (queue == 0 || condition == 0 || current_thread == 0) {
|
if (queue == 0 || condition == 0 || current_thread == 0) {
|
||||||
return -EINVAL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -208,7 +127,7 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
|
|||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
if (queue == 0 || condition == 0 || current_thread == 0) {
|
if (queue == 0 || condition == 0 || current_thread == 0) {
|
||||||
return -EINVAL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (condition(arg) != 0) {
|
if (condition(arg) != 0) {
|
||||||
@ -216,7 +135,7 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ticks == 0) {
|
if (ticks == 0) {
|
||||||
return -ETIMEDOUT;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
deadline = timer_ticks() + ticks;
|
deadline = timer_ticks() + ticks;
|
||||||
@ -233,7 +152,7 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
|
|||||||
if (now >= deadline) {
|
if (now >= deadline) {
|
||||||
current_thread->wake_tick = 0;
|
current_thread->wake_tick = 0;
|
||||||
spin_unlock_irqrestore(&queue->lock, flags);
|
spin_unlock_irqrestore(&queue->lock, flags);
|
||||||
return -ETIMEDOUT;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_set_sleeping(current_thread, deadline);
|
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)
|
void wait_queue_wake_one(struct wait_queue *queue)
|
||||||
{
|
{
|
||||||
|
struct thread *thread;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
if (queue == 0) {
|
if (queue == 0) {
|
||||||
@ -257,12 +177,25 @@ void wait_queue_wake_one(struct wait_queue *queue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&queue->lock, &flags);
|
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);
|
spin_unlock_irqrestore(&queue->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_queue_wake_all(struct wait_queue *queue)
|
void wait_queue_wake_all(struct wait_queue *queue)
|
||||||
{
|
{
|
||||||
|
struct thread *thread;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
if (queue == 0) {
|
if (queue == 0) {
|
||||||
@ -270,6 +203,15 @@ void wait_queue_wake_all(struct wait_queue *queue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&queue->lock, &flags);
|
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);
|
spin_unlock_irqrestore(&queue->lock, flags);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <tianole/early_log.h>
|
#include <tianole/early_log.h>
|
||||||
#include <tianole/errno.h>
|
|
||||||
#include <tianole/mm.h>
|
#include <tianole/mm.h>
|
||||||
|
|
||||||
#include "arch/x86/mm/page_table.h"
|
#include "arch/x86/mm/page_table.h"
|
||||||
@ -21,26 +20,10 @@ void page_table_selftest(void)
|
|||||||
|
|
||||||
page_tables_init();
|
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) {
|
if (map_page(TEST_VIRTUAL_PAGE, page, PAGE_WRITABLE) != 0) {
|
||||||
panic("page table selftest map failed");
|
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 ||
|
if (virt_to_phys(TEST_VIRTUAL_PAGE, &resolved) != 0 ||
|
||||||
resolved != page) {
|
resolved != page) {
|
||||||
panic("page table selftest resolve failed");
|
panic("page table selftest resolve failed");
|
||||||
@ -55,10 +38,6 @@ void page_table_selftest(void)
|
|||||||
panic("page table selftest unmap failed");
|
panic("page table selftest unmap failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unmap_page(TEST_VIRTUAL_PAGE) != -ENOENT) {
|
|
||||||
panic("page table selftest missing unmap errno failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
free_page(page);
|
free_page(page);
|
||||||
early_log_puts("page table selftest ok\n");
|
early_log_puts("page table selftest ok\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <tianole/early_log.h>
|
#include <tianole/early_log.h>
|
||||||
#include <tianole/errno.h>
|
|
||||||
#include <tianole/sched.h>
|
#include <tianole/sched.h>
|
||||||
#include <tianole/spinlock.h>
|
#include <tianole/spinlock.h>
|
||||||
|
|
||||||
@ -14,53 +13,15 @@ static void thread_selftest_entry(void *arg)
|
|||||||
(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)
|
void sched_selftest(void)
|
||||||
{
|
{
|
||||||
struct spinlock test_lock;
|
struct spinlock test_lock;
|
||||||
struct wait_queue test_wait_queue;
|
|
||||||
struct thread *first =
|
struct thread *first =
|
||||||
kernel_thread_create("worker-a", thread_selftest_entry, 0);
|
kernel_thread_create("worker-a", thread_selftest_entry, 0);
|
||||||
struct thread *second =
|
struct thread *second =
|
||||||
kernel_thread_create("worker-b", thread_selftest_entry, 0);
|
kernel_thread_create("worker-b", thread_selftest_entry, 0);
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
sched_state_machine_selftest();
|
|
||||||
|
|
||||||
test_lock.locked = 0;
|
test_lock.locked = 0;
|
||||||
|
|
||||||
if (first == 0 || second == 0 || first == second) {
|
if (first == 0 || second == 0 || first == second) {
|
||||||
@ -72,10 +33,6 @@ void sched_selftest(void)
|
|||||||
panic("kernel thread selftest state failed");
|
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 ||
|
if ((first->stack_top & (STACK_ALIGNMENT - 1)) != 0 ||
|
||||||
(second->stack_top & (STACK_ALIGNMENT - 1)) != 0) {
|
(second->stack_top & (STACK_ALIGNMENT - 1)) != 0) {
|
||||||
panic("kernel thread selftest stack alignment failed");
|
panic("kernel thread selftest stack alignment failed");
|
||||||
@ -89,15 +46,6 @@ void sched_selftest(void)
|
|||||||
spin_lock_irqsave(&test_lock, &flags);
|
spin_lock_irqsave(&test_lock, &flags);
|
||||||
spin_unlock_irqrestore(&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");
|
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)
|
static void condition_wait_demo_waker(void *arg)
|
||||||
{
|
{
|
||||||
uint64_t flags;
|
|
||||||
|
|
||||||
(void)arg;
|
(void)arg;
|
||||||
|
|
||||||
early_log_puts("condition waker sleeping\n");
|
early_log_puts("condition waker sleeping\n");
|
||||||
sched_sleep(5);
|
sched_sleep(5);
|
||||||
wait_queue_lock_irqsave(&condition_wait_queue, &flags);
|
|
||||||
condition_ready = 1;
|
condition_ready = 1;
|
||||||
early_log_puts("condition waker wake_all\n");
|
early_log_puts("condition waker wake_all\n");
|
||||||
wait_queue_wake_all_locked(&condition_wait_queue);
|
wait_queue_wake_all(&condition_wait_queue);
|
||||||
wait_queue_unlock_irqrestore(&condition_wait_queue, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timeout_wait_demo_waiter(void *arg)
|
static void timeout_wait_demo_waiter(void *arg)
|
||||||
@ -182,75 +126,41 @@ static void timeout_wait_demo_waiter(void *arg)
|
|||||||
(void)arg;
|
(void)arg;
|
||||||
|
|
||||||
early_log_puts("timeout waiter sleeping\n");
|
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(
|
if (wait_queue_wait_timeout(
|
||||||
&timeout_wait_queue, condition_is_ready, &never_ready, 3) !=
|
&timeout_wait_queue, condition_is_ready, &never_ready, 3) !=
|
||||||
-ETIMEDOUT) {
|
-1) {
|
||||||
panic("timeout wait did not time out");
|
panic("timeout wait did not time out");
|
||||||
}
|
}
|
||||||
early_log_puts("timeout waiter timed out\n");
|
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)
|
void sched_demo_start(void)
|
||||||
{
|
{
|
||||||
struct thread *first;
|
struct thread *first = kernel_thread_create(
|
||||||
struct thread *second;
|
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
|
||||||
struct thread *waiter;
|
struct thread *second = kernel_thread_create(
|
||||||
struct thread *waker;
|
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
|
||||||
struct thread *condition_waiter;
|
struct thread *waiter =
|
||||||
struct thread *condition_waker;
|
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
||||||
struct thread *timeout_waiter;
|
struct thread *waker =
|
||||||
struct thread *return_exit;
|
kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
||||||
struct thread *explicit_exit;
|
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(&demo_wait_queue);
|
||||||
wait_queue_init(&condition_wait_queue);
|
wait_queue_init(&condition_wait_queue);
|
||||||
wait_queue_init(&timeout_wait_queue);
|
wait_queue_init(&timeout_wait_queue);
|
||||||
condition_ready = 0;
|
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");
|
early_log_puts("scheduler starting\n");
|
||||||
sched_yield();
|
sched_yield();
|
||||||
|
|
||||||
|
|||||||
21
mm/heap.c
21
mm/heap.c
@ -2,7 +2,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <tianole/early_log.h>
|
#include <tianole/early_log.h>
|
||||||
#include <tianole/errno.h>
|
|
||||||
#include <tianole/mm.h>
|
#include <tianole/mm.h>
|
||||||
|
|
||||||
#define HEAP_BASE 0xffffff2000000000ull
|
#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) {
|
for (current = start; current < end; current += PAGE_SIZE) {
|
||||||
phys_addr_t page = alloc_page();
|
phys_addr_t page = alloc_page();
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (page == 0) {
|
if (page == 0 ||
|
||||||
return -ENOMEM;
|
map_page(current,
|
||||||
}
|
page,
|
||||||
|
PAGE_WRITABLE | PAGE_NO_EXECUTE) != 0) {
|
||||||
ret = map_page(current, page, PAGE_WRITABLE | PAGE_NO_EXECUTE);
|
return -1;
|
||||||
if (ret != 0) {
|
|
||||||
free_page(page);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,11 +102,9 @@ static int heap_extend(size_t min_size)
|
|||||||
size_t bytes =
|
size_t bytes =
|
||||||
align_up_size(min_size + sizeof(struct heap_block), PAGE_SIZE);
|
align_up_size(min_size + sizeof(struct heap_block), PAGE_SIZE);
|
||||||
struct heap_block *block = (struct heap_block *)(uintptr_t)heap_end;
|
struct heap_block *block = (struct heap_block *)(uintptr_t)heap_end;
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = map_heap_range(heap_end, bytes);
|
if (map_heap_range(heap_end, bytes) != 0) {
|
||||||
if (ret != 0) {
|
return -1;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_end += bytes;
|
heap_end += bytes;
|
||||||
|
|||||||
@ -34,12 +34,6 @@ check_lines build/debug.log \
|
|||||||
"condition waiter sleeping" \
|
"condition waiter sleeping" \
|
||||||
"condition waker sleeping" \
|
"condition waker sleeping" \
|
||||||
"timeout waiter 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 1 step=2" \
|
||||||
"preempt thread 2 step=2" \
|
"preempt thread 2 step=2" \
|
||||||
"timeout waiter timed out" \
|
"timeout waiter timed out" \
|
||||||
@ -77,12 +71,6 @@ check_lines build/serial.log \
|
|||||||
"condition waiter sleeping" \
|
"condition waiter sleeping" \
|
||||||
"condition waker sleeping" \
|
"condition waker sleeping" \
|
||||||
"timeout waiter 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 1 step=2" \
|
||||||
"preempt thread 2 step=2" \
|
"preempt thread 2 step=2" \
|
||||||
"timeout waiter timed out" \
|
"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
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from structure_checks.common import (
|
|
||||||
all_files,
|
SOURCE_ROOTS = ("arch", "include", "kernel", "mm")
|
||||||
c_files,
|
C_SOURCE_ROOTS = ("arch", "kernel", "mm")
|
||||||
changed_files,
|
MAKEFILE_SOURCE_VARS = (
|
||||||
public_headers,
|
"ARCH_KERNEL_SRCS",
|
||||||
repo_root,
|
"ARCH_MM_SRCS",
|
||||||
source_files,
|
"BOOT_SRCS",
|
||||||
)
|
"KERNEL_SRCS",
|
||||||
from structure_checks.makefiles import check_makefile_source_lists
|
"MM_SRCS",
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
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")
|
parser = argparse.ArgumentParser(description="Check Tianole structure rules")
|
||||||
mode = parser.add_mutually_exclusive_group()
|
mode = parser.add_mutually_exclusive_group()
|
||||||
mode.add_argument("--all", action="store_true", help="check the whole tree")
|
mode.add_argument("--all", action="store_true", help="check the whole tree")
|
||||||
mode.add_argument(
|
mode.add_argument(
|
||||||
"--changed", action="store_true", help="check changed and untracked files"
|
"--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()
|
root = repo_root()
|
||||||
all_mode = not args.changed
|
all_mode = not args.changed
|
||||||
files = all_files(root) if all_mode else changed_files(root)
|
files = all_files(root) if all_mode else changed_files(root)
|
||||||
|
|
||||||
errors = []
|
errors = []
|
||||||
sources = source_files(files)
|
errors.extend(check_no_relative_parent_includes(root, 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_selftests_are_centralized(c_files(files)))
|
errors.extend(check_selftests_are_centralized(c_files(files)))
|
||||||
errors.extend(check_makefile_source_lists(root, files, all_mode))
|
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