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 syscall
s 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 = .;
}
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.