The Pedigree Project 0.1
load_options.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_UEFI_LOAD_OPTIONS_H
3#define PEDIGREE_UEFI_LOAD_OPTIONS_H
4
5#include <stdint.h>
6
7#define UEFI_LOAD_OPTIONS_CAPACITY 1024U
8#define UEFI_COMMAND_LINE_CAPACITY 4096U
9
10enum uefi_boot_directory {
11 UEFI_BOOT_DIRECTORY_DEFAULT,
12 UEFI_BOOT_DIRECTORY_CURRENT,
13 UEFI_BOOT_DIRECTORY_KNOWN_GOOD,
14 UEFI_BOOT_DIRECTORY_DEBUG
15};
16
17typedef struct uefi_load_options {
18 enum uefi_boot_directory directory;
19 uint32_t arguments_offset;
20 uint32_t arguments_length;
21 char text[UEFI_LOAD_OPTIONS_CAPACITY];
23
24static int parse_load_options(const void* raw, uint32_t size, uefi_load_options_t* options) {
25 if (!options)
26 return 0;
27 options->directory = UEFI_BOOT_DIRECTORY_DEFAULT;
28 options->arguments_offset = options->arguments_length = 0;
29 options->text[0] = 0;
30 if (!size)
31 return 1;
32 if (!raw || size > UEFI_LOAD_OPTIONS_CAPACITY * 2)
33 return 0;
34
35 const uint8_t* bytes = (const uint8_t*)raw;
36 const uint32_t stride = size >= 2 && !bytes[1] ? 2 : 1;
37 if (size % stride)
38 return 0;
39 uint32_t length = 0;
40 int terminated = 0;
41 for (uint32_t offset = 0; offset < size; offset += stride) {
42 if (stride == 2 && bytes[offset + 1])
43 return 0;
44 const uint8_t value = bytes[offset];
45 if (!value) {
46 terminated = 1;
47 continue;
48 }
49 if (terminated || (value != '\t' && (value < ' ' || value > '~')) ||
50 length + 1 >= UEFI_LOAD_OPTIONS_CAPACITY)
51 return 0;
52 // Kernel consumers split on spaces, while EFI launchers may supply tabs.
53 options->text[length++] = value == '\t' ? ' ' : (char)value;
54 }
55 while (length && options->text[length - 1] == ' ')
56 --length;
57 options->text[length] = 0;
58 uint32_t start = 0;
59 while (start < length && options->text[start] == ' ')
60 ++start;
61 uint32_t end = start;
62 while (end < length && options->text[end] != ' ')
63 ++end;
64 static const char* selectors[] = {"current", "known-good", "debug"};
65 static const enum uefi_boot_directory directories[] = {
66 UEFI_BOOT_DIRECTORY_CURRENT, UEFI_BOOT_DIRECTORY_KNOWN_GOOD, UEFI_BOOT_DIRECTORY_DEBUG};
67 for (uint32_t selector = 0; selector < 3; ++selector) {
68 uint32_t matched = 0;
69 while (start + matched < end && selectors[selector][matched] &&
70 options->text[start + matched] == selectors[selector][matched])
71 ++matched;
72 if (start + matched == end && !selectors[selector][matched]) {
73 options->directory = directories[selector];
74 start = end;
75 while (start < length && options->text[start] == ' ')
76 ++start;
77 break;
78 }
79 }
80 options->arguments_offset = start;
81 options->arguments_length = length - start;
82 return 1;
83}
84
85static int append_load_options(const uint8_t* command_line, uint64_t length,
86 const uefi_load_options_t* options, char* output,
87 uint64_t capacity) {
88 if (!options || !output || (!command_line && length) || !capacity ||
89 length > UEFI_COMMAND_LINE_CAPACITY)
90 return 0;
91 if (options->arguments_length) {
92 while (length && (!command_line[length - 1] || command_line[length - 1] == '\r' ||
93 command_line[length - 1] == '\n'))
94 --length;
95 for (uint64_t i = 0; i < length; ++i) {
96 if (!command_line[i])
97 return 0;
98 }
99 }
100 const uint64_t separator = length && options->arguments_length ? 1 : 0;
101 if (length >= capacity || options->arguments_length > capacity - length - 1 ||
102 separator > capacity - length - options->arguments_length - 1)
103 return 0;
104 for (uint64_t i = 0; i < length; ++i)
105 output[i] = (char)command_line[i];
106 if (separator)
107 output[length++] = ' ';
108 for (uint32_t i = 0; i < options->arguments_length; ++i)
109 output[length++] = options->text[options->arguments_offset + i];
110 output[length] = 0;
111 return 1;
112}
113
114#endif