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