feat(boot): load separate x86 kernel image from UEFI bootloader

This commit is contained in:
Microindole 2026-05-08 19:44:32 +08:00
parent 80a2ec024d
commit f60b6af52f
No known key found for this signature in database
GPG Key ID: 22FB34CD2133ACA1
9 changed files with 539 additions and 25 deletions

View File

@ -1,12 +1,14 @@
SHELL := /bin/bash SHELL := /bin/bash
CC := clang CC := clang
ELF_LD := ld.lld
LD := lld-link LD := lld-link
ARCH ?= x86_64 ARCH ?= x86_64
ifeq ($(ARCH),x86_64) ifeq ($(ARCH),x86_64)
ARCH_DIR := arch/x86 ARCH_DIR := arch/x86
EFI_ARCH_TARGET := x86_64-unknown-windows EFI_ARCH_TARGET := x86_64-unknown-windows
KERNEL_ARCH_TARGET := x86_64-unknown-none-elf
BOOT_EFI_NAME := BOOTX64.EFI BOOT_EFI_NAME := BOOTX64.EFI
else else
$(error Unsupported ARCH '$(ARCH)') $(error Unsupported ARCH '$(ARCH)')
@ -16,6 +18,8 @@ BUILD_DIR := build
EFI_DIR := $(BUILD_DIR)/image/EFI/BOOT EFI_DIR := $(BUILD_DIR)/image/EFI/BOOT
BOOT_EFI := $(EFI_DIR)/$(BOOT_EFI_NAME) BOOT_EFI := $(EFI_DIR)/$(BOOT_EFI_NAME)
BOOT_OBJ := $(BUILD_DIR)/arch/boot/main.obj BOOT_OBJ := $(BUILD_DIR)/arch/boot/main.obj
KERNEL_ELF := $(BUILD_DIR)/image/kernel.elf
KERNEL_OBJS := $(BUILD_DIR)/arch/kernel/entry.o $(BUILD_DIR)/kernel/main.o
DEBUG_LOG := $(BUILD_DIR)/debug.log DEBUG_LOG := $(BUILD_DIR)/debug.log
OVMF_CODE ?= /usr/share/OVMF/OVMF_CODE_4M.fd OVMF_CODE ?= /usr/share/OVMF/OVMF_CODE_4M.fd
OVMF_VARS_TEMPLATE ?= /usr/share/OVMF/OVMF_VARS_4M.fd OVMF_VARS_TEMPLATE ?= /usr/share/OVMF/OVMF_VARS_4M.fd
@ -34,6 +38,23 @@ CFLAGS := \
-Iinclude \ -Iinclude \
-I$(ARCH_DIR)/include -I$(ARCH_DIR)/include
KERNEL_CFLAGS := \
-target $(KERNEL_ARCH_TARGET) \
-ffreestanding \
-fno-stack-protector \
-fno-builtin \
-fno-pic \
-mno-red-zone \
-Wall \
-Wextra \
-Werror \
-Iinclude \
-I$(ARCH_DIR)/include
KERNEL_ASFLAGS := \
-target $(KERNEL_ARCH_TARGET) \
-ffreestanding
LDFLAGS := \ LDFLAGS := \
/subsystem:efi_application \ /subsystem:efi_application \
/entry:efi_main \ /entry:efi_main \
@ -42,19 +63,34 @@ LDFLAGS := \
/machine:x64 \ /machine:x64 \
/out:$(BOOT_EFI) /out:$(BOOT_EFI)
KERNEL_LDFLAGS := \
-m elf_x86_64 \
-nostdlib \
-T $(ARCH_DIR)/kernel/linker.ld \
-o $(KERNEL_ELF)
.PHONY: all clean run run-headless dirs .PHONY: all clean run run-headless dirs
all: $(BOOT_EFI) all: $(BOOT_EFI) $(KERNEL_ELF)
dirs: dirs:
mkdir -p $(BUILD_DIR)/arch/boot $(EFI_DIR) mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/kernel $(EFI_DIR)
$(BOOT_OBJ): $(ARCH_DIR)/boot/main.c $(ARCH_DIR)/include/efi.h include/tianole/boot_info.h | dirs $(BOOT_OBJ): $(ARCH_DIR)/boot/main.c $(ARCH_DIR)/include/efi.h include/tianole/boot_info.h include/tianole/elf.h | dirs
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
$(BOOT_EFI): $(BOOT_OBJ) | dirs $(BOOT_EFI): $(BOOT_OBJ) | dirs
$(LD) $(LDFLAGS) $< $(LD) $(LDFLAGS) $<
$(BUILD_DIR)/arch/kernel/entry.o: $(ARCH_DIR)/kernel/entry.S | dirs
$(CC) $(KERNEL_ASFLAGS) -c $< -o $@
$(BUILD_DIR)/kernel/main.o: kernel/main.c include/tianole/boot_info.h | dirs
$(CC) $(KERNEL_CFLAGS) -c $< -o $@
$(KERNEL_ELF): $(KERNEL_OBJS) $(ARCH_DIR)/kernel/linker.ld | dirs
$(ELF_LD) $(KERNEL_LDFLAGS) $(KERNEL_OBJS)
$(OVMF_VARS): | dirs $(OVMF_VARS): | dirs
cp $(OVMF_VARS_TEMPLATE) $@ cp $(OVMF_VARS_TEMPLATE) $@

View File

@ -1,16 +1,18 @@
#include "efi.h" #include "efi.h"
#include "tianole/elf.h"
#include "tianole/boot_info.h" #include "tianole/boot_info.h"
static efi_char16_t banner_text[] = { typedef void __attribute__((sysv_abi)) (*kernel_entry_fn_t)(const tianole_boot_info_t *);
static efi_char16_t boot_banner_text[] = {
'T', 'i', 'a', 'n', 'o', 'l', 'e', ' ', 'T', 'i', 'a', 'n', 'o', 'l', 'e', ' ',
'x', '8', '6', ' ', 'b', 'o', 'o', 't', 'x', '8', '6', ' ', 'b', 'o', 'o', 't',
' ', 's', 't', 'u', 'b', ' ', 'l', 'o', 'l', 'o', 'a', 'd', 'e', 'r', '.', '\r', '\n', 0
'a', 'd', 'e', 'd', '.', '\r', '\n', 0
}; };
static const tianole_boot_info_t boot_info = { static efi_char16_t kernel_path_text[] = {
.version = TIANOLE_BOOT_INFO_VERSION, '\\', 'k', 'e', 'r', 'n', 'e', 'l', '.',
.boot_flags = 0, 'e', 'l', 'f', 0
}; };
static inline void debug_putc(char ch) { static inline void debug_putc(char ch) {
@ -26,19 +28,240 @@ static void debug_puts(const char *text) {
} }
} }
static void debug_boot_info(const tianole_boot_info_t *info) { static void *mem_copy(void *dst, const void *src, uint64_t size) {
(void)info; uint8_t *out = (uint8_t *)dst;
debug_puts("boot_info.version=1\n"); const uint8_t *in = (const uint8_t *)src;
uint64_t i;
for (i = 0; i < size; ++i) {
out[i] = in[i];
}
return dst;
}
static void mem_zero(void *dst, uint64_t size) {
uint8_t *out = (uint8_t *)dst;
uint64_t i;
for (i = 0; i < size; ++i) {
out[i] = 0;
}
}
static efi_status open_root(
efi_handle image_handle,
efi_system_table_t *system_table,
efi_file_protocol_t **root
) {
efi_guid_t loaded_image_guid = efi_loaded_image_protocol_guid();
efi_guid_t simple_fs_guid = efi_simple_file_system_protocol_guid();
efi_loaded_image_protocol_t *loaded_image;
efi_simple_file_system_protocol_t *fs;
efi_status status;
status = system_table->boot_services->handle_protocol(
image_handle,
&loaded_image_guid,
(void **)&loaded_image
);
if (status != EFI_SUCCESS) {
return status;
}
status = system_table->boot_services->handle_protocol(
loaded_image->device_handle,
&simple_fs_guid,
(void **)&fs
);
if (status != EFI_SUCCESS) {
return status;
}
return fs->open_volume(fs, root);
}
static efi_status read_entire_file(
efi_system_table_t *system_table,
efi_file_protocol_t *root,
efi_char16_t *path,
void **buffer,
uint64_t *size
) {
efi_guid_t file_info_guid = efi_file_info_guid();
efi_file_protocol_t *file;
efi_file_info_t *file_info;
efi_uintn_t info_size;
efi_uintn_t read_size;
efi_status status;
status = root->open(root, &file, path, EFI_OPEN_MODE_READ, 0);
if (status != EFI_SUCCESS) {
return status;
}
info_size = 0;
status = file->get_info(file, &file_info_guid, &info_size, 0);
if (status != EFI_BUFFER_TOO_SMALL) {
file->close(file);
return status;
}
status = system_table->boot_services->allocate_pool(EFI_LOADER_DATA, info_size, (void **)&file_info);
if (status != EFI_SUCCESS) {
file->close(file);
return status;
}
status = file->get_info(file, &file_info_guid, &info_size, file_info);
if (status != EFI_SUCCESS) {
file->close(file);
return status;
}
*size = file_info->file_size;
status = system_table->boot_services->allocate_pool(EFI_LOADER_DATA, *size, buffer);
if (status != EFI_SUCCESS) {
file->close(file);
return status;
}
read_size = *size;
status = file->read(file, &read_size, *buffer);
file->close(file);
if (status != EFI_SUCCESS) {
return status;
}
if (read_size != *size) {
return EFI_LOAD_ERROR;
}
return EFI_SUCCESS;
}
static efi_status validate_kernel_elf(const elf64_ehdr_t *ehdr) {
if (*(const uint32_t *)ehdr->ident != ELF_MAGIC) {
return EFI_LOAD_ERROR;
}
if (ehdr->ident[4] != ELFCLASS64 || ehdr->ident[5] != ELFDATA2LSB) {
return EFI_LOAD_ERROR;
}
if (ehdr->ident[6] != EV_CURRENT || ehdr->version != EV_CURRENT) {
return EFI_LOAD_ERROR;
}
if (ehdr->type != ET_EXEC || ehdr->machine != EM_X86_64) {
return EFI_LOAD_ERROR;
}
if (ehdr->phentsize != sizeof(elf64_phdr_t)) {
return EFI_LOAD_ERROR;
}
return EFI_SUCCESS;
}
static efi_status load_kernel_image(
efi_system_table_t *system_table,
const void *kernel_image,
uint64_t kernel_size,
kernel_entry_fn_t *entry_out
) {
const elf64_ehdr_t *ehdr = (const elf64_ehdr_t *)kernel_image;
const elf64_phdr_t *phdrs;
uint16_t index;
efi_status status;
if (kernel_size < sizeof(*ehdr)) {
return EFI_LOAD_ERROR;
}
status = validate_kernel_elf(ehdr);
if (status != EFI_SUCCESS) {
return status;
}
phdrs = (const elf64_phdr_t *)((const uint8_t *)kernel_image + ehdr->phoff);
for (index = 0; index < ehdr->phnum; ++index) {
const elf64_phdr_t *phdr = &phdrs[index];
efi_physical_address_t segment_base;
uint64_t page_count;
if (phdr->type != PT_LOAD) {
continue;
}
if (phdr->memsz < phdr->filesz) {
return EFI_LOAD_ERROR;
}
if (phdr->offset + phdr->filesz > kernel_size) {
return EFI_LOAD_ERROR;
}
segment_base = phdr->paddr;
page_count = (phdr->memsz + 0xfffULL) >> 12;
status = system_table->boot_services->allocate_pages(
EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA,
page_count,
&segment_base
);
if (status != EFI_SUCCESS) {
return status;
}
mem_zero((void *)(uintptr_t)phdr->paddr, phdr->memsz);
mem_copy(
(void *)(uintptr_t)phdr->paddr,
(const uint8_t *)kernel_image + phdr->offset,
phdr->filesz
);
}
*entry_out = (kernel_entry_fn_t)(uintptr_t)ehdr->entry;
return EFI_SUCCESS;
} }
efi_status EFIAPI efi_main(efi_handle image_handle, efi_system_table_t *system_table) { efi_status EFIAPI efi_main(efi_handle image_handle, efi_system_table_t *system_table) {
(void)image_handle; efi_file_protocol_t *root;
void *kernel_image;
uint64_t kernel_size;
kernel_entry_fn_t kernel_entry;
tianole_boot_info_t boot_info = {
.version = TIANOLE_BOOT_INFO_VERSION,
.boot_flags = TIANOLE_BOOT_FLAG_BOOT_SERVICES_ACTIVE,
};
efi_status status;
system_table->con_out->output_string(system_table->con_out, banner_text); system_table->con_out->output_string(system_table->con_out, boot_banner_text);
debug_puts("Tianole x86 boot stub loaded.\n"); debug_puts("Tianole x86 bootloader loaded.\n");
debug_boot_info(&boot_info);
status = open_root(image_handle, system_table, &root);
if (status != EFI_SUCCESS) {
debug_puts("failed: open_root\n");
return status;
}
status = read_entire_file(system_table, root, kernel_path_text, &kernel_image, &kernel_size);
root->close(root);
if (status != EFI_SUCCESS) {
debug_puts("failed: read kernel.elf\n");
return status;
}
status = load_kernel_image(system_table, kernel_image, kernel_size, &kernel_entry);
if (status != EFI_SUCCESS) {
debug_puts("failed: load kernel image\n");
return status;
}
debug_puts("jumping to kernel entry\n");
kernel_entry(&boot_info);
debug_puts("kernel returned unexpectedly\n");
for (;;) { for (;;) {
system_table->boot_services->stall(1000 * 1000); system_table->boot_services->stall(1000 * 1000);
} }
return EFI_SUCCESS;
} }

View File

@ -7,8 +7,28 @@ typedef uint64_t efi_status;
typedef void *efi_handle; typedef void *efi_handle;
typedef uint16_t efi_char16_t; typedef uint16_t efi_char16_t;
typedef uint64_t efi_uintn_t; typedef uint64_t efi_uintn_t;
typedef uint64_t efi_physical_address_t;
#define EFI_SUCCESS 0 #define EFI_SUCCESS 0
#define EFI_LOAD_ERROR 0x8000000000000001ULL
#define EFI_INVALID_PARAMETER 0x8000000000000002ULL
#define EFI_BUFFER_TOO_SMALL 0x8000000000000005ULL
#define EFI_OUT_OF_RESOURCES 0x8000000000000009ULL
#define EFI_NOT_FOUND 0x800000000000000eULL
#define EFI_ALLOCATE_ADDRESS 2
#define EFI_LOADER_DATA 4
#define EFI_OPEN_MODE_READ 0x0000000000000001ULL
#define EFI_FILE_INFO_ID 0x09576e92
#define EFI_FILE_INFO_ID_A 0x6d3f
#define EFI_FILE_INFO_ID_B 0x11d2
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID_A 0x964e5b22
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID_B 0x6459
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID_C 0x11d2
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_A 0x5b1b31a1
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_B 0x9562
#define EFI_LOADED_IMAGE_PROTOCOL_GUID_C 0x11d2
#if defined(__x86_64__) #if defined(__x86_64__)
#define EFIAPI __attribute__((ms_abi)) #define EFIAPI __attribute__((ms_abi))
@ -16,6 +36,13 @@ typedef uint64_t efi_uintn_t;
#define EFIAPI #define EFIAPI
#endif #endif
typedef struct {
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
} efi_guid_t;
typedef struct { typedef struct {
uint64_t signature; uint64_t signature;
uint32_t revision; uint32_t revision;
@ -26,6 +53,10 @@ typedef struct {
typedef struct efi_simple_text_output_protocol efi_simple_text_output_protocol_t; typedef struct efi_simple_text_output_protocol efi_simple_text_output_protocol_t;
typedef struct efi_boot_services efi_boot_services_t; typedef struct efi_boot_services efi_boot_services_t;
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_system_table efi_system_table_t;
struct efi_simple_text_output_protocol { struct efi_simple_text_output_protocol {
void *reset; void *reset;
@ -35,14 +66,87 @@ struct efi_simple_text_output_protocol {
); );
}; };
typedef struct {
uint64_t size;
uint64_t file_size;
uint64_t physical_size;
uint64_t create_time[2];
uint64_t last_access_time[2];
uint64_t modification_time[2];
uint64_t attribute;
efi_char16_t file_name[1];
} efi_file_info_t;
struct efi_file_protocol {
uint64_t revision;
efi_status(EFIAPI *open)(
efi_file_protocol_t *self,
efi_file_protocol_t **new_handle,
efi_char16_t *file_name,
uint64_t open_mode,
uint64_t attributes
);
efi_status(EFIAPI *close)(efi_file_protocol_t *self);
void *delete_;
efi_status(EFIAPI *read)(
efi_file_protocol_t *self,
efi_uintn_t *buffer_size,
void *buffer
);
void *write;
void *get_position;
void *set_position;
efi_status(EFIAPI *get_info)(
efi_file_protocol_t *self,
efi_guid_t *information_type,
efi_uintn_t *buffer_size,
void *buffer
);
void *set_info;
void *flush;
};
struct efi_simple_file_system_protocol {
uint64_t revision;
efi_status(EFIAPI *open_volume)(
efi_simple_file_system_protocol_t *self,
efi_file_protocol_t **root
);
};
struct efi_loaded_image_protocol {
uint32_t revision;
efi_handle parent_handle;
efi_system_table_t *system_table;
efi_handle device_handle;
void *file_path;
void *reserved;
uint32_t load_options_size;
void *load_options;
void *image_base;
uint64_t image_size;
uint32_t image_code_type;
uint32_t image_data_type;
void *unload;
};
struct efi_boot_services { struct efi_boot_services {
efi_table_header_t hdr; efi_table_header_t hdr;
void *raise_tpl; void *raise_tpl;
void *restore_tpl; void *restore_tpl;
void *allocate_pages; efi_status(EFIAPI *allocate_pages)(
int type,
int memory_type,
efi_uintn_t pages,
efi_physical_address_t *memory
);
void *free_pages; void *free_pages;
void *get_memory_map; void *get_memory_map;
void *allocate_pool; efi_status(EFIAPI *allocate_pool)(
int pool_type,
efi_uintn_t size,
void **buffer
);
void *free_pool; void *free_pool;
void *create_event; void *create_event;
void *set_timer; void *set_timer;
@ -53,7 +157,11 @@ struct efi_boot_services {
void *install_protocol_interface; void *install_protocol_interface;
void *reinstall_protocol_interface; void *reinstall_protocol_interface;
void *uninstall_protocol_interface; void *uninstall_protocol_interface;
void *handle_protocol; efi_status(EFIAPI *handle_protocol)(
efi_handle handle,
efi_guid_t *protocol,
void **interface
);
void *reserved; void *reserved;
void *register_protocol_notify; void *register_protocol_notify;
void *locate_handle; void *locate_handle;
@ -69,7 +177,7 @@ struct efi_boot_services {
void *set_watchdog_timer; void *set_watchdog_timer;
}; };
typedef struct { struct efi_system_table {
efi_table_header_t hdr; efi_table_header_t hdr;
efi_char16_t *firmware_vendor; efi_char16_t *firmware_vendor;
uint32_t firmware_revision; uint32_t firmware_revision;
@ -83,6 +191,33 @@ typedef struct {
efi_boot_services_t *boot_services; efi_boot_services_t *boot_services;
uint64_t number_of_table_entries; uint64_t number_of_table_entries;
void *configuration_table; void *configuration_table;
} efi_system_table_t; };
static inline efi_guid_t efi_file_info_guid(void) {
return (efi_guid_t){
.data1 = EFI_FILE_INFO_ID,
.data2 = EFI_FILE_INFO_ID_A,
.data3 = EFI_FILE_INFO_ID_B,
.data4 = {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b},
};
}
static inline efi_guid_t efi_simple_file_system_protocol_guid(void) {
return (efi_guid_t){
.data1 = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID_A,
.data2 = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID_B,
.data3 = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID_C,
.data4 = {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b},
};
}
static inline efi_guid_t efi_loaded_image_protocol_guid(void) {
return (efi_guid_t){
.data1 = EFI_LOADED_IMAGE_PROTOCOL_GUID_A,
.data2 = EFI_LOADED_IMAGE_PROTOCOL_GUID_B,
.data3 = EFI_LOADED_IMAGE_PROTOCOL_GUID_C,
.data4 = {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b},
};
}
#endif #endif

17
arch/x86/kernel/entry.S Normal file
View File

@ -0,0 +1,17 @@
.global _start
.type _start, @function
.section .text
_start:
lea kernel_stack_top(%rip), %rsp
call kernel_main
1:
hlt
jmp 1b
.section .bss
.balign 16
kernel_stack:
.skip 16384
kernel_stack_top:

23
arch/x86/kernel/linker.ld Normal file
View File

@ -0,0 +1,23 @@
ENTRY(_start)
SECTIONS
{
. = 0x200000;
.text : ALIGN(4K) {
*(.text .text.*)
}
.rodata : ALIGN(4K) {
*(.rodata .rodata.*)
}
.data : ALIGN(4K) {
*(.data .data.*)
}
.bss : ALIGN(4K) {
*(COMMON)
*(.bss .bss.*)
}
}

View File

@ -149,6 +149,12 @@
- 屏幕和 `debug.log` 有可见输出 - 屏幕和 `debug.log` 有可见输出
- 程序可常驻,便于调试 - 程序可常驻,便于调试
当前已经开始进入第 1 阶段:
- 代码结构已经为 `bootloader -> kernel` 交接预留边界
- `boot_info` 已经作为项目自有交接结构存在
- `arch/x86/` 已经承载当前架构专用启动代码
当前还没有完成: 当前还没有完成:
- 独立 `kernel` 接管 - 独立 `kernel` 接管

View File

@ -14,5 +14,6 @@ typedef struct {
} tianole_boot_info_t; } tianole_boot_info_t;
#define TIANOLE_BOOT_INFO_VERSION 1u #define TIANOLE_BOOT_INFO_VERSION 1u
#define TIANOLE_BOOT_FLAG_BOOT_SERVICES_ACTIVE (1u << 0)
#endif #endif

44
include/tianole/elf.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef TIANOLE_ELF_H
#define TIANOLE_ELF_H
#include <stdint.h>
#define ELF_MAGIC 0x464c457fu
#define ELFCLASS64 2
#define ELFDATA2LSB 1
#define EV_CURRENT 1
#define ET_EXEC 2
#define EM_X86_64 62
#define PT_LOAD 1
typedef struct {
unsigned char ident[16];
uint16_t type;
uint16_t machine;
uint32_t version;
uint64_t entry;
uint64_t phoff;
uint64_t shoff;
uint32_t flags;
uint16_t ehsize;
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstrndx;
} elf64_ehdr_t;
typedef struct {
uint32_t type;
uint32_t flags;
uint64_t offset;
uint64_t vaddr;
uint64_t paddr;
uint64_t filesz;
uint64_t memsz;
uint64_t align;
} elf64_phdr_t;
#endif

View File

@ -1,4 +1,33 @@
/* #include <stdint.h>
* Kernel code starts in the next stage.
* The initial milestone is a verified UEFI boot path. #include "tianole/boot_info.h"
*/
static inline void debug_putc(char ch) {
__asm__ volatile("outb %0, $0xe9" : : "a"(ch));
}
static void debug_puts(const char *text) {
while (*text != '\0') {
if (*text == '\n') {
debug_putc('\r');
}
debug_putc(*text++);
}
}
void kernel_main(const tianole_boot_info_t *boot_info) {
debug_puts("kernel_main entered\n");
if (boot_info != 0 && boot_info->version == TIANOLE_BOOT_INFO_VERSION) {
debug_puts("boot_info.version ok\n");
} else {
debug_puts("boot_info.version invalid\n");
}
if (boot_info != 0 && (boot_info->boot_flags & TIANOLE_BOOT_FLAG_BOOT_SERVICES_ACTIVE) != 0) {
debug_puts("boot services still active\n");
}
for (;;) {
__asm__ volatile("hlt");
}
}