Nothing to see here, move along
1use super::object::{CNodeData, ObjectTag};
2use super::pool::ObjectPool;
3use super::table::{CapRef, CapSlot, Rights};
4use crate::error::KernelError;
5use crate::mem::addr;
6use crate::mem::phys::BitmapFrameAllocator;
7use crate::proc::ProcessManager;
8use crate::types::{Generation, ObjectId, Pid};
9use x86_64::PhysAddr;
10
11pub use lancer_core::cnode::{MAX_CNODE_BITS, MIN_CNODE_BITS};
12use lancer_core::cnode::{MAX_RESOLVE_DEPTH, PAGE_SIZE, extract_index, frames_for_cnode};
13
14pub fn create_cnode(
15 size_bits: u8,
16 allocator: &BitmapFrameAllocator,
17) -> Result<CNodeData, KernelError> {
18 if !(MIN_CNODE_BITS..=MAX_CNODE_BITS).contains(&size_bits) {
19 return Err(KernelError::InvalidParameter);
20 }
21 let frame_count = frames_for_cnode(size_bits);
22 let slots_phys = allocator
23 .allocate_contiguous(frame_count as usize)
24 .ok_or(KernelError::ResourceExhausted)?;
25
26 let slot_count = 1usize << size_bits;
27 let base_ptr = addr::phys_to_virt(slots_phys).as_mut_ptr::<CapSlot>();
28 (0..slot_count).for_each(|i| unsafe {
29 base_ptr.add(i).write(CapSlot::Empty);
30 });
31
32 Ok(CNodeData {
33 slots_phys,
34 size_bits,
35 frame_count,
36 })
37}
38
39pub fn destroy_cnode(cnode: &CNodeData, allocator: &BitmapFrameAllocator) {
40 let base_frame_idx = (cnode.slots_phys.as_u64() / PAGE_SIZE as u64) as usize;
41 (0..cnode.frame_count as usize).for_each(|i| {
42 let frame_phys = PhysAddr::new((base_frame_idx + i) as u64 * PAGE_SIZE as u64);
43 crate::mem::addr::zero_frame(frame_phys);
44 let frame = unsafe {
45 x86_64::structures::paging::PhysFrame::from_start_address_unchecked(frame_phys)
46 };
47 allocator.deallocate_frame(frame);
48 });
49}
50
51unsafe fn read_slot(slots_phys: PhysAddr, index: usize) -> CapSlot {
52 let base = addr::phys_to_virt(slots_phys).as_ptr::<CapSlot>();
53 unsafe { base.add(index).read() }
54}
55
56unsafe fn slot_ptr(slots_phys: PhysAddr, index: usize) -> *mut CapSlot {
57 let base = addr::phys_to_virt(slots_phys).as_mut_ptr::<CapSlot>();
58 unsafe { base.add(index) }
59}
60
61pub fn resolve(
62 pool: &ObjectPool,
63 cnode_id: ObjectId,
64 cnode_gen: Generation,
65 address: u64,
66 depth: u8,
67) -> Result<CapSlot, KernelError> {
68 resolve_inner(pool, cnode_id, cnode_gen, address, depth, 0)
69}
70
71fn resolve_inner(
72 pool: &ObjectPool,
73 cnode_id: ObjectId,
74 cnode_gen: Generation,
75 address: u64,
76 depth: u8,
77 recursion: u8,
78) -> Result<CapSlot, KernelError> {
79 if recursion >= MAX_RESOLVE_DEPTH {
80 return Err(KernelError::InvalidSlot);
81 }
82
83 let (slots_phys, size_bits) = {
84 let data = pool.get(cnode_id, cnode_gen)?;
85 let cnode = data.as_cnode()?;
86 (cnode.slots_phys, cnode.size_bits)
87 };
88
89 if depth < size_bits {
90 return Err(KernelError::InvalidSlot);
91 }
92
93 let index = extract_index(address, depth, size_bits) as usize;
94 let remaining = depth - size_bits;
95 let slot = unsafe { read_slot(slots_phys, index) };
96
97 match remaining {
98 0 => Ok(slot),
99 _ => match slot {
100 CapSlot::Active(cap) if cap.tag() == ObjectTag::CNode => resolve_inner(
101 pool,
102 cap.object_id(),
103 cap.generation(),
104 address,
105 remaining,
106 recursion + 1,
107 ),
108 _ => Err(KernelError::InvalidSlot),
109 },
110 }
111}
112
113fn resolve_slot_ptr(
114 pool: &ObjectPool,
115 cnode_id: ObjectId,
116 cnode_gen: Generation,
117 address: u64,
118 depth: u8,
119) -> Result<*mut CapSlot, KernelError> {
120 resolve_slot_ptr_inner(pool, cnode_id, cnode_gen, address, depth, 0)
121}
122
123fn resolve_slot_ptr_inner(
124 pool: &ObjectPool,
125 cnode_id: ObjectId,
126 cnode_gen: Generation,
127 address: u64,
128 depth: u8,
129 recursion: u8,
130) -> Result<*mut CapSlot, KernelError> {
131 if recursion >= MAX_RESOLVE_DEPTH {
132 return Err(KernelError::InvalidSlot);
133 }
134
135 let (slots_phys, size_bits) = {
136 let data = pool.get(cnode_id, cnode_gen)?;
137 let cnode = data.as_cnode()?;
138 (cnode.slots_phys, cnode.size_bits)
139 };
140
141 if depth < size_bits {
142 return Err(KernelError::InvalidSlot);
143 }
144
145 let index = extract_index(address, depth, size_bits) as usize;
146 let remaining = depth - size_bits;
147
148 match remaining {
149 0 => Ok(unsafe { slot_ptr(slots_phys, index) }),
150 _ => {
151 let slot = unsafe { read_slot(slots_phys, index) };
152 match slot {
153 CapSlot::Active(cap) if cap.tag() == ObjectTag::CNode => {
154 resolve_slot_ptr_inner(
155 pool,
156 cap.object_id(),
157 cap.generation(),
158 address,
159 remaining,
160 recursion + 1,
161 )
162 }
163 _ => Err(KernelError::InvalidSlot),
164 }
165 }
166 }
167}
168
169pub fn resolve_and_insert(
170 pool: &ObjectPool,
171 cnode_id: ObjectId,
172 cnode_gen: Generation,
173 address: u64,
174 depth: u8,
175 cap: CapRef,
176) -> Result<(), KernelError> {
177 let ptr = resolve_slot_ptr(pool, cnode_id, cnode_gen, address, depth)?;
178 let slot = unsafe { &mut *ptr };
179 match slot {
180 CapSlot::Active(_) => Err(KernelError::SlotOccupied),
181 CapSlot::Empty => {
182 *slot = CapSlot::Active(cap);
183 Ok(())
184 }
185 }
186}
187
188pub fn resolve_and_validate(
189 pool: &ObjectPool,
190 cnode_id: ObjectId,
191 cnode_gen: Generation,
192 address: u64,
193 depth: u8,
194 expected_tag: ObjectTag,
195 required_rights: Rights,
196) -> Result<CapRef, KernelError> {
197 match resolve(pool, cnode_id, cnode_gen, address, depth)? {
198 CapSlot::Empty => Err(KernelError::SlotEmpty),
199 CapSlot::Active(cap) => {
200 if cap.tag() != expected_tag {
201 return Err(KernelError::InvalidType);
202 }
203 if !cap.rights().contains(required_rights) {
204 return Err(KernelError::InsufficientRights);
205 }
206 Ok(cap)
207 }
208 }
209}
210
211pub fn resolve_and_read(
212 pool: &ObjectPool,
213 cnode_id: ObjectId,
214 cnode_gen: Generation,
215 address: u64,
216 depth: u8,
217) -> Result<CapRef, KernelError> {
218 match resolve(pool, cnode_id, cnode_gen, address, depth)? {
219 CapSlot::Empty => Err(KernelError::SlotEmpty),
220 CapSlot::Active(cap) => Ok(cap),
221 }
222}
223
224pub fn resolve_and_clear(
225 pool: &ObjectPool,
226 cnode_id: ObjectId,
227 cnode_gen: Generation,
228 address: u64,
229 depth: u8,
230) -> Result<CapRef, KernelError> {
231 let ptr = resolve_slot_ptr(pool, cnode_id, cnode_gen, address, depth)?;
232 let slot = unsafe { &mut *ptr };
233 match slot {
234 CapSlot::Empty => Err(KernelError::SlotEmpty),
235 CapSlot::Active(cap) => {
236 let cap = *cap;
237 *slot = CapSlot::Empty;
238 Ok(cap)
239 }
240 }
241}
242
243pub fn walk_cnode_slots(
244 pool: &ObjectPool,
245 cnode_id: ObjectId,
246 cnode_gen: Generation,
247 mut f: impl FnMut(&mut CapSlot),
248) -> Result<(), KernelError> {
249 let (slots_phys, size_bits) = {
250 let data = pool.get(cnode_id, cnode_gen)?;
251 let cnode = data.as_cnode()?;
252 (cnode.slots_phys, cnode.size_bits)
253 };
254
255 let slot_count = 1usize << size_bits;
256 (0..slot_count).for_each(|i| {
257 let slot = unsafe { &mut *slot_ptr(slots_phys, i) };
258 f(slot);
259 });
260 Ok(())
261}
262
263pub fn cnode_coords(
264 pid: Pid,
265 ptable: &ProcessManager,
266) -> Result<(ObjectId, Generation, u8), KernelError> {
267 let proc = ptable.get(pid).ok_or(KernelError::InvalidObject)?;
268 let (cnode_id, cnode_gen) = proc.root_cnode().ok_or(KernelError::InvalidObject)?;
269 Ok((cnode_id, cnode_gen, proc.cnode_depth()))
270}
271
272pub fn resolve_caller_validate(
273 pid: Pid,
274 address: u64,
275 expected_tag: ObjectTag,
276 required_rights: Rights,
277 ptable: &ProcessManager,
278 pool: &ObjectPool,
279) -> Result<CapRef, KernelError> {
280 let (cnode_id, cnode_gen, depth) = cnode_coords(pid, ptable)?;
281 resolve_and_validate(pool, cnode_id, cnode_gen, address, depth, expected_tag, required_rights)
282}
283
284pub fn resolve_caller_read(
285 pid: Pid,
286 address: u64,
287 ptable: &ProcessManager,
288 pool: &ObjectPool,
289) -> Result<CapRef, KernelError> {
290 let (cnode_id, cnode_gen, depth) = cnode_coords(pid, ptable)?;
291 resolve_and_read(pool, cnode_id, cnode_gen, address, depth)
292}
293
294pub fn resolve_caller_insert(
295 pid: Pid,
296 address: u64,
297 cap: CapRef,
298 ptable: &ProcessManager,
299 pool: &ObjectPool,
300) -> Result<(), KernelError> {
301 let (cnode_id, cnode_gen, depth) = cnode_coords(pid, ptable)?;
302 resolve_and_insert(pool, cnode_id, cnode_gen, address, depth, cap)
303}
304
305pub fn resolve_caller_clear(
306 pid: Pid,
307 address: u64,
308 ptable: &ProcessManager,
309 pool: &ObjectPool,
310) -> Result<CapRef, KernelError> {
311 let (cnode_id, cnode_gen, depth) = cnode_coords(pid, ptable)?;
312 resolve_and_clear(pool, cnode_id, cnode_gen, address, depth)
313}
314
315#[cfg(lancer_test)]
316pub fn drain_cnode_tree(
317 pool: &mut ObjectPool,
318 cnode_id: ObjectId,
319 cnode_gen: Generation,
320 callback: &mut impl FnMut(CapRef, &mut ObjectPool),
321) -> Result<(), KernelError> {
322 drain_cnode_tree_inner(pool, cnode_id, cnode_gen, callback, 0)
323}
324
325#[cfg(lancer_test)]
326fn drain_cnode_tree_inner(
327 pool: &mut ObjectPool,
328 cnode_id: ObjectId,
329 cnode_gen: Generation,
330 callback: &mut impl FnMut(CapRef, &mut ObjectPool),
331 recursion: u8,
332) -> Result<(), KernelError> {
333 if recursion >= MAX_RESOLVE_DEPTH {
334 return Err(KernelError::InvalidSlot);
335 }
336
337 let (slots_phys, size_bits) = {
338 let data = pool.get(cnode_id, cnode_gen)?;
339 let cnode = data.as_cnode()?;
340 (cnode.slots_phys, cnode.size_bits)
341 };
342
343 let slot_count = 1usize << size_bits;
344 (0..slot_count).for_each(|i| {
345 let slot = unsafe { &mut *slot_ptr(slots_phys, i) };
346 match slot {
347 CapSlot::Active(cap) => {
348 let cap = *cap;
349 *slot = CapSlot::Empty;
350 if cap.tag() == ObjectTag::CNode {
351 let _ = drain_cnode_tree_inner(
352 pool,
353 cap.object_id(),
354 cap.generation(),
355 callback,
356 recursion + 1,
357 );
358 }
359 callback(cap, pool);
360 }
361 CapSlot::Empty => {}
362 }
363 });
364 Ok(())
365}
366
367pub fn drain_cnode_data(
368 cnode: &super::object::CNodeData,
369 pool: &mut ObjectPool,
370 callback: &mut impl FnMut(CapRef, &mut ObjectPool),
371) {
372 drain_cnode_data_inner(cnode, pool, callback, 0);
373}
374
375fn drain_cnode_data_inner(
376 cnode: &super::object::CNodeData,
377 pool: &mut ObjectPool,
378 callback: &mut impl FnMut(CapRef, &mut ObjectPool),
379 recursion: u8,
380) {
381 if recursion >= MAX_RESOLVE_DEPTH {
382 return;
383 }
384
385 let slot_count = 1usize << cnode.size_bits;
386 (0..slot_count).for_each(|i| {
387 let slot = unsafe { &mut *slot_ptr(cnode.slots_phys, i) };
388 match slot {
389 CapSlot::Active(cap) => {
390 let cap = *cap;
391 *slot = CapSlot::Empty;
392 match cap.tag() == ObjectTag::CNode {
393 true => match pool.dec_ref(cap.object_id(), cap.generation()) {
394 Some(super::object::ObjectData::CNode(ref nested)) => {
395 drain_cnode_data_inner(nested, pool, callback, recursion + 1);
396 destroy_cnode(nested, &BitmapFrameAllocator);
397 }
398 Some(ref data) => {
399 super::ops::cleanup_object_data(data);
400 }
401 None => {}
402 },
403 false => callback(cap, pool),
404 }
405 }
406 CapSlot::Empty => {}
407 }
408 });
409}
410
411pub fn invalidate_stale_in_cnode(
412 pool: &ObjectPool,
413 cnode_id: ObjectId,
414 cnode_gen: Generation,
415 target_oid: ObjectId,
416 target_gen: Generation,
417) -> Result<(), KernelError> {
418 invalidate_stale_inner(pool, cnode_id, cnode_gen, target_oid, target_gen, 0)
419}
420
421fn invalidate_stale_inner(
422 pool: &ObjectPool,
423 cnode_id: ObjectId,
424 cnode_gen: Generation,
425 target_oid: ObjectId,
426 target_gen: Generation,
427 recursion: u8,
428) -> Result<(), KernelError> {
429 if recursion >= MAX_RESOLVE_DEPTH {
430 return Err(KernelError::InvalidSlot);
431 }
432
433 let (slots_phys, size_bits) = {
434 let data = pool.get(cnode_id, cnode_gen)?;
435 let cnode = data.as_cnode()?;
436 (cnode.slots_phys, cnode.size_bits)
437 };
438
439 let slot_count = 1usize << size_bits;
440 (0..slot_count).for_each(|i| {
441 let slot = unsafe { &mut *slot_ptr(slots_phys, i) };
442 match slot {
443 CapSlot::Active(cap) => {
444 if cap.tag() == ObjectTag::CNode {
445 let _ = invalidate_stale_inner(
446 pool,
447 cap.object_id(),
448 cap.generation(),
449 target_oid,
450 target_gen,
451 recursion + 1,
452 );
453 }
454 if cap.object_id() == target_oid && cap.generation() == target_gen {
455 *slot = CapSlot::Empty;
456 }
457 }
458 CapSlot::Empty => {}
459 }
460 });
461 Ok(())
462}