compiler/
kernel/
kernel/
tools/
build.rad
10.0 KiB
kernel.rad
684 B
mmio.rad
2.5 KiB
root.rad
2.6 KiB
scheduler.rad
1.7 KiB
lib/
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
10.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
551 B
kernel/tools/build.rad
raw
| 1 | //! Build a native kernel image with shared packages and a persistent boot catalog. |
| 2 | |
| 3 | use std::sys; |
| 4 | use std::io; |
| 5 | use std::mem; |
| 6 | use std::sys::unix; |
| 7 | use std::lang::alloc; |
| 8 | use std::lang::strings; |
| 9 | use std::lang::il::binary; |
| 10 | use std::lang::il::binary::program; |
| 11 | use std::lang::gen::data; |
| 12 | use std::arch::rv64; |
| 13 | use std::arch::rv64::asm; |
| 14 | use std::arch::rv64::emit; |
| 15 | use std::arch::rv64::encode; |
| 16 | use std::arch::rv64::image; |
| 17 | use std::arch::rv64::shared; |
| 18 | use std::arch::rv64::shared::catalog; |
| 19 | |
| 20 | /// Native entry page, above the platform firmware data. |
| 21 | constant CODE_ADDRESS: u64 = 0x81000000; |
| 22 | /// Maximum shared packages in the boot image. |
| 23 | constant PACKAGES: u32 = 4; |
| 24 | /// Package code arenas retained until image output completes. |
| 25 | unsafe static CODE: [[u8; 16777216]; PACKAGES] = undefined; |
| 26 | /// Reusable function workspace. |
| 27 | static SCRATCH: [u8; 16777216] = [0; 16777216]; |
| 28 | /// Persistent decoded RIL storage. |
| 29 | static DECODE: [u8; 67108864] = [0; 67108864]; |
| 30 | /// Exact package bytes retained in the native catalog. |
| 31 | unsafe static INPUT: [[u8; 8388608]; PACKAGES] = undefined; |
| 32 | /// Combined startup and boundary assembly source. |
| 33 | static SOURCE: [u8; 65536] = [0; 65536]; |
| 34 | /// Assembler workspace. |
| 35 | static ASSEMBLY: [u8; 4194304] = [0; 4194304]; |
| 36 | /// Assembled startup words. |
| 37 | static TEXT: [u32; 16384] = [0; 16384]; |
| 38 | /// Assembly identifiers retained through package linking. |
| 39 | unsafe static STRINGS: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
| 40 | /// Per-package data layout workspaces. |
| 41 | unsafe static DATA: [[data::DataSym; 4096]; PACKAGES] = undefined; |
| 42 | /// Local function and data definitions. |
| 43 | unsafe static SYMBOLS: [[shared::Symbol; 4096]; PACKAGES] = undefined; |
| 44 | /// Public package symbols. |
| 45 | unsafe static EXPORTS: [[shared::Symbol; 4096]; PACKAGES] = undefined; |
| 46 | /// Exports from dependencies compiled before the current package. |
| 47 | unsafe static IMPORTS: [shared::Symbol; 16384] = undefined; |
| 48 | /// Private-state relocation records. |
| 49 | unsafe static RELOCS: [[shared::Relocation; 4096]; PACKAGES] = undefined; |
| 50 | /// Initialized package templates. |
| 51 | unsafe static TEMPLATES: [[u8; 1048576]; PACKAGES] = undefined; |
| 52 | /// Read-only catalog and retained package payloads. |
| 53 | static RO: [u8; 16777216] = [0; 16777216]; |
| 54 | /// Kernel state table and initialized private package graph. |
| 55 | static RW: [u8; 33554432] = [0; 33554432]; |
| 56 | /// Contiguous native code segment, including entry trampoline and alignment padding. |
| 57 | static NATIVE: [u32; 4194304] = [0; 4194304]; |
| 58 | |
| 59 | /// Round an extent up to a power-of-two alignment. |
| 60 | fn aligned(value: u64, alignment: u32) -> u64 { |
| 61 | assert alignment > 0 and (alignment & (alignment - 1)) == 0; |
| 62 | assert value <= 0xffffffffffffffff - alignment as u64 + 1; |
| 63 | return (value + alignment as u64 - 1) & ~(alignment as u64 - 1); |
| 64 | } |
| 65 | |
| 66 | /// Print a bounded shared-backend failure class. |
| 67 | fn report(error: shared::Error) { |
| 68 | match error { |
| 69 | case shared::Error::Codegen(_) => io::printLn("native build: code generation failed"), |
| 70 | case shared::Error::Capacity => io::printLn("native build: output capacity exhausted"), |
| 71 | case shared::Error::Symbol => io::printLn("native build: unresolved or duplicate symbol"), |
| 72 | case shared::Error::Range => io::printLn("native build: address out of range"), |
| 73 | case shared::Error::Alignment => io::printLn("native build: invalid alignment"), |
| 74 | case shared::Error::Instance => io::printLn("native build: missing package instance"), |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /// Compile one boot package into resident code and private-state metadata. |
| 79 | unsafe fn compile(input: &binary::Package, slot: u32, address: u64, imports: &[shared::Symbol], assembly: asm::Program) |
| 80 | -> shared::Package throws (shared::Error) |
| 81 | { |
| 82 | let mut arena = alloc::new(&mut CODE[slot][..]); |
| 83 | let mut scratch = alloc::new(&mut SCRATCH[..]); |
| 84 | return try shared::compileAssembly(shared::AssemblyInput { package: input as *unsafe binary::Package, assembly }, slot, address, imports, |
| 85 | shared::Storage { |
| 86 | data: &mut DATA[slot][..], symbols: &mut SYMBOLS[slot][..], exports: &mut EXPORTS[slot][..], |
| 87 | template: &mut TEMPLATES[slot][..], relocations: &mut RELOCS[slot][..], |
| 88 | }, &mut arena, &mut scratch); |
| 89 | } |
| 90 | |
| 91 | /// Emit a trampoline that preserves firmware arguments and supplies the native catalog. |
| 92 | unsafe fn trampoline(kernel: u64, ro: u64, rw: u64, count: u32) { |
| 93 | let mut arena = alloc::new(&mut SCRATCH[..]); |
| 94 | let mut e = try! emit::emitter(&mut arena, false); |
| 95 | emit::loadImm(&mut e, rv64::GP, rw as i64); |
| 96 | emit::loadImm(&mut e, rv64::A3, ro as i64); |
| 97 | emit::loadImm(&mut e, rv64::A4, count as i64); |
| 98 | emit::loadImm(&mut e, rv64::T0, kernel as i64); |
| 99 | emit::emit(&mut e, encode::jalr(rv64::ZERO, rv64::T0, 0)); |
| 100 | assert e.codeLen <= 1024; |
| 101 | for word, i in emit::getCode(&e) { set NATIVE[i] = word; } |
| 102 | } |
| 103 | |
| 104 | /// Link shared boot packages, retain their catalog, and instantiate the kernel graph. |
| 105 | @default unsafe fn main(env: *sys::Env) -> i32 { |
| 106 | assert env.args.len >= 5 and env.args.len <= PACKAGES + 3; |
| 107 | let count = env.args.len - 3; |
| 108 | let mut decoder = alloc::new(&mut DECODE[..]); |
| 109 | let libraryBytesLength = unix::readFile(env.args[1], &mut INPUT[0][..]) else panic "std RIL"; |
| 110 | let libraryBytes = &INPUT[0][..libraryBytesLength]; |
| 111 | let libraryInput = try! program::decode(libraryBytes, &mut decoder, binary::Limits { registers: 8192, blocks: 4096 }); |
| 112 | let kernelBytesLength = unix::readFile(env.args[2], &mut INPUT[1][..]) else panic "kernel RIL"; |
| 113 | let kernelBytes = &INPUT[1][..kernelBytesLength]; |
| 114 | let kernelInput = try! program::decode(kernelBytes, &mut decoder, binary::Limits { registers: 8192, blocks: 4096 }); |
| 115 | assert mem::eq(libraryInput.name, "std") and mem::eq(kernelInput.name, "kernel"); |
| 116 | assert libraryInput.dependencies.len == 0; |
| 117 | for dependency in kernelInput.dependencies { assert mem::eq(dependency, "std"); } |
| 118 | let sourceLength = unix::readFile(env.args[3], &mut SOURCE[..]) else panic "kernel assembly"; |
| 119 | let source = &SOURCE[..sourceLength]; |
| 120 | let mut assemblyArena = alloc::new(&mut ASSEMBLY[..]); |
| 121 | let empty: *mut [u8] = &mut []; |
| 122 | let assembly = try! asm::assemble(asm::scanner::SourceKind::String, source, |
| 123 | &mut TEXT[..], &mut empty[..], &mut assemblyArena, &mut STRINGS, 0); |
| 124 | let library = try compile(&libraryInput, 0, CODE_ADDRESS + 4096, &[], |
| 125 | asm::Program { text: &[], data: &[], symbols: &[], externalFixups: &[] }) catch error { report(error); return 1; }; |
| 126 | let kernelAddress = aligned(library.codeAddress + library.code.len as u64 * 4, 4096); |
| 127 | let kernel = try compile(&kernelInput, 1, kernelAddress, library.exports, assembly) catch error { report(error); return 1; }; |
| 128 | let mut catalogEntries: [catalog::Entry; PACKAGES] = undefined; |
| 129 | set catalogEntries[0] = catalog::Entry { source: libraryBytes, package: library }; |
| 130 | set catalogEntries[1] = catalog::Entry { source: kernelBytes, package: kernel }; |
| 131 | let mut codeEnd = kernel.codeAddress + kernel.code.len as u64 * 4; |
| 132 | for slot in 2..count { |
| 133 | let bytesLength = unix::readFile(env.args[slot + 3], &mut INPUT[slot][..]) else panic "boot package RIL"; |
| 134 | let bytes = &INPUT[slot][..bytesLength]; |
| 135 | let input = try! program::decode(bytes, &mut decoder, binary::Limits { registers: 8192, blocks: 4096 }); |
| 136 | let mut imported: u32 = 0; |
| 137 | for previous in 0..slot { |
| 138 | for symbol in catalogEntries[previous].package.exports { |
| 139 | assert imported < IMPORTS.len; |
| 140 | set IMPORTS[imported] = symbol; |
| 141 | set imported += 1; |
| 142 | } |
| 143 | } |
| 144 | let package = try compile(&input, slot, aligned(codeEnd, 4096), &IMPORTS[..imported], |
| 145 | asm::Program { text: &[], data: &[], symbols: &[], externalFixups: &[] }) catch error { report(error); return 1; }; |
| 146 | set catalogEntries[slot] = catalog::Entry { source: bytes, package }; |
| 147 | set codeEnd = package.codeAddress + package.code.len as u64 * 4; |
| 148 | } |
| 149 | let codeSize = (codeEnd - CODE_ADDRESS) as u32; |
| 150 | assert codeEnd - CODE_ADDRESS <= @sizeOf([u32; 4194304]) as u64; |
| 151 | let roAddress = aligned(codeEnd, 4096); |
| 152 | let roSize = try catalog::pack(&catalogEntries[..count], roAddress, &mut RO[..]) catch error { report(error); return 1; }; |
| 153 | let rwAddress = aligned(roAddress + roSize as u64, 4096); |
| 154 | let mut bases: [u64; shared::MAX_PACKAGES] = [0; shared::MAX_PACKAGES]; |
| 155 | let mut rwSize: u32 = @sizeOf([u64; shared::MAX_PACKAGES]); |
| 156 | for entry in &catalogEntries[..2] { |
| 157 | let at = aligned(rwAddress + rwSize as u64, entry.package.alignment); |
| 158 | assert at - rwAddress <= RW.len as u64 and entry.package.memory <= RW.len - (at - rwAddress) as u32; |
| 159 | set bases[entry.package.slot] = at; |
| 160 | set rwSize = (at - rwAddress) as u32 + entry.package.memory; |
| 161 | } |
| 162 | try! mem::copy(&mut RW[..@sizeOf([u64; shared::MAX_PACKAGES])], |
| 163 | @sliceOf(&bases[0] as *unsafe u8, @sizeOf([u64; shared::MAX_PACKAGES]))); |
| 164 | for entry in &catalogEntries[..2] { |
| 165 | let at = (bases[entry.package.slot] - rwAddress) as u32; |
| 166 | try shared::instantiate(&entry.package, &bases[..], &mut RW[at..at + entry.package.memory]) |
| 167 | catch error { report(error); return 1; }; |
| 168 | } |
| 169 | for i in 0..codeSize / 4 { set NATIVE[i] = encode::nop(); } |
| 170 | for entry in &catalogEntries[..count] { |
| 171 | let at = ((entry.package.codeAddress - CODE_ADDRESS) / 4) as u32; |
| 172 | for word, i in entry.package.code { set NATIVE[at + i] = word; } |
| 173 | } |
| 174 | trampoline(kernelAddress, roAddress, rwAddress, count); |
| 175 | let header = try! image::header(image::Layout { |
| 176 | entry: CODE_ADDRESS, |
| 177 | code: image::Segment { address: CODE_ADDRESS, initialized: codeSize, memory: codeSize }, |
| 178 | roData: image::Segment { address: roAddress, initialized: roSize, memory: roSize }, |
| 179 | rwData: image::Segment { address: rwAddress, initialized: rwSize, memory: rwSize }, |
| 180 | }); |
| 181 | let fd = unix::openOpts(env.args[4], unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420); |
| 182 | assert fd >= 0; |
| 183 | let written = unix::writeAll(fd, &header[..]) and unix::writeAll(fd, @sliceOf(&NATIVE[0] as *u8, codeSize)) and unix::writeAll(fd, &RO[..roSize]) and unix::writeAll(fd, &RW[..rwSize]); |
| 184 | let closed = unix::close(fd) == 0; |
| 185 | assert written and closed; |
| 186 | return 0; |
| 187 | } |