Nothing to see here, move along
1use crate::cap::cnode;
2use crate::cap::pool::POOL;
3use crate::cap::retype;
4use crate::mem::phys::BitmapFrameAllocator;
5use crate::proc::address_space;
6use crate::proc::{PROCESSES, ProcessState};
7use lancer_core::object_tag::ObjectTag;
8
9fn retype_thread(
10 ptable: &mut crate::sync::IrqMutexGuard<'_, crate::proc::ProcessManager, 0>,
11 ut_id: crate::types::ObjPhys,
12 ut_gen: crate::types::Generation,
13 parent_pid: crate::types::Pid,
14 dest_slot: u64,
15) -> Result<crate::types::Pid, crate::error::KernelError> {
16 let (cnode_id, cnode_gen, depth, gv, gb) = cnode::cnode_coords(parent_pid, ptable)?;
17 let mut pool = POOL.lock_after(ptable);
18 retype::retype_thread_from_untyped(
19 &mut pool, ptable, ut_id, ut_gen, parent_pid, cnode_id, cnode_gen, dest_slot, depth, gv, gb,
20 )
21}
22
23struct ThreadPair {
24 owner_pid: crate::types::Pid,
25 child_pid: crate::types::Pid,
26}
27
28fn with_thread_pair(
29 dest_slot: u64,
30 body: fn(&mut crate::sync::IrqMutexGuard<'_, crate::proc::ProcessManager, 0>, &ThreadPair),
31) {
32 let mut allocator = BitmapFrameAllocator;
33 let mut ptable = PROCESSES.lock();
34
35 let created = ptable.allocate(&mut allocator).expect("alloc owner");
36 ptable.start(created).expect("start");
37 let owner_pid = created.pid();
38 crate::tests::helpers::bootstrap_test_cnode(owner_pid, &mut ptable);
39
40 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_small_untyped(&ptable, 16);
41 let child_pid =
42 retype_thread(&mut ptable, ut_id, ut_gen, owner_pid, dest_slot).expect("retype thread");
43
44 let pair = ThreadPair {
45 owner_pid,
46 child_pid,
47 };
48 body(&mut ptable, &pair);
49
50 ptable.destroy(child_pid, &mut allocator);
51 ptable.destroy(owner_pid, &mut allocator);
52}
53
54crate::kernel_test!(
55 fn thread_create_shares_pml4() {
56 with_thread_pair(200, |ptable, p| {
57 let parent_pml4 = ptable.exec(p.owner_pid).unwrap().pml4_phys;
58 assert!(
59 ptable.exec(p.child_pid).unwrap().pml4_phys == parent_pml4,
60 "thread must share parent PML4"
61 );
62
63 let refcount = address_space::pml4_ref_get(parent_pml4.raw());
64 assert!(refcount == 2, "PML4 refcount must be 2, got {}", refcount);
65 });
66 }
67);
68
69crate::kernel_test!(
70 fn thread_create_independent_context() {
71 with_thread_pair(201, |ptable, p| {
72 assert!(
73 ptable[p.child_pid].state() == ProcessState::Created,
74 "thread initial state must be Created"
75 );
76 assert!(
77 p.child_pid != p.owner_pid,
78 "thread must have a different PID"
79 );
80 });
81 }
82);
83
84crate::kernel_test!(
85 fn thread_teardown_preserves_address_space() {
86 let mut allocator = BitmapFrameAllocator;
87 let mut ptable = PROCESSES.lock();
88
89 let created = ptable.allocate(&mut allocator).expect("alloc owner");
90 ptable.start(created).expect("start");
91 let owner_pid = created.pid();
92 crate::tests::helpers::bootstrap_test_cnode(owner_pid, &mut ptable);
93
94 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_small_untyped(&ptable, 16);
95 let child_pid =
96 retype_thread(&mut ptable, ut_id, ut_gen, owner_pid, 202).expect("retype thread");
97 let pml4 = ptable.exec(owner_pid).unwrap().pml4_phys;
98
99 assert!(
100 address_space::pml4_ref_get(pml4.raw()) == 2,
101 "refcount should be 2"
102 );
103
104 ptable.destroy(child_pid, &mut allocator);
105
106 assert!(
107 address_space::pml4_ref_get(pml4.raw()) == 1,
108 "refcount should drop to 1 after thread destroy"
109 );
110 assert!(ptable.get(owner_pid).is_some(), "parent must still exist");
111 assert!(
112 ptable.exec(owner_pid).unwrap().pml4_phys == pml4,
113 "parent PML4 must be unchanged"
114 );
115
116 ptable.destroy(owner_pid, &mut allocator);
117 }
118);
119
120crate::kernel_test!(
121 fn last_thread_teardown_frees_pml4() {
122 let mut allocator = BitmapFrameAllocator;
123 let mut ptable = PROCESSES.lock();
124
125 let created = ptable.allocate(&mut allocator).expect("alloc owner");
126 ptable.start(created).expect("start");
127 let owner_pid = created.pid();
128 crate::tests::helpers::bootstrap_test_cnode(owner_pid, &mut ptable);
129
130 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_small_untyped(&ptable, 16);
131 let child_pid =
132 retype_thread(&mut ptable, ut_id, ut_gen, owner_pid, 203).expect("retype thread");
133 let pml4 = ptable.exec(owner_pid).unwrap().pml4_phys;
134
135 ptable.destroy(owner_pid, &mut allocator);
136 assert!(
137 address_space::pml4_ref_get(pml4.raw()) == 1,
138 "refcount should be 1 after parent destroy"
139 );
140
141 ptable.destroy(child_pid, &mut allocator);
142 assert!(
143 address_space::pml4_ref_get(pml4.raw()) == 0,
144 "refcount should be 0 after last thread destroy"
145 );
146 }
147);
148
149crate::kernel_test!(
150 fn fs_base_default_zero() {
151 with_thread_pair(204, |ptable, p| {
152 assert!(
153 ptable.exec(p.child_pid).unwrap().fs_base == 0,
154 "thread fs_base must default to 0"
155 );
156 assert!(
157 ptable.exec(p.owner_pid).unwrap().fs_base == 0,
158 "parent fs_base must default to 0"
159 );
160 });
161 }
162);
163
164crate::kernel_test!(
165 fn fs_base_stored_per_process() {
166 with_thread_pair(205, |ptable, p| {
167 ptable.exec_mut(p.owner_pid).unwrap().fs_base = 0x7FFF_0000_1000;
168 ptable.exec_mut(p.child_pid).unwrap().fs_base = 0x7FFF_0000_2000;
169
170 assert!(
171 ptable.exec(p.owner_pid).unwrap().fs_base == 0x7FFF_0000_1000,
172 "parent fs_base mismatch"
173 );
174 assert!(
175 ptable.exec(p.child_pid).unwrap().fs_base == 0x7FFF_0000_2000,
176 "child fs_base mismatch"
177 );
178 });
179 }
180);
181
182crate::kernel_test!(
183 fn thread_inherits_parent_cnode() {
184 with_thread_pair(206, |ptable, p| {
185 assert!(
186 ptable.exec(p.child_pid).unwrap().root_cnode()
187 == ptable.exec(p.owner_pid).unwrap().root_cnode(),
188 "thread must inherit parent's root cnode"
189 );
190 });
191 }
192);
193
194crate::kernel_test!(
195 fn thread_retype_from_untyped_marks_from_untyped() {
196 with_thread_pair(207, |ptable, p| {
197 assert!(
198 ptable.exec(p.child_pid).unwrap().from_untyped,
199 "thread created via retype must have from_untyped = true"
200 );
201 });
202 }
203);
204
205crate::kernel_test!(
206 fn thread_retype_device_untyped_rejected() {
207 let mut allocator = BitmapFrameAllocator;
208 let mut ptable = PROCESSES.lock();
209
210 let created = ptable.allocate(&mut allocator).expect("alloc owner");
211 ptable.start(created).expect("start");
212 let owner_pid = created.pid();
213 crate::tests::helpers::bootstrap_test_cnode(owner_pid, &mut ptable);
214
215 let (dev_id, dev_gen, _) = crate::tests::helpers::allocate_untyped_inner(&ptable, 16, true);
216 let result = retype_thread(&mut ptable, dev_id, dev_gen, owner_pid, 208);
217
218 assert!(
219 result.is_err(),
220 "thread retype from device untyped must fail"
221 );
222
223 ptable.destroy(owner_pid, &mut allocator);
224 }
225);
226
227crate::kernel_test!(
228 fn fs_base_msr_round_trip() {
229 let original = crate::sched::switch::read_fs_base();
230
231 crate::sched::switch::write_fs_base(0x7FFF_DEAD_BEEF);
232 let readback = crate::sched::switch::read_fs_base();
233 assert!(
234 readback == 0x7FFF_DEAD_BEEF,
235 "fs_base readback mismatch: expected 0x7FFF_DEAD_BEEF, got {:#x}",
236 readback
237 );
238
239 crate::sched::switch::write_fs_base(0x0000_CAFE_0000);
240 let readback2 = crate::sched::switch::read_fs_base();
241 assert!(
242 readback2 == 0x0000_CAFE_0000,
243 "fs_base second readback mismatch: expected 0x0000_CAFE_0000, got {:#x}",
244 readback2
245 );
246
247 crate::sched::switch::write_fs_base(original);
248 }
249);
250
251crate::kernel_test!(
252 fn thread_retype_insufficient_space_rejected() {
253 let mut allocator = BitmapFrameAllocator;
254 let mut ptable = PROCESSES.lock();
255
256 let created = ptable.allocate(&mut allocator).expect("alloc owner");
257 ptable.start(created).expect("start");
258 let owner_pid = created.pid();
259 crate::tests::helpers::bootstrap_test_cnode(owner_pid, &mut ptable);
260
261 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_small_untyped(&ptable, 12);
262
263 let (cnode_id, cnode_gen, depth, gv, gb) =
264 cnode::cnode_coords(owner_pid, &ptable).expect("coords");
265 {
266 let mut pool = POOL.lock_after(&ptable);
267 crate::cap::retype::kernel_retype(
268 &mut pool,
269 Some(&mut *ptable),
270 ut_id,
271 ut_gen,
272 ObjectTag::Endpoint,
273 0,
274 cnode_id,
275 cnode_gen,
276 250,
277 depth,
278 gv,
279 gb,
280 1,
281 )
282 .expect("consume untyped space");
283 }
284
285 let result = retype_thread(&mut ptable, ut_id, ut_gen, owner_pid, 209);
286 assert!(
287 result.is_err(),
288 "thread retype from too-small untyped must fail"
289 );
290
291 ptable.destroy(owner_pid, &mut allocator);
292 }
293);