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
|
$(OVMF_VARS): | dirs
|
||||||
cp $(OVMF_VARS_TEMPLATE) $@
|
cp $(OVMF_VARS_TEMPLATE) $@
|
||||||
|
|
||||||
run: $(BOOT_EFI) $(OVMF_VARS)
|
run: $(BOOT_EFI) $(KERNEL_ELF) $(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) $(OVMF_VARS)
|
run-headless: $(BOOT_EFI) $(KERNEL_ELF) $(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,6 +2,7 @@ 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
|
||||||
|
|
||||||
|
|||||||
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 "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";
|
||||||
@ -24,6 +25,7 @@ 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,6 +29,9 @@ 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))
|
||||||
@ -58,6 +61,7 @@ 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 {
|
||||||
@ -122,6 +126,31 @@ 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;
|
||||||
@ -164,6 +193,15 @@ 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 {
|
||||||
@ -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
|
#endif
|
||||||
|
|||||||
@ -3,6 +3,8 @@ 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,6 +4,8 @@
|
|||||||
|
|
||||||
#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
|
||||||
@ -37,7 +39,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(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_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);
|
||||||
@ -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_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)
|
||||||
|
|||||||
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。
|
- 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` 中的关键启动行。
|
||||||
|
|
||||||
|
|||||||
@ -154,6 +154,7 @@
|
|||||||
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
|
||||||
- 已建立统一 `kernel_thread_exit()`/`sched_thread_exit()`,线程入口返回和显式退出都会进入明确退出路径,再由调度安全边界回收非当前 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、条件等待、超时等待、线程返回退出、显式退出和 DEAD 线程回收。
|
||||||
|
|
||||||
@ -167,8 +168,8 @@
|
|||||||
|
|
||||||
### B. wait queue 锁语义
|
### B. wait queue 锁语义
|
||||||
|
|
||||||
- 为 wait queue 增加内部锁或要求调用方持有指定锁,并在接口命名中体现约束。
|
- 已为 wait queue 增加内部锁,并提供显式 locked wakeup 接口;后续继续扩大调用方按条件锁规则更新条件的覆盖面。
|
||||||
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队和 wakeup 队列修改。
|
- 继续把条件所属数据的修改规则文档化;当前 wait queue 内部锁已经覆盖条件检查、等待入队、wakeup 队列修改,以及 demo 中的条件修改 + wakeup。
|
||||||
- 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。
|
- 区分 `wake_one`、`wake_all`、timeout wakeup 和条件 wakeup 的状态处理。
|
||||||
- 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。
|
- 检查 wakeup 是否可能唤醒 DEAD、RUNNING 或未入队线程。
|
||||||
|
|
||||||
|
|||||||
@ -3,12 +3,15 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tianole/boot_info.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* arch_early_log_init() - Initialize architecture early log devices.
|
* 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.
|
* 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.
|
* 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_size: Size of each memory descriptor entry.
|
||||||
* @memory_descriptor_version: Firmware descriptor version.
|
* @memory_descriptor_version: Firmware descriptor version.
|
||||||
* @reserved0: Reserved field for alignment and future expansion.
|
* @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
|
* 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.
|
* or architecture API. Fields can grow while the kernel entry stays stable.
|
||||||
@ -55,12 +61,18 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BOOT_INFO_VERSION - Current boot_info_t layout version.
|
* 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.
|
* BOOT_FLAG_SERVICES_ACTIVE - Firmware boot services were active at handoff.
|
||||||
|
|||||||
@ -3,13 +3,16 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tianole/boot_info.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* early_log_init() - Initialize early logging backends.
|
* 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
|
* Sets up the architecture-provided early output path. This must be usable
|
||||||
* before heap, scheduler, VFS or normal drivers exist.
|
* 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.
|
* early_log_putc() - Write one character to every early log backend.
|
||||||
|
|||||||
@ -45,8 +45,9 @@ enum thread_state {
|
|||||||
* @head: Oldest waiting thread.
|
* @head: Oldest waiting thread.
|
||||||
* @tail: Newest waiting thread.
|
* @tail: Newest waiting thread.
|
||||||
*
|
*
|
||||||
* The queue owns only wait links. The condition being waited on may be owned
|
* The queue owns only wait links. A condition protected by this queue must be
|
||||||
* by another subsystem and must have a documented synchronization rule.
|
* 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;
|
||||||
@ -163,6 +164,40 @@ void sched_sleep(uint64_t ticks);
|
|||||||
*/
|
*/
|
||||||
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.
|
* wait_queue_sleep() - Sleep until another thread wakes the queue.
|
||||||
* @queue: Queue to sleep on.
|
* @queue: Queue to sleep on.
|
||||||
@ -177,6 +212,10 @@ void wait_queue_sleep(struct wait_queue *queue);
|
|||||||
* @condition: Predicate checked before and after sleeping.
|
* @condition: Predicate checked before and after sleeping.
|
||||||
* @arg: Opaque predicate argument.
|
* @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.
|
* Return: 0 when the condition is true, -EINVAL on invalid input.
|
||||||
*/
|
*/
|
||||||
int wait_queue_wait(
|
int wait_queue_wait(
|
||||||
@ -189,6 +228,9 @@ int wait_queue_wait(
|
|||||||
* @arg: Opaque predicate argument.
|
* @arg: Opaque predicate argument.
|
||||||
* @ticks: Maximum number of timer ticks to wait.
|
* @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.
|
* 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,
|
||||||
|
|||||||
@ -5,20 +5,20 @@
|
|||||||
|
|
||||||
static int early_log_ready;
|
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) {
|
if (early_log_ready != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
arch_early_log_init();
|
arch_early_log_init(boot_info);
|
||||||
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();
|
early_log_init(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
early_log_init(boot_info);
|
||||||
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);
|
||||||
|
|||||||
@ -91,8 +91,58 @@ static int wait_queue_remove_locked(
|
|||||||
|
|
||||||
static void wait_queue_mark_ready_locked(struct thread *thread)
|
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);
|
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)
|
void wait_queue_wake_one(struct wait_queue *queue)
|
||||||
{
|
{
|
||||||
struct thread *thread;
|
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
if (queue == 0) {
|
if (queue == 0) {
|
||||||
@ -208,14 +257,12 @@ void wait_queue_wake_one(struct wait_queue *queue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&queue->lock, &flags);
|
spin_lock_irqsave(&queue->lock, &flags);
|
||||||
thread = wait_queue_remove_head_locked(queue);
|
wait_queue_wake_one_locked(queue);
|
||||||
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) {
|
||||||
@ -223,9 +270,6 @@ void wait_queue_wake_all(struct wait_queue *queue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&queue->lock, &flags);
|
spin_lock_irqsave(&queue->lock, &flags);
|
||||||
while (queue->head != 0) {
|
wait_queue_wake_all_locked(queue);
|
||||||
thread = wait_queue_remove_head_locked(queue);
|
|
||||||
wait_queue_mark_ready_locked(thread);
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&queue->lock, flags);
|
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)
|
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(&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)
|
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)
|
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);
|
"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);
|
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
|
||||||
struct thread *waiter =
|
waiter = kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
||||||
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
|
waker = kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
||||||
struct thread *waker =
|
condition_waiter = kernel_thread_create(
|
||||||
kernel_thread_create("waker", wait_queue_demo_waker, 0);
|
|
||||||
struct thread *condition_waiter = kernel_thread_create(
|
|
||||||
"condition-waiter", condition_wait_demo_waiter, 0);
|
"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);
|
"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);
|
"timeout-waiter", timeout_wait_demo_waiter, 0);
|
||||||
struct thread *return_exit =
|
return_exit =
|
||||||
kernel_thread_create("return-exit", return_exit_demo_thread, 0);
|
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);
|
"explicit-exit", explicit_exit_demo_thread, 0);
|
||||||
|
|
||||||
if (first == 0 || second == 0 || waiter == 0 || waker == 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");
|
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");
|
early_log_puts("scheduler starting\n");
|
||||||
sched_yield();
|
sched_yield();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user