Skip to content

Commit 177decb

Browse files
tool: improve emulation of kernel boot
One of the things the tool has to do is emulate the kernel boot process, this involves getting a layout of all memory and putting into two categories, 'device' memory and 'normal' memory as defined by the kernel. The way we do this currently is by looking at certain symbols in the kernel ELF that encode this information. This doesn't work well when the compiler decides to optimise that symbol out and instead embed the information directly into where it's being used in the kernel code. So, instead we use the `platform_gen.json` file that gets generated by the kernel build system to get the same information. There should be no difference at all in terms of what the tool actually spits out at the end, it just means that the process of doing this emulation is less fragile. Signed-off-by: Ivan-Velickovic <i.velickovic@unsw.edu.au>
1 parent f46095c commit 177decb

File tree

5 files changed

+49
-101
lines changed

5 files changed

+49
-101
lines changed

build_sdk.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,13 @@ def build_sel4(
459459
copy(p, dest)
460460
dest.chmod(0o744)
461461

462+
463+
platform_gen = sel4_build_dir / "gen_headers" / "plat" / "machine" / "platform_gen.json"
464+
dest = root_dir / "board" / board.name / config.name / "platform_gen.json"
465+
dest.unlink(missing_ok=True)
466+
copy(platform_gen, dest)
467+
dest.chmod(0o744)
468+
462469
gen_config_path = sel4_install_dir / "libsel4/include/kernel/gen_config.json"
463470
with open(gen_config_path, "r") as f:
464471
gen_config = json.load(f)

tool/microkit/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ path = "src/main.rs"
1616

1717
[dependencies]
1818
roxmltree = "0.19.0"
19-
serde = "1.0.203"
19+
serde = { version = "1.0.203", features = ["derive"] }
2020
serde_json = "1.0.117"
2121

2222
[profile.release]

tool/microkit/src/main.rs

Lines changed: 24 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use sdf::{
2020
use sel4::{
2121
default_vm_attr, Aarch64Regs, Arch, ArmVmAttributes, BootInfo, Config, Invocation,
2222
InvocationArgs, Object, ObjectType, PageSize, Rights, Riscv64Regs, RiscvVirtualMemory,
23-
RiscvVmAttributes,
23+
RiscvVmAttributes, PlatformConfig
2424
};
2525
use std::cmp::{max, min};
2626
use std::collections::{HashMap, HashSet};
@@ -30,7 +30,7 @@ use std::iter::zip;
3030
use std::mem::size_of;
3131
use std::path::{Path, PathBuf};
3232
use util::{
33-
bytes_to_struct, comma_sep_u64, comma_sep_usize, json_str, json_str_as_bool, json_str_as_u64,
33+
comma_sep_u64, comma_sep_usize, json_str, json_str_as_bool, json_str_as_u64,
3434
monitor_serialise_names, monitor_serialise_u64_vec, struct_to_bytes,
3535
};
3636

@@ -566,88 +566,6 @@ struct KernelPartialBootInfo {
566566
boot_region: MemoryRegion,
567567
}
568568

569-
// Corresponds to kernel_frame_t in the kernel
570-
#[repr(C)]
571-
struct KernelFrameRiscv64 {
572-
pub paddr: u64,
573-
pub pptr: u64,
574-
pub user_accessible: i32,
575-
}
576-
577-
#[repr(C)]
578-
struct KernelFrameAarch64 {
579-
pub paddr: u64,
580-
pub pptr: u64,
581-
pub execute_never: i32,
582-
pub user_accessible: i32,
583-
}
584-
585-
fn kernel_device_addrs(config: &Config, kernel_elf: &ElfFile) -> Vec<u64> {
586-
assert!(config.word_size == 64, "Unsupported word-size");
587-
588-
let mut kernel_devices = Vec::new();
589-
let (vaddr, size) = kernel_elf
590-
.find_symbol("kernel_device_frames")
591-
.expect("Could not find 'kernel_device_frames' symbol");
592-
let kernel_frame_bytes = kernel_elf.get_data(vaddr, size).unwrap();
593-
let kernel_frame_size = match config.arch {
594-
Arch::Aarch64 => size_of::<KernelFrameAarch64>(),
595-
Arch::Riscv64 => size_of::<KernelFrameRiscv64>(),
596-
};
597-
let mut offset: usize = 0;
598-
while offset < size as usize {
599-
let (user_accessible, paddr) = unsafe {
600-
match config.arch {
601-
Arch::Aarch64 => {
602-
let frame = bytes_to_struct::<KernelFrameAarch64>(
603-
&kernel_frame_bytes[offset..offset + kernel_frame_size],
604-
);
605-
(frame.user_accessible, frame.paddr)
606-
}
607-
Arch::Riscv64 => {
608-
let frame = bytes_to_struct::<KernelFrameRiscv64>(
609-
&kernel_frame_bytes[offset..offset + kernel_frame_size],
610-
);
611-
(frame.user_accessible, frame.paddr)
612-
}
613-
}
614-
};
615-
if user_accessible == 0 {
616-
kernel_devices.push(paddr);
617-
}
618-
offset += kernel_frame_size;
619-
}
620-
621-
kernel_devices
622-
}
623-
624-
// Corresponds to p_region_t in the kernel
625-
#[repr(C)]
626-
struct KernelRegion64 {
627-
start: u64,
628-
end: u64,
629-
}
630-
631-
fn kernel_phys_mem(kernel_config: &Config, kernel_elf: &ElfFile) -> Vec<(u64, u64)> {
632-
assert!(kernel_config.word_size == 64, "Unsupported word-size");
633-
let mut phys_mem = Vec::new();
634-
let (vaddr, size) = kernel_elf
635-
.find_symbol("avail_p_regs")
636-
.expect("Could not find 'avail_p_regs' symbol");
637-
let p_region_bytes = kernel_elf.get_data(vaddr, size).unwrap();
638-
let p_region_size = size_of::<KernelRegion64>();
639-
let mut offset: usize = 0;
640-
while offset < size as usize {
641-
let p_region = unsafe {
642-
bytes_to_struct::<KernelRegion64>(&p_region_bytes[offset..offset + p_region_size])
643-
};
644-
phys_mem.push((p_region.start, p_region.end));
645-
offset += p_region_size;
646-
}
647-
648-
phys_mem
649-
}
650-
651569
fn kernel_self_mem(kernel_elf: &ElfFile) -> MemoryRegion {
652570
let segments = kernel_elf.loadable_segments();
653571
let base = segments[0].phys_addr;
@@ -683,23 +601,11 @@ fn kernel_partial_boot(kernel_config: &Config, kernel_elf: &ElfFile) -> KernelPa
683601
let mut device_memory = DisjointMemoryRegion::default();
684602
let mut normal_memory = DisjointMemoryRegion::default();
685603

686-
// Start by allocating the entire physical address space
687-
// as device memory.
688-
device_memory.insert_region(0, kernel_config.paddr_user_device_top);
689-
690-
// Next, remove all the kernel devices.
691-
// NOTE: There is an assumption each kernel device is one frame
692-
// in size only. It's possible this assumption could break in the
693-
// future.
694-
for paddr in kernel_device_addrs(kernel_config, kernel_elf) {
695-
device_memory.remove_region(paddr, paddr + kernel_config.kernel_frame_size);
604+
for r in &kernel_config.device_regions {
605+
device_memory.insert_region(r.start, r.end);
696606
}
697-
698-
// Remove all the actual physical memory from the device regions
699-
// but add it all to the actual normal memory regions
700-
for (start, end) in kernel_phys_mem(kernel_config, kernel_elf) {
701-
device_memory.remove_region(start, end);
702-
normal_memory.insert_region(start, end);
607+
for r in &kernel_config.normal_regions {
608+
normal_memory.insert_region(r.start, r.end);
703609
}
704610

705611
// Remove the kernel image itself
@@ -3235,6 +3141,12 @@ fn main() -> Result<(), String> {
32353141
.join(args.config)
32363142
.join("include/kernel/gen_config.json");
32373143

3144+
let kernel_platform_config_path = sdk_dir
3145+
.join("board")
3146+
.join(args.board)
3147+
.join(args.config)
3148+
.join("platform_gen.json");
3149+
32383150
let invocations_all_path = sdk_dir
32393151
.join("board")
32403152
.join(args.board)
@@ -3276,6 +3188,13 @@ fn main() -> Result<(), String> {
32763188
);
32773189
std::process::exit(1);
32783190
}
3191+
if !kernel_platform_config_path.exists() {
3192+
eprintln!(
3193+
"Error: kernel platform configuration file '{}' does not exist",
3194+
kernel_platform_config_path.display()
3195+
);
3196+
std::process::exit(1);
3197+
}
32793198
if !invocations_all_path.exists() {
32803199
eprintln!(
32813200
"Error: invocations JSON file '{}' does not exist",
@@ -3298,6 +3217,9 @@ fn main() -> Result<(), String> {
32983217
let kernel_config_json: serde_json::Value =
32993218
serde_json::from_str(&fs::read_to_string(kernel_config_path).unwrap()).unwrap();
33003219

3220+
let kernel_platform_config: PlatformConfig =
3221+
serde_json::from_str(&fs::read_to_string(kernel_platform_config_path).unwrap()).unwrap();
3222+
33013223
let invocations_labels: serde_json::Value =
33023224
serde_json::from_str(&fs::read_to_string(invocations_all_path).unwrap()).unwrap();
33033225

@@ -3352,6 +3274,8 @@ fn main() -> Result<(), String> {
33523274
arm_smc,
33533275
riscv_pt_levels: Some(RiscvVirtualMemory::Sv39),
33543276
invocations_labels,
3277+
device_regions: kernel_platform_config.devices,
3278+
normal_regions: kernel_platform_config.memory,
33553279
};
33563280

33573281
if let Arch::Aarch64 = kernel_config.arch {

tool/microkit/src/sel4.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use crate::UntypedObject;
88
use std::collections::HashMap;
99
use std::io::{BufWriter, Write};
10+
use serde::{Deserialize};
1011

1112
#[derive(Clone)]
1213
pub struct BootInfo {
@@ -18,6 +19,18 @@ pub struct BootInfo {
1819
pub first_available_cap: u64,
1920
}
2021

22+
#[derive(Deserialize)]
23+
pub struct PlatformConfigRegion {
24+
pub start: u64,
25+
pub end: u64,
26+
}
27+
28+
#[derive(Deserialize)]
29+
pub struct PlatformConfig {
30+
pub devices: Vec<PlatformConfigRegion>,
31+
pub memory: Vec<PlatformConfigRegion>,
32+
}
33+
2134
/// Represents an allocated kernel object.
2235
///
2336
/// Kernel objects can have multiple caps (and caps can have multiple addresses).
@@ -57,6 +70,8 @@ pub struct Config {
5770
/// RISC-V specific, what kind of virtual memory system (e.g Sv39)
5871
pub riscv_pt_levels: Option<RiscvVirtualMemory>,
5972
pub invocations_labels: serde_json::Value,
73+
pub device_regions: Vec<PlatformConfigRegion>,
74+
pub normal_regions: Vec<PlatformConfigRegion>,
6075
}
6176

6277
impl Config {

tool/microkit/tests/test.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const DEFAULT_KERNEL_CONFIG: sel4::Config = sel4::Config {
2424
riscv_pt_levels: None,
2525
// Not necessary for SDF parsing
2626
invocations_labels: json!(null),
27+
device_regions: vec![],
28+
normal_regions: vec![],
2729
};
2830

2931
fn check_error(test_name: &str, expected_err: &str) {

0 commit comments

Comments
 (0)