1use crate::error::RvfError;
8
9pub const KERNEL_MAGIC: u32 = 0x5256_4B4E;
11
12pub const KERNEL_FLAG_SIGNED: u32 = 1 << 8;
14pub const KERNEL_FLAG_COMPRESSED: u32 = 1 << 10;
16pub const KERNEL_FLAG_REQUIRES_TEE: u32 = 1 << 0;
18pub const KERNEL_FLAG_MEASURED: u32 = 1 << 9;
20pub const KERNEL_FLAG_REQUIRES_KVM: u32 = 1 << 1;
22pub const KERNEL_FLAG_REQUIRES_UEFI: u32 = 1 << 2;
24pub const KERNEL_FLAG_HAS_NETWORKING: u32 = 1 << 3;
26pub const KERNEL_FLAG_HAS_QUERY_API: u32 = 1 << 4;
28pub const KERNEL_FLAG_HAS_INGEST_API: u32 = 1 << 5;
30pub const KERNEL_FLAG_HAS_ADMIN_API: u32 = 1 << 6;
32pub const KERNEL_FLAG_ATTESTATION_READY: u32 = 1 << 7;
34pub const KERNEL_FLAG_RELOCATABLE: u32 = 1 << 11;
36pub const KERNEL_FLAG_HAS_VIRTIO_NET: u32 = 1 << 12;
38pub const KERNEL_FLAG_HAS_VIRTIO_BLK: u32 = 1 << 13;
40pub const KERNEL_FLAG_HAS_VSOCK: u32 = 1 << 14;
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46#[repr(u8)]
47pub enum KernelArch {
48 X86_64 = 0x00,
50 Aarch64 = 0x01,
52 Riscv64 = 0x02,
54 Universal = 0xFE,
56 Unknown = 0xFF,
58}
59
60impl TryFrom<u8> for KernelArch {
61 type Error = RvfError;
62
63 fn try_from(value: u8) -> Result<Self, Self::Error> {
64 match value {
65 0x00 => Ok(Self::X86_64),
66 0x01 => Ok(Self::Aarch64),
67 0x02 => Ok(Self::Riscv64),
68 0xFE => Ok(Self::Universal),
69 0xFF => Ok(Self::Unknown),
70 _ => Err(RvfError::InvalidEnumValue {
71 type_name: "KernelArch",
72 value: value as u64,
73 }),
74 }
75 }
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81#[repr(u8)]
82pub enum KernelType {
83 Hermit = 0x00,
85 MicroLinux = 0x01,
87 Asterinas = 0x02,
89 WasiPreview2 = 0x03,
91 Custom = 0x04,
93 TestStub = 0xFE,
95}
96
97impl TryFrom<u8> for KernelType {
98 type Error = RvfError;
99
100 fn try_from(value: u8) -> Result<Self, Self::Error> {
101 match value {
102 0x00 => Ok(Self::Hermit),
103 0x01 => Ok(Self::MicroLinux),
104 0x02 => Ok(Self::Asterinas),
105 0x03 => Ok(Self::WasiPreview2),
106 0x04 => Ok(Self::Custom),
107 0xFE => Ok(Self::TestStub),
108 _ => Err(RvfError::InvalidEnumValue {
109 type_name: "KernelType",
110 value: value as u64,
111 }),
112 }
113 }
114}
115
116#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
118#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
119#[repr(u8)]
120pub enum ApiTransport {
121 TcpHttp = 0x00,
123 TcpGrpc = 0x01,
125 Vsock = 0x02,
127 SharedMem = 0x03,
129 None = 0xFF,
131}
132
133impl TryFrom<u8> for ApiTransport {
134 type Error = RvfError;
135
136 fn try_from(value: u8) -> Result<Self, Self::Error> {
137 match value {
138 0x00 => Ok(Self::TcpHttp),
139 0x01 => Ok(Self::TcpGrpc),
140 0x02 => Ok(Self::Vsock),
141 0x03 => Ok(Self::SharedMem),
142 0xFF => Ok(Self::None),
143 _ => Err(RvfError::InvalidEnumValue {
144 type_name: "ApiTransport",
145 value: value as u64,
146 }),
147 }
148 }
149}
150
151#[derive(Clone, Copy, Debug)]
157#[repr(C)]
158pub struct KernelHeader {
159 pub kernel_magic: u32,
161 pub header_version: u16,
163 pub arch: u8,
165 pub kernel_type: u8,
167 pub kernel_flags: u32,
169 pub min_memory_mb: u32,
171 pub entry_point: u64,
173 pub image_size: u64,
175 pub compressed_size: u64,
177 pub compression: u8,
179 pub api_transport: u8,
181 pub api_port: u16,
183 pub api_version: u32,
185 pub image_hash: [u8; 32],
187 pub build_id: [u8; 16],
189 pub build_timestamp: u64,
191 pub vcpu_count: u32,
193 pub reserved_0: u32,
195 pub cmdline_offset: u64,
197 pub cmdline_length: u32,
199 pub reserved_1: u32,
201}
202
203const _: () = assert!(core::mem::size_of::<KernelHeader>() == 128);
205
206impl KernelHeader {
207 pub fn to_bytes(&self) -> [u8; 128] {
209 let mut buf = [0u8; 128];
210 buf[0x00..0x04].copy_from_slice(&self.kernel_magic.to_le_bytes());
211 buf[0x04..0x06].copy_from_slice(&self.header_version.to_le_bytes());
212 buf[0x06] = self.arch;
213 buf[0x07] = self.kernel_type;
214 buf[0x08..0x0C].copy_from_slice(&self.kernel_flags.to_le_bytes());
215 buf[0x0C..0x10].copy_from_slice(&self.min_memory_mb.to_le_bytes());
216 buf[0x10..0x18].copy_from_slice(&self.entry_point.to_le_bytes());
217 buf[0x18..0x20].copy_from_slice(&self.image_size.to_le_bytes());
218 buf[0x20..0x28].copy_from_slice(&self.compressed_size.to_le_bytes());
219 buf[0x28] = self.compression;
220 buf[0x29] = self.api_transport;
221 buf[0x2A..0x2C].copy_from_slice(&self.api_port.to_be_bytes());
222 buf[0x2C..0x30].copy_from_slice(&self.api_version.to_le_bytes());
223 buf[0x30..0x50].copy_from_slice(&self.image_hash);
224 buf[0x50..0x60].copy_from_slice(&self.build_id);
225 buf[0x60..0x68].copy_from_slice(&self.build_timestamp.to_le_bytes());
226 buf[0x68..0x6C].copy_from_slice(&self.vcpu_count.to_le_bytes());
227 buf[0x6C..0x70].copy_from_slice(&self.reserved_0.to_le_bytes());
228 buf[0x70..0x78].copy_from_slice(&self.cmdline_offset.to_le_bytes());
229 buf[0x78..0x7C].copy_from_slice(&self.cmdline_length.to_le_bytes());
230 buf[0x7C..0x80].copy_from_slice(&self.reserved_1.to_le_bytes());
231 buf
232 }
233
234 pub fn from_bytes(data: &[u8; 128]) -> Result<Self, RvfError> {
236 let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
237 if magic != KERNEL_MAGIC {
238 return Err(RvfError::BadMagic {
239 expected: KERNEL_MAGIC,
240 got: magic,
241 });
242 }
243
244 Ok(Self {
245 kernel_magic: magic,
246 header_version: u16::from_le_bytes([data[0x04], data[0x05]]),
247 arch: data[0x06],
248 kernel_type: data[0x07],
249 kernel_flags: u32::from_le_bytes([data[0x08], data[0x09], data[0x0A], data[0x0B]]),
250 min_memory_mb: u32::from_le_bytes([data[0x0C], data[0x0D], data[0x0E], data[0x0F]]),
251 entry_point: u64::from_le_bytes([
252 data[0x10], data[0x11], data[0x12], data[0x13],
253 data[0x14], data[0x15], data[0x16], data[0x17],
254 ]),
255 image_size: u64::from_le_bytes([
256 data[0x18], data[0x19], data[0x1A], data[0x1B],
257 data[0x1C], data[0x1D], data[0x1E], data[0x1F],
258 ]),
259 compressed_size: u64::from_le_bytes([
260 data[0x20], data[0x21], data[0x22], data[0x23],
261 data[0x24], data[0x25], data[0x26], data[0x27],
262 ]),
263 compression: data[0x28],
264 api_transport: data[0x29],
265 api_port: u16::from_be_bytes([data[0x2A], data[0x2B]]),
266 api_version: u32::from_le_bytes([data[0x2C], data[0x2D], data[0x2E], data[0x2F]]),
267 image_hash: {
268 let mut h = [0u8; 32];
269 h.copy_from_slice(&data[0x30..0x50]);
270 h
271 },
272 build_id: {
273 let mut id = [0u8; 16];
274 id.copy_from_slice(&data[0x50..0x60]);
275 id
276 },
277 build_timestamp: u64::from_le_bytes([
278 data[0x60], data[0x61], data[0x62], data[0x63],
279 data[0x64], data[0x65], data[0x66], data[0x67],
280 ]),
281 vcpu_count: u32::from_le_bytes([data[0x68], data[0x69], data[0x6A], data[0x6B]]),
282 reserved_0: u32::from_le_bytes([data[0x6C], data[0x6D], data[0x6E], data[0x6F]]),
283 cmdline_offset: u64::from_le_bytes([
284 data[0x70], data[0x71], data[0x72], data[0x73],
285 data[0x74], data[0x75], data[0x76], data[0x77],
286 ]),
287 cmdline_length: u32::from_le_bytes([data[0x78], data[0x79], data[0x7A], data[0x7B]]),
288 reserved_1: u32::from_le_bytes([data[0x7C], data[0x7D], data[0x7E], data[0x7F]]),
289 })
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 fn sample_header() -> KernelHeader {
298 KernelHeader {
299 kernel_magic: KERNEL_MAGIC,
300 header_version: 1,
301 arch: KernelArch::X86_64 as u8,
302 kernel_type: KernelType::Hermit as u8,
303 kernel_flags: KERNEL_FLAG_HAS_QUERY_API | KERNEL_FLAG_COMPRESSED,
304 min_memory_mb: 32,
305 entry_point: 0x0020_0000,
306 image_size: 400_000,
307 compressed_size: 180_000,
308 compression: 2, api_transport: ApiTransport::TcpHttp as u8,
310 api_port: 8080,
311 api_version: 1,
312 image_hash: [0xAB; 32],
313 build_id: [0xCD; 16],
314 build_timestamp: 1_700_000_000_000_000_000,
315 vcpu_count: 1,
316 reserved_0: 0,
317 cmdline_offset: 128,
318 cmdline_length: 64,
319 reserved_1: 0,
320 }
321 }
322
323 #[test]
324 fn header_size_is_128() {
325 assert_eq!(core::mem::size_of::<KernelHeader>(), 128);
326 }
327
328 #[test]
329 fn magic_bytes_match_ascii() {
330 let bytes_be = KERNEL_MAGIC.to_be_bytes();
331 assert_eq!(&bytes_be, b"RVKN");
332 }
333
334 #[test]
335 fn round_trip_serialization() {
336 let original = sample_header();
337 let bytes = original.to_bytes();
338 let decoded = KernelHeader::from_bytes(&bytes).expect("from_bytes should succeed");
339
340 assert_eq!(decoded.kernel_magic, KERNEL_MAGIC);
341 assert_eq!(decoded.header_version, 1);
342 assert_eq!(decoded.arch, KernelArch::X86_64 as u8);
343 assert_eq!(decoded.kernel_type, KernelType::Hermit as u8);
344 assert_eq!(decoded.kernel_flags, KERNEL_FLAG_HAS_QUERY_API | KERNEL_FLAG_COMPRESSED);
345 assert_eq!(decoded.min_memory_mb, 32);
346 assert_eq!(decoded.entry_point, 0x0020_0000);
347 assert_eq!(decoded.image_size, 400_000);
348 assert_eq!(decoded.compressed_size, 180_000);
349 assert_eq!(decoded.compression, 2);
350 assert_eq!(decoded.api_transport, ApiTransport::TcpHttp as u8);
351 assert_eq!(decoded.api_port, 8080);
352 assert_eq!(decoded.api_version, 1);
353 assert_eq!(decoded.image_hash, [0xAB; 32]);
354 assert_eq!(decoded.build_id, [0xCD; 16]);
355 assert_eq!(decoded.build_timestamp, 1_700_000_000_000_000_000);
356 assert_eq!(decoded.vcpu_count, 1);
357 assert_eq!(decoded.reserved_0, 0);
358 assert_eq!(decoded.cmdline_offset, 128);
359 assert_eq!(decoded.cmdline_length, 64);
360 assert_eq!(decoded.reserved_1, 0);
361 }
362
363 #[test]
364 fn bad_magic_returns_error() {
365 let mut bytes = sample_header().to_bytes();
366 bytes[0] = 0x00; let err = KernelHeader::from_bytes(&bytes).unwrap_err();
368 match err {
369 RvfError::BadMagic { expected, .. } => assert_eq!(expected, KERNEL_MAGIC),
370 other => panic!("expected BadMagic, got {other:?}"),
371 }
372 }
373
374 #[test]
375 fn field_offsets() {
376 let h = sample_header();
377 let base = &h as *const _ as usize;
378
379 assert_eq!(&h.kernel_magic as *const _ as usize - base, 0x00);
380 assert_eq!(&h.header_version as *const _ as usize - base, 0x04);
381 assert_eq!(&h.arch as *const _ as usize - base, 0x06);
382 assert_eq!(&h.kernel_type as *const _ as usize - base, 0x07);
383 assert_eq!(&h.kernel_flags as *const _ as usize - base, 0x08);
384 assert_eq!(&h.min_memory_mb as *const _ as usize - base, 0x0C);
385 assert_eq!(&h.entry_point as *const _ as usize - base, 0x10);
386 assert_eq!(&h.image_size as *const _ as usize - base, 0x18);
387 assert_eq!(&h.compressed_size as *const _ as usize - base, 0x20);
388 assert_eq!(&h.compression as *const _ as usize - base, 0x28);
389 assert_eq!(&h.api_transport as *const _ as usize - base, 0x29);
390 assert_eq!(&h.api_port as *const _ as usize - base, 0x2A);
391 assert_eq!(&h.api_version as *const _ as usize - base, 0x2C);
392 assert_eq!(&h.image_hash as *const _ as usize - base, 0x30);
393 assert_eq!(&h.build_id as *const _ as usize - base, 0x50);
394 assert_eq!(&h.build_timestamp as *const _ as usize - base, 0x60);
395 assert_eq!(&h.vcpu_count as *const _ as usize - base, 0x68);
396 assert_eq!(&h.reserved_0 as *const _ as usize - base, 0x6C);
397 assert_eq!(&h.cmdline_offset as *const _ as usize - base, 0x70);
398 assert_eq!(&h.cmdline_length as *const _ as usize - base, 0x78);
399 assert_eq!(&h.reserved_1 as *const _ as usize - base, 0x7C);
400 }
401
402 #[test]
403 fn kernel_arch_try_from() {
404 assert_eq!(KernelArch::try_from(0x00), Ok(KernelArch::X86_64));
405 assert_eq!(KernelArch::try_from(0x01), Ok(KernelArch::Aarch64));
406 assert_eq!(KernelArch::try_from(0x02), Ok(KernelArch::Riscv64));
407 assert_eq!(KernelArch::try_from(0xFE), Ok(KernelArch::Universal));
408 assert_eq!(KernelArch::try_from(0xFF), Ok(KernelArch::Unknown));
409 assert!(KernelArch::try_from(0x03).is_err());
410 assert!(KernelArch::try_from(0x80).is_err());
411 }
412
413 #[test]
414 fn kernel_type_try_from() {
415 assert_eq!(KernelType::try_from(0x00), Ok(KernelType::Hermit));
416 assert_eq!(KernelType::try_from(0x01), Ok(KernelType::MicroLinux));
417 assert_eq!(KernelType::try_from(0x02), Ok(KernelType::Asterinas));
418 assert_eq!(KernelType::try_from(0x03), Ok(KernelType::WasiPreview2));
419 assert_eq!(KernelType::try_from(0x04), Ok(KernelType::Custom));
420 assert_eq!(KernelType::try_from(0xFE), Ok(KernelType::TestStub));
421 assert!(KernelType::try_from(0x05).is_err());
422 assert!(KernelType::try_from(0xFF).is_err());
423 }
424
425 #[test]
426 fn api_transport_try_from() {
427 assert_eq!(ApiTransport::try_from(0x00), Ok(ApiTransport::TcpHttp));
428 assert_eq!(ApiTransport::try_from(0x01), Ok(ApiTransport::TcpGrpc));
429 assert_eq!(ApiTransport::try_from(0x02), Ok(ApiTransport::Vsock));
430 assert_eq!(ApiTransport::try_from(0x03), Ok(ApiTransport::SharedMem));
431 assert_eq!(ApiTransport::try_from(0xFF), Ok(ApiTransport::None));
432 assert!(ApiTransport::try_from(0x04).is_err());
433 assert!(ApiTransport::try_from(0x80).is_err());
434 }
435
436 #[test]
437 fn kernel_flags_bit_positions() {
438 assert_eq!(KERNEL_FLAG_REQUIRES_TEE, 0x0001);
439 assert_eq!(KERNEL_FLAG_REQUIRES_KVM, 0x0002);
440 assert_eq!(KERNEL_FLAG_REQUIRES_UEFI, 0x0004);
441 assert_eq!(KERNEL_FLAG_HAS_NETWORKING, 0x0008);
442 assert_eq!(KERNEL_FLAG_HAS_QUERY_API, 0x0010);
443 assert_eq!(KERNEL_FLAG_HAS_INGEST_API, 0x0020);
444 assert_eq!(KERNEL_FLAG_HAS_ADMIN_API, 0x0040);
445 assert_eq!(KERNEL_FLAG_ATTESTATION_READY, 0x0080);
446 assert_eq!(KERNEL_FLAG_SIGNED, 0x0100);
447 assert_eq!(KERNEL_FLAG_MEASURED, 0x0200);
448 assert_eq!(KERNEL_FLAG_COMPRESSED, 0x0400);
449 assert_eq!(KERNEL_FLAG_RELOCATABLE, 0x0800);
450 assert_eq!(KERNEL_FLAG_HAS_VIRTIO_NET, 0x1000);
451 assert_eq!(KERNEL_FLAG_HAS_VIRTIO_BLK, 0x2000);
452 assert_eq!(KERNEL_FLAG_HAS_VSOCK, 0x4000);
453 }
454
455 #[test]
456 fn api_port_network_byte_order() {
457 let mut h = sample_header();
458 h.api_port = 0x1F90; let bytes = h.to_bytes();
460 assert_eq!(bytes[0x2A], 0x1F);
462 assert_eq!(bytes[0x2B], 0x90);
463 let decoded = KernelHeader::from_bytes(&bytes).unwrap();
464 assert_eq!(decoded.api_port, 0x1F90);
465 }
466
467 #[test]
468 fn zero_filled_reserved_fields() {
469 let h = sample_header();
470 let bytes = h.to_bytes();
471 assert_eq!(&bytes[0x6C..0x70], &[0, 0, 0, 0]);
473 assert_eq!(&bytes[0x7C..0x80], &[0, 0, 0, 0]);
475 }
476}