Skip to main content

javm_cap/
yield_cap.rs

1//! `YieldSender` / `YieldReceiver` kernel-assisted cap helpers + the reserved
2//! `kernel:*` yield-key namespace.
3//!
4//! A **yield_key** is a ≤[`MAX_KEY_LEN`](crate::slot::MAX_KEY_LEN)-byte
5//! [`Key`] — the same byte-string key type used for cnode slots. It is the
6//! routing key for `host_yield`: the kernel walks the call stack and the
7//! nearest snapshotted [`YieldReceiver`](KernelImage::YieldReceiver) containing
8//! the key catches the yield. Keys whose first byte is [`KERNEL_YIELD_NS`] are
9//! reserved `kernel:*` syscalls, caught by the kernel as the implicit ROOT
10//! YieldReceiver (bottom of the stack); chain/user yield_keys must not use that
11//! namespace.
12//!
13//! The two per-Instance kernel-assisted variants:
14//! - `YieldSender{yield_key}` — the EMIT right. The yield_key is packed into
15//!   the handle's `regs[0..1]` (same packing as `Gas{meter_key}`; see
16//!   [`key_to_regs`]).
17//! - `YieldReceiver{Set<Key>}` — the CATCH right. The catch-set is serialized
18//!   into the handle's `mem` DataCap so the kernel can short-circuit it during
19//!   routing. Wire form: `u16 count` then, per key, `u8 len` + `len` bytes
20//!   (the page-pad tail is ignored).
21
22use alloc::vec::Vec;
23
24use crate::NUM_REGS;
25use crate::cache::CapHashOrRef;
26use crate::cap::data::DataCap;
27use crate::cap::instance::InstanceCap;
28use crate::kernel_image::{KernelImage, kernel_image_hash, recognize_kernel_image};
29use crate::slot::{Key, key_from_regs, key_to_regs};
30
31/// Namespace marker (first byte) of a reserved `kernel:*` yield_key.
32pub const KERNEL_YIELD_NS: u8 = 0xCE;
33
34/// `kernel:mint_yield` — mint a (YieldSender, YieldReceiver) pair for a key.
35pub const YK_MINT_YIELD: [u8; 2] = [KERNEL_YIELD_NS, 0x01];
36/// `kernel:merge_yield_receiver` — union two YieldReceiver catch-sets.
37pub const YK_MERGE_YIELD_RECEIVER: [u8; 2] = [KERNEL_YIELD_NS, 0x02];
38/// `kernel:set_gas_meter` — set a meter, return previous.
39pub const YK_SET_GAS_METER: [u8; 2] = [KERNEL_YIELD_NS, 0x03];
40/// `kernel:mint_gas` — mint a `Gas{meter_key}` handle.
41pub const YK_MINT_GAS: [u8; 2] = [KERNEL_YIELD_NS, 0x04];
42/// `kernel:set_storage_quota` — set a quota, return previous.
43pub const YK_SET_STORAGE_QUOTA: [u8; 2] = [KERNEL_YIELD_NS, 0x05];
44/// `kernel:mint_quota` — mint a `Quota{quota_key}` handle.
45pub const YK_MINT_QUOTA: [u8; 2] = [KERNEL_YIELD_NS, 0x06];
46/// `kernel:oog` — kernel-injected on gas exhaustion.
47pub const YK_OOG: [u8; 2] = [KERNEL_YIELD_NS, 0x10];
48/// `kernel:storage_exhausted` — kernel-injected on quota exhaustion.
49pub const YK_STORAGE_EXHAUSTED: [u8; 2] = [KERNEL_YIELD_NS, 0x11];
50/// `kernel:attest` — attestation request (§15).
51pub const YK_ATTEST: [u8; 2] = [KERNEL_YIELD_NS, 0x20];
52
53/// True iff `key` is in the reserved `kernel:*` namespace (caught by the kernel
54/// as the implicit root receiver).
55pub fn is_kernel_yield_key(key: &Key) -> bool {
56    key.as_slice().first() == Some(&KERNEL_YIELD_NS)
57}
58
59/// Build a `YieldSender{yield_key}` unit handle: a `Cap::Instance` with the
60/// well-known YieldSender image-hash chain and the yield_key packed into
61/// `regs[0..1]`.
62pub fn yield_sender(yield_key: &Key) -> InstanceCap {
63    unit_handle(KernelImage::YieldSender, yield_key)
64}
65
66/// Read the `yield_key` from a `YieldSender` handle. `None` if `inst` is not a
67/// YieldSender.
68pub fn yield_sender_key(inst: &InstanceCap) -> Option<Key> {
69    unit_handle_key(KernelImage::YieldSender, inst)
70}
71
72/// Build a `YieldReceiver{keys}` unit handle: a `Cap::Instance` with the
73/// well-known YieldReceiver image-hash chain and the catch-set serialized into
74/// its `mem` DataCap. The set is normalized (sorted, deduped).
75pub fn yield_receiver(keys: &[Key]) -> InstanceCap {
76    let mut set: Vec<Key> = keys.to_vec();
77    set.sort();
78    set.dedup();
79    let mut bytes = Vec::new();
80    bytes.extend_from_slice(&(set.len() as u16).to_le_bytes());
81    for k in &set {
82        let s = k.as_slice();
83        bytes.push(s.len() as u8);
84        bytes.extend_from_slice(s);
85    }
86    InstanceCap {
87        image_hash_chain: kernel_image_hash(KernelImage::YieldReceiver),
88        image_hash: [0u8; 32],
89        root_cnode: CapHashOrRef::Hash([0u8; 32]),
90        mem: DataCap::from_bytes(&bytes),
91        regs: [0u64; NUM_REGS],
92        pc: 0,
93        gas_remaining: 0,
94    }
95}
96
97/// Read the catch-set from a `YieldReceiver` handle (normalized: sorted,
98/// deduped). `None` if `inst` is not a YieldReceiver; an empty/short mem
99/// decodes to an empty set.
100pub fn yield_receiver_keys(inst: &InstanceCap) -> Option<Vec<Key>> {
101    if recognize_kernel_image(inst.image_hash_chain) != Some(KernelImage::YieldReceiver) {
102        return None;
103    }
104    let len = inst.mem.content_len() as usize;
105    if len < 2 {
106        return Some(Vec::new());
107    }
108    let mut buf = alloc::vec![0u8; len];
109    inst.mem.copy_into(0, &mut buf);
110    let count = u16::from_le_bytes([buf[0], buf[1]]) as usize;
111    let mut keys = Vec::with_capacity(count);
112    let mut off = 2usize;
113    for _ in 0..count {
114        if off >= buf.len() {
115            break;
116        }
117        let klen = buf[off] as usize;
118        off += 1;
119        if off + klen > buf.len() {
120            break;
121        }
122        keys.push(Key::from(&buf[off..off + klen]));
123        off += klen;
124    }
125    Some(keys)
126}
127
128/// Union the catch-sets of two `YieldReceiver` handles (the
129/// `kernel:merge_yield_receiver` operation). `None` if either is not a
130/// YieldReceiver.
131pub fn merge_yield_receivers(a: &InstanceCap, b: &InstanceCap) -> Option<InstanceCap> {
132    let mut keys = yield_receiver_keys(a)?;
133    keys.extend(yield_receiver_keys(b)?);
134    Some(yield_receiver(&keys))
135}
136
137/// Build a `Gas{meter_key}` unit handle: a `Cap::Instance` with the well-known
138/// Gas image-hash chain and the `meter_key` packed into `regs[0..1]` (same
139/// packing as [`yield_sender`]). The kernel reads these handles from an
140/// Instance's ordered `gas_slots` to index the gas-meter mapping; minted by the
141/// `kernel:mint_gas` syscall.
142pub fn gas_handle(meter_key: &Key) -> InstanceCap {
143    unit_handle(KernelImage::Gas, meter_key)
144}
145
146/// Read the `meter_key` from a `Gas` handle. `None` if `inst` is not a Gas
147/// handle.
148pub fn gas_meter_key(inst: &InstanceCap) -> Option<Key> {
149    unit_handle_key(KernelImage::Gas, inst)
150}
151
152/// Build a `Quota{quota_key}` unit handle (storage-quota analogue of
153/// [`gas_handle`]); minted by the `kernel:mint_quota` syscall.
154pub fn quota_handle(quota_key: &Key) -> InstanceCap {
155    unit_handle(KernelImage::Quota, quota_key)
156}
157
158/// Read the `quota_key` from a `Quota` handle. `None` if `inst` is not a Quota
159/// handle.
160pub fn quota_key(inst: &InstanceCap) -> Option<Key> {
161    unit_handle_key(KernelImage::Quota, inst)
162}
163
164/// A kernel unit handle naming a single `Key` (`Gas{meter_key}` /
165/// `Quota{quota_key}` / `YieldSender{yield_key}`): a `Cap::Instance` carrying
166/// `image`'s well-known image-hash chain with the key packed into `regs[0..1]`.
167fn unit_handle(image: KernelImage, key: &Key) -> InstanceCap {
168    let (packed, len) = key_to_regs(key);
169    let mut regs = [0u64; NUM_REGS];
170    regs[0] = packed;
171    regs[1] = len;
172    InstanceCap {
173        image_hash_chain: kernel_image_hash(image),
174        image_hash: [0u8; 32],
175        root_cnode: CapHashOrRef::Hash([0u8; 32]),
176        mem: DataCap::empty(),
177        regs,
178        pc: 0,
179        gas_remaining: 0,
180    }
181}
182
183/// Read the packed key from a unit handle, requiring it to carry `image`'s
184/// image-hash chain.
185fn unit_handle_key(image: KernelImage, inst: &InstanceCap) -> Option<Key> {
186    if recognize_kernel_image(inst.image_hash_chain) != Some(image) {
187        return None;
188    }
189    Some(key_from_regs(inst.regs[0], inst.regs[1]))
190}