kernel/tools/build.rad 10.3 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
    let codeEmitter: 'code = &e in {
102
        for word, i in emit::getCode 'code (codeEmitter) {
103
            set NATIVE[i] = word;
104
        }
105
    }
106
}
107
108
/// Link shared boot packages, retain their catalog, and instantiate the kernel graph.
109
@default unsafe fn main(env: *sys::Env) -> i32 {
110
    assert env.args.len >= 5 and env.args.len <= PACKAGES + 3;
111
    let count = env.args.len - 3;
112
    let mut decoder = alloc::new(&mut DECODE[..]);
113
    let libraryBytesLength = unix::readFile(env.args[1], &mut INPUT[0][..]) else panic "std RIL";
114
    let libraryBytes = &INPUT[0][..libraryBytesLength];
115
    let libraryInput = try! program::decode(libraryBytes, &mut decoder, binary::Limits { registers: 8192, blocks: 4096 });
116
    let kernelBytesLength = unix::readFile(env.args[2], &mut INPUT[1][..]) else panic "kernel RIL";
117
    let kernelBytes = &INPUT[1][..kernelBytesLength];
118
    let kernelInput = try! program::decode(kernelBytes, &mut decoder, binary::Limits { registers: 8192, blocks: 4096 });
119
    assert mem::eq(libraryInput.name, "std") and mem::eq(kernelInput.name, "kernel");
120
    assert libraryInput.dependencies.len == 0;
121
    for dependency in kernelInput.dependencies {
122
        assert mem::eq(dependency, "std");
123
    }
124
    let sourceLength = unix::readFile(env.args[3], &mut SOURCE[..]) else panic "kernel assembly";
125
    let source = &SOURCE[..sourceLength];
126
    let mut assemblyArena = alloc::new(&mut ASSEMBLY[..]);
127
    let empty: *mut [u8] = &mut [];
128
    let assembly = try! asm::assemble(asm::scanner::SourceKind::String, source,
129
        &mut TEXT[..], &mut empty[..], &mut assemblyArena, &mut STRINGS, 0);
130
    let library = try compile(&libraryInput, 0, CODE_ADDRESS + 4096, &[],
131
        asm::Program { text: &[], data: &[], symbols: &[], externalFixups: &[] }) catch error {
132
            report(error);
133
            return 1;
134
        };
135
    let kernelAddress = aligned(library.codeAddress + library.code.len as u64 * 4, 4096);
136
    let kernel = try compile(&kernelInput, 1, kernelAddress, library.exports, assembly) catch error {
137
        report(error);
138
        return 1;
139
    };
140
    let mut catalogEntries: [catalog::Entry; PACKAGES] = undefined;
141
    set catalogEntries[0] = catalog::Entry { source: libraryBytes, package: library };
142
    set catalogEntries[1] = catalog::Entry { source: kernelBytes, package: kernel };
143
    let mut codeEnd = kernel.codeAddress + kernel.code.len as u64 * 4;
144
    for slot in 2..count {
145
        let bytesLength = unix::readFile(env.args[slot + 3], &mut INPUT[slot][..]) else panic "boot package RIL";
146
        let bytes = &INPUT[slot][..bytesLength];
147
        let input = try! program::decode(bytes, &mut decoder, binary::Limits { registers: 8192, blocks: 4096 });
148
        let mut imported: u32 = 0;
149
        for previous in 0..slot {
150
            for symbol in catalogEntries[previous].package.exports {
151
                assert imported < IMPORTS.len;
152
                set IMPORTS[imported] = symbol;
153
                set imported += 1;
154
            }
155
        }
156
        let package = try compile(&input, slot, aligned(codeEnd, 4096), &IMPORTS[..imported],
157
            asm::Program { text: &[], data: &[], symbols: &[], externalFixups: &[] }) catch error {
158
                report(error);
159
                return 1;
160
            };
161
        set catalogEntries[slot] = catalog::Entry { source: bytes, package };
162
        set codeEnd = package.codeAddress + package.code.len as u64 * 4;
163
    }
164
    let codeSize = (codeEnd - CODE_ADDRESS) as u32;
165
    assert codeEnd - CODE_ADDRESS <= @sizeOf([u32; 4194304]) as u64;
166
    let roAddress = aligned(codeEnd, 4096);
167
    let roSize = try catalog::pack(&catalogEntries[..count], roAddress, &mut RO[..]) catch error {
168
        report(error);
169
        return 1;
170
    };
171
    let rwAddress = aligned(roAddress + roSize as u64, 4096);
172
    let mut bases: [u64; shared::MAX_PACKAGES] = [0; shared::MAX_PACKAGES];
173
    let mut rwSize: u32 = @sizeOf([u64; shared::MAX_PACKAGES]);
174
    for entry in &catalogEntries[..2] {
175
        let at = aligned(rwAddress + rwSize as u64, entry.package.alignment);
176
        assert at - rwAddress <= RW.len as u64 and entry.package.memory <= RW.len - (at - rwAddress) as u32;
177
        set bases[entry.package.slot] = at;
178
        set rwSize = (at - rwAddress) as u32 + entry.package.memory;
179
    }
180
    try! mem::copy(&mut RW[..@sizeOf([u64; shared::MAX_PACKAGES])],
181
        @sliceOf(&bases[0] as *unsafe u8, @sizeOf([u64; shared::MAX_PACKAGES])));
182
    for entry in &catalogEntries[..2] {
183
        let at = (bases[entry.package.slot] - rwAddress) as u32;
184
        try shared::instantiate(&entry.package, &bases[..], &mut RW[at..at + entry.package.memory])
185
            catch error {
186
                report(error);
187
                return 1;
188
            };
189
    }
190
    for i in 0..codeSize / 4 {
191
        set NATIVE[i] = encode::nop();
192
    }
193
    for entry in &catalogEntries[..count] {
194
        let at = ((entry.package.codeAddress - CODE_ADDRESS) / 4) as u32;
195
        for word, i in entry.package.code {
196
            set NATIVE[at + i] = word;
197
        }
198
    }
199
    trampoline(kernelAddress, roAddress, rwAddress, count);
200
    let header = try! image::header(image::Layout {
201
        entry: CODE_ADDRESS,
202
        code: image::Segment { address: CODE_ADDRESS, initialized: codeSize, memory: codeSize },
203
        roData: image::Segment { address: roAddress, initialized: roSize, memory: roSize },
204
        rwData: image::Segment { address: rwAddress, initialized: rwSize, memory: rwSize },
205
    });
206
    let fd = unix::openOpts(env.args[4], unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420);
207
    assert fd >= 0;
208
    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]);
209
    let closed = unix::close(fd) == 0;
210
    assert written and closed;
211
    return 0;
212
}