1use alloc::{boxed::Box, sync::Arc};
10use spin::{Mutex, RwLock};
11
12use crate::{
13 device::{
14 Device, DeviceType,
15 graphics::{FramebufferConfig, GraphicsDevice, PixelFormat},
16 },
17 drivers::virtio::{
18 device::VirtioDevice,
19 queue::{DescriptorFlag, VirtQueue},
20 },
21 mem::page::{Page, allocate_raw_pages},
22 object::capability::{ControlOps, MemoryMappingOps, Selectable},
23 timer::{TimerHandler, add_timer, get_tick, ms_to_ticks},
24};
25use core::ptr;
26
27const VIRTIO_GPU_F_VIRGL: u32 = 0;
29const VIRTIO_GPU_F_EDID: u32 = 1;
30
31const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100;
33const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0101;
34const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0102;
35const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0103;
36const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104;
37const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105;
38const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106;
39const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x0107;
40
41const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
43const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
44
45const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
47const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2;
48const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3;
49const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4;
50const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67;
51const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68;
52const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121;
53const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134;
54
55const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
57
58#[repr(C)]
60struct VirtioGpuCtrlHdr {
61 hdr_type: u32,
62 flags: u32,
63 fence_id: u64,
64 ctx_id: u32,
65 padding: u32,
66}
67
68#[repr(C)]
70#[derive(Clone, Copy)]
71struct VirtioGpuRect {
72 x: u32,
73 y: u32,
74 width: u32,
75 height: u32,
76}
77
78#[repr(C)]
80struct VirtioGpuRespDisplayInfo {
81 hdr: VirtioGpuCtrlHdr,
82 pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_SCANOUTS],
83}
84
85#[repr(C)]
87#[derive(Clone, Copy)]
88struct VirtioGpuDisplayOne {
89 r: VirtioGpuRect,
90 enabled: u32,
91 flags: u32,
92}
93
94#[repr(C)]
96struct VirtioGpuResourceCreate2d {
97 hdr: VirtioGpuCtrlHdr,
98 resource_id: u32,
99 format: u32,
100 width: u32,
101 height: u32,
102}
103
104#[repr(C)]
106struct VirtioGpuSetScanout {
107 hdr: VirtioGpuCtrlHdr,
108 r: VirtioGpuRect,
109 scanout_id: u32,
110 resource_id: u32,
111}
112
113#[repr(C)]
115struct VirtioGpuResourceFlush {
116 hdr: VirtioGpuCtrlHdr,
117 r: VirtioGpuRect,
118 resource_id: u32,
119 padding: u32,
120}
121
122#[repr(C)]
124struct VirtioGpuTransferToHost2d {
125 hdr: VirtioGpuCtrlHdr,
126 r: VirtioGpuRect,
127 offset: u64,
128 resource_id: u32,
129 padding: u32,
130}
131
132#[repr(C)]
134struct VirtioGpuResourceAttachBacking {
135 hdr: VirtioGpuCtrlHdr,
136 resource_id: u32,
137 nr_entries: u32,
138}
139
140#[repr(C)]
142struct VirtioGpuMemEntry {
143 addr: u64,
144 length: u32,
145 padding: u32,
146}
147
148pub struct VirtioGpuDeviceCore {
150 base_addr: usize,
151 virtqueues: Mutex<[VirtQueue<'static>; 2]>, display_info: RwLock<Option<VirtioGpuRespDisplayInfo>>,
153 framebuffer_addr: RwLock<Option<usize>>,
154 shadow_framebuffer_addr: RwLock<Option<usize>>,
155 boxed_framebuffer: RwLock<Option<Box<[Page]>>>, boxed_shadow_framebuffer: RwLock<Option<Box<[Page]>>>, resource_id: Mutex<u32>,
158 initialized: Mutex<bool>,
159 resources: Mutex<alloc::collections::BTreeMap<u32, (usize, usize)>>, }
162
163impl VirtioGpuDeviceCore {
164 pub fn new(base_addr: usize) -> Self {
174 let mut device = Self {
175 base_addr,
176 virtqueues: Mutex::new([VirtQueue::new(64), VirtQueue::new(64)]), display_info: RwLock::new(None),
178 framebuffer_addr: RwLock::new(None),
179 shadow_framebuffer_addr: RwLock::new(None),
180 boxed_framebuffer: RwLock::new(None),
181 boxed_shadow_framebuffer: RwLock::new(None),
182 resource_id: Mutex::new(1),
183 initialized: Mutex::new(false),
184 resources: Mutex::new(alloc::collections::BTreeMap::new()),
185 };
186
187 {
189 let mut virtqueues = device.virtqueues.lock();
190 for queue in virtqueues.iter_mut() {
191 queue.init();
192 }
193 }
194
195 if device.init().is_err() {
197 crate::early_println!("[Virtio GPU] Warning: Failed to initialize VirtIO device");
198 }
199
200 device
202 }
203
204 fn next_resource_id(&self) -> u32 {
206 let mut id = self.resource_id.lock();
207 let current = *id;
208 *id += 1;
209 current
210 }
211
212 fn send_control_command<T>(&self, cmd: &T) -> Result<(), &'static str> {
214 let mut resp_buffer = [0u8; 128];
215 self.send_control_command_with_resp_buffer(cmd, &mut resp_buffer)
216 }
217
218 fn send_control_command_with_resp_buffer<T>(
220 &self,
221 cmd: &T,
222 resp_buffer: &mut [u8],
223 ) -> Result<(), &'static str> {
224 let mut virtqueues = self.virtqueues.lock();
225 let control_queue = &mut virtqueues[0]; let cmd_desc = control_queue
229 .alloc_desc()
230 .ok_or("Failed to allocate command descriptor")?;
231 let resp_desc = match control_queue.alloc_desc() {
232 Some(desc) => desc,
233 None => {
234 control_queue.free_desc(cmd_desc);
236 return Err("Failed to allocate response descriptor");
237 }
238 };
239
240 let cmd_desc_ptr =
242 &mut control_queue.desc[cmd_desc] as *mut crate::drivers::virtio::queue::Descriptor;
243 let cmd_virt_addr = cmd as *const T as usize;
244 let cmd_phys_addr = crate::vm::get_kernel_vm_manager()
245 .translate_vaddr(cmd_virt_addr)
246 .ok_or("Failed to translate cmd vaddr to paddr")?;
247 unsafe {
248 core::ptr::write_volatile(&mut (*cmd_desc_ptr).addr, cmd_phys_addr as u64);
249 core::ptr::write_volatile(&mut (*cmd_desc_ptr).len, core::mem::size_of::<T>() as u32);
250 core::ptr::write_volatile(&mut (*cmd_desc_ptr).flags, DescriptorFlag::Next as u16);
251 core::ptr::write_volatile(&mut (*cmd_desc_ptr).next, resp_desc as u16);
252 }
253
254 let resp_desc_ptr =
256 &mut control_queue.desc[resp_desc] as *mut crate::drivers::virtio::queue::Descriptor;
257 let resp_virt_addr = resp_buffer.as_mut_ptr() as usize;
258 let resp_phys_addr = crate::vm::get_kernel_vm_manager()
259 .translate_vaddr(resp_virt_addr)
260 .ok_or("Failed to translate resp_buffer vaddr to paddr")?;
261 unsafe {
262 core::ptr::write_volatile(&mut (*resp_desc_ptr).addr, resp_phys_addr as u64);
263 core::ptr::write_volatile(&mut (*resp_desc_ptr).len, resp_buffer.len() as u32); core::ptr::write_volatile(&mut (*resp_desc_ptr).flags, DescriptorFlag::Write as u16);
265 }
266
267 if let Err(e) = control_queue.push(cmd_desc) {
272 control_queue.free_desc(resp_desc);
274 control_queue.free_desc(cmd_desc);
275 return Err(e);
276 }
277
278 self.notify(0); while control_queue.is_busy() {}
284 while *control_queue.used.idx == control_queue.last_used_idx {}
285
286 let _resp_idx = match control_queue.pop() {
288 Some(idx) => idx,
289 None => {
290 control_queue.free_desc(resp_desc);
292 control_queue.free_desc(cmd_desc);
293 return Err("No response from device");
294 }
295 };
296
297 control_queue.free_desc(resp_desc);
299 control_queue.free_desc(cmd_desc);
300
301 Ok(())
304 }
305
306 fn get_display_info_internal(&mut self) -> Result<(), &'static str> {
308 let cmd = VirtioGpuCtrlHdr {
310 hdr_type: VIRTIO_GPU_CMD_GET_DISPLAY_INFO,
311 flags: 0,
312 fence_id: 0,
313 ctx_id: 0,
314 padding: 0,
315 };
316
317 let mut resp_buffer = alloc::vec![0u8; core::mem::size_of::<VirtioGpuRespDisplayInfo>()];
323 self.send_control_command_with_resp_buffer(&cmd, &mut resp_buffer)?;
324
325 let mut display_info = VirtioGpuRespDisplayInfo {
327 hdr: VirtioGpuCtrlHdr {
328 hdr_type: VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
329 flags: 0,
330 fence_id: 0,
331 ctx_id: 0,
332 padding: 0,
333 },
334 pmodes: [VirtioGpuDisplayOne {
335 r: VirtioGpuRect {
336 x: 0,
337 y: 0,
338 width: 1024,
339 height: 768,
340 },
341 enabled: 1,
342 flags: 0,
343 }; VIRTIO_GPU_MAX_SCANOUTS],
344 };
345
346 for i in 1..VIRTIO_GPU_MAX_SCANOUTS {
348 display_info.pmodes[i].enabled = 0;
349 }
350
351 *self.display_info.write() = Some(display_info);
352 Ok(())
353 }
354
355 fn create_2d_resource(
357 &self,
358 width: u32,
359 height: u32,
360 format: u32,
361 ) -> Result<u32, &'static str> {
362 let resource_id = self.next_resource_id();
363
364 let cmd = VirtioGpuResourceCreate2d {
365 hdr: VirtioGpuCtrlHdr {
366 hdr_type: VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
367 flags: 0,
368 fence_id: 0,
369 ctx_id: 0,
370 padding: 0,
371 },
372 resource_id,
373 format,
374 width,
375 height,
376 };
377
378 self.send_control_command(&cmd)?;
379 Ok(resource_id)
380 }
381
382 fn attach_backing_to_resource(
384 &self,
385 resource_id: u32,
386 addr: usize,
387 size: usize,
388 ) -> Result<(), &'static str> {
389 #[repr(C)]
391 struct AttachBackingWithEntry {
392 attach: VirtioGpuResourceAttachBacking,
393 entry: VirtioGpuMemEntry,
394 }
395
396 let cmd = AttachBackingWithEntry {
397 attach: VirtioGpuResourceAttachBacking {
398 hdr: VirtioGpuCtrlHdr {
399 hdr_type: VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
400 flags: 0,
401 fence_id: 0,
402 ctx_id: 0,
403 padding: 0,
404 },
405 resource_id,
406 nr_entries: 1,
407 },
408 entry: VirtioGpuMemEntry {
409 addr: addr as u64,
410 length: size as u32,
411 padding: 0,
412 },
413 };
414
415 self.send_control_command(&cmd)?;
418 Ok(())
419 }
420
421 fn setup_framebuffer(&self) -> Result<(), &'static str> {
423 let display_info = self.display_info.read();
424 let display_info = display_info.as_ref().ok_or("No display info available")?;
425 let primary_display = &display_info.pmodes[0];
426 if primary_display.enabled == 0 {
427 return Err("Primary display not enabled");
428 }
429 let width = primary_display.r.width;
430 let height = primary_display.r.height;
431 let resource_id =
432 self.create_2d_resource(width, height, VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM)?;
433 let fb_size = (width * height * 4) as usize;
434 let fb_pages = (fb_size + 4095) / 4096;
435 let fb_pages_ptr = allocate_raw_pages(fb_pages);
436 if fb_pages_ptr.is_null() {
437 return Err("Failed to allocate framebuffer memory");
438 }
439 let fb_addr = fb_pages_ptr as usize;
440 self.boxed_framebuffer.write().replace(unsafe {
442 Box::from_raw(core::ptr::slice_from_raw_parts_mut(fb_pages_ptr, fb_pages))
443 });
444 self.attach_backing_to_resource(resource_id, fb_addr, fb_size)?; let scanout_cmd = VirtioGpuSetScanout {
447 hdr: VirtioGpuCtrlHdr {
448 hdr_type: VIRTIO_GPU_CMD_SET_SCANOUT,
449 flags: 0,
450 fence_id: 0,
451 ctx_id: 0,
452 padding: 0,
453 },
454 r: VirtioGpuRect {
455 x: 0,
456 y: 0,
457 width,
458 height,
459 },
460 scanout_id: 0,
461 resource_id,
462 };
463 self.send_control_command(&scanout_cmd)?;
464 {
465 let mut resources = self.resources.lock();
466 resources.insert(resource_id, (fb_addr, fb_size));
467 }
468 *self.framebuffer_addr.write() = Some(fb_addr);
469 let shadow_pages_ptr = allocate_raw_pages(fb_pages);
471 if shadow_pages_ptr.is_null() {
472 return Err("Failed to allocate shadow framebuffer memory");
473 }
474 let shadow_addr = shadow_pages_ptr as usize;
475 self.boxed_shadow_framebuffer.write().replace(unsafe {
477 Box::from_raw(core::ptr::slice_from_raw_parts_mut(
478 shadow_pages_ptr,
479 fb_pages,
480 ))
481 });
482 let fb_size = fb_size as usize;
484 unsafe {
485 ptr::copy_nonoverlapping(fb_addr as *const u8, shadow_addr as *mut u8, fb_size);
486 }
487 *self.shadow_framebuffer_addr.write() = Some(shadow_addr);
488 Ok(())
489 }
490
491 fn get_display_name(&self) -> &'static str {
492 "virtio-gpu"
493 }
494
495 fn get_framebuffer_config(&self) -> Result<FramebufferConfig, &'static str> {
496 let display_info = self.display_info.read();
497 let display_info = display_info.as_ref().ok_or("Device not initialized")?;
498
499 let primary_display = &display_info.pmodes[0];
500 if primary_display.enabled == 0 {
501 return Err("Primary display not enabled");
502 }
503
504 Ok(FramebufferConfig::new(
505 primary_display.r.width,
506 primary_display.r.height,
507 PixelFormat::BGRA8888, ))
509 }
510
511 fn get_framebuffer_address(&self) -> Result<usize, &'static str> {
512 self.framebuffer_addr
513 .read()
514 .ok_or("Framebuffer not initialized")
515 }
516
517 fn flush_framebuffer(
518 &self,
519 x: u32,
520 y: u32,
521 width: u32,
522 height: u32,
523 ) -> Result<(), &'static str> {
524 let display_info = self.display_info.read();
525 let _display_info = display_info.as_ref().ok_or("Device not initialized")?;
526
527 let resource_id = {
529 let resources = self.resources.lock();
530 if let Some((_, _)) = resources.get(&1) {
531 1 } else {
533 return Err("No framebuffer resource found");
534 }
535 };
536
537 let transfer_cmd = VirtioGpuTransferToHost2d {
544 hdr: VirtioGpuCtrlHdr {
545 hdr_type: VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
546 flags: 0,
547 fence_id: 0,
548 ctx_id: 0,
549 padding: 0,
550 },
551 r: VirtioGpuRect {
552 x,
553 y,
554 width,
555 height,
556 },
557 offset: 0,
558 resource_id,
559 padding: 0,
560 };
561
562 self.send_control_command(&transfer_cmd)?;
563
564 let flush_cmd = VirtioGpuResourceFlush {
567 hdr: VirtioGpuCtrlHdr {
568 hdr_type: VIRTIO_GPU_CMD_RESOURCE_FLUSH,
569 flags: 0,
570 fence_id: 0,
571 ctx_id: 0,
572 padding: 0,
573 },
574 r: VirtioGpuRect {
575 x,
576 y,
577 width,
578 height,
579 },
580 resource_id,
581 padding: 0,
582 };
583
584 self.send_control_command(&flush_cmd)?;
585 Ok(())
587 }
588}
589
590impl VirtioDevice for VirtioGpuDeviceCore {
591 fn get_base_addr(&self) -> usize {
592 self.base_addr
593 }
594
595 fn get_virtqueue_count(&self) -> usize {
596 2 }
598
599 fn get_virtqueue_size(&self, queue_idx: usize) -> usize {
600 if queue_idx >= self.get_virtqueue_count() {
601 panic!("Invalid queue index: {}", queue_idx);
602 }
603
604 let virtqueues = self.virtqueues.lock();
605 virtqueues[queue_idx].get_queue_size()
606 }
607
608 fn get_queue_desc_addr(&self, queue_idx: usize) -> Option<u64> {
609 if queue_idx >= self.get_virtqueue_count() {
610 return None;
611 }
612
613 let virtqueues = self.virtqueues.lock();
614 Some(virtqueues[queue_idx].desc.as_ptr() as u64)
615 }
616
617 fn get_queue_driver_addr(&self, queue_idx: usize) -> Option<u64> {
618 if queue_idx >= self.get_virtqueue_count() {
619 return None;
620 }
621
622 let virtqueues = self.virtqueues.lock();
623 Some(virtqueues[queue_idx].avail.flags as *const u16 as u64)
624 }
625
626 fn get_queue_device_addr(&self, queue_idx: usize) -> Option<u64> {
627 if queue_idx >= self.get_virtqueue_count() {
628 return None;
629 }
630
631 let virtqueues = self.virtqueues.lock();
632 Some(virtqueues[queue_idx].used.flags as *const u16 as u64)
633 }
634
635 fn get_supported_features(&self, _device_features: u32) -> u32 {
636 0
638 }
639}
640
641pub struct VirtioGpuDevice {
642 core: Arc<Mutex<VirtioGpuDeviceCore>>,
643 handler: RwLock<Option<Arc<dyn TimerHandler>>>,
644}
645
646impl VirtioGpuDevice {
647 pub fn new(base_addr: usize) -> Self {
657 Self {
658 core: Arc::new(Mutex::new(VirtioGpuDeviceCore::new(base_addr))),
659 handler: RwLock::new(None),
660 }
661 }
662}
663
664impl Device for VirtioGpuDevice {
665 fn device_type(&self) -> DeviceType {
666 DeviceType::Graphics
667 }
668
669 fn name(&self) -> &'static str {
670 "virtio-gpu"
671 }
672
673 fn as_any(&self) -> &dyn core::any::Any {
674 self
675 }
676
677 fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
678 self
679 }
680
681 fn as_graphics_device(&self) -> Option<&dyn GraphicsDevice> {
682 Some(self)
683 }
684}
685
686impl ControlOps for VirtioGpuDevice {
687 fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
689 Err("Control operations not supported")
690 }
691}
692
693impl MemoryMappingOps for VirtioGpuDevice {
694 fn get_mapping_info(
695 &self,
696 _offset: usize,
697 _length: usize,
698 ) -> Result<(usize, usize, bool), &'static str> {
699 Err("Memory mapping not supported by VirtIO GPU device")
700 }
701
702 fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
703 }
705
706 fn on_unmapped(&self, _vaddr: usize, _length: usize) {
707 }
709
710 fn supports_mmap(&self) -> bool {
711 false
712 }
713}
714
715impl Selectable for VirtioGpuDevice {
716 fn wait_until_ready(
717 &self,
718 _interest: crate::object::capability::selectable::ReadyInterest,
719 _trapframe: &mut crate::arch::Trapframe,
720 _timeout_ticks: Option<u64>,
721 ) -> crate::object::capability::selectable::SelectWaitOutcome {
722 crate::object::capability::selectable::SelectWaitOutcome::Ready
723 }
724}
725
726impl GraphicsDevice for VirtioGpuDevice {
727 fn get_display_name(&self) -> &'static str {
728 "virtio-gpu"
729 }
730
731 fn get_framebuffer_config(&self) -> Result<FramebufferConfig, &'static str> {
732 self.core.lock().get_framebuffer_config()
733 }
734
735 fn get_framebuffer_address(&self) -> Result<usize, &'static str> {
736 self.core.lock().get_framebuffer_address()
737 }
738
739 fn flush_framebuffer(
740 &self,
741 x: u32,
742 y: u32,
743 width: u32,
744 height: u32,
745 ) -> Result<(), &'static str> {
746 self.core.lock().flush_framebuffer(x, y, width, height)
747 }
748
749 fn init_graphics(&self) -> Result<(), &'static str> {
750 {
751 let core = self.core.lock();
752 let mut initialized = core.initialized.lock();
753 if *initialized {
754 return Ok(());
755 }
756 *initialized = true;
757 }
758
759 self.core.lock().get_display_info_internal()?;
763
764 self.core.lock().setup_framebuffer()?;
766
767 let handler: Arc<dyn TimerHandler> = Arc::new(FramebufferUpdateHandler {
768 device: self.core.clone(),
769 });
770
771 add_timer(get_tick() + ms_to_ticks(16), &handler, 0);
772
773 *self.handler.write() = Some(handler);
775
776 Ok(())
778 }
779}
780
781struct FramebufferUpdateHandler {
782 device: Arc<Mutex<VirtioGpuDeviceCore>>,
783}
784
785impl FramebufferUpdateHandler {
786 fn compare_and_flush(&self) {
787 let (_fb_addr, _shadow_addr, width, height, fb_size) = {
788 let core = self.device.lock();
789 let fb_addr = match *core.framebuffer_addr.read() {
790 Some(addr) => addr,
791 None => return,
792 };
793 let shadow_addr = match *core.shadow_framebuffer_addr.read() {
794 Some(addr) => addr,
795 None => return,
796 };
797 let display_info_guard = core.display_info.read();
798 let display_info = match display_info_guard.as_ref() {
799 Some(info) => info,
800 None => return,
801 };
802 let width = display_info.pmodes[0].r.width;
803 let height = display_info.pmodes[0].r.height;
804 let fb_size = (width * height * 4) as usize;
805 (fb_addr, shadow_addr, width, height, fb_size)
806 };
807 let _ = self.device.lock().flush_framebuffer(0, 0, width, height);
808
809 }
823}
824
825impl TimerHandler for FramebufferUpdateHandler {
826 fn on_timer_expired(self: Arc<Self>, context: usize) {
827 self.compare_and_flush();
828 let handler = self as Arc<dyn TimerHandler>;
829 add_timer(get_tick() + ms_to_ticks(16), &handler, context);
830 }
831}
832
833#[cfg(test)]
834mod tests {
835 use super::*;
836
837 #[test_case]
838 fn test_virtio_gpu_device_creation() {
839 let device = VirtioGpuDevice::new(0x10002000);
840 assert_eq!(device.core.lock().get_base_addr(), 0x10002000);
841 assert_eq!(device.core.lock().get_virtqueue_count(), 2);
842 assert_eq!(device.device_type(), DeviceType::Graphics);
843 assert_eq!(device.name(), "virtio-gpu");
844 assert_eq!(device.core.lock().get_display_name(), "virtio-gpu");
845 }
846
847 #[test_case]
848 fn test_virtio_gpu_resource_id_generation() {
849 let device = VirtioGpuDevice::new(0x10002000);
850 assert_eq!(device.core.lock().next_resource_id(), 1);
851 assert_eq!(device.core.lock().next_resource_id(), 2);
852 assert_eq!(device.core.lock().next_resource_id(), 3);
853 }
854
855 #[test_case]
856 fn test_virtio_gpu_before_init() {
857 let device = VirtioGpuDevice::new(0x10002000);
858 assert!(device.get_framebuffer_config().is_err());
860 assert!(device.get_framebuffer_address().is_err());
861 }
862
863 #[cfg(target_arch = "riscv64")]
864 #[test_case]
865 fn test_virtio_gpu_init_graphics() {
866 let mut device = VirtioGpuDevice::new(0x10002000);
867 device.init_graphics().unwrap();
868 }
869
870 #[cfg(target_arch = "riscv64")]
871 #[test_case]
872 fn test_virtio_gpu_framebuffer_operations() {
873 let mut device = VirtioGpuDevice::new(0x10002000);
874
875 device.init_graphics().unwrap();
877
878 let config = device.get_framebuffer_config().unwrap();
880 assert_eq!(config.width, 1024);
881 assert_eq!(config.height, 768);
882 assert_eq!(config.format, PixelFormat::BGRA8888);
883
884 let fb_addr = device.get_framebuffer_address().unwrap();
886 assert_ne!(fb_addr, 0);
887
888 unsafe {
890 let fb_ptr = fb_addr as *mut u32;
891 let pixel_count = (config.width * config.height) as usize;
892
893 for y in 0..config.height {
895 for x in 0..config.width {
896 let pixel_index = (y * config.width + x) as usize;
897 if pixel_index < pixel_count {
898 let red = if config.width > 1 {
900 (x * 255) / (config.width - 1)
901 } else {
902 0
903 };
904 let blue = if config.height > 1 {
905 (y * 255) / (config.height - 1)
906 } else {
907 0
908 };
909 let green = 0x80; let alpha = 0xFF; let pixel = (alpha << 24) | (red << 16) | (green << 8) | blue;
914 *fb_ptr.add(pixel_index) = pixel;
915 }
916 }
917 }
918 }
919
920 device
922 .flush_framebuffer(0, 0, config.width, config.height)
923 .unwrap();
924
925 unsafe {
927 let fb_ptr = fb_addr as *mut u32;
928
929 let top_left = *fb_ptr;
931 assert_eq!((top_left >> 24) & 0xFF, 0xFF); assert_eq!((top_left >> 16) & 0xFF, 0x00); assert_eq!((top_left >> 8) & 0xFF, 0x80); assert_eq!(top_left & 0xFF, 0x00); let bottom_right_index =
938 ((config.height - 1) * config.width + (config.width - 1)) as usize;
939 let bottom_right = *fb_ptr.add(bottom_right_index);
940 assert_eq!((bottom_right >> 24) & 0xFF, 0xFF); assert_eq!((bottom_right >> 16) & 0xFF, 0xFF); assert_eq!((bottom_right >> 8) & 0xFF, 0x80); assert_eq!(bottom_right & 0xFF, 0xFF); }
945 }
946
947 #[cfg(target_arch = "riscv64")]
948 #[test_case]
949 fn test_virtio_gpu_pixel_drawing() {
950 let mut device = VirtioGpuDevice::new(0x10002000);
951 device.init_graphics().unwrap();
952
953 let config = device.get_framebuffer_config().unwrap();
954 let fb_addr = device.get_framebuffer_address().unwrap();
955
956 let set_pixel = |x: u32, y: u32, color: u32| {
958 if x < config.width && y < config.height {
959 unsafe {
960 let fb_ptr = fb_addr as *mut u32;
961 let pixel_index = (y * config.width + x) as usize;
962 *fb_ptr.add(pixel_index) = color;
963 }
964 }
965 };
966
967 for x in 0..config.width {
970 set_pixel(x, 100, 0xFF0000FF); }
972
973 for y in 0..config.height {
975 set_pixel(200, y, 0xFF00FF00); }
977
978 let min_dim = config.width.min(config.height);
980 for i in 0..min_dim {
981 set_pixel(i, i, 0xFFFF0000); }
983
984 device
986 .flush_framebuffer(0, 0, config.width, config.height)
987 .unwrap();
988
989 unsafe {
991 let fb_ptr = fb_addr as *mut u32;
992
993 let red_pixel_index = (100 * config.width + 50) as usize;
995 let red_pixel = *fb_ptr.add(red_pixel_index);
996 assert_eq!(red_pixel, 0xFF0000FF);
997
998 let green_pixel_index = (50 * config.width + 200) as usize;
1000 let green_pixel = *fb_ptr.add(green_pixel_index);
1001 assert_eq!(green_pixel, 0xFF00FF00);
1002
1003 let blue_pixel_index = (100 * config.width + 100) as usize;
1005 let blue_pixel = *fb_ptr.add(blue_pixel_index);
1006 assert_eq!(blue_pixel, 0xFFFF0000);
1007 }
1008 }
1009
1010 #[cfg(target_arch = "riscv64")]
1011 #[test_case]
1012 fn test_virtio_gpu_rectangle_drawing() {
1013 let mut device = VirtioGpuDevice::new(0x10002000);
1014 device.init_graphics().unwrap();
1015
1016 let config = device.get_framebuffer_config().unwrap();
1017 let fb_addr = device.get_framebuffer_address().unwrap();
1018
1019 let draw_rectangle = |x: u32, y: u32, width: u32, height: u32, color: u32| {
1021 for dy in 0..height {
1022 for dx in 0..width {
1023 let px = x + dx;
1024 let py = y + dy;
1025 if px < config.width && py < config.height {
1026 unsafe {
1027 let fb_ptr = fb_addr as *mut u32;
1028 let pixel_index = (py * config.width + px) as usize;
1029 *fb_ptr.add(pixel_index) = color;
1030 }
1031 }
1032 }
1033 }
1034 };
1035
1036 unsafe {
1038 let fb_ptr = fb_addr as *mut u32;
1039 let pixel_count = (config.width * config.height) as usize;
1040 for i in 0..pixel_count {
1041 *fb_ptr.add(i) = 0xFF000000; }
1043 }
1044
1045 draw_rectangle(50, 50, 100, 75, 0xFF0000FF); draw_rectangle(200, 100, 150, 100, 0xFF00FF00); draw_rectangle(400, 200, 80, 120, 0xFFFF0000); device
1052 .flush_framebuffer(0, 0, config.width, config.height)
1053 .unwrap();
1054
1055 unsafe {
1057 let fb_ptr = fb_addr as *mut u32;
1058
1059 let red_center_index = ((50 + 37) * config.width + (50 + 50)) as usize;
1061 let red_pixel = *fb_ptr.add(red_center_index);
1062 assert_eq!(red_pixel, 0xFF0000FF);
1063
1064 let green_center_index = ((100 + 50) * config.width + (200 + 75)) as usize;
1066 let green_pixel = *fb_ptr.add(green_center_index);
1067 assert_eq!(green_pixel, 0xFF00FF00);
1068
1069 let blue_center_index = ((200 + 60) * config.width + (400 + 40)) as usize;
1071 let blue_pixel = *fb_ptr.add(blue_center_index);
1072 assert_eq!(blue_pixel, 0xFFFF0000);
1073
1074 let background_index = (10 * config.width + 10) as usize;
1076 let background_pixel = *fb_ptr.add(background_index);
1077 assert_eq!(background_pixel, 0xFF000000);
1078 }
1079 }
1080
1081 #[cfg(target_arch = "riscv64")]
1082 #[test_case]
1083 fn test_virtio_gpu_border_drawing() {
1084 let mut device = VirtioGpuDevice::new(0x10002000);
1085 device.init_graphics().unwrap();
1086
1087 let config = device.get_framebuffer_config().unwrap();
1088 let fb_addr = device.get_framebuffer_address().unwrap();
1089
1090 let draw_border = |x: u32, y: u32, width: u32, height: u32, color: u32| {
1092 for dx in 0..width {
1094 let px = x + dx;
1095 if px < config.width {
1096 if y < config.height {
1098 unsafe {
1099 let fb_ptr = fb_addr as *mut u32;
1100 let pixel_index = (y * config.width + px) as usize;
1101 *fb_ptr.add(pixel_index) = color;
1102 }
1103 }
1104 let bottom_y = y + height - 1;
1106 if bottom_y < config.height {
1107 unsafe {
1108 let fb_ptr = fb_addr as *mut u32;
1109 let pixel_index = (bottom_y * config.width + px) as usize;
1110 *fb_ptr.add(pixel_index) = color;
1111 }
1112 }
1113 }
1114 }
1115
1116 for dy in 0..height {
1118 let py = y + dy;
1119 if py < config.height {
1120 if x < config.width {
1122 unsafe {
1123 let fb_ptr = fb_addr as *mut u32;
1124 let pixel_index = (py * config.width + x) as usize;
1125 *fb_ptr.add(pixel_index) = color;
1126 }
1127 }
1128 let right_x = x + width - 1;
1130 if right_x < config.width {
1131 unsafe {
1132 let fb_ptr = fb_addr as *mut u32;
1133 let pixel_index = (py * config.width + right_x) as usize;
1134 *fb_ptr.add(pixel_index) = color;
1135 }
1136 }
1137 }
1138 }
1139 };
1140
1141 unsafe {
1143 let fb_ptr = fb_addr as *mut u32;
1144 let pixel_count = (config.width * config.height) as usize;
1145 for i in 0..pixel_count {
1146 *fb_ptr.add(i) = 0xFF000000; }
1148 }
1149
1150 draw_border(10, 10, 200, 150, 0xFF0000FF); draw_border(20, 20, 180, 130, 0xFF00FF00); draw_border(30, 30, 160, 110, 0xFFFF0000); device
1156 .flush_framebuffer(0, 0, config.width, config.height)
1157 .unwrap();
1158
1159 unsafe {
1161 let fb_ptr = fb_addr as *mut u32;
1162
1163 let top_left_red = *fb_ptr.add((10 * config.width + 10) as usize);
1165 assert_eq!(top_left_red, 0xFF0000FF);
1166
1167 let top_right_red = *fb_ptr.add((10 * config.width + 209) as usize);
1168 assert_eq!(top_right_red, 0xFF0000FF);
1169
1170 let green_border = *fb_ptr.add((20 * config.width + 20) as usize);
1172 assert_eq!(green_border, 0xFF00FF00);
1173
1174 let blue_border = *fb_ptr.add((30 * config.width + 30) as usize);
1176 assert_eq!(blue_border, 0xFFFF0000);
1177
1178 let inside = *fb_ptr.add((50 * config.width + 50) as usize);
1180 assert_eq!(inside, 0xFF000000);
1181 }
1182 }
1183
1184 #[cfg(target_arch = "riscv64")]
1185 #[test_case]
1186 fn test_virtio_gpu_pixel_format_verification() {
1187 let mut device = VirtioGpuDevice::new(0x10002000);
1188 device.init_graphics().unwrap();
1189
1190 let config = device.get_framebuffer_config().unwrap();
1191 let fb_addr = device.get_framebuffer_address().unwrap();
1192
1193 unsafe {
1195 let fb_ptr = fb_addr as *mut u32;
1196
1197 let test_colors = [
1199 (0xFF0000FF, "red"), (0xFF00FF00, "green"), (0xFFFF0000, "blue"), (0xFFFFFFFF, "white"), (0xFF000000, "black"), (0xFF808080, "gray"), ];
1206
1207 for (i, (color, _name)) in test_colors.iter().enumerate() {
1209 let x = (i as u32 * 100) % config.width;
1210 let y = (i as u32 * 100) / config.width;
1211 if y < config.height {
1212 let pixel_index = (y * config.width + x) as usize;
1213 *fb_ptr.add(pixel_index) = *color;
1214 }
1215 }
1216
1217 device
1218 .flush_framebuffer(0, 0, config.width, config.height)
1219 .unwrap();
1220
1221 for (i, (expected_color, _name)) in test_colors.iter().enumerate() {
1223 let x = (i as u32 * 100) % config.width;
1224 let y = (i as u32 * 100) / config.width;
1225 if y < config.height {
1226 let pixel_index = (y * config.width + x) as usize;
1227 let actual_color = *fb_ptr.add(pixel_index);
1228 assert_eq!(actual_color, *expected_color);
1229 }
1230 }
1231 }
1232
1233 unsafe {
1235 let fb_ptr = fb_addr as *mut u32;
1236 let semi_transparent_red = 0x800000FF; let pixel_index = (100 * config.width + 100) as usize;
1238 *fb_ptr.add(pixel_index) = semi_transparent_red;
1239
1240 device.flush_framebuffer(100, 100, 1, 1).unwrap();
1241
1242 let written_pixel = *fb_ptr.add(pixel_index);
1243 assert_eq!(written_pixel, semi_transparent_red);
1244 }
1245 }
1246
1247 #[cfg(target_arch = "riscv64")]
1248 #[test_case]
1249 fn test_virtio_gpu_command_flow_verification() {
1250 let mut device = VirtioGpuDevice::new(0x10002000);
1251
1252 crate::early_println!("[Test] Starting VirtIO GPU command flow verification");
1254 device.init_graphics().unwrap();
1255
1256 let config = device.get_framebuffer_config().unwrap();
1257 let fb_addr = device.get_framebuffer_address().unwrap();
1258
1259 crate::early_println!(
1260 "[Test] Framebuffer initialized at {:#x}, config: {}x{}",
1261 fb_addr,
1262 config.width,
1263 config.height
1264 );
1265
1266 unsafe {
1268 let fb_ptr = fb_addr as *mut u32;
1269
1270 for y in 0..config.height.min(100) {
1272 for x in 0..config.width.min(100) {
1273 let pixel_index = (y * config.width + x) as usize;
1274 let color = if (x / 10 + y / 10) % 2 == 0 {
1275 0xFFFF0000 } else {
1277 0xFF00FF00 };
1279 *fb_ptr.add(pixel_index) = color;
1280 }
1281 }
1282 }
1283
1284 crate::early_println!("[Test] Written checkerboard pattern to framebuffer");
1285
1286 device.flush_framebuffer(0, 0, 50, 50).unwrap();
1288 device.flush_framebuffer(50, 50, 50, 50).unwrap();
1289 device
1290 .flush_framebuffer(0, 0, config.width, config.height)
1291 .unwrap();
1292
1293 crate::early_println!("[Test] VirtIO GPU command flow verification completed");
1294 }
1295
1296 #[cfg(target_arch = "riscv64")]
1297 #[test_case]
1298 fn test_virtio_gpu_resource_management() {
1299 let mut device = VirtioGpuDevice::new(0x10002000);
1300 device.init_graphics().unwrap();
1301
1302 let config = device.get_framebuffer_config().unwrap();
1304 let fb_addr = device.get_framebuffer_address().unwrap();
1305
1306 crate::early_println!("[Test] Testing VirtIO GPU resource management");
1307 crate::early_println!("[Test] Primary framebuffer resource should be ID 1");
1308
1309 unsafe {
1314 let fb_ptr = fb_addr as *mut u32;
1315 for i in 0..config.width.min(config.height).min(500) {
1317 let pixel_index = (i * config.width + i) as usize;
1318 *fb_ptr.add(pixel_index) = 0xFFFFFF00; }
1320 }
1321
1322 device
1324 .flush_framebuffer(0, 0, config.width.min(500), config.height.min(500))
1325 .unwrap();
1326
1327 crate::early_println!("[Test] Resource management test completed");
1328 }
1329}