cassemblyarmkernel-modeusermode

How to implement SVC handler on ARM926EJ-S?


I'm writing an amateur operating system for ARM-based devices and currently trying to make it working in QEMU's versatilepb (ARM926EJ-S).

The problem arrives when I try to implement syscalls to my kernel. The idea is pretty simple: to implement system calls via SVC (SWI) instruction. So applications work in user mode, and to call a kernel function, they do SVC <code> instruction, so ARM processor switches to supervisor mode and calls the appropriate SVC handler.

But the problem is that when I call __asm__("SVC #0x08");, the device just resets and calls RESET_HANDLER, so it looks like the emulator just reboots.

I spent a few hours already to figure out what is the problem, but still got no idea.

Here is the code of ivt.s (the initial code with handlers):

.global __RESET

__RESET:
    B RESET_HANDLER /* Reset */
    B . /* Undefined */
    B SWI_HANDLER   /* SWI */
    B . /* Prefetch Abort */
    B . /* Data Abort */
    B . /* reserved */
    B . /* IRQ */
    B . /* FIQ */

RESET_HANDLER:
    MSR CPSR_c, 0x13 /* Supervisor mode */
    LDR SP, =stack_top
    MSR CPSR_c, 0x10 /* User mode */
    LDR SP, =usr_stack_top
    BL  usermode_function
    B   .

SWI_HANDLER:
    PUSH    {LR}
    BL      syscall
    POP     {LR}
    MOVS    PC, LR

This is how I make the syscall:

void usermode_function() {
    __asm__("SVC #0x00"); // Make syscall
}

And syscall implementation:

void syscall() {
    // NEVER CALLED
    __asm__("PUSH {r0-r7}");
    __asm__("POP {r0-r7}");
}

But the code under SWI_HANDLER even never invoked.

I really even don't know how to ask the question, since it looks like I'm missing some very basic information in my mind.

So what could be the problem? Which information I should provide to make you able to help me?

Here is also the linker script:

ENTRY(__RESET)
SECTIONS
{
    . = 0x10000;
    .ivt .  : { ivt.o(.text) }
    .text   : { *(.text) }
    .data   : { *(.data) }
    .bss    : { *(.bss COMMON) }
    . = ALIGN(8);
    . = . + 0x1000; /* 4KB of stack memory */
    stack_top = .;
    . = . + 0x100;
    usr_stack_top = .;
}

Solution

  • Many thanks to @Jester and @old_timer, the problem is solved.

    The problem was not with code, but with linker script. I have put my vector table at 0x10000, as you can see in the linker script, but it should be placed at 0x0. So SVC was not handled properly because the handler was placed in a wrong place.

    When I changed the base address in my ld script and tried to load the firmware as ELF, everything starts to work perfectly.