You are clearly struggeling with this, so let me try to explain several things in more detail.
Let's start again with the System V x86_64 ABI. When discussing the registers, there are four different things that you must be aware of.
- The first six arguments (assuming they are integers or pointers) are passed via the registers (rdi, rsi, rdx, rcx, r8, r9). If there are additional arguments (or the arguments are NOT integers/pointers), the values are stored on the stack. I will not discuss this.
- The registers (rbp, rbx, r12, r13, r14, r15) are so-called "callee-save registers", which means that these registers must be saved by the function that you are calling (in this case your interrupt handler).
- All remaining registers are "caller-save registers", which means that these registers must be saved by the function that makes the call. Of course, these registers should only be saved if they actually contain information needed after the call has completed.
- The rax register is used to store the return value from the callee.
However, to ensure that all of these things are satisfied, it might be simpler if you save ALL the general purpose registers in the interrupt handler, to ensure that nothing breaks. This is probably what you already do in your code for exception handling.
Now concerning your code, it seems that you are mixing your syscall handler and your exception handler. This is wrong, these two things have nothing in common, except that the assembly code looks similar, because it has to save and restore register.
Code:
isr128:
cli
push byte 0 ; THIS IS RELATED TO YOUR EXCEPTION HANDLER, IT DOES NOT SERVE ANY PURPOSE HERE
push byte 80 ; THIS IS RELATED TO YOUR EXCEPTION HANDLER, IT DOES NOT SERVE ANY PURPOSE HERE
; The System V x86_64 calling convention...
push rax ; THE PURPOSE OF PUSHING THESE VALUES ON THE STACK IS TO ACCESS THEM IN syscall_wrapper NOT TO SAVE THEM
push rcx
push rdx
push rsi
push rdi
cld
mov rdi, rsp
call syscall_wrapper
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
jmp isr_common_stub ; HERE YOU ARE (PRESUMABLY) CALLING YOUR EXCEPTION HANDLER, WHY?
Depending on how you invoke the above assembly code, the arguments for the syscall_wrapper are, as previously mentioned, stored in the registers (rdi, rsi, rdx, rcx, r8, r9). If we simply want something that works (but not necessarily something that is particularly clever), we can do the following.
Code:
isr128:
cli
push rax ; Push all general purpose registers
push rbx
push rcx
push rdx
push rdi
push rsi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rdi, rsp ; Store the stack pointer in rdi. The first argument of syscall_wrapper is now a pointer to the values stored on the stack.
call syscall_wrapper
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rsi
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
iretq
Code:
struct _stack {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rbp;
uint64_t rsi;
uint64_t rdi;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
};
void syscall_wrapper(struct _stack *stack)
{
switch (stack->rax) {
case 4:
print_string((char*)stack->rcx);
break;
default:
break;
}
}
In this code, pushing all the general purpose registers has a dual purpose. We want these registers to be accessible in syscall_wrapper and we want to ensure that they are correctly restored when the function returns. Again, depending on how you invoke "isr128" this can perhaps be done in a smarter way, but then it really depends on the specific design details.
In the above code, we also restore rax. If (at some point) you want to return a value from your syscall_wrapper function, then you should not pop the rax register, but simply adjust rsp instead.
I hope this helps a bit.