test/smp/kernel/dispatchcheck.rad 31.9 KiB raw
1
//! One domain executing separate user contexts on every online hart.
2
use std::mem;
3
use std::arch::rv64::shared;
4
use kernel::abi;
5
use kernel::invariants;
6
use kernel::frames;
7
use kernel::slots;
8
use kernel::backing;
9
use kernel::pages;
10
use kernel::capability;
11
use kernel::registry;
12
use kernel::loader;
13
use kernel::domains;
14
use kernel::budgets;
15
use kernel::dispatch;
16
use kernel::boot;
17
use kernel::sync;
18
use kernel::remote;
19
use kernel::calls;
20
use kernel::events;
21
use kernel::timers;
22
use kernel::interrupts;
23
use kernel::plic;
24
use kernel::lifecycle;
25
use kernel::dispatchinput;
26
27
/// Bootstrap resource authority.
28
unsafe static TABLE: capability::Table = undefined;
29
/// Release/acquire barrier after fixture publication.
30
static READY: u64 = 0;
31
/// Shared user domain generation.
32
unsafe static USER: abi::Ref = undefined;
33
/// Pending domain in the user's creation subtree.
34
unsafe static DESCENDANT: abi::Ref = undefined;
35
/// Per-hart user execution contexts.
36
unsafe static CONTEXTS: [abi::Ref; 8] = undefined;
37
/// Mapped progress counters in the user's private state.
38
static COUNTERS: u64 = 0;
39
/// Per-context migration observations in the user's private state.
40
static MIGRATIONS: u64 = 0;
41
/// Shared handshake for the independently activated context on hart one.
42
static WAKE_STATE: u64 = 0;
43
/// Bootstrap management capability for the pending idle-wakeup domain.
44
unsafe static IDLE_HANDLE: abi::Handle = undefined;
45
/// Context whose Wait call must release the remote hart.
46
unsafe static IDLE_CONTEXT: abi::Ref = undefined;
47
/// Validated initial stack pointer for the idle-wakeup domain.
48
static IDLE_STACK: u64 = 0;
49
/// Generation of the idle-wakeup domain.
50
unsafe static IDLE_DOMAIN: abi::Ref = undefined;
51
/// Completion checker's live domain generation.
52
unsafe static CHECKER: abi::Ref = undefined;
53
/// Unbound window that expires before the checker starts.
54
unsafe static EXPIRED: abi::Handle = undefined;
55
/// Checker's authority to activate and notify the idle-wakeup domain.
56
unsafe static RECEIVER: abi::Handle = undefined;
57
/// Unbound authority reserved for the idle-wakeup check on hart one.
58
unsafe static IDLE_WINDOW: abi::Handle = undefined;
59
/// Validated physical memory mapping.
60
fn memory(address: u64) -> *mut u8;
61
/// Current kernel package-state table.
62
fn kernelGp() -> u64;
63
/// M-mode entry of the completion checker.
64
fn completion() -> u64;
65
/// Write one diagnostic byte to the fixture UART.
66
fn put(byte: u8);
67
/// Set the fixture UART interrupt-enable register.
68
fn uartInterrupt(value: u8);
69
/// Finish the machine test successfully.
70
fn finish();
71
72
/// Publish fixture state on the last initialized hart and wait on every hart.
73
export unsafe fn start(last: bool) {
74
    if last { setup(); sync::storeRelease(&mut READY, 1); }
75
    while sync::loadAcquire(&READY) == 0 {}
76
}
77
78
/// Create one context per online hart before any dispatch can access the fixture.
79
unsafe fn setup() {
80
    let pending = try! slots::reserve(&mut domains::STORE.slots[..]);
81
    let owner = try! slots::commit(&mut domains::STORE.slots[..], pending);
82
    capability::initialize(&mut TABLE, owner);
83
    try! backing::registerDomain(&mut pages::STORE.backings, owner);
84
    let authority = try! capability::install(&mut TABLE, capability::Entry {
85
        kind: abi::Kind::Domain, object: owner, rights: abi::Rights(abi::CREATE | abi::ALLOCATE),
86
    });
87
    let source = try! pages::allocate(&mut pages::STORE, &mut TABLE, authority, (dispatchinput::INPUT.len as u64 + 4095) / 4096);
88
    let sourceEntry = try! capability::get(&TABLE, source);
89
    let page = try! pages::get(&pages::STORE, sourceEntry.object);
90
    let bytes = @sliceOf(memory(page.base), page.count * 4096);
91
    let length = try! mem::copy(&mut bytes[..], &dispatchinput::INPUT[..]);
92
    let image = try! loader::load(&mut loader::STATE, &mut pages::STORE, &mut registry::STORE, &mut TABLE,
93
        loader::Request { authority, source, offset: 0, length: length as u64 });
94
    let handle = try! domains::create(&mut domains::STORE, &mut pages::STORE.backings, &registry::STORE, &mut TABLE, authority, image);
95
    let cap = try! capability::get(&TABLE, handle);
96
    set USER = cap.object;
97
    createDescendant();
98
    let mut user = try! domains::get(&domains::STORE, USER);
99
    let self = try! capability::install(&mut user.memory.table, capability::Entry {
100
        kind: abi::Kind::Domain, object: USER, rights: abi::Rights(abi::ALLOCATE | abi::WAKE),
101
    });
102
    assert boot::PLATFORM.irqSources == 31;
103
    let irq = try! interrupts::seed(&mut interrupts::STORE, &mut user.memory.table, 7);
104
    sync::storeRelease(memory(dataAddress(&user, &"spin::interrupt"[..])) as *mut u64, *irq);
105
    let target = try! registry::exported(&registry::STORE, user.image, &"spin::worker"[..]);
106
    let case shared::Target::Function(entry) = target else panic "worker entry";
107
    set COUNTERS = dataAddress(&user, &"spin::count"[..]);
108
    set MIGRATIONS = dataAddress(&user, &"spin::migrations"[..]);
109
    let notification = @sliceOf(memory(dataAddress(&user, &"spin::notification"[..])), 8);
110
    for i in 0..8 { set notification[i] = ((*self) >> (i as u64 * 8)) as u8; }
111
    for hart in 0..8 {
112
        if (boot::PLATFORM.harts & (1 << hart)) == 0 { continue; }
113
        let stack = try! pages::allocate(&mut pages::STORE, &mut TABLE, authority, 1);
114
        let granted = try! pages::grant(&mut pages::STORE, &TABLE, &mut user.memory.table, stack, (abi::READ | abi::WRITE) as u64);
115
        let permission = try! capability::get(&TABLE, stack);
116
        let page = try! pages::get(&pages::STORE, permission.object);
117
        let argument = @sliceOf(memory(page.base), 8);
118
        for i in 0..8 { set argument[i] = ((hart as u64) >> (i as u64 * 8)) as u8; }
119
        if hart == 0 {
120
            try! domains::activate(&mut domains::STORE, &pages::STORE, &TABLE, handle, page.base + 4096, page.base, 8);
121
            set CONTEXTS[hart] = user.initial;
122
        } else {
123
            set CONTEXTS[hart] = try! domains::contextCreate(&mut domains::STORE, &mut pages::STORE, &registry::STORE,
124
                &TABLE, handle, abi::ContextStart { entry, stack: page.base + 4096, args: page.base, size: 8 });
125
        }
126
    }
127
    let checkerHandle = try! domains::create(&mut domains::STORE, &mut pages::STORE.backings, &registry::STORE, &mut TABLE, authority, image);
128
    let checkerCap = try! capability::get(&TABLE, checkerHandle);
129
    let mut checker = try! domains::get(&domains::STORE, checkerCap.object);
130
    set CHECKER = checkerCap.object;
131
    set domains::STORE.records[checkerCap.object.index].state = domains::Lifecycle::Active;
132
    set domains::STORE.contexts[checker.initial.index].frame.pc = completion();
133
    set domains::STORE.contexts[checker.initial.index].frame.status = 0x1880;
134
    set domains::STORE.contexts[checker.initial.index].frame.registers[2] = domains::STORE.contexts[checker.initial.index].kernelStack.end;
135
    set domains::STORE.contexts[checker.initial.index].frame.registers[3] = kernelGp();
136
    idleSetup(authority, image);
137
    let clock = dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0));
138
    let start = clock + 1000000;
139
    let mut harts: u64 = 0;
140
    for hart in 0..8 { if (boot::PLATFORM.harts & (1 << hart)) <> 0 { set harts += 1; } }
141
    let end = start + 10000000 * harts * harts;
142
    let migrationEnd = end + 10000000 * harts * harts;
143
    let windowBase = dataAddress(&user, &"spin::windows"[..]);
144
    for hart in 0..8 {
145
        if (boot::PLATFORM.harts & (1 << hart)) == 0 { continue; }
146
        let begin = clock if hart == 0 else start;
147
        let mut budget = try! budgets::seed(&mut budgets::STORE, &mut TABLE, hart, begin, 0xffffffffffffffff);
148
        if hart == 0 {
149
            let next = try! budgets::split(&mut budgets::STORE, &mut TABLE, budget, start, clock);
150
            set EXPIRED = try! budgets::transfer(&mut budgets::STORE, &mut TABLE, &mut checker.memory.table,
151
                budget, budgets::DEFAULT_RIGHTS as u64, clock);
152
            set budget = next;
153
        }
154
        let final = try! budgets::split(&mut budgets::STORE, &mut TABLE, budget, end, clock);
155
        let checking = try! budgets::split(&mut budgets::STORE, &mut TABLE, final, migrationEnd, clock);
156
        let future = try! budgets::split(&mut budgets::STORE, &mut TABLE, checking, migrationEnd + 10000000000, clock);
157
        let transferred = try! budgets::transfer(&mut budgets::STORE, &mut TABLE, &mut user.memory.table,
158
            future, budgets::DEFAULT_RIGHTS as u64, clock);
159
        let caller = (hart as u64 + harts - 1) % harts;
160
        let output = @sliceOf(memory(windowBase + caller * 8), 8);
161
        for i in 0..8 { set output[i] = ((*transferred) >> (i as u64 * 8)) as u8; }
162
        let bound = try! budgets::bind(&mut budgets::STORE, &domains::STORE, &mut TABLE,
163
            budgets::Binding { budget, domain: handle, context: CONTEXTS[hart] }, clock);
164
        let rotated = ((hart as u64 + 1) % harts) as u32;
165
        let moved = try! budgets::bind(&mut budgets::STORE, &domains::STORE, &mut TABLE,
166
            budgets::Binding { budget: final, domain: handle, context: CONTEXTS[rotated] }, clock);
167
        if hart == 0 {
168
            let bound = try! budgets::bind(&mut budgets::STORE, &domains::STORE, &mut TABLE,
169
                budgets::Binding { budget: checking, domain: checkerHandle, context: checker.initial }, clock);
170
        }
171
        if hart == 1 { set IDLE_WINDOW = checking; }
172
    }
173
}
174
175
/// Create an actual pending descendant before secondary harts enter dispatch.
176
unsafe fn createDescendant() {
177
    let mut user = try! domains::get(&domains::STORE, USER);
178
    let root = user.image;
179
    let authority = try! capability::install(&mut user.memory.table, capability::Entry {
180
        kind: abi::Kind::Domain, object: USER, rights: abi::Rights(abi::CREATE),
181
    });
182
    let image = try! registry::install(&registry::STORE, &mut user.memory.table, root);
183
    let handle = try! domains::create(&mut domains::STORE, &mut pages::STORE.backings,
184
        &registry::STORE, &mut user.memory.table, authority, image);
185
    set DESCENDANT = (try! capability::get(&user.memory.table, handle)).object;
186
}
187
188
/// Resolve one exported private-data address in the fixture's user instance.
189
unsafe fn dataAddress(user: &domains::Domain, name: &[u8]) -> u64 {
190
    let target = try! registry::exported(&registry::STORE, user.image, name);
191
    let case shared::Target::Data(data) = target else panic "fixture data export";
192
    return user.graph.table[data.slot] + data.offset as u64;
193
}
194
195
/// Prepare a pending domain and its stack for activation on an idle hart.
196
unsafe fn idleSetup(authority: abi::Handle, image: abi::Handle) {
197
    set IDLE_HANDLE = try! domains::create(&mut domains::STORE, &mut pages::STORE.backings,
198
        &registry::STORE, &mut TABLE, authority, image);
199
    let permit = try! capability::get(&TABLE, IDLE_HANDLE);
200
    set IDLE_DOMAIN = permit.object;
201
    let mut domain = try! domains::get(&domains::STORE, IDLE_DOMAIN);
202
    let allocation = try! capability::install(&mut domain.memory.table, capability::Entry {
203
        kind: abi::Kind::Domain, object: IDLE_DOMAIN, rights: abi::Rights(abi::ALLOCATE),
204
    });
205
    set IDLE_CONTEXT = domain.initial;
206
    set WAKE_STATE = dataAddress(&domain, &"spin::wakeState"[..]);
207
    sync::storeRelease(memory(dataAddress(&domain, &"spin::idle"[..])) as *mut u64, 1);
208
    let stack = try! pages::allocate(&mut pages::STORE, &mut TABLE, authority, 1);
209
    let granted = try! pages::grant(&mut pages::STORE, &TABLE, &mut domain.memory.table, stack, (abi::READ | abi::WRITE) as u64);
210
    let permission = try! capability::get(&TABLE, stack);
211
    let page = try! pages::get(&pages::STORE, permission.object);
212
    set IDLE_STACK = page.base + 4096;
213
    let mut checker = try! domains::get(&domains::STORE, CHECKER);
214
    set RECEIVER = try! capability::install(&mut checker.memory.table, capability::Entry {
215
        kind: abi::Kind::Domain, object: IDLE_DOMAIN, rights: abi::Rights(abi::WAKE | abi::EXECUTE | abi::DESTROY),
216
    });
217
}
218
219
/// Check shared progress and context isolation after the user windows finish.
220
export unsafe fn verify() {
221
    let guard = sync::enter();
222
    let user = try! domains::get(&domains::STORE, USER);
223
    let mut counts: [u64; 8] = [0; 8];
224
    let mut notifications: u32 = 0;
225
    for hart in 0..8 { if (boot::PLATFORM.harts & (1 << hart)) <> 0 { set notifications += 1; } }
226
    for hart in 0..8 {
227
        if (boot::PLATFORM.harts & (1 << hart)) == 0 { continue; }
228
        let value = sync::loadAcquire(memory(COUNTERS + hart as u64 * 8) as *u64);
229
        set counts[hart] = value;
230
        let context = try! domains::context(&domains::STORE, USER, CONTEXTS[hart]);
231
        assert context.owner == USER;
232
        let mode = context.frame.status & 0x1800;
233
        assert mode == 0 or mode == 0x1800;
234
        if mode == 0x1800 {
235
            assert context.frame.registers[3] == kernelGp();
236
            assert context.frame.registers[2] >= context.kernelStack.start and context.frame.registers[2] <= context.kernelStack.end;
237
        } else {
238
            assert context.frame.registers[3] == user.graph.table.ptr as u64;
239
        }
240
        let destination = (hart + notifications - 1) % notifications;
241
        assert context.hart == nil or context.hart == destination;
242
        assert sync::loadAcquire(memory(MIGRATIONS + hart as u64 * 8) as *u64) == destination as u64 + 1;
243
        assert context.pending == nil;
244
    }
245
    assert domains::STORE.events.queues[USER.index].tail == notifications + 1;
246
    assert user.memory.ring.data[0].kind == events::INTERRUPT and user.memory.ring.data[0].code == 7;
247
    sync::leave(guard);
248
    for byte in "smp completed transactions:" { put(byte); }
249
    for hart in 0..8 {
250
        if (boot::PLATFORM.harts & (1 << hart)) == 0 { continue; }
251
        put(32); number(counts[hart]);
252
    }
253
    put(10);
254
    for hart in 0..8 {
255
        if (boot::PLATFORM.harts & (1 << hart)) <> 0 { assert counts[hart] == 16; }
256
    }
257
    for target in 0..8 {
258
        if (boot::PLATFORM.harts & (1 << target)) == 0 { continue; }
259
        for action in &[remote::Action::Reschedule, remote::Action::InstructionSync] {
260
            let posting = sync::enter();
261
            let id = try! dispatch::request(target, action);
262
            sync::leave(posting);
263
            let mut acknowledged = false;
264
            for attempt in 0..100000 {
265
                let guard = sync::enter();
266
                if try! remote::completed(&remote::STORE, 0, id) {
267
                    try! remote::release(&mut remote::STORE, 0, id);
268
                    set acknowledged = true;
269
                }
270
                sync::leave(guard);
271
                if acknowledged { break; }
272
            }
273
            assert acknowledged;
274
        }
275
    }
276
    for byte in "smp request acknowledgements passed\n" { put(byte); }
277
    expiredCall();
278
    if (boot::PLATFORM.harts & 2) <> 0 { idleWakeup(); }
279
    interruptOwnership();
280
    interruptStorm();
281
    if (boot::PLATFORM.harts & 2) <> 0 { terminalWakeup(); cancelAllocation(); reclaimedDomains(); }
282
    invariants::check(&TABLE);
283
    for byte in "smp final ownership and interval invariants passed\n" { put(byte); }
284
    let maximum = sync::maximum();
285
    assert maximum > 0 and maximum < 1000000;
286
    for byte in "smp metadata instructions: 0x" { put(byte); }
287
    number(maximum);
288
    put(10);
289
    let waiting = sync::maximumWait();
290
    assert waiting > 0;
291
    for byte in "smp acquisition instructions: 0x" { put(byte); }
292
    number(waiting);
293
    put(10);
294
    finish();
295
}
296
297
/// Validate elapsed authority against the current clock at the serialized boundary.
298
unsafe fn expiredCall() {
299
    let guard = sync::enter();
300
    let checker = try! domains::get(&domains::STORE, CHECKER);
301
    let before = try! budgets::query(&budgets::STORE, &checker.memory.table, EXPIRED);
302
    assert before.end < dispatch::clock();
303
    sync::leave(guard);
304
    let mut rejected = false;
305
    try calls::synchronized(CHECKER, 70, &[*EXPIRED, before.start + (before.end - before.start) / 2, 0, 0], 0) catch error {
306
        assert error == abi::Error::Busy; set rejected = true;
307
    };
308
    assert rejected;
309
    let checking = sync::enter();
310
    let after = try! budgets::query(&budgets::STORE, &checker.memory.table, EXPIRED);
311
    assert after.start == before.start and after.end == before.end;
312
    sync::leave(checking);
313
    for byte in "smp resumed call rejected expired authority\n" { put(byte); }
314
}
315
316
/// Transfer a queued IRQ, rearm after consumption, and mask it before capability drop.
317
unsafe fn interruptOwnership() {
318
    let guard = sync::enter();
319
    let mut user = try! domains::get(&domains::STORE, USER);
320
    let mut checker = try! domains::get(&domains::STORE, CHECKER);
321
    let device = try! plic::mapping(&boot::PLATFORM) else panic "missing PLIC";
322
    let enabled = memory(device.base + 0x2000 + device.context as u64 * 0x80) as *u32;
323
    assert (sync::read32(enabled) & (1 << 7)) == 0;
324
    assert interrupts::STORE.records[6].state == interrupts::State::Queued;
325
    let original = interrupts::STORE.records[6].handle;
326
    let target = try! capability::install(&mut user.memory.table, capability::Entry {
327
        kind: abi::Kind::Domain, object: CHECKER, rights: abi::Rights(0),
328
    });
329
    let back = try! capability::install(&mut checker.memory.table, capability::Entry {
330
        kind: abi::Kind::Domain, object: USER, rights: abi::Rights(0),
331
    });
332
    let moved = try! calls::invoke(USER, 11, &[*original, *target, (abi::READ | abi::TRANSFER) as u64, 0], 0);
333
    assert interrupts::STORE.records[6].owner == CHECKER;
334
    let first = plic::service(dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0)));
335
    assert interrupts::STORE.records[6].state == interrupts::State::Queued;
336
    assert (sync::read32(enabled) & (1 << 7)) == 0;
337
    let event = try! events::pop(&mut checker.memory.ring) else panic "missing transferred IRQ";
338
    assert event.kind == events::INTERRUPT and event.code == 7;
339
    let before = domains::STORE.events.queues[USER.index].tail;
340
    let returned = try! calls::invoke(CHECKER, 11, &[moved, *back, (abi::READ | abi::TRANSFER) as u64, 0], 0);
341
    let second = plic::service(dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0)));
342
    assert domains::STORE.events.queues[USER.index].tail == before;
343
    assert (sync::read32(enabled) & (1 << 7)) == 0;
344
    let consumed = try! events::pop(&mut user.memory.ring) else panic "missing original IRQ";
345
    assert consumed.kind == events::INTERRUPT and consumed.code == 7;
346
    let rearmed = plic::service(dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0)));
347
    assert interrupts::STORE.records[6].state == interrupts::State::Idle;
348
    assert (sync::read32(enabled) & (1 << 7)) <> 0;
349
    let dropped = try! calls::invoke(USER, 12, &[returned, 0, 0, 0], 0);
350
    assert dropped == 0 and interrupts::STORE.slots[6].state == slots::State::Free;
351
    assert (sync::read32(enabled) & (1 << 7)) == 0;
352
    sync::leave(guard);
353
    for byte in "smp IRQ transfer, rearm, and drop passed\n" { put(byte); }
354
}
355
356
/// Keep a level source asserted across full queues and repeated driver progress.
357
unsafe fn interruptStorm() {
358
    let guard = sync::enter();
359
    let mut checker = try! domains::get(&domains::STORE, CHECKER);
360
    let handle = try! interrupts::seed(&mut interrupts::STORE, &mut checker.memory.table, 10);
361
    let device = try! plic::mapping(&boot::PLATFORM) else panic "missing PLIC";
362
    let enabled = memory(device.base + 0x2000 + device.context as u64 * 0x80) as *u32;
363
    sync::leave(guard);
364
    for i in 0..events::ORDINARY {
365
        let filling = sync::enter();
366
        assert try! domains::notify(&mut domains::STORE, CHECKER,
367
            events::Event { kind: events::WAKEUP, reserved: 0, code: i, value: 0 });
368
        sync::leave(filling);
369
    }
370
    let arming = sync::enter();
371
    let ready = plic::service(dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0)));
372
    uartInterrupt(2);
373
    sync::leave(arming);
374
    let mut pending = false;
375
    for attempt in 0..10000 {
376
        let checking = sync::enter();
377
        set pending = interrupts::STORE.records[9].state == interrupts::State::Pending;
378
        if pending {
379
            assert (sync::read32(enabled) & (1 << 10)) == 0;
380
            let queue = domains::STORE.events.queues[CHECKER.index];
381
            assert queue.tail - queue.head == events::ORDINARY;
382
        }
383
        sync::leave(checking);
384
        if pending { break; }
385
    }
386
    assert pending;
387
    for i in 0..events::ORDINARY {
388
        let draining = sync::enter();
389
        let mut receiver = try! domains::get(&domains::STORE, CHECKER);
390
        let event = try! events::pop(&mut receiver.memory.ring) else panic "missing saturation event";
391
        assert event.kind == events::WAKEUP and event.code == i;
392
        sync::leave(draining);
393
    }
394
    let posting = sync::enter();
395
    let clock = dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0));
396
    try! timers::request(&mut timers::STORE, &domains::STORE, CHECKER, 0, clock, 2000000, 0x77);
397
    let request = try! dispatch::request(0, remote::Action::Reschedule);
398
    sync::leave(posting);
399
    let acknowledged = try! dispatch::awaitRequest(0, request);
400
    assert acknowledged == 0;
401
    let mut received: u32 = 0;
402
    let mut timed = false;
403
    for attempt in 0..1000 {
404
        let consuming = sync::enter();
405
        let mut receiver = try! domains::get(&domains::STORE, CHECKER);
406
        if let event = try! events::pop(&mut receiver.memory.ring) {
407
            if event.kind == events::INTERRUPT {
408
                assert event.code == 10;
409
                set received += 1;
410
            } else {
411
                assert event.kind == events::TIMEOUT and event.code == 0x77;
412
                assert dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0)) >= clock + 2000000;
413
                set timed = true;
414
            }
415
        }
416
        let notified = plic::service(dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0)));
417
        sync::leave(consuming);
418
        if timed and received >= 16 { break; }
419
    }
420
    let stopping = sync::enter();
421
    uartInterrupt(0);
422
    let dropped = try! calls::invoke(CHECKER, 12, &[*handle, 0, 0, 0], 0);
423
    assert dropped == 0 and (sync::read32(enabled) & (1 << 10)) == 0;
424
    sync::leave(stopping);
425
    assert timed and received >= 16;
426
    for byte in "smp IRQ saturation and storm timeout passed\n" { put(byte); }
427
}
428
429
/// Resume an actual U-mode Wait call through an IPI to an idle remote hart.
430
unsafe fn idleWakeup() {
431
    let guard = sync::enter();
432
    let clock = dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0));
433
    let handle = try! budgets::bind(&mut budgets::STORE, &domains::STORE, &mut TABLE,
434
        budgets::Binding { budget: IDLE_WINDOW, domain: IDLE_HANDLE, context: IDLE_CONTEXT }, clock);
435
    let request = try! dispatch::request(1, remote::Action::Reschedule);
436
    sync::leave(guard);
437
    let acknowledged = try! dispatch::awaitRequest(0, request);
438
    assert acknowledged == 0;
439
    let activated = try! calls::synchronized(CHECKER, 21, &[*RECEIVER, IDLE_STACK, 0, 0], clock);
440
    assert activated == 0 and waiting();
441
    let now = dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0));
442
    let result = try! calls::synchronized(CHECKER, 50, &[*RECEIVER, 0x55, 0, 0], now);
443
    assert result == 0;
444
    let mut resumed = false;
445
    for attempt in 0..100000 {
446
        if sync::loadAcquire(memory(WAKE_STATE) as *u64) == 3 { set resumed = true; break; }
447
    }
448
    assert resumed;
449
    for byte in "smp idle-hart activation and wakeup passed\n" { put(byte); }
450
    remoteTimeout();
451
}
452
453
/// Deliver a timeout from hart zero to a waiting user context on idle hart one.
454
unsafe fn remoteTimeout() {
455
    let guard = sync::enter();
456
    let mut domain = try! domains::get(&domains::STORE, IDLE_DOMAIN);
457
    let event = try! events::pop(&mut domain.memory.ring) else panic "missing wakeup";
458
    assert event.kind == events::WAKEUP and event.code == 0x55;
459
    sync::storeRelease(memory(WAKE_STATE) as *mut u64, 4);
460
    sync::leave(guard);
461
    assert waiting();
462
    let posting = sync::enter();
463
    let clock = dispatch::now(try! dispatch::timer(&boot::PLATFORM, 0));
464
    try! timers::request(&mut timers::STORE, &domains::STORE, IDLE_DOMAIN, 0, clock, 100000, 0x66);
465
    let request = try! dispatch::request(0, remote::Action::Reschedule);
466
    sync::leave(posting);
467
    let acknowledged = try! dispatch::awaitRequest(0, request);
468
    assert acknowledged == 0;
469
    let mut resumed = false;
470
    for attempt in 0..100000 {
471
        if sync::loadAcquire(memory(WAKE_STATE) as *u64) == 6 { set resumed = true; break; }
472
    }
473
    assert resumed;
474
    for byte in "smp remote timeout passed\n" { put(byte); }
475
}
476
477
/// Wake a parent waiting on hart one through terminal delivery on hart zero.
478
unsafe fn terminalWakeup() {
479
    let guard = sync::enter();
480
    let mut receiver = try! domains::get(&domains::STORE, IDLE_DOMAIN);
481
    let timeout = try! events::pop(&mut receiver.memory.ring) else panic "missing idle timeout";
482
    assert timeout.kind == events::TIMEOUT and timeout.code == 0x66;
483
    sync::storeRelease(memory(WAKE_STATE) as *mut u64, 7);
484
    sync::leave(guard);
485
    assert waiting();
486
    let stopping = sync::enter();
487
    let mut checker = try! domains::get(&domains::STORE, CHECKER);
488
    let child = try! capability::install(&mut checker.memory.table, capability::Entry {
489
        kind: abi::Kind::Domain, object: USER, rights: abi::Rights(abi::DESTROY),
490
    });
491
    let parent = try! calls::invoke(CHECKER, 23, &[*child, *RECEIVER, 0, 0], 0);
492
    assert parent == 0;
493
    let cancelled = try! dispatch::request(1, remote::Action::Budget(remote::Call {
494
        owner: USER, context: CONTEXTS[0], operation: 70, arguments: [0; 4],
495
    }));
496
    set domains::STORE.contexts[CONTEXTS[0].index].pending = cancelled;
497
    let readiness = try! remote::reserve(&mut remote::STORE, 0, 2);
498
    let batch = remote::publish(&mut remote::STORE, readiness, remote::Action::Ready(CONTEXTS[1]));
499
    dispatch::startReady(CONTEXTS[1], &batch);
500
    let stopped = try! domains::terminateTree(&mut domains::STORE, USER, events::CHILD_EXIT, 23);
501
    assert stopped == 0;
502
    assert domains::STORE.records[DESCENDANT.index].state == domains::Lifecycle::Dead;
503
    assert try! events::acknowledged(&domains::STORE.events, DESCENDANT);
504
    let request = try! dispatch::request(0, remote::Action::Reschedule);
505
    sync::leave(stopping);
506
    let acknowledged = try! dispatch::awaitRequest(0, request);
507
    assert acknowledged == 0;
508
    let mut resumed = false;
509
    for attempt in 0..100000 {
510
        if sync::loadAcquire(memory(WAKE_STATE) as *u64) == 9 { set resumed = true; break; }
511
    }
512
    assert resumed;
513
    let checking = sync::enter();
514
    assert remote::STORE.slots[cancelled.index].state == slots::State::Free;
515
    assert remote::STORE.slots[batch.ids[1].index].state == slots::State::Free;
516
    assert domains::STORE.contexts[CONTEXTS[0].index].pending == nil;
517
    assert domains::STORE.contexts[CONTEXTS[1].index].pending == nil;
518
    let mut domain = try! domains::get(&domains::STORE, IDLE_DOMAIN);
519
    let event = try! events::pop(&mut domain.memory.ring) else panic "missing remote exit";
520
    assert event.kind == events::CHILD_EXIT and event.code == 23 and event.value == abi::id(USER);
521
    sync::leave(checking);
522
    for byte in "smp cascade cancelled calls and woke remote parent\n" { put(byte); }
523
}
524
525
/// Stop a real page-clear continuation and recover its unpublished frames.
526
unsafe fn cancelAllocation() {
527
    sync::storeRelease(memory(WAKE_STATE) as *mut u64, 10);
528
    let mut allocation: pages::Allocation = undefined;
529
    let mut request: ?abi::Ref = nil;
530
    for attempt in 0..100000 {
531
        let guard = sync::enter();
532
        let index = IDLE_CONTEXT.index;
533
        if (lifecycle::CALLS.active[index / 32] & (1 << (index % 32))) <> 0 {
534
            match lifecycle::CALLS.records[index].allocation {
535
                case lifecycle::Allocation::Page(value) => { set allocation = value; },
536
                else => panic "page allocation required",
537
            }
538
            assert allocation.owner == IDLE_DOMAIN and allocation.run.count == 4096;
539
            sync::leave(guard);
540
            let result = try! calls::synchronized(CHECKER, 22, &[*RECEIVER, 0, 0, 0], dispatch::clock());
541
            assert result == 0;
542
            let checking = sync::enter();
543
            assert domains::STORE.records[IDLE_DOMAIN.index].state == domains::Lifecycle::Dead;
544
            set request = try! dispatch::request(1, remote::Action::Reschedule);
545
            sync::leave(checking);
546
            break;
547
        }
548
        sync::leave(guard);
549
    }
550
    let stopped = request else panic "page call did not reserve frames";
551
    let acknowledged = try! dispatch::awaitRequest(0, stopped);
552
    assert acknowledged == 0;
553
    let snapshot = sync::enter();
554
    let context = try! domains::context(&domains::STORE, IDLE_DOMAIN, IDLE_CONTEXT);
555
    for byte in "smp cancelled page status and progress: " { put(byte); }
556
    number(context.frame.status); put(32);
557
    number(sync::loadAcquire(memory(WAKE_STATE) as *u64)); put(10);
558
    assert context.hart == nil and context.state == domains::ContextState::Stopped;
559
    assert (context.frame.status & 0x1800) == 0x1800;
560
    assert sync::loadAcquire(memory(WAKE_STATE) as *u64) == 11;
561
    sync::leave(snapshot);
562
    let mut reclaimed = false;
563
    for attempt in 0..1000 {
564
        let checking = sync::enter();
565
        set reclaimed = pages::STORE.backings.pool.retiredCount == 0;
566
        if reclaimed { sync::leave(checking); break; }
567
        let progress = try! dispatch::request(0, remote::Action::Reschedule);
568
        sync::leave(checking);
569
        let result = try! dispatch::awaitRequest(0, progress);
570
        assert result == 0;
571
    }
572
    assert reclaimed;
573
    let guard = sync::enter();
574
    assert (lifecycle::CALLS.active[IDLE_CONTEXT.index / 32] & (1 << (IDLE_CONTEXT.index % 32))) == 0;
575
    assert pages::STORE.slots[allocation.page.index].state == slots::State::Free;
576
    assert pages::STORE.backings.slots[allocation.backing.index].state == slots::State::Free;
577
    for i in allocation.run.first..allocation.run.first + allocation.run.count {
578
        assert frames::available(&pages::STORE.backings.pool, i);
579
    }
580
    sync::leave(guard);
581
    for byte in "smp cancelled preempted page allocation\n" { put(byte); }
582
}
583
584
/// Wait for dispatch-driven teardown and verify all dead execution ownership is gone.
585
unsafe fn reclaimedDomains() {
586
    for attempt in 0..100000 {
587
        let guard = sync::enter();
588
        let mut done = pages::STORE.backings.pool.retiredCount == 0;
589
        for owner in [USER, IDLE_DOMAIN, DESCENDANT] {
590
            if slots::matches(&domains::STORE.slots[..], owner, slots::State::Live) { set done = false; }
591
        }
592
        if done {
593
            assert domains::STORE.dead == 0;
594
            for owner in [USER, IDLE_DOMAIN, DESCENDANT] {
595
                assert domains::STORE.slots[owner.index].state == slots::State::Free;
596
                assert pages::STORE.backings.domains[owner.index] == 0;
597
                for i in 0..domains::STORE.contextSlots.len {
598
                    if domains::STORE.contextSlots[i].state == slots::State::Live {
599
                        assert domains::STORE.contexts[i].owner <> owner;
600
                    }
601
                }
602
                for i in 0..budgets::STORE.slots.len {
603
                    if budgets::STORE.slots[i].state == slots::State::Live { assert budgets::STORE.windows[i].owner <> owner; }
604
                }
605
                for i in 0..interrupts::STORE.slots.len {
606
                    if interrupts::STORE.slots[i].state == slots::State::Live { assert interrupts::STORE.records[i].owner <> owner; }
607
                }
608
            }
609
            sync::leave(guard);
610
            for byte in "smp reaped dead domains and recovered ownership\n" { put(byte); }
611
            return;
612
        }
613
        let progress = try! dispatch::request(0, remote::Action::Reschedule);
614
        sync::leave(guard);
615
        let result = try! dispatch::awaitRequest(0, progress);
616
        assert result == 0;
617
    }
618
    panic "domain reaper did not finish";
619
}
620
621
/// Confirm the waiting context has released hart ownership before its wakeup.
622
unsafe fn waiting() -> bool {
623
    for attempt in 0..100000 {
624
        let guard = sync::enter();
625
        let context = try! domains::context(&domains::STORE, IDLE_DOMAIN, IDLE_CONTEXT);
626
        let stopped = context.state == domains::ContextState::Waiting and context.hart == nil;
627
        sync::leave(guard);
628
        if stopped { return true; }
629
    }
630
    return false;
631
}
632
633
/// Print one full-width hexadecimal diagnostic value.
634
fn number(value: u64) {
635
    let digits = "0123456789abcdef";
636
    for i in 0..16 { put(digits[((value >> ((15 - i) as u64 * 4)) & 15) as u32]); }
637
}