This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Vector Table relocation on M33/M55

I'm trying to relocate and dynamically change the vector table's contents. Keeping this in mind, I have the following code:

sample.c:

extern void svc_handler(void);

volatile uint32_t *VTOR;
stl_vect_t *vect = ptr; // I've ensured that ptr is aligned to 1024B and has sufficient space
VTOR = 0xE000ED08u;
vect->SVC = svc_handler; // svc_handler is a custom function defined elsewhere
*VTOR = vect; // Don't care about the other exceptions.
// ......
// Some Code
// ......
svc #0

where,

sample.h:

typedef struct {
uint32_t *SPMain;
void (*Reset)(void);
void (*NMI)(void);
void (*HardFault)(void);
void (*MemManage)(void);
void (*BusFault)(void);
void (*UsageFault)(void);
void (*SecureFault)(void);
uint32_t Res[3];
void (*SVC)(void);
void (*DebugMonitor)(void);
uint32_t Res4;
void (*PendSV)(void);
void (*SysTick)(void);
void (*IRQ[NUMIRQ])(void);
} stl_vect_t __attribute__((aligned(1024)));

The code in sample.c seems to trigger an SVC, however, this keeps escalating to a HardFault due to the SVC. It seems like this is due to an INVST fault upon SVC entry. I've checked to ensure that the vect->SVC[0] is set to 1. Any other reason this can come up?

Thanks in advance.


Update 23/11/2020: I see that the EPSR.T bit is set to 0 upon the attempt to handle the SVC - Can't understand why. As noted earlier, I can see that vect->SVC's bit[0] is set to '1'. Also, there's definitely a thumb instruction at svc_handler.


Update 24/11/2020: I made the following changes:

sample.c:

extern void svc_handler(void);
const stl_vect_t vect = {.SVC = svc_handler}; // Global allocated in CODE-Region

....
volatile uint32_t *VTOR;
VTOR = 0xE000ED08u;
*VTOR = (uint32_t) &vect;
// ......
// Some Code
// ......
svc #0

Now, this seems to work perfectly!

Again, after make just one change from this code,

sample.c:

extern void svc_handler(void);
stl_vect_t vect; // Global allocated in SRAM-Region

....
volatile uint32_t *VTOR;
VTOR = 0xE000ED08u;
vect.SVC = svc_handler;
*VTOR = (uint32_t) &vect;
// ......
// Some Code
// ......
svc #0

This causes an INVSTATE exception. Any idea why declaring vect as a non-const causes an INVSTATE fault? The v8M architecture specification clearly states that you locate the vector table in the CODE, SRAM, External RAM, or External Device areas of the system memory map. So I would assume that nothing in the architecture should stop anyone changing the VectorTable entries at runtime. Yet it seems this is the issue.

Parents
  • Finally found the root-cause for this:
    The issue was related to a bad handling of memory in the D-cache.

    extern void svc_handler(void);
    const stl_vect_t vect = {.SVC = svc_handler}; // Global allocated in CODE-Region
    
    ....
    volatile uint32_t *VTOR;
    VTOR = 0xE000ED08u;
    *VTOR = (uint32_t) &vect;
    // ......
    // Some Code
    // ......
    svc #0

    This works because the I-cache actually fetches the correct data at vect.SVC which is initialized before any instruction execution. In the second case i.e.,

    extern void svc_handler(void);
    stl_vect_t vect; // Global allocated in SRAM-Region
    
    ....
    volatile uint32_t *VTOR;
    VTOR = 0xE000ED08u;
    vect.SVC = svc_handler;
    *VTOR = (uint32_t) &vect;
    // ......
    // Some Code
    // ......
    svc #0

    I did not actually flush out the write from the D-cache. As a result, a stale copy in the I-cache kept being invoked on the SVC. The fix would be:

    extern void svc_handler(void);
    stl_vect_t vect; // Global allocated in SRAM-Region
    
    ....
    volatile uint32_t *VTOR, *DCCIMVAC, *ICCIALLU;
    VTOR = 0xE000ED08u;
    DCCIMVAC = 0xE000EF70u;
    ICCIALLU = 0xE000EF50u;
    vect.SVC = svc_handler;
    *VTOR = (uint32_t) &vect;
    *DCCIMVAC = &vect.SVC;
    *ICCIALLU = 0x0u;
    // ......
    // Some Code
    // ......
    svc #0

    This works! I should have been more careful here as ARM v8M does say "If the vector table is located in a region of memory that is cacheable, you must treat any store to the vector as self-modifying code and use cache maintenance instructions to synchronize the update".

Reply
  • Finally found the root-cause for this:
    The issue was related to a bad handling of memory in the D-cache.

    extern void svc_handler(void);
    const stl_vect_t vect = {.SVC = svc_handler}; // Global allocated in CODE-Region
    
    ....
    volatile uint32_t *VTOR;
    VTOR = 0xE000ED08u;
    *VTOR = (uint32_t) &vect;
    // ......
    // Some Code
    // ......
    svc #0

    This works because the I-cache actually fetches the correct data at vect.SVC which is initialized before any instruction execution. In the second case i.e.,

    extern void svc_handler(void);
    stl_vect_t vect; // Global allocated in SRAM-Region
    
    ....
    volatile uint32_t *VTOR;
    VTOR = 0xE000ED08u;
    vect.SVC = svc_handler;
    *VTOR = (uint32_t) &vect;
    // ......
    // Some Code
    // ......
    svc #0

    I did not actually flush out the write from the D-cache. As a result, a stale copy in the I-cache kept being invoked on the SVC. The fix would be:

    extern void svc_handler(void);
    stl_vect_t vect; // Global allocated in SRAM-Region
    
    ....
    volatile uint32_t *VTOR, *DCCIMVAC, *ICCIALLU;
    VTOR = 0xE000ED08u;
    DCCIMVAC = 0xE000EF70u;
    ICCIALLU = 0xE000EF50u;
    vect.SVC = svc_handler;
    *VTOR = (uint32_t) &vect;
    *DCCIMVAC = &vect.SVC;
    *ICCIALLU = 0x0u;
    // ......
    // Some Code
    // ......
    svc #0

    This works! I should have been more careful here as ARM v8M does say "If the vector table is located in a region of memory that is cacheable, you must treat any store to the vector as self-modifying code and use cache maintenance instructions to synchronize the update".

Children
No data