kernel/tools/build.rad 10.0 KiB 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
}