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:
Justin C. Miller
2020-05-10 01:42:22 -07:00
parent 9aa749e877
commit 10c8f6e4b5
11 changed files with 191 additions and 102 deletions

View File

@@ -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

View File

@@ -1,3 +1,5 @@
/// \file elf.h
/// Definitions and related constants for ELF64 structures
#pragma once
#include <stdint.h>

View File

@@ -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); \

View File

@@ -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

View File

@@ -1,3 +1,5 @@
/// \file hardware.h
/// Functions and definitions for detecting and dealing with hardware
#pragma once
#include <uefi/tables.h>

View File

@@ -10,8 +10,6 @@
namespace boot {
namespace loader {
using memory::offset_ptr;
static bool
is_elfheader_valid(const elf::header *header)
{

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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)
{

View File

@@ -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

View 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