Skip to content

Read config file snippets from limine.d #496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
*.dtb
*~

# Keep limine.d, since the .d conflicts with the '*.d' rule
!test/limine.d

/bin
/build
/toolchain-files
Expand Down
7 changes: 7 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ candidates in subsequent partitions or directories are ignored.
It is thus imperative that the intended config file is placed in a location
that will not be shadowed by another candidate config file.

## Split config files

Limine allows loading multiple additional config file snippets from a directory
named `limine.d/`. All files in that directory will be appended to
`limine.conf`, sorted by their file name in ascending order.
`limine.d/` has to be located in the same directory as `limine.conf`.

## Structure of the config file

The Limine configuration file is comprised of *menu entries* and *options*.
Expand Down
136 changes: 136 additions & 0 deletions common/fs/fat32.s2.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ struct fat32_directory_entry {
uint32_t file_size_bytes;
} __attribute__((packed));

struct fat32_directory_handle {
struct fat32_context context;
struct fat32_directory_entry entry;
};

struct fat32_lfn_entry {
uint8_t sequence_number;
char name1[10];
Expand Down Expand Up @@ -453,6 +458,7 @@ char *fat32_get_label(struct volume *part) {

static void fat32_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count);
static void fat32_close(struct file_handle *file);
static struct dir_entry *fat32_readdir(struct file_handle *file, size_t *out_size);

struct file_handle *fat32_open(struct volume *part, const char *path) {
struct fat32_context context;
Expand Down Expand Up @@ -515,6 +521,22 @@ struct file_handle *fat32_open(struct volume *part, const char *path) {
if (expect_directory) {
_current_directory = current_file;
current_directory = &_current_directory;

if (path[current_index] == 0) {
struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
struct fat32_directory_handle *ret = ext_mem_alloc(sizeof(struct fat32_directory_handle));

ret->context = context;
ret->entry = _current_directory;

handle->fd = (void *)ret;
handle->readdir = (void *)fat32_readdir;
handle->vol = part;
#if defined (UEFI)
handle->efi_part_handle = part->efi_part_handle;
#endif
return handle;
}
} else {
struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
struct fat32_file_handle *ret = ext_mem_alloc(sizeof(struct fat32_file_handle));
Expand Down Expand Up @@ -551,3 +573,117 @@ static void fat32_close(struct file_handle *file) {
pmm_free(f->cluster_chain, f->chain_len * sizeof(uint32_t));
pmm_free(f, sizeof(struct fat32_file_handle));
}

static struct dir_entry *fat32_readdir(struct file_handle *file, size_t *out_size) {
if (!file)
return NULL;

struct fat32_directory_handle *d = file->fd;
uint32_t current_cluster_number = d->entry.cluster_num_low;
if (d->context.type == 32)
current_cluster_number |= (uint32_t)d->entry.cluster_num_high << 16;

size_t dir_chain_len = 0;
uint32_t *directory_cluster_chain = cache_cluster_chain(&d->context, current_cluster_number, &dir_chain_len);

if (directory_cluster_chain == NULL)
return NULL;

struct fat32_directory_entry *directory_entries;
size_t block_size = d->context.sectors_per_cluster * d->context.bytes_per_sector;
directory_entries = ext_mem_alloc(dir_chain_len * block_size);
read_cluster_chain(&d->context, directory_cluster_chain, directory_entries, 0, dir_chain_len * block_size);
char current_lfn[FAT32_LFN_MAX_FILENAME_LENGTH] = {0};

const size_t num_entries = (dir_chain_len * block_size) / sizeof(struct fat32_directory_entry);
struct dir_entry *buffer = ext_mem_alloc(num_entries * sizeof(struct dir_entry));
size_t buffer_idx = 0;

for (size_t i = 0; i < num_entries; i++) {
if (directory_entries[i].file_name_and_ext[0] == 0x00) {
// no more entries here
break;
}

if (directory_entries[i].attribute == FAT32_LFN_ATTRIBUTE) {
struct fat32_lfn_entry* lfn = (struct fat32_lfn_entry*) &directory_entries[i];

if (lfn->sequence_number & 0b01000000) {
// this lfn is the first entry in the table, clear the lfn buffer
memset(current_lfn, ' ', sizeof(current_lfn));
}

const unsigned int lfn_index = ((lfn->sequence_number & 0b00011111) - 1U) * 13U;
if (lfn_index >= FAT32_LFN_MAX_ENTRIES * 13) {
continue;
}

fat32_lfncpy(current_lfn + lfn_index + 00, lfn->name1, 5);
fat32_lfncpy(current_lfn + lfn_index + 05, lfn->name2, 6);
fat32_lfncpy(current_lfn + lfn_index + 11, lfn->name3, 2);

if (lfn_index != 0)
continue;

// Remove trailing spaces.
for (int j = SIZEOF_ARRAY(current_lfn) - 2; j >= -1; j--) {
if (j == -1 || current_lfn[j] != ' ') {
current_lfn[j + 1] = 0;
break;
}
}

continue;
}

if (directory_entries[i].attribute & (1 << 3)) {
// It is a volume label, skip
continue;
}

if (i > 0 && directory_entries[i - 1].attribute == FAT32_LFN_ATTRIBUTE) {
memcpy(buffer[buffer_idx].name, current_lfn, sizeof(current_lfn));
} else {
// SFN
char sfn[8 + 1 + 3 + 1]; // 8 + '.' + 3 + NUL
size_t sfn_idx = 8;
memcpy(sfn, directory_entries[i].file_name_and_ext, 8);
// Remove trailing spaces.
for (int j = 7; j >= 0; j--) {
if (sfn[j] == ' ') {
sfn[j] = 0;
sfn_idx = j;
continue;
}
break;
}
// Check if we have a filename extension.
if (directory_entries[i].file_name_and_ext[8] != ' ') {
sfn[sfn_idx++] = '.';
memcpy(sfn + sfn_idx, directory_entries[i].file_name_and_ext + 8, 3);
for (int j = 2; j >= 0; j--) {
if (sfn[sfn_idx + j] == ' ') {
sfn[sfn_idx + j] = 0;
continue;
}
break;
}
}
memcpy(buffer[buffer_idx].name, sfn, sizeof(sfn));
}

if (directory_entries[i].attribute == FAT32_ATTRIBUTE_SUBDIRECTORY) {
buffer[buffer_idx].type = DIR_ENTRY_TYPE_DIRECTORY;
} else {
buffer[buffer_idx].type = DIR_ENTRY_TYPE_FILE;
}

buffer_idx++;
}

pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));

*out_size = buffer_idx;

return buffer;
}
13 changes: 13 additions & 0 deletions common/fs/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ extern bool case_insensitive_fopen;
bool fs_get_guid(struct guid *guid, struct volume *part);
char *fs_get_label(struct volume *part);

enum {
DIR_ENTRY_TYPE_FILE,
DIR_ENTRY_TYPE_DIRECTORY,
};

#define DIR_ENTRY_NAME_LEN 384
struct dir_entry {
char name[DIR_ENTRY_NAME_LEN];
int type;
};

struct file_handle {
bool is_memfile;
bool readall;
Expand All @@ -23,6 +34,7 @@ struct file_handle {
void *fd;
void (*read)(void *fd, void *buf, uint64_t loc, uint64_t count);
void (*close)(void *fd);
void *(*readdir)(void *fd, size_t *out_size);
uint64_t size;
#if defined (UEFI)
EFI_HANDLE efi_part_handle;
Expand All @@ -35,6 +47,7 @@ struct file_handle {
struct file_handle *fopen(struct volume *part, const char *filename);
void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count);
void fclose(struct file_handle *fd);
struct dir_entry *freaddir(struct file_handle *fd, size_t *out_size);
void *freadall(struct file_handle *fd, uint32_t type);
void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs
#if defined (__i386__)
Expand Down
8 changes: 8 additions & 0 deletions common/fs/file.s2.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count) {
}
}

struct dir_entry *freaddir(struct file_handle *fd, size_t *out_size) {
if (!fd || !fd->readdir) {
return NULL;
}

return fd->readdir(fd, out_size);
}

void *freadall(struct file_handle *fd, uint32_t type) {
return freadall_mode(fd, type, false
#if defined (__i386__)
Expand Down
59 changes: 57 additions & 2 deletions common/lib/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ no_unwind bool bad_config = false;
static char *config_addr;

int init_config_disk(struct volume *part) {
struct file_handle *f;
struct file_handle *f; // The config file
struct file_handle *dir; // The limine.d directory
struct file_handle **files; // Files inside the limine.d directory
size_t num_files = 0; // Amount of files inside limine.d
size_t config_size = 0;

bool old_cif = case_insensitive_fopen;
case_insensitive_fopen = true;
Expand All @@ -49,11 +53,62 @@ int init_config_disk(struct volume *part) {

opened:
case_insensitive_fopen = old_cif;
config_size = f->size + 2;

// Get the path of the loaded config file so we can get limine.d/ from the same directory.
char* loaded_dir = ext_mem_alloc(f->path_len);
memcpy(loaded_dir, f->path, f->path_len);
for (int i = f->path_len; i >= 0; i--) {
if (loaded_dir[i] == '/') {
memcpy(loaded_dir + i, "/limine.d/", 11);
break;
}
}

// Open the directory and get the amount of memory we need to allocate.
if ((dir = fopen(part, loaded_dir)) != NULL) {
size_t num_entries;
struct dir_entry *dir_entries = freaddir(dir, &num_entries);
files = ext_mem_alloc(num_entries * sizeof(struct file_handle *));
for (size_t i = 0; i < num_entries; i++) {
if (dir_entries[i].type == DIR_ENTRY_TYPE_FILE) {
// Build an absolute path.
char *file_path = ext_mem_alloc(strlen(loaded_dir) + strlen(dir_entries[i].name));
memcpy(file_path, loaded_dir, strlen(loaded_dir));
memcpy(file_path + strlen(loaded_dir), dir_entries[i].name, strlen(dir_entries[i].name));

struct file_handle *entry = fopen(part, file_path);
if (entry) {
config_size += entry->size;
files[num_files++] = entry;
}
}
}
}

size_t config_size = f->size + 2;
config_addr = ext_mem_alloc(config_size);

// Read the main config file.
fread(f, config_addr, 0, f->size);
size_t config_cursor = f->size;

// Sort all files by name in ascending order.
struct file_handle* temp = NULL;
for (size_t i = 1; i < num_files; i++) {
for (size_t j = 0; j < num_files - i; j++) {
if (strcmp(files[j]->path, files[j + 1]->path) > 0) {
temp = files[j];
files[j] = files[j + 1];
files[j + 1] = temp;
}
}
}

// Concatenate all limine.d files.
for (size_t i = 0; i < num_files; i++) {
fread(files[i], config_addr + config_cursor, 0, files[i]->size);
config_cursor += files[i]->size;
}

fclose(f);

Expand Down
35 changes: 0 additions & 35 deletions test/limine.conf
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,3 @@ backdrop: 008080

module_path: boot():/boot/bg.jpg
dtb_path: boot():/boot/device_tree.dtb

/Multiboot2 Test
comment: Test of the multiboot2 boot protocol.

protocol: multiboot2
kernel_path: boot():/boot/multiboot2.elf
kernel_cmdline: This is an example kernel command line.

module_path: boot():/boot/bg.jpg
module_string: This is the first module.

/EFI Chainloading
comment: Test EFI image chainloading.

protocol: efi_chainload
image_path: boot():/EFI/BOOT/BOOTX64.EFI

/BIOS Chainloading
comment: Test BIOS chainloading.

protocol: bios_chainload
drive: 1

/+Legacy
comment: Directory containing legacy entries.

//Multiboot1 Test
comment: Test of the multiboot1 boot protocol.

protocol: multiboot1
kernel_path: boot():/boot/multiboot.elf
kernel_cmdline: This is an example kernel command line.

module_path: boot():/boot/bg.jpg
module_string: This is the first module.
9 changes: 9 additions & 0 deletions test/limine.d/00-multiboot.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/Multiboot2 Test
comment: Test of the multiboot2 boot protocol.

protocol: multiboot2
kernel_path: boot():/boot/multiboot2.elf
kernel_cmdline: This is an example kernel command line.

module_path: boot():/boot/bg.jpg
module_string: This is the first module.
5 changes: 5 additions & 0 deletions test/limine.d/01-efi-chainload.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/EFI Chainloading
comment: Test EFI image chainloading.

protocol: efi_chainload
image_path: boot():/EFI/BOOT/BOOTX64.EFI
5 changes: 5 additions & 0 deletions test/limine.d/02-bios-chainload.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/BIOS Chainloading
comment: Test BIOS chainloading.

protocol: bios_chainload
drive: 1
12 changes: 12 additions & 0 deletions test/limine.d/03-legacy.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/+Legacy
comment: Directory containing legacy entries.

//Multiboot1 Test
comment: Test of the multiboot1 boot protocol.

protocol: multiboot1
kernel_path: boot():/boot/multiboot.elf
kernel_cmdline: This is an example kernel command line.

module_path: boot():/boot/bg.jpg
module_string: This is the first module.