kernel: Complete preemptible runtime loading

407a0145ce370ff9fde042af1b3b1ecd075db506943939f32d16545bf4b6708a
Alexis Sellier committed ago 1 parent 60847643
kernel/kernel/frames.rad +89 -21
21 21
22 22
/// Physical addresses and occupancy of allocatable frames, sorted by address.
23 23
export record Pool: Copy {
24 24
    /// Physical base address of each eligible frame.
25 25
    addresses: [u64; limits::FRAMES],
26 -
    /// True when the frame is available for reservation.
27 -
    free: [bool; limits::FRAMES],
26 +
    /// One set bit per available frame, indexed from the low bit of each word.
27 +
    free: [u64; limits::FRAMES / 64],
28 28
    /// Number of valid frame-map entries.
29 29
    count: u32,
30 30
    /// Retired runs whose frames remain unavailable until reclamation.
31 31
    retired: [Run; limits::FRAMES],
32 32
    /// Index of the next retired run to reclaim.
46 46
47 47
/// Build a fresh frame map from complete RAM pages with no reserved byte.
48 48
/// The pool has zero valid entries if initialization fails.
49 49
export unsafe fn initialize(pool: &mut Pool, machine: &platform::Platform) throws (abi::Error) {
50 50
    set pool.count = 0;
51 +
    set pool.free = [0; limits::FRAMES / 64];
51 52
    set pool.retiredHead = 0;
52 53
    set pool.retiredCount = 0;
53 54
    if machine.ramCount > platform::RAM_BANKS or machine.reservedCount > platform::RESERVATIONS {
54 55
        throw abi::Error::InvalidArg;
55 56
    }
95 96
            }
96 97
            if count == limits::FRAMES {
97 98
                throw abi::Error::Exhausted;
98 99
            }
99 100
            set pool.addresses[count] = address;
100 -
            set pool.free[count] = true;
101 +
            set pool.free[count / 64] |= 1 << (count % 64) as u64;
101 102
            set count += 1;
102 103
            set address += limits::FRAME_SIZE;
103 104
        }
104 105
    }
105 106
    set pool.count = count;
106 107
}
107 108
108 -
/// Reserve the first physically contiguous free run with a bounded scan.
109 +
/// Test one valid frame's availability without exposing unused bitmap bits.
110 +
export fn available(pool: &Pool, index: u32) -> bool {
111 +
    return index < pool.count and (pool.free[index / 64] & (1 << (index % 64) as u64)) <> 0;
112 +
}
113 +
114 +
/// Low bits selected by a nonzero word-sized span.
115 +
fn mask(count: u32) -> u64 {
116 +
    assert count > 0 and count <= 64;
117 +
    if count == 64 {
118 +
        return 0xffffffffffffffff;
119 +
    }
120 +
    return (1 << count as u64) - 1;
121 +
}
122 +
123 +
/// Change a validated run's occupancy one bitmap word at a time.
124 +
fn assign(pool: &mut Pool, run: Run, free: bool) {
125 +
    let mut first = run.first;
126 +
    let end = first + run.count;
127 +
    while first < end {
128 +
        let offset = first % 64;
129 +
        let mut count = 64 - offset;
130 +
        if count > end - first {
131 +
            set count = end - first;
132 +
        }
133 +
        let bits = mask(count) << offset as u64;
134 +
        if free {
135 +
            set pool.free[first / 64] |= bits;
136 +
        }
137 +
        else {
138 +
            set pool.free[first / 64] &= ~bits;
139 +
        }
140 +
        set first += count;
141 +
    }
142 +
}
143 +
144 +
/// Reserve the first contiguous run, scanning uniform bitmap words together.
109 145
export fn reserve(pool: &mut Pool, count: u32) -> Reservation throws (abi::Error) {
110 146
    if count == 0 {
111 147
        throw abi::Error::InvalidArg;
112 148
    }
113 149
    let mut first: u32 = 0;
114 150
    let mut found: u32 = 0;
115 151
    let mut previous: u64 = 0;
116 -
    for i in 0..pool.count {
117 -
        if not pool.free[i] {
118 -
            set found = 0;
119 -
            continue;
152 +
    for word in 0..(pool.count + 63) / 64 {
153 +
        let base = word * 64;
154 +
        let mut length = pool.count - base;
155 +
        if length > 64 {
156 +
            set length = 64;
120 157
        }
121 -
        if found == 0 or pool.addresses[i] <> previous + limits::FRAME_SIZE {
122 -
            set first = i;
158 +
        let full = mask(length);
159 +
        let bits = pool.free[word] & full;
160 +
        if bits == 0 {
123 161
            set found = 0;
162 +
            continue;
124 163
        }
125 -
        set found += 1;
126 -
        set previous = pool.addresses[i];
127 -
        if found == count {
128 -
            for j in first..first + count {
129 -
                set pool.free[j] = false;
164 +
        let address = pool.addresses[base];
165 +
        if bits == full and pool.addresses[base + length - 1] - address == (length - 1) as u64 * limits::FRAME_SIZE {
166 +
            if found == 0 or address <> previous + limits::FRAME_SIZE {
167 +
                set first = base;
168 +
                set found = 0;
130 169
            }
131 -
            return Reservation::Held(Run { first, count });
170 +
            set found += length;
171 +
            set previous = pool.addresses[base + length - 1];
172 +
        } else {
173 +
            for offset in 0..length {
174 +
                let i = base + offset;
175 +
                if (bits & (1 << offset as u64)) == 0 {
176 +
                    set found = 0;
177 +
                    continue;
178 +
                }
179 +
                if found == 0 or pool.addresses[i] <> previous + limits::FRAME_SIZE {
180 +
                    set first = i;
181 +
                    set found = 0;
182 +
                }
183 +
                set found += 1;
184 +
                set previous = pool.addresses[i];
185 +
                if found >= count {
186 +
                    break;
187 +
                }
188 +
            }
189 +
        }
190 +
        if found >= count {
191 +
            let run = Run { first, count };
192 +
            assign(pool, run, false);
193 +
            return Reservation::Held(run);
132 194
        }
133 195
    }
134 196
    throw abi::Error::OutOfMemory;
135 197
}
136 198
157 219
158 220
/// Return a backing allocation's frames after all handles and exposures end.
159 221
/// Only the owner of a committed run may release it.
160 222
export fn release(pool: &mut Pool, run: Run) throws (abi::Error) {
161 223
    let memory = try extent(pool, run);
162 -
    for i in run.first..run.first + run.count {
163 -
        if pool.free[i] {
224 +
    let mut first = run.first;
225 +
    let end = first + run.count;
226 +
    while first < end {
227 +
        let offset = first % 64;
228 +
        let mut count = 64 - offset;
229 +
        if count > end - first {
230 +
            set count = end - first;
231 +
        }
232 +
        if (pool.free[first / 64] & (mask(count) << offset as u64)) <> 0 {
164 233
            throw abi::Error::BadHandle;
165 234
        }
235 +
        set first += count;
166 236
    }
167 -
    for i in run.first..run.first + run.count {
168 -
        set pool.free[i] = true;
169 -
    }
237 +
    assign(pool, run, true);
170 238
}
171 239
172 240
/// Transfer exclusive ownership of a committed run to bounded reclamation.
173 241
/// The caller must not release or retire this run again.
174 242
export fn retire(pool: &mut Pool, run: Run) throws (abi::Error) {
kernel/kernel/tests/backing.rad +13 -13
15 15
    try! backing::registerDomain(&mut STORE, abi::Ref { index: 0, generation: 1 });
16 16
    try! backing::registerDomain(&mut STORE, abi::Ref { index: 1, generation: 1 });
17 17
    set STORE.pool.count = 4;
18 18
    for i in 0..4 {
19 19
        set STORE.pool.addresses[i] = 0x80000000 + i as u64 * 4096;
20 -
        set STORE.pool.free[i] = true;
20 +
        set STORE.pool.free[i / 64] |= 1 << (i % 64) as u64;
21 21
    }
22 22
}
23 23
24 24
/// Publish one backing record with its initial page and origin exposure.
25 25
unsafe fn allocate() -> abi::Ref {
50 50
    };
51 51
    assert invalid and backing::domainLive(&STORE, owner);
52 52
    assert not try! backing::endDomainStep(&mut STORE, owner, 1);
53 53
    assert not backing::domainLive(&STORE, owner);
54 54
    assert STORE.slots[objects[0].index].state == slots::State::Free;
55 -
    assert STORE.pool.retiredCount == 1 and not STORE.pool.free[0];
55 +
    assert STORE.pool.retiredCount == 1 and not frames::available(&STORE.pool, 0);
56 56
    assert STORE.records[objects[1].index].exposed == 2;
57 57
    let mut busy = false;
58 58
    try backing::registerDomain(&mut STORE, abi::Ref { index: 0, generation: 2 }) catch error {
59 59
        assert error == abi::Error::Busy; set busy = true;
60 60
    };
69 69
    assert STORE.records[objects[2].index].exposed == 1;
70 70
    while not try! backing::endDomainStep(&mut STORE, owner, 64) {
71 71
    }
72 72
    assert STORE.records[objects[2].index].exposed == 0 and STORE.records[objects[2].index].pages == 1;
73 73
    assert STORE.pool.retiredCount == 1;
74 -
    assert frames::reclaim(&mut STORE.pool, 1) == 1 and STORE.pool.free[0];
75 -
    assert not STORE.pool.free[1] and not STORE.pool.free[2];
74 +
    assert frames::reclaim(&mut STORE.pool, 1) == 1 and frames::available(&STORE.pool, 0);
75 +
    assert not frames::available(&STORE.pool, 1) and not frames::available(&STORE.pool, 2);
76 76
    let next = abi::Ref { index: 0, generation: 2 };
77 77
    try! backing::registerDomain(&mut STORE, next);
78 78
    assert backing::domainLive(&STORE, next);
79 79
    let mut stale = false;
80 80
    try backing::endDomainStep(&mut STORE, owner, 64) catch error {
81 81
        assert error == abi::Error::BadHandle; set stale = true;
82 82
    };
83 83
    assert stale and backing::domainLive(&STORE, next);
84 84
    try! backing::releasePage(&mut STORE, objects[2]);
85 -
    assert STORE.pool.free[2];
85 +
    assert frames::available(&STORE.pool, 2);
86 86
    while not try! backing::endDomainStep(&mut STORE, receiver, 64) {
87 87
    }
88 -
    assert STORE.pool.retiredCount == 1 and not STORE.pool.free[1];
89 -
    assert frames::reclaim(&mut STORE.pool, 1) == 1 and STORE.pool.free[1];
88 +
    assert STORE.pool.retiredCount == 1 and not frames::available(&STORE.pool, 1);
89 +
    assert frames::reclaim(&mut STORE.pool, 1) == 1 and frames::available(&STORE.pool, 1);
90 90
}
91 91
92 92
/// Dropping all page handles preserves capacity while an exposed domain lives.
93 93
@test unsafe fn exposures() throws (testing::TestError) {
94 94
    initialize();
97 97
    try! backing::expose(&mut STORE, object, receiver);
98 98
    try! backing::expose(&mut STORE, object, receiver);
99 99
    try testing::expect(STORE.records[object.index].exposed == 2);
100 100
    try! backing::releasePage(&mut STORE, object);
101 101
    try! backing::endDomain(&mut STORE, abi::Ref { index: 0, generation: 1 });
102 -
    try testing::expect(not STORE.pool.free[0] and slots::matches(&STORE.slots[..], object, slots::State::Live));
102 +
    assert not frames::available(&STORE.pool, 0) and slots::matches(&STORE.slots[..], object, slots::State::Live);
103 103
    try! backing::endDomain(&mut STORE, receiver);
104 104
    for i in 0..4 {
105 -
        try testing::expect(STORE.pool.free[i]);
105 +
        assert frames::available(&STORE.pool, i);
106 106
    }
107 107
    try testing::expect(not slots::matches(&STORE.slots[..], object, slots::State::Live));
108 108
}
109 109
110 110
/// Splits keep their common backing alive until every page object is gone.
112 112
    initialize();
113 113
    let object = allocate();
114 114
    try! backing::retainPage(&mut STORE, object);
115 115
    try! backing::endDomain(&mut STORE, abi::Ref { index: 0, generation: 1 });
116 116
    try! backing::releasePage(&mut STORE, object);
117 -
    try testing::expect(not STORE.pool.free[0] and STORE.records[object.index].pages == 1);
117 +
    assert not frames::available(&STORE.pool, 0) and STORE.records[object.index].pages == 1;
118 118
    try! backing::releasePage(&mut STORE, object);
119 -
    try testing::expect(STORE.pool.free[0]);
119 +
    assert frames::available(&STORE.pool, 0);
120 120
    let mut stale = false;
121 121
    try backing::releasePage(&mut STORE, object) catch err {
122 122
        try testing::expect(err == abi::Error::BadHandle); set stale = true;
123 123
    };
124 124
    try testing::expect(stale);
137 137
        try testing::expect(err == abi::Error::BadHandle); set stale = true;
138 138
    };
139 139
    try testing::expect(stale and STORE.records[object.index].exposed == 1);
140 140
    try! backing::releasePage(&mut STORE, object);
141 141
    try! backing::endDomain(&mut STORE, abi::Ref { index: 0, generation: 1 });
142 -
    try testing::expect(STORE.pool.free[0]);
142 +
    assert frames::available(&STORE.pool, 0);
143 143
}
144 144
145 145
/// Retained backing metadata cannot create new page authority after the last page ends.
146 146
@test unsafe fn noResurrection() throws (testing::TestError) {
147 147
    initialize();
154 154
    try backing::expose(&mut STORE, object, abi::Ref { index: 1, generation: 1 }) catch err {
155 155
        try testing::expect(err == abi::Error::BadHandle); set failures += 1;
156 156
    };
157 157
    try testing::expect(failures == 2 and STORE.records[object.index].exposed == 1);
158 158
    try! backing::endDomain(&mut STORE, abi::Ref { index: 0, generation: 1 });
159 -
    try testing::expect(STORE.pool.free[0]);
159 +
    assert frames::available(&STORE.pool, 0);
160 160
}
kernel/kernel/tests/calls.rad +2 -1
1 1
//! Direct-call scalar results, checked record copies, and architectural replies.
2 2
3 3
use std::testing;
4 4
use kernel::abi;
5 +
use kernel::frames;
5 6
use kernel::calls;
6 7
use kernel::domains;
7 8
use kernel::pages;
8 9
use kernel::backing;
9 10
use kernel::capability;
302 303
    let aligned = (base + 4095) & ~4095;
303 304
    set OFFSET = ((aligned - base) / 8) as u32;
304 305
    set pages::STORE.backings.pool.count = 4;
305 306
    for i in 0..4 {
306 307
        set pages::STORE.backings.pool.addresses[i] = aligned + i as u64 * 4096;
307 -
        set pages::STORE.backings.pool.free[i] = true;
308 +
        set pages::STORE.backings.pool.free[i / 64] |= 1 << (i % 64) as u64;
308 309
    }
309 310
    let pending = try! slots::reserve(&mut domains::STORE.slots[..]);
310 311
    let owner = try! slots::commit(&mut domains::STORE.slots[..], pending);
311 312
    try! backing::registerDomain(&mut pages::STORE.backings, owner);
312 313
    capability::initialize(&mut MEMORY.table, owner);
kernel/kernel/tests/domains.rad +15 -15
49 49
    let base = pointer as u64;
50 50
    let aligned = (base + 4095) & ~4095;
51 51
    set BACKINGS.pool.count = (@sizeOf([u64; 131072]) - (aligned - base) as u32) / 4096;
52 52
    for i in 0..BACKINGS.pool.count {
53 53
        set BACKINGS.pool.addresses[i] = aligned + i as u64 * 4096;
54 -
        set BACKINGS.pool.free[i] = true;
54 +
        set BACKINGS.pool.free[i / 64] |= 1 << (i % 64) as u64;
55 55
    }
56 56
    let pending = try! slots::reserve(&mut DOMAINS.slots[..]);
57 57
    let owner = try! slots::commit(&mut DOMAINS.slots[..], pending);
58 58
    set DOMAINS.records[owner.index].state = domains::Lifecycle::Active;
59 59
    set DOMAINS.records[owner.index].parent = nil;
102 102
    set DOMAINS.records[permit.object.index].state = domains::Lifecycle::Dead;
103 103
    set DOMAINS.contexts[caller.initial.index].state = domains::ContextState::Stopped;
104 104
    set DOMAINS.contexts[caller.initial.index].hart = 1;
105 105
    lifecycle::cancelAllocations(&mut CALLS, &mut DOMAINS, &mut PAGES, &mut loader::STATE, &mut registry::STORE);
106 106
    assert DOMAINS.contexts[caller.initial.index].reservation;
107 -
    assert not PAGES.backings.pool.free[creation.storage.allocation.first];
107 +
    assert not frames::available(&PAGES.backings.pool, creation.storage.allocation.first);
108 108
    match lease {
109 109
        case lifecycle::Lease::Held(context) => {
110 110
            assert context == caller.initial;
111 111
        },
112 112
    }
121 121
        let reclaimed = frames::reclaim(&mut PAGES.backings.pool, 256);
122 122
        assert reclaimed > 0 and reclaimed <= 256;
123 123
    }
124 124
    for run in [creation.storage.allocation, creation.storage.kernelFrames, creation.storage.graph.frames] {
125 125
        for i in run.first..run.first + run.count {
126 -
            assert PAGES.backings.pool.free[i];
126 +
            assert frames::available(&PAGES.backings.pool, i);
127 127
        }
128 128
    }
129 129
    set DOMAINS.records[permit.object.index].state = domains::Lifecycle::Active;
130 130
    let next = try! domains::reserve(&mut DOMAINS, &mut PAGES.backings, &PACKAGES, &mut caller.memory.table, authority, image);
131 131
    let active = lifecycle::holdDomain(&mut CALLS, &mut DOMAINS, caller.initial, next);
277 277
    assert not lifecycle::reapNext(&mut DOMAINS, &mut PAGES, &mut WINDOWS, &mut IRQS);
278 278
    while PAGES.backings.pool.retiredCount > 0 {
279 279
        let reclaimed = frames::reclaim(&mut PAGES.backings.pool, 256);
280 280
        assert reclaimed > 0 and reclaimed <= 256;
281 281
    }
282 -
    assert not PAGES.backings.pool.free[run.first];
282 +
    assert not frames::available(&PAGES.backings.pool, run.first);
283 283
    assert PAGES.backings.records[allocation.index].exposed == 1;
284 284
    assert (try! pages::get(&PAGES, (try! capability::get(&parent.memory.table, shared)).object)).count == 1;
285 285
    assert try! events::flush(&mut DOMAINS.events, receiver, &mut parent.memory.ring) == 1;
286 286
    let event = try! events::pop(&mut parent.memory.ring) else panic "missing reaped child event";
287 287
    assert event.value == abi::id(target) and event.code == 19;
299 299
    while PAGES.backings.pool.retiredCount > 0 {
300 300
        let reclaimed = frames::reclaim(&mut PAGES.backings.pool, 256);
301 301
        assert reclaimed > 0 and reclaimed <= 256;
302 302
    }
303 303
    for i in 0..PAGES.backings.pool.count {
304 -
        assert PAGES.backings.pool.free[i];
304 +
        assert frames::available(&PAGES.backings.pool, i);
305 305
    }
306 306
}
307 307
308 308
/// Initial return reports to the current parent and retains its terminal identity.
309 309
@test unsafe fn terminalParent() throws (testing::TestError) {
496 496
    assert overlap;
497 497
    let reservedFrames = DOMAINS.contexts[index].kernelFrames;
498 498
    domains::contextCancel(&mut DOMAINS, &mut PAGES, reserved);
499 499
    assert DOMAINS.contextSlots[index].state == slots::State::Free;
500 500
    for i in reservedFrames.first..reservedFrames.first + reservedFrames.count {
501 -
        assert PAGES.backings.pool.free[i];
501 +
        assert frames::available(&PAGES.backings.pool, i);
502 502
    }
503 503
    let pendingContext = try! domains::contextReserve(&mut DOMAINS, &mut PAGES, &PACKAGES, &TABLE, handle, start);
504 504
    let stackBase = DOMAINS.contexts[index].kernelStack.start;
505 505
    let base = (&RAM[0]) as u64;
506 506
    let firstWord = ((stackBase - base) / 8) as u32;
580 580
    try domains::contextDestroy(&mut DOMAINS, &mut PAGES, &TABLE, handle, context) catch error {
581 581
        assert error == abi::Error::Busy; set retained = true;
582 582
    };
583 583
    assert retained and DOMAINS.contextSlots[context.index].state == slots::State::Live;
584 584
    for i in created.kernelFrames.first..created.kernelFrames.first + created.kernelFrames.count {
585 -
        assert not PAGES.backings.pool.free[i];
585 +
        assert not frames::available(&PAGES.backings.pool, i);
586 586
    }
587 587
    set DOMAINS.contexts[context.index].pending = nil;
588 588
    set DOMAINS.contexts[context.index].reservation = true;
589 589
    set retained = false;
590 590
    try domains::contextDestroy(&mut DOMAINS, &mut PAGES, &TABLE, handle, context) catch error {
592 592
    };
593 593
    assert retained and DOMAINS.contextSlots[context.index].state == slots::State::Live;
594 594
    set DOMAINS.contexts[context.index].reservation = false;
595 595
    try! domains::contextDestroy(&mut DOMAINS, &mut PAGES, &TABLE, handle, context);
596 596
    for i in created.kernelFrames.first..created.kernelFrames.first + created.kernelFrames.count {
597 -
        try testing::expect(PAGES.backings.pool.free[i]);
597 +
        assert frames::available(&PAGES.backings.pool, i);
598 598
    }
599 599
    try domains::context(&DOMAINS, authority.object, context) catch error {
600 600
        try testing::expect(error == abi::Error::BadHandle); set rejected += 1;
601 601
    };
602 602
    let next = try! domains::contextCreate(&mut DOMAINS, &mut PAGES, &PACKAGES, &TABLE, handle, start);
673 673
    domains::cancel(&mut DOMAINS, &mut BACKINGS, &mut TABLE, cancelled);
674 674
    assert DOMAINS.slots[1].state == slots::State::Free;
675 675
    assert DOMAINS.contextSlots[0].state == slots::State::Free;
676 676
    assert TABLE.slots[2].state == slots::State::Free;
677 677
    for i in 0..BACKINGS.pool.count {
678 -
        assert BACKINGS.pool.free[i];
678 +
        assert frames::available(&BACKINGS.pool, i);
679 679
    }
680 680
    set PACKAGES.entries[0].package.relocations = &[shared::Relocation {
681 681
        offset: 0, count: 1, target: shared::DataRef { slot: 5, offset: 0 },
682 682
    }];
683 683
    let mut invalid = false;
684 684
    try domains::create(&mut DOMAINS, &mut BACKINGS, &PACKAGES, &mut TABLE, abi::Handle(0), IMAGE) catch error {
685 685
        assert error == abi::Error::VerifyFailed; set invalid = true;
686 686
    };
687 687
    assert invalid and DOMAINS.slots[1].state == slots::State::Free;
688 688
    for i in 0..BACKINGS.pool.count {
689 -
        assert BACKINGS.pool.free[i];
689 +
        assert frames::available(&BACKINGS.pool, i);
690 690
    }
691 691
    initialize(abi::CREATE);
692 692
    let abandoned = try! domains::reserve(&mut DOMAINS, &mut BACKINGS, &PACKAGES, &mut TABLE, abi::Handle(0), IMAGE);
693 693
    try! domains::prepare(&PACKAGES, &abandoned);
694 694
    try! backing::endDomain(&mut BACKINGS, TABLE.owner);
697 697
        assert error == abi::Error::BadHandle; set dead = true;
698 698
    };
699 699
    assert dead and DOMAINS.slots[1].state == slots::State::Free;
700 700
    assert TABLE.slots[2].state == slots::State::Free;
701 701
    for i in 0..BACKINGS.pool.count {
702 -
        assert BACKINGS.pool.free[i];
702 +
        assert frames::available(&BACKINGS.pool, i);
703 703
    }
704 704
}
705 705
706 706
/// Creation installs only Events in the child and preserves selected authorizing rights.
707 707
@test unsafe fn pending() throws (testing::TestError) {
734 734
    try domains::create(&mut DOMAINS, &mut BACKINGS, &PACKAGES, &mut TABLE, abi::Handle(0), IMAGE) catch err {
735 735
        try testing::expect(err == expected); set rejected = true;
736 736
    };
737 737
    try testing::expect(rejected);
738 738
    for i in 0..BACKINGS.pool.count {
739 -
        try testing::expect(BACKINGS.pool.free[i]);
739 +
        assert frames::available(&BACKINGS.pool, i);
740 740
    }
741 741
    try testing::expect(DOMAINS.slots[1].state == slots::State::Free);
742 742
    try testing::expect(DOMAINS.contextSlots[0].state == slots::State::Free);
743 743
    try testing::expect(TABLE.slots[2].state == slots::State::Free);
744 744
    try testing::expect(BACKINGS.domains[1] == 0);
775 775
        try testing::expect(err == abi::Error::Exhausted); set rejected = true;
776 776
    };
777 777
    try testing::expect(rejected and DOMAINS.slots[1].state == slots::State::Free);
778 778
    try testing::expect(TABLE.slots[2].state == slots::State::Free);
779 779
    for i in 0..BACKINGS.pool.count {
780 -
        try testing::expect(BACKINGS.pool.free[i]);
780 +
        assert frames::available(&BACKINGS.pool, i);
781 781
    }
782 782
}
783 783
784 784
/// A creator can grant Create without Allocate and the next creation preserves that choice.
785 785
@test unsafe fn selectiveAuthority() throws (testing::TestError) {
831 831
    try domains::create(&mut DOMAINS, &mut BACKINGS, &PACKAGES, &mut TABLE, abi::Handle(0), IMAGE) catch err {
832 832
        try testing::expect(err == abi::Error::Exhausted); set failures += 1;
833 833
    };
834 834
    try testing::expect(DOMAINS.slots[1].state == slots::State::Free);
835 835
    for i in 0..BACKINGS.pool.count {
836 -
        try testing::expect(BACKINGS.pool.free[i]);
836 +
        assert frames::available(&BACKINGS.pool, i);
837 837
    }
838 838
    initialize(abi::CREATE);
839 839
    for i in 1..DOMAINS.slots.len {
840 840
        set DOMAINS.slots[i].state = slots::State::Retired;
841 841
    }
843 843
        try testing::expect(err == abi::Error::Exhausted); set failures += 1;
844 844
    };
845 845
    try testing::expect(failures == 2 and TABLE.slots[2].state == slots::State::Free);
846 846
    try testing::expect(DOMAINS.contextSlots[0].state == slots::State::Free);
847 847
    for i in 0..BACKINGS.pool.count {
848 -
        try testing::expect(BACKINGS.pool.free[i]);
848 +
        assert frames::available(&BACKINGS.pool, i);
849 849
    }
850 850
}
kernel/kernel/tests/frames.rad +81 -6
18 18
    set machine.reservedCount = 1;
19 19
    set machine.reserved[0] = range::Range { start: 0x80003000, end: 0x80005000 };
20 20
    try! frames::initialize(&mut POOL, &machine);
21 21
}
22 22
23 +
/// Bitmap allocation agrees with a scalar scan across holes and word boundaries.
24 +
@test unsafe fn bitmapRuns() throws (testing::TestError) {
25 +
    let mut machine: platform::Platform = undefined;
26 +
    set machine.ramCount = 1;
27 +
    set machine.ram[0] = range::Range { start: 0x80000000, end: 0x800c2000 };
28 +
    set machine.reservedCount = 1;
29 +
    set machine.reserved[0] = range::Range { start: 0x8001f000, end: 0x80020000 };
30 +
    for pattern in 0..5 {
31 +
        for count in [1 as u32, 2, 31, 32, 63, 64, 65, 127, 128, 129, 193, 194] {
32 +
            try! frames::initialize(&mut POOL, &machine);
33 +
            assert POOL.count == 193;
34 +
            for i in 0..POOL.count {
35 +
                if pattern > 0 and i % (pattern * 32) == 0 {
36 +
                    set POOL.free[i / 64] &= ~(1 << (i % 64) as u64);
37 +
                }
38 +
            }
39 +
            // Padding bits are outside the pool even when their values are set.
40 +
            set POOL.free[3] |= 0xfffffffffffffffe;
41 +
            let before = [POOL.free[0], POOL.free[1], POOL.free[2], POOL.free[3]];
42 +
            let mut expected: ?u32 = nil;
43 +
            let mut found: u32 = 0;
44 +
            for i in 0..POOL.count {
45 +
                if not frames::available(&POOL, i) {
46 +
                    set found = 0;
47 +
                    continue;
48 +
                }
49 +
                if found > 0 and POOL.addresses[i] <> POOL.addresses[i - 1] + 4096 {
50 +
                    set found = 0;
51 +
                }
52 +
                set found += 1;
53 +
                if found == count {
54 +
                    set expected = i + 1 - count;
55 +
                    break;
56 +
                }
57 +
            }
58 +
            let reservation = try frames::reserve(&mut POOL, count) catch error {
59 +
                assert expected == nil and error == abi::Error::OutOfMemory;
60 +
                for i in 0..4 {
61 +
                    assert POOL.free[i] == before[i];
62 +
                }
63 +
                continue;
64 +
            };
65 +
            let first = expected else panic "unexpected frame reservation";
66 +
            let run = frames::commit(reservation);
67 +
            assert run.first == first and run.count == count;
68 +
            for i in 0..POOL.count {
69 +
                if i >= first and i - first < count {
70 +
                    assert not frames::available(&POOL, i);
71 +
                }
72 +
                else {
73 +
                    assert frames::available(&POOL, i) == ((before[i / 64] & (1 << (i % 64) as u64)) <> 0);
74 +
                }
75 +
            }
76 +
            try! frames::release(&mut POOL, run);
77 +
            for i in 0..4 {
78 +
                assert POOL.free[i] == before[i];
79 +
            }
80 +
        }
81 +
    }
82 +
    assert not frames::available(&POOL, POOL.count);
83 +
    try! frames::initialize(&mut POOL, &machine);
84 +
    let run = frames::commit(try! frames::reserve(&mut POOL, 128));
85 +
    assert run.first == 31;
86 +
    let before = [POOL.free[0], POOL.free[1], POOL.free[2], POOL.free[3]];
87 +
    let mut invalid = false;
88 +
    try frames::release(&mut POOL, frames::Run { first: run.first, count: 129 }) catch error {
89 +
        assert error == abi::Error::BadHandle; set invalid = true;
90 +
    };
91 +
    assert invalid;
92 +
    for i in 0..4 {
93 +
        assert POOL.free[i] == before[i];
94 +
    }
95 +
    try! frames::release(&mut POOL, run);
96 +
}
97 +
23 98
/// Retired frames stay unavailable until bounded reclamation returns them.
24 99
@test unsafe fn retirement() throws (testing::TestError) {
25 100
    initialize();
26 101
    set POOL.retiredHead = limits::FRAMES - 1;
27 102
    let left = frames::commit(try! frames::reserve(&mut POOL, 3));
28 103
    let right = frames::commit(try! frames::reserve(&mut POOL, 5));
29 104
    try! frames::retire(&mut POOL, left);
30 105
    try! frames::retire(&mut POOL, right);
31 106
    assert POOL.retiredCount == 2;
32 107
    for i in 0..8 {
33 -
        assert not POOL.free[i];
108 +
        assert not frames::available(&POOL, i);
34 109
    }
35 110
    assert frames::reclaim(&mut POOL, 0) == 0;
36 111
    assert frames::reclaim(&mut POOL, 2) == 2;
37 -
    assert POOL.free[0] and POOL.free[1] and not POOL.free[2];
112 +
    assert frames::available(&POOL, 0) and frames::available(&POOL, 1) and not frames::available(&POOL, 2);
38 113
    let reused = frames::commit(try! frames::reserve(&mut POOL, 2));
39 114
    assert reused.first == 0;
40 115
    assert frames::reclaim(&mut POOL, 3) == 3;
41 -
    assert POOL.retiredCount == 1 and not POOL.free[5];
42 -
    assert not POOL.free[0] and not POOL.free[1];
116 +
    assert POOL.retiredCount == 1 and not frames::available(&POOL, 5);
117 +
    assert not frames::available(&POOL, 0) and not frames::available(&POOL, 1);
43 118
    try! frames::retire(&mut POOL, reused);
44 119
    assert frames::reclaim(&mut POOL, 20) == 5;
45 120
    assert POOL.retiredCount == 0;
46 121
    for i in 0..8 {
47 -
        assert POOL.free[i];
122 +
        assert frames::available(&POOL, i);
48 123
    }
49 124
    assert frames::reclaim(&mut POOL, 20) == 0;
50 125
}
51 126
52 127
/// Adjacent physical frames form runs; a reserved hole cannot be crossed.
193 268
    try! frames::initialize(&mut POOL, &machine);
194 269
    let mut count: u32 = 0;
195 270
    for i in 0..96 {
196 271
        let start = 0x80000000 + i as u64 * 4096;
197 272
        if platform::available(&machine, range::Range { start, end: start + 4096 }) {
198 -
            try testing::expect(count < POOL.count and POOL.addresses[count] == start and POOL.free[count]);
273 +
            assert count < POOL.count and POOL.addresses[count] == start and frames::available(&POOL, count);
199 274
            set count += 1;
200 275
        }
201 276
    }
202 277
    try testing::expect(count == POOL.count);
203 278
}
kernel/kernel/tests/instances.rad +4 -4
21 21
    let base = pointer as u64;
22 22
    let aligned = (base + 4095) & ~4095;
23 23
    set POOL.count = (@sizeOf([u64; 131072]) - (aligned - base) as u32) / 4096;
24 24
    for i in 0..POOL.count {
25 25
        set POOL.addresses[i] = aligned + i as u64 * 4096;
26 -
        set POOL.free[i] = true;
26 +
        set POOL.free[i / 64] |= 1 << (i % 64) as u64;
27 27
    }
28 28
    registry::initialize(&mut PACKAGES);
29 29
}
30 30
31 31
/// Publish a package with one private word and optional pointer relocation.
67 67
    try! frames::release(&mut POOL, first.frames);
68 68
    try! frames::release(&mut POOL, other.frames);
69 69
    let cancelled = try! instances::reserve(&PACKAGES, &mut POOL, root);
70 70
    instances::cancel(&mut POOL, cancelled);
71 71
    for i in 0..POOL.count {
72 -
        assert POOL.free[i];
72 +
        assert frames::available(&POOL, i);
73 73
    }
74 74
}
75 75
76 76
/// A diamond has one dependency instance per domain and independent relocated pointers.
77 77
@test unsafe fn diamond() throws (testing::TestError) {
93 93
    try testing::expect(*word(second.table[base.index]) == 7);
94 94
    try testing::expect(first.table[4] == 0 and second.table[4] == 0);
95 95
    try! frames::release(&mut POOL, first.frames);
96 96
    try! frames::release(&mut POOL, second.frames);
97 97
    for i in 0..POOL.count {
98 -
        try testing::expect(POOL.free[i]);
98 +
        assert frames::available(&POOL, i);
99 99
    }
100 100
}
101 101
102 102
/// State alignment can exceed a physical frame and zero-fill follows the template.
103 103
@test unsafe fn alignment() throws (testing::TestError) {
130 130
    try instances::create(&PACKAGES, &mut POOL, abi::Ref { index: root.index, generation: 2 }) catch err {
131 131
        try testing::expect(err == abi::Error::BadHandle); set failures += 1;
132 132
    };
133 133
    try testing::expect(failures == 3);
134 134
    for i in 0..POOL.count {
135 -
        try testing::expect(POOL.free[i]);
135 +
        assert frames::available(&POOL, i);
136 136
    }
137 137
    let retry = try! instances::create(&PACKAGES, &mut POOL, root);
138 138
    try testing::expect(*word(retry.table[root.index]) == 7);
139 139
    try! frames::release(&mut POOL, retry.frames);
140 140
}
kernel/kernel/tests/lifecycle.rad +11 -11
80 80
    });
81 81
    let base = ((&RAM[0]) as u64 + 4095) & ~4095;
82 82
    set MEMORY.backings.pool.count = 2;
83 83
    for i in 0..2 {
84 84
        set MEMORY.backings.pool.addresses[i] = base + i as u64 * 4096;
85 -
        set MEMORY.backings.pool.free[i] = true;
85 +
        set MEMORY.backings.pool.free[i / 64] |= 1 << (i % 64) as u64;
86 86
    }
87 87
    let reservation = try! pages::reserve(&mut MEMORY, &mut FIRST.table, authority, 2);
88 88
    let held = lifecycle::holdPage(&mut CALLS, &mut STORE, owner, reservation);
89 89
    assert STORE.contexts[0].reservation;
90 90
    lifecycle::clearPage(&CALLS, &held);
91 91
    lifecycle::cancelAllocations(&mut CALLS, &mut STORE, &mut MEMORY, &mut loader::STATE, &mut registry::STORE);
92 -
    assert not MEMORY.backings.pool.free[0] and not MEMORY.backings.pool.free[1];
92 +
    assert not frames::available(&MEMORY.backings.pool, 0) and not frames::available(&MEMORY.backings.pool, 1);
93 93
    set STORE.records[0].state = domains::Lifecycle::Dead;
94 94
    set STORE.contexts[0].state = domains::ContextState::Stopped;
95 95
    set STORE.contexts[0].hart = 0;
96 96
    lifecycle::cancelAllocations(&mut CALLS, &mut STORE, &mut MEMORY, &mut loader::STATE, &mut registry::STORE);
97 -
    assert not MEMORY.backings.pool.free[0];
97 +
    assert not frames::available(&MEMORY.backings.pool, 0);
98 98
    // Model the end of the stopped continuation before hart release.
99 99
    match held {
100 100
        case lifecycle::Lease::Held(context) => {
101 101
            assert context == owner;
102 102
        },
103 103
    }
104 104
    set STORE.contexts[0].hart = nil;
105 105
    lifecycle::cancelAllocations(&mut CALLS, &mut STORE, &mut MEMORY, &mut loader::STATE, &mut registry::STORE);
106 -
    assert MEMORY.backings.pool.retiredCount == 1 and not MEMORY.backings.pool.free[0];
106 +
    assert MEMORY.backings.pool.retiredCount == 1 and not frames::available(&MEMORY.backings.pool, 0);
107 107
    assert frames::reclaim(&mut MEMORY.backings.pool, 256) == 2;
108 -
    assert MEMORY.backings.pool.free[0] and MEMORY.backings.pool.free[1];
108 +
    assert frames::available(&MEMORY.backings.pool, 0) and frames::available(&MEMORY.backings.pool, 1);
109 109
    assert MEMORY.slots[0].state == slots::State::Free;
110 110
    assert MEMORY.backings.slots[0].state == slots::State::Free;
111 111
    assert FIRST.table.slots[1].state == slots::State::Free and CALLS.active[0] == 0;
112 112
    assert not STORE.contexts[0].reservation;
113 113
    set STORE.records[0].state = domains::Lifecycle::Active;
137 137
    });
138 138
    let base = ((&RAM[0]) as u64 + 4095) & ~4095;
139 139
    set MEMORY.backings.pool.count = 2;
140 140
    for i in 0..2 {
141 141
        set MEMORY.backings.pool.addresses[i] = base + i as u64 * 4096;
142 -
        set MEMORY.backings.pool.free[i] = true;
142 +
        set MEMORY.backings.pool.free[i / 64] |= 1 << (i % 64) as u64;
143 143
    }
144 144
    let firstPage = try! pages::reserve(&mut MEMORY, &mut FIRST.table, authority, 1);
145 145
    let first = lifecycle::holdPage(&mut CALLS, &mut STORE, owner, firstPage);
146 146
    let secondPage = try! pages::reserve(&mut MEMORY, &mut FIRST.table, authority, 1);
147 147
    let other = lifecycle::holdPage(&mut CALLS, &mut STORE, second, secondPage);
162 162
    assert CALLS.active[0] == 2 and MEMORY.backings.pool.retiredCount == 1;
163 163
    assert not STORE.contexts[0].reservation and STORE.contexts[1].reservation;
164 164
    assert lifecycle::cancelAllocations(&mut CALLS, &mut STORE, &mut MEMORY, &mut loader::STATE, &mut registry::STORE);
165 165
    assert CALLS.active[0] == 0 and MEMORY.backings.pool.retiredCount == 2;
166 166
    assert not lifecycle::cancelAllocations(&mut CALLS, &mut STORE, &mut MEMORY, &mut loader::STATE, &mut registry::STORE);
167 -
    assert not MEMORY.backings.pool.free[0] and not MEMORY.backings.pool.free[1];
167 +
    assert not frames::available(&MEMORY.backings.pool, 0) and not frames::available(&MEMORY.backings.pool, 1);
168 168
    assert frames::reclaim(&mut MEMORY.backings.pool, 1) == 1;
169 169
    assert frames::reclaim(&mut MEMORY.backings.pool, 1) == 1;
170 170
    assert MEMORY.backings.pool.retiredCount == 0;
171 171
}
172 172
178 178
    let caller = abi::Ref { index: 0, generation: 1 };
179 179
    let target = abi::Ref { index: 1, generation: 1 };
180 180
    let base = ((&RAM[0]) as u64 + 4095) & ~4095;
181 181
    set MEMORY.backings.pool.count = 1;
182 182
    set MEMORY.backings.pool.addresses[0] = base;
183 -
    set MEMORY.backings.pool.free[0] = true;
183 +
    set MEMORY.backings.pool.free[0] |= 1;
184 184
    let slot = try! slots::reserve(&mut STORE.contextSlots[..]);
185 185
    let object = slots::reference(&slot);
186 186
    let run = frames::commit(try! frames::reserve(&mut MEMORY.backings.pool, 1));
187 187
    set STORE.contexts[object.index].owner = target;
188 188
    set STORE.contexts[object.index].kernelFrames = run;
197 197
    });
198 198
    let lease = lifecycle::holdContext(&mut CALLS, &mut STORE, caller, pending);
199 199
    lifecycle::clearContext(&CALLS, &lease);
200 200
    set STORE.records[1].state = domains::Lifecycle::Dead;
201 201
    lifecycle::cancelAllocations(&mut CALLS, &mut STORE, &mut MEMORY, &mut loader::STATE, &mut registry::STORE);
202 -
    assert STORE.contexts[0].reservation and not MEMORY.backings.pool.free[0];
202 +
    assert STORE.contexts[0].reservation and not frames::available(&MEMORY.backings.pool, 0);
203 203
    set STORE.records[0].state = domains::Lifecycle::Dead;
204 204
    set STORE.contexts[0].state = domains::ContextState::Stopped;
205 205
    set STORE.contexts[0].hart = 1;
206 206
    lifecycle::cancelAllocations(&mut CALLS, &mut STORE, &mut MEMORY, &mut loader::STATE, &mut registry::STORE);
207 207
    assert STORE.contextSlots[object.index].state == slots::State::Reserved;
210 210
            assert id == caller;
211 211
        },
212 212
    }
213 213
    set STORE.contexts[0].hart = nil;
214 214
    lifecycle::cancelAllocations(&mut CALLS, &mut STORE, &mut MEMORY, &mut loader::STATE, &mut registry::STORE);
215 -
    assert MEMORY.backings.pool.retiredCount == 1 and not MEMORY.backings.pool.free[0];
215 +
    assert MEMORY.backings.pool.retiredCount == 1 and not frames::available(&MEMORY.backings.pool, 0);
216 216
    assert frames::reclaim(&mut MEMORY.backings.pool, 256) == 1;
217 -
    assert MEMORY.backings.pool.free[0];
217 +
    assert frames::available(&MEMORY.backings.pool, 0);
218 218
    assert STORE.contextSlots[object.index].state == slots::State::Free;
219 219
    assert not STORE.contexts[0].reservation and CALLS.active[0] == 0;
220 220
}
221 221
222 222
/// Dead contexts retain requests until hart release and completion of taken work.
kernel/kernel/tests/loader.rad +2 -2
50 50
    let aligned = (base + 4095) & ~4095;
51 51
    set OFFSET = (aligned - base) as u32;
52 52
    set PAGES.backings.pool.count = (@sizeOf([u64; 18874368]) - OFFSET) / 4096;
53 53
    for i in 0..PAGES.backings.pool.count {
54 54
        set PAGES.backings.pool.addresses[i] = aligned + i as u64 * 4096;
55 -
        set PAGES.backings.pool.free[i] = true;
55 +
        set PAGES.backings.pool.free[i / 64] |= 1 << (i % 64) as u64;
56 56
    }
57 57
    let owner = abi::Ref { index: 0, generation: 1 };
58 58
    capability::initialize(&mut TABLE, owner);
59 59
    try! backing::registerDomain(&mut PAGES.backings, owner);
60 60
    let authority = try! capability::install(&mut TABLE, capability::Entry {
91 91
92 92
/// Count available frames to detect leaked temporary or unpublished output storage.
93 93
unsafe fn free() -> u32 {
94 94
    let mut count: u32 = 0;
95 95
    for i in 0..PAGES.backings.pool.count {
96 -
        if PAGES.backings.pool.free[i] {
96 +
        if frames::available(&PAGES.backings.pool, i) {
97 97
            set count += 1;
98 98
        }
99 99
    }
100 100
    return count;
101 101
}
kernel/kernel/tests/pages.rad +16 -15
1 1
//! Page allocation, zeroing, split atomicity, and retained-pointer lifetimes.
2 2
3 3
use std::testing;
4 4
use kernel::abi;
5 +
use kernel::frames;
5 6
use kernel::capability;
6 7
use kernel::pages;
7 8
use kernel::backing;
8 9
use kernel::slots;
9 10
use kernel::limits;
31 32
    slots::initialize(&mut STORE.slots[..]);
32 33
    backing::initialize(&mut STORE.backings);
33 34
    set STORE.backings.pool.count = 8;
34 35
    for i in 0..8 {
35 36
        set STORE.backings.pool.addresses[i] = aligned + i as u64 * 4096;
36 -
        set STORE.backings.pool.free[i] = true;
37 +
        set STORE.backings.pool.free[i / 64] |= 1 << (i % 64) as u64;
37 38
    }
38 39
    let owner = abi::Ref { index: 0, generation: 1 };
39 40
    capability::initialize(&mut TABLE, owner);
40 41
    try! backing::registerDomain(&mut STORE.backings, owner);
41 42
    let authority = try! capability::install(&mut TABLE, capability::Entry {
52 53
@test unsafe fn stagedAllocation() throws (testing::TestError) {
53 54
    initialize();
54 55
    let pending = try! pages::reserve(&mut STORE, &mut TABLE, abi::Handle(0), 2);
55 56
    assert STORE.slots[0].state == slots::State::Reserved;
56 57
    assert TABLE.slots[1].state == slots::State::Reserved;
57 -
    assert not STORE.backings.pool.free[0] and not STORE.backings.pool.free[1];
58 +
    assert not frames::available(&STORE.backings.pool, 0) and not frames::available(&STORE.backings.pool, 1);
58 59
    assert RAM[OFFSET] == 0xa5;
59 60
    let other = try! pages::allocate(&mut STORE, &mut TABLE, abi::Handle(0), 6);
60 61
    set RAM[OFFSET + 8192] = 0x7b;
61 62
    pages::clear(&pending);
62 63
    for i in OFFSET..OFFSET + 8192 {
68 69
    let page = try! pages::get(&STORE, entry.object);
69 70
    assert page.count == 2 and page.origin == TABLE.owner;
70 71
    drop(handle); drop(other);
71 72
    try! backing::endDomain(&mut STORE.backings, TABLE.owner);
72 73
    for i in 0..8 {
73 -
        assert STORE.backings.pool.free[i];
74 +
        assert frames::available(&STORE.backings.pool, i);
74 75
    }
75 76
}
76 77
77 78
/// Cancelling unpublished storage returns all capacity without exposing its bytes.
78 79
@test unsafe fn cancelAllocation() throws (testing::TestError) {
81 82
    pages::cancel(&mut STORE, &mut TABLE, pending);
82 83
    assert STORE.slots[0].state == slots::State::Free;
83 84
    assert STORE.backings.slots[0].state == slots::State::Free;
84 85
    assert TABLE.slots[1].state == slots::State::Free;
85 86
    for i in 0..8 {
86 -
        assert STORE.backings.pool.free[i];
87 +
        assert frames::available(&STORE.backings.pool, i);
87 88
    }
88 89
    assert RAM[OFFSET] == 0xa5;
89 90
    let full = try! pages::allocate(&mut STORE, &mut TABLE, abi::Handle(0), 8);
90 91
    let entry = try! capability::get(&TABLE, full);
91 92
    assert (try! pages::get(&STORE, entry.object)).count == 8;
104 105
    try testing::expect(page.handles == 1 and STORE.backings.records[page.backing.index].exposed == 2);
105 106
    let actual = try! capability::get(&TARGET, moved);
106 107
    try testing::expect(*actual.rights == abi::READ);
107 108
    try! pages::drop(&mut STORE, &mut TARGET, moved);
108 109
    try! backing::endDomain(&mut STORE.backings, receiver);
109 -
    try testing::expect(not STORE.backings.pool.free[0]);
110 +
    assert not frames::available(&STORE.backings.pool, 0);
110 111
    try! backing::endDomain(&mut STORE.backings, TABLE.owner);
111 -
    try testing::expect(STORE.backings.pool.free[0]);
112 +
    assert frames::available(&STORE.backings.pool, 0);
112 113
}
113 114
114 115
/// Grants acquire references only after capacity and receiver checks succeed.
115 116
@test unsafe fn grantRollback() throws (testing::TestError) {
116 117
    initialize();
152 153
    try testing::expect(STORE.records[entry.object.index].handles == 2);
153 154
    try testing::expect(STORE.backings.records[page.backing.index].exposed == 2);
154 155
    drop(original);
155 156
    try! pages::drop(&mut STORE, &mut TARGET, granted);
156 157
    try! backing::endDomain(&mut STORE.backings, TABLE.owner);
157 -
    try testing::expect(not STORE.backings.pool.free[0]);
158 +
    assert not frames::available(&STORE.backings.pool, 0);
158 159
    try! backing::endDomain(&mut STORE.backings, receiver);
159 -
    try testing::expect(STORE.backings.pool.free[0]);
160 +
    assert frames::available(&STORE.backings.pool, 0);
160 161
}
161 162
162 163
/// Self-attenuation adds one reference, preserves authority, and prevents splitting.
163 164
@test unsafe fn selfAttenuation() throws (testing::TestError) {
164 165
    initialize();
171 172
    try testing::expect(*none.rights == 0);
172 173
    drop(narrowed); drop(copy);
173 174
    let right = try! pages::split(&mut STORE, &mut TABLE, original, 1);
174 175
    drop(original); drop(right);
175 176
    try! backing::endDomain(&mut STORE.backings, TABLE.owner);
176 -
    try testing::expect(STORE.backings.pool.free[0] and STORE.backings.pool.free[1]);
177 +
    assert frames::available(&STORE.backings.pool, 0) and frames::available(&STORE.backings.pool, 1);
177 178
}
178 179
179 180
/// New allocations are zeroed completely and carry the documented default rights.
180 181
@test unsafe fn allocation() throws (testing::TestError) {
181 182
    initialize();
196 197
    for count in &[65537 as u64, 0xffffffffffffffff] {
197 198
        let mut exhausted = false;
198 199
        try pages::allocate(&mut STORE, &mut TABLE, abi::Handle(0), count) catch err {
199 200
            try testing::expect(err == abi::Error::OutOfMemory); set exhausted = true;
200 201
        };
201 -
        try testing::expect(exhausted and STORE.backings.pool.free[2]);
202 +
        assert exhausted and frames::available(&STORE.backings.pool, 2);
202 203
    }
203 204
}
204 205
205 206
/// Splitting preserves contents and backing identity while changing only page extents.
206 207
@test unsafe fn split() throws (testing::TestError) {
242 243
    try! pages::retain(&mut STORE, entry.object, receiver);
243 244
    set RAM[OFFSET] = 99;
244 245
    drop(handle);
245 246
    try! pages::release(&mut STORE, entry.object);
246 247
    try! backing::endDomain(&mut STORE.backings, TABLE.owner);
247 -
    try testing::expect(not STORE.backings.pool.free[0] and RAM[OFFSET] == 99);
248 +
    assert not frames::available(&STORE.backings.pool, 0) and RAM[OFFSET] == 99;
248 249
    try! backing::endDomain(&mut STORE.backings, receiver);
249 -
    try testing::expect(STORE.backings.pool.free[0]);
250 +
    assert frames::available(&STORE.backings.pool, 0);
250 251
    let owner = abi::Ref { index: 0, generation: 2 };
251 252
    capability::initialize(&mut TABLE, owner);
252 253
    try! backing::registerDomain(&mut STORE.backings, owner);
253 254
    let authority = try! capability::install(&mut TABLE, capability::Entry {
254 255
        kind: abi::Kind::Domain, object: owner, rights: abi::Rights(abi::ALLOCATE),
274 275
    };
275 276
    try pages::split(&mut STORE, &mut TABLE, handle, 2) catch err {
276 277
        try testing::expect(err == abi::Error::Exhausted); set failed += 1;
277 278
    };
278 279
    let page = try! pages::get(&STORE, entry.object);
279 -
    try testing::expect(failed == 2 and page.count == 4 and STORE.backings.pool.free[4]);
280 +
    assert failed == 2 and page.count == 4 and frames::available(&STORE.backings.pool, 4);
280 281
}
281 282
282 283
/// Failure at each storage stage releases all earlier reservations.
283 284
@test unsafe fn rollback() throws (testing::TestError) {
284 285
    initialize();
302 303
        }
303 304
        let mut exhausted = false;
304 305
        try pages::allocate(&mut STORE, &mut TABLE, abi::Handle(0), 1) catch err {
305 306
            try testing::expect(err == abi::Error::Exhausted); set exhausted = true;
306 307
        };
307 -
        try testing::expect(exhausted and TABLE.slots[1].state == slots::State::Free and STORE.backings.pool.free[0]);
308 +
        assert exhausted and TABLE.slots[1].state == slots::State::Free and frames::available(&STORE.backings.pool, 0);
308 309
        if stage == 0 {
309 310
            try testing::expect(STORE.slots[0].state == slots::State::Free);
310 311
        }
311 312
    }
312 313
}
319 320
    });
320 321
    let mut denied = false;
321 322
    try pages::allocate(&mut STORE, &mut TABLE, other, 1) catch err {
322 323
        try testing::expect(err == abi::Error::Denied); set denied = true;
323 324
    };
324 -
    try testing::expect(denied and STORE.backings.pool.free[0] and TABLE.slots[2].state == slots::State::Free);
325 +
    assert denied and frames::available(&STORE.backings.pool, 0) and TABLE.slots[2].state == slots::State::Free;
325 326
}
326 327
327 328
/// Buffer authority covers complete ranges and rejects overflow and stale objects.
328 329
@test unsafe fn bufferAuthority() throws (testing::TestError) {
329 330
    initialize();
test/runtime/kernel/dispatchcheck.rad added +249 -0
1 +
//! Runtime compiler ownership across scheduled windows and domain destruction.
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::calls;
17 +
use kernel::lifecycle;
18 +
use kernel::remote;
19 +
use kernel::dispatchinput;
20 +
21 +
/// Bootstrap resource capabilities.
22 +
unsafe static TABLE: capability::Table = undefined;
23 +
/// First loader, retry loader, witness, and controller domain handles.
24 +
unsafe static HANDLES: [abi::Handle; 4] = undefined;
25 +
/// Generation-bearing domain identities for each actor.
26 +
unsafe static ACTORS: [abi::Ref; 4] = undefined;
27 +
/// Initial context identities for each actor.
28 +
unsafe static CONTEXTS: [abi::Ref; 4] = undefined;
29 +
/// Controller capability for the first loader.
30 +
unsafe static VICTIM: abi::Handle = undefined;
31 +
/// Controller capability for the binary input page.
32 +
unsafe static SOURCE: abi::Handle = undefined;
33 +
/// Shared user progress and published package identity.
34 +
unsafe static PROGRESS: *mut u64 = undefined;
35 +
/// Start of the controller's final execution window.
36 +
static FINAL: u64 = 0;
37 +
/// Write one diagnostic byte to the fixture UART.
38 +
fn put(byte: u8);
39 +
/// Write a fixed-width hexadecimal diagnostic value.
40 +
fn number(value: u64) {
41 +
    let digits = "0123456789abcdef";
42 +
    for i in 0..16 {
43 +
        put(digits[((value >> ((15 - i) as u64 * 4)) & 15) as u32]);
44 +
    }
45 +
}
46 +
47 +
/// Map a validated physical page.
48 +
fn memory(address: u64) -> *mut u8;
49 +
/// Current kernel package-state base.
50 +
fn kernelGp() -> u64;
51 +
/// Native controller entry address.
52 +
fn completion() -> u64;
53 +
/// Execute published code with one private package-state table.
54 +
fn invoke(address: u64, table: u64) -> i64;
55 +
/// Signal native fixture completion.
56 +
fn finish();
57 +
58 +
/// Copy trusted package bytes into a bootstrap-owned page allocation.
59 +
unsafe fn input(authority: abi::Handle, bytes: &[u8]) -> abi::Handle {
60 +
    let handle = try! pages::allocate(&mut pages::STORE, &mut TABLE, authority, (bytes.len as u64 + 4095) / 4096);
61 +
    let page = try! pages::get(&pages::STORE, (try! capability::get(&TABLE, handle)).object);
62 +
    let copied = try! mem::copy(@sliceOf(memory(page.base), page.count * 4096), bytes);
63 +
    assert copied == bytes.len;
64 +
    return handle;
65 +
}
66 +
67 +
/// Install user actors and disjoint windows that interrupt two compiler calls.
68 +
export unsafe fn setup() {
69 +
    let pending = try! slots::reserve(&mut domains::STORE.slots[..]);
70 +
    let owner = try! slots::commit(&mut domains::STORE.slots[..], pending);
71 +
    capability::initialize(&mut TABLE, owner);
72 +
    try! backing::registerDomain(&mut pages::STORE.backings, owner);
73 +
    let authority = try! capability::install(&mut TABLE, capability::Entry {
74 +
        kind: abi::Kind::Domain, object: owner, rights: abi::Rights(abi::CREATE | abi::ALLOCATE),
75 +
    });
76 +
    let source = input(authority, &dispatchinput::INPUT[..]);
77 +
    let image = try! loader::load(&mut loader::STATE, &mut pages::STORE, &mut registry::STORE, &mut TABLE,
78 +
        loader::Request { authority, source, offset: 0, length: dispatchinput::INPUT.len as u64 });
79 +
    let payload = input(authority, &dispatchinput::PAYLOAD[..]);
80 +
    let progress = try! pages::allocate(&mut pages::STORE, &mut TABLE, authority, 1);
81 +
    let shared = try! pages::get(&pages::STORE, (try! capability::get(&TABLE, progress)).object);
82 +
    set PROGRESS = memory(shared.base) as *mut u64;
83 +
    for i in 0..4 {
84 +
        let handle = try! domains::create(&mut domains::STORE, &mut pages::STORE.backings, &registry::STORE, &mut TABLE, authority, image);
85 +
        let object = (try! capability::get(&TABLE, handle)).object;
86 +
        let mut child = try! domains::get(&domains::STORE, object);
87 +
        let mut rights = abi::ALLOCATE;
88 +
        if i == 3 {
89 +
            set rights |= abi::CREATE;
90 +
        }
91 +
        let self = try! capability::install(&mut child.memory.table, capability::Entry {
92 +
            kind: abi::Kind::Domain, object, rights: abi::Rights(rights),
93 +
        });
94 +
        set HANDLES[i] = handle; set ACTORS[i] = object; set CONTEXTS[i] = child.initial;
95 +
        let readable = try! pages::grant(&mut pages::STORE, &TABLE, &mut child.memory.table, payload, abi::READ as u64);
96 +
        let writable = try! pages::grant(&mut pages::STORE, &TABLE, &mut child.memory.table, progress, (abi::READ | abi::WRITE) as u64);
97 +
        if i == 3 {
98 +
            set SOURCE = readable;
99 +
            set VICTIM = try! capability::install(&mut child.memory.table, capability::Entry {
100 +
                kind: abi::Kind::Domain, object: ACTORS[0], rights: abi::Rights(abi::DESTROY),
101 +
            });
102 +
            set domains::STORE.records[object.index].state = domains::Lifecycle::Active;
103 +
            let frame = &mut domains::STORE.contexts[child.initial.index].frame;
104 +
            set frame.pc = completion(); set frame.status = 0x1880;
105 +
            set frame.registers[2] = domains::STORE.contexts[child.initial.index].kernelStack.end;
106 +
            set frame.registers[3] = kernelGp();
107 +
            continue;
108 +
        }
109 +
        let stack = try! pages::allocate(&mut pages::STORE, &mut TABLE, authority, 1);
110 +
        let granted = try! pages::grant(&mut pages::STORE, &TABLE, &mut child.memory.table, stack, (abi::READ | abi::WRITE) as u64);
111 +
        let storage = try! pages::get(&pages::STORE, (try! capability::get(&TABLE, stack)).object);
112 +
        let args = @sliceOf(memory(storage.base) as *mut u64, 3);
113 +
        set args[0] = *readable; set args[1] = dispatchinput::PAYLOAD.len as u64; set args[2] = shared.base;
114 +
        try! domains::activate(&mut domains::STORE, &pages::STORE, &TABLE, handle, storage.base + 4096, storage.base, i as u64);
115 +
    }
116 +
    for byte in "runtime actors ready\n" {
117 +
        put(byte);
118 +
    }
119 +
    let now = dispatch::clock();
120 +
    let start = now + 1000000;
121 +
    set FINAL = start + 300000000;
122 +
    let mut remaining = try! budgets::seed(&mut budgets::STORE, &mut TABLE, 0, start, FINAL + 10000000);
123 +
    let actors = [0 as u32, 3, 1, 2, 1];
124 +
    let ends = [10000000 as u64, 50000000, 60000000, 62000000, 300000000];
125 +
    for i in 0..5 {
126 +
        let next = try! budgets::split(&mut budgets::STORE, &mut TABLE, remaining, start + ends[i], now);
127 +
        let actor = actors[i];
128 +
        let bound = try! budgets::bind(&mut budgets::STORE, &domains::STORE, &mut TABLE,
129 +
            budgets::Binding { budget: remaining, domain: HANDLES[actor], context: CONTEXTS[actor] }, now);
130 +
        set remaining = next;
131 +
    }
132 +
    let bound = try! budgets::bind(&mut budgets::STORE, &domains::STORE, &mut TABLE,
133 +
        budgets::Binding { budget: remaining, domain: HANDLES[3], context: CONTEXTS[3] }, now);
134 +
}
135 +
136 +
/// Verify cancellation, competing admission, progress, and resident code reuse.
137 +
export unsafe fn verify() {
138 +
    for byte in "runtime admission metadata instructions: " {
139 +
        put(byte);
140 +
    }
141 +
    number(sync::maximum()); put(10);
142 +
    let progress = @sliceOf(PROGRESS, 4);
143 +
    let guard = sync::enter();
144 +
    let first = CONTEXTS[0];
145 +
    for byte in "runtime first progress and owner: " {
146 +
        put(byte);
147 +
    }
148 +
    number(progress[0]); put(32); number(abi::id(loader::STATE.owner)); put(10);
149 +
    assert progress[0] == 1 and loader::STATE.busy and loader::STATE.owner == ACTORS[0];
150 +
    assert domains::STORE.contexts[first.index].reservation;
151 +
    assert (domains::STORE.contexts[first.index].frame.status & 0x1800) == 0x1800;
152 +
    let allocation = lifecycle::CALLS.records[first.index].allocation;
153 +
    let mut loading: lifecycle::Loading = undefined;
154 +
    match allocation {
155 +
        case lifecycle::Allocation::Load(value) => {
156 +
            set loading = value;
157 +
        },
158 +
        else => panic "loader reservation required",
159 +
    }
160 +
    let output = loading.output else panic "compilation output required";
161 +
    sync::leave(guard);
162 +
    let mut busy = false;
163 +
    try calls::synchronized(ACTORS[3], 51, &[0, *SOURCE, 0, dispatchinput::PAYLOAD.len as u64], dispatch::clock()) catch error {
164 +
        assert error == abi::Error::Busy; set busy = true;
165 +
    };
166 +
    assert busy;
167 +
    for byte in "runtime competing load rejected\n" {
168 +
        put(byte);
169 +
    }
170 +
    let destroyed = try! calls::synchronized(ACTORS[3], 22, &[*VICTIM, 0, 0, 0], dispatch::clock());
171 +
    assert destroyed == 0;
172 +
    let checking = sync::enter();
173 +
    let request = try! dispatch::request(0, remote::Action::Reschedule);
174 +
    sync::leave(checking);
175 +
    let result = try! dispatch::awaitRequest(0, request);
176 +
    assert result == 0;
177 +
    let cancelled = sync::enter();
178 +
    assert not loader::STATE.busy;
179 +
    assert (lifecycle::CALLS.active[first.index / 32] & (1 << (first.index % 32))) == 0;
180 +
    assert registry::STORE.slots[output.object.index].state == slots::State::Free;
181 +
    sync::leave(cancelled);
182 +
    let mut reclaimed = false;
183 +
    for attempt in 0..10000 {
184 +
        let guard = sync::enter();
185 +
        set reclaimed = pages::STORE.backings.pool.retiredCount == 0;
186 +
        if reclaimed {
187 +
            sync::leave(guard);
188 +
            break;
189 +
        }
190 +
        let request = try! dispatch::request(0, remote::Action::Reschedule);
191 +
        sync::leave(guard);
192 +
        let result = try! dispatch::awaitRequest(0, request);
193 +
        assert result == 0;
194 +
    }
195 +
    assert reclaimed;
196 +
    for run in [loading.input.frames, output.code, output.metadata] {
197 +
        let mut offset: u32 = 0;
198 +
        while offset < run.count {
199 +
            let mut count = run.count - offset;
200 +
            if count > 256 {
201 +
                set count = 256;
202 +
            }
203 +
            let recovered = sync::enter();
204 +
            for i in run.first + offset..run.first + offset + count {
205 +
                assert frames::available(&pages::STORE.backings.pool, i);
206 +
            }
207 +
            sync::leave(recovered);
208 +
            set offset += count;
209 +
        }
210 +
    }
211 +
    for byte in "runtime cancellation passed\n" {
212 +
        put(byte);
213 +
    }
214 +
    while dispatch::clock() < FINAL {
215 +
    }
216 +
    let complete = sync::enter();
217 +
    for byte in "runtime final progress: " {
218 +
        put(byte);
219 +
    }
220 +
    for i in 0..4 {
221 +
        number(progress[i]);
222 +
        put(32);
223 +
    }
224 +
    put(10);
225 +
    assert progress[0] == 1 and progress[1] == 2 and progress[2] == 1;
226 +
    assert not loader::STATE.busy;
227 +
    let package = try! registry::get(&registry::STORE, abi::reference(progress[3]));
228 +
    let entry = package.entry else panic "published entry required";
229 +
    let mut controller = try! domains::get(&domains::STORE, ACTORS[3]);
230 +
    let image = try! registry::install(&registry::STORE, &mut controller.memory.table, abi::reference(progress[3]));
231 +
    sync::leave(complete);
232 +
    let handle = abi::Handle(try! calls::synchronized(ACTORS[3], 20, &[0, *image, 0, 0], dispatch::clock()));
233 +
    let created = sync::enter();
234 +
    let object = (try! capability::get(&controller.memory.table, handle)).object;
235 +
    let domain = try! domains::get(&domains::STORE, object);
236 +
    sync::leave(created);
237 +
    assert invoke(entry, domain.graph.table.ptr as u64) == 43;
238 +
    assert invoke(entry, domain.graph.table.ptr as u64) == 85;
239 +
    for byte in "runtime retry published executable code\n" {
240 +
        put(byte);
241 +
    }
242 +
    let maximum = sync::maximum();
243 +
    for byte in "runtime metadata instructions: " {
244 +
        put(byte);
245 +
    }
246 +
    number(maximum); put(10);
247 +
    assert maximum > 0 and maximum < 1000000;
248 +
    finish();
249 +
}
test/runtime/machine.ras added +44 -0
1 +
//! Boot one hart and check user termination through production dispatch.
2 +
.text;
3 +
    call @kernel::boot::initialize;
4 +
    call @kernel::dispatchcheck::setup;
5 +
    call @kernel::boot::run;
6 +
.export @kernel::dispatchcheck::memory;
7 +
.export @kernel::dispatchcheck::kernelGp;
8 +
.export @kernel::dispatchcheck::completion;
9 +
.export @kernel::dispatchcheck::finish;
10 +
@kernel::dispatchcheck::memory
11 +
    ret;
12 +
@kernel::dispatchcheck::kernelGp
13 +
    mv %a0 %gp;
14 +
    ret;
15 +
@kernel::dispatchcheck::completion
16 +
    la %a0 @complete;
17 +
    ret;
18 +
@complete
19 +
    call @kernel::dispatchcheck::verify;
20 +
    ebreak;
21 +
@kernel::dispatchcheck::finish
22 +
    li %t0 0x10001000;
23 +
    li %t1 0x5555;
24 +
    sw %t1 0(%t0);
25 +
    ebreak;
26 +
27 +
.export @kernel::dispatchcheck::put;
28 +
@kernel::dispatchcheck::put
29 +
    li %t0 0x10000000;
30 +
    sb %a0 0(%t0);
31 +
    ret;
32 +
33 +
.export @kernel::dispatchcheck::invoke;
34 +
// Preserve kernel state while calling one private instance of shared code.
35 +
@kernel::dispatchcheck::invoke
36 +
    addi %sp %sp -16;
37 +
    sd %ra 8(%sp);
38 +
    sd %gp 0(%sp);
39 +
    mv %gp %a1;
40 +
    jalr %ra %a0 0;
41 +
    ld %gp 0(%sp);
42 +
    ld %ra 8(%sp);
43 +
    addi %sp %sp 16;
44 +
    ret;
test/runtime/run added +39 -0
1 +
#!/bin/sh
2 +
# Compile, cancel, and retry a package through scheduled user calls.
3 +
set -eu
4 +
emulator=${RAD_EMULATOR:-emulator}
5 +
fixture=runtime
6 +
work=$(mktemp -d)
7 +
trap 'rm -rf "$work"' EXIT HUP INT TERM
8 +
mkdir "$work/spin"
9 +
cp "test/$fixture/spin.rad" "$work/spin.rad"
10 +
cp kernel/kernel/abi.rad kernel/kernel/sys.rad "$work/spin/"
11 +
"$emulator" -memory-size=385024 -data-size=348160 -stack-size=512 -run bin/radiance.rv64.dev \
12 +
    -pkg spin -mod "$work/spin.rad" -mod "$work/spin/abi.rad" -mod "$work/spin/sys.rad" -entry spin -ril "$work"
13 +
cp test/loader/loaded.rad "$work/loaded.rad"
14 +
index=0
15 +
while [ "$index" -lt 128 ]; do
16 +
    printf '\n/// Compute a distinct exported value.\nexport fn value%s(input: u64) -> u64 { return input + %s; }\n' "$index" "$index" >> "$work/loaded.rad"
17 +
    index=$((index + 1))
18 +
done
19 +
"$emulator" -memory-size=385024 -data-size=348160 -stack-size=512 -run bin/radiance.rv64.dev \
20 +
    -pkg loaded -mod "$work/loaded.rad" -entry loaded -ril "$work"
21 +
cp kernel/kernel.rad "$work/kernel.rad"
22 +
printf '\nexport mod dispatchinput;\nexport mod dispatchcheck;\n' >> "$work/kernel.rad"
23 +
mkdir "$work/kernel"
24 +
cp kernel/kernel/*.rad "$work/kernel/"
25 +
cp "test/$fixture/kernel/dispatchcheck.rad" "$work/kernel/"
26 +
length=$(wc -c < "$work/spin.ril")
27 +
printf '//! Binary user loop.\n/// Complete trusted input package.\nexport static INPUT: [u8; %s] = [\n' "$length" > "$work/kernel/dispatchinput.rad"
28 +
od -An -v -tu1 "$work/spin.ril" | awk '{ for (i = 1; i <= NF; i++) printf "%s,", $i; print "" }' >> "$work/kernel/dispatchinput.rad"
29 +
printf '];\n' >> "$work/kernel/dispatchinput.rad"
30 +
length=$(wc -c < "$work/loaded.ril")
31 +
printf '\n/// Package compiled by scheduled user calls.\nexport static PAYLOAD: [u8; %s] = [\n' "$length" >> "$work/kernel/dispatchinput.rad"
32 +
od -An -v -tu1 "$work/loaded.ril" | awk '{ for (i = 1; i <= NF; i++) printf "%s,", $i; print "" }' >> "$work/kernel/dispatchinput.rad"
33 +
printf '];\n' >> "$work/kernel/dispatchinput.rad"
34 +
sh test/acceptance/compile "$emulator" "$work"
35 +
cat "test/$fixture/machine.ras" kernel/kernel/*.ras > "$work/dispatch.ras"
36 +
"$emulator" -memory-size=385024 -data-size=348160 -stack-size=512 -run bin/kernel.build.rv64 \
37 +
    -- "$work/std.ril" "$work/kernel.ril" "$work/dispatch.ras" "$work/dispatch.rv64"
38 +
"$emulator" -machine -harts=1 -memory-size=262144 -max-steps=2000000000 -run "$work/dispatch.rv64"
39 +
printf 'runtime: scheduled cancellation, retry, and publication passed\n'
test/runtime/spin.rad added +53 -0
1 +
//! Scheduled runtime compilation and an independent window witness.
2 +
export mod abi;
3 +
export mod sys;
4 +
5 +
/// Startup fields used by the scheduled fixture.
6 +
record Request: Copy {
7 +
    /// Readable binary package capability.
8 +
    source: u64,
9 +
    /// Complete binary package byte count.
10 +
    length: u64,
11 +
    /// Shared progress counters.
12 +
    progress: *unsafe mut u64,
13 +
}
14 +
15 +
/// Values supplied at domain activation.
16 +
record Env: Copy {
17 +
    /// Input handle, byte count, and shared progress address.
18 +
    argsPointer: *Request,
19 +
    /// Actor index selected by the parent.
20 +
    argsSize: u64,
21 +
    /// Event capability installed at creation.
22 +
    eventsHandle: u64,
23 +
    /// Shared event queue address.
24 +
    eventsPointer: u64,
25 +
}
26 +
27 +
/// Compile a package twice or record progress during another domain's load.
28 +
@default unsafe fn main(env: *Env) {
29 +
    let args = env.argsPointer;
30 +
    let progress = @sliceOf(args.progress, 4);
31 +
    let actor = env.argsSize as u32;
32 +
    if actor == 2 {
33 +
        assert progress[1] == 1;
34 +
        let mut busy = false;
35 +
        try sys::imageLoad(abi::Handle(0), abi::Handle(args.source), 0, args.length) catch error {
36 +
            assert error == abi::Error::Busy; set busy = true;
37 +
        };
38 +
        assert busy;
39 +
        set progress[2] = 1;
40 +
        return;
41 +
    }
42 +
    set progress[actor] = 1;
43 +
    let image = try sys::imageLoad(abi::Handle(0), abi::Handle(args.source), 0, args.length) catch error {
44 +
        set progress[actor] = 100 + error as u64; return;
45 +
    };
46 +
    let info = try! sys::queryImage(image);
47 +
    let repeated = try sys::imageLoad(abi::Handle(0), abi::Handle(args.source), 0, args.length) catch error {
48 +
        set progress[actor] = 200 + error as u64; return;
49 +
    };
50 +
    assert (try! sys::queryImage(repeated)).id == info.id;
51 +
    set progress[3] = info.id;
52 +
    set progress[actor] = 2;
53 +
}
test/smp/kernel/dispatchcheck.rad +2 -1
1 1
//! One domain executing separate user contexts on every online hart.
2 2
use std::mem;
3 3
use std::arch::rv64::shared;
4 4
use kernel::abi;
5 +
use kernel::frames;
5 6
use kernel::slots;
6 7
use kernel::backing;
7 8
use kernel::pages;
8 9
use kernel::capability;
9 10
use kernel::registry;
642 643
    let guard = sync::enter();
643 644
    assert (lifecycle::CALLS.active[IDLE_CONTEXT.index / 32] & (1 << (IDLE_CONTEXT.index % 32))) == 0;
644 645
    assert pages::STORE.slots[allocation.page.index].state == slots::State::Free;
645 646
    assert pages::STORE.backings.slots[allocation.backing.index].state == slots::State::Free;
646 647
    for i in allocation.run.first..allocation.run.first + allocation.run.count {
647 -
        assert pages::STORE.backings.pool.free[i];
648 +
        assert frames::available(&pages::STORE.backings.pool, i);
648 649
    }
649 650
    sync::leave(guard);
650 651
    for byte in "smp cancelled preempted page allocation\n" {
651 652
        put(byte);
652 653
    }