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