test/dispatch/spin.rad 7.3 KiB 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
}