Initial ramdisk support

- Create initrd library to support definitions and loading
- Allow tools compiled for the host machine to be built by wscript
- Create makerd tool to build initrd from manifest
- Move screenfont to initrd, so don't load framebuffer initially
This commit is contained in:
Justin C. Miller
2018-09-05 22:42:18 -07:00
parent dc40c2f6ad
commit 1758ee4215
20 changed files with 592 additions and 181 deletions

View File

@@ -7,7 +7,7 @@
#define PAGE_SIZE 0x1000
static CHAR16 kernel_name[] = KERNEL_FILENAME;
static CHAR16 font_name[] = KERNEL_FONT;
static CHAR16 initrd_name[] = INITRD_FILENAME;
EFI_STATUS
loader_alloc_pages(
@@ -38,7 +38,7 @@ loader_alloc_pages(
}
EFI_STATUS
loader_load_font(
loader_load_initrd(
EFI_BOOT_SERVICES *bootsvc,
EFI_FILE_PROTOCOL *root,
struct loader_data *data)
@@ -46,13 +46,13 @@ loader_load_font(
EFI_STATUS status;
EFI_FILE_PROTOCOL *file = NULL;
status = root->Open(root, &file, (CHAR16 *)font_name, EFI_FILE_MODE_READ,
status = root->Open(root, &file, (CHAR16 *)initrd_name, EFI_FILE_MODE_READ,
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
if (status == EFI_NOT_FOUND)
return status;
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", font_name);
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", initrd_name);
char info[sizeof(EFI_FILE_INFO) + 100];
size_t info_length = sizeof(info);
@@ -60,16 +60,16 @@ loader_load_font(
status = file->GetInfo(file, &guid_file_info, &info_length, info);
CHECK_EFI_STATUS_OR_RETURN(status, L"Getting file info");
data->font_length = ((EFI_FILE_INFO *)info)->FileSize;
data->initrd_length = ((EFI_FILE_INFO *)info)->FileSize;
status = loader_alloc_pages(
bootsvc,
KERNEL_FONT_MEMTYPE,
&data->font_length,
&data->font);
INITRD_MEMTYPE,
&data->initrd_length,
&data->initrd);
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages");
status = file->Read(file, &data->font_length, data->font);
status = file->Read(file, &data->initrd_length, data->initrd);
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file");
status = file->Close(file);
@@ -217,12 +217,12 @@ loader_load_kernel(
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_elf: %s", kernel_name);
data->font = (void *)((uint64_t)data->kernel + data->kernel_length);
status = loader_load_font(bootsvc, root, data);
data->initrd = (void *)((uint64_t)data->kernel + data->kernel_length);
status = loader_load_initrd(bootsvc, root, data);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", font_name);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", initrd_name);
data->data = (void *)((uint64_t)data->font + data->font_length);
data->data = (void *)((uint64_t)data->initrd + data->initrd_length);
data->data_length += PAGE_SIZE; // extra page for map growth
status = loader_alloc_pages(
bootsvc,

View File

@@ -16,24 +16,20 @@
#define KERNEL_MEMTYPE 0x80000000
#endif
#ifndef KERNEL_FONT_MEMTYPE
#define KERNEL_FONT_MEMTYPE 0x80000001
#ifndef INITRD_MEMTYPE
#define INITRD_MEMTYPE 0x80000001
#endif
#ifndef KERNEL_DATA_MEMTYPE
#define KERNEL_DATA_MEMTYPE 0x80000002
#endif
#ifndef KERNEL_PT_MEMTYPE
#define KERNEL_PT_MEMTYPE 0x80000004
#endif
#ifndef KERNEL_FILENAME
#define KERNEL_FILENAME L"kernel.elf"
#endif
#ifndef KERNEL_FONT
#define KERNEL_FONT L"screenfont.psf"
#ifndef INITRD_FILENAME
#define INITRD_FILENAME L"initrd.img"
#endif
struct loader_data {
@@ -41,8 +37,8 @@ struct loader_data {
void *kernel_entry;
size_t kernel_length;
void *font;
size_t font_length;
void *initrd;
size_t initrd_length;
void *data;
size_t data_length;

View File

@@ -82,9 +82,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
status = loader_load_kernel(bootsvc, &load);
CHECK_EFI_STATUS_OR_FAIL(status);
con_printf(L" image bytes at 0x%x : %x\r\n", load.kernel, load.kernel_length);
con_printf(L" font bytes at 0x%x : %x\r\n", load.font, load.font_length);
con_printf(L" data bytes at 0x%x : %x\r\n", load.data, load.data_length);
con_printf(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel);
con_printf(L" %u initrd bytes at 0x%x\r\n", load.initrd_length, load.initrd);
con_printf(L" %u data bytes at 0x%x\r\n", load.data_length, load.data);
struct kernel_header *version = (struct kernel_header *)load.kernel;
if (version->magic != KERNEL_HEADER_MAGIC) {
@@ -111,9 +111,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
data_header->flags = 0;
data_header->font = load.font;
data_header->font_length = load.font_length;
memory_mark_pointer_fixup((void **)&data_header->font);
data_header->initrd = load.initrd;
data_header->initrd_length = load.initrd_length;
memory_mark_pointer_fixup((void **)&data_header->initrd);
data_header->data = load.data;
data_header->data_length = load.data_length;

View File

@@ -188,7 +188,7 @@ memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
while (d < end) {
switch (d->Type) {
case KERNEL_MEMTYPE:
case KERNEL_FONT_MEMTYPE:
case INITRD_MEMTYPE:
case KERNEL_DATA_MEMTYPE:
d->Attribute |= EFI_MEMORY_RUNTIME;
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;

View File

@@ -16,8 +16,8 @@ struct popcorn_data {
uint32_t _reserved0;
uint32_t flags;
void *font;
size_t font_length;
void *initrd;
size_t initrd_length;
void *data;
size_t data_length;

View File

@@ -41,19 +41,10 @@ struct acpi2_rsdp
uint8_t reserved[3];
} __attribute__ ((packed));
uint8_t
acpi_checksum(const void *p, size_t len, size_t off = 0)
{
uint8_t sum = 0;
const uint8_t *c = reinterpret_cast<const uint8_t *>(p);
for (int i = off; i < len; ++i) sum += c[i];
return sum;
}
bool
acpi_table_header::validate(uint32_t expected_type) const
{
if (acpi_checksum(this, length) != 0) return false;
if (kutil::checksum(this, length) != 0) return false;
return !expected_type || (expected_type == type);
}
@@ -86,7 +77,7 @@ device_manager::device_manager(const void *root_table) :
kassert(acpi1->signature[i] == expected_signature[i],
"ACPI RSDP table signature mismatch");
uint8_t sum = acpi_checksum(acpi1, sizeof(acpi1_rsdp), 0);
uint8_t sum = kutil::checksum(acpi1, sizeof(acpi1_rsdp), 0);
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
@@ -94,7 +85,7 @@ device_manager::device_manager(const void *root_table) :
const acpi2_rsdp *acpi2 =
reinterpret_cast<const acpi2_rsdp *>(acpi1);
sum = acpi_checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
sum = kutil::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
load_xsdt(reinterpret_cast<const acpi_xsdt *>(acpi2->xsdt_address));

View File

@@ -1,13 +1,13 @@
#include <stddef.h>
#include <stdint.h>
#include "initrd/initrd.h"
#include "kutil/assert.h"
#include "kutil/memory.h"
#include "block_device.h"
#include "console.h"
#include "cpu.h"
#include "device_manager.h"
#include "font.h"
#include "gdt.h"
#include "interrupts.h"
#include "io.h"
@@ -29,23 +29,11 @@ extern "C" {
extern void __kernel_assert(const char *, unsigned, const char *);
void
init_console(const popcorn_data *header)
init_console()
{
serial_port *com1 = new (&g_com1) serial_port(COM1);
console *cons = new (&g_console) console(com1);
if (header->frame_buffer) {
screen *s = new screen(
header->frame_buffer,
header->hres,
header->vres,
header->rmask,
header->gmask,
header->bmask);
font *f = new font(header->font);
cons->init_screen(s, f);
}
cons->set_color(0x21, 0x00);
cons->puts("Popcorn OS ");
cons->set_color(0x08, 0x00);
@@ -83,16 +71,20 @@ kernel_main(popcorn_data *header)
&header->frame_buffer,
header->frame_buffer_length);
init_console(header);
init_console();
log::debug(logs::boot, " Popcorn header is at: %016lx", header);
log::debug(logs::boot, " Framebuffer is at: %016lx", header->frame_buffer);
log::debug(logs::boot, " Font data is at: %016lx", header->font);
log::debug(logs::boot, " Kernel data is at: %016lx", header->data);
log::debug(logs::boot, " Memory map is at: %016lx", header->memory_map);
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime);
initrd::disk ird(header->initrd);
log::info(logs::boot, "initrd loaded with %d files.", ird.files().count());
for (auto &f : ird.files())
log::info(logs::boot, " %s (%d bytes).", f.name(), f.size());
/*
pager->dump_pml4(nullptr, 0);
pager->dump_blocks(true);

View File

@@ -15,7 +15,7 @@ def build(bld):
name = 'kernel',
includes = '.',
target = bld.env.KERNEL_FILENAME,
use = 'kutil',
use = ['kutil', 'initrd'],
linkflags = "-T {}".format(lds),
)

View File

@@ -0,0 +1,38 @@
#pragma once
#include <stdint.h>
#include "kutil/enum_bitfields.h"
namespace initrd {
enum class disk_flags : uint16_t
{
};
struct disk_header
{
uint16_t file_count;
disk_flags flags;
uint32_t length;
uint8_t checksum;
uint8_t reserved[3];
} __attribute__ ((packed));
enum class file_flags : uint16_t
{
executable = 0x01
};
struct file_header
{
uint32_t offset;
uint32_t length;
uint16_t name_offset;
file_flags flags;
} __attribute__ ((packed));
} // namepsace initrd
IS_BITFIELD(initrd::disk_flags);
IS_BITFIELD(initrd::file_flags);

View File

@@ -0,0 +1,41 @@
#include "initrd/initrd.h"
#include "kutil/assert.h"
#include "kutil/enum_bitfields.h"
#include "headers.h"
namespace initrd {
file::file(const file_header *header, const void *start) :
m_header(header)
{
m_data = kutil::offset_pointer(start, m_header->offset);
auto *name = kutil::offset_pointer(start, m_header->name_offset);
m_name = reinterpret_cast<const char *>(name);
}
const char * file::name() const { return m_name; }
const size_t file::size() const { return m_header->length; }
const void * file::data() const { return m_data; }
bool
file::executable() const {
return bitfield_has(m_header->flags, file_flags::executable);
}
disk::disk(const void *start)
{
auto *header = reinterpret_cast<const disk_header *>(start);
size_t length = header->length;
uint8_t sum = kutil::checksum(start, length);
kassert(sum == 0, "initrd checksum failed");
auto *files = reinterpret_cast<const file_header *>(header + 1);
m_files.ensure_capacity(header->file_count);
for (int i = 0; i < header->file_count; ++i) {
m_files.emplace(&files[i], start);
}
}
} // namespace initrd

View File

@@ -0,0 +1,61 @@
#pragma once
/// \file initrd.h
/// Definitions defining the simple inital ramdisk file format used by the
/// popcorn kernel.
#include <stdint.h>
#include "kutil/vector.h"
// File format:
// 1x disk_header
// Nx file_header
// filename string data
// file data
namespace initrd {
struct disk_header;
struct file_header;
/// Encasulates methods for a file on the ramdisk
class file
{
public:
file(const file_header *header, const void *start);
/// Get the filename
const char * name() const;
/// Get the file size
const size_t size() const;
/// Get a pointer to the file data
const void * data() const;
/// Whether this file is an executable
bool executable() const;
private:
const file_header *m_header;
void const *m_data;
char const *m_name;
};
/// Encasulates access methods for the ramdisk
class disk
{
public:
/// Constructor.
/// \arg start The start of the initrd in memory
disk(const void *start);
/// Get the vector of files on the disk
const kutil::vector<file> & files() const { return m_files; }
private:
kutil::vector<file> m_files;
};
} // namespace initrd

View File

@@ -0,0 +1,14 @@
def configure(ctx):
pass
def build(bld):
sources = bld.path.ant_glob("**/*.cpp")
bld.stlib(
source = sources,
name = 'initrd',
target = 'initrd',
)
# vim: ft=python et

View File

@@ -25,4 +25,13 @@ memcpy(void *dest, void *src, size_t n)
return d;
}
uint8_t
checksum(const void *p, size_t len, size_t off)
{
uint8_t sum = 0;
const uint8_t *c = reinterpret_cast<const uint8_t *>(p);
for (int i = off; i < len; ++i) sum += c[i];
return sum;
}
} // namespace kutil

View File

@@ -66,4 +66,10 @@ inline T* mask_pointer(T *p, addr_t mask)
return reinterpret_cast<T *>(reinterpret_cast<addr_t>(p) & ~mask);
}
/// Do a simple byte-wise checksum of an area of memory.
/// \arg p The start of the memory region
/// \arg len The number of bytes in the region
/// \arg off An optional offset into the region
uint8_t checksum(const void *p, size_t len, size_t off = 0);
} // namespace kutil

View File

@@ -0,0 +1,10 @@
#include "entry.h"
entry::entry(const std::string &in, const std::string &out) :
m_in(in), m_out(out), m_file(in, std::ios_base::binary)
{
m_file.seekg(0, std::ios_base::end);
m_size = m_file.tellg();
m_file.seekg(0);
}

23
src/tools/makerd/entry.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <fstream>
class entry
{
public:
entry(const std::string &in, const std::string &out);
inline const std::string & in() const { return m_in; }
inline const std::string & out() const { return m_out; }
inline const std::ifstream & file() const { return m_file; }
inline size_t size() const { return m_size; }
inline bool good() const { return m_file.good(); }
private:
std::string m_in;
std::string m_out;
std::ifstream m_file;
size_t m_size;
};

115
src/tools/makerd/main.cpp Normal file
View File

@@ -0,0 +1,115 @@
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include "initrd/headers.h"
#include "entry.h"
int main(int argc, char **argv)
{
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <manifest> <output>" << std::endl;
return 1;
}
std::vector<entry> entries;
std::ifstream manifest(argv[1]);
size_t files_size = 0;
size_t names_size = 0;
while (manifest.good()) {
std::string in, out;
manifest >> in;
if (in.length() < 1)
continue;
manifest >> out;
if (in.front() == '#')
continue;
entries.emplace_back(in, out);
const entry &e = entries.back();
if (!e.good()) {
std::cerr << "Error reading file: " << in << std::endl;
return 1;
}
files_size += e.size();
names_size += out.length() + 1;
}
std::fstream out(argv[2],
std::ios_base::binary |
std::ios_base::trunc |
std::ios_base::out |
std::ios_base::in);
if (!out.good()) {
std::cerr << "Error opening file: " << argv[2] << std::endl;
return 1;
}
uint32_t total_length =
sizeof(initrd::disk_header) +
sizeof(initrd::file_header) * entries.size() +
names_size + files_size;
initrd::disk_header dheader;
std::memset(&dheader, 0, sizeof(dheader));
dheader.file_count = entries.size();
dheader.length = total_length;
out.write(reinterpret_cast<const char *>(&dheader), sizeof(dheader));
size_t name_offset =
sizeof(initrd::disk_header) +
sizeof(initrd::file_header) * entries.size();
size_t file_offset = name_offset + names_size;
for (auto &e : entries) {
initrd::file_header fheader;
std::memset(&fheader, 0, sizeof(fheader));
fheader.offset = file_offset;
fheader.length = e.size();
fheader.name_offset = name_offset;
out.write(
reinterpret_cast<const char *>(&fheader),
sizeof(fheader));
name_offset += e.out().length() + 1;
file_offset += e.size();
}
for (auto &e : entries) {
out.write(e.out().c_str(), e.out().length() + 1);
}
for (auto &e : entries) {
out << e.file().rdbuf();
}
char buffer[1024];
uint8_t checksum = 0;
out.seekg(0);
while (true) {
size_t n = out.readsome(&buffer[0], 1024);
if (n == 0 || out.eof()) break;
for (size_t i=0; i<n; ++i)
checksum += static_cast<uint8_t>(buffer[i]);
}
dheader.checksum = static_cast<uint8_t>(0 - checksum);
out.seekp(0);
out.write(reinterpret_cast<const char *>(&dheader), sizeof(dheader));
out.close();
return 0;
}

15
src/tools/makerd/wscript Normal file
View File

@@ -0,0 +1,15 @@
def configure(ctx):
pass
def build(bld):
sources = bld.path.ant_glob("**/*.cpp")
bld.program(
source = sources,
name = 'makerd',
target = 'makerd',
use = 'initrd',
)
# vim: ft=python et