kernel: add framebuffer early console and tighten wait queues
Some checks failed
check / build-and-boot (push) Has been cancelled
Some checks failed
check / build-and-boot (push) Has been cancelled
This commit is contained in:
parent
98463ad991
commit
11a1a8234b
4
Makefile
4
Makefile
@ -66,14 +66,14 @@ $(KERNEL_ELF): $(KERNEL_OBJS) $(ARCH_DIR)/kernel/linker.ld | dirs
|
||||
$(OVMF_VARS): | dirs
|
||||
cp $(OVMF_VARS_TEMPLATE) $@
|
||||
|
||||
run: $(BOOT_EFI) $(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 \
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
35
arch/x86/boot/framebuffer.c
Normal file
35
arch/x86/boot/framebuffer.c
Normal 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;
|
||||
}
|
||||
19
arch/x86/boot/framebuffer.h
Normal file
19
arch/x86/boot/framebuffer.h
Normal 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
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 := \
|
||||
|
||||
@ -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
210
arch/x86/kernel/screen.c
Normal 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
18
arch/x86/kernel/screen.h
Normal 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
|
||||
81
arch/x86/kernel/screen_font.c
Normal file
81
arch/x86/kernel/screen_font.c
Normal 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;
|
||||
}
|
||||
11
arch/x86/kernel/screen_font.h
Normal file
11
arch/x86/kernel/screen_font.h
Normal 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
|
||||
@ -79,6 +79,7 @@
|
||||
|
||||
- kernel early log 已拆成通用前端和 x86 backend。
|
||||
- x86 backend 已同时写 QEMU debug port 和 COM1。
|
||||
- x86 backend 已可在 UEFI GOP framebuffer 上显示 early log,QEMU 图形窗口可直接观察启动日志。
|
||||
- `panic()` 已接入 early log 和 `arch_halt_forever()`。
|
||||
- `scripts/check.sh` 已验证 `build/debug.log` 和 `build/serial.log` 中的关键启动行。
|
||||
|
||||
|
||||
@ -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 或未入队线程。
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -91,9 +91,59 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
void wait_queue_sleep(struct wait_queue *queue)
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user