1use 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
24const 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
31const PLIC_ENABLE_CONTEXT_STRIDE: usize = 0x80;
33const PLIC_CONTEXT_STRIDE: usize = 0x1000;
35
36const MAX_INTERRUPTS: InterruptId = 1024;
38
39const MAX_CPUS: CpuId = 15872; pub struct Plic {
44 base_addr: usize,
46 max_interrupts: InterruptId,
48 max_cpus: CpuId,
50 s_mode_contexts: Option<Vec<usize>>,
54}
55
56impl Plic {
57 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 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 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 (cpu_id as usize * 2) + 1
102 }
103 }
104
105 fn priority_addr(&self, interrupt_id: InterruptId) -> usize {
107 self.base_addr + PLIC_PRIORITY_BASE + (interrupt_id as usize * 4)
108 }
109
110 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 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 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 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 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 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 #[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 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 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 for cpu_id in 0..self.max_cpus {
203 self.set_threshold(cpu_id, 0)?;
204 }
205
206 for interrupt_id in 1..=self.max_interrupts {
208 self.set_priority(interrupt_id, 1)?;
209 }
210
211 Ok(())
212 }
213
214 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 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 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 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 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 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 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 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 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 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 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 fn max_interrupts(&self) -> InterruptId {
396 self.max_interrupts
397 }
398
399 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 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 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 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
462fn 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 let soc = fdt.find_node("/soc")?;
512 let plic_node = soc.children().find(|node| node.name == device_name)?;
513
514 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 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 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 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 DeviceManager::get_manager().register_driver(Box::new(driver), DriverPriority::Critical)
613}
614
615early_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 assert_eq!(plic.priority_addr(1), 0x1000_0004);
635 assert_eq!(plic.priority_addr(10), 0x1000_0028);
636
637 assert_eq!(plic.enable_addr(0, 10), 0x1000_2080);
640 assert_eq!(plic.enable_addr(1, 40), 0x1000_2184);
642
643 assert_eq!(plic.threshold_addr(0), 0x1020_1000);
646 assert_eq!(plic.threshold_addr(1), 0x1020_3000);
648
649 assert_eq!(plic.claim_addr(0), 0x1020_1004);
652 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 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 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}