Handout
This commit is contained in:
22
boot/multiboot/config.inc
Normal file
22
boot/multiboot/config.inc
Normal 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
167
boot/multiboot/data.cc
Normal 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
230
boot/multiboot/data.h
Normal 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
33
boot/multiboot/header.asm
Normal 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
|
||||
Reference in New Issue
Block a user