compiler/
kernel/
lib/
scripts/
seed/
sublime/
test/
acceptance/
boot/
bootstrap/
cycles/
kernel/
dispatchcheck.rad
10.7 KiB
job.rad
651 B
machine.ras
817 B
run
2.2 KiB
spin.rad
3.7 KiB
support.rad
242 B
dispatch/
loader/
mmio/
modules/
native/
packages/
pages/
runtime/
scheduling/
shared/
slots/
smp/
sync/
termination/
tests/
trap/
run
2.7 KiB
runner.rad
10.3 KiB
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
test/cycles/kernel/dispatchcheck.rad
raw
| 1 | //! Concurrent private package graphs and complete worker resource recovery. |
| 2 | use std::mem; |
| 3 | use kernel::abi; |
| 4 | use kernel::frames; |
| 5 | use kernel::slots; |
| 6 | use kernel::backing; |
| 7 | use kernel::pages; |
| 8 | use kernel::capability; |
| 9 | use kernel::registry; |
| 10 | use kernel::loader; |
| 11 | use kernel::domains; |
| 12 | use kernel::budgets; |
| 13 | use kernel::dispatch; |
| 14 | use kernel::boot; |
| 15 | use kernel::sync; |
| 16 | use kernel::remote; |
| 17 | use kernel::events; |
| 18 | use kernel::devices; |
| 19 | use kernel::platform; |
| 20 | use kernel::invariants; |
| 21 | use kernel::dispatchinput; |
| 22 | |
| 23 | /// Bootstrap resource capabilities. |
| 24 | unsafe static TABLE: capability::Table = undefined; |
| 25 | /// Fixture publication barrier. |
| 26 | static READY: u64 = 0; |
| 27 | /// Surviving event consumer. |
| 28 | unsafe static PARENT: abi::Ref = undefined; |
| 29 | /// Controller identities indexed by hart. |
| 30 | unsafe static CHILDREN: [abi::Ref; 8] = undefined; |
| 31 | /// Bootstrap-owned argument page addresses indexed by hart. |
| 32 | static ARGUMENTS: [u64; 8] = [0; 8]; |
| 33 | /// Free frames before controller creation. |
| 34 | static FREE: u32 = 0; |
| 35 | /// Reusable page, backing, domain, and context slots before controller creation. |
| 36 | static SLOTS: [u32; 4] = [0; 4]; |
| 37 | /// Validated physical page mapping. |
| 38 | fn memory(address: u64) -> *mut u8; |
| 39 | /// Current kernel package-state table. |
| 40 | fn kernelGp() -> u64; |
| 41 | /// Native checker entry. |
| 42 | fn completion() -> u64; |
| 43 | /// Write one diagnostic byte. |
| 44 | fn put(byte: u8); |
| 45 | /// Finish the native fixture. |
| 46 | fn finish(); |
| 47 | |
| 48 | /// Count reusable frames in short metadata sections. |
| 49 | unsafe fn freeFrames() -> u32 { |
| 50 | let mut count: u32 = 0; |
| 51 | let mut first: u32 = 0; |
| 52 | while first < pages::STORE.backings.pool.count { |
| 53 | let mut end = first + 128; |
| 54 | if end > pages::STORE.backings.pool.count { set end = pages::STORE.backings.pool.count; } |
| 55 | let guard = sync::enter(); |
| 56 | for i in first..end { if frames::available(&pages::STORE.backings.pool, i) { set count += 1; } } |
| 57 | sync::leave(guard); |
| 58 | set first = end; |
| 59 | } |
| 60 | return count; |
| 61 | } |
| 62 | |
| 63 | /// Count reusable object slots after all allocation transactions have stopped. |
| 64 | fn freeSlots(entries: &[slots::Slot]) -> u32 { |
| 65 | let mut count: u32 = 0; |
| 66 | let mut first: u32 = 0; |
| 67 | while first < entries.len { |
| 68 | let mut end = first + 128; |
| 69 | if end > entries.len { set end = entries.len; } |
| 70 | let guard = sync::enter(); |
| 71 | for i in first..end { |
| 72 | assert entries[i].state <> slots::State::Reserved; |
| 73 | if entries[i].state == slots::State::Free { set count += 1; } |
| 74 | } |
| 75 | sync::leave(guard); |
| 76 | set first = end; |
| 77 | } |
| 78 | return count; |
| 79 | } |
| 80 | |
| 81 | /// Read object capacity at the quiescent boundaries of the workload. |
| 82 | unsafe fn capacity() -> [u32; 4] { |
| 83 | return [freeSlots(&pages::STORE.slots[..]), freeSlots(&pages::STORE.backings.slots[..]), |
| 84 | freeSlots(&domains::STORE.slots[..]), freeSlots(&domains::STORE.contextSlots[..])]; |
| 85 | } |
| 86 | |
| 87 | /// Copy one trusted package into bootstrap-owned memory. |
| 88 | unsafe fn input(authority: abi::Handle, bytes: &[u8]) -> abi::Handle { |
| 89 | let handle = try! pages::allocate(&mut pages::STORE, &mut TABLE, authority, (bytes.len as u64 + 4095) / 4096); |
| 90 | let page = try! pages::get(&pages::STORE, (try! capability::get(&TABLE, handle)).object); |
| 91 | assert try! mem::copy(@sliceOf(memory(page.base), page.count * 4096), bytes) == bytes.len; |
| 92 | return handle; |
| 93 | } |
| 94 | |
| 95 | /// Publish fixture state before any hart enters dispatch. |
| 96 | export unsafe fn start(last: bool) { |
| 97 | if last { setup(); sync::storeRelease(&mut READY, 1); } |
| 98 | while sync::loadAcquire(&READY) == 0 {} |
| 99 | } |
| 100 | |
| 101 | /// Prepare one controller per hart and a later surviving checker. |
| 102 | unsafe fn setup() { |
| 103 | let pending = try! slots::reserve(&mut domains::STORE.slots[..]); |
| 104 | let owner = try! slots::commit(&mut domains::STORE.slots[..], pending); |
| 105 | capability::initialize(&mut TABLE, owner); |
| 106 | try! backing::registerDomain(&mut pages::STORE.backings, owner); |
| 107 | let authority = try! capability::install(&mut TABLE, capability::Entry { |
| 108 | kind: abi::Kind::Domain, object: owner, rights: abi::Rights(abi::CREATE | abi::ALLOCATE), |
| 109 | }); |
| 110 | let mut image = abi::Handle(0); |
| 111 | for bytes in [&dispatchinput::support[..], &dispatchinput::spin[..]] { |
| 112 | let source = input(authority, &bytes[..]); |
| 113 | set image = try! loader::load(&mut loader::STATE, &mut pages::STORE, &mut registry::STORE, &mut TABLE, |
| 114 | loader::Request { authority, source, offset: 0, length: bytes.len as u64 }); |
| 115 | } |
| 116 | let source = input(authority, &dispatchinput::job[..]); |
| 117 | let parentHandle = try! domains::create(&mut domains::STORE, &mut pages::STORE.backings, ®istry::STORE, &mut TABLE, authority, image); |
| 118 | set PARENT = (try! capability::get(&TABLE, parentHandle)).object; |
| 119 | let parent = try! domains::get(&domains::STORE, PARENT); |
| 120 | set domains::STORE.records[PARENT.index].state = domains::Lifecycle::Active; |
| 121 | let frame = &mut domains::STORE.contexts[parent.initial.index].frame; |
| 122 | set frame.pc = completion(); set frame.status = 0x1880; |
| 123 | set frame.registers[2] = domains::STORE.contexts[parent.initial.index].kernelStack.end; |
| 124 | set frame.registers[3] = kernelGp(); |
| 125 | let mut arguments: [abi::Handle; 8] = undefined; |
| 126 | let mut handles: [abi::Handle; 8] = undefined; |
| 127 | let mut contexts: [abi::Ref; 8] = undefined; |
| 128 | let mut count: u64 = 0; |
| 129 | for hart in 0..8 { |
| 130 | if (boot::PLATFORM.harts & (1 << hart)) == 0 { continue; } |
| 131 | set count += 1; |
| 132 | set arguments[hart] = try! pages::allocate(&mut pages::STORE, &mut TABLE, authority, 1); |
| 133 | set ARGUMENTS[hart] = (try! pages::get(&pages::STORE, (try! capability::get(&TABLE, arguments[hart])).object)).base; |
| 134 | } |
| 135 | set FREE = freeFrames(); |
| 136 | set SLOTS = capacity(); |
| 137 | for hart in 0..8 { |
| 138 | if (boot::PLATFORM.harts & (1 << hart)) == 0 { continue; } |
| 139 | let handle = try! domains::create(&mut domains::STORE, &mut pages::STORE.backings, ®istry::STORE, &mut TABLE, authority, image); |
| 140 | let object = (try! capability::get(&TABLE, handle)).object; |
| 141 | let mut child = try! domains::get(&domains::STORE, object); |
| 142 | set CHILDREN[hart] = object; set handles[hart] = handle; set contexts[hart] = child.initial; |
| 143 | try! domains::reparent(&mut domains::STORE, &TABLE, handle, parentHandle); |
| 144 | let self = try! capability::install(&mut child.memory.table, capability::Entry { |
| 145 | kind: abi::Kind::Domain, object, rights: abi::Rights(abi::CREATE | abi::ALLOCATE), |
| 146 | }); |
| 147 | let stack = try! pages::allocate(&mut pages::STORE, &mut child.memory.table, self, 4); |
| 148 | let storage = try! pages::get(&pages::STORE, (try! capability::get(&child.memory.table, stack)).object); |
| 149 | let args = @sliceOf(memory(ARGUMENTS[hart]) as *mut u64, 6); |
| 150 | set args[0] = *(try! pages::grant(&mut pages::STORE, &TABLE, &mut child.memory.table, source, abi::READ as u64)); |
| 151 | set args[1] = dispatchinput::job.len as u64; |
| 152 | for i in 0..devices::STORE.count { |
| 153 | if devices::STORE.regions[i].kind == platform::Kind::Clint { |
| 154 | set args[2] = *(try! devices::install(&devices::STORE, &mut child.memory.table, i)); |
| 155 | } |
| 156 | } |
| 157 | assert args[2] <> 0; |
| 158 | set args[3] = *(try! pages::grant(&mut pages::STORE, &TABLE, &mut child.memory.table, arguments[hart], (abi::READ | abi::WRITE | abi::GRANT) as u64)); |
| 159 | set args[4] = 10000000 * count; |
| 160 | try! domains::activate(&mut domains::STORE, &pages::STORE, &TABLE, handle, storage.base + storage.count as u64 * 4096, ARGUMENTS[hart], 56); |
| 161 | } |
| 162 | for byte in "cycles controllers ready\n" { put(byte); } |
| 163 | let now = dispatch::clock(); |
| 164 | let start = now + 1000000 * count; |
| 165 | for hart in 0..8 { |
| 166 | if (boot::PLATFORM.harts & (1 << hart)) == 0 { continue; } |
| 167 | let window = try! budgets::seed(&mut budgets::STORE, &mut TABLE, hart, start, 0xffffffffffffffff); |
| 168 | let tail = try! budgets::split(&mut budgets::STORE, &mut TABLE, window, start + 800000000 * count, now); |
| 169 | let bound = try! budgets::bind(&mut budgets::STORE, &domains::STORE, &mut TABLE, |
| 170 | budgets::Binding { budget: window, domain: handles[hart], context: contexts[hart] }, now); |
| 171 | if hart == 0 { |
| 172 | let final = try! budgets::bind(&mut budgets::STORE, &domains::STORE, &mut TABLE, |
| 173 | budgets::Binding { budget: tail, domain: parentHandle, context: parent.initial }, now); |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | /// Check all terminal records, shared code identity, and complete transient recovery. |
| 179 | export unsafe fn verify() { |
| 180 | for byte in "cycles checking completion\n" { put(byte); } |
| 181 | let guard = sync::enter(); |
| 182 | let mut parent = try! domains::get(&domains::STORE, PARENT); |
| 183 | let job = registry::find(®istry::STORE, &"job"[..]) else panic "worker package required"; |
| 184 | let package = try! registry::get(®istry::STORE, job); |
| 185 | let mut seen: u64 = 0; |
| 186 | while let event = try! events::pop(&mut parent.memory.ring) { |
| 187 | assert event.kind == events::CHILD_EXIT and event.code == 68; |
| 188 | let mut found = false; |
| 189 | for hart in 0..8 { |
| 190 | if (boot::PLATFORM.harts & (1 << hart)) == 0 { continue; } |
| 191 | if event.value == abi::id(CHILDREN[hart]) { |
| 192 | assert (seen & (1 << hart)) == 0; |
| 193 | set seen |= 1 << hart; set found = true; |
| 194 | let args = @sliceOf(memory(ARGUMENTS[hart]) as *mut u64, 7); |
| 195 | assert args[5] == package.codeAddress and args[6] == 4; |
| 196 | } |
| 197 | } |
| 198 | assert found; |
| 199 | } |
| 200 | assert seen == boot::PLATFORM.harts; |
| 201 | try! events::refresh(&mut domains::STORE.events, PARENT, &parent.memory.ring); |
| 202 | assert loader::STATE.live[job.index] and not loader::STATE.busy; |
| 203 | let retained = loader::STATE.resident[job.index].code.count + loader::STATE.resident[job.index].metadata.count; |
| 204 | sync::leave(guard); |
| 205 | let mut done = false; |
| 206 | for attempt in 0..100000 { |
| 207 | let guard = sync::enter(); |
| 208 | set done = domains::STORE.dead == 0 and pages::STORE.backings.pool.retiredCount == 0; |
| 209 | if done { sync::leave(guard); break; } |
| 210 | let request = try! dispatch::request(0, remote::Action::Reschedule); |
| 211 | sync::leave(guard); |
| 212 | assert try! dispatch::awaitRequest(0, request) == 0; |
| 213 | } |
| 214 | assert done; |
| 215 | assert freeFrames() + retained == FREE; |
| 216 | let recovered = capacity(); |
| 217 | for i in 0..4 { assert recovered[i] == SLOTS[i]; } |
| 218 | invariants::check(&TABLE); |
| 219 | for byte in "cycles final recovery and invariants passed\n" { put(byte); } |
| 220 | let maximum = sync::maximum(); |
| 221 | assert maximum > 0 and maximum < 1000000; |
| 222 | for byte in "cycles metadata instructions: " { put(byte); } |
| 223 | number(maximum); |
| 224 | put(10); |
| 225 | let waiting = sync::maximumWait(); |
| 226 | assert waiting > 0; |
| 227 | for byte in "cycles acquisition instructions: " { put(byte); } |
| 228 | number(waiting); |
| 229 | put(10); |
| 230 | finish(); |
| 231 | } |
| 232 | |
| 233 | /// Write a fixed-width diagnostic value. |
| 234 | fn number(value: u64) { |
| 235 | let digits = "0123456789abcdef"; |
| 236 | for i in 0..16 { put(digits[((value >> ((15 - i) as u64 * 4)) & 15) as u32]); } |
| 237 | } |