Skip to content

Commit e1dbf04

Browse files
authored
Update ch 8. Scheduling.md
1 parent d4d0102 commit e1dbf04

1 file changed

Lines changed: 103 additions & 1 deletion

File tree

‎ch 8. Scheduling.md‎

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,106 @@ Now, before we dive deeper into the scheduler architecture, let us first make th
2828

2929
## Identifying IRQ Exceptions
3030

31-
31+
In this section we're simply going to introduce a branch for the exception handling. In this new branch the exception handler will identify that the current exception came from an IRQ interrupt. It is also going to identify that among all the hardware events, it came from the timer event.
32+
33+
This process is similar to how we identified `svc` caused exception back in chapter 5 on syscalls. In it, firstly we narrowed the exception down to SYNC type from the exception context. And then we narrowed it down to an `svc` based exception through the information encoded in the exception syndrome register `ESR_EL1`.
34+
35+
The process for a timer exception is the same. You know it is an IRQ from the exception context. However for narrowing it down further, instead of reading from the exception syndrome register, you need to read from a different register. This register is in fact located in the interrupt handler QA7. We can find it in [the documentation](https://github.com/Tekki/raspberrypi-documentation/blob/master/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf) we refered to in the last chapter. In it open topic **4.10 Core interrupt sources**. In it, it depicts four registers for each of the four cores in the CPU. It has 18 bits of data, where each field is associated with one possible IRQ source. When an IRQ occurs, associated field in this register is set to 1. The CPU can then read this register and know which source an IRQ may have come from.
36+
37+
<img width="642" height="450" alt="image" src="https://github.com/user-attachments/assets/2fde94ff-fa81-48c6-8737-a46cb62eaec7" />
38+
39+
There's also four congruent registers which serve this exact same purpose but for FIQs.
40+
41+
### Abstraction
42+
43+
Let's quickly introduce a basic abstraction for this in our `Interrupts` struct.
44+
45+
```rust
46+
// Core interrupt sources
47+
pub const CORE_0_IRQ_SRC: *const u32 = (QA7_BASE + 0x60) as *const u32;
48+
pub const CORE_0_FIQ_SRC: *const u32 = (QA7_BASE + 0x70) as *const u32;
49+
50+
#[repr(u32)]
51+
#[derive(Copy, Clone, Debug)]
52+
pub enum InterruptSource {
53+
PhysicalSecureTimer = 1 << 0,
54+
PhysicalNonSecureTimer = 1 << 1,
55+
HypervisorTimer = 1 << 2,
56+
VirtualTimer = 1 << 3,
57+
Mailbox0 = 1 << 4,
58+
Mailbox1 = 1 << 5,
59+
Mailbox2 = 1 << 6,
60+
Mailbox3 = 1 << 7,
61+
GPU = 1 << 8,
62+
PMU = 1 << 9,
63+
AXI = 1 << 10,
64+
LocalTimer = 1 << 11,
65+
PeripheralInterrupt = 0x3f << 12, // last 6 bits are for peripheral interrupts
66+
// we don't know yet how they are implemented and used so for now i just wrote it this way
67+
// so (irq_sources_register_value | PeripheralInterrupt) would give you the entire peripheral
68+
// interrupts field.
69+
}
70+
```
71+
72+
Then, inside the struct implementation:
73+
74+
```rust
75+
pub fn pending_irq() -> u32 {
76+
unsafe { read_volatile(CORE_0_IRQ_SRC) }
77+
}
78+
79+
pub fn pending_fiq() -> u32 {
80+
unsafe { read_volatile(CORE_0_FIQ_SRC) }
81+
}
82+
83+
pub fn is_irq_pending(source: InterruptSource) -> bool {
84+
(Self::pending_irq() & (source as u32)) != 0
85+
}
86+
87+
pub fn is_fiq_pending(source: InterruptSource) -> bool {
88+
(Self::pending_fiq() & (source as u32)) != 0
89+
}
90+
```
91+
92+
Now, our interrupts abstraction is ready to handle queries related to IRQ and FIQ identification. We can now go on and create a new branch in the exception handler for an IRQ, and inside it for an IRQ which came from the NSP timer.
93+
94+
### Implementation
95+
96+
Inside `exceptions.rs`
97+
```rust
98+
use crate::kernel::interrupts::{Interrupts, InterruptSource};
99+
```
100+
And then afterwards, inside the main exception handler function, add a new `match` branch for IRQ type exceptions.
101+
102+
```
103+
// called by `exceptions.s`
104+
#[unsafe(no_mangle)]
105+
pub extern "C" fn handle_exception_el1(ctx: &mut ExceptionContext) {
106+
107+
// handling the exception based on the type.
108+
match ctx.etype {
109+
ExceptionType::_SYNC => handle_sync_exception(ctx),
110+
ExceptionType::_IRQ => handle_irq_exception(ctx),
111+
_ => unhandled_exception!(ctx),
112+
}
113+
114+
}
115+
```
116+
117+
Then we will create the needed `handle_irq_exception(ctx)` function as follows:
118+
119+
```rust
120+
fn handle_irq_exception(ctx: &mut ExceptionContext) -> () {
121+
let mut irq_sources: u32 = Interrupts::pending_irq();
122+
123+
if irq_sources & (InterruptSource::PhysicalNonSecureTimer as u32) != 0 {
124+
PhysicalTimer::handle_irq(ctx);
125+
irq_sources &= !(InterruptSource::PhysicalNonSecureTimer as u32);
126+
}
127+
128+
if irq_sources > 0 {
129+
println!("Other Unhandled IRQ sources pending: {:#x}", irq_sources).unwrap();
130+
unhandled_exception!(ctx);
131+
}
132+
}
133+
```

0 commit comments

Comments
 (0)