Compare commits

..

5 commits

Author SHA1 Message Date
babbleberry
71c3b30bd0 Updated part14 to reflect changes to part13 2024-02-06 12:22:54 +00:00
babbleberry
31bb21b323 Fixing typo 2024-02-06 12:18:01 +00:00
babbleberry
e087746a5d Updated README for part13-interrupts 2024-02-06 12:16:07 +00:00
babbleberry
9566e13a7f Added irq_barrier to part13-interrupts just to be sure 2024-02-06 12:01:39 +00:00
babbleberry
3faf8d10ff Fixed a bug in the interrupt handler in part13 to avoid an infinite loop 2024-02-06 11:56:03 +00:00
9 changed files with 26 additions and 4 deletions

View file

@ -35,6 +35,7 @@ The timers are set up using these calls:
```c
irq_init_vectors();
enable_interrupt_controller();
irq_barrier();
irq_enable();
timer_init();
```
@ -77,7 +78,7 @@ In the middle we simply call a function called `handle_irq()` which is written i
void handle_irq() {
unsigned int irq = REGS_IRQ->irq0_pending_0;
while(irq) {
while(irq & (SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3)) {
if (irq & SYS_TIMER_IRQ_1) {
irq &= ~SYS_TIMER_IRQ_1;
@ -117,7 +118,9 @@ To begin receiving interrupts, we need to take one more step: unmasking all type
Masking is a technique used by the CPU to prevent a particular piece of code from being stopped in its tracks by an interrupt. It's used to protect important code that *must* complete. Imagine what would happen if our `kernel_entry` code (that saves register state) was interrupted halfway through! In this case, the register state would be overwritten and lost. This is why the CPU automatically masks all interrupts when an exception handler is executed.
The `irq_enable` and `irq_disable` functions in _utils.S_ are responsible for masking and unmasking interrupts:
The `irq_enable` and `irq_disable` functions in _utils.S_ are responsible for masking and unmasking interrupts.
They are helped by the `irq_barrier` function which ensures that the `enable_interrupt_controller()` call properly finishes before the `irq_enable()` call is made:
```c
.globl irq_enable
@ -129,6 +132,11 @@ irq_enable:
irq_disable:
msr daifset, #2
ret
.globl irq_barrier
irq_barrier:
dsb sy
ret
```
As soon as `irq_enable()` is called from `main()` in _kernel.c_, the timer handler is run when the timer interrupt fires. Well, sort of...!

View file

@ -11,7 +11,7 @@ void disable_interrupt_controller() {
void handle_irq() {
unsigned int irq = REGS_IRQ->irq0_pending_0;
while(irq) {
while(irq & (SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3)) {
if (irq & SYS_TIMER_IRQ_1) {
irq &= ~SYS_TIMER_IRQ_1;

View file

@ -120,6 +120,7 @@ void main(void)
irq_init_vectors();
enable_interrupt_controller();
irq_barrier();
irq_enable();
timer_init();

View file

@ -38,6 +38,7 @@ enum vc_irqs {
void irq_init_vectors();
void irq_enable();
void irq_barrier();
void irq_disable();
void enable_interrupt_controller();
void disable_interrupt_controller();

View file

@ -13,3 +13,8 @@ irq_enable:
irq_disable:
msr daifset, #2
ret
.globl irq_barrier
irq_barrier:
dsb sy
ret

View file

@ -11,7 +11,7 @@ void disable_interrupt_controller() {
void handle_irq() {
unsigned int irq = REGS_IRQ->irq0_pending_0;
while(irq) {
while(irq & (SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3)) {
if (irq & SYS_TIMER_IRQ_1) {
irq &= ~SYS_TIMER_IRQ_1;

View file

@ -147,6 +147,7 @@ void main(void)
irq_init_vectors();
enable_interrupt_controller();
irq_barrier();
irq_enable();
timer_init();

View file

@ -38,6 +38,7 @@ enum vc_irqs {
void irq_init_vectors();
void irq_enable();
void irq_barrier();
void irq_disable();
void enable_interrupt_controller();
void disable_interrupt_controller();

View file

@ -13,3 +13,8 @@ irq_enable:
irq_disable:
msr daifset, #2
ret
.globl irq_barrier
irq_barrier:
dsb sy
ret