compiler/
kernel/
lib/
scripts/
seed/
sublime/
test/
acceptance/
boot/
bootstrap/
cycles/
dispatch/
kernel/
machine.ras
1.5 KiB
run
2.2 KiB
spin.rad
7.3 KiB
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/dispatch/spin.rad
raw
| 1 | //! User calls, targeted handoff, and timer preemption of busy loops. |
| 2 | export mod abi; |
| 3 | export mod sys; |
| 4 | /// RV64 direct-call intrinsic used to check the machine ABI. |
| 5 | @intrinsic fn ecall(number: u32, a0: i64, a1: i64, a2: i64, a3: i64) -> i64; |
| 6 | /// Local capabilities supplied to the user fixture. |
| 7 | record Arguments: Copy { |
| 8 | /// Local capability for the stack allocation. |
| 9 | stack: u64, |
| 10 | /// Local capability for the resident user package. |
| 11 | image: u64, |
| 12 | /// Local self-Domain capability with allocation authority. |
| 13 | domain: u64, |
| 14 | /// Future window that the user can partition between its contexts. |
| 15 | future: u64, |
| 16 | /// Completion checker with authority to receive child lifecycle events. |
| 17 | parent: u64, |
| 18 | } |
| 19 | |
| 20 | /// Startup environment supplied by the kernel. |
| 21 | record Env: Copy { |
| 22 | /// Readable startup capabilities. |
| 23 | argsPointer: *Arguments, |
| 24 | /// Size of the startup word. |
| 25 | argsSize: u64, |
| 26 | /// Installed event handle. |
| 27 | eventsHandle: u64, |
| 28 | /// Shared event ring address. |
| 29 | eventsPointer: u64, |
| 30 | } |
| 31 | /// Private query output used to check writable package-state authority. |
| 32 | static INFO: [u64; 2] = [0; 2]; |
| 33 | /// Private progress counter observed by the machine fixture. |
| 34 | export static count: u64 = 0; |
| 35 | /// Additional-context progress in the same private package state. |
| 36 | export static extraCount: u64 = 0; |
| 37 | /// Successful user-side return and destruction checks. |
| 38 | export static destroyed: u64 = 0; |
| 39 | /// Generation-bearing identity of the child that returns normally. |
| 40 | export static childId: u64 = 0; |
| 41 | /// Local self-Domain authority shared by both user contexts. |
| 42 | static SELF: abi::Handle = abi::Handle(0); |
| 43 | |
| 44 | /// Update shared private state and return from an additional context. |
| 45 | export fn worker(args: *opaque, size: u64) { |
| 46 | assert size == 0; |
| 47 | set extraCount += 1; |
| 48 | try! sys::timeout(2000000, 77); |
| 49 | sys::wait(); |
| 50 | set extraCount += 1; |
| 51 | set count += 1; |
| 52 | } |
| 53 | /// Check direct calls and handoff, then exercise independently bound contexts. |
| 54 | @default unsafe fn main(env: *Env) { |
| 55 | if env.argsSize == 0 { |
| 56 | try! sys::timeout(10000, 88); |
| 57 | sys::wait(); |
| 58 | return; |
| 59 | } |
| 60 | assert env.argsSize == 40; |
| 61 | set SELF = abi::Handle(env.argsPointer.domain); |
| 62 | let handle = env.argsPointer.stack as i64; |
| 63 | let info = sys::queryPage(abi::Handle(handle as u64)); |
| 64 | assert info.base <> 0 and info.count == 1; |
| 65 | let mut rejected = false; |
| 66 | try sys::pageAllocate(abi::Handle(0), 0) catch error { |
| 67 | assert error == abi::Error::InvalidArg; |
| 68 | set rejected = true; |
| 69 | }; |
| 70 | assert rejected; |
| 71 | let allocated = try! sys::pageAllocate(abi::Handle(0), 1); |
| 72 | assert sys::queryPage(allocated).count == 1; |
| 73 | let narrowed = try! sys::capabilityGrant(allocated, abi::Handle(0), abi::READ as u64); |
| 74 | try! sys::capabilityDrop(narrowed); |
| 75 | try! sys::capabilityDrop(allocated); |
| 76 | assert sys::queryEvents(abi::Handle(env.eventsHandle)).capacity == 256; |
| 77 | let domain = sys::queryDomain(abi::Handle(env.argsPointer.domain)); |
| 78 | assert domain.id <> 0 and domain.state == 1; |
| 79 | let image = try! sys::queryImage(abi::Handle(env.argsPointer.image)); |
| 80 | assert image.entry <> 0 and image.codeBase <> 0 and image.codeBytes > 0 and image.dataBytes > 0; |
| 81 | let child = try! sys::domainCreate(abi::Handle(0), abi::Handle(env.argsPointer.image)); |
| 82 | set childId = sys::queryDomain(child).id; |
| 83 | try! sys::domainReparent(child, abi::Handle(env.argsPointer.parent)); |
| 84 | let stack = try! sys::pageAllocate(abi::Handle(0), 1); |
| 85 | let granted = try! sys::capabilityGrant(stack, child, (abi::READ | abi::WRITE) as u64); |
| 86 | let childStack = sys::queryPage(stack); |
| 87 | try! sys::domainActivate(child, childStack.base + 4096, (&*env) as *unsafe opaque, 0); |
| 88 | assert sys::queryDomain(child).state == 1; |
| 89 | set rejected = false; |
| 90 | try sys::domainActivate(child, childStack.base + 4096, (&*env) as *unsafe opaque, 0) catch error { |
| 91 | assert error == abi::Error::NotPending; |
| 92 | set rejected = true; |
| 93 | }; |
| 94 | assert rejected; |
| 95 | set rejected = false; |
| 96 | try sys::imageLoad(abi::Handle(0), stack, 0, 0) catch error { |
| 97 | assert error == abi::Error::InvalidArg; |
| 98 | set rejected = true; |
| 99 | }; |
| 100 | assert rejected; |
| 101 | let current = sys::currentContext(); |
| 102 | assert current.context <> 0 and current.hart == 0 and current.budget <> 0; |
| 103 | let window = try! sys::queryBudget(abi::Handle(current.budget)); |
| 104 | assert window.context == current.context and window.hart == current.hart and window.start < window.end; |
| 105 | let extraStack = try! sys::pageAllocate(abi::Handle(0), 1); |
| 106 | let extraMemory = sys::queryPage(extraStack); |
| 107 | let extra = try! sys::contextCreate(abi::Handle(env.argsPointer.domain), abi::ContextStart { |
| 108 | entry: worker as u64, stack: extraMemory.base + 4096, args: 0, size: 0, |
| 109 | }); |
| 110 | let context = try! sys::queryContext(abi::Handle(env.argsPointer.domain), extra); |
| 111 | assert context.context == extra and context.domain == domain.id; |
| 112 | assert context.stackBase == extraMemory.base and context.stackTop == extraMemory.base + 4096; |
| 113 | let future = abi::Handle(env.argsPointer.future); |
| 114 | let later = try! sys::queryBudget(future); |
| 115 | let tail = try! sys::budgetSplit(future, later.start + (later.end - later.start) / 2); |
| 116 | let childWindow = try! sys::budgetSplit(tail, later.start + (later.end - later.start) * 5 / 8); |
| 117 | let resumption = try! sys::budgetSplit(childWindow, later.start + (later.end - later.start) * 11 / 16); |
| 118 | let awakened = try! sys::budgetSplit(resumption, later.start + (later.end - later.start) * 3 / 4); |
| 119 | let finalWindow = try! sys::budgetSplit(awakened, later.start + (later.end - later.start) * 13 / 16); |
| 120 | let bound = try! sys::budgetBind(tail, abi::Handle(env.argsPointer.domain), extra); |
| 121 | let resumed = try! sys::budgetBind(awakened, SELF, extra); |
| 122 | let initial = try! sys::queryContext(child, 0); |
| 123 | let childBound = try! sys::budgetBind(childWindow, child, initial.context); |
| 124 | let mut local: [u64; 2] = [0; 2]; |
| 125 | assert ecall(44, handle, (&mut local[0]) as i64, 16, 0) == 0; |
| 126 | assert local[0] <> 0 and local[1] == 1; |
| 127 | assert ecall(44, handle, (&mut INFO[0]) as i64, 16, 0) == 0; |
| 128 | assert INFO[0] == local[0] and INFO[1] == local[1]; |
| 129 | assert ecall(44, handle, 0, 16, 0) == -4; |
| 130 | assert ecall(0xffffffff, 0, 0, 0, 0) == -4; |
| 131 | set rejected = false; |
| 132 | try sys::yield(SELF) catch error { |
| 133 | assert error == abi::Error::Busy; |
| 134 | set rejected = true; |
| 135 | }; |
| 136 | assert rejected and sys::currentContext().budget == current.budget; |
| 137 | set count = 1; |
| 138 | try! sys::yield(child); |
| 139 | let continued = sys::currentContext(); |
| 140 | assert continued.context == current.context and continued.budget == *future; |
| 141 | while extraCount == 0 { set count += 1; } |
| 142 | let waiting = try! sys::queryContext(SELF, extra); |
| 143 | assert waiting.state == 2 and waiting.hart == 0xffffffffffffffff; |
| 144 | try! sys::wakeup(SELF, 123); |
| 145 | assert extraCount == 1 or extraCount == 2; |
| 146 | while extraCount == 1 { set count += 1; } |
| 147 | let stopped = try! sys::queryContext(abi::Handle(env.argsPointer.domain), extra); |
| 148 | assert stopped.state == 1 and stopped.hart == 0xffffffffffffffff; |
| 149 | try! sys::contextDestroy(abi::Handle(env.argsPointer.domain), extra); |
| 150 | set rejected = false; |
| 151 | try sys::queryContext(abi::Handle(env.argsPointer.domain), extra) catch error { |
| 152 | assert error == abi::Error::BadHandle; |
| 153 | set rejected = true; |
| 154 | }; |
| 155 | assert rejected; |
| 156 | set destroyed = 1; |
| 157 | while true { set count += 1; } |
| 158 | } |