Skip to main content

ruvix_boot/
capability_distribution.rs

1//! Capability distribution during boot (ADR-087 Section 6.3).
2//!
3//! At boot time:
4//! 1. Root task receives ALL physical memory capabilities
5//! 2. Stage 3 distributes capabilities per manifest
6//! 3. **SEC-001**: Root task drops to minimum capability set
7
8use crate::manifest::RvfManifest;
9use ruvix_cap::BootCapabilitySet;
10use ruvix_types::{CapRights, KernelError, ObjectType};
11
12#[cfg(feature = "alloc")]
13use alloc::vec::Vec;
14
15/// Capability distribution result from Stage 3.
16#[derive(Debug, Clone)]
17pub struct CapabilityDistribution {
18    /// Per-component capability grants.
19    #[cfg(feature = "alloc")]
20    pub component_grants: Vec<ComponentCapabilityGrant>,
21    /// Per-component capability grants (fixed-size no_std variant).
22    #[cfg(not(feature = "alloc"))]
23    pub component_grants: [Option<ComponentCapabilityGrant>; 256],
24    /// Number of active grants in the component_grants array.
25    #[cfg(not(feature = "alloc"))]
26    pub grant_count: usize,
27
28    /// SEC-001: Whether root task has been dropped to minimum set.
29    pub root_dropped_to_minimum: bool,
30
31    /// The minimum capability set for root task after drop.
32    pub root_minimum_set: MinimumCapabilitySet,
33}
34
35impl CapabilityDistribution {
36    /// Creates a capability distribution from a manifest.
37    pub fn from_manifest(
38        manifest: &RvfManifest,
39        _boot_capabilities: &BootCapabilitySet,
40    ) -> Result<Self, KernelError> {
41        let mut distribution = Self {
42            #[cfg(feature = "alloc")]
43            component_grants: Vec::new(),
44            #[cfg(not(feature = "alloc"))]
45            component_grants: [const { None }; 256],
46            #[cfg(not(feature = "alloc"))]
47            grant_count: 0,
48            root_dropped_to_minimum: false,
49            root_minimum_set: MinimumCapabilitySet::default(),
50        };
51
52        // Create grants per component from manifest
53        distribution.create_grants_from_manifest(manifest)?;
54
55        Ok(distribution)
56    }
57
58    fn create_grants_from_manifest(&mut self, manifest: &RvfManifest) -> Result<(), KernelError> {
59        // Phase A: Simplified grant creation
60        // In production, this would parse the manifest's capability requirements
61        // and create appropriate grants
62
63        let component_count = manifest.component_graph.component_count();
64
65        for i in 0..component_count {
66            if i >= 256 {
67                return Err(KernelError::LimitExceeded);
68            }
69
70            // Each component gets basic read/execute on its regions
71            let grant = ComponentCapabilityGrant {
72                component_index: i as u32,
73                grants: [None; 32],
74                grant_count: 0,
75            };
76
77            #[cfg(feature = "alloc")]
78            self.component_grants.push(grant);
79            #[cfg(not(feature = "alloc"))]
80            {
81                self.component_grants[i] = Some(grant);
82                self.grant_count += 1;
83            }
84        }
85
86        Ok(())
87    }
88
89    /// Returns the number of component grants.
90    #[inline]
91    #[must_use]
92    pub fn grant_count(&self) -> usize {
93        #[cfg(feature = "alloc")]
94        {
95            self.component_grants.len()
96        }
97        #[cfg(not(feature = "alloc"))]
98        {
99            self.grant_count
100        }
101    }
102}
103
104/// Capability grants for a single component.
105#[derive(Debug, Clone)]
106pub struct ComponentCapabilityGrant {
107    /// Component index in the manifest.
108    pub component_index: u32,
109
110    /// Individual capability grants.
111    pub grants: [Option<CapabilityGrant>; 32],
112
113    /// Number of grants.
114    pub grant_count: usize,
115}
116
117impl ComponentCapabilityGrant {
118    /// Adds a capability grant to this component.
119    pub fn add_grant(&mut self, grant: CapabilityGrant) -> Result<(), KernelError> {
120        if self.grant_count >= 32 {
121            return Err(KernelError::LimitExceeded);
122        }
123
124        self.grants[self.grant_count] = Some(grant);
125        self.grant_count += 1;
126        Ok(())
127    }
128}
129
130/// A single capability grant.
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132#[repr(C)]
133pub struct CapabilityGrant {
134    /// Object ID being granted access to.
135    pub object_id: u64,
136
137    /// Object type.
138    pub object_type: ObjectType,
139
140    /// Rights being granted.
141    pub rights: CapRights,
142
143    /// Badge for this grant.
144    pub badge: u64,
145}
146
147impl CapabilityGrant {
148    /// Creates a new capability grant.
149    #[must_use]
150    pub const fn new(
151        object_id: u64,
152        object_type: ObjectType,
153        rights: CapRights,
154        badge: u64,
155    ) -> Self {
156        Self {
157            object_id,
158            object_type,
159            rights,
160            badge,
161        }
162    }
163
164    /// Creates a read-only region grant.
165    #[must_use]
166    pub const fn region_readonly(object_id: u64, badge: u64) -> Self {
167        Self {
168            object_id,
169            object_type: ObjectType::Region,
170            rights: CapRights::READ,
171            badge,
172        }
173    }
174
175    /// Creates a read-write region grant.
176    #[must_use]
177    pub const fn region_readwrite(object_id: u64, badge: u64) -> Self {
178        Self {
179            object_id,
180            object_type: ObjectType::Region,
181            rights: CapRights::READ.union(CapRights::WRITE),
182            badge,
183        }
184    }
185
186    /// Creates a queue send grant.
187    #[must_use]
188    pub const fn queue_send(object_id: u64, badge: u64) -> Self {
189        Self {
190            object_id,
191            object_type: ObjectType::Queue,
192            rights: CapRights::WRITE,
193            badge,
194        }
195    }
196
197    /// Creates a queue receive grant.
198    #[must_use]
199    pub const fn queue_recv(object_id: u64, badge: u64) -> Self {
200        Self {
201            object_id,
202            object_type: ObjectType::Queue,
203            rights: CapRights::READ,
204            badge,
205        }
206    }
207}
208
209/// Minimum capability set for root task after SEC-001 drop.
210///
211/// After Stage 3, the root task should only retain:
212/// - Witness log (write-only for attestation)
213/// - Timer (for scheduling)
214/// - Self capability (for task management)
215#[derive(Debug, Clone, Default)]
216pub struct MinimumCapabilitySet {
217    /// Witness log capability (append-only).
218    pub witness_log: Option<CapabilityGrant>,
219
220    /// Timer capability.
221    pub timer: Option<CapabilityGrant>,
222
223    /// Self task capability.
224    pub self_task: Option<CapabilityGrant>,
225
226    /// IPC queue for system calls.
227    pub syscall_queue: Option<CapabilityGrant>,
228}
229
230impl MinimumCapabilitySet {
231    /// Creates a minimum capability set for the root task.
232    #[must_use]
233    pub fn for_root_task(
234        witness_log_id: u64,
235        timer_id: u64,
236        task_id: u64,
237        syscall_queue_id: u64,
238    ) -> Self {
239        Self {
240            witness_log: Some(CapabilityGrant::new(
241                witness_log_id,
242                ObjectType::WitnessLog,
243                CapRights::WRITE, // Append-only
244                0,
245            )),
246            timer: Some(CapabilityGrant::new(
247                timer_id,
248                ObjectType::Timer,
249                CapRights::READ.union(CapRights::WRITE),
250                0,
251            )),
252            self_task: Some(CapabilityGrant::new(
253                task_id,
254                ObjectType::Task,
255                CapRights::READ.union(CapRights::EXECUTE),
256                0,
257            )),
258            syscall_queue: Some(CapabilityGrant::new(
259                syscall_queue_id,
260                ObjectType::Queue,
261                CapRights::READ.union(CapRights::WRITE),
262                0,
263            )),
264        }
265    }
266
267    /// Returns the total number of capabilities in the minimum set.
268    #[inline]
269    #[must_use]
270    pub fn count(&self) -> usize {
271        let mut count = 0;
272        if self.witness_log.is_some() { count += 1; }
273        if self.timer.is_some() { count += 1; }
274        if self.self_task.is_some() { count += 1; }
275        if self.syscall_queue.is_some() { count += 1; }
276        count
277    }
278
279    /// Checks if this set is valid (all required capabilities present).
280    #[inline]
281    #[must_use]
282    pub fn is_valid(&self) -> bool {
283        self.witness_log.is_some()
284            && self.timer.is_some()
285            && self.self_task.is_some()
286    }
287}
288
289/// Root capability drop operation per SEC-001.
290///
291/// After Stage 3, this struct orchestrates:
292/// 1. Identifying capabilities to revoke
293/// 2. Revoking all non-minimum capabilities
294/// 3. Verifying the drop succeeded
295pub struct RootCapabilityDrop {
296    /// Capabilities that were revoked.
297    pub revoked_count: usize,
298
299    /// Whether the drop completed successfully.
300    pub completed: bool,
301
302    /// Final capability count for root task.
303    pub final_capability_count: usize,
304}
305
306impl RootCapabilityDrop {
307    /// Creates a new root capability drop operation.
308    #[must_use]
309    pub fn new() -> Self {
310        Self {
311            revoked_count: 0,
312            completed: false,
313            final_capability_count: 0,
314        }
315    }
316
317    /// Executes the capability drop.
318    ///
319    /// # SEC-001 Requirements
320    ///
321    /// - Root task MUST lose access to all physical memory
322    /// - Root task MUST retain only minimum capability set
323    /// - Drop MUST be irreversible (cannot re-escalate)
324    pub fn execute(
325        &mut self,
326        boot_capabilities: &BootCapabilitySet,
327        minimum_set: &MinimumCapabilitySet,
328    ) -> Result<(), KernelError> {
329        // Count capabilities to revoke
330        let total_boot_caps = boot_capabilities.total_count();
331        let minimum_caps = minimum_set.count();
332
333        self.revoked_count = total_boot_caps.saturating_sub(minimum_caps);
334        self.final_capability_count = minimum_caps;
335        self.completed = true;
336
337        // In a real implementation, this would:
338        // 1. Iterate through boot_capabilities
339        // 2. For each capability not in minimum_set:
340        //    - Call cap_revoke syscall
341        //    - Verify revocation succeeded
342        // 3. Verify no escalation paths remain
343
344        Ok(())
345    }
346
347    /// Verifies the drop was successful.
348    #[must_use]
349    pub fn verify(&self, minimum_set: &MinimumCapabilitySet) -> bool {
350        self.completed
351            && self.final_capability_count == minimum_set.count()
352            && minimum_set.is_valid()
353    }
354}
355
356impl Default for RootCapabilityDrop {
357    fn default() -> Self {
358        Self::new()
359    }
360}
361
362#[cfg(test)]
363mod tests {
364    use super::*;
365
366    #[test]
367    fn test_capability_grant_creation() {
368        let grant = CapabilityGrant::new(
369            0x1000,
370            ObjectType::Region,
371            CapRights::READ,
372            42,
373        );
374
375        assert_eq!(grant.object_id, 0x1000);
376        assert_eq!(grant.object_type, ObjectType::Region);
377        assert_eq!(grant.rights, CapRights::READ);
378        assert_eq!(grant.badge, 42);
379    }
380
381    #[test]
382    fn test_capability_grant_shortcuts() {
383        let ro = CapabilityGrant::region_readonly(0x1000, 1);
384        assert!(ro.rights.contains(CapRights::READ));
385        assert!(!ro.rights.contains(CapRights::WRITE));
386
387        let rw = CapabilityGrant::region_readwrite(0x2000, 2);
388        assert!(rw.rights.contains(CapRights::READ));
389        assert!(rw.rights.contains(CapRights::WRITE));
390
391        let send = CapabilityGrant::queue_send(0x3000, 3);
392        assert!(send.rights.contains(CapRights::WRITE));
393        assert!(!send.rights.contains(CapRights::READ));
394    }
395
396    #[test]
397    fn test_minimum_capability_set() {
398        let min_set = MinimumCapabilitySet::for_root_task(
399            0x1000, // witness_log
400            0x2000, // timer
401            0x3000, // task
402            0x4000, // syscall_queue
403        );
404
405        assert_eq!(min_set.count(), 4);
406        assert!(min_set.is_valid());
407    }
408
409    #[test]
410    fn test_minimum_capability_set_validity() {
411        let invalid_set = MinimumCapabilitySet::default();
412        assert!(!invalid_set.is_valid());
413        assert_eq!(invalid_set.count(), 0);
414    }
415
416    #[test]
417    fn test_root_capability_drop() {
418        let mut drop = RootCapabilityDrop::new();
419        let boot_caps = BootCapabilitySet::full(1, 0x1000, 0x10000, 0x2000, 0xCAFE);
420        let min_set = MinimumCapabilitySet::for_root_task(0x1000, 0x2000, 0x3000, 0x4000);
421
422        drop.execute(&boot_caps, &min_set).unwrap();
423
424        assert!(drop.completed);
425        assert_eq!(drop.final_capability_count, 4);
426        assert!(drop.verify(&min_set));
427    }
428
429    #[test]
430    fn test_component_capability_grant() {
431        let mut grant = ComponentCapabilityGrant {
432            component_index: 0,
433            grants: [None; 32],
434            grant_count: 0,
435        };
436
437        grant.add_grant(CapabilityGrant::region_readonly(0x1000, 0)).unwrap();
438        grant.add_grant(CapabilityGrant::queue_send(0x2000, 1)).unwrap();
439
440        assert_eq!(grant.grant_count, 2);
441    }
442
443    #[test]
444    fn test_component_capability_grant_limit() {
445        let mut grant = ComponentCapabilityGrant {
446            component_index: 0,
447            grants: [None; 32],
448            grant_count: 0,
449        };
450
451        // Fill up all 32 slots
452        for i in 0..32 {
453            grant.add_grant(CapabilityGrant::region_readonly(i as u64, 0)).unwrap();
454        }
455
456        // 33rd should fail
457        let result = grant.add_grant(CapabilityGrant::region_readonly(32, 0));
458        assert_eq!(result, Err(KernelError::LimitExceeded));
459    }
460}