1use crate::cpu::memory;
2use std::collections::HashMap;
3use std::path::{Path, PathBuf};
4use std::sync::{Mutex, OnceLock};
5
6const DESC_NEXT: u16 = 1;
7const DESC_WRITE: u16 = 2;
8const VIRTIO_9P_COMMON: i32 = 0xA800;
9const VIRTIO_9P_NOTIFY: i32 = 0xA900;
10const VIRTIO_9P_ISR: i32 = 0xA700;
11const VIRTIO_9P_CONFIG: i32 = 0xA600;
12const PCI_CONFIG_ADDRESS: i32 = 0xCF8;
13const PCI_CONFIG_DATA: i32 = 0xCFC;
14
15#[derive(Clone, Default)]
16struct Queue {
17 size: u16,
18 enabled: bool,
19 desc: u32,
20 avail: u32,
21 avail_last: u16,
22 used: u32,
23 staged: u16,
24}
25
26#[derive(Clone, Default)]
27struct Fid {
28 path: PathBuf,
29 opened: bool,
30}
31
32struct Virtio9p {
33 queue: Queue,
34 status: u8,
35 isr: u8,
36 device_feature_select: u32,
37 driver_feature_select: u32,
38 driver_feature: u32,
39 queue_select: u16,
40 tag: Vec<u8>,
41 root: Option<PathBuf>,
42 fids: HashMap<u32, Fid>,
43}
44
45impl Default for Virtio9p {
46 fn default() -> Self {
47 Self {
48 queue: Queue {
49 size: 32,
50 ..Queue::default()
51 },
52 status: 0,
53 isr: 0,
54 device_feature_select: 0,
55 driver_feature_select: 0,
56 driver_feature: 0,
57 queue_select: 0,
58 tag: b"host9p".to_vec(),
59 root: None,
60 fids: HashMap::new(),
61 }
62 }
63}
64
65#[derive(Default)]
66struct DeviceBus {
67 ninep: Virtio9p,
68 pci_address: u32,
69}
70static BUS: OnceLock<Mutex<DeviceBus>> = OnceLock::new();
71
72fn bus() -> &'static Mutex<DeviceBus> {
73 BUS.get_or_init(|| {
74 Mutex::new(DeviceBus {
75 ninep: Virtio9p::default(),
76 pci_address: 0,
77 })
78 })
79}
80
81pub fn set_9p_root(path: impl AsRef<Path>) -> Result<(), String> {
82 let path = path
83 .as_ref()
84 .canonicalize()
85 .map_err(|e| format!("9p root {}: {e}", path.as_ref().display()))?;
86 let mut b = bus().lock().map_err(|_| "device bus poisoned".to_owned())?;
87 b.ninep.root = Some(path.clone());
88 b.ninep.fids.insert(
89 0,
90 Fid {
91 path,
92 opened: false,
93 },
94 );
95 Ok(())
96}
97
98pub fn restore_state(state: &serde_json::Value, buffers: &[Vec<u8>]) -> Result<(), String> {
99 let slots = state.as_array().ok_or("v86 state is not an array")?;
100 let mut b = bus().lock().map_err(|_| "device bus poisoned".to_owned())?;
101 let Some(s) = slots.get(45).and_then(|v| v.as_array()) else {
102 return Ok(());
103 };
104 if let Some(tag) = s.get(0).and_then(|v| v.as_array()) {
105 b.ninep.tag = tag
106 .iter()
107 .filter_map(|v| v.as_u64().map(|x| x as u8))
108 .collect();
109 }
110 if let Some(v) = s.get(2).and_then(|v| v.as_array()) {
111 if let Some(q) = v.get(10).and_then(|v| v.as_array()) {
112 b.ninep.queue.size = q.first().and_then(|v| v.as_u64()).unwrap_or(32) as u16;
113 b.ninep.queue.enabled = q.get(2).and_then(|v| v.as_bool()).unwrap_or(false);
114 b.ninep.queue.desc = q.get(4).and_then(|v| v.as_u64()).unwrap_or(0) as u32;
115 b.ninep.queue.avail = q.get(5).and_then(|v| v.as_u64()).unwrap_or(0) as u32;
116 b.ninep.queue.avail_last = q.get(6).and_then(|v| v.as_u64()).unwrap_or(0) as u16;
117 b.ninep.queue.used = q.get(7).and_then(|v| v.as_u64()).unwrap_or(0) as u32;
118 }
119 }
120 b.ninep.fids.clear();
121 if let Some(root) = b.ninep.root.clone() {
126 b.ninep.fids.insert(
127 0,
128 Fid {
129 path: root,
130 opened: false,
131 },
132 );
133 } else if let Some(fids) = s.get(8).and_then(|v| v.as_array()) {
134 for (id, fid) in fids.iter().enumerate() {
137 let inode = fid.get(0).and_then(|v| v.as_i64()).unwrap_or(0);
138 b.ninep.fids.insert(
139 id as u32,
140 Fid {
141 path: PathBuf::from(format!("inode-{inode}")),
142 opened: false,
143 },
144 );
145 }
146 }
147 let _ = buffers;
148 Ok(())
149}
150
151pub fn io_read8(port: i32) -> Option<i32> {
152 if (VIRTIO_9P_ISR..VIRTIO_9P_ISR + 4).contains(&port) {
153 let value = {
154 let mut b = bus().lock().ok()?;
155 let value = b.ninep.isr;
156 b.ninep.isr = 0;
157 value
158 };
159 unsafe { crate::cpu::cpu::device_lower_irq(9) };
160 return Some(value as i32);
161 }
162 let b = bus().lock().ok()?;
163 if (PCI_CONFIG_ADDRESS..PCI_CONFIG_ADDRESS + 4).contains(&port) {
164 return Some(((b.pci_address >> (8 * (port - PCI_CONFIG_ADDRESS))) & 0xFF) as i32);
165 }
166 if (PCI_CONFIG_DATA..PCI_CONFIG_DATA + 4).contains(&port) {
167 let value = pci_config_read(b.pci_address);
168 return Some(((value >> (8 * (port - PCI_CONFIG_DATA))) & 0xFF) as i32);
169 }
170 if (VIRTIO_9P_CONFIG..VIRTIO_9P_CONFIG + 8).contains(&port) {
171 let off = port - VIRTIO_9P_CONFIG;
172 return Some(if off < b.ninep.tag.len() as i32 {
173 b.ninep.tag[off as usize] as i32
174 } else {
175 0
176 });
177 }
178 None
179}
180
181pub fn mmio_read8(addr: u32) -> Option<i32> {
182 io_read8(addr as i32)
183}
184
185pub fn mmio_read32(addr: u32) -> Option<i32> {
186 io_read32(addr as i32)
187}
188
189pub fn mmio_write8(addr: u32, value: i32) -> bool {
190 io_write8(addr as i32, value)
191}
192
193pub fn mmio_write16(addr: u32, value: i32) -> bool {
194 io_write16(addr as i32, value)
195}
196
197pub fn mmio_write32(addr: u32, value: i32) -> bool {
198 io_write32(addr as i32, value)
199}
200
201pub fn io_read16(port: i32) -> Option<i32> {
202 let b = bus().lock().ok()?;
203 if (VIRTIO_9P_COMMON..VIRTIO_9P_COMMON + 0x40).contains(&port) {
204 let off = port - VIRTIO_9P_COMMON;
205 return Some(match off {
206 20 => b.ninep.status as i32,
207 22 => b.ninep.queue_select as i32,
208 24 => b.ninep.queue.size as i32,
209 26 => 0xFFFF,
210 28 => b.ninep.queue.enabled as i32,
211 30 => 0,
212 _ => 0,
213 });
214 }
215 if (PCI_CONFIG_ADDRESS..PCI_CONFIG_ADDRESS + 3).contains(&port) {
216 return Some(((b.pci_address >> (8 * (port - PCI_CONFIG_ADDRESS))) & 0xFFFF) as i32);
217 }
218 if (PCI_CONFIG_DATA..PCI_CONFIG_DATA + 3).contains(&port) {
219 let value = pci_config_read(b.pci_address);
220 return Some(((value >> (8 * (port - PCI_CONFIG_DATA))) & 0xFFFF) as i32);
221 }
222 drop(b);
223 io_read32(port)
224}
225
226pub fn io_read32(port: i32) -> Option<i32> {
227 let b = bus().lock().ok()?;
228 if (VIRTIO_9P_COMMON..VIRTIO_9P_COMMON + 0x40).contains(&port) {
229 let off = port - VIRTIO_9P_COMMON;
230 return Some(match off {
231 0 => 0,
232 4 => 0,
233 8 => b.ninep.driver_feature_select as i32,
234 12 => b.ninep.driver_feature as i32,
235 20 => b.ninep.status as i32,
236 24 => b.ninep.queue.size as i32,
237 32 => b.ninep.queue.desc as i32,
238 40 => b.ninep.queue.avail as i32,
239 48 => b.ninep.queue.used as i32,
240 _ => 0,
241 });
242 }
243 if port == PCI_CONFIG_DATA {
244 return Some(pci_config_read(b.pci_address) as i32);
245 }
246 if (VIRTIO_9P_COMMON..VIRTIO_9P_COMMON + 0x100).contains(&port) {
247 let off = port - VIRTIO_9P_COMMON;
248 return Some(match off {
249 0 => 0,
250 4 => 0,
251 8 => 0,
252 12 => 0,
253 20 => b.ninep.status as i32,
254 24 => 1,
255 _ => 0,
256 });
257 }
258 None
259}
260
261pub fn io_write8(port: i32, value: i32) -> bool {
262 if port == VIRTIO_9P_ISR {
263 if let Ok(mut b) = bus().lock() {
264 b.ninep.isr = 0;
265 }
266 return true;
267 }
268 if (VIRTIO_9P_COMMON..VIRTIO_9P_COMMON + 0x100).contains(&port) {
269 if let Ok(mut b) = bus().lock() {
270 if port - VIRTIO_9P_COMMON == 20 {
271 b.ninep.status = value as u8;
272 }
273 }
274 return true;
275 }
276 false
277}
278
279pub fn io_write16(port: i32, value: i32) -> bool {
280 if port == VIRTIO_9P_NOTIFY {
281 process_queue();
282 return true;
283 }
284 if let Ok(mut b) = bus().lock() {
285 if (VIRTIO_9P_COMMON..VIRTIO_9P_COMMON + 0x40).contains(&port) {
286 match port - VIRTIO_9P_COMMON {
287 22 => b.ninep.queue_select = value as u16,
288 24 => b.ninep.queue.size = (value as u16).min(32),
289 28 => b.ninep.queue.enabled = value as u16 == 1,
290 _ => {}
291 }
292 return true;
293 }
294 }
295 false
296}
297
298pub fn io_write32(port: i32, value: i32) -> bool {
299 if let Ok(mut b) = bus().lock() {
300 if port == PCI_CONFIG_ADDRESS {
301 b.pci_address = value as u32;
302 return true;
303 }
304 if (VIRTIO_9P_COMMON..VIRTIO_9P_COMMON + 0x40).contains(&port) {
305 match port - VIRTIO_9P_COMMON {
306 0 => b.ninep.device_feature_select = value as u32,
307 8 => b.ninep.driver_feature_select = value as u32,
308 12 => b.ninep.driver_feature = value as u32,
309 32 => b.ninep.queue.desc = value as u32,
310 40 => b.ninep.queue.avail = value as u32,
311 48 => b.ninep.queue.used = value as u32,
312 _ => {}
313 }
314 return true;
315 }
316 }
317 io_write16(port, 0)
318}
319
320fn pci_config_read(address: u32) -> u32 {
321 if address & 0x8000_0000 == 0 || (address >> 11) & 0x1F != 0 {
322 return 0xFFFF_FFFF;
323 }
324 let offset = ((address >> 2) & 0x3F) * 4;
325 match offset {
326 0x00 => 0x1009_1AF4,
327 0x08 => 0x0180_0000,
328 0x10 => 0x0000_A001,
329 0x14 => 0x0000_A101,
330 0x18 => 0x0000_A201,
331 0x1C => 0x0000_A301,
332 0x34 => 0x0000_0040,
333 0x40 => 0x0110_5009,
335 0x44 => 0,
336 0x48 => 0,
337 0x4C => 0x0000_0038,
338 0x50 => 0x0214_6009,
341 0x54 => 1,
342 0x58 => 0,
343 0x5C => 0x0000_0020,
344 0x60 => 4,
345 0x64 => 0x0310_7409,
347 0x68 => 2,
348 0x6C => 0,
349 0x70 => 1,
350 0x74 => 0x0410_8409,
352 0x78 => 3,
353 0x7C => 0,
354 0x80 => 0x0000_0100,
355 0x84 => 0x0510_0009,
357 0x88 => 0,
358 0x8C => 0,
359 0x90 => 0,
360 0x94 => 0,
361 0x98 => 0,
362 0x9C => 0,
363 0xA0 => 0,
364 0xA4 => 0,
365 0xA8 => 0,
366 0xAC => 0,
367 0xB0 => 0,
368 0xB4 => 0,
369 0xB8 => 0,
370 0xBC => 0,
371 0xC0 => 0,
372 0xC4 => 0,
373 0xC8 => 0,
374 0xCC => 0,
375 0xD0 => 0,
376 0xD4 => 0,
377 0xD8 => 0,
378 0xDC => 0,
379 0xE0 => 0,
380 0xE4 => 0,
381 0xE8 => 0,
382 0xEC => 0,
383 0xF0 => 0,
384 0xF4 => 0,
385 0xF8 => 0,
386 0xFC => 0,
387 _ => 0,
388 }
389}
390
391fn read8(addr: u32) -> u8 {
392 unsafe { memory::read8_no_mmap_check(addr) as u8 }
393}
394fn read16(addr: u32) -> u16 {
395 unsafe { memory::read16_no_mmap_check(addr) as u16 }
396}
397fn read32(addr: u32) -> u32 {
398 unsafe { memory::read32_no_mmap_check(addr) as u32 }
399}
400fn write16(addr: u32, value: u16) {
401 unsafe { memory::write16_no_mmap_or_dirty_check(addr, value as i32) }
402}
403fn write32(addr: u32, value: u32) {
404 unsafe { memory::write32_no_mmap_or_dirty_check(addr, value as i32) }
405}
406
407fn process_queue() {
408 let mut b = match bus().lock() {
409 Ok(x) => x,
410 Err(_) => return,
411 };
412 let mut q = b.ninep.queue.clone();
413 if !q.enabled || q.desc == 0 || q.avail == 0 || q.used == 0 {
414 return;
415 }
416 let avail_idx = read16(q.avail + 2);
417 while q.avail_last != avail_idx {
418 let head = read16(q.avail + 4 + 2u32 * (q.avail_last & q.size.saturating_sub(1)) as u32);
419 let mut request = Vec::new();
420 let mut writable = Vec::new();
421 let mut idx = head;
422 for _ in 0..q.size {
423 let p = q.desc + idx as u32 * 16;
424 let addr = read32(p);
425 let len = read32(p + 8);
426 let flags = read16(p + 12);
427 let next = read16(p + 14);
428 let mut buf = vec![0u8; len as usize];
429 for (i, x) in buf.iter_mut().enumerate() {
430 *x = read8(addr + i as u32);
431 }
432 if flags & DESC_WRITE != 0 {
433 writable.push((addr, len));
434 } else {
435 request.extend_from_slice(&buf);
436 }
437 if flags & DESC_NEXT == 0 {
438 break;
439 }
440 idx = next;
441 }
442 let reply = handle_9p(&mut b.ninep, &request);
443 let mut pos = 0usize;
444 let mut written = 0u32;
445 for (addr, len) in writable {
446 let n = (len as usize).min(reply.len().saturating_sub(pos));
447 for i in 0..n {
448 unsafe {
449 memory::write8_no_mmap_or_dirty_check(addr + i as u32, reply[pos + i] as i32);
450 }
451 }
452 pos += n;
453 written += n as u32;
454 }
455 let used_idx = read16(q.used + 2);
456 let ring_off = 8u32 * (used_idx & (q.size - 1)) as u32;
457 write32(q.used + 4 + ring_off, head as u32);
458 write32(q.used + 8 + ring_off, written);
459 write16(q.used + 2, used_idx.wrapping_add(1));
460 q.avail_last = q.avail_last.wrapping_add(1);
461 b.ninep.isr |= 1;
462 unsafe { crate::cpu::cpu::device_raise_irq(9) };
465 }
466 b.ninep.queue.avail_last = q.avail_last;
467}
468
469fn u16_at(x: &[u8], p: &mut usize) -> u16 {
470 let v = u16::from_le_bytes([x[*p], x[*p + 1]]);
471 *p += 2;
472 v
473}
474fn u32_at(x: &[u8], p: &mut usize) -> u32 {
475 let v = u32::from_le_bytes(x[*p..*p + 4].try_into().unwrap());
476 *p += 4;
477 v
478}
479fn u64_at(x: &[u8], p: &mut usize) -> u64 {
480 let v = u64::from_le_bytes(x[*p..*p + 8].try_into().unwrap());
481 *p += 8;
482 v
483}
484fn string_at(x: &[u8], p: &mut usize) -> String {
485 let n = u16_at(x, p) as usize;
486 let e = (*p + n).min(x.len());
487 let s = String::from_utf8_lossy(&x[*p..e]).into_owned();
488 *p = e;
489 s
490}
491fn string_put(out: &mut Vec<u8>, value: &str) {
492 let bytes = value.as_bytes();
493 let len = bytes.len().min(u16::MAX as usize) as u16;
494 out.extend_from_slice(&len.to_le_bytes());
495 out.extend_from_slice(&bytes[..len as usize]);
496}
497fn reply(id: u8, tag: u16, payload: &[u8]) -> Vec<u8> {
498 let mut r = Vec::with_capacity(payload.len() + 7);
499 r.extend_from_slice(&((payload.len() + 7) as u32).to_le_bytes());
500 r.push(id + 1);
501 r.extend_from_slice(&tag.to_le_bytes());
502 r.extend_from_slice(payload);
503 r
504}
505
506fn append_qid(out: &mut Vec<u8>, path: &Path, metadata: &std::fs::Metadata) {
507 use std::hash::{Hash, Hasher};
508 let mut hasher = std::collections::hash_map::DefaultHasher::new();
509 path.to_string_lossy().hash(&mut hasher);
510 let qid_path = hasher.finish();
511 let qid_type = if metadata.is_dir() { 0x80 } else { 0 };
512 out.push(qid_type);
513 out.extend_from_slice(&0u32.to_le_bytes());
514 out.extend_from_slice(&qid_path.to_le_bytes());
515}
516
517fn append_getattr(out: &mut Vec<u8>, path: &Path, metadata: &std::fs::Metadata) {
518 out.extend_from_slice(&0x1FFFu64.to_le_bytes());
523 append_qid(out, path, metadata);
524 let mut mode = if metadata.permissions().readonly() {
525 0o444u32
526 } else {
527 0o666u32
528 };
529 if metadata.is_dir() {
530 mode = 0o755;
531 mode |= 0x8000_0000;
532 }
533 out.extend_from_slice(&mode.to_le_bytes());
534 out.extend_from_slice(&0u32.to_le_bytes());
535 out.extend_from_slice(&0u32.to_le_bytes());
536 out.extend_from_slice(&1u64.to_le_bytes());
537 out.extend_from_slice(&0u64.to_le_bytes());
538 out.extend_from_slice(&metadata.len().to_le_bytes());
539 out.extend_from_slice(&8192u64.to_le_bytes());
540 out.extend_from_slice(&metadata.len().div_ceil(512).to_le_bytes());
541 for _ in 0..8 {
542 out.extend_from_slice(&0u64.to_le_bytes());
543 }
544}
545
546fn handle_9p(dev: &mut Virtio9p, req: &[u8]) -> Vec<u8> {
547 if req.len() < 7 {
548 return reply(6, 0, &2u32.to_le_bytes());
549 }
550 let mut p = 4;
551 let id = req[p];
552 p += 1;
553 let tag = u16_at(req, &mut p);
554 match id {
555 100 => {
556 let m = u32_at(req, &mut p);
557 let _version = string_at(req, &mut p);
558 let mut out = Vec::new();
559 out.extend_from_slice(&m.min(8192).to_le_bytes());
560 out.extend_from_slice(&6u16.to_le_bytes());
561 out.extend_from_slice(b"9P2000.L");
562 reply(id, tag, &out)
563 }
564 104 => {
565 let fid = u32_at(req, &mut p);
566 let _afid = u32_at(req, &mut p);
567 let _uname = string_at(req, &mut p);
568 let _aname = string_at(req, &mut p);
569 let root = dev.root.clone().unwrap_or_else(|| PathBuf::from("."));
570 dev.fids.insert(
571 fid,
572 Fid {
573 path: root,
574 opened: false,
575 },
576 );
577 let mut out = vec![0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
578 reply(id, tag, &out)
579 }
580 110 => {
581 let fid = u32_at(req, &mut p);
582 let newfid = u32_at(req, &mut p);
583 let nwname = u16_at(req, &mut p);
584 let base = dev
585 .fids
586 .get(&fid)
587 .map(|f| f.path.clone())
588 .unwrap_or_default();
589 let mut path = base;
590 let mut qids = Vec::new();
591 for _ in 0..nwname {
592 let name = string_at(req, &mut p);
593 path.push(&name);
594 if !path.exists() {
595 return reply(6, tag, &2u32.to_le_bytes());
596 }
597 qids.extend_from_slice(&[0u8; 13]);
598 }
599 dev.fids.insert(
600 newfid,
601 Fid {
602 path,
603 opened: false,
604 },
605 );
606 let mut out = (nwname as u16).to_le_bytes().to_vec();
607 out.extend_from_slice(&qids);
608 reply(id, tag, &out)
609 }
610 12 | 112 => {
611 let fid = u32_at(req, &mut p);
612 dev.fids.entry(fid).or_default().opened = true;
613 let mut out = vec![0u8; 13];
614 out.extend_from_slice(&8192u32.to_le_bytes());
615 reply(id, tag, &out)
616 }
617 24 => {
618 let fid = u32_at(req, &mut p);
619 let _request_mask = u64_at(req, &mut p);
620 let Some(path) = dev.fids.get(&fid).map(|f| f.path.clone()) else {
621 return reply(6, tag, &2u32.to_le_bytes());
622 };
623 let Ok(metadata) = std::fs::symlink_metadata(&path) else {
624 return reply(6, tag, &2u32.to_le_bytes());
625 };
626 let mut out = Vec::with_capacity(200);
627 append_getattr(&mut out, &path, &metadata);
628 reply(id, tag, &out)
629 }
630 40 => {
631 let fid = u32_at(req, &mut p);
632 let offset = u64_at(req, &mut p) as usize;
633 let count = u32_at(req, &mut p) as usize;
634 let Some(path) = dev.fids.get(&fid).map(|f| f.path.clone()) else {
635 return reply(6, tag, &2u32.to_le_bytes());
636 };
637 let Ok(entries) = std::fs::read_dir(&path) else {
638 return reply(6, tag, &2u32.to_le_bytes());
639 };
640 let mut payload = Vec::new();
641 for (index, entry) in entries.flatten().enumerate().skip(offset) {
642 let entry_path = entry.path();
643 let Ok(metadata) = entry.metadata() else {
644 continue;
645 };
646 let mut item = Vec::new();
647 append_qid(&mut item, &entry_path, &metadata);
648 item.extend_from_slice(&((index + 1) as u64).to_le_bytes());
649 item.push(if metadata.is_dir() { 4 } else { 8 });
650 let name = entry.file_name().to_string_lossy().into_owned();
651 string_put(&mut item, &name);
652 if payload.len() + item.len() > count {
653 break;
654 }
655 payload.extend_from_slice(&item);
656 }
657 let mut out = (payload.len() as u32).to_le_bytes().to_vec();
658 out.extend_from_slice(&payload);
659 reply(id, tag, &out)
660 }
661 120 => {
662 let fid = u32_at(req, &mut p);
663 dev.fids.remove(&fid);
664 reply(id, tag, &[])
665 }
666 8 => {
667 let mut out = Vec::new();
668 out.extend_from_slice(&0x01021997u32.to_le_bytes());
669 out.extend_from_slice(&8192u32.to_le_bytes());
670 out.extend_from_slice(&1_000_000u64.to_le_bytes());
671 out.extend_from_slice(&900_000u64.to_le_bytes());
672 out.extend_from_slice(&900_000u64.to_le_bytes());
673 out.extend_from_slice(&1_000_000u64.to_le_bytes());
674 out.extend_from_slice(&900_000u64.to_le_bytes());
675 out.extend_from_slice(&0u64.to_le_bytes());
676 out.extend_from_slice(&256u16.to_le_bytes());
677 reply(id, tag, &out)
678 }
679 116 => {
680 let fid = u32_at(req, &mut p);
681 let off = u64::from_le_bytes(req[p..p + 8].try_into().unwrap());
682 p += 8;
683 let count = u32_at(req, &mut p) as usize;
684 let path = dev.fids.get(&fid).map(|f| f.path.clone());
685 let mut data = Vec::new();
686 if let Some(path) = path {
687 if let Ok(mut f) = std::fs::File::open(path) {
688 use std::io::{Read, Seek};
689 let _ = f.seek(std::io::SeekFrom::Start(off));
690 let _ = f.take(count as u64).read_to_end(&mut data);
691 }
692 }
693 let mut out = (data.len() as u32).to_le_bytes().to_vec();
694 out.extend_from_slice(&data);
695 reply(id, tag, &out)
696 }
697 _ => reply(6, tag, &2u32.to_le_bytes()),
698 }
699}
700
701#[cfg(test)]
702mod tests {
703 use super::*;
704
705 fn request(id: u8, tag: u16, payload: &[u8]) -> Vec<u8> {
706 let mut r = Vec::new();
707 r.extend_from_slice(&((payload.len() + 7) as u32).to_le_bytes());
708 r.push(id);
709 r.extend_from_slice(&tag.to_le_bytes());
710 r.extend_from_slice(payload);
711 r
712 }
713 fn string(value: &str) -> Vec<u8> {
714 let mut r = (value.len() as u16).to_le_bytes().to_vec();
715 r.extend_from_slice(value.as_bytes());
716 r
717 }
718
719 #[test]
720 fn ninep_host_directory_round_trip() {
721 let root = std::env::temp_dir().join(format!("x86-native-9p-{}", std::process::id()));
722 let _ = std::fs::remove_dir_all(&root);
723 std::fs::create_dir_all(&root).unwrap();
724 std::fs::write(root.join("hello.txt"), b"hello").unwrap();
725 let mut dev = Virtio9p {
726 root: Some(root.clone()),
727 ..Virtio9p::default()
728 };
729
730 let mut version = 8192u32.to_le_bytes().to_vec();
731 version.extend_from_slice(&string("9P2000.L"));
732 assert_eq!(handle_9p(&mut dev, &request(100, 1, &version))[4], 101);
733
734 let mut attach = 0u32.to_le_bytes().to_vec();
735 attach.extend_from_slice(&u32::MAX.to_le_bytes());
736 attach.extend_from_slice(&string("root"));
737 attach.extend_from_slice(&string(""));
738 assert_eq!(handle_9p(&mut dev, &request(104, 2, &attach))[4], 105);
739
740 let mut walk = 0u32.to_le_bytes().to_vec();
741 walk.extend_from_slice(&1u32.to_le_bytes());
742 walk.extend_from_slice(&1u16.to_le_bytes());
743 walk.extend_from_slice(&string("hello.txt"));
744 assert_eq!(handle_9p(&mut dev, &request(110, 3, &walk))[4], 111);
745
746 let mut open = 1u32.to_le_bytes().to_vec();
747 open.extend_from_slice(&0u32.to_le_bytes());
748 assert_eq!(handle_9p(&mut dev, &request(12, 4, &open))[4], 13);
749
750 let mut read = 1u32.to_le_bytes().to_vec();
751 read.extend_from_slice(&0u64.to_le_bytes());
752 read.extend_from_slice(&5u32.to_le_bytes());
753 let response = handle_9p(&mut dev, &request(116, 5, &read));
754 assert_eq!(response[4], 117);
755 assert_eq!(&response[7 + 4..7 + 9], b"hello");
756 let mut getattr = 0u32.to_le_bytes().to_vec();
757 getattr.extend_from_slice(&0x1FFFu64.to_le_bytes());
758 let getattr_response = handle_9p(&mut dev, &request(24, 6, &getattr));
759 assert_eq!(getattr_response[4], 25);
760 assert!(getattr_response.len() >= 7 + 8 + 13 + 4);
761
762 let mut readdir = 0u32.to_le_bytes().to_vec();
763 readdir.extend_from_slice(&0u64.to_le_bytes());
764 readdir.extend_from_slice(&4096u32.to_le_bytes());
765 let readdir_response = handle_9p(&mut dev, &request(40, 7, &readdir));
766 assert_eq!(readdir_response[4], 41);
767 let readdir_count = u32::from_le_bytes(readdir_response[7..11].try_into().unwrap());
768 assert!(readdir_count > 0);
769
770 let clunk = 1u32.to_le_bytes().to_vec();
771 assert_eq!(handle_9p(&mut dev, &request(120, 8, &clunk))[4], 121);
772 let _ = std::fs::remove_dir_all(root);
773 }
774}
775
776#[cfg(test)]
777mod pci_tests {
778 use super::*;
779
780 #[test]
781 fn pci_config_exposes_virtio_9p_identity() {
782 assert!(io_write32(PCI_CONFIG_ADDRESS, 0x8000_0000u32 as i32).to_owned());
783 let id = io_read32(PCI_CONFIG_DATA).expect("PCI data port");
784 assert_eq!(id as u32, 0x1009_1AF4);
785 assert_eq!(io_read16(PCI_CONFIG_DATA).unwrap() as u16, 0x1AF4);
786 assert_eq!(io_read8(PCI_CONFIG_DATA).unwrap() as u8, 0xF4);
787
788 assert!(io_write32(PCI_CONFIG_ADDRESS, 0x8000_0010u32 as i32).to_owned());
789 let bar = io_read32(PCI_CONFIG_DATA).expect("PCI BAR");
790 assert_eq!(bar as u32, 0x0000_A001);
791
792 assert!(io_write32(PCI_CONFIG_ADDRESS, 0x8000_0034u32 as i32));
793 assert_eq!(io_read32(PCI_CONFIG_DATA).unwrap() as u32, 0x40);
794 assert!(io_write32(PCI_CONFIG_ADDRESS, 0x8000_0040u32 as i32));
795 assert_eq!(io_read32(PCI_CONFIG_DATA).unwrap() as u32, 0x0110_5009);
796 assert!(io_write32(PCI_CONFIG_ADDRESS, 0x8000_0050u32 as i32));
797 assert_eq!(io_read32(PCI_CONFIG_DATA).unwrap() as u32, 0x0214_6009);
798 assert!(io_write32(PCI_CONFIG_ADDRESS, 0x8000_0054u32 as i32));
799 assert_eq!(io_read32(PCI_CONFIG_DATA).unwrap() as u32, 1);
800 assert!(io_write32(PCI_CONFIG_ADDRESS, 0x8000_0064u32 as i32));
801 assert_eq!(io_read32(PCI_CONFIG_DATA).unwrap() as u32, 0x0310_7409);
802 assert!(io_write32(PCI_CONFIG_ADDRESS, 0x8000_0074u32 as i32));
803 assert_eq!(io_read32(PCI_CONFIG_DATA).unwrap() as u32, 0x0410_8409);
804 }
805
806 #[test]
807 fn virtio_common_queue_registers_round_trip() {
808 assert!(io_write16(VIRTIO_9P_COMMON + 22, 0));
809 assert!(io_write16(VIRTIO_9P_COMMON + 24, 16));
810 assert!(io_write32(VIRTIO_9P_COMMON + 32, 0x0010_0000));
811 assert!(io_write32(VIRTIO_9P_COMMON + 40, 0x0010_0200));
812 assert!(io_write32(VIRTIO_9P_COMMON + 48, 0x0010_0280));
813 assert_eq!(io_read16(VIRTIO_9P_COMMON + 24).unwrap(), 16);
814 assert_eq!(
815 io_read32(VIRTIO_9P_COMMON + 32).unwrap() as u32,
816 0x0010_0000
817 );
818 assert_eq!(
819 io_read32(VIRTIO_9P_COMMON + 40).unwrap() as u32,
820 0x0010_0200
821 );
822 assert_eq!(
823 io_read32(VIRTIO_9P_COMMON + 48).unwrap() as u32,
824 0x0010_0280
825 );
826 assert!(io_write16(VIRTIO_9P_COMMON + 28, 1));
827 assert_eq!(io_read16(VIRTIO_9P_COMMON + 28).unwrap(), 1);
828 }
829}