Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Problem] Add software interrupt. #392

Closed
AmberCrafter opened this issue Sep 23, 2022 · 5 comments
Closed

[Problem] Add software interrupt. #392

AmberCrafter opened this issue Sep 23, 2022 · 5 comments

Comments

@AmberCrafter
Copy link

I would like to implment the software interrupt on idt[0x80] ( likes linux's syscall ), but I've run into a calling convention problem:
eax will be overwrited when extern "x86-interrupt" fn foo {} is executed.
Is this the feature of the x86-interrupt? Or am I missing anything?
And where can I found the doucuments about x86-interrupt?

01
02
03

@AmberCrafter
Copy link
Author

RFC mention x86-interrupt was clobbered rax.
Is that mean we need to use other register to pass the syscall's number? More over, should we need to consider other registers are callee-save registers or define them by myself?

@Freax13
Copy link
Member

Freax13 commented Sep 23, 2022

AFAICT there are no guarantees about the contents of registers once the function has been entered. If you want to inspect/modify registers you could use the #[naked] attribute on a function and implement the calling convention yourself.

@AmberCrafter
Copy link
Author

@Freax13 Thanks for your suggestion.
In these days, I trace the asm code of different kind x86-inerrupt function, and found the interesting behavior:

If we use the InterruptStackFrame, the asm code like below, which will overwrite the rax
x86-interrupt_calling_convention3

however, if we unuse the InterruptStackFrame, it looks like this, which will not overwrite the rax.
x86-interrupt_calling_convention3(diff)

On the other hand, @xfoxfu (repo.) provides a good solution to do the type wrapping (extern "C" -> extern "x86-interrupt") which has already perplexed me for a long time.

@Freax13
Copy link
Member

Freax13 commented Sep 26, 2022

however, if we unuse the InterruptStackFrame, it looks like this, which will not overwrite the rax. x86-interrupt_calling_convention3(diff)

There's no guarantee that this will always be the case. This might work for now, but can (and most certainly will) break at any time in the future.

On the other hand, @xfoxfu (repo.) provides a good solution to do the type wrapping (extern "C" -> extern "x86-interrupt") which has already perplexed me for a long time.

The thing that makes this work is their wrap! macro which generates a naked function that pushes the registers onto the stack and prepares the registers for calling a function with the C abi before calling the wrapped function.

I think they could use the x86-interrupt calling convention for their generated function and then they wouldn't have to transmute the function when setting the entry.

@AmberCrafter
Copy link
Author

AmberCrafter commented Sep 26, 2022

however, if we unuse the InterruptStackFrame, it looks like this, which will not overwrite the rax. x86-interrupt_calling_convention3(diff)

There's no guarantee that this will always be the case. This might work for now, but can (and most certainly will) break at any time in the future.

@Freax13 Your right! I can't ensure these behaviors.

On the other hand, @xfoxfu (repo.) provides a good solution to do the type wrapping (extern "C" -> extern "x86-interrupt") which has already perplexed me for a long time.

The thing that makes this work is their wrap! macro which generates a naked function that pushes the registers onto the stack and prepares the registers for calling a function with the C abi before calling the wrapped function.

I think they could use the x86-interrupt calling convention for their generated function and then they wouldn't have to transmute the function when setting the entry.

I think you mean like this

// idt setting
idt[0x80].set_handler_fn(syscall_handler_naked_wrap);

// handler function
#[naked]
pub extern "x86-interrupt" fn syscall_handler_naked_wrap(stack_frame: InterruptStackFrame) {
    unsafe {
        core::arch::asm!(
            "
                push rbp
                push rax
                push rbx
                push rcx
                push rdx
                push rsi
                push rdi
                push r8
                push r9
                push r10
                push r11
                push r12
                push r13
                push r14
                push r15
                mov rsi, rsp    // second arg: register list
                mov rdi, rsp
                add rdi, 15*8   // first arg: interrupt frame
                call {}
                pop r15
                pop r14
                pop r13
                pop r12 
                pop r11
                pop r10
                pop r9
                pop r8
                pop rdi
                pop rsi
                pop rdx
                pop rcx
                pop rbx
                pop rax
                pop rbp
                iretq
            ",
            sym syscall_handler_naked,
            options(noreturn)
        );
    }
}

pub extern "C" fn syscall_handler_naked(sf: &mut InterruptStackFrame, regs: &mut Registers) {
    // something i want to do...
    serial_println!(
        "
            rax: {:?}\n
            rdi: {:?}\n
            rsi: {:?}\n
            rdx: {:?}
        ",
        regs.rax,
        regs.rdi,
        regs.rsi,
        regs.rdx
    );

    serial_println!("{:?}", sf);
    serial_println!("syscall finished!");
}

and it work for me. Hurray!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants