Skip to content

Commit aae44b2

Browse files
committed
Reimplement custom config parsing with serde
1 parent a9f2d21 commit aae44b2

File tree

4 files changed

+99
-93
lines changed

4 files changed

+99
-93
lines changed

Cargo.lock

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,15 @@ optional = true
5656
[build-dependencies]
5757
llvm-tools-build = { version = "0.1", optional = true, package = "llvm-tools" }
5858
toml = { version = "0.5.1", optional = true }
59+
serde = { version = "1.0", features = ["derive"], optional = true}
5960

6061
[features]
6162
default = []
6263
builder = ["argh", "thiserror", "displaydoc", "anyhow", "llvm-tools", "json"]
6364
runner = ["anyhow"]
6465
bios_bin = ["binary", "vga_320x200", "rsdp"]
6566
uefi_bin = ["binary", "uefi", "font8x8"]
66-
binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top"]
67+
binary = ["llvm-tools-build", "x86_64", "toml", "xmas-elf", "usize_conversions", "log", "conquer-once", "spinning_top", "serde"]
6768
vga_320x200 = ["font8x8"]
6869
recursive_page_table = []
6970
map_physical_memory = []

build.rs

Lines changed: 79 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ fn main() {
88

99
#[cfg(feature = "binary")]
1010
mod binary {
11+
use std::fmt;
12+
1113
pub fn main() {
1214
use llvm_tools_build as llvm_tools;
1315
use std::{
@@ -193,7 +195,7 @@ mod binary {
193195
}
194196

195197
// Parse configuration from the kernel's Cargo.toml
196-
let config = match env::var("KERNEL_MANIFEST") {
198+
let config: Config = match env::var("KERNEL_MANIFEST") {
197199
Err(env::VarError::NotPresent) => {
198200
panic!("The KERNEL_MANIFEST environment variable must be set for building the bootloader.\n\n\
199201
Please use `cargo builder` for building.");
@@ -213,13 +215,14 @@ mod binary {
213215
.parse::<Value>()
214216
.expect("failed to parse kernel's Cargo.toml");
215217

216-
let table = manifest
218+
let config_table = manifest
217219
.get("package")
218220
.and_then(|table| table.get("metadata"))
219221
.and_then(|table| table.get("bootloader"))
220-
.and_then(|table| table.as_table());
222+
.cloned()
223+
.unwrap_or_else(|| toml::Value::Table(toml::map::Map::new()));
221224

222-
table.map(|t| Config::parse(t)).unwrap_or_default()
225+
config_table.try_into().expect("failed to parse config")
223226
}
224227
};
225228

@@ -244,105 +247,92 @@ mod binary {
244247
println!("cargo:rerun-if-changed=build.rs");
245248
}
246249

247-
include!("src/config.rs");
250+
fn val_true() -> bool {
251+
true
252+
}
248253

249-
impl Config {
250-
fn parse(table: &toml::value::Table) -> Self {
251-
use std::convert::TryFrom;
252-
use toml::Value;
254+
/// Must be always identical with the struct in `src/config.rs`
255+
///
256+
/// This copy is needed because we can't derive Deserialize in the `src/config.rs`
257+
/// module itself, since cargo currently unifies dependencies (the `toml` crate enables
258+
/// serde's standard feature).
259+
#[derive(Debug, serde::Deserialize)]
260+
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
261+
struct Config {
262+
#[serde(default)]
263+
pub map_physical_memory: bool,
264+
#[serde(default)]
265+
pub map_page_table_recursively: bool,
266+
#[serde(default = "val_true")]
267+
pub map_framebuffer: bool,
268+
pub kernel_stack_size: Option<AlignedAddress>,
269+
pub physical_memory_offset: Option<AlignedAddress>,
270+
pub recursive_index: Option<u16>,
271+
pub kernel_stack_address: Option<AlignedAddress>,
272+
pub boot_info_address: Option<AlignedAddress>,
273+
pub framebuffer_address: Option<AlignedAddress>,
274+
}
253275

254-
let mut config = Self::default();
276+
struct AlignedAddress(u64);
255277

256-
for (key, value) in table {
257-
match (key.as_str(), value.clone()) {
258-
("map-physical-memory", Value::Boolean(b)) => {
259-
config.map_physical_memory = b;
260-
}
261-
("map-page-table-recursively", Value::Boolean(b)) => {
262-
config.map_page_table_recursively = b;
263-
}
264-
("map-framebuffer", Value::Boolean(b)) => {
265-
config.map_framebuffer = b;
266-
}
267-
("kernel-stack-size", Value::Integer(i)) => {
268-
if i <= 0 {
269-
panic!("`kernel-stack-size` in kernel manifest must be positive");
270-
} else {
271-
config.kernel_stack_size = Some(i as u64);
272-
}
273-
}
278+
impl fmt::Debug for AlignedAddress {
279+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280+
write!(f, "{}", self.0)
281+
}
282+
}
274283

275-
("recursive-page-table-index", Value::Integer(i)) => {
276-
let index = match u16::try_from(i) {
277-
Ok(index) if index < 512 => index,
278-
_other => panic!(
279-
"`recursive-page-table-index` must be a number between 0 and 512"
280-
),
281-
};
282-
config.recursive_index = Some(index);
283-
}
284+
impl<'de> serde::Deserialize<'de> for AlignedAddress {
285+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
286+
where
287+
D: serde::Deserializer<'de>,
288+
{
289+
deserializer.deserialize_str(AlignedAddressVisitor)
290+
}
291+
}
284292

285-
("physical-memory-offset", Value::Integer(i))
286-
| ("kernel-stack-address", Value::Integer(i))
287-
| ("boot-info-address", Value::Integer(i))
288-
| ("framebuffer-address", Value::Integer(i)) => {
289-
panic!(
290-
"`{0}` in the kernel manifest must be given as a string, \
291-
as toml does not support unsigned 64-bit integers (try `{0} = \"{1}\"`)",
292-
key.as_str(),
293-
i
294-
);
295-
}
296-
("physical-memory-offset", Value::String(s)) => {
297-
config.physical_memory_offset =
298-
Some(Self::parse_aligned_addr(key.as_str(), &s));
299-
}
300-
("kernel-stack-address", Value::String(s)) => {
301-
config.kernel_stack_address =
302-
Some(Self::parse_aligned_addr(key.as_str(), &s));
303-
}
304-
("boot-info-address", Value::String(s)) => {
305-
config.boot_info_address = Some(Self::parse_aligned_addr(key.as_str(), &s));
306-
}
307-
("framebuffer-address", Value::String(s)) => {
308-
config.framebuffer_address =
309-
Some(Self::parse_aligned_addr(key.as_str(), &s));
310-
}
293+
/// Helper struct for implementing the `optional_version_deserialize` function.
294+
struct AlignedAddressVisitor;
311295

312-
(s, _) => {
313-
let help = if s.contains("_") {
314-
"\nkeys use `-` instead of `_`"
315-
} else {
316-
""
317-
};
318-
panic!("unknown bootloader configuration key '{}'{}", s, help);
319-
}
320-
}
321-
}
296+
impl serde::de::Visitor<'_> for AlignedAddressVisitor {
297+
type Value = AlignedAddress;
298+
299+
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
300+
write!(
301+
formatter,
302+
"a page-aligned memory address, either as integer or as decimal or hexadecimal \
303+
string (e.g. \"0xffff0000\"); large addresses must be given as string because \
304+
TOML does not support unsigned 64-bit integers"
305+
)
306+
}
322307

323-
config
308+
fn visit_u64<E>(self, num: u64) -> Result<Self::Value, E>
309+
where
310+
E: serde::de::Error,
311+
{
312+
if num % 0x1000 == 0 {
313+
Ok(AlignedAddress(num))
314+
} else {
315+
Err(serde::de::Error::invalid_value(
316+
serde::de::Unexpected::Unsigned(num),
317+
&self,
318+
))
319+
}
324320
}
325321

326-
fn parse_aligned_addr(key: &str, value: &str) -> u64 {
322+
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
323+
where
324+
E: serde::de::Error,
325+
{
327326
let num = if value.starts_with("0x") {
328327
u64::from_str_radix(&value[2..], 16)
329328
} else {
330329
u64::from_str_radix(&value, 10)
331-
};
332-
333-
let num = num.expect(&format!(
334-
"`{}` in the kernel manifest must be an integer (is `{}`)",
335-
key, value
336-
));
337-
338-
if num % 0x1000 != 0 {
339-
panic!(
340-
"`{}` in the kernel manifest must be aligned to 4KiB (is `{}`)",
341-
key, value
342-
);
343-
} else {
344-
num
345330
}
331+
.map_err(|_err| {
332+
serde::de::Error::invalid_value(serde::de::Unexpected::Str(value), &self)
333+
})?;
334+
335+
self.visit_u64(num)
346336
}
347337
}
348338
}

src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ impl Default for Config {
1616
Config {
1717
map_physical_memory: false,
1818
map_page_table_recursively: false,
19+
map_framebuffer: true,
1920
physical_memory_offset: None,
2021
recursive_index: None,
2122
kernel_stack_address: None,
2223
kernel_stack_size: None,
2324
boot_info_address: None,
24-
map_framebuffer: true,
2525
framebuffer_address: None,
2626
}
2727
}

0 commit comments

Comments
 (0)