kernel: activate private image instances and independent contexts
04ed5958ed7ad2a3599dbc975e8709c98e9da258a66bf9232b968980154cd5ac
Verified: make -C kernel check; activation checks pass and native contexts return 42, 44, and 42 across shared and independent instances.
1 parent
14a97c54
kernel/Makefile
+3 -3
| 7 | 7 | CORE_ASM := arch/atomic.ras arch/context.ras arch/clock.ras arch/mmio.ras arch/physical.ras |
|
| 8 | 8 | CORE := -pkg core -mod core.rad $(addprefix -mod ,$(MODULES) $(CORE_ASM)) |
|
| 9 | 9 | CHECK_MODULES := $(wildcard check/*.rad) |
|
| 10 | 10 | USER_INPUTS := user.rad $(wildcard user/*.rad user/*/*.rad) core/abi.rad |
|
| 11 | 11 | USER_BASE := -pkg abi -mod core/abi.rad -pkg user -mod user.rad -mod user/sys.rad -pkg probe -mod user/probe.rad |
|
| 12 | - | BASE_IMAGES := sample_root sample_control sample_scalars sample_memory sample_overflow sample_events sample_alias |
|
| 12 | + | BASE_IMAGES := sample_root sample_control sample_scalars sample_memory sample_overflow sample_events sample_alias sample_instance |
|
| 13 | 13 | BASE_RIL := $(addprefix build/,$(addsuffix .ril,$(BASE_IMAGES))) |
|
| 14 | 14 | IMAGE_MODULES_sample_control := user/sample_control/math.rad |
|
| 15 | 15 | BASE_CATALOG := -zero-bss -pkg images -mod build/baseline/images.rad -mod user/sys.ras $(addprefix -image ,$(BASE_RIL)) |
|
| 16 | 16 | MACHINE := $(EMU) -machine -no-guard-stack -max-steps=100000000 -count-instructions |
|
| 17 | 17 |
| 44 | 44 | $(COMPILE) $(BASE_CATALOG) $(CORE) -pkg context -start arch/entry.ras -mod context.rad -mod context/wait.rad -mod check/context.ras -entry context -o $@ |
|
| 45 | 45 | ||
| 46 | 46 | interrupt.rv64: interrupt.rad check/interrupt.ras arch/entry.ras core.rad $(MODULES) $(CORE_ASM) build/baseline/images.rad user/sys.ras $(COMPILER) |
|
| 47 | 47 | $(COMPILE) $(BASE_CATALOG) $(CORE) -pkg interrupt -start arch/entry.ras -mod interrupt.rad -mod check/interrupt.ras -entry interrupt -o $@ |
|
| 48 | 48 | ||
| 49 | - | native.rv64: native.rad arch/entry.ras core.rad $(MODULES) $(CORE_ASM) build/baseline/images.rad user/sys.ras $(COMPILER) |
|
| 50 | - | $(COMPILE) $(BASE_CATALOG) $(CORE) -pkg native -start arch/entry.ras -mod native.rad -entry native -o $@ |
|
| 49 | + | native.rv64: native.rad native/instances.rad arch/entry.ras core.rad $(MODULES) $(CORE_ASM) build/baseline/images.rad user/sys.ras $(COMPILER) |
|
| 50 | + | $(COMPILE) $(BASE_CATALOG) $(CORE) -pkg native -start arch/entry.ras -mod native.rad -mod native/instances.rad -entry native -o $@ |
|
| 51 | 51 | ||
| 52 | 52 | check: all check.rv64 context.rv64 interrupt.rv64 native.rv64 |
|
| 53 | 53 | $(HOST_EMU) -run check.rv64 |
|
| 54 | 54 | $(MACHINE) -run kernel.rv64 |
|
| 55 | 55 | $(MACHINE) -max-steps=1000000 -run context.rv64 |
kernel/NOTES.md
+20 -3
| 1 | 1 | # Kernel implementation decisions |
|
| 2 | 2 | ||
| 3 | 3 | The specification at https://radiant.computer/system/kernel takes precedence |
|
| 4 | 4 | for fixed call numbers, handle layout, rights, and object behavior. These notes |
|
| 5 | - | record the contracts established through step 17 of the 22-step plan. |
|
| 5 | + | record the contracts established through step 18 of the 22-step plan. |
|
| 6 | 6 | ||
| 7 | 7 | ## Source and trust boundary |
|
| 8 | 8 | ||
| 9 | 9 | - Kernel mechanisms use freestanding Radiance; RAS owns machine entry, register |
|
| 10 | 10 | state, atomics, and MMIO. Hosted checks exercise the same mechanism modules. |
| 239 | 239 | - -zero-bss includes zero-initialized kernel storage in native data sections so |
|
| 240 | 240 | the image header and firmware reservations cover globals. Physical clear/copy |
|
| 241 | 241 | callbacks accept validated owned ranges; constructing physical byte views is |
|
| 242 | 242 | unsafe. |
|
| 243 | 243 | ||
| 244 | + | ## Complete image instances |
|
| 245 | + | ||
| 246 | + | - DomainCreate preflights handle, domain, and terminal-event capacity. It copies |
|
| 247 | + | only the immutable initializer into owned private frames. Failed reservations |
|
| 248 | + | return provisional frames without installing authority. |
|
| 249 | + | - Activation requires Execute, a writable target Page stack, and readable |
|
| 250 | + | coverage of the complete nonempty argument range. A descending stack may start |
|
| 251 | + | at a Page's exclusive end with 16-byte alignment. |
|
| 252 | + | - Activation owns setup of each context's immutable 80-byte Env and private |
|
| 253 | + | 64 KiB native stack, rather than relying on machine-fixture setup. |
|
| 254 | + | - Contexts in one domain share writable image state and Events; different domains |
|
| 255 | + | have independent state. Creation and activation grant no budget or hart. |
|
| 256 | + | - Creation ancestry survives intermediate creator death. Slot reuse clears the |
|
| 257 | + | old incarnation's ancestry bit without erasing surviving ancestors. Private |
|
| 258 | + | frame ownership prevents reuse before reclamation. |
|
| 259 | + | - The eighth native image checks instance state: initial value 41 becomes 42 |
|
| 260 | + | and 44 across two contexts in one domain, while another domain reports 42. |
|
| 261 | + | ||
| 244 | 262 | ## Validation |
|
| 245 | 263 | ||
| 246 | 264 | Use the current machine-capable sibling emulator. Set `RAD_EMULATOR`, pass |
|
| 247 | 265 | `EMU` to the kernel Make invocation, or put `emulator` on PATH. The kernel build |
|
| 248 | 266 | checks compiler dependencies. From the repository root, run: |
|
| 249 | 267 | ||
| 250 | 268 | ```sh |
|
| 251 | 269 | make -C kernel check |
|
| 252 | - | make std-test bin-test |
|
| 253 | 270 | ``` |
|
| 254 | 271 | ||
| 255 | - | Run seven source-compiled binary images through native machine traps. The fixture handles PageAllocate (30), QueryPage (44), checked Page materialization (60), and Exit (49), plus breakpoint fault entry. Check control flow, scalar operations, Page bounds, Events consumption, and overlapping memory. Fixture setup supplies private state, Env, and stack; this is not the complete calls dispatcher. |
|
| 272 | + | Run the eight-image native workload and hosted activation checks. Check complete argument/stack coverage, independent private instances, and two contexts sharing one instance with reported values 42 and 44 versus 42 in another. |
|
| 256 | 273 | ||
| 257 | 274 | Run the context reservation probe with an emulator that retains LR/SC |
|
| 258 | 275 | reservations across traps. This checks the kernel's reservation invalidation. |
kernel/check.rad
+2 -0
| 11 | 11 | mod budgets; |
|
| 12 | 12 | mod budget_caps; |
|
| 13 | 13 | mod timers; |
|
| 14 | 14 | mod notifications; |
|
| 15 | 15 | mod devices; |
|
| 16 | + | mod activation; |
|
| 16 | 17 | ||
| 17 | 18 | /// Run the available kernel mechanism checks. |
|
| 18 | 19 | @default fn main() -> u32 { |
|
| 19 | 20 | frames::run(); |
|
| 20 | 21 | boot::run(); |
| 26 | 27 | budgets::run(); |
|
| 27 | 28 | budget_caps::run(); |
|
| 28 | 29 | timers::run(); |
|
| 29 | 30 | notifications::run(); |
|
| 30 | 31 | devices::run(); |
|
| 32 | + | activation::run(); |
|
| 31 | 33 | return 0; |
|
| 32 | 34 | } |
kernel/check/activation.rad
added
+174 -0
| 1 | + | //! Transactional activation, live Page bounds, and private context ownership. |
|
| 2 | + | ||
| 3 | + | use core::abi; |
|
| 4 | + | use core::activation; |
|
| 5 | + | use core::capabilities; |
|
| 6 | + | use core::contexts; |
|
| 7 | + | use core::domains; |
|
| 8 | + | use core::events; |
|
| 9 | + | use core::fdt; |
|
| 10 | + | use core::frames; |
|
| 11 | + | use core::handles; |
|
| 12 | + | use core::memory; |
|
| 13 | + | use core::pages; |
|
| 14 | + | use core::platform; |
|
| 15 | + | use core::resources; |
|
| 16 | + | use core::state; |
|
| 17 | + | use images; |
|
| 18 | + | ||
| 19 | + | /// Test-owned physical memory for Page and private-stack allocations. |
|
| 20 | + | static RAM: [u8; 64 * frames::PAGE_SIZE] = undefined; |
|
| 21 | + | /// Domain slots include spare creation capacity unless explicitly exhausted. |
|
| 22 | + | static DOMAINS: [domains::Domain; 3] = undefined; |
|
| 23 | + | /// Page resource metadata. |
|
| 24 | + | static OBJECTS: [resources::Slot; 8] = undefined; |
|
| 25 | + | /// Two incarnations let us distinguish initial and additional contexts. |
|
| 26 | + | static CONTEXTS: [contexts::Context; 2] = undefined; |
|
| 27 | + | /// Allocation and persistent Page lifetime metadata. |
|
| 28 | + | static POOL: frames::Pool = undefined; |
|
| 29 | + | static PINS: [u16; 64] = undefined; |
|
| 30 | + | static ASSIGNED: [bool; 64] = undefined; |
|
| 31 | + | static GRANTS: [u64; 3] = undefined; |
|
| 32 | + | /// Shared state for the exercised public operations. |
|
| 33 | + | static KERNEL: state::State = undefined; |
|
| 34 | + | ||
| 35 | + | /// Zero only the supplied simulated physical range. |
|
| 36 | + | fn clear(base: u64, size: u32) { |
|
| 37 | + | assert base <= RAM.len as u64 and size as u64 <= RAM.len as u64 - base; |
|
| 38 | + | for i in base as u32..base as u32 + size { set RAM[i] = 0; } |
|
| 39 | + | } |
|
| 40 | + | ||
| 41 | + | /// Creation rejection must happen before any initializer can be copied. |
|
| 42 | + | fn unexpectedCopy(destination: u64, source: u64, size: u32) { |
|
| 43 | + | panic "unexpectedCopy: rejected creation reached initializer"; |
|
| 44 | + | } |
|
| 45 | + | ||
| 46 | + | /// Fresh state; the low-level child supplies an admitted entry for bounds tests. |
|
| 47 | + | fn setup() -> abi::Handle { |
|
| 48 | + | let mut machine: platform::Platform = undefined; |
|
| 49 | + | set machine.memoryCount = 1; |
|
| 50 | + | set machine.memory[0] = fdt::Range { base: 0, size: RAM.len as u64 }; |
|
| 51 | + | set machine.reservedCount = 0; |
|
| 52 | + | try! frames::init(&mut POOL, &machine); |
|
| 53 | + | let mut ram: memory::Memory = undefined; |
|
| 54 | + | memory::init(&mut ram, &mut POOL, &mut PINS[..], &mut ASSIGNED[..], &mut GRANTS[..], 3); |
|
| 55 | + | domains::init(&mut DOMAINS[..]); |
|
| 56 | + | resources::init(&mut OBJECTS[..]); |
|
| 57 | + | for i in 0..CONTEXTS.len { contexts::init(&mut CONTEXTS[i], i); } |
|
| 58 | + | set KERNEL = state::State { domains: &mut DOMAINS[..], resources: &mut OBJECTS[..], contexts: &mut CONTEXTS[..], memory: ram }; |
|
| 59 | + | let handle = domains::root(&mut DOMAINS[..], images::ROOT); |
|
| 60 | + | let root = try! domains::resolve(&DOMAINS[..], 0, handle, abi::CREATE); |
|
| 61 | + | let child = try! domains::create(&mut DOMAINS[..], root, images::ROOT); |
|
| 62 | + | return handles::install(&mut DOMAINS[0].handles, 2, child, abi::DOMAIN_RIGHTS); |
|
| 63 | + | } |
|
| 64 | + | ||
| 65 | + | /// Read a rejected call's public error without accepting accidental success. |
|
| 66 | + | fn createError(authority: abi::Handle, image: u64) -> abi::Error { |
|
| 67 | + | let _handle = try activation::create(&mut KERNEL, 0, authority, image, clear, unexpectedCopy) catch error { |
|
| 68 | + | return error; |
|
| 69 | + | }; |
|
| 70 | + | panic "createError: creation unexpectedly succeeded"; |
|
| 71 | + | } |
|
| 72 | + | ||
| 73 | + | /// Read activation errors for the initial or an additional context. |
|
| 74 | + | fn startError(target: abi::Handle, stack: u64, args: u64, size: u64, initial: bool) -> abi::Error { |
|
| 75 | + | if initial { |
|
| 76 | + | let _id = try activation::activate(&mut KERNEL, 0, target, stack, args, size, clear) catch error { |
|
| 77 | + | return error; |
|
| 78 | + | }; |
|
| 79 | + | } else { |
|
| 80 | + | let _id = try activation::context(&mut KERNEL, 0, target, stack, args, size, clear) catch error { |
|
| 81 | + | return error; |
|
| 82 | + | }; |
|
| 83 | + | } |
|
| 84 | + | panic "startError: context unexpectedly started"; |
|
| 85 | + | } |
|
| 86 | + | ||
| 87 | + | /// A rejected image/capacity request cannot consume authority or an incarnation. |
|
| 88 | + | fn creation() { |
|
| 89 | + | let target = setup(); |
|
| 90 | + | let self = abi::Handle { bits: 0 }; |
|
| 91 | + | assert createError(self, images::COUNT as u64) == abi::Error::VerifyFailed; |
|
| 92 | + | assert createError(self, 0x100000000) == abi::Error::VerifyFailed; |
|
| 93 | + | assert createError(target, images::ROOT as u64) == abi::Error::Denied; |
|
| 94 | + | let child = try! domains::resolve(&DOMAINS[..], 0, target, abi::EXECUTE); |
|
| 95 | + | let foreign = handles::install(&mut DOMAINS[0].handles, 3, child, abi::CREATE); |
|
| 96 | + | assert createError(foreign, images::ROOT as u64) == abi::Error::Denied; |
|
| 97 | + | set DOMAINS[0].events.reserved = events::CRITICAL; |
|
| 98 | + | assert createError(self, images::ROOT as u64) == abi::Error::Exhausted; |
|
| 99 | + | assert DOMAINS[0].events.reserved == events::CRITICAL; |
|
| 100 | + | set DOMAINS[0].events.reserved = 1; |
|
| 101 | + | let root = try! domains::authority(&DOMAINS[..], 0, self, abi::CREATE); |
|
| 102 | + | for i in 4..abi::MAX_HANDLES { |
|
| 103 | + | let _handle = handles::install(&mut DOMAINS[0].handles, i, root, abi::EXECUTE); |
|
| 104 | + | } |
|
| 105 | + | assert createError(self, images::ROOT as u64) == abi::Error::Exhausted; |
|
| 106 | + | for i in 4..abi::MAX_HANDLES { let _entry = handles::remove(&mut DOMAINS[0].handles, i); } |
|
| 107 | + | assert DOMAINS[2].epoch == 0 and DOMAINS[2].state == domains::Lifecycle::Dead; |
|
| 108 | + | let _last = try! domains::create(&mut DOMAINS[..], root, images::ROOT); |
|
| 109 | + | assert createError(self, images::ROOT as u64) == abi::Error::Exhausted; |
|
| 110 | + | assert POOL.available == 64; |
|
| 111 | + | assert domains::live(&DOMAINS[..], try! domains::resolve(&DOMAINS[..], 0, target, abi::EXECUTE)); |
|
| 112 | + | } |
|
| 113 | + | ||
| 114 | + | /// Verify stack edges, adjacent args, allocation rollback, and context isolation. |
|
| 115 | + | export fn run() { |
|
| 116 | + | creation(); |
|
| 117 | + | let target = setup(); |
|
| 118 | + | let whole = try! pages::allocate(&mut KERNEL, 0, abi::Handle { bits: 0 }, 4, clear); |
|
| 119 | + | let right = try! pages::split(&mut KERNEL, 0, whole, 2); |
|
| 120 | + | let _leftGrant = try! capabilities::grant(&mut KERNEL, 0, whole, target, (abi::READ | abi::WRITE) as u64); |
|
| 121 | + | let rightGrant = try! capabilities::grant(&mut KERNEL, 0, right, target, abi::READ as u64); |
|
| 122 | + | let restricted = handles::install(&mut DOMAINS[0].handles, try! handles::vacant(&DOMAINS[0].handles), |
|
| 123 | + | try! domains::resolve(&DOMAINS[..], 0, target, abi::EXECUTE), abi::WAKE); |
|
| 124 | + | assert startError(restricted, 8192, 4096, 8192, true) == abi::Error::Denied; |
|
| 125 | + | assert startError(target, 8192, 4096, 8192, false) == abi::Error::Busy; |
|
| 126 | + | assert startError(target, 0, 0, 0, true) == abi::Error::InvalidArg; |
|
| 127 | + | assert startError(target, 8191, 0, 0, true) == abi::Error::InvalidArg; |
|
| 128 | + | assert startError(target, 8208, 0, 0, true) == abi::Error::InvalidArg; |
|
| 129 | + | assert startError(target, 8192, 4096, 12289, true) == abi::Error::InvalidArg; |
|
| 130 | + | assert startError(target, 8192, 0xfffffffffffffff0, 32, true) == abi::Error::InvalidArg; |
|
| 131 | + | // A dropped readable handle no longer authorizes args, even while its |
|
| 132 | + | // persistent lifetime pin keeps the physical bytes from being reused. |
|
| 133 | + | try! capabilities::drop(&mut KERNEL, 1, rightGrant); |
|
| 134 | + | assert startError(target, 8192, 4096, 8192, true) == abi::Error::InvalidArg; |
|
| 135 | + | let _readAgain = try! capabilities::grant(&mut KERNEL, 0, right, target, abi::READ as u64); |
|
| 136 | + | set CONTEXTS[0].epoch = 0xffffffff; |
|
| 137 | + | set CONTEXTS[1].epoch = 0xffffffff; |
|
| 138 | + | assert startError(target, 8192, 4096, 8192, true) == abi::Error::Exhausted; |
|
| 139 | + | set CONTEXTS[0].epoch = 0; |
|
| 140 | + | set CONTEXTS[1].epoch = 0; |
|
| 141 | + | let held = try! frames::allocate(&mut POOL, POOL.available, clear); |
|
| 142 | + | assert startError(target, 8192, 4096, 8192, true) == abi::Error::OutOfMemory; |
|
| 143 | + | assert DOMAINS[1].state == domains::Lifecycle::Pending and DOMAINS[1].env.context == 0; |
|
| 144 | + | assert CONTEXTS[0].status == contexts::Status::Vacant and CONTEXTS[0].epoch == 0; |
|
| 145 | + | assert CONTEXTS[1].status == contexts::Status::Vacant and CONTEXTS[1].epoch == 0; |
|
| 146 | + | frames::discard(&mut POOL, held); |
|
| 147 | + | let first = try! activation::activate(&mut KERNEL, 0, target, 8192, 4096, 8192, clear); |
|
| 148 | + | assert DOMAINS[1].state == domains::Lifecycle::Active; |
|
| 149 | + | let a = try! contexts::resolve(&CONTEXTS[..], first); |
|
| 150 | + | assert DOMAINS[1].env.context == first; |
|
| 151 | + | assert CONTEXTS[a].env.stackBase == 0 and CONTEXTS[a].env.stackTop == 8192; |
|
| 152 | + | assert CONTEXTS[a].env.argsPointer == 4096 and CONTEXTS[a].env.argsSize == 8192; |
|
| 153 | + | assert CONTEXTS[a].env.eventsPointer == &DOMAINS[1].events.ring as u64; |
|
| 154 | + | assert CONTEXTS[a].env.privateStackTop - CONTEXTS[a].env.privateStackBase == activation::PRIVATE_STACK_SIZE as u64; |
|
| 155 | + | assert CONTEXTS[a].frame.pc == images::get(images::ROOT).entry; |
|
| 156 | + | assert startError(target, 8192, 0, 0, true) == abi::Error::NotPending; |
|
| 157 | + | // Empty args need no Page, even for an otherwise invalid pointer. |
|
| 158 | + | let second = try! activation::context(&mut KERNEL, 0, target, 4096, 0xffffffffffffffff, 0, clear); |
|
| 159 | + | let b = try! contexts::resolve(&CONTEXTS[..], second); |
|
| 160 | + | assert first <> second and a <> b; |
|
| 161 | + | assert CONTEXTS[b].env.stackTop == 4096 and DOMAINS[1].env.stackTop == 8192; |
|
| 162 | + | assert DOMAINS[1].env.context == first; |
|
| 163 | + | assert CONTEXTS[a].env.privateStackTop <= CONTEXTS[b].env.privateStackBase |
|
| 164 | + | or CONTEXTS[b].env.privateStackTop <= CONTEXTS[a].env.privateStackBase; |
|
| 165 | + | assert CONTEXTS[a].budget.kind == abi::Kind::Empty and CONTEXTS[b].budget.kind == abi::Kind::Empty; |
|
| 166 | + | assert CONTEXTS[a].hart == contexts::NO_HART and CONTEXTS[b].hart == contexts::NO_HART; |
|
| 167 | + | assert startError(target, 8192, 0, 0, false) == abi::Error::Exhausted; |
|
| 168 | + | assert try! contexts::resolve(&CONTEXTS[..], first) == a; |
|
| 169 | + | assert try! contexts::resolve(&CONTEXTS[..], second) == b; |
|
| 170 | + | assert POOL.available == 64 - 4 - 2 * (activation::PRIVATE_STACK_SIZE / frames::PAGE_SIZE); |
|
| 171 | + | for i in 4..64 { |
|
| 172 | + | assert not ASSIGNED[i] and PINS[i] == 0; |
|
| 173 | + | } |
|
| 174 | + | } |
kernel/core.rad
+1 -0
| 22 | 22 | export mod notifications; |
|
| 23 | 23 | export mod devices; |
|
| 24 | 24 | export mod interrupts; |
|
| 25 | 25 | export mod mmio; |
|
| 26 | 26 | export mod physical; |
|
| 27 | + | export mod activation; |
kernel/core/activation.rad
added
+182 -0
| 1 | + | //! Capability-mediated image instances and private execution startup state. |
|
| 2 | + | //! |
|
| 3 | + | //! Callers hold the kernel critical section across each operation, including |
|
| 4 | + | //! callbacks, and supply a live caller index. Domain/context storage must not |
|
| 5 | + | //! move while live environments or Events pointers refer into it. Callbacks |
|
| 6 | + | //! cannot fail, reenter the kernel, mutate kernel metadata, or retain a range. |
|
| 7 | + | ||
| 8 | + | use core::abi; |
|
| 9 | + | use core::contexts; |
|
| 10 | + | use core::domains; |
|
| 11 | + | use core::events; |
|
| 12 | + | use core::frames; |
|
| 13 | + | use core::handles; |
|
| 14 | + | use core::pages; |
|
| 15 | + | use core::resources; |
|
| 16 | + | use core::state; |
|
| 17 | + | use images; |
|
| 18 | + | ||
| 19 | + | /// Fixed private native stack per context: 64 KiB for locals and saved registers. |
|
| 20 | + | /// The authorized Page stack has separate bounds in Env. |
|
| 21 | + | export constant PRIVATE_STACK_SIZE: u32 = 64 * 1024; |
|
| 22 | + | ||
| 23 | + | /// Exclusive private initializer storage for one domain instance. |
|
| 24 | + | export record ImageState: Copy { |
|
| 25 | + | /// Owned frames, or an empty run for a stateless graph. |
|
| 26 | + | run: frames::Run, |
|
| 27 | + | /// Aligned mutable-state base within the owned run. |
|
| 28 | + | base: u64, |
|
| 29 | + | } |
|
| 30 | + | ||
| 31 | + | /// Allocate and copy a checked catalog initializer before domain installation. |
|
| 32 | + | /// The caller installs the returned ownership or reclaims it on failure. |
|
| 33 | + | export fn initialize(pool: *mut frames::Pool, image: u32, clear: fn(u64, u32), copy: fn(u64, u64, u32)) -> ImageState throws (abi::Error) { |
|
| 34 | + | let descriptor = images::get(image); |
|
| 35 | + | let mut run = frames::Run { first: 0, count: 0 }; |
|
| 36 | + | let mut base: u64 = 0; |
|
| 37 | + | if descriptor.stateSize <> 0 { |
|
| 38 | + | let alignment = descriptor.stateAlignment as u64; |
|
| 39 | + | // These are trusted compiler facts, not caller-provided metadata. |
|
| 40 | + | assert alignment <> 0 and alignment & (alignment - 1) == 0; |
|
| 41 | + | let mut padding: u64 = 0; |
|
| 42 | + | if alignment > frames::PAGE_SIZE as u64 { |
|
| 43 | + | // Every run already starts on a frame boundary. |
|
| 44 | + | set padding = alignment - frames::PAGE_SIZE as u64; |
|
| 45 | + | } |
|
| 46 | + | let bytes = descriptor.stateSize as u64 + padding; |
|
| 47 | + | let count = (bytes + frames::PAGE_SIZE as u64 - 1) / frames::PAGE_SIZE as u64; |
|
| 48 | + | if count > frames::MAX_FRAMES as u64 { throw abi::Error::OutOfMemory; } |
|
| 49 | + | let allocation = try frames::allocate(pool, count as u32, clear) catch { |
|
| 50 | + | throw abi::Error::OutOfMemory; |
|
| 51 | + | }; |
|
| 52 | + | set run = frames::install(allocation); |
|
| 53 | + | let first = run.first as u64 * frames::PAGE_SIZE as u64; |
|
| 54 | + | set base = (first + alignment - 1) & ~(alignment - 1); |
|
| 55 | + | copy(base, descriptor.initial, descriptor.stateSize); |
|
| 56 | + | descriptor.relocate(base); |
|
| 57 | + | } |
|
| 58 | + | return ImageState { run, base }; |
|
| 59 | + | } |
|
| 60 | + | ||
| 61 | + | /// Instantiate an admitted catalog graph under the caller's Create authority. |
|
| 62 | + | /// image is only a catalog index, never an entry address or runtime descriptor. |
|
| 63 | + | /// clear(base, size) zeros exactly the supplied physical allocation; |
|
| 64 | + | /// copy(destination, source, size) copies exactly the immutable initializer. |
|
| 65 | + | /// Both callbacks obey the serialization and lifetime rules of this module. |
|
| 66 | + | /// All capacity checks precede allocation; failure installs no authority or |
|
| 67 | + | /// domain incarnation and returns any provisionally allocated private frames. |
|
| 68 | + | export fn create(kernel: *mut state::State, caller: u32, authority: abi::Handle, image: u64, clear: fn(u64, u32), copy: fn(u64, u64, u32)) -> abi::Handle throws (abi::Error) { |
|
| 69 | + | let creator = try domains::authority(kernel.domains, caller, authority, abi::CREATE); |
|
| 70 | + | if creator.index <> caller { throw abi::Error::Denied; } |
|
| 71 | + | if image >= images::COUNT as u64 { throw abi::Error::VerifyFailed; } |
|
| 72 | + | let slot = try handles::vacant(&kernel.domains[caller].handles); |
|
| 73 | + | let _domainSlot = try domains::vacant(kernel.domains); |
|
| 74 | + | // Refresh only consumed event credits; do not reserve a child until commit. |
|
| 75 | + | try events::refresh(&mut kernel.domains[caller].events); |
|
| 76 | + | if kernel.domains[caller].events.reserved == events::CRITICAL { |
|
| 77 | + | throw abi::Error::Exhausted; |
|
| 78 | + | } |
|
| 79 | + | let initial = try initialize(kernel.memory.pool, image as u32, clear, copy); |
|
| 80 | + | // The consumer can publish another head during allocation. Revalidate it |
|
| 81 | + | // when reserving the terminal event; even that late failure returns RAM. |
|
| 82 | + | let object = try domains::create(kernel.domains, creator, image as u32) catch error { |
|
| 83 | + | if initial.run.count <> 0 { frames::reclaim(kernel.memory.pool, initial.run); } |
|
| 84 | + | throw error; |
|
| 85 | + | }; |
|
| 86 | + | set kernel.domains[object.index].private = initial.run; |
|
| 87 | + | set kernel.domains[object.index].env.stateBase = initial.base; |
|
| 88 | + | return handles::install(&mut kernel.domains[caller].handles, slot, object, abi::DOMAIN_RIGHTS); |
|
| 89 | + | } |
|
| 90 | + | ||
| 91 | + | /// Return the base of a live writable Page containing bytes below stack. |
|
| 92 | + | /// A descending stack may start at the Page's exclusive end, but not its base. |
|
| 93 | + | fn stackBase(kernel: *state::State, domain: u32, stack: u64) -> u64 throws (abi::Error) { |
|
| 94 | + | if stack & 15 <> 0 { throw abi::Error::InvalidArg; } |
|
| 95 | + | for i in 0..abi::MAX_HANDLES { |
|
| 96 | + | let entry = &kernel.domains[domain].handles.entries[i]; |
|
| 97 | + | if entry.object.kind <> abi::Kind::Page or entry.rights & abi::WRITE == 0 |
|
| 98 | + | or not resources::live(kernel.resources, kernel.domains, entry.object) { continue; } |
|
| 99 | + | let case resources::Value::Page(page) = kernel.resources[entry.object.index].value else { panic "stackBase: page kind mismatch"; }; |
|
| 100 | + | let base = page.run.first as u64 * frames::PAGE_SIZE as u64; |
|
| 101 | + | let end = base + page.run.count as u64 * frames::PAGE_SIZE as u64; |
|
| 102 | + | if stack > base and stack <= end { return base; } |
|
| 103 | + | } |
|
| 104 | + | throw abi::Error::InvalidArg; |
|
| 105 | + | } |
|
| 106 | + | ||
| 107 | + | /// Prepare one context without altering the domain until all fallible work ends. |
|
| 108 | + | fn start(kernel: *mut state::State, owner: abi::Object, stack: u64, args: u64, size: u64, clear: fn(u64, u32), initial: bool) -> u64 throws (abi::Error) { |
|
| 109 | + | let lifecycle = kernel.domains[owner.index].state; |
|
| 110 | + | if initial { |
|
| 111 | + | if lifecycle <> domains::Lifecycle::Pending { throw abi::Error::NotPending; } |
|
| 112 | + | } else { |
|
| 113 | + | if lifecycle <> domains::Lifecycle::Active { throw abi::Error::Busy; } |
|
| 114 | + | } |
|
| 115 | + | let base = try stackBase(kernel, owner.index, stack); |
|
| 116 | + | if not pages::covers(kernel, owner.index, args, size, abi::READ) { |
|
| 117 | + | throw abi::Error::InvalidArg; |
|
| 118 | + | } |
|
| 119 | + | let mut slot = kernel.contexts.len; |
|
| 120 | + | for i in 0..kernel.contexts.len { |
|
| 121 | + | let context = &kernel.contexts[i]; |
|
| 122 | + | if context.status == contexts::Status::Vacant and context.epoch < 0xffffffff { |
|
| 123 | + | set slot = i; |
|
| 124 | + | break; |
|
| 125 | + | } |
|
| 126 | + | } |
|
| 127 | + | if slot == kernel.contexts.len { throw abi::Error::Exhausted; } |
|
| 128 | + | let domain = &kernel.domains[owner.index]; |
|
| 129 | + | assert domain.image < images::COUNT; |
|
| 130 | + | let descriptor = images::get(domain.image); |
|
| 131 | + | let allocation = try frames::allocate(kernel.memory.pool, PRIVATE_STACK_SIZE / frames::PAGE_SIZE, clear) catch { |
|
| 132 | + | throw abi::Error::OutOfMemory; |
|
| 133 | + | }; |
|
| 134 | + | let private = frames::install(allocation); |
|
| 135 | + | let privateBase = private.first as u64 * frames::PAGE_SIZE as u64; |
|
| 136 | + | let env = domains::Env { |
|
| 137 | + | argsPointer: args, argsSize: size, |
|
| 138 | + | eventsHandle: domain.env.eventsHandle, |
|
| 139 | + | eventsPointer: &domain.events.ring as u64, |
|
| 140 | + | stateBase: domain.env.stateBase, |
|
| 141 | + | stackBase: base, stackTop: stack, context: 0, |
|
| 142 | + | privateStackBase: privateBase, |
|
| 143 | + | privateStackTop: privateBase + PRIVATE_STACK_SIZE as u64, |
|
| 144 | + | }; |
|
| 145 | + | // Architectural sp starts at the authorized Page stack. The image entry |
|
| 146 | + | // selects env.privateStackTop for native locals and saved registers. |
|
| 147 | + | try contexts::prepare(&mut kernel.contexts[slot], owner, descriptor.entry, stack, env) catch error { |
|
| 148 | + | frames::reclaim(kernel.memory.pool, private); |
|
| 149 | + | throw error; |
|
| 150 | + | }; |
|
| 151 | + | set kernel.contexts[slot].private = private; |
|
| 152 | + | let id = contexts::identity(&kernel.contexts[slot]); |
|
| 153 | + | if initial { |
|
| 154 | + | set kernel.domains[owner.index].env = kernel.contexts[slot].env; |
|
| 155 | + | set kernel.domains[owner.index].state = domains::Lifecycle::Active; |
|
| 156 | + | } |
|
| 157 | + | return id; |
|
| 158 | + | } |
|
| 159 | + | ||
| 160 | + | /// Activate a Pending domain at its admitted entry and return its context ID. |
|
| 161 | + | /// Execute is required on target. stack is 16-aligned and above the base of a |
|
| 162 | + | /// live writable Page held by target, at most its exclusive end. Nonempty args |
|
| 163 | + | /// must be wholly covered by target's live readable Pages, including adjacency. |
|
| 164 | + | /// Failure leaves the domain Pending and context slots unchanged. No budget is |
|
| 165 | + | /// minted or bound and no hart is chosen. The dispatcher may discard this ID |
|
| 166 | + | /// for the void DomainActivate ABI. clear zeros exactly its supplied physical |
|
| 167 | + | /// byte range and obeys this module's caller serialization/callback invariants. |
|
| 168 | + | export fn activate(kernel: *mut state::State, caller: u32, target: abi::Handle, stack: u64, args: u64, size: u64, clear: fn(u64, u32)) -> u64 throws (abi::Error) { |
|
| 169 | + | let owner = try domains::resolve(kernel.domains, caller, target, abi::EXECUTE); |
|
| 170 | + | return try start(kernel, owner, stack, args, size, clear, true); |
|
| 171 | + | } |
|
| 172 | + | ||
| 173 | + | /// Add a context to an Active domain, always at that domain's admitted entry. |
|
| 174 | + | /// Requires Execute and the same stack/args and serialized clear contract as |
|
| 175 | + | /// activate; a Pending target returns Busy. Each context owns a distinct Env |
|
| 176 | + | /// and private stack but shares image state and Events with its domain. The |
|
| 177 | + | /// first environment/identity stays in Domain.env. No budget or hart is bound. |
|
| 178 | + | /// Failure preserves every existing context and the target's Active state. |
|
| 179 | + | export fn context(kernel: *mut state::State, caller: u32, target: abi::Handle, stack: u64, args: u64, size: u64, clear: fn(u64, u32)) -> u64 throws (abi::Error) { |
|
| 180 | + | let owner = try domains::resolve(kernel.domains, caller, target, abi::EXECUTE); |
|
| 181 | + | return try start(kernel, owner, stack, args, size, clear, false); |
|
| 182 | + | } |
kernel/core/domains.rad
+48 -12
| 1 | 1 | //! Domain identities, initial authority, and private startup state. |
|
| 2 | 2 | ||
| 3 | 3 | use core::abi; |
|
| 4 | 4 | use core::events; |
|
| 5 | + | use core::frames; |
|
| 5 | 6 | use core::handles; |
|
| 6 | 7 | ||
| 7 | 8 | /// Domain state; Dying is an internal quiescence phase. |
|
| 8 | 9 | export union Lifecycle: Copy { |
|
| 9 | 10 | /// Startup capabilities can be installed before activation. |
| 26 | 27 | eventsHandle: u64, |
|
| 27 | 28 | /// Physical address of this domain's shared event ring. |
|
| 28 | 29 | eventsPointer: u64, |
|
| 29 | 30 | /// Physical base of this image instance's mutable data. |
|
| 30 | 31 | stateBase: u64, |
|
| 31 | - | /// Lowest address available to Page-backed local stack frames. |
|
| 32 | + | /// Lowest address in the authorized Page stack. |
|
| 32 | 33 | stackBase: u64, |
|
| 33 | - | /// Exclusive upper bound of the addressable local stack. |
|
| 34 | + | /// Exclusive upper bound of the authorized Page stack. |
|
| 34 | 35 | stackTop: u64, |
|
| 35 | 36 | /// Kernel execution-context identity. |
|
| 36 | 37 | context: u64, |
|
| 37 | - | /// Lowest private address for SSA values and saved registers. |
|
| 38 | + | /// Lowest native stack address for locals and saved registers. |
|
| 38 | 39 | privateStackBase: u64, |
|
| 39 | 40 | /// Exclusive upper bound of the never-granted private stack. |
|
| 40 | 41 | privateStackTop: u64, |
|
| 41 | 42 | } |
|
| 42 | 43 |
| 50 | 51 | epoch: u32, |
|
| 51 | 52 | /// Protection-domain lifecycle. |
|
| 52 | 53 | state: Lifecycle, |
|
| 53 | 54 | /// Historical creator identity; independent of the current parent. |
|
| 54 | 55 | creator: abi::Object, |
|
| 56 | + | /// Creation ancestry by slot; reuse clears only that slot's historical bit. |
|
| 57 | + | /// Surviving ancestor bits outlive intermediate creator incarnations. |
|
| 58 | + | ancestors: [u64; abi::MAX_DOMAINS / 64], |
|
| 55 | 59 | /// Current receiver of terminal lifecycle notifications. |
|
| 56 | 60 | parent: abi::Object, |
|
| 61 | + | /// First terminal event kind: Fault=3 or ChildExit=4; zero while live. |
|
| 62 | + | terminalKind: u16, |
|
| 63 | + | /// First terminal fault code or exit status; immutable while Dying. |
|
| 64 | + | terminalCode: u32, |
|
| 57 | 65 | /// Trusted immutable image registry index. |
|
| 58 | 66 | image: u32, |
|
| 67 | + | /// Exclusive image-state frames, never Page claims or lifetime pins. |
|
| 68 | + | /// A zero count means empty state; reclaim and zero after quiescence. |
|
| 69 | + | /// env.stateBase may be aligned inside this run rather than at its base. |
|
| 70 | + | private: frames::Run, |
|
| 59 | 71 | /// Initial execution environment. |
|
| 60 | 72 | env: Env, |
|
| 61 | 73 | } |
|
| 62 | 74 | ||
| 63 | 75 | /// Return the identity used for a missing parent or creator. |
| 73 | 85 | handles::init(&mut domain.handles); |
|
| 74 | 86 | events::init(&mut domain.events); |
|
| 75 | 87 | set domain.epoch = 0; |
|
| 76 | 88 | set domain.state = Lifecycle::Dead; |
|
| 77 | 89 | set domain.creator = none(); |
|
| 90 | + | set domain.ancestors = [0; abi::MAX_DOMAINS / 64]; |
|
| 78 | 91 | set domain.parent = none(); |
|
| 92 | + | set domain.terminalKind = 0; |
|
| 93 | + | set domain.terminalCode = 0; |
|
| 79 | 94 | set domain.image = 0; |
|
| 95 | + | set domain.private = frames::Run { first: 0, count: 0 }; |
|
| 80 | 96 | set domain.env = Env { |
|
| 81 | 97 | argsPointer: 0, argsSize: 0, eventsHandle: 0, eventsPointer: 0, |
|
| 82 | 98 | stateBase: 0, stackBase: 0, stackTop: 0, context: 0, |
|
| 83 | 99 | privateStackBase: 0, privateStackTop: 0, |
|
| 84 | 100 | }; |
| 118 | 134 | throw abi::Error::Denied; |
|
| 119 | 135 | } |
|
| 120 | 136 | ||
| 121 | 137 | /// Prepare a free slot with an Events handle and no ambient authority. |
|
| 122 | 138 | fn prepare(domains: *mut [Domain], index: u32, creator: abi::Object, image: u32) -> abi::Object { |
|
| 139 | + | // Index bits name only the current incarnation. Remove the old incarnation |
|
| 140 | + | // without erasing surviving ancestry through an exited intermediate creator. |
|
| 141 | + | let word = index / 64; |
|
| 142 | + | let bit = 1 as u64 << (index as u64 & 63); |
|
| 143 | + | for i in 0..domains.len { set domains[i].ancestors[word] &= ~bit; } |
|
| 123 | 144 | let domain = &mut domains[index]; |
|
| 124 | 145 | assert domain.state == Lifecycle::Dead and domain.epoch < 0xffffffff; |
|
| 146 | + | assert domain.private.count == 0; |
|
| 125 | 147 | for i in 0..abi::MAX_HANDLES { |
|
| 126 | 148 | assert domain.handles.entries[i].object.kind == abi::Kind::Empty; |
|
| 127 | 149 | } |
|
| 128 | 150 | set domain.epoch += 1; |
|
| 129 | 151 | set domain.creator = creator; |
|
| 152 | + | set domain.ancestors = [0; abi::MAX_DOMAINS / 64]; |
|
| 153 | + | if creator.kind == abi::Kind::Domain { |
|
| 154 | + | set domain.ancestors = domains[creator.index].ancestors; |
|
| 155 | + | set domain.ancestors[creator.index / 64] |= 1 as u64 << (creator.index as u64 & 63); |
|
| 156 | + | } |
|
| 130 | 157 | set domain.parent = creator; |
|
| 158 | + | set domain.terminalKind = 0; |
|
| 159 | + | set domain.terminalCode = 0; |
|
| 131 | 160 | set domain.image = image; |
|
| 132 | 161 | events::init(&mut domain.events); |
|
| 133 | 162 | let object = abi::Object { kind: abi::Kind::Domain, index, epoch: domain.epoch }; |
|
| 134 | 163 | let eventObject = abi::Object { kind: abi::Kind::Events, index, epoch: domain.epoch }; |
|
| 135 | 164 | let eventHandle = handles::install(&mut domain.handles, 1, eventObject, abi::READ | abi::WRITE); |
|
| 136 | 165 | set domain.env = Env { |
|
| 137 | - | argsPointer: 0, argsSize: 0, eventsHandle: eventHandle.bits, eventsPointer: 0, |
|
| 166 | + | argsPointer: 0, argsSize: 0, eventsHandle: eventHandle.bits, |
|
| 167 | + | eventsPointer: &domain.events.ring as u64, |
|
| 138 | 168 | stateBase: 0, stackBase: 0, stackTop: 0, context: 0, |
|
| 139 | 169 | privateStackBase: 0, privateStackTop: 0, |
|
| 140 | 170 | }; |
|
| 141 | 171 | set domain.state = Lifecycle::Pending; |
|
| 142 | 172 | return object; |
| 147 | 177 | assert domains[0].epoch == 0; |
|
| 148 | 178 | let object = prepare(domains, 0, none(), image); |
|
| 149 | 179 | return handles::install(&mut domains[0].handles, 0, object, abi::ROOT_RIGHTS); |
|
| 150 | 180 | } |
|
| 151 | 181 | ||
| 152 | - | /// Create a pending domain after the caller has authorized image admission. |
|
| 153 | - | /// Reserve terminal event capacity before accepting the parent relationship. |
|
| 154 | - | export fn create(domains: *mut [Domain], creator: abi::Object, image: u32) -> abi::Object throws (abi::Error) { |
|
| 155 | - | if not live(domains, creator) { throw abi::Error::BadHandle; } |
|
| 182 | + | /// Find a reusable domain slot without changing its incarnation or authority. |
|
| 183 | + | /// Dead slots still owning private state must be reclaimed before reuse. |
|
| 184 | + | export fn vacant(domains: *[Domain]) -> u32 throws (abi::Error) { |
|
| 156 | 185 | for index in 0..domains.len { |
|
| 157 | 186 | let domain = &domains[index]; |
|
| 158 | 187 | if domain.state == Lifecycle::Dead and domain.epoch < 0xffffffff |
|
| 159 | - | and domain.handles.entries[1].generation <> 0 { |
|
| 160 | - | try events::reserve(&mut domains[creator.index].events); |
|
| 161 | - | let result = prepare(domains, index, creator, image); |
|
| 162 | - | return result; |
|
| 188 | + | and domain.private.count == 0 and domain.handles.entries[1].generation <> 0 { |
|
| 189 | + | return index; |
|
| 163 | 190 | } |
|
| 164 | 191 | } |
|
| 165 | 192 | throw abi::Error::Exhausted; |
|
| 166 | 193 | } |
|
| 194 | + | ||
| 195 | + | /// Create a pending domain after the caller has authorized image admission. |
|
| 196 | + | /// Reserve terminal event capacity before accepting the parent relationship. |
|
| 197 | + | export fn create(domains: *mut [Domain], creator: abi::Object, image: u32) -> abi::Object throws (abi::Error) { |
|
| 198 | + | if not live(domains, creator) { throw abi::Error::BadHandle; } |
|
| 199 | + | let index = try vacant(domains); |
|
| 200 | + | try events::reserve(&mut domains[creator.index].events); |
|
| 201 | + | return prepare(domains, index, creator, image); |
|
| 202 | + | } |
kernel/native.rad
+10 -7
| 14 | 14 | use core::platform; |
|
| 15 | 15 | use core::resources; |
|
| 16 | 16 | use core::state; |
|
| 17 | 17 | use images; |
|
| 18 | 18 | ||
| 19 | + | mod instances; |
|
| 20 | + | ||
| 19 | 21 | /// Isolated consumer used by the native graph regressions. |
|
| 20 | - | static DOMAINS: [domains::Domain; 1] = undefined; |
|
| 22 | + | static DOMAINS: [domains::Domain; 3] = undefined; |
|
| 21 | 23 | /// Page objects exercised by checked materialization calls. |
|
| 22 | 24 | static OBJECTS: [resources::Slot; 16] = undefined; |
|
| 23 | 25 | /// Storage for the execution-state view. |
|
| 24 | - | static CONTEXTS: [contexts::Context; 1] = undefined; |
|
| 26 | + | static CONTEXTS: [contexts::Context; 3] = undefined; |
|
| 25 | 27 | /// Physical frame ownership from the firmware reservations. |
|
| 26 | 28 | static POOL: frames::Pool = undefined; |
|
| 27 | 29 | /// Persistent physical Page pins. |
|
| 28 | 30 | static PINS: [u16; frames::MAX_FRAMES] = undefined; |
|
| 29 | 31 | /// Live Page claims. |
|
| 30 | 32 | static CLAIMS: [bool; frames::MAX_FRAMES] = undefined; |
|
| 31 | 33 | /// Domain-lifetime frame bitmap. |
|
| 32 | - | static GRANTS: [u64; frames::MAX_FRAMES / 64] = undefined; |
|
| 34 | + | static GRANTS: [u64; 3 * frames::MAX_FRAMES / 64] = undefined; |
|
| 33 | 35 | /// Actual capability and Page mechanisms used by the fixture calls. |
|
| 34 | 36 | static KERNEL: state::State = undefined; |
|
| 35 | 37 | ||
| 36 | 38 | /// Run one graph against actual Page authority and check its terminal call. |
|
| 37 | 39 | unsafe fn run(image: u32, expected: u32, own: abi::Handle, queued: bool) { |
| 100 | 102 | frames::reclaim(&mut POOL, data); |
|
| 101 | 103 | } |
|
| 102 | 104 | ||
| 103 | 105 | /// Check graph linking, native control flow, bounded memory, and protected spills. |
|
| 104 | 106 | @default unsafe fn main(hart: u64, description: *u8) -> u32 { |
|
| 105 | - | assert hart == 0 and images::COUNT == 7; |
|
| 107 | + | assert hart == 0 and images::COUNT == 8; |
|
| 106 | 108 | let size = try! fdt::word(@sliceOf(description, 40), 4); |
|
| 107 | 109 | assert size >= 40 and size <= fdt::MAX_BYTES; |
|
| 108 | 110 | let mut tree: fdt::Tree = undefined; |
|
| 109 | 111 | try! fdt::decode(@sliceOf(description, size), &mut tree); |
|
| 110 | 112 | let mut machine: platform::Platform = undefined; |
|
| 111 | 113 | try! platform::discover(&tree, &mut machine); |
|
| 112 | 114 | try! frames::init(&mut POOL, &machine); |
|
| 113 | 115 | domains::init(&mut DOMAINS[..]); |
|
| 114 | 116 | resources::init(&mut OBJECTS[..]); |
|
| 115 | - | contexts::init(&mut CONTEXTS[0], 0); |
|
| 117 | + | for i in 0..CONTEXTS.len { contexts::init(&mut CONTEXTS[i], i); } |
|
| 116 | 118 | let mut ram: memory::Memory = undefined; |
|
| 117 | - | memory::init(&mut ram, &mut POOL, &mut PINS[..], &mut CLAIMS[..], &mut GRANTS[..], 1); |
|
| 119 | + | memory::init(&mut ram, &mut POOL, &mut PINS[..], &mut CLAIMS[..], &mut GRANTS[..], 3); |
|
| 118 | 120 | set KERNEL = state::State { domains: &mut DOMAINS[..], resources: &mut OBJECTS[..], contexts: &mut CONTEXTS[..], memory: ram }; |
|
| 119 | 121 | let own = domains::root(&mut DOMAINS[..], images::ROOT); |
|
| 120 | 122 | let expected: [u32; 7] = [42, 21, 73, 77, 0, 42, 81]; |
|
| 121 | - | for i in 0..images::COUNT { run(i, expected[i], own, i == 5); } |
|
| 123 | + | for i in 0..expected.len { run(i, expected[i], own, i == 5); } |
|
| 122 | 124 | run(5, 42, own, false); |
|
| 125 | + | instances::run(&mut KERNEL, own, machine.clint.base); |
|
| 123 | 126 | return 0; |
|
| 124 | 127 | } |
kernel/native/instances.rad
added
+61 -0
| 1 | + | //! Native proof of independent domain state and shared same-domain contexts. |
|
| 2 | + | ||
| 3 | + | use core::abi; |
|
| 4 | + | use core::activation; |
|
| 5 | + | use core::budget_caps; |
|
| 6 | + | use core::budgets; |
|
| 7 | + | use core::capabilities; |
|
| 8 | + | use core::clock; |
|
| 9 | + | use core::contexts; |
|
| 10 | + | use core::cpu; |
|
| 11 | + | ||
| 12 | + | use core::handles; |
|
| 13 | + | use core::pages; |
|
| 14 | + | ||
| 15 | + | use core::physical; |
|
| 16 | + | use core::resources; |
|
| 17 | + | use core::state; |
|
| 18 | + | ||
| 19 | + | /// Give one explicit context its own finite budget and observe its native Exit. |
|
| 20 | + | unsafe fn execute(kernel: *mut state::State, target: abi::Handle, id: u64, clint: u64, expected: u32) { |
|
| 21 | + | let object = try! resources::create(kernel.resources, resources::Value::Budget(try! budgets::init(0, 1000000))); |
|
| 22 | + | resources::retain(kernel.resources, object); |
|
| 23 | + | let handle = handles::install(&mut kernel.domains[0].handles, |
|
| 24 | + | try! handles::vacant(&kernel.domains[0].handles), object, abi::EXECUTE); |
|
| 25 | + | try! budget_caps::bind(kernel, 0, handle, target, id); |
|
| 26 | + | let index = try! contexts::resolve(kernel.contexts, id); |
|
| 27 | + | let context = &mut kernel.contexts[index]; |
|
| 28 | + | let budget = budget_caps::value(kernel, object); |
|
| 29 | + | let running = try! contexts::begin(context, budget, clock::read(clint), 0); |
|
| 30 | + | clock::arm(clint, 0, context.deadline); |
|
| 31 | + | clock::interrupts(128); |
|
| 32 | + | cpu::enter(&mut context.frame); |
|
| 33 | + | let stopped = clock::read(clint); |
|
| 34 | + | clock::interrupts(0); |
|
| 35 | + | clock::arm(clint, 0, 0xffffffffffffffff); |
|
| 36 | + | assert contexts::finish(context, budget, running, stopped) > 0; |
|
| 37 | + | assert context.frame.cause == 8 and context.frame.registers[17] == 49; |
|
| 38 | + | assert context.frame.registers[10] == expected as u64; |
|
| 39 | + | set context.status = contexts::Status::Stopped; |
|
| 40 | + | } |
|
| 41 | + | ||
| 42 | + | /// Instantiate one graph twice, then run two contexts in the first instance. |
|
| 43 | + | export unsafe fn run(kernel: *mut state::State, own: abi::Handle, clint: u64) { |
|
| 44 | + | let image: u64 = 7; |
|
| 45 | + | let first = try! activation::create(kernel, 0, own, image, physical::clear, physical::copy); |
|
| 46 | + | let second = try! activation::create(kernel, 0, own, image, physical::clear, physical::copy); |
|
| 47 | + | let page = try! pages::allocate(kernel, 0, own, 1, physical::clear); |
|
| 48 | + | let range = try! pages::access(kernel, 0, page, abi::READ | abi::WRITE); |
|
| 49 | + | let words = @sliceOf(physical::bytes(range.base, 8).ptr as *mut u32, 2); |
|
| 50 | + | set words[0] = 1; |
|
| 51 | + | set words[1] = 2; |
|
| 52 | + | let _firstPage = try! capabilities::grant(kernel, 0, page, first, (abi::READ | abi::WRITE) as u64); |
|
| 53 | + | let _secondPage = try! capabilities::grant(kernel, 0, page, second, (abi::READ | abi::WRITE) as u64); |
|
| 54 | + | let top = range.base + range.size; |
|
| 55 | + | let firstId = try! activation::activate(kernel, 0, first, top, range.base, 4, physical::clear); |
|
| 56 | + | let extraId = try! activation::context(kernel, 0, first, top, range.base + 4, 4, physical::clear); |
|
| 57 | + | let secondId = try! activation::activate(kernel, 0, second, top, range.base, 4, physical::clear); |
|
| 58 | + | execute(kernel, first, firstId, clint, 42); |
|
| 59 | + | execute(kernel, first, extraId, clint, 44); |
|
| 60 | + | execute(kernel, second, secondId, clint, 42); |
|
| 61 | + | } |
kernel/user/sample_instance.rad
added
+14 -0
| 1 | + | //! Independent domains initialize at 41; contexts share their domain's counter. |
|
| 2 | + | use user::sys; |
|
| 3 | + | ||
| 4 | + | /// Mutable state shared by contexts in one image instance. |
|
| 5 | + | static COUNT: u32 = 41; |
|
| 6 | + | ||
| 7 | + | @default fn main(env: *sys::Env) -> u32 { |
|
| 8 | + | let args = sys::envArgs(env); |
|
| 9 | + | assert args.len >= 4; |
|
| 10 | + | let increment = args[0] as u32 | (args[1] as u32 << 8) |
|
| 11 | + | | (args[2] as u32 << 16) | (args[3] as u32 << 24); |
|
| 12 | + | set COUNT += increment; |
|
| 13 | + | return COUNT; |
|
| 14 | + | } |