blippy
Posts: 68
Joined: Fri Nov 03, 2017 3:07 pm

Setting up exceptions made easy (??)

Mon Jul 27, 2020 6:30 am

I've been trying to follow through the setting of interrupts. It seems quite complicated from the examples I've seen.

Seeings as we know where the interrupt table is, why don't we just set the relevant location? E.g.

Code: Select all

void my_swi_routine()
{
  ....
 }
 
 void kernel_main()
 {
     *0x8 = my_swi_routine;
     ...
}     
I figure I must be missing something.

Schnoogle
Posts: 131
Joined: Sun Feb 11, 2018 4:47 pm

Re: Setting up exceptions made easy (??)

Mon Jul 27, 2020 7:49 am

Hi,

well there is a reason that interrupt/exception handler seem complicated at first look:
Any exception is interrupting the process flow on an arbitrary position where any number of registers has a specific value.
If the CPU calls the exception handler it expects to find the register values after the return of such exception handler to have the same values they had before.

If you would just put an pointer to your exception handler routine this one will quite likely mess with several registers - if the handler is written in C the compiler does only need to adhere to the ABI call conventions. So it is quite likely that once your handler returns and normal processing continues this will cause unpredictable behavior.

For this reason it's kind of best practice to craft some stub code in assembly to ensure once the exception is raised the specific registers will be stored/safed on stack before calling the respective handler code that quite likely is written in C or any other language.

This approach also ensures that you can handle special cases like re-entrant interrupts, exceptions that are recoverable and exceptions that are not recoverable and should lead to halting the core.

There are several different examples out there to see whoe such a stub could look like.
Please be aware that those stubs also differ for aarch32 and aarch64 systems.

blippy
Posts: 68
Joined: Fri Nov 03, 2017 3:07 pm

Re: Setting up exceptions made easy (??)

Tue Jul 28, 2020 7:22 am

Well, I managed to get something working. I'm not sure I entirely understand how it gets set up, but and actual IRQ handler function itself doesn't seem that difficult: save the registers, do your stuff, clear the handling flag, restore the registers.

One thing I did notice is that a common way of setting up the vector table is something like:

Code: Select all

section .text.boot
.globl _start
_start:
    ldr pc,reset_handler
    ldr pc,undefined_handler
    ldr pc,swi_handler
    ldr pc,prefetch_handler
    ldr pc,data_handler
    ldr pc,unused_handler
    ldr pc,irq_handler
    ldr pc,fiq_handler
reset_handler:      .word reset
undefined_handler:  .word hang
swi_handler:        .word hang
prefetch_handler:   .word hang
data_handler:       .word hang
unused_handler:     .word hang
irq_handler:        .word irq
fiq_handler:        .word hang

This seems a little odd to me. That code is put in address 0x8000 (depending on architecture). But you really want the vectors 0x0000. So why not tell the linker that that's where you want to put the vectors? After all, the linker allows you to lay out memory however you want.

Is there any reason it isn't done the way I suggested? My approach seems a little too obvious.

LdB
Posts: 1645
Joined: Wed Dec 07, 2016 2:29 pm

Re: Setting up exceptions made easy (??)

Tue Jul 28, 2020 8:10 am

If you use the VC loader it places the image file starting at 0x8000 so you can't load code directly at 0x0 :-)
You can only do that if you use the settings in config.sys to run non standard defaults.

So using defaults you have to copy a table from somewhere beyond 0x8000 down to 0x0.
Why the VC starts the load there is a whole other question that I am not sure has an answer other than that is the way it is.

The only reason I have ever seen comes from ARM linux documentation ... under convention
http://www.simtec.co.uk/products/SWLINU ... ticle.html
Despite the ability to place zImage anywhere within memory, convention has it that it is loaded at the base of physical RAM plus an offset of 0x8000 (32K). This leaves space for the parameter block usually placed at offset 0x100, zero page exception vectors and page tables. This convention is very common.

blippy
Posts: 68
Joined: Fri Nov 03, 2017 3:07 pm

Re: Setting up exceptions made easy (??)

Tue Jul 28, 2020 8:39 am

blippy wrote:
Mon Jul 27, 2020 6:30 am
Seeings as we know where the interrupt table is, why don't we just set the relevant location? E.g.

Code: Select all

void my_swi_routine()
{
  ....
 }
 
 void kernel_main()
 {
     *0x8 = my_swi_routine;
     ...
}     
I figure I must be missing something.
Aha. I see what I'm missing. The point is that the interrupt table is not a vector of addresses, but a vector of instructions. swi_routine is an address, not an instruction, so that approach won't work.

An instruction in the interrupt table might be:

Code: Select all

ldr pc, irq_handler
So what's happening here is that that instruction will be executed, which sets the program counter to irq_handler, making it a branch instruction.

Phew. So many things to figure out!

blippy
Posts: 68
Joined: Fri Nov 03, 2017 3:07 pm

Re: Setting up exceptions made easy (??)

Tue Jul 28, 2020 9:35 am

LdB wrote:
Tue Jul 28, 2020 8:10 am
You can only do that if you use the settings in config.sys to run non standard defaults.
Ah, I see. That explains it.

Also, why are the instructions of the form

Code: Select all

ldr pc,reset_handler
rather than

Code: Select all

b reset_handler
I'm guessing that it's because the latter jumps relatively, whilst the former jumps absolutely. So, if you relocate the instructions then the relative offsets will be wrong.

Am I correct in my thinking, or wide of the mark?

Return to “Bare metal, Assembly language”