Categories
Uncategorized

advpi – Week 9 – Interrupts?

This week I’m trying to process/read interrupts. This is not setting an interrupt table, but rather understanding how I can insert interrupts into the ARM CPU.

This will be useful in adding timers and other external input to the CPU.

Initial Research: No VGIC

Initial research led me to the KVM documentation, asking me to create a VGIC. It promised to solve my problems and I would be able to raise interrupts through that. Then well.

https://forums.raspberrypi.com/viewtopic.php?t=161697

The RPI does not have a VGIC, which the KVM API requires to set up the device. In fact, trying to query for the feature KVM_CAP_IRQCHIP, shows that this device doesn’t have it.

Bummer. 🙁

The Saviour: KVM_IRQ_LINE

After rummaging through documentation, I see this line in the KVM API, for the KVM_IRQ_LINE api:

KVM_IRQ_LINE can always be used for a userspace interrupt controller.

So, I whipped up a tiny program that would write to a memory location, then sleep till an interrupt.

mov r0,#0x4000

nop
nop
nop
add r0,r0, #0x0004
mov r1,#0x1500
mov r2,#0x4049
str r2,[r0]
WFI

sub r15,r15,#28

The program has a no-op sled just because I don’t remember the exact PC target location I should achieve. I know its standard in malicious software, but this works good enough.

tl;dr – This program will make the vm execution pause until any interrupt, then will resume. I’m assuming some implementations use a NOOP, but this is worth a try.

After the write to the MMIO location 0x4004, which I’ve designated for logging right now, it pauses. Awesome!

Raising an interrupt

So, to raise an interrupt, I cooked up a dirty piece of code – a thread that raises an interrupt after 4 seconds, then switches it off immediately.

std::thread t1([&](){
        sleep(4);
        struct kvm_irq_level level = {
            .irq = 0b0001,
            .level=1
        };
        int res = ioctl(this->vmFd,KVM_IRQ_LINE,&level);

        spdlog::warn("PUT INTERRUPT thread, status={}",res);
        
        //turn it off
        level.level = 0;
        res = ioctl(this->vmFd,KVM_IRQ_LINE,&level);
        spdlog::warn("OUT INTERRUPT thread, status={}",res);
    });

With this, I get the same as before, but –

We get one more round of execution! And it reads the next memory element 0x4008!

Conclusion

Small bit of work, but that’s mostly because of other work at hand and that such things often require deep dives.

The next steps will be to have some form of response from the CPU to the interrupt. Some form of ISRs would be nice to verify how these are working.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.