kernel/drivers/pic/
plic.rs

1//! RISC-V Platform-Level Interrupt Controller (PLIC) Implementation
2//!
3//! The PLIC is responsible for managing external interrupts from devices and
4//! routing them to different CPUs with priority support.
5
6use crate::{
7    device::{
8        DeviceInfo,
9        fdt::FdtManager,
10        manager::{DeviceManager, DriverPriority},
11        platform::{
12            PlatformDeviceDriver, PlatformDeviceInfo, resource::PlatformDeviceResourceType,
13        },
14    },
15    early_initcall,
16    interrupt::{
17        CpuId, InterruptError, InterruptId, InterruptManager, InterruptResult, Priority,
18        controllers::ExternalInterruptController,
19    },
20};
21use alloc::{boxed::Box, vec, vec::Vec};
22use core::ptr::{read_volatile, write_volatile};
23
24/// PLIC register offsets
25const PLIC_PRIORITY_BASE: usize = 0x0000_0000;
26const PLIC_PENDING_BASE: usize = 0x0000_1000;
27const PLIC_ENABLE_BASE: usize = 0x0000_2000;
28const PLIC_THRESHOLD_BASE: usize = 0x0020_0000;
29const PLIC_CLAIM_BASE: usize = 0x0020_0004;
30
31/// PLIC context stride for enable registers (per context)
32const PLIC_ENABLE_CONTEXT_STRIDE: usize = 0x80;
33/// PLIC context stride for threshold/claim registers (per context)
34const PLIC_CONTEXT_STRIDE: usize = 0x1000;
35
36/// Maximum number of interrupts supported by this PLIC implementation
37const MAX_INTERRUPTS: InterruptId = 1024;
38
39/// Maximum number of CPUs supported by this PLIC implementation
40const MAX_CPUS: CpuId = 15872; // RISC-V spec allows up to 15872 contexts
41
42/// RISC-V PLIC Implementation
43pub struct Plic {
44    /// Base address of the PLIC
45    base_addr: usize,
46    /// Maximum number of interrupts this PLIC supports
47    max_interrupts: InterruptId,
48    /// Maximum number of CPUs (harts) this PLIC supports
49    max_cpus: CpuId,
50    /// S-mode context ID for each CPU (hart).
51    /// Index = CPU ID, Value = PLIC context ID for S-mode external interrupt.
52    /// If None, use the default formula: (cpu_id * 2) + 1
53    s_mode_contexts: Option<Vec<usize>>,
54}
55
56impl Plic {
57    /// Create a new PLIC instance
58    ///
59    /// # Arguments
60    ///
61    /// * `base_addr` - Physical base address of the PLIC
62    /// * `max_interrupts` - Maximum interrupt ID supported (1-based)
63    /// * `max_cpus` - Maximum number of CPUs supported
64    pub fn new(base_addr: usize, max_interrupts: InterruptId, max_cpus: CpuId) -> Self {
65        Self {
66            base_addr,
67            max_interrupts: max_interrupts.min(MAX_INTERRUPTS),
68            max_cpus: max_cpus.min(MAX_CPUS),
69            s_mode_contexts: None,
70        }
71    }
72
73    /// Create a new PLIC instance with explicit S-mode context mapping
74    ///
75    /// # Arguments
76    ///
77    /// * `base_addr` - Physical base address of the PLIC
78    /// * `max_interrupts` - Maximum interrupt ID supported (1-based)
79    /// * `s_mode_context_ids` - Vector mapping CPU ID -> PLIC context ID for S-mode
80    pub fn with_contexts(
81        base_addr: usize,
82        max_interrupts: InterruptId,
83        s_mode_context_ids: Vec<usize>,
84    ) -> Self {
85        let max_cpus = s_mode_context_ids.len() as CpuId;
86        Self {
87            base_addr,
88            max_interrupts: max_interrupts.min(MAX_INTERRUPTS),
89            max_cpus: max_cpus.min(MAX_CPUS),
90            s_mode_contexts: Some(s_mode_context_ids),
91        }
92    }
93
94    /// Convert CPU ID to PLIC context ID for Supervisor mode.
95    /// If explicit mapping exists, use it; otherwise Hart 0 S-Mode -> Context 1, etc.
96    fn context_id_for_cpu(&self, cpu_id: CpuId) -> usize {
97        if let Some(ref contexts) = self.s_mode_contexts {
98            contexts.get(cpu_id as usize).copied().unwrap_or(0)
99        } else {
100            // Default: Hart 0 S-Mode -> Context 1, Hart 1 S-Mode -> Context 3, etc.
101            (cpu_id as usize * 2) + 1
102        }
103    }
104
105    /// Get the address of a priority register for an interrupt
106    fn priority_addr(&self, interrupt_id: InterruptId) -> usize {
107        self.base_addr + PLIC_PRIORITY_BASE + (interrupt_id as usize * 4)
108    }
109
110    /// Get the address of a pending register for an interrupt
111    fn pending_addr(&self, interrupt_id: InterruptId) -> usize {
112        let word_offset = interrupt_id / 32;
113        self.base_addr + PLIC_PENDING_BASE + (word_offset as usize * 4)
114    }
115
116    /// Get the address of an enable register for a CPU and interrupt
117    fn enable_addr(&self, cpu_id: CpuId, interrupt_id: InterruptId) -> usize {
118        let word_offset = interrupt_id / 32;
119        let context_id = self.context_id_for_cpu(cpu_id);
120        let context_offset = context_id * PLIC_ENABLE_CONTEXT_STRIDE;
121        self.base_addr + PLIC_ENABLE_BASE + context_offset + (word_offset as usize * 4)
122    }
123
124    /// Get the address of a threshold register for a CPU
125    fn threshold_addr(&self, cpu_id: CpuId) -> usize {
126        let context_id = self.context_id_for_cpu(cpu_id);
127        let context_offset = context_id * PLIC_CONTEXT_STRIDE;
128        self.base_addr + PLIC_THRESHOLD_BASE + context_offset
129    }
130
131    /// Get the address of a claim register for a CPU
132    fn claim_addr(&self, cpu_id: CpuId) -> usize {
133        let context_id = self.context_id_for_cpu(cpu_id);
134        let context_offset = context_id * PLIC_CONTEXT_STRIDE;
135        self.base_addr + PLIC_CLAIM_BASE + context_offset
136    }
137
138    /// Validate interrupt ID
139    fn validate_interrupt_id(&self, interrupt_id: InterruptId) -> InterruptResult<()> {
140        if interrupt_id == 0 || interrupt_id > self.max_interrupts {
141            Err(InterruptError::InvalidInterruptId)
142        } else {
143            Ok(())
144        }
145    }
146
147    /// Validate CPU ID
148    fn validate_cpu_id(&self, cpu_id: CpuId) -> InterruptResult<()> {
149        if cpu_id >= self.max_cpus {
150            Err(InterruptError::InvalidCpuId)
151        } else {
152            Ok(())
153        }
154    }
155
156    /// MMIO write with readback verification
157    #[inline(always)]
158    fn mmio_write32_with_readback(addr: usize, value: u32) -> u32 {
159        unsafe {
160            write_volatile(addr as *mut u32, value);
161            crate::arch::mmio_fence();
162            read_volatile(addr as *const u32)
163        }
164    }
165}
166
167impl ExternalInterruptController for Plic {
168    /// Initialize the PLIC
169    fn init(&mut self) -> InterruptResult<()> {
170        crate::early_println!(
171            "[PLIC] init: max_cpus={}, max_interrupts={}, s_mode_contexts={:?}",
172            self.max_cpus,
173            self.max_interrupts,
174            self.s_mode_contexts
175        );
176
177        // Establish a known baseline:
178        // - Disable all interrupts for all contexts first.
179        //   This prevents any firmware/previous-stage configuration from leaking into the kernel.
180        //   Device drivers will later enable only what they need.
181        let word_count = ((self.max_interrupts as usize) + 31) / 32;
182        for cpu_id in 0..self.max_cpus {
183            let context_id = self.context_id_for_cpu(cpu_id);
184            let context_offset = context_id * PLIC_ENABLE_CONTEXT_STRIDE;
185            for word in 0..word_count {
186                let addr = self.base_addr + PLIC_ENABLE_BASE + context_offset + (word * 4);
187                let verify = Self::mmio_write32_with_readback(addr, 0);
188                if verify != 0 {
189                    crate::early_println!(
190                        "PLIC init: clear enable verify failed: cpu={}, context={}, addr={:#x}, read={}",
191                        cpu_id,
192                        context_id,
193                        addr,
194                        verify
195                    );
196                    return Err(InterruptError::HardwareError);
197                }
198            }
199        }
200
201        // Set threshold to 0 for all CPUs (allow all priorities)
202        for cpu_id in 0..self.max_cpus {
203            self.set_threshold(cpu_id, 0)?;
204        }
205
206        // Set all interrupt priorities to 1 (lowest non-zero priority)
207        for interrupt_id in 1..=self.max_interrupts {
208            self.set_priority(interrupt_id, 1)?;
209        }
210
211        Ok(())
212    }
213
214    /// Enable a specific interrupt for a CPU
215    fn enable_interrupt(
216        &mut self,
217        interrupt_id: InterruptId,
218        cpu_id: CpuId,
219    ) -> InterruptResult<()> {
220        self.validate_interrupt_id(interrupt_id)?;
221        self.validate_cpu_id(cpu_id)?;
222
223        let context_id = self.context_id_for_cpu(cpu_id);
224        let addr = self.enable_addr(cpu_id, interrupt_id);
225        let bit_offset = interrupt_id % 32;
226
227        unsafe {
228            let current = read_volatile(addr as *const u32);
229            let new_value = current | (1 << bit_offset);
230            let verify = Self::mmio_write32_with_readback(addr, new_value);
231            if verify != new_value {
232                crate::early_println!(
233                    "PLIC enable_interrupt verify failed: irq={}, cpu={}, context={}, addr={:#x}, bit={}, wrote={}, read={}",
234                    interrupt_id,
235                    cpu_id,
236                    context_id,
237                    addr,
238                    bit_offset,
239                    new_value,
240                    verify
241                );
242                return Err(InterruptError::InvalidInterruptId);
243            }
244        }
245
246        Ok(())
247    }
248
249    /// Disable a specific interrupt for a CPU
250    fn disable_interrupt(
251        &mut self,
252        interrupt_id: InterruptId,
253        cpu_id: CpuId,
254    ) -> InterruptResult<()> {
255        self.validate_interrupt_id(interrupt_id)?;
256        self.validate_cpu_id(cpu_id)?;
257
258        let addr = self.enable_addr(cpu_id, interrupt_id);
259        let bit_offset = interrupt_id % 32;
260
261        unsafe {
262            let current = read_volatile(addr as *const u32);
263            let new_value = current & !(1 << bit_offset);
264            write_volatile(addr as *mut u32, new_value);
265        }
266
267        Ok(())
268    }
269
270    /// Set priority for a specific interrupt
271    fn set_priority(
272        &mut self,
273        interrupt_id: InterruptId,
274        priority: Priority,
275    ) -> InterruptResult<()> {
276        self.validate_interrupt_id(interrupt_id)?;
277
278        if priority > 7 {
279            return Err(InterruptError::InvalidPriority);
280        }
281
282        let addr = self.priority_addr(interrupt_id);
283        let verify = Self::mmio_write32_with_readback(addr, priority);
284        if verify != priority {
285            // Verification failed: MMIO write did not persist the expected value.
286            // Return an InterruptError instead of panicking for consistent error handling.
287            crate::early_println!(
288                "PLIC set_priority verify failed: irq={}, addr={:#x}, wrote={}, read={}",
289                interrupt_id,
290                addr,
291                priority,
292                verify
293            );
294            return Err(InterruptError::InvalidPriority);
295        }
296
297        Ok(())
298    }
299
300    /// Get priority for a specific interrupt
301    fn get_priority(&self, interrupt_id: InterruptId) -> InterruptResult<Priority> {
302        self.validate_interrupt_id(interrupt_id)?;
303
304        let addr = self.priority_addr(interrupt_id);
305        let priority = unsafe { read_volatile(addr as *const u32) };
306
307        Ok(priority)
308    }
309
310    /// Set priority threshold for a CPU
311    fn set_threshold(&mut self, cpu_id: CpuId, threshold: Priority) -> InterruptResult<()> {
312        self.validate_cpu_id(cpu_id)?;
313
314        if threshold > 7 {
315            return Err(InterruptError::InvalidPriority);
316        }
317
318        let addr = self.threshold_addr(cpu_id);
319        let verify = Self::mmio_write32_with_readback(addr, threshold);
320
321        if verify != threshold {
322            // Verification failed: MMIO write did not persist the expected value.
323            // Return an InterruptError instead of panicking for consistent error handling.
324            crate::early_println!(
325                "PLIC set_threshold verify failed: cpu={}, addr={:#x}, wrote={}, read={}",
326                cpu_id,
327                addr,
328                threshold,
329                verify
330            );
331            return Err(InterruptError::InvalidPriority);
332        }
333
334        Ok(())
335    }
336
337    /// Get priority threshold for a CPU
338    fn get_threshold(&self, cpu_id: CpuId) -> InterruptResult<Priority> {
339        self.validate_cpu_id(cpu_id)?;
340
341        let addr = self.threshold_addr(cpu_id);
342        let threshold = unsafe { read_volatile(addr as *const u32) };
343
344        Ok(threshold)
345    }
346
347    /// Claim an interrupt (acknowledge and get the interrupt ID)
348    fn claim_interrupt(&mut self, cpu_id: CpuId) -> InterruptResult<Option<InterruptId>> {
349        self.validate_cpu_id(cpu_id)?;
350
351        let addr = self.claim_addr(cpu_id);
352        let interrupt_id = unsafe { read_volatile(addr as *const u32) };
353
354        if interrupt_id == 0 {
355            Ok(None)
356        } else {
357            Ok(Some(interrupt_id))
358        }
359    }
360
361    /// Complete an interrupt (signal that handling is finished)
362    fn complete_interrupt(
363        &mut self,
364        cpu_id: CpuId,
365        interrupt_id: InterruptId,
366    ) -> InterruptResult<()> {
367        self.validate_cpu_id(cpu_id)?;
368        self.validate_interrupt_id(interrupt_id)?;
369
370        let addr = self.claim_addr(cpu_id);
371        unsafe {
372            write_volatile(addr as *mut u32, interrupt_id);
373            crate::arch::mmio_fence();
374        }
375
376        Ok(())
377    }
378
379    /// Check if a specific interrupt is pending
380    fn is_pending(&self, interrupt_id: InterruptId) -> bool {
381        if self.validate_interrupt_id(interrupt_id).is_err() {
382            return false;
383        }
384
385        let addr = self.pending_addr(interrupt_id);
386        let bit_offset = interrupt_id % 32;
387
388        unsafe {
389            let pending_word = read_volatile(addr as *const u32);
390            (pending_word & (1 << bit_offset)) != 0
391        }
392    }
393
394    /// Get the maximum number of interrupts supported
395    fn max_interrupts(&self) -> InterruptId {
396        self.max_interrupts
397    }
398
399    /// Get the number of CPUs supported
400    fn max_cpus(&self) -> CpuId {
401        self.max_cpus
402    }
403}
404
405unsafe impl Send for Plic {}
406unsafe impl Sync for Plic {}
407
408fn probe_fn(device: &PlatformDeviceInfo) -> Result<(), &'static str> {
409    let res = device.get_resources();
410    if res.is_empty() {
411        return Err("No resources found");
412    }
413
414    // Get memory region resource (res_type == PlatformDeviceResourceType::MEM)
415    let mem_res = res
416        .iter()
417        .find(|r| r.res_type == PlatformDeviceResourceType::MEM)
418        .ok_or("Memory resource not found")?;
419
420    let base_addr = mem_res.start as usize;
421
422    // Try to get PLIC configuration from FDT for proper context mapping
423    let controller =
424        if let Some((max_interrupts, s_mode_contexts)) = get_plic_config_from_fdt(device.name()) {
425            crate::early_println!(
426                "[interrupt] PLIC: FDT config found - ndev={}, contexts={:?}",
427                max_interrupts,
428                s_mode_contexts
429            );
430            Box::new(Plic::with_contexts(
431                base_addr,
432                max_interrupts,
433                s_mode_contexts,
434            ))
435        } else {
436            // Fallback to hardcoded values (TCG-style: M+S per hart)
437            crate::early_println!(
438                "[interrupt] PLIC: Using default config (1023 interrupts, 4 contexts)"
439            );
440            Box::new(Plic::new(base_addr, 1023, 4))
441        };
442
443    match InterruptManager::global()
444        .lock()
445        .register_external_controller(controller)
446    {
447        Ok(_) => {
448            crate::early_println!(
449                "[interrupt] PLIC registered at base address: {:#x}",
450                base_addr
451            );
452        }
453        Err(e) => {
454            crate::early_println!("[interrupt] Failed to register PLIC: {}", e);
455            return Err("Failed to register PLIC");
456        }
457    }
458
459    Ok(())
460}
461
462/// Extract PLIC configuration from FDT
463///
464/// Parses the `riscv,ndev` property for max interrupt count and
465/// `interrupts-extended` property to determine S-mode context IDs per hart.
466///
467/// # Arguments
468/// * `device_name` - The name of the PLIC device node (e.g., "plic@c000000")
469///
470/// # Returns
471/// * `Some((max_interrupts, s_mode_contexts))` on success
472/// * `None` if FDT is not available or properties cannot be read
473fn get_plic_config_from_fdt(device_name: &str) -> Option<(InterruptId, Vec<usize>)> {
474    let fdt_manager = FdtManager::get_manager();
475    let fdt = fdt_manager.get_fdt()?;
476
477    fn read_be_u32(bytes: &[u8]) -> Option<u32> {
478        if bytes.len() < 4 {
479            return None;
480        }
481        Some(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
482    }
483
484    fn get_u32_prop<'a, 'b>(node: &fdt::node::FdtNode<'a, 'b>, name: &str) -> Option<u32> {
485        let prop = node.property(name)?;
486        read_be_u32(prop.value)
487    }
488
489    fn find_node_by_phandle<'a>(
490        fdt: &'a fdt::Fdt<'a>,
491        phandle: u32,
492    ) -> Option<fdt::node::FdtNode<'a, 'a>> {
493        let mut stack: alloc::vec::Vec<fdt::node::FdtNode<'a, 'a>> = alloc::vec::Vec::new();
494        stack.push(fdt.find_node("/")?);
495
496        while let Some(node) = stack.pop() {
497            if let Some(p) = get_u32_prop(&node, "phandle") {
498                if p == phandle {
499                    return Some(node);
500                }
501            }
502            for child in node.children() {
503                stack.push(child);
504            }
505        }
506
507        None
508    }
509
510    // Find the PLIC node in /soc
511    let soc = fdt.find_node("/soc")?;
512    let plic_node = soc.children().find(|node| node.name == device_name)?;
513
514    // Read riscv,ndev property for max interrupt count
515    let max_interrupts = plic_node
516        .property("riscv,ndev")
517        .and_then(|prop| {
518            if prop.value.len() >= 4 {
519                Some(u32::from_be_bytes([
520                    prop.value[0],
521                    prop.value[1],
522                    prop.value[2],
523                    prop.value[3],
524                ]))
525            } else {
526                None
527            }
528        })
529        .unwrap_or(1023);
530
531    // Read interrupts-extended property to find S-mode contexts.
532    // The entry size depends on the referenced interrupt-controller node's
533    // #interrupt-cells, so we must decode it dynamically.
534    //
535    // Typical RISC-V CPU interrupt controller uses #interrupt-cells = <1>
536    // and provides irq_type values:
537    // - 9  = Supervisor External Interrupt (SEI)
538    // - 11 = Machine External Interrupt (MEI)
539    let s_mode_contexts = plic_node
540        .property("interrupts-extended")
541        .map(|prop| {
542            let mut contexts = Vec::new();
543            let mut offset = 0usize;
544            let mut context_id = 0usize;
545            let bytes = prop.value;
546
547            while offset + 4 <= bytes.len() {
548                let phandle = match read_be_u32(&bytes[offset..offset + 4]) {
549                    Some(v) => v,
550                    None => break,
551                };
552                offset += 4;
553
554                let intc_node = find_node_by_phandle(fdt, phandle);
555                let interrupt_cells = intc_node
556                    .as_ref()
557                    .and_then(|n| get_u32_prop(n, "#interrupt-cells"))
558                    .unwrap_or(1) as usize;
559
560                if interrupt_cells == 0 {
561                    break;
562                }
563                let needed = interrupt_cells.saturating_mul(4);
564                if offset + needed > bytes.len() {
565                    break;
566                }
567
568                // Interpret the first interrupt cell as the irq_type.
569                let irq_type = read_be_u32(&bytes[offset..offset + 4]).unwrap_or(0);
570                if irq_type == 9 {
571                    contexts.push(context_id);
572                }
573
574                offset += needed;
575                context_id += 1;
576            }
577
578            // Backward-compatible fallback: if decoding failed (e.g. phandle lookup),
579            // fall back to fixed 2-cell entries.
580            if contexts.is_empty() {
581                for (idx, chunk) in bytes.chunks_exact(8).enumerate() {
582                    let irq_type = u32::from_be_bytes([chunk[4], chunk[5], chunk[6], chunk[7]]);
583                    if irq_type == 9 {
584                        contexts.push(idx);
585                    }
586                }
587            }
588
589            contexts
590        })
591        .unwrap_or_else(Vec::new);
592
593    if s_mode_contexts.is_empty() {
594        return None;
595    }
596
597    Some((max_interrupts, s_mode_contexts))
598}
599
600fn remove_fn(_device: &PlatformDeviceInfo) -> Result<(), &'static str> {
601    Ok(())
602}
603
604fn register_driver() {
605    let driver = PlatformDeviceDriver::new(
606        "riscv-plic",
607        probe_fn,
608        remove_fn,
609        vec!["sifive,plic-1.0.0", "riscv,plic0"],
610    );
611    // Register the driver with the kernel
612    DeviceManager::get_manager().register_driver(Box::new(driver), DriverPriority::Critical)
613}
614
615// driver_initcall!(register_driver);
616early_initcall!(register_driver);
617
618#[cfg(test)]
619mod tests {
620    use super::*;
621
622    #[test_case]
623    fn test_plic_creation() {
624        let plic = Plic::new(0x1000_0000, 100, 8);
625        assert_eq!(plic.max_interrupts(), 100);
626        assert_eq!(plic.max_cpus(), 8);
627    }
628
629    #[test_case]
630    fn test_address_calculation() {
631        let plic = Plic::new(0x1000_0000, 100, 8);
632
633        // Test priority address
634        assert_eq!(plic.priority_addr(1), 0x1000_0004);
635        assert_eq!(plic.priority_addr(10), 0x1000_0028);
636
637        // Test enable address for S-Mode
638        // CPU 0 -> Context 1
639        assert_eq!(plic.enable_addr(0, 10), 0x1000_2080);
640        // CPU 1 -> Context 3
641        assert_eq!(plic.enable_addr(1, 40), 0x1000_2184);
642
643        // Test threshold address for S-Mode
644        // CPU 0 -> Context 1
645        assert_eq!(plic.threshold_addr(0), 0x1020_1000);
646        // CPU 1 -> Context 3
647        assert_eq!(plic.threshold_addr(1), 0x1020_3000);
648
649        // Test claim address for S-Mode
650        // CPU 0 -> Context 1
651        assert_eq!(plic.claim_addr(0), 0x1020_1004);
652        // CPU 1 -> Context 3
653        assert_eq!(plic.claim_addr(1), 0x1020_3004);
654    }
655
656    #[test_case]
657    fn test_validation() {
658        let plic = Plic::new(0x1000_0000, 100, 8);
659
660        // Valid IDs should pass
661        assert!(plic.validate_interrupt_id(1).is_ok());
662        assert!(plic.validate_interrupt_id(100).is_ok());
663        assert!(plic.validate_cpu_id(0).is_ok());
664        assert!(plic.validate_cpu_id(7).is_ok());
665
666        // Invalid IDs should fail
667        assert!(plic.validate_interrupt_id(0).is_err());
668        assert!(plic.validate_interrupt_id(101).is_err());
669        assert!(plic.validate_cpu_id(8).is_err());
670    }
671}