kernel: add framebuffer early console and tighten wait queues
Some checks failed
check / build-and-boot (push) Has been cancelled

This commit is contained in:
Microindole 2026-05-13 11:42:24 +08:00
parent 98463ad991
commit 11a1a8234b
No known key found for this signature in database
GPG Key ID: 22FB34CD2133ACA1
22 changed files with 590 additions and 40 deletions

View File

@ -66,14 +66,14 @@ $(KERNEL_ELF): $(KERNEL_OBJS) $(ARCH_DIR)/kernel/linker.ld | dirs
$(OVMF_VARS): | dirs
cp $(OVMF_VARS_TEMPLATE) $@
run: $(BOOT_EFI) $(OVMF_VARS)
run: $(BOOT_EFI) $(KERNEL_ELF) $(OVMF_VARS)
qemu-system-x86_64 \
-drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \
-drive if=pflash,format=raw,file=$(OVMF_VARS) \
-drive format=raw,file=fat:rw:$(BUILD_DIR)/image \
-serial stdio
run-headless: $(BOOT_EFI) $(OVMF_VARS)
run-headless: $(BOOT_EFI) $(KERNEL_ELF) $(OVMF_VARS)
rm -f $(DEBUG_LOG) $(SERIAL_LOG)
qemu-system-x86_64 \
-display none \

View File

@ -2,6 +2,7 @@ BOOT_SRCS := \
$(ARCH_DIR)/boot/main.c \
$(ARCH_DIR)/boot/file.c \
$(ARCH_DIR)/boot/elf_loader.c \
$(ARCH_DIR)/boot/framebuffer.c \
$(ARCH_DIR)/boot/memory_map.c \
$(ARCH_DIR)/boot/support.c

View File

@ -0,0 +1,35 @@
#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;
}

View File

@ -0,0 +1,19 @@
#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

View File

@ -4,6 +4,7 @@
#include "efi.h"
#include "elf_loader.h"
#include "file.h"
#include "framebuffer.h"
#include "memory_map.h"
static efi_char16_t boot_banner_text[] = u"Tianole x86 bootloader.\r\n";
@ -24,6 +25,7 @@ efi_status EFIAPI efi_main(
system_table->con_out->output_string(
system_table->con_out, boot_banner_text);
boot_debug_log_puts("Tianole x86 bootloader loaded.\n");
(void)boot_capture_framebuffer(system_table, &boot_info);
status = boot_read_file(image_handle,
system_table,

View File

@ -29,6 +29,9 @@ typedef uint64_t efi_physical_address_t;
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_A 0x5b1b31a1
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_B 0x9562
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_C 0x11d2
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_A 0x9042a9de
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_B 0x23dc
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID_C 0x4a38
#if defined(__x86_64__)
#define EFIAPI __attribute__((ms_abi))
@ -58,6 +61,7 @@ typedef struct efi_simple_file_system_protocol
efi_simple_file_system_protocol_t;
typedef struct efi_file_protocol efi_file_protocol_t;
typedef struct efi_loaded_image_protocol efi_loaded_image_protocol_t;
typedef struct efi_graphics_output_protocol efi_graphics_output_protocol_t;
typedef struct efi_system_table efi_system_table_t;
struct efi_simple_text_output_protocol {
@ -122,6 +126,31 @@ struct efi_loaded_image_protocol {
void *unload;
};
typedef struct {
uint32_t version;
uint32_t horizontal_resolution;
uint32_t vertical_resolution;
uint32_t pixel_format;
uint32_t pixel_information[4];
uint32_t pixels_per_scan_line;
} efi_graphics_output_mode_information_t;
typedef struct {
uint32_t max_mode;
uint32_t mode;
efi_graphics_output_mode_information_t *info;
efi_uintn_t size_of_info;
efi_physical_address_t frame_buffer_base;
efi_uintn_t frame_buffer_size;
} efi_graphics_output_protocol_mode_t;
struct efi_graphics_output_protocol {
void *query_mode;
void *set_mode;
void *blt;
efi_graphics_output_protocol_mode_t *mode;
};
struct efi_boot_services {
efi_table_header_t hdr;
void *raise_tpl;
@ -164,6 +193,15 @@ struct efi_boot_services {
void *get_next_monotonic_count;
void(EFIAPI *stall)(efi_uintn_t microseconds);
void *set_watchdog_timer;
void *connect_controller;
void *disconnect_controller;
void *open_protocol;
void *close_protocol;
void *open_protocol_information;
void *protocols_per_handle;
void *locate_handle_buffer;
efi_status(EFIAPI *locate_protocol)(
efi_guid_t *protocol, void *registration, void **interface);
};
struct efi_system_table {
@ -212,4 +250,14 @@ 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

View File

@ -3,6 +3,8 @@ ARCH_KERNEL_SRCS := \
$(ARCH_DIR)/kernel/gdt.c \
$(ARCH_DIR)/kernel/idt.c \
$(ARCH_DIR)/kernel/irq.c \
$(ARCH_DIR)/kernel/screen.c \
$(ARCH_DIR)/kernel/screen_font.c \
$(ARCH_DIR)/kernel/traps.c
ARCH_KERNEL_OBJS := \

View File

@ -4,6 +4,8 @@
#include <tianole/arch.h>
#include "screen.h"
#define X86_QEMU_DEBUG_PORT 0xe9
#define X86_COM1_BASE 0x3f8
@ -37,7 +39,7 @@ static void serial_putc(char ch)
outb(X86_COM1_BASE + X86_COM_DATA, (uint8_t)ch);
}
void arch_early_log_init(void)
void arch_early_log_init(const boot_info_t *boot_info)
{
outb(X86_COM1_BASE + X86_COM_INTERRUPT_ENABLE, 0x00);
outb(X86_COM1_BASE + X86_COM_LINE_CONTROL, X86_COM_LCR_DLAB);
@ -46,12 +48,14 @@ void arch_early_log_init(void)
outb(X86_COM1_BASE + X86_COM_LINE_CONTROL, X86_COM_LCR_8N1);
outb(X86_COM1_BASE + X86_COM_FIFO_CONTROL, 0xc7);
outb(X86_COM1_BASE + X86_COM_MODEM_CONTROL, 0x0b);
screen_console_init(boot_info);
}
void arch_early_log_putc(char ch)
{
debug_port_putc(ch);
serial_putc(ch);
screen_console_putc(ch);
}
void arch_halt_forever(void)

210
arch/x86/kernel/screen.c Normal file
View File

@ -0,0 +1,210 @@
#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();
}
}

18
arch/x86/kernel/screen.h Normal file
View File

@ -0,0 +1,18 @@
#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

View File

@ -0,0 +1,81 @@
#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;
}

View File

@ -0,0 +1,11 @@
#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

View File

@ -79,6 +79,7 @@
- kernel early log 已拆成通用前端和 x86 backend。
- x86 backend 已同时写 QEMU debug port 和 COM1。
- x86 backend 已可在 UEFI GOP framebuffer 上显示 early logQEMU 图形窗口可直接观察启动日志。
- `panic()` 已接入 early log 和 `arch_halt_forever()`
- `scripts/check.sh` 已验证 `build/debug.log``build/serial.log` 中的关键启动行。

View File

@ -154,6 +154,7 @@
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
- 已建立统一 `kernel_thread_exit()`/`sched_thread_exit()`,线程入口返回和显式退出都会进入明确退出路径,再由调度安全边界回收非当前 DEAD 线程。
- 已在调度私有头中加入 thread state helper调度核心、线程退出和 wait queue 路径不再直接散写主要状态转换。
- 已提供 `wait_queue_lock_irqsave()` / `wait_queue_unlock_irqrestore()` 和 locked wakeup 接口,条件修改与 wakeup 可以收敛在同一 wait queue 锁边界内。
- 已把调度代码按职责拆分为 `core.c`、`thread.c`、`wait.c`、`idle.c` 和私有 `sched.h`,并把当前阶段自测/演示线程移到 `kernel/selftest/sched.c`
- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized`、`kernel thread selftest ok`、timer 驱动线程轮转、`sched_sleep()`、wait queue wakeup、条件等待、超时等待、线程返回退出、显式退出和 DEAD 线程回收。
@ -167,8 +168,8 @@
### B. wait queue 锁语义
- 为 wait queue 增加内部锁或要求调用方持有指定锁,并在接口命名中体现约束
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队和 wakeup 队列修改
- 已为 wait queue 增加内部锁,并提供显式 locked wakeup 接口;后续继续扩大调用方按条件锁规则更新条件的覆盖面
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队、wakeup 队列修改,以及 demo 中的条件修改 + wakeup
- 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。
- 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。

View File

@ -3,12 +3,15 @@
#include <stdint.h>
#include <tianole/boot_info.h>
/**
* arch_early_log_init() - Initialize architecture early log devices.
* @boot_info: Bootloader handoff data, or NULL for backend-only setup.
*
* Provides the backend setup used by the generic early log front end.
*/
void arch_early_log_init(void);
void arch_early_log_init(const boot_info_t *boot_info);
/**
* arch_early_log_putc() - Emit one character through arch early log devices.

View File

@ -42,6 +42,12 @@ typedef struct {
* @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.
@ -55,12 +61,18 @@ typedef struct {
uint64_t memory_descriptor_size;
uint32_t memory_descriptor_version;
uint32_t reserved0;
uint64_t framebuffer_base;
uint64_t framebuffer_size;
uint32_t framebuffer_width;
uint32_t framebuffer_height;
uint32_t framebuffer_pixels_per_scan_line;
uint32_t framebuffer_pixel_format;
} boot_info_t;
/**
* BOOT_INFO_VERSION - Current boot_info_t layout version.
*/
#define BOOT_INFO_VERSION 1u
#define BOOT_INFO_VERSION 2u
/**
* BOOT_FLAG_SERVICES_ACTIVE - Firmware boot services were active at handoff.

View File

@ -3,13 +3,16 @@
#include <stdint.h>
#include <tianole/boot_info.h>
/**
* early_log_init() - Initialize early logging backends.
* @boot_info: Bootloader handoff data used by optional display backends.
*
* Sets up the architecture-provided early output path. This must be usable
* before heap, scheduler, VFS or normal drivers exist.
*/
void early_log_init(void);
void early_log_init(const boot_info_t *boot_info);
/**
* early_log_putc() - Write one character to every early log backend.

View File

@ -45,8 +45,9 @@ enum thread_state {
* @head: Oldest waiting thread.
* @tail: Newest waiting thread.
*
* The queue owns only wait links. The condition being waited on may be owned
* by another subsystem and must have a documented synchronization rule.
* The queue owns only wait links. A condition protected by this queue must be
* updated under wait_queue_lock_irqsave(), followed by a locked wakeup before
* releasing the lock; otherwise the caller must document its own lock rule.
*/
struct wait_queue {
struct spinlock lock;
@ -163,6 +164,40 @@ void sched_sleep(uint64_t ticks);
*/
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.
@ -177,6 +212,10 @@ void wait_queue_sleep(struct wait_queue *queue);
* @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(
@ -189,6 +228,9 @@ int wait_queue_wait(
* @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,

View File

@ -5,20 +5,20 @@
static int early_log_ready;
void early_log_init(void)
void early_log_init(const boot_info_t *boot_info)
{
if (early_log_ready != 0) {
return;
}
arch_early_log_init();
arch_early_log_init(boot_info);
early_log_ready = 1;
}
void early_log_putc(char ch)
{
if (early_log_ready == 0) {
early_log_init();
early_log_init(0);
}
arch_early_log_putc(ch);

View File

@ -8,7 +8,7 @@
void kernel_main(const boot_info_t *boot_info)
{
early_log_init();
early_log_init(boot_info);
early_log_puts("kernel_main entered\n");
arch_traps_init();
kernel_report_boot_state(boot_info);

View File

@ -91,8 +91,58 @@ static int wait_queue_remove_locked(
static void wait_queue_mark_ready_locked(struct thread *thread)
{
if (thread_is_waiting(thread) || thread_is_sleeping(thread)) {
if (thread == 0) {
return;
}
if (!thread_is_waiting(thread) && !thread_is_sleeping(thread)) {
panic("wait queue wakeup found non-waiting thread");
}
thread_set_ready(thread);
}
void wait_queue_lock_irqsave(struct wait_queue *queue, uint64_t *flags)
{
if (queue == 0 || flags == 0) {
return;
}
spin_lock_irqsave(&queue->lock, flags);
}
void wait_queue_unlock_irqrestore(struct wait_queue *queue, uint64_t flags)
{
if (queue == 0) {
return;
}
spin_unlock_irqrestore(&queue->lock, flags);
}
void wait_queue_wake_one_locked(struct wait_queue *queue)
{
struct thread *thread;
if (queue == 0) {
return;
}
thread = wait_queue_remove_head_locked(queue);
wait_queue_mark_ready_locked(thread);
}
void wait_queue_wake_all_locked(struct wait_queue *queue)
{
struct thread *thread;
if (queue == 0) {
return;
}
while (queue->head != 0) {
thread = wait_queue_remove_head_locked(queue);
wait_queue_mark_ready_locked(thread);
}
}
@ -200,7 +250,6 @@ int wait_queue_wait_timeout(struct wait_queue *queue,
void wait_queue_wake_one(struct wait_queue *queue)
{
struct thread *thread;
uint64_t flags;
if (queue == 0) {
@ -208,14 +257,12 @@ void wait_queue_wake_one(struct wait_queue *queue)
}
spin_lock_irqsave(&queue->lock, &flags);
thread = wait_queue_remove_head_locked(queue);
wait_queue_mark_ready_locked(thread);
wait_queue_wake_one_locked(queue);
spin_unlock_irqrestore(&queue->lock, flags);
}
void wait_queue_wake_all(struct wait_queue *queue)
{
struct thread *thread;
uint64_t flags;
if (queue == 0) {
@ -223,9 +270,6 @@ void wait_queue_wake_all(struct wait_queue *queue)
}
spin_lock_irqsave(&queue->lock, &flags);
while (queue->head != 0) {
thread = wait_queue_remove_head_locked(queue);
wait_queue_mark_ready_locked(thread);
}
wait_queue_wake_all_locked(queue);
spin_unlock_irqrestore(&queue->lock, flags);
}

View File

@ -162,13 +162,17 @@ static void condition_wait_demo_waiter(void *arg)
static void condition_wait_demo_waker(void *arg)
{
uint64_t flags;
(void)arg;
early_log_puts("condition waker sleeping\n");
sched_sleep(5);
wait_queue_lock_irqsave(&condition_wait_queue, &flags);
condition_ready = 1;
early_log_puts("condition waker wake_all\n");
wait_queue_wake_all(&condition_wait_queue);
wait_queue_wake_all_locked(&condition_wait_queue);
wait_queue_unlock_irqrestore(&condition_wait_queue, flags);
}
static void timeout_wait_demo_waiter(void *arg)
@ -209,23 +213,36 @@ static void explicit_exit_demo_thread(void *arg)
void sched_demo_start(void)
{
struct thread *first = kernel_thread_create(
struct thread *first;
struct thread *second;
struct thread *waiter;
struct thread *waker;
struct thread *condition_waiter;
struct thread *condition_waker;
struct thread *timeout_waiter;
struct thread *return_exit;
struct thread *explicit_exit;
wait_queue_init(&demo_wait_queue);
wait_queue_init(&condition_wait_queue);
wait_queue_init(&timeout_wait_queue);
condition_ready = 0;
first = kernel_thread_create(
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
struct thread *second = kernel_thread_create(
second = kernel_thread_create(
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
struct thread *waiter =
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
struct thread *waker =
kernel_thread_create("waker", wait_queue_demo_waker, 0);
struct thread *condition_waiter = kernel_thread_create(
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);
struct thread *condition_waker = kernel_thread_create(
condition_waker = kernel_thread_create(
"condition-waker", condition_wait_demo_waker, 0);
struct thread *timeout_waiter = kernel_thread_create(
timeout_waiter = kernel_thread_create(
"timeout-waiter", timeout_wait_demo_waiter, 0);
struct thread *return_exit =
return_exit =
kernel_thread_create("return-exit", return_exit_demo_thread, 0);
struct thread *explicit_exit = kernel_thread_create(
explicit_exit = kernel_thread_create(
"explicit-exit", explicit_exit_demo_thread, 0);
if (first == 0 || second == 0 || waiter == 0 || waker == 0 ||
@ -234,10 +251,6 @@ void sched_demo_start(void)
panic("scheduler demo thread creation failed");
}
wait_queue_init(&demo_wait_queue);
wait_queue_init(&condition_wait_queue);
wait_queue_init(&timeout_wait_queue);
condition_ready = 0;
early_log_puts("scheduler starting\n");
sched_yield();