This commit is contained in:
Niklas Gollenstede
2025-04-14 11:20:52 +02:00
commit 5a2e32aaeb
126 changed files with 16742 additions and 0 deletions

22
boot/multiboot/config.inc Normal file
View File

@@ -0,0 +1,22 @@
; Magic Header, has to be present in Kernel to indicate Multiboot compliance
MULTIBOOT_HEADER_MAGIC_OS equ 0x1badb002
; Answer by the boot loader for Multiboot compliance, written in eax register
MULTIBOOT_HEADER_MAGIC_LOADER equ 0x2badb002
; Flags instructing the Multiboot compliant boot loader to setup the system
; according to your needs
MULTIBOOT_PAGE_ALIGN equ 1<<0 ; Align boot modules (initrds) at 4 KiB border
MULTIBOOT_MEMORY_INFO equ 1<<1 ; Request Memory Map information
MULTIBOOT_VIDEO_MODE equ 1<<2 ; Configure video mode
MULTIBOOT_HEADER_FLAGS equ 0
; Desired video mode (only considered if MULTIBOOT_VIDEO_MODE set)
; (boot loader will choose the best fitting mode, which might differ from the settings below)
MULTIBOOT_VIDEO_WIDTH equ 1280 ; Desired width
MULTIBOOT_VIDEO_HEIGHT equ 1024 ; Desired height
MULTIBOOT_VIDEO_BITDEPTH equ 32 ; Desired bit depth
; Checksum
MULTIBOOT_HEADER_CHKSUM equ -(MULTIBOOT_HEADER_MAGIC_OS + MULTIBOOT_HEADER_FLAGS)

167
boot/multiboot/data.cc Normal file
View File

@@ -0,0 +1,167 @@
#include "boot/multiboot/data.h"
/*! \brief Multiboot Information Structure according to Specification
* \see [Multiboot Specification]{@ref multiboot}
*/
struct multiboot_info {
/*! \brief Helper Structure
*/
struct Array {
uint32_t size; ///< Length
uint32_t addr; ///< Begin (physical address)
} __attribute__((packed));
enum Flag : uint32_t {
Memory = 1 << 0, ///< is there basic lower/upper memory information?
BootDev = 1 << 1, ///< is there a boot device set?
CmdLine = 1 << 2, ///< is the command-line defined?
Modules = 1 << 3, ///< are there modules to do something with?
/* These next two are mutually exclusive */
SymbolTable = 1 << 4, ///< is there an a.out symbol table loaded?
SectionHeader = 1 << 5, ///< is there an ELF section header table?
MemoryMap = 1 << 6, ///< is there a full memory map?
DriveInfo = 1 << 7, ///< Is there drive info?
ConfigTable = 1 << 8, ///< Is there a config table?
BootLoaderName = 1 << 9, ///< Is there a boot loader name?
ApmTable = 1 << 10, ///< Is there a APM table?
// Is there video information?
VbeInfo = 1 << 11, ///< Vesa bios extension
FramebufferInfo = 1 << 12 ///< Framebuffer
} flags;
/*! \brief Available memory retrieved from BIOS
*/
struct {
uint32_t lower; ///< Amount of memory below 1 MiB in kilobytes
uint32_t upper; ///< Amount of memory above 1 MiB in kilobytes
} mem __attribute__((packed));
uint32_t boot_device; ///< "root" partition
uint32_t cmdline; ///< Kernel command line
Array mods; ///< List of boot modules
union {
/*! \brief Symbol table for kernel in a.out format
*/
struct {
uint32_t tabsize;
uint32_t strsize;
uint32_t addr;
uint32_t reserved;
} aout_symbol_table __attribute__((packed));
/*! \brief Section header table for kernel in ELF
*/
struct {
uint32_t num; ///< Number of entries
uint32_t size; ///< Size per entry
uint32_t addr; ///< Start of the header table
uint32_t shndx; ///< String table index
} elf_section_header_table __attribute__((packed));
};
struct Array mmap; ///< Memory Map
struct Array drives; ///< Drive Information
uint32_t config_table; ///< ROM configuration table
uint32_t boot_loader_name; ///< Boot Loader Name
uint32_t apm_table; ///< APM table
struct Multiboot::VBE vbe; ///< VBE Information
struct Multiboot::Framebuffer framebuffer; ///< Framebuffer information
/*! \brief Check if setting is available
* \param flag Flag to check
* \return `true` if available
*/
bool has(enum Flag flag) const { return (flags & flag) != 0; }
} __attribute__((packed));
assert_size(multiboot_info, 116);
/*! \brief The pointer to the multiboot structures will be assigned in the
* assembler startup code (multiboot.inc)
*/
struct multiboot_info *multiboot_addr = 0;
namespace Multiboot {
Module *getModule(unsigned i) {
if (multiboot_addr != nullptr &&
multiboot_addr->has(multiboot_info::Flag::Modules) &&
i < multiboot_addr->mods.size) {
return i + reinterpret_cast<Module *>(
static_cast<uintptr_t>(multiboot_addr->mods.addr));
} else {
return nullptr;
}
}
unsigned getModuleCount() { return multiboot_addr->mods.size; }
void *Memory::getStartAddress() const {
if (sizeof(void *) == 4 && (addr >> 32) != 0) {
return reinterpret_cast<void *>(addr & 0xffffffff);
} else {
return reinterpret_cast<void *>(static_cast<uintptr_t>(addr));
}
}
void *Memory::getEndAddress() const {
uint64_t end = addr + len;
if (sizeof(void *) == 4 && (end >> 32) != 0) {
return reinterpret_cast<void *>(addr & 0xffffffff);
} else {
return reinterpret_cast<void *>(static_cast<uintptr_t>(end));
}
}
bool Memory::isAvailable() const { return type == AVAILABLE; }
Memory *Memory::getNext() const {
if (multiboot_addr != nullptr &&
multiboot_addr->has(multiboot_info::Flag::MemoryMap)) {
uintptr_t next = reinterpret_cast<uintptr_t>(this) + size + sizeof(size);
if (next < multiboot_addr->mmap.addr + multiboot_addr->mmap.size) {
return reinterpret_cast<Memory *>(next);
}
}
return nullptr;
}
Memory *getMemoryMap() {
if (multiboot_addr != nullptr &&
multiboot_addr->has(multiboot_info::Flag::MemoryMap) &&
multiboot_addr->mmap.size > 0) {
return reinterpret_cast<Memory *>(
static_cast<uintptr_t>(multiboot_addr->mmap.addr));
} else {
return nullptr;
}
}
char *getCommandLine() {
return reinterpret_cast<char *>(
static_cast<uintptr_t>(multiboot_addr->cmdline));
}
char *getBootLoader() {
return reinterpret_cast<char *>(
static_cast<uintptr_t>(multiboot_addr->boot_loader_name));
}
VBE *getVesaBiosExtensionInfo() {
if (multiboot_addr != nullptr &&
multiboot_addr->has(multiboot_info::Flag::VbeInfo)) {
return &(multiboot_addr->vbe);
} else {
return nullptr;
}
}
Framebuffer *getFramebufferInfo() {
if (multiboot_addr != nullptr &&
multiboot_addr->has(multiboot_info::Flag::FramebufferInfo)) {
return &(multiboot_addr->framebuffer);
} else {
return nullptr;
}
}
} // namespace Multiboot

230
boot/multiboot/data.h Normal file
View File

@@ -0,0 +1,230 @@
/*! \file
* \brief \ref Multiboot Interface
*/
#pragma once
#include "../../compiler/fix.h"
#include "../../debug/assert.h"
#include "../../types.h"
/*! \brief Interface for Multiboot
*
* Due to historical reasons, a normal BIOS allows you to do quite an egg dance
* until you finally reach the actual kernel (especially with only 512 bytes
* available in the master boot record...).
* Fortunately, there are [boot loaders](https://wiki.osdev.org/Bootloader) that
* (partly) do this ungrateful job for you:
* They load your kernel into memory, switch (the bootstrap processor) to
* protected mode (32 bit) and jump to the entry point of our kernel -- saving
* you a lot of boring (or enlightening?) work: reading ancient systems
* documentation. One of the most famous representatives is the [Grand Unified
* Bootloader (GRUB)](https://www.gnu.org/software/grub/), which is also the
* reference implementation of the [Multiboot Specification]{@ref multiboot}.
*
* A Multiboot compliant boot loader will prepare the system according to your
* needs and can hand you a lot of useful information (e.g. references to
* initial ramdisks).
*
* However, you have to inform the loader that you are also compliant to the
* specification, and (if required) instruct the loader to adjust specific
* settings (e.g. the graphics mode).
*
* For this purpose you have to configure the beginning of the kernel (the first
* 8192 bytes of the kernel binary) accordingly (see `compiler/section.ld`) --
* this is were the boot loader will search for a magic header and parse the
* subsequent entries containing the desired system configuration.
* In StuBS these flags are set in `boot/multiboot/config.inc` and the header
* structure is generated in `boot/multiboot/header.asm`.
*
* The first step in your \ref startup_bsp() "kernel entry function" is saving
* the pointer to the struct with the information from the boot loader
* (transferred via register `ebx`) -- and \ref Multiboot provides you the
* interface to comfortably access its contents!
*/
namespace Multiboot {
/*! \brief Boot Module
* (also known as `initrd` = initial Ramdisk)
*
* \see [1.7 Boot modules]{@ref multiboot}
* \see [3.3 Boot information format]{@ref multiboot}
*/
class Module {
uint32_t start; ///< Start address
uint32_t end; ///< End address (excluded)
uint32_t cmdline; ///< commandline parameter
uint32_t pad [[maybe_unused]]; ///< alignment; must be 0
public:
/*! \brief Get start of this boot module
* \return Pointer to begin of modules physical address
*/
void* getStartAddress() const {
return reinterpret_cast<void*>(static_cast<uintptr_t>(start));
}
/*! \brief Get end of this boot module
* \return Pointer beyond the modules physical address
*/
void* getEndAddress() const {
return reinterpret_cast<void*>(static_cast<uintptr_t>(end));
}
/*! \brief Get the size of this boot module
* \return Module size in bytes (difference of end and start address)
*/
size_t getSize() const { return static_cast<size_t>(end - start); }
/*! \brief Get the command line for this module
* \return pointer to zero terminated string
*/
char* getCommandLine() const {
return reinterpret_cast<char*>(static_cast<uintptr_t>(cmdline));
}
} __attribute__((packed));
assert_size(Module, 16);
/*! \brief Retrieve a certain boot module
* \param i boot module number
* \return Pointer to structure with boot module information
*/
Module* getModule(unsigned i);
/*! \brief Get the number of modules
* \return Pointer to structure with boot module information
*/
unsigned getModuleCount();
/*! \brief Get the kernel command line
* \return pointer to zero terminated string
*/
char* getCommandLine();
/*! \brief Get the name of the boot loader
* \return pointer to zero terminated string
*/
char* getBootLoader();
/*! \brief Memory Map
*
* The boot loader queries the BIOS for a memory map and stores its result in
* (something like) a linked list. However, this list may not be complete,
* can have contradictory entries and does not take the location of your kernel
* or any boot modules into account.
* (Anyways, it is still the best memory map you will have in StuBS...)
*
* \note Needs to be enabled explicitly by setting the `MULTIBOOT_MEMORY_INFO`
* flag in the multiboot header (see `boot/multiboot/config.inc`)!
*
* \see [Detecting Memory](https://wiki.osdev.org/Detecting_Memory_(x86))
*/
class Memory {
uint32_t size; ///< Size of this entry (can exceed size of the class, rest
///< will be padding bits)
uint64_t addr; ///< Begin of memory area
uint64_t len; ///< length of the memory area
/*! \brief Usage Type
*/
enum Type : uint32_t {
AVAILABLE = 1, ///< Memory is available and usable in kernel
RESERVED = 2, ///< Memory is reserved (without further explanation)
ACPI = 3, ///< Memory may be reclaimed by ACPI
NVS = 4, ///< Memory is non volatile storage for ACPI
BADRAM = 5 ///< Area contains bad memory
} type;
public:
/*! \brief Get start of this memory area
* \return Pointer to begin of the physical address of the memory area
*/
void* getStartAddress() const;
/*! \brief Get end of this memory area
* \return Pointer beyond the physical address of this memory area
*/
void* getEndAddress() const;
/*! \brief Is the memory marked as usable
* \return `true` if available, `false` if not usable.
*/
bool isAvailable() const;
/*! \brief Get the next memory area
* \return pointer to the next memory area entry
*/
Memory* getNext() const;
} __attribute__((packed));
assert_size(Memory, 24);
/*! \brief Retrieve the first entry of the memory map
*/
Memory* getMemoryMap();
/*! \brief Video mode: Vesa BIOS Extension
*
* \see [VESA BIOS Extension (VBE) Core Functions (Version 3)](vbe3.pdf)
*/
struct VBE {
uint32_t control_info; ///< Pointer to VBE control information
uint32_t mode_info; ///< Pointer to VBE mode information
uint16_t mode; ///< Selected video mode (as defined in the standard)
uint16_t interface_seg; ///< Protected mode interface (unused)
uint16_t interface_off; ///< Protected mode interface (unused)
uint16_t interface_len; ///< Protected mode interface (unused)
} __attribute__((packed));
assert_size(VBE, 16);
/*! \brief Get pointer to Vesa BIOS Extension information
*
* \note Only available if the `MULTIBOOT_VIDEO_MODE` flag was explicitly set
* in the multiboot header (see `boot/multiboot/config.inc`)!
*/
VBE* getVesaBiosExtensionInfo();
/*! \brief Video mode: Framebuffer
*
* This beautiful structure contains everything required for using the graphic
* framebuffer in a very handy manner -- however, it may not be well supported
* by current boot loaders...
* These information can be retrieved from \ref VBE as well, though you then
* have to parse these huge structures containing a lot of useless stuff.
*/
struct Framebuffer {
uint64_t address; ///< Physical address of the framebuffer
uint32_t pitch; ///< Number of bytes per row
uint32_t width; ///< Width of framebuffer
uint32_t height; ///< Height of framebuffer
uint8_t bpp; ///< Bits per pixel
enum Type : uint8_t {
INDEXED = 0, ///< Using a custom color palette
RGB = 1, ///< Standard red-green-blue
EGA_TEXT = 2 ///< Enhanced Graphics Adapter color palette
} type;
union {
/*! \brief For INDEXED type
*/
struct {
uint32_t palette_addr; ///< Address of an array with RGB values
uint16_t palette_num_colors; ///< Number of colors (in array above)
} __attribute__((packed));
/*! \brief For RGB type
*/
struct {
uint8_t offset_red; ///< Offset of red value
uint8_t bits_red; ///< Bits used in red value
uint8_t offset_green; ///< Offset of green value
uint8_t bits_green; ///< Bits used in green value
uint8_t offset_blue; ///< Offset of blue value
uint8_t bits_blue; ///< Bits used in blue value
} __attribute__((packed));
} __attribute__((packed));
} __attribute__((packed));
assert_size(Framebuffer, 28);
/*! \brief Get pointer to framebuffer information
*
* \note Only available if the `MULTIBOOT_VIDEO_MODE` flag was explicitly set
* in the multiboot header (see `boot/multiboot/config.inc`)!
*/
Framebuffer* getFramebufferInfo();
} // namespace Multiboot

33
boot/multiboot/header.asm Normal file
View File

@@ -0,0 +1,33 @@
; The first 8192 bytes of the kernel binary must contain a header with
; predefined (and sometimes "magic") values according to the Multiboot standard.
; Based on these values, the boot loader decides whether and how to load the
; kernel -- which is compiled and linked into an ELF file.
; To make this possible with your StuBS kernel, the linker places the following
; entry `multiboot_header` at the very beginning of the file thanks to the
; linker script (located in compiler/sections.ld).
[SECTION .multiboot_header]
; Include configuration
%include 'boot/multiboot/config.inc'
; Multiboot Header
align 4
multiboot_header:
dd MULTIBOOT_HEADER_MAGIC_OS ; Magic Header Value
dd MULTIBOOT_HEADER_FLAGS ; Flags (affects following entries)
dd MULTIBOOT_HEADER_CHKSUM ; Header Checksum
; Following fields would have been required to be defined
; if flag A_OUT KLUDGE was set (but we don't need this)
dd 0 ; Header address
dd 0 ; Begin of load address
dd 0 ; end of load address
dd 0 ; end of bss segment
dd 0 ; address of entry function
; Following fields are required for video mode (flag MULTIBOOT_VIDEO_MODE)
dd 0 ; Mode: 0 = Graphic / 1 = Text
dd MULTIBOOT_VIDEO_WIDTH ; Width (pixels / columns)
dd MULTIBOOT_VIDEO_HEIGHT ; Height (pixels / rows)
dd MULTIBOOT_VIDEO_BITDEPTH ; color depth / number of colors