⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/virt/
devices.rs

1//! Virtual device emulation for guest VMs
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::{boxed::Box, collections::BTreeMap, vec::Vec};
8
9use super::VmError;
10
11pub trait VirtualDevice: Send {
12    fn handle_io(&mut self, port: u16, is_write: bool, data: &mut [u8]) -> Result<(), VmError>;
13    fn name(&self) -> &str;
14    fn base_port(&self) -> u16;
15    fn port_count(&self) -> u16;
16}
17
18const UART_BUFFER_SIZE: usize = 256;
19
20pub struct VirtualUart {
21    base: u16,
22    #[cfg(feature = "alloc")]
23    output_buffer: Vec<u8>,
24    line_status: u8,
25}
26
27impl VirtualUart {
28    pub fn new(base: u16) -> Self {
29        Self {
30            base,
31            #[cfg(feature = "alloc")]
32            output_buffer: Vec::with_capacity(UART_BUFFER_SIZE),
33            line_status: 0x60,
34        }
35    }
36
37    fn write_byte(&mut self, byte: u8) {
38        #[cfg(feature = "alloc")]
39        {
40            self.output_buffer.push(byte);
41            if byte == b'\n' || self.output_buffer.len() >= UART_BUFFER_SIZE {
42                self.flush();
43            }
44        }
45        #[cfg(not(feature = "alloc"))]
46        {
47            let _ = byte;
48        }
49    }
50
51    fn read_status(&self) -> u8 {
52        self.line_status
53    }
54
55    #[cfg(feature = "alloc")]
56    fn flush(&mut self) {
57        if !self.output_buffer.is_empty() {
58            for &b in &self.output_buffer {
59                if b.is_ascii() {
60                    crate::print!("{}", b as char);
61                }
62            }
63            self.output_buffer.clear();
64        }
65    }
66}
67
68impl VirtualDevice for VirtualUart {
69    fn handle_io(&mut self, port: u16, is_write: bool, data: &mut [u8]) -> Result<(), VmError> {
70        let offset = port - self.base;
71        match offset {
72            0 => {
73                if is_write {
74                    if let Some(&b) = data.first() {
75                        self.write_byte(b);
76                    }
77                } else if let Some(d) = data.first_mut() {
78                    *d = 0xFF;
79                }
80            }
81            5 => {
82                if !is_write {
83                    if let Some(d) = data.first_mut() {
84                        *d = self.read_status();
85                    }
86                }
87            }
88            1..=4 | 6 | 7 => {
89                if !is_write {
90                    if let Some(d) = data.first_mut() {
91                        *d = 0x00;
92                    }
93                }
94            }
95            _ => {}
96        }
97        Ok(())
98    }
99    fn name(&self) -> &str {
100        "8250 UART"
101    }
102    fn base_port(&self) -> u16 {
103        self.base
104    }
105    fn port_count(&self) -> u16 {
106        8
107    }
108}
109
110pub struct VirtualPic {
111    base: u16,
112    mask: u8,
113    isr: u8,
114    is_master: bool,
115}
116
117impl VirtualPic {
118    pub fn new(base: u16) -> Self {
119        Self {
120            base,
121            mask: 0xFF,
122            isr: 0,
123            is_master: base == 0x20,
124        }
125    }
126}
127
128impl VirtualDevice for VirtualPic {
129    fn handle_io(&mut self, port: u16, is_write: bool, data: &mut [u8]) -> Result<(), VmError> {
130        let offset = port - self.base;
131        match offset {
132            0 => {
133                if is_write {
134                    if let Some(&b) = data.first() {
135                        if b & 0x10 != 0 {
136                            self.mask = 0xFF;
137                            self.isr = 0;
138                        } else if b == 0x20 {
139                            self.isr = 0;
140                        }
141                    }
142                } else if let Some(d) = data.first_mut() {
143                    *d = self.isr;
144                }
145            }
146            1 => {
147                if is_write {
148                    if let Some(&b) = data.first() {
149                        self.mask = b;
150                    }
151                } else if let Some(d) = data.first_mut() {
152                    *d = self.mask;
153                }
154            }
155            _ => {}
156        }
157        Ok(())
158    }
159    fn name(&self) -> &str {
160        if self.is_master {
161            "8259A PIC (master)"
162        } else {
163            "8259A PIC (slave)"
164        }
165    }
166    fn base_port(&self) -> u16 {
167        self.base
168    }
169    fn port_count(&self) -> u16 {
170        2
171    }
172}
173
174#[cfg(feature = "alloc")]
175struct DeviceRegistration {
176    base: u16,
177    size: u16,
178    device: Box<dyn VirtualDevice>,
179}
180
181#[cfg(feature = "alloc")]
182pub struct DeviceManager {
183    devices: BTreeMap<u16, DeviceRegistration>,
184}
185
186#[cfg(feature = "alloc")]
187impl DeviceManager {
188    pub fn new() -> Self {
189        Self {
190            devices: BTreeMap::new(),
191        }
192    }
193
194    pub fn register_device(&mut self, base: u16, size: u16, device: Box<dyn VirtualDevice>) {
195        self.devices
196            .insert(base, DeviceRegistration { base, size, device });
197    }
198
199    pub fn handle_io(&mut self, port: u16, is_write: bool, data: &mut [u8]) -> Result<(), VmError> {
200        for reg in self.devices.values_mut() {
201            if port >= reg.base && port < reg.base + reg.size {
202                return reg.device.handle_io(port, is_write, data);
203            }
204        }
205        if !is_write {
206            for b in data.iter_mut() {
207                *b = 0xFF;
208            }
209        }
210        Ok(())
211    }
212
213    pub fn device_count(&self) -> usize {
214        self.devices.len()
215    }
216
217    pub fn list_devices(&self) {
218        for reg in self.devices.values() {
219            crate::println!(
220                "  [vdev] {} at ports 0x{:04x}-0x{:04x}",
221                reg.device.name(),
222                reg.base,
223                reg.base + reg.size - 1
224            );
225        }
226    }
227
228    pub fn with_standard_devices() -> Self {
229        let mut mgr = Self::new();
230        mgr.register_device(0x3F8, 8, Box::new(VirtualUart::new(0x3F8)));
231        mgr.register_device(0x2F8, 8, Box::new(VirtualUart::new(0x2F8)));
232        mgr.register_device(0x20, 2, Box::new(VirtualPic::new(0x20)));
233        mgr.register_device(0xA0, 2, Box::new(VirtualPic::new(0xA0)));
234        mgr
235    }
236}
237
238#[cfg(feature = "alloc")]
239impl Default for DeviceManager {
240    fn default() -> Self {
241        Self::new()
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248
249    #[test]
250    fn test_uart_write() {
251        let mut uart = VirtualUart::new(0x3F8);
252        let mut data = [b'A'];
253        assert!(uart.handle_io(0x3F8, true, &mut data).is_ok());
254    }
255
256    #[test]
257    fn test_uart_status() {
258        let mut uart = VirtualUart::new(0x3F8);
259        let mut data = [0u8];
260        assert!(uart.handle_io(0x3FD, false, &mut data).is_ok());
261        assert_eq!(data[0], 0x60);
262    }
263
264    #[test]
265    fn test_pic_eoi() {
266        let mut pic = VirtualPic::new(0x20);
267        pic.isr = 1;
268        let mut data = [0x20];
269        assert!(pic.handle_io(0x20, true, &mut data).is_ok());
270        assert_eq!(pic.isr, 0);
271    }
272
273    #[test]
274    fn test_device_manager() {
275        let mut mgr = DeviceManager::new();
276        mgr.register_device(0x3F8, 8, Box::new(VirtualUart::new(0x3F8)));
277        assert_eq!(mgr.device_count(), 1);
278        let mut data = [b'X'];
279        assert!(mgr.handle_io(0x3F8, true, &mut data).is_ok());
280    }
281
282    #[test]
283    fn test_standard_devices() {
284        let mgr = DeviceManager::with_standard_devices();
285        assert_eq!(mgr.device_count(), 4);
286    }
287}