kernel/device/graphics/
manager.rs

1//! # Graphics Manager Module
2//!
3//! This module provides functionality for managing graphics devices and resources in the kernel.
4//!
5//! ## Overview
6//!
7//! The GraphicsManager is responsible for:
8//! - Managing framebuffer resources from graphics devices
9//! - Coordinating with DeviceManager for device discovery
10//! - Creating and managing character devices for framebuffer access
11//! - Future support for multi-display configurations and mmap operations
12//!
13//! ## Key Components
14//!
15//! - `GraphicsManager`: The main graphics management system
16//! - `FramebufferResource`: Resource information extracted from graphics devices
17//! - `DisplayConfiguration`: Configuration for display setups (future use)
18//! - `MmapRegion`: Memory mapping region tracking (future use)
19
20extern crate alloc;
21
22use alloc::{
23    format,
24    string::{String, ToString},
25    sync::Arc,
26    vec::Vec,
27};
28use hashbrown::HashMap;
29use spin::{Mutex, RwLock};
30
31use crate::device::{
32    DeviceType,
33    graphics::{FramebufferConfig, GraphicsDevice},
34    manager::{DeviceManager, SharedDevice},
35};
36
37/// Framebuffer resource extracted from graphics devices
38#[derive(Debug)]
39pub struct FramebufferResource {
40    /// DeviceManager's device id
41    pub source_device_id: usize,
42    /// Logical name for user access (e.g., "fb0")
43    pub logical_name: String,
44    /// Framebuffer configuration (resolution, format, etc.)
45    pub config: FramebufferConfig,
46    /// Physical memory address of the framebuffer
47    pub physical_addr: usize,
48    /// Size of the allocated framebuffer memory in bytes (page-aligned).
49    /// This is the actual allocated size, which may be larger than the logical
50    /// framebuffer size (config.size()) due to page alignment requirements.
51    /// Use this size for memory mapping operations.
52    /// For the logical pixel data size, use config.size() instead.
53    pub size: usize,
54    /// ID of the created /dev/fbX character device (if any)
55    pub created_char_device_id: RwLock<Option<usize>>,
56}
57
58impl FramebufferResource {
59    /// Create a new framebuffer resource
60    pub fn new(
61        source_device_id: usize,
62        logical_name: String,
63        config: FramebufferConfig,
64        physical_addr: usize,
65        size: usize,
66    ) -> Self {
67        Self {
68            source_device_id,
69            logical_name,
70            config,
71            physical_addr,
72            size,
73            created_char_device_id: RwLock::new(None),
74        }
75    }
76}
77
78/// Display configuration for multi-display setups (future use)
79#[derive(Debug, Clone)]
80pub struct DisplayConfiguration {
81    /// Display identifier
82    pub display_id: String,
83    /// Associated framebuffer logical name
84    pub framebuffer_name: String,
85    /// Display position in multi-display setup
86    pub position: (u32, u32),
87    /// Display resolution
88    pub resolution: (u32, u32),
89    /// Whether this display is the primary display
90    pub is_primary: bool,
91}
92
93/// Memory mapped region tracking (future use)
94#[derive(Debug, Clone)]
95pub struct MmapRegion {
96    /// Virtual address of the mapped region
97    pub virtual_addr: usize,
98    /// Physical address of the mapped region  
99    pub physical_addr: usize,
100    /// Size of the mapped region
101    pub size: usize,
102    /// Associated framebuffer name
103    pub framebuffer_name: String,
104}
105
106/// Graphics Manager - singleton for managing graphics resources
107pub struct GraphicsManager {
108    /// Framebuffer resources mapped by logical name
109    framebuffers: Mutex<Option<HashMap<String, Arc<FramebufferResource>>>>,
110    /// Multi-display configuration (future use)
111    display_configs: Mutex<Vec<DisplayConfiguration>>,
112    /// Active mmap regions (future use)
113    active_mappings: Mutex<Vec<MmapRegion>>,
114}
115
116static MANAGER: GraphicsManager = GraphicsManager::new();
117
118impl GraphicsManager {
119    /// Create a new GraphicsManager instance
120    pub const fn new() -> Self {
121        Self {
122            framebuffers: Mutex::new(None),
123            display_configs: Mutex::new(Vec::new()),
124            active_mappings: Mutex::new(Vec::new()),
125        }
126    }
127
128    /// Get reference to the global GraphicsManager instance
129    pub fn get_manager() -> &'static GraphicsManager {
130        &MANAGER
131    }
132
133    /// Discover and register graphics devices from DeviceManager
134    ///
135    /// This method scans all devices in the DeviceManager for graphics devices
136    /// and extracts their framebuffer resources for management.
137    pub fn discover_graphics_devices(&self) {
138        let device_manager = DeviceManager::get_manager();
139        let device_count = device_manager.get_devices_count();
140
141        for device_id in 0..device_count {
142            core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
143
144            let device = match device_manager.get_device(device_id) {
145                Some(device) => device,
146                None => {
147                    crate::early_println!("[GraphicsManager] Device not found: {}", device_id);
148                    continue;
149                }
150            };
151
152            if device.device_type() == DeviceType::Graphics {
153                if let Err(e) = self.register_framebuffer_from_device(device_id, device) {
154                    crate::early_println!(
155                        "[GraphicsManager] Failed to register framebuffer from device {}: {}",
156                        device_id,
157                        e
158                    );
159                } else {
160                    crate::early_println!(
161                        "[GraphicsManager] Successfully registered framebuffer from device {}",
162                        device_id
163                    );
164                }
165            }
166        }
167    }
168
169    /// Register a framebuffer resource from a specific graphics device
170    ///
171    /// # Arguments
172    ///
173    /// * `device_name` - The name of the device in DeviceManager
174    /// * `device` - The shared device reference
175    ///
176    /// # Returns
177    ///
178    /// Result indicating success or failure
179    pub fn register_framebuffer_from_device(
180        &self,
181        device_id: usize,
182        device: SharedDevice,
183    ) -> Result<(), &'static str> {
184        // Cast to graphics device
185        let graphics_device = device
186            .as_graphics_device()
187            .ok_or("Device is not a graphics device")?;
188
189        // Initialize the graphics device if needed via trait (no downcast)
190        graphics_device.init_graphics()?;
191
192        // Extract framebuffer configuration
193        let config = graphics_device.get_framebuffer_config()?;
194
195        // Extract framebuffer address
196        let physical_addr = graphics_device.get_framebuffer_address()?;
197
198        // Calculate framebuffer size
199        // The logical size is what the pixels actually use
200        let logical_size = config.size();
201        // The physical size must be page-aligned for memory mapping
202        let physical_size = (logical_size + crate::environment::PAGE_SIZE - 1)
203            & !(crate::environment::PAGE_SIZE - 1);
204
205        // Generate logical name (fb0, fb1, etc.)
206        let mut framebuffers = self.framebuffers.lock();
207        if framebuffers.is_none() {
208            *framebuffers = Some(HashMap::new());
209        }
210        let map = framebuffers.as_ref().unwrap();
211        let logical_name = format!("fb{}", map.len());
212        drop(framebuffers);
213
214        // Create framebuffer resource with page-aligned physical size
215        let resource = Arc::new(FramebufferResource::new(
216            device_id,
217            logical_name.clone(),
218            config,
219            physical_addr,
220            physical_size,
221        ));
222
223        // Store the resource
224        let mut framebuffers = self.framebuffers.lock();
225        if framebuffers.is_none() {
226            *framebuffers = Some(HashMap::new());
227        }
228        framebuffers
229            .as_mut()
230            .unwrap()
231            .insert(logical_name.clone(), resource);
232        drop(framebuffers);
233
234        crate::early_println!(
235            "[GraphicsManager] Registered framebuffer resource: {} -> {}",
236            device_id,
237            logical_name
238        );
239
240        // Automatically create and register the character device
241        if let Err(e) = self.create_framebuffer_char_device(&logical_name) {
242            crate::early_println!(
243                "[GraphicsManager] Warning: Failed to create character device for {}: {}",
244                logical_name,
245                e
246            );
247        }
248
249        Ok(())
250    }
251
252    /// Get a framebuffer resource by logical name
253    ///
254    /// # Arguments
255    ///
256    /// * `fb_name` - The logical name of the framebuffer (e.g., "fb0")
257    ///
258    /// # Returns
259    ///
260    /// Optional reference to the framebuffer resource
261    pub fn get_framebuffer(&self, fb_name: &str) -> Option<Arc<FramebufferResource>> {
262        let framebuffers = self.framebuffers.lock();
263        framebuffers.as_ref()?.get(fb_name).cloned()
264    }
265
266    /// Get all registered framebuffer names
267    ///
268    /// # Returns
269    ///
270    /// Vector of logical framebuffer names
271    pub fn get_framebuffer_names(&self) -> Vec<String> {
272        let framebuffers = self.framebuffers.lock();
273        if let Some(map) = framebuffers.as_ref() {
274            map.keys().cloned().collect()
275        } else {
276            Vec::new()
277        }
278    }
279
280    /// Get number of registered framebuffers
281    ///
282    /// # Returns
283    ///
284    /// Number of registered framebuffers
285    pub fn get_framebuffer_count(&self) -> usize {
286        let framebuffers = self.framebuffers.lock();
287        if let Some(map) = framebuffers.as_ref() {
288            map.len()
289        } else {
290            0
291        }
292    }
293
294    /// Create a FramebufferCharDevice and register it with DeviceManager
295    ///
296    /// # Arguments
297    ///
298    /// * `fb_name` - The logical name of the framebuffer (e.g., "fb0")
299    ///
300    /// # Returns
301    ///
302    /// Result indicating success or failure
303    pub fn create_framebuffer_char_device(&self, fb_name: &str) -> Result<(), &'static str> {
304        use crate::device::{
305            graphics::framebuffer_device::FramebufferCharDevice, manager::DeviceManager,
306        };
307        use alloc::sync::Arc;
308
309        // Get framebuffer resource
310        let fb_resource = {
311            let framebuffers = self.framebuffers.lock();
312            framebuffers
313                .as_ref()
314                .and_then(|map| map.get(fb_name))
315                .cloned()
316                .ok_or("Framebuffer not found")?
317        };
318
319        // Create the character device
320        let fb_char_device = FramebufferCharDevice::new(fb_resource);
321
322        // Register with DeviceManager (this will automatically publish to DevFS)
323        let device_manager = DeviceManager::get_manager();
324        let device_id =
325            device_manager.register_device_with_name(fb_name.to_string(), Arc::new(fb_char_device));
326
327        // Update the framebuffer resource with the device ID
328        self.set_char_device_id(fb_name, device_id)?;
329
330        crate::early_println!(
331            "[GraphicsManager] Created framebuffer character device: /dev/{}",
332            fb_name
333        );
334        Ok(())
335    }
336
337    /// Update the character device ID for a framebuffer resource
338    ///
339    /// # Arguments
340    ///
341    /// * `fb_name` - The logical name of the framebuffer
342    /// * `char_device_id` - The character device ID from DeviceManager
343    ///
344    /// # Returns
345    ///
346    /// Result indicating success or failure
347    pub fn set_char_device_id(
348        &self,
349        fb_name: &str,
350        char_device_id: usize,
351    ) -> Result<(), &'static str> {
352        let mut framebuffers = self.framebuffers.lock();
353        if let Some(map) = framebuffers.as_mut() {
354            if let Some(resource) = map.get_mut(fb_name) {
355                *resource.created_char_device_id.write() = Some(char_device_id);
356                Ok(())
357            } else {
358                Err("Framebuffer not found")
359            }
360        } else {
361            Err("Framebuffer not found")
362        }
363    }
364
365    /// Read a single byte from the specified framebuffer
366    ///
367    /// # Arguments
368    ///
369    /// * `fb_name` - The logical name of the framebuffer
370    /// * `position` - The position to read from
371    ///
372    /// # Returns
373    ///
374    /// The byte at the specified position, or None if invalid
375    pub fn read_byte_from_framebuffer(&self, fb_name: &str, position: usize) -> Option<u8> {
376        let fb_resource = self.get_framebuffer(fb_name)?;
377
378        if position >= fb_resource.size {
379            return None;
380        }
381
382        // Read byte from framebuffer memory
383        unsafe {
384            let fb_ptr = fb_resource.physical_addr as *const u8;
385            Some(*fb_ptr.add(position))
386        }
387    }
388
389    /// Write a single byte to the specified framebuffer
390    ///
391    /// # Arguments
392    ///
393    /// * `fb_name` - The logical name of the framebuffer
394    /// * `position` - The position to write to
395    /// * `byte` - The byte to write
396    ///
397    /// # Returns
398    ///
399    /// Result indicating success or failure
400    pub fn write_byte_to_framebuffer(
401        &self,
402        fb_name: &str,
403        position: usize,
404        byte: u8,
405    ) -> Result<(), &'static str> {
406        let fb_resource = self
407            .get_framebuffer(fb_name)
408            .ok_or("Framebuffer not found")?;
409
410        if position >= fb_resource.size {
411            return Err("Position beyond framebuffer size");
412        }
413
414        // Write byte to framebuffer memory
415        unsafe {
416            let fb_ptr = fb_resource.physical_addr as *mut u8;
417            *fb_ptr.add(position) = byte;
418        }
419
420        Ok(())
421    }
422
423    /// Read multiple bytes from the specified framebuffer
424    ///
425    /// # Arguments
426    ///
427    /// * `fb_name` - The logical name of the framebuffer
428    /// * `position` - The starting position to read from
429    /// * `buffer` - The buffer to read data into
430    ///
431    /// # Returns
432    ///
433    /// The number of bytes actually read
434    pub fn read_framebuffer(&self, fb_name: &str, position: usize, buffer: &mut [u8]) -> usize {
435        let fb_resource = match self.get_framebuffer(fb_name) {
436            Some(resource) => resource,
437            None => return 0,
438        };
439
440        let available_bytes = fb_resource.size.saturating_sub(position);
441        let bytes_to_read = buffer.len().min(available_bytes);
442
443        if bytes_to_read == 0 {
444            return 0;
445        }
446
447        // Read bytes from framebuffer memory.
448        //
449        // NOTE: For QEMU+HVF, avoid memcpy-style accesses that may VM-exit as
450        // EC_DATAABORT without ISV and abort the host (assert(isv)).
451        unsafe {
452            let fb_ptr = fb_resource.physical_addr as *const u8;
453            let src = fb_ptr.add(position);
454            for i in 0..bytes_to_read {
455                buffer[i] = core::ptr::read_volatile(src.add(i));
456            }
457        }
458
459        bytes_to_read
460    }
461
462    /// Write multiple bytes to the specified framebuffer
463    ///
464    /// # Arguments
465    ///
466    /// * `fb_name` - The logical name of the framebuffer
467    /// * `position` - The starting position to write to
468    /// * `buffer` - The buffer containing data to write
469    ///
470    /// # Returns
471    ///
472    /// Result containing the number of bytes written or an error
473    pub fn write_framebuffer(
474        &self,
475        fb_name: &str,
476        position: usize,
477        buffer: &[u8],
478    ) -> Result<usize, &'static str> {
479        let fb_resource = self
480            .get_framebuffer(fb_name)
481            .ok_or("Framebuffer not found")?;
482
483        let available_space = fb_resource.size.saturating_sub(position);
484        let bytes_to_write = buffer.len().min(available_space);
485
486        if bytes_to_write == 0 {
487            return Err("No space available in framebuffer");
488        }
489
490        // Write bytes to framebuffer memory.
491        // See note in read_framebuffer() about QEMU+HVF and ISV.
492        unsafe {
493            let fb_ptr = fb_resource.physical_addr as *mut u8;
494            let dst = fb_ptr.add(position);
495            for i in 0..bytes_to_write {
496                core::ptr::write_volatile(dst.add(i), buffer[i]);
497            }
498        }
499
500        Ok(bytes_to_write)
501    }
502
503    /// Clear all framebuffers (for testing only)
504    /// This allows tests to start with a clean GraphicsManager state
505    #[cfg(test)]
506    pub fn clear_for_test(&self) {
507        use crate::device::manager::DeviceManager;
508
509        // Clear GraphicsManager state
510        let mut framebuffers = self.framebuffers.lock();
511        *framebuffers = None;
512
513        let mut display_configs = self.display_configs.lock();
514        display_configs.clear();
515
516        let mut active_mappings = self.active_mappings.lock();
517        active_mappings.clear();
518
519        // Also clear DeviceManager state to ensure test isolation
520        DeviceManager::get_manager().clear_for_test();
521    }
522}
523
524#[cfg(test)]
525mod test_utils {
526    use super::*;
527
528    /// Create an independent GraphicsManager for testing
529    /// This allows each test to have its own isolated manager instance
530    pub fn create_test_graphics_manager() -> GraphicsManager {
531        GraphicsManager::new()
532    }
533
534    /// Setup a clean GraphicsManager for testing
535    /// This clears the global singleton and returns a reference to it
536    /// ensuring each test starts with a clean state
537    pub fn setup_clean_global_graphics_manager() -> &'static GraphicsManager {
538        let manager = GraphicsManager::get_manager();
539        manager.clear_for_test();
540        manager
541    }
542}
543
544#[cfg(test)]
545mod tests {
546    use super::*;
547    use crate::device::{
548        Device,
549        graphics::{
550            FramebufferConfig, GenericGraphicsDevice, PixelFormat, manager::GraphicsManager,
551        },
552    };
553    use alloc::{string::ToString, sync::Arc};
554
555    #[test_case]
556    fn test_framebuffer_resource_creation() {
557        let config = FramebufferConfig::new(1024, 768, PixelFormat::RGBA8888);
558        let resource = FramebufferResource::new(
559            0,
560            "fb0".to_string(),
561            config.clone(),
562            0x80000000,
563            config.size(),
564        );
565
566        assert_eq!(resource.source_device_id, 0);
567        assert_eq!(resource.logical_name, "fb0");
568        assert_eq!(resource.config.width, 1024);
569        assert_eq!(resource.config.height, 768);
570        assert_eq!(resource.physical_addr, 0x80000000);
571        assert_eq!(resource.size, 1024 * 768 * 4);
572        assert_eq!(*resource.created_char_device_id.read(), None);
573    }
574
575    #[test_case]
576    fn test_graphics_manager_initialization() {
577        let manager = GraphicsManager::new();
578        assert_eq!(manager.get_framebuffer_count(), 0);
579        assert_eq!(manager.get_framebuffer_names().len(), 0);
580    }
581
582    #[test_case]
583    fn test_graphics_manager_singleton() {
584        let manager1 = GraphicsManager::get_manager();
585        let manager2 = GraphicsManager::get_manager();
586
587        // Both should point to the same instance
588        assert_eq!(manager1 as *const _, manager2 as *const _);
589    }
590
591    #[test_case]
592    fn test_framebuffer_registration() {
593        let mut manager = test_utils::create_test_graphics_manager();
594
595        // Create a test graphics device
596        let mut device = GenericGraphicsDevice::new("test-gpu");
597        let config = FramebufferConfig::new(800, 600, PixelFormat::BGRA8888);
598        device.set_framebuffer_config(config.clone());
599        device.set_framebuffer_address(0x90000000);
600
601        let shared_device: SharedDevice = Arc::new(device);
602
603        // Register the device
604        let result = manager.register_framebuffer_from_device(0, shared_device);
605        assert!(result.is_ok());
606
607        // Check that framebuffer was registered
608        assert_eq!(manager.get_framebuffer_count(), 1);
609        let names = manager.get_framebuffer_names();
610        assert_eq!(names.len(), 1);
611        assert_eq!(names[0], "fb0");
612
613        // Check framebuffer details
614        let fb = manager.get_framebuffer("fb0").unwrap();
615        assert_eq!(fb.source_device_id, 0);
616        assert_eq!(fb.logical_name, "fb0");
617        assert_eq!(fb.config.width, 800);
618        assert_eq!(fb.config.height, 600);
619        assert_eq!(fb.physical_addr, 0x90000000);
620        let page_aligned_size = (800 * 600 * 4 + crate::environment::PAGE_SIZE - 1)
621            & !(crate::environment::PAGE_SIZE - 1);
622        assert_eq!(fb.size, page_aligned_size);
623    }
624
625    #[test_case]
626    fn test_multiple_framebuffer_registration() {
627        let mut manager = test_utils::create_test_graphics_manager();
628
629        // Create first device
630        let mut device1 = GenericGraphicsDevice::new("test-gpu1");
631        let config1 = FramebufferConfig::new(1920, 1080, PixelFormat::RGBA8888);
632        device1.set_framebuffer_config(config1.clone());
633        device1.set_framebuffer_address(0x80000000);
634        let shared_device1: SharedDevice = Arc::new(device1);
635
636        // Create second device
637        let mut device2 = GenericGraphicsDevice::new("test-gpu2");
638        let config2 = FramebufferConfig::new(1024, 768, PixelFormat::BGRA8888);
639        device2.set_framebuffer_config(config2.clone());
640        device2.set_framebuffer_address(0x90000000);
641        let shared_device2: SharedDevice = Arc::new(device2);
642
643        // Register both devices
644        assert!(
645            manager
646                .register_framebuffer_from_device(1, shared_device1)
647                .is_ok()
648        );
649        assert!(
650            manager
651                .register_framebuffer_from_device(2, shared_device2)
652                .is_ok()
653        );
654
655        // Check registration
656        assert_eq!(manager.get_framebuffer_count(), 2);
657        let names = manager.get_framebuffer_names();
658        assert_eq!(names.len(), 2);
659        assert!(names.contains(&"fb0".to_string()));
660        assert!(names.contains(&"fb1".to_string()));
661
662        // Check individual framebuffers
663        let fb0 = manager.get_framebuffer("fb0").unwrap();
664        let fb1 = manager.get_framebuffer("fb1").unwrap();
665
666        assert_eq!(fb0.source_device_id, 1);
667        assert_eq!(fb1.source_device_id, 2);
668        assert_ne!(fb0.physical_addr, fb1.physical_addr);
669    }
670
671    #[test_case]
672    fn test_char_device_id_assignment() {
673        let mut manager = test_utils::create_test_graphics_manager();
674
675        // Create and register a device
676        let mut device = GenericGraphicsDevice::new("test-gpu");
677        let config = FramebufferConfig::new(640, 480, PixelFormat::RGB888);
678        device.set_framebuffer_config(config);
679        device.set_framebuffer_address(0x80000000);
680        let shared_device: SharedDevice = Arc::new(device);
681
682        manager
683            .register_framebuffer_from_device(0, shared_device)
684            .unwrap();
685
686        // Set character device ID
687        assert!(manager.set_char_device_id("fb0", 42).is_ok());
688
689        // Verify the ID was set
690        let fb = manager.get_framebuffer("fb0").unwrap();
691        assert_eq!(*fb.created_char_device_id.read(), Some(42));
692
693        // Test setting ID for non-existent framebuffer
694        assert!(manager.set_char_device_id("fb999", 123).is_err());
695    }
696
697    #[test_case]
698    fn test_framebuffer_not_found() {
699        let manager = GraphicsManager::new();
700
701        // Try to get non-existent framebuffer
702        assert!(manager.get_framebuffer("non_existent").is_none());
703
704        // Empty manager should return empty results
705        assert_eq!(manager.get_framebuffer_count(), 0);
706        assert_eq!(manager.get_framebuffer_names().len(), 0);
707    }
708}