Proper kernel
This commit is contained in:
21
Makefile
21
Makefile
@@ -38,7 +38,7 @@ TARGET = BOOTX64.EFI
|
|||||||
TARGET_SO = bootx64.so
|
TARGET_SO = bootx64.so
|
||||||
OBJ = $(BUILD_DIR)/main.o
|
OBJ = $(BUILD_DIR)/main.o
|
||||||
KERNEL_TARGET = kernel.elf
|
KERNEL_TARGET = kernel.elf
|
||||||
KERNEL_OBJ = $(BUILD_DIR)/kernel.o
|
KERNEL_OBJS = $(BUILD_DIR)/kernel.o $(BUILD_DIR)/string_utils.o $(BUILD_DIR)/commands.o
|
||||||
KERNEL_LD = kernel.ld
|
KERNEL_LD = kernel.ld
|
||||||
|
|
||||||
# QEMU settings
|
# QEMU settings
|
||||||
@@ -76,7 +76,8 @@ QEMU_FLAGS = -machine q35 \
|
|||||||
-drive if=pflash,format=raw,file=$(BUILD_DIR)/OVMF_VARS.fd \
|
-drive if=pflash,format=raw,file=$(BUILD_DIR)/OVMF_VARS.fd \
|
||||||
-drive format=raw,file=fat:rw:$(BUILD_DIR) \
|
-drive format=raw,file=fat:rw:$(BUILD_DIR) \
|
||||||
-net none \
|
-net none \
|
||||||
-nographic
|
-nographic \
|
||||||
|
-no-reboot
|
||||||
|
|
||||||
# Phony targets
|
# Phony targets
|
||||||
.PHONY: all clean run setup install-deps
|
.PHONY: all clean run setup install-deps
|
||||||
@@ -96,15 +97,25 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
|||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
# Compile kernel source
|
# Compile kernel source
|
||||||
$(KERNEL_OBJ): $(SRC_DIR)/kernel.c
|
$(BUILD_DIR)/kernel.o: $(SRC_DIR)/kernel.c
|
||||||
@echo "Compiling kernel.c..."
|
@echo "Compiling kernel.c..."
|
||||||
$(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
|
$(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
|
||||||
-mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
|
-mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/string_utils.o: $(SRC_DIR)/string_utils.c
|
||||||
|
@echo "Compiling string_utils.c..."
|
||||||
|
$(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
|
||||||
|
-mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/commands.o: $(SRC_DIR)/commands.c
|
||||||
|
@echo "Compiling commands.c..."
|
||||||
|
$(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
|
||||||
|
-mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
# Link kernel ELF
|
# Link kernel ELF
|
||||||
$(BUILD_DIR)/$(KERNEL_TARGET): $(KERNEL_OBJ) $(KERNEL_LD)
|
$(BUILD_DIR)/$(KERNEL_TARGET): $(KERNEL_OBJS) $(KERNEL_LD)
|
||||||
@echo "Linking kernel ELF..."
|
@echo "Linking kernel ELF..."
|
||||||
$(LD) -nostdlib -T $(KERNEL_LD) $(KERNEL_OBJ) -o $@
|
$(LD) -nostdlib -T $(KERNEL_LD) $(KERNEL_OBJS) -o $@
|
||||||
|
|
||||||
# Link to create shared object
|
# Link to create shared object
|
||||||
$(BUILD_DIR)/$(TARGET_SO): $(OBJ)
|
$(BUILD_DIR)/$(TARGET_SO): $(OBJ)
|
||||||
|
|||||||
65
README.md
65
README.md
@@ -7,7 +7,8 @@ A minimal bootable UEFI operating system written in C that demonstrates basic UE
|
|||||||
- **UEFI Boot**: Boots directly on UEFI firmware
|
- **UEFI Boot**: Boots directly on UEFI firmware
|
||||||
- **Console I/O**: Interactive keyboard input and screen output
|
- **Console I/O**: Interactive keyboard input and screen output
|
||||||
- **System Information**: Displays firmware details
|
- **System Information**: Displays firmware details
|
||||||
- **Simple Commands**: Interactive command interface
|
- **Kernel Loader**: Loads an ELF64 kernel from the EFI partition
|
||||||
|
- **Simple Commands**: Minimal interactive command interface
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -44,13 +45,13 @@ sudo dnf install gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Build the UEFI application:
|
Build the UEFI application and kernel:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
This will create `build/EFI/BOOT/BOOTX64.EFI`
|
This will create `build/EFI/BOOT/BOOTX64.EFI` and `build/kernel.elf`.
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
@@ -60,24 +61,43 @@ Test with QEMU (no graphics mode):
|
|||||||
make run
|
make run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The loader expects `kernel.elf` at the root of the EFI partition (next to the `EFI/` directory).
|
||||||
|
|
||||||
### QEMU Controls:
|
### QEMU Controls:
|
||||||
- **Exit QEMU**: Press `Ctrl+A`, then `X`
|
- **Exit QEMU**: Press `Ctrl+A`, then `X`
|
||||||
- **Shutdown OS**: Press `q` in the OS
|
- **Shutdown OS**: Type `shutdown` in the OS
|
||||||
|
|
||||||
## Commands in the OS
|
## Commands in the OS
|
||||||
|
|
||||||
Once the OS boots, you can use these commands:
|
Once the OS boots, you can use these commands:
|
||||||
|
|
||||||
- `h` - Display help
|
- `help` - Display all available commands
|
||||||
- `i` - Display system information
|
- `man <command>` - Display detailed help for a command
|
||||||
- `c` - Clear screen
|
- `shutdown` - Shutdown the system
|
||||||
- `q` - Shutdown the system
|
- `clear` - Clear the screen
|
||||||
|
- `about` - Display system information
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
```
|
||||||
|
-> help
|
||||||
|
-> man shutdown
|
||||||
|
-> clear
|
||||||
|
-> about
|
||||||
|
-> shutdown
|
||||||
|
```
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
├── main.c # Main UEFI application source code
|
├── main.c # UEFI loader (reads and loads kernel ELF)
|
||||||
|
├── kernel.c # Kernel entry point and main loop
|
||||||
|
├── commands.c # Command registry and handlers
|
||||||
|
├── commands.h # Command interface definitions
|
||||||
|
├── string_utils.c # String utility functions
|
||||||
|
├── string_utils.h # String utility declarations
|
||||||
|
├── kernel.ld # Kernel linker script
|
||||||
|
├── boot_info.h # Shared boot interface
|
||||||
├── Makefile # Build configuration
|
├── Makefile # Build configuration
|
||||||
└── README.md # This file
|
└── README.md # This file
|
||||||
```
|
```
|
||||||
@@ -87,9 +107,17 @@ Once the OS boots, you can use these commands:
|
|||||||
1. **UEFI Entry Point**: The `efi_main` function is the entry point called by UEFI firmware
|
1. **UEFI Entry Point**: The `efi_main` function is the entry point called by UEFI firmware
|
||||||
2. **Initialization**: GNU-EFI library is initialized with the system table
|
2. **Initialization**: GNU-EFI library is initialized with the system table
|
||||||
3. **Console Setup**: Output is configured and screen is cleared
|
3. **Console Setup**: Output is configured and screen is cleared
|
||||||
4. **Main Loop**: The OS enters a loop waiting for keyboard input
|
4. **Kernel Load**: The loader reads `kernel.elf` and maps its loadable segments
|
||||||
5. **Command Processing**: User commands are processed and executed
|
5. **Main Loop**: The kernel enters a loop waiting for keyboard input
|
||||||
6. **Shutdown**: UEFI runtime services are used to shutdown the system
|
6. **Command Processing**: User commands are parsed and dispatched to registered handlers
|
||||||
|
7. **Command Execution**: Each command is executed via its handler function
|
||||||
|
8. **Shutdown**: UEFI runtime services are used to shutdown the system
|
||||||
|
|
||||||
|
**Command System Architecture:**
|
||||||
|
- Commands are registered in a static array with metadata (name, description, usage, handler)
|
||||||
|
- The `execute_command()` function parses input, splits command and arguments, and dispatches to handlers
|
||||||
|
- The `man` command provides detailed help by looking up command metadata
|
||||||
|
- New commands can be easily added by implementing a handler and registering it
|
||||||
|
|
||||||
## Building for Real Hardware
|
## Building for Real Hardware
|
||||||
|
|
||||||
@@ -99,7 +127,8 @@ To create a bootable USB drive:
|
|||||||
2. Format a USB drive with GPT and create an EFI partition (FAT32)
|
2. Format a USB drive with GPT and create an EFI partition (FAT32)
|
||||||
3. Mount the EFI partition
|
3. Mount the EFI partition
|
||||||
4. Copy `build/EFI/BOOT/BOOTX64.EFI` to `/EFI/BOOT/` on the USB drive
|
4. Copy `build/EFI/BOOT/BOOTX64.EFI` to `/EFI/BOOT/` on the USB drive
|
||||||
5. Boot from the USB drive in UEFI mode
|
5. Copy `build/kernel.elf` to the root of the EFI partition
|
||||||
|
6. Boot from the USB drive in UEFI mode
|
||||||
|
|
||||||
**Warning**: Always backup your data before creating bootable media!
|
**Warning**: Always backup your data before creating bootable media!
|
||||||
|
|
||||||
@@ -146,7 +175,15 @@ This is a minimal educational example. Feel free to use and modify as needed.
|
|||||||
|
|
||||||
## Further Development
|
## Further Development
|
||||||
|
|
||||||
Ideas for expansion:
|
The codebase is now modular and easy to extend:
|
||||||
|
|
||||||
|
**Adding New Commands:**
|
||||||
|
1. Open `commands.c`
|
||||||
|
2. Create a new handler function: `static void cmd_yourcommand(BootInfo *Boot, CHAR16 *Args)`
|
||||||
|
3. Add your command to the `commands[]` array with name, description, usage, and handler
|
||||||
|
4. Rebuild with `make`
|
||||||
|
|
||||||
|
**Other Ideas for expansion:**
|
||||||
- File system support
|
- File system support
|
||||||
- Memory management
|
- Memory management
|
||||||
- Graphics output
|
- Graphics output
|
||||||
|
|||||||
242
commands.c
Normal file
242
commands.c
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
#include <efi.h>
|
||||||
|
#include "commands.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
|
||||||
|
#define SAFE_PRINT(Boot, ...) \
|
||||||
|
do { \
|
||||||
|
if ((Boot) != NULL && (Boot)->print != NULL) { \
|
||||||
|
(Boot)->print(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args);
|
||||||
|
static void cmd_help(BootInfo *Boot, CHAR16 *Args);
|
||||||
|
static void cmd_man(BootInfo *Boot, CHAR16 *Args);
|
||||||
|
static void cmd_clear(BootInfo *Boot, CHAR16 *Args);
|
||||||
|
static void cmd_about(BootInfo *Boot, CHAR16 *Args);
|
||||||
|
|
||||||
|
// Command registry
|
||||||
|
static Command commands[] = {
|
||||||
|
{
|
||||||
|
L"shutdown",
|
||||||
|
L"Shutdown the system",
|
||||||
|
L"Usage: shutdown\n\r Initiates a system shutdown using UEFI runtime services.",
|
||||||
|
cmd_shutdown
|
||||||
|
},
|
||||||
|
{
|
||||||
|
L"help",
|
||||||
|
L"Display available commands",
|
||||||
|
L"Usage: help\n\r Lists all available commands with brief descriptions.",
|
||||||
|
cmd_help
|
||||||
|
},
|
||||||
|
{
|
||||||
|
L"man",
|
||||||
|
L"Display manual page for a command",
|
||||||
|
L"Usage: man <command>\n\r Shows detailed help for the specified command.",
|
||||||
|
cmd_man
|
||||||
|
},
|
||||||
|
{
|
||||||
|
L"clear",
|
||||||
|
L"Clear the screen",
|
||||||
|
L"Usage: clear\n\r Clears the console screen.",
|
||||||
|
cmd_clear
|
||||||
|
},
|
||||||
|
{
|
||||||
|
L"about",
|
||||||
|
L"Display system information",
|
||||||
|
L"Usage: about\n\r Shows information about this operating system.",
|
||||||
|
cmd_about
|
||||||
|
},
|
||||||
|
{NULL, NULL, NULL, NULL} // Sentinel
|
||||||
|
};
|
||||||
|
|
||||||
|
static void request_shutdown(BootInfo *Boot)
|
||||||
|
{
|
||||||
|
if (Boot == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Boot->shutdown != NULL) {
|
||||||
|
Boot->shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Boot->SystemTable != NULL &&
|
||||||
|
Boot->SystemTable->RuntimeServices != NULL &&
|
||||||
|
Boot->SystemTable->RuntimeServices->ResetSystem != NULL) {
|
||||||
|
Boot->SystemTable->RuntimeServices->ResetSystem(
|
||||||
|
EfiResetShutdown, EFI_SUCCESS, 0, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"Shutdown service unavailable.\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args)
|
||||||
|
{
|
||||||
|
(void)Args; // Unused
|
||||||
|
SAFE_PRINT(Boot, L"Shutting down...\n\r");
|
||||||
|
request_shutdown(Boot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmd_help(BootInfo *Boot, CHAR16 *Args)
|
||||||
|
{
|
||||||
|
(void)Args; // Unused
|
||||||
|
show_help(Boot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmd_man(BootInfo *Boot, CHAR16 *Args)
|
||||||
|
{
|
||||||
|
UINTN i = 0;
|
||||||
|
|
||||||
|
if (Args == NULL || Args[0] == L'\0') {
|
||||||
|
SAFE_PRINT(Boot, L"Usage: man <command>\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"Try 'help' for a list of available commands.\n\r");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trim_spaces_inplace(Args);
|
||||||
|
|
||||||
|
for (i = 0; commands[i].name != NULL; i++) {
|
||||||
|
if (ascii_streq_ci(Args, commands[i].name)) {
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"NAME\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" %s - %s\n\r\n\r", commands[i].name, commands[i].description);
|
||||||
|
SAFE_PRINT(Boot, L"DESCRIPTION\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" %s\n\r", commands[i].usage);
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"No manual entry for '%s'\n\r", Args);
|
||||||
|
SAFE_PRINT(Boot, L"Try 'help' for a list of available commands.\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmd_clear(BootInfo *Boot, CHAR16 *Args)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
(void)Args; // Unused
|
||||||
|
|
||||||
|
if (Boot != NULL && Boot->clear_screen != NULL) {
|
||||||
|
Status = Boot->clear_screen();
|
||||||
|
if (EFI_ERROR(Status)) {
|
||||||
|
SAFE_PRINT(Boot, L"Failed to clear screen: %r\n\r", Status);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SAFE_PRINT(Boot, L"Clear screen function unavailable.\n\r");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmd_about(BootInfo *Boot, CHAR16 *Args)
|
||||||
|
{
|
||||||
|
(void)Args; // Unused
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"================================================\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" Simple UEFI Operating System\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"================================================\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"A minimal bootable UEFI operating system written in C.\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"Features:\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" - UEFI Boot\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" - Console I/O\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" - ELF64 Kernel Loader\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" - Interactive Command Interface\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"Type 'help' for available commands.\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_help(BootInfo *Boot)
|
||||||
|
{
|
||||||
|
UINTN i = 0;
|
||||||
|
UINTN max_len = 0;
|
||||||
|
UINTN name_len = 0;
|
||||||
|
UINTN padding = 0;
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"Available commands:\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
|
||||||
|
// Find longest command name for formatting
|
||||||
|
for (i = 0; commands[i].name != NULL; i++) {
|
||||||
|
name_len = 0;
|
||||||
|
while (commands[i].name[name_len] != L'\0') {
|
||||||
|
name_len++;
|
||||||
|
}
|
||||||
|
if (name_len > max_len) {
|
||||||
|
max_len = name_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; commands[i].name != NULL; i++) {
|
||||||
|
SAFE_PRINT(Boot, L" %s", commands[i].name);
|
||||||
|
|
||||||
|
// Add padding
|
||||||
|
name_len = 0;
|
||||||
|
while (commands[i].name[name_len] != L'\0') {
|
||||||
|
name_len++;
|
||||||
|
}
|
||||||
|
for (padding = 0; padding < (max_len - name_len + 2); padding++) {
|
||||||
|
SAFE_PRINT(Boot, L" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"- %s\n\r", commands[i].description);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"For detailed help on a command, type: man <command>\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute_command(BootInfo *Boot, CHAR16 *Input)
|
||||||
|
{
|
||||||
|
CHAR16 *cmd_start = NULL;
|
||||||
|
CHAR16 *args_start = NULL;
|
||||||
|
UINTN i = 0;
|
||||||
|
|
||||||
|
if (Boot == NULL || Input == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trim_spaces_inplace(Input);
|
||||||
|
|
||||||
|
if (Input[0] == L'\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split command and arguments
|
||||||
|
cmd_start = Input;
|
||||||
|
args_start = Input;
|
||||||
|
|
||||||
|
// Find first space
|
||||||
|
while (*args_start != L'\0' && !is_space16(*args_start)) {
|
||||||
|
args_start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found a space, null-terminate command and advance to args
|
||||||
|
if (*args_start != L'\0') {
|
||||||
|
*args_start = L'\0';
|
||||||
|
args_start++;
|
||||||
|
|
||||||
|
// Skip leading spaces in args
|
||||||
|
while (*args_start != L'\0' && is_space16(*args_start)) {
|
||||||
|
args_start++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for command
|
||||||
|
for (i = 0; commands[i].name != NULL; i++) {
|
||||||
|
if (ascii_streq_ci(cmd_start, commands[i].name)) {
|
||||||
|
commands[i].handler(Boot, args_start);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command not found
|
||||||
|
SAFE_PRINT(Boot, L"Unknown command: %s\n\r", cmd_start);
|
||||||
|
SAFE_PRINT(Boot, L"Type 'help' for a list of available commands.\n\r");
|
||||||
|
}
|
||||||
18
commands.h
Normal file
18
commands.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef COMMANDS_H
|
||||||
|
#define COMMANDS_H
|
||||||
|
|
||||||
|
#include "boot_info.h"
|
||||||
|
|
||||||
|
typedef void (*CommandHandlerFn)(BootInfo *Boot, CHAR16 *Args);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const CHAR16 *name;
|
||||||
|
const CHAR16 *description;
|
||||||
|
const CHAR16 *usage;
|
||||||
|
CommandHandlerFn handler;
|
||||||
|
} Command;
|
||||||
|
|
||||||
|
void execute_command(BootInfo *Boot, CHAR16 *Input);
|
||||||
|
void show_help(BootInfo *Boot);
|
||||||
|
|
||||||
|
#endif
|
||||||
136
kernel.c
136
kernel.c
@@ -1,69 +1,109 @@
|
|||||||
#include <efi.h>
|
#include <efi.h>
|
||||||
|
|
||||||
#include "boot_info.h"
|
#include "boot_info.h"
|
||||||
|
#include "commands.h"
|
||||||
|
|
||||||
|
#define SAFE_PRINT(Boot, ...) \
|
||||||
|
do { \
|
||||||
|
if ((Boot) != NULL && (Boot)->print != NULL) { \
|
||||||
|
(Boot)->print(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
void kmain(BootInfo *Boot)
|
void kmain(BootInfo *Boot)
|
||||||
{
|
{
|
||||||
EFI_INPUT_KEY Key;
|
EFI_INPUT_KEY Key;
|
||||||
|
EFI_STATUS Status;
|
||||||
|
UINTN read_errors = 0;
|
||||||
|
|
||||||
Boot->clear_screen();
|
if (Boot == NULL) {
|
||||||
Boot->set_attribute(EFI_TEXT_ATTR(EFI_LIGHTGREEN, EFI_BLACK));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Boot->print(L"================================================\n\r");
|
if (Boot->clear_screen != NULL) {
|
||||||
Boot->print(L" Welcome to Simple UEFI Operating System!\n\r");
|
Status = Boot->clear_screen();
|
||||||
Boot->print(L"================================================\n\r");
|
if (EFI_ERROR(Status)) {
|
||||||
Boot->print(L"\n\r");
|
SAFE_PRINT(Boot, L"clear_screen failed: %r\n\r", Status);
|
||||||
Boot->print(L"System Information:\n\r");
|
}
|
||||||
Boot->print(L" UEFI Firmware Vendor: %s\n\r", Boot->SystemTable->FirmwareVendor);
|
}
|
||||||
Boot->print(L" UEFI Firmware Revision: %d.%d\n\r",
|
|
||||||
|
if (Boot->set_attribute != NULL) {
|
||||||
|
Status = Boot->set_attribute(EFI_TEXT_ATTR(EFI_LIGHTGREEN, EFI_BLACK));
|
||||||
|
if (EFI_ERROR(Status)) {
|
||||||
|
SAFE_PRINT(Boot, L"set_attribute failed: %r\n\r", Status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"================================================\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" Welcome to Simple UEFI Operating System!\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"================================================\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"System Information:\n\r");
|
||||||
|
if (Boot->SystemTable != NULL) {
|
||||||
|
SAFE_PRINT(Boot, L" UEFI Firmware Vendor: %s\n\r",
|
||||||
|
Boot->SystemTable->FirmwareVendor);
|
||||||
|
SAFE_PRINT(Boot, L" UEFI Firmware Revision: %d.%d\n\r",
|
||||||
Boot->SystemTable->FirmwareRevision >> 16,
|
Boot->SystemTable->FirmwareRevision >> 16,
|
||||||
Boot->SystemTable->FirmwareRevision & 0xFFFF);
|
Boot->SystemTable->FirmwareRevision & 0xFFFF);
|
||||||
Boot->print(L"\n\r");
|
} else {
|
||||||
|
SAFE_PRINT(Boot, L" UEFI System Table: Unavailable\n\r");
|
||||||
|
}
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
|
||||||
Boot->print(L"Available Services:\n\r");
|
SAFE_PRINT(Boot, L"Available Services:\n\r");
|
||||||
Boot->print(L" - Console Input/Output: Active\n\r");
|
SAFE_PRINT(Boot, L" - Console Input/Output: %s\n\r",
|
||||||
Boot->print(L" - Runtime Services: Active\n\r");
|
(Boot->read_key != NULL && Boot->print != NULL) ? L"Active" : L"Unavailable");
|
||||||
Boot->print(L" - Boot Services: Active\n\r");
|
SAFE_PRINT(Boot, L" - Runtime Services: %s\n\r",
|
||||||
Boot->print(L"\n\r");
|
(Boot->SystemTable != NULL &&
|
||||||
|
Boot->SystemTable->RuntimeServices != NULL) ? L"Active" : L"Unavailable");
|
||||||
|
SAFE_PRINT(Boot, L" - Boot Services: %s\n\r",
|
||||||
|
(Boot->SystemTable != NULL &&
|
||||||
|
Boot->SystemTable->BootServices != NULL) ? L"Active" : L"Unavailable");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"Type 'help' for a list of commands.\n\r\n\r");
|
||||||
|
|
||||||
Boot->print(L"Commands:\n\r");
|
// Simple line buffer
|
||||||
Boot->print(L" 'h' - Display help\n\r");
|
CHAR16 line[128];
|
||||||
Boot->print(L" 'i' - Display system info\n\r");
|
UINTN len = 0;
|
||||||
Boot->print(L" 'c' - Clear screen\n\r");
|
|
||||||
Boot->print(L" 'q' - Shutdown\n\r");
|
SAFE_PRINT(Boot, L"-> ");
|
||||||
Boot->print(L"\n\r");
|
|
||||||
Boot->print(L"Press a key to start...\n\r");
|
|
||||||
|
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
if (EFI_ERROR(Boot->read_key(&Key))) {
|
if (Boot->read_key == NULL) {
|
||||||
continue;
|
SAFE_PRINT(Boot, L"Console input unavailable.\n\r");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Key.UnicodeChar == L'q' || Key.UnicodeChar == L'Q') {
|
Status = Boot->read_key(&Key);
|
||||||
Boot->print(L"\n\rShutting down...\n\r");
|
if (EFI_ERROR(Status)) {
|
||||||
Boot->shutdown();
|
read_errors++;
|
||||||
} else if (Key.UnicodeChar == L'h' || Key.UnicodeChar == L'H') {
|
if (read_errors == 1 || (read_errors % 64) == 0) {
|
||||||
Boot->print(L"\n\r=== Help ===\n\r");
|
SAFE_PRINT(Boot, L"read_key failed: %r\n\r", Status);
|
||||||
Boot->print(L"This is a minimal UEFI operating system.\n\r");
|
}
|
||||||
Boot->print(L"It demonstrates basic UEFI functionality.\n\r");
|
continue;
|
||||||
Boot->print(L"\n\r");
|
}
|
||||||
} else if (Key.UnicodeChar == L'i' || Key.UnicodeChar == L'I') {
|
read_errors = 0;
|
||||||
Boot->print(L"\n\r=== System Info ===\n\r");
|
|
||||||
Boot->print(L"Firmware Vendor: %s\n\r", Boot->SystemTable->FirmwareVendor);
|
if (Key.UnicodeChar == L'\r' || Key.UnicodeChar == L'\n') {
|
||||||
Boot->print(L"Firmware Revision: %d.%d\n\r",
|
// Enter pressed: terminate string and handle
|
||||||
Boot->SystemTable->FirmwareRevision >> 16,
|
line[len] = L'\0';
|
||||||
Boot->SystemTable->FirmwareRevision & 0xFFFF);
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
Boot->print(L"UEFI Specification: %d.%d\n\r",
|
execute_command(Boot, line);
|
||||||
Boot->SystemTable->Hdr.Revision >> 16,
|
|
||||||
Boot->SystemTable->Hdr.Revision & 0xFFFF);
|
// Reset for next command
|
||||||
Boot->print(L"\n\r");
|
len = 0;
|
||||||
} else if (Key.UnicodeChar == L'c' || Key.UnicodeChar == L'C') {
|
SAFE_PRINT(Boot, L"-> ");
|
||||||
Boot->clear_screen();
|
} else if (Key.UnicodeChar == L'\b' || Key.UnicodeChar == 0x7F) {
|
||||||
Boot->print(L"Screen cleared. Press 'h' for help.\n\r");
|
// Backspace
|
||||||
|
if (len > 0) {
|
||||||
|
len--;
|
||||||
|
SAFE_PRINT(Boot, L"\b \b");
|
||||||
|
}
|
||||||
} else if (Key.UnicodeChar >= 32 && Key.UnicodeChar < 127) {
|
} else if (Key.UnicodeChar >= 32 && Key.UnicodeChar < 127) {
|
||||||
Boot->print(L"%c", Key.UnicodeChar);
|
if (len < (sizeof(line) / sizeof(line[0]) - 1)) {
|
||||||
} else if (Key.UnicodeChar == L'\r') {
|
line[len++] = Key.UnicodeChar;
|
||||||
Boot->print(L"\n\r> ");
|
SAFE_PRINT(Boot, L"%c", Key.UnicodeChar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
61
string_utils.c
Normal file
61
string_utils.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include "string_utils.h"
|
||||||
|
|
||||||
|
BOOLEAN is_space16(CHAR16 Ch)
|
||||||
|
{
|
||||||
|
return (Ch == L' ' || Ch == L'\t');
|
||||||
|
}
|
||||||
|
|
||||||
|
CHAR16 ascii_lower16(CHAR16 Ch)
|
||||||
|
{
|
||||||
|
if (Ch >= L'A' && Ch <= L'Z') {
|
||||||
|
return (CHAR16)(Ch + 32);
|
||||||
|
}
|
||||||
|
return Ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN ascii_streq_ci(const CHAR16 *A, const CHAR16 *B)
|
||||||
|
{
|
||||||
|
if (A == NULL || B == NULL) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*A != L'\0' && *B != L'\0') {
|
||||||
|
if (ascii_lower16(*A) != ascii_lower16(*B)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
A++;
|
||||||
|
B++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*A == L'\0' && *B == L'\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
void trim_spaces_inplace(CHAR16 *Cmd)
|
||||||
|
{
|
||||||
|
UINTN start = 0;
|
||||||
|
UINTN end = 0;
|
||||||
|
UINTN i = 0;
|
||||||
|
|
||||||
|
if (Cmd == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (Cmd[start] != L'\0' && is_space16(Cmd[start])) {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = start;
|
||||||
|
while (Cmd[end] != L'\0') {
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (end > start && is_space16(Cmd[end - 1])) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i < (end - start)) {
|
||||||
|
Cmd[i] = Cmd[start + i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
Cmd[i] = L'\0';
|
||||||
|
}
|
||||||
11
string_utils.h
Normal file
11
string_utils.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef STRING_UTILS_H
|
||||||
|
#define STRING_UTILS_H
|
||||||
|
|
||||||
|
#include <efi.h>
|
||||||
|
|
||||||
|
BOOLEAN is_space16(CHAR16 Ch);
|
||||||
|
CHAR16 ascii_lower16(CHAR16 Ch);
|
||||||
|
BOOLEAN ascii_streq_ci(const CHAR16 *A, const CHAR16 *B);
|
||||||
|
void trim_spaces_inplace(CHAR16 *Cmd);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user