Clean and document header files.
- Add missing doc comments to header files - Move allocate_kernel_args to main.cpp - Split functions out into pointer_manipulation.h
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
/// \file console.h
|
||||
/// Text output and status message handling
|
||||
#pragma once
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
@@ -6,6 +8,7 @@
|
||||
|
||||
namespace boot {
|
||||
|
||||
/// Object providing basic output functionality to the UEFI console
|
||||
class console
|
||||
{
|
||||
public:
|
||||
@@ -32,16 +35,30 @@ private:
|
||||
static console *s_console;
|
||||
};
|
||||
|
||||
/// Scoped status line reporter. Prints a message and an "OK" if no errors
|
||||
/// or warnings were reported before destruction, otherwise reports the
|
||||
/// error or warning.
|
||||
class status_line
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg message Description of the operation in progress
|
||||
/// \arg context If non-null, printed after `message` and a colon
|
||||
status_line(const wchar_t *message, const wchar_t *context = nullptr);
|
||||
~status_line();
|
||||
|
||||
/// Set the state to warning, and print a message. If the state is already at
|
||||
/// warning or error, the state is unchanged but the message is still printed.
|
||||
/// \arg message The warning message to print
|
||||
/// \arg error If non-null, printed after `message`
|
||||
inline static void warn(const wchar_t *message, const wchar_t *error = nullptr) {
|
||||
if (s_current) s_current->do_warn(message, error);
|
||||
}
|
||||
|
||||
/// Set the state to error, and print a message. If the state is already at
|
||||
/// error, the state is unchanged but the message is still printed.
|
||||
/// \arg message The error message to print
|
||||
/// \arg error If non-null, printed after `message`
|
||||
inline static void fail(const wchar_t *message, const wchar_t *error = nullptr) {
|
||||
if (s_current) s_current->do_fail(message, error);
|
||||
}
|
||||
@@ -60,15 +77,4 @@ private:
|
||||
static status_line *s_current;
|
||||
};
|
||||
|
||||
uefi::status
|
||||
con_get_framebuffer(
|
||||
uefi::boot_services *bs,
|
||||
void **buffer,
|
||||
size_t *buffer_size,
|
||||
uint32_t *hres,
|
||||
uint32_t *vres,
|
||||
uint32_t *rmask,
|
||||
uint32_t *gmask,
|
||||
uint32_t *bmask);
|
||||
|
||||
} // namespace boot
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/// \file elf.h
|
||||
/// Definitions and related constants for ELF64 structures
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/// \file error.h
|
||||
/// Error handling definitions
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -30,6 +32,8 @@ private:
|
||||
static handler *s_current;
|
||||
};
|
||||
|
||||
/// Error handler using UEFI boot services. Integrates with `status_line`
|
||||
/// to print formatted error messages to the screen.
|
||||
class uefi_handler :
|
||||
public handler
|
||||
{
|
||||
@@ -41,6 +45,8 @@ private:
|
||||
console &m_con;
|
||||
};
|
||||
|
||||
/// Error handler that doesn't rely on UEFI. Sets status into CPU
|
||||
/// registers and then causes a CPU #DE exception.
|
||||
class cpu_assert_handler :
|
||||
public handler
|
||||
{
|
||||
@@ -52,6 +58,9 @@ public:
|
||||
} // namespace error
|
||||
} // namespace boot
|
||||
|
||||
/// Helper macro to raise an error if an operation fails.
|
||||
/// \arg s An expression evaluating to a UEFI status
|
||||
/// \arg m The error message to use on failure
|
||||
#define try_or_raise(s, m) \
|
||||
do { \
|
||||
uefi::status _s = (s); \
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/// \file fs.h
|
||||
/// Definitions for dealing with UEFI's disk access functions
|
||||
#pragma once
|
||||
|
||||
#include <uefi/types.h>
|
||||
@@ -7,6 +9,7 @@
|
||||
namespace boot {
|
||||
namespace fs {
|
||||
|
||||
/// A file or directory in a filesystem.
|
||||
class file
|
||||
{
|
||||
public:
|
||||
@@ -14,7 +17,15 @@ public:
|
||||
file(file &o);
|
||||
~file();
|
||||
|
||||
/// Open another file or directory, relative to this one.
|
||||
/// \arg path Relative path to the target file from this one
|
||||
file open(const wchar_t *path);
|
||||
|
||||
/// Load the contents of this file into memory.
|
||||
/// \arg out_size _out:_ The number of bytes loaded
|
||||
/// \arg mem_type The UEFI memory type to use for allocation
|
||||
/// \returns A pointer to the loaded memory. Memory will be
|
||||
/// page-aligned.
|
||||
void * load(
|
||||
size_t *out_size,
|
||||
uefi::memory_type mem_type = uefi::memory_type::loader_data);
|
||||
@@ -28,6 +39,8 @@ private:
|
||||
uefi::boot_services *m_bs;
|
||||
};
|
||||
|
||||
/// Get the filesystem this loader was loaded from.
|
||||
/// \returns A `file` object representing the root directory of the volume
|
||||
file get_boot_volume(uefi::handle image, uefi::boot_services *bs);
|
||||
|
||||
} // namespace fs
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/// \file hardware.h
|
||||
/// Functions and definitions for detecting and dealing with hardware
|
||||
#pragma once
|
||||
|
||||
#include <uefi/tables.h>
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
namespace boot {
|
||||
namespace loader {
|
||||
|
||||
using memory::offset_ptr;
|
||||
|
||||
static bool
|
||||
is_elfheader_valid(const elf::header *header)
|
||||
{
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
/// \file loader.h
|
||||
/// Definitions for loading the kernel into memory
|
||||
#pragma once
|
||||
|
||||
namespace boot {
|
||||
namespace loader {
|
||||
|
||||
/// Structure to hold information about loaded binary image.
|
||||
struct loaded_elf
|
||||
{
|
||||
void *data;
|
||||
uintptr_t vaddr;
|
||||
uintptr_t entrypoint;
|
||||
void *data; ///< Start of the kernel in memory
|
||||
uintptr_t vaddr; ///< Virtual address to map to
|
||||
uintptr_t entrypoint; ///< (Virtual) address of the kernel entrypoint
|
||||
};
|
||||
|
||||
/// Parse and load an ELF file in memory into a loaded image.
|
||||
/// \arg data The start of the ELF file in memory
|
||||
/// \arg size The size of the ELF file in memory
|
||||
/// \returns A `loaded_elf` structure defining the loaded image
|
||||
loaded_elf load(const void *data, size_t size, uefi::boot_services *bs);
|
||||
|
||||
} // namespace loader
|
||||
|
||||
@@ -85,6 +85,42 @@ detect_debug_mode(EFI_RUNTIME_SERVICES *run, kernel_args *header) {
|
||||
}
|
||||
*/
|
||||
|
||||
/// Allocate space for kernel args. Allocates enough space from pool
|
||||
/// memory for the args header and `max_modules` module headers.
|
||||
kernel::args::header *
|
||||
allocate_args_structure(
|
||||
uefi::boot_services *bs,
|
||||
size_t max_modules)
|
||||
{
|
||||
status_line status(L"Setting up kernel args memory");
|
||||
|
||||
kernel::args::header *args = nullptr;
|
||||
|
||||
size_t args_size =
|
||||
sizeof(kernel::args::header) + // The header itself
|
||||
max_modules * sizeof(kernel::args::module); // The module structures
|
||||
|
||||
try_or_raise(
|
||||
bs->allocate_pool(memory::args_type, args_size,
|
||||
reinterpret_cast<void**>(&args)),
|
||||
L"Could not allocate argument memory");
|
||||
|
||||
bs->set_mem(args, args_size, 0);
|
||||
|
||||
args->modules =
|
||||
reinterpret_cast<kernel::args::module*>(args + 1);
|
||||
args->num_modules = 0;
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// Load a file from disk into memory. Also adds an entry to the kernel
|
||||
/// args module headers pointing at the loaded data.
|
||||
/// \arg disk The opened UEFI filesystem to load from
|
||||
/// \arg args The kernel args header to update with module information
|
||||
/// \arg name Name of the module (informational only)
|
||||
/// \arg path Path on `disk` of the file to load
|
||||
/// \arg type Type specifier of this module (eg, initrd or kernel)
|
||||
kernel::args::module *
|
||||
load_module(
|
||||
fs::file &disk,
|
||||
@@ -104,6 +140,9 @@ load_module(
|
||||
return &module;
|
||||
}
|
||||
|
||||
/// The main procedure for the portion of the loader that runs while
|
||||
/// UEFI is still in control of the machine. (ie, while the loader still
|
||||
/// has access to boot services.
|
||||
loader::loaded_elf
|
||||
bootloader_main_uefi(uefi::handle image, uefi::system_table *st, console &con)
|
||||
{
|
||||
@@ -115,7 +154,7 @@ bootloader_main_uefi(uefi::handle image, uefi::system_table *st, console &con)
|
||||
memory::init_pointer_fixup(bs, rs);
|
||||
|
||||
kernel::args::header *args =
|
||||
memory::allocate_args_structure(bs, max_modules);
|
||||
allocate_args_structure(bs, max_modules);
|
||||
|
||||
args->magic = kernel::args::magic;
|
||||
args->version = kernel::args::version;
|
||||
@@ -238,6 +277,7 @@ bootloader_main_uefi(uefi::handle image, uefi::system_table *st, console &con)
|
||||
*/
|
||||
} // namespace boot
|
||||
|
||||
/// The UEFI entrypoint for the loader.
|
||||
extern "C" uefi::status
|
||||
efi_main(uefi::handle image_handle, uefi::system_table *st)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include <stddef.h>
|
||||
#include <uefi/types.h>
|
||||
#include "kernel_args.h"
|
||||
|
||||
#include "error.h"
|
||||
#include "console.h"
|
||||
#include "error.h"
|
||||
#include "memory.h"
|
||||
|
||||
namespace boot {
|
||||
@@ -146,31 +147,6 @@ memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
|
||||
}
|
||||
*/
|
||||
|
||||
kernel::args::header *
|
||||
allocate_args_structure(uefi::boot_services *bs, size_t max_modules)
|
||||
{
|
||||
status_line status(L"Setting up kernel args memory");
|
||||
|
||||
kernel::args::header *args = nullptr;
|
||||
|
||||
size_t args_size =
|
||||
sizeof(kernel::args::header) + // The header itself
|
||||
max_modules * sizeof(kernel::args::module); // The module structures
|
||||
|
||||
try_or_raise(
|
||||
bs->allocate_pool(args_type, args_size,
|
||||
reinterpret_cast<void**>(&args)),
|
||||
L"Could not allocate argument memory");
|
||||
|
||||
bs->set_mem(args, args_size, 0);
|
||||
|
||||
args->modules =
|
||||
reinterpret_cast<kernel::args::module*>(args + 1);
|
||||
args->num_modules = 0;
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
efi_mem_map
|
||||
get_mappings(uefi::boot_services *bs)
|
||||
{
|
||||
|
||||
@@ -1,88 +1,83 @@
|
||||
/// \file memory.h
|
||||
/// Memory-related constants and functions.
|
||||
#pragma once
|
||||
#include <uefi/boot_services.h>
|
||||
#include <uefi/runtime_services.h>
|
||||
#include <stdint.h>
|
||||
#include "kernel_args.h"
|
||||
#include "pointer_manipulation.h"
|
||||
|
||||
namespace boot {
|
||||
namespace memory {
|
||||
|
||||
/// UEFI specifies that pages are always 4 KiB.
|
||||
constexpr size_t page_size = 0x1000;
|
||||
|
||||
constexpr uefi::memory_type args_type = static_cast<uefi::memory_type>(0x80000000);
|
||||
constexpr uefi::memory_type module_type = static_cast<uefi::memory_type>(0x80000001);
|
||||
constexpr uefi::memory_type kernel_type = static_cast<uefi::memory_type>(0x80000002);
|
||||
constexpr uefi::memory_type table_type = static_cast<uefi::memory_type>(0x80000003);
|
||||
|
||||
template <typename T, typename S>
|
||||
static T* offset_ptr(S* input, ptrdiff_t offset) {
|
||||
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
|
||||
}
|
||||
|
||||
/// Get the number of pages needed to hold `bytes` bytes
|
||||
inline constexpr size_t bytes_to_pages(size_t bytes) {
|
||||
return ((bytes - 1) / page_size) + 1;
|
||||
}
|
||||
|
||||
void init_pointer_fixup(
|
||||
uefi::boot_services *bs,
|
||||
uefi::runtime_services *rs);
|
||||
/// \defgroup memory_types
|
||||
/// Custom UEFI memory type values used for data being passed to the kernel
|
||||
/// @{
|
||||
|
||||
/// Memory containing the kernel args structure
|
||||
constexpr uefi::memory_type args_type =
|
||||
static_cast<uefi::memory_type>(0x80000000);
|
||||
|
||||
/// Memory containing any loaded modules to be passed to the kernel
|
||||
constexpr uefi::memory_type module_type =
|
||||
static_cast<uefi::memory_type>(0x80000001);
|
||||
|
||||
/// Memory containing loaded kernel code and data sections
|
||||
constexpr uefi::memory_type kernel_type =
|
||||
static_cast<uefi::memory_type>(0x80000002);
|
||||
|
||||
/// Memory containing page tables set up by the loader
|
||||
constexpr uefi::memory_type table_type =
|
||||
static_cast<uefi::memory_type>(0x80000003);
|
||||
|
||||
/// @}
|
||||
|
||||
/// \defgroup pointer_fixup
|
||||
/// Memory virtualization pointer fixup functions. Handles changing affected pointers
|
||||
/// when calling UEFI's `set_virtual_address_map` function to change the location of
|
||||
/// runtime services in virtual memory.
|
||||
/// @{
|
||||
|
||||
/// Set up the pointer fixup UEFI events. This registers the necessary callbacks for
|
||||
/// runtime services to call when `set_virtual_address_map` is called.
|
||||
void init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs);
|
||||
|
||||
/// Mark a given pointer as needing to be updated when doing pointer fixup.
|
||||
void mark_pointer_fixup(void **p);
|
||||
|
||||
kernel::args::header * allocate_args_structure(uefi::boot_services *bs, size_t max_modules);
|
||||
|
||||
template <typename T>
|
||||
class offset_iterator
|
||||
{
|
||||
T* m_t;
|
||||
size_t m_off;
|
||||
public:
|
||||
offset_iterator(T* t, size_t offset=0) : m_t(t), m_off(offset) {}
|
||||
|
||||
T* operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
|
||||
T* operator++(int) { T* tmp = m_t; operator++(); return tmp; }
|
||||
bool operator==(T* p) { return p == m_t; }
|
||||
|
||||
T* operator*() const { return m_t; }
|
||||
operator T*() const { return m_t; }
|
||||
T* operator->() const { return m_t; }
|
||||
};
|
||||
/// @}
|
||||
|
||||
/// Struct that represents UEFI's memory map. Contains a pointer to the map data
|
||||
/// as well as the data on how to read it.
|
||||
struct efi_mem_map {
|
||||
size_t length;
|
||||
size_t size;
|
||||
size_t key;
|
||||
uint32_t version;
|
||||
uefi::memory_descriptor *entries;
|
||||
using desc = uefi::memory_descriptor;
|
||||
using iterator = offset_iterator<desc>;
|
||||
|
||||
size_t length; ///< Total length of the map data
|
||||
size_t size; ///< Size of an entry in the array
|
||||
size_t key; ///< Key for detecting changes
|
||||
uint32_t version; ///< Version of the `memory_descriptor` struct
|
||||
desc *entries; ///< The array of UEFI descriptors
|
||||
|
||||
/// Get the count of entries in the array
|
||||
inline size_t num_entries() const { return length / size; }
|
||||
|
||||
inline uefi::memory_descriptor * operator[](size_t i) {
|
||||
size_t offset = i * size;
|
||||
if (offset > length) return nullptr;
|
||||
return offset_ptr<uefi::memory_descriptor>(entries, offset);
|
||||
}
|
||||
/// Return an iterator to the beginning of the array
|
||||
iterator begin() { return iterator(entries, size); }
|
||||
|
||||
offset_iterator<uefi::memory_descriptor> begin() { return offset_iterator<uefi::memory_descriptor>(entries, size); }
|
||||
offset_iterator<uefi::memory_descriptor> end() { return offset_ptr<uefi::memory_descriptor>(entries, length); }
|
||||
/// Return an iterator to the end of the array
|
||||
iterator end() { return offset_ptr<desc>(entries, length); }
|
||||
};
|
||||
|
||||
/// Get the memory map from UEFI.
|
||||
efi_mem_map get_mappings(uefi::boot_services *bs);
|
||||
|
||||
enum class memory_type
|
||||
{
|
||||
free,
|
||||
loader_used,
|
||||
system_used
|
||||
};
|
||||
|
||||
/*
|
||||
EFI_STATUS memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size);
|
||||
EFI_STATUS memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map);
|
||||
EFI_STATUS memory_dump_map(struct memory_map *map);
|
||||
|
||||
void memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map);
|
||||
*/
|
||||
|
||||
} // namespace boot
|
||||
} // namespace memory
|
||||
|
||||
41
src/boot/pointer_manipulation.h
Normal file
41
src/boot/pointer_manipulation.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/// \file pointer_manipulation.h
|
||||
/// Helper functions and types for doing type-safe byte-wise pointer math.
|
||||
#pragma once
|
||||
|
||||
namespace boot {
|
||||
|
||||
/// Return a pointer offset from `input` by `offset` bytes.
|
||||
/// \tparam T Cast the return value to a pointer to `T`
|
||||
/// \tparam S The type pointed to by the `input` pointer
|
||||
template <typename T, typename S>
|
||||
inline T* offset_ptr(S* input, ptrdiff_t offset) {
|
||||
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
|
||||
}
|
||||
|
||||
/// Iterator for an array of `T` whose size is known at runtime
|
||||
/// \tparam T Type of the objects in the array, whose size might not be
|
||||
/// what is returned by sizeof(T).
|
||||
template <typename T>
|
||||
class offset_iterator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg t Pointer to the first item in the array
|
||||
/// \arg off Offset applied to reach successive items. Default is 0,
|
||||
/// which creates an effectively constant iterator.
|
||||
offset_iterator(T* t, size_t off=0) : m_t(t), m_off(off) {}
|
||||
|
||||
T* operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
|
||||
T* operator++(int) { T* tmp = m_t; operator++(); return tmp; }
|
||||
bool operator==(T* p) { return p == m_t; }
|
||||
|
||||
T* operator*() const { return m_t; }
|
||||
operator T*() const { return m_t; }
|
||||
T* operator->() const { return m_t; }
|
||||
|
||||
private:
|
||||
T* m_t;
|
||||
size_t m_off;
|
||||
};
|
||||
|
||||
} // namespace boot
|
||||
Reference in New Issue
Block a user