1#![expect(missing_docs)]
18#![forbid(unsafe_code)]
19
20mod emu;
21mod non_linear;
22mod render;
23mod spec;
24mod text_mode;
25
26use chipset_device::ChipsetDevice;
27use chipset_device::io::IoResult;
28use chipset_device::mmio::MmioIntercept;
29use chipset_device::pci::PciConfigSpace;
30use chipset_device::pio::PortIoIntercept;
31use framebuffer::FramebufferLocalControl;
32use guestmem::MapRom;
33use inspect::InspectMut;
34use render::Renderer;
35use thiserror::Error;
36use video_core::FramebufferFormat;
37use vmcore::device_state::ChangeDeviceState;
38use vmcore::save_restore::SaveRestore;
39use vmcore::save_restore::SavedStateNotSupported;
40use vmcore::vm_task::VmTaskDriver;
41use vmcore::vmtime::VmTimeSource;
42
43#[derive(InspectMut)]
44pub struct VgaDevice {
45 #[inspect(flatten)]
46 emu: emu::Emulator,
47 renderer: Renderer,
48}
49
50#[derive(Debug, Error)]
51pub enum Error {
52 #[error("failed to map framebuffer")]
53 Framebuffer(#[source] std::io::Error),
54}
55
56impl VgaDevice {
57 pub fn new(
58 driver: &VmTaskDriver,
59 vmtime: &VmTimeSource,
60 mut control: FramebufferLocalControl,
61 rom: Option<Box<dyn MapRom>>,
62 ) -> Result<Self, Error> {
63 control.set_format(FramebufferFormat {
64 width: 800,
65 height: 600,
66 bytes_per_line: 800 * 4,
67 offset: 0,
68 });
69
70 let vram = control.memory().map_err(Error::Framebuffer)?;
71 let renderer = Renderer::new(driver, control.clone(), vram.clone());
72 let emu = emu::Emulator::new(control, vram, vmtime, rom, renderer.control());
73 Ok(Self { emu, renderer })
74 }
75}
76
77impl ChangeDeviceState for VgaDevice {
78 fn start(&mut self) {
79 self.renderer.start();
80 }
81
82 async fn stop(&mut self) {
83 self.renderer.stop().await;
84 }
85
86 async fn reset(&mut self) {
87 self.emu.reset();
88 }
89}
90
91impl ChipsetDevice for VgaDevice {
92 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
93 Some(self)
94 }
95
96 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
97 Some(self)
98 }
99
100 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
101 Some(self)
102 }
103}
104
105impl PciConfigSpace for VgaDevice {
106 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> IoResult {
107 self.emu.notify_pci_config_access_read(offset, value)
108 }
109
110 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult {
111 self.emu.notify_pci_config_access_write(offset, value)
112 }
113
114 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
115 Some((0, 8, 0)) }
117}
118
119impl MmioIntercept for VgaDevice {
120 fn mmio_read(&mut self, addr: u64, data: &mut [u8]) -> IoResult {
121 self.emu.notify_mmio_read(addr, data);
122 IoResult::Ok
123 }
124
125 fn mmio_write(&mut self, addr: u64, data: &[u8]) -> IoResult {
126 self.emu.notify_mmio_write(addr, data);
127 IoResult::Ok
128 }
129
130 fn get_static_regions(&mut self) -> &[(&str, std::ops::RangeInclusive<u64>)] {
131 &[("vga", 0xa0000..=0xbffff)]
133 }
134}
135
136impl PortIoIntercept for VgaDevice {
137 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
138 let v = self.emu.io_port_read(io_port, data.len() as u16);
139 data.copy_from_slice(&v.to_ne_bytes()[..data.len()]);
140 IoResult::Ok
141 }
142
143 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
144 let mut v = [0; 4];
145 v[..data.len()].copy_from_slice(data);
146 self.emu
147 .io_port_write(io_port, data.len() as u16, u32::from_ne_bytes(v));
148 IoResult::Ok
149 }
150
151 fn get_static_regions(&mut self) -> &[(&str, std::ops::RangeInclusive<u16>)] {
152 &[
153 ("mda", 0x3b0..=0x3bf),
154 ("vga", 0x3c0..=0x3cf),
155 ("cga", 0x3d0..=0x3df),
156 ("s3", 0x4ae8..=0x4ae8),
157 ]
158 }
159}
160
161impl SaveRestore for VgaDevice {
162 type SavedState = SavedStateNotSupported;
163
164 fn save(&mut self) -> Result<Self::SavedState, vmcore::save_restore::SaveError> {
165 todo!()
166 }
167
168 fn restore(
169 &mut self,
170 state: Self::SavedState,
171 ) -> Result<(), vmcore::save_restore::RestoreError> {
172 match state {}
173 }
174}