Is anyone using the x86_64 crate for 64-bit osdev in rust?
It provides a nice layer on top of the IDT, which I'm using but, for-the-life-of-me, I can't see how to implement multitasking using the API.
I have a timer interrupt working by setting up the x2apic and then setting the interrupt handler:
```
idt[InterruptIndex::Timer as u8].set_handler_fn(timer_interrupt_handler);
```
But that assumes that timer_interrupt_handler looks like:
```
pub extern "x86-interrupt" fn timer_interrupt_handler(
stack_frame: InterruptStackFrame)
```
Which provides me the values I want for the current task (eg. cs:rip, rflags, ss:rsp) but it doesn't allow me to modify them (and the abstraction has modified the stack in a non-determinant way, so I don't think I can easily just modify in place).
It *seems* like the API has made provisions for modifying these values:
https://docs.rs/x86_64/latest/x86_64/st ... hod.as_mut
But that can't be called because the API enforces that the frame is immutable.
I feel like I'm missing something.
I could just write everything natively, but I'd prefer to use the abstractions in the crate if I can.
Thanks,
Jeff
Modifying the stack (multitasking) using x86_64 crate
- carbonBased
- Member

- Posts: 389
- Joined: Sat Nov 20, 2004 12:00 am
- Location: Wellesley, Ontario, Canada
- Contact:
-
Octocontrabass
- Member

- Posts: 6275
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Modifying the stack (multitasking) using x86_64 crate
Why are you trying to build task switching on top of interrupt handling? They're two entirely separate things.
...But it does seem like you'll have trouble implementing things like signals and debuggers if you can't manipulate the interrupted program's state at all.
...But it does seem like you'll have trouble implementing things like signals and debuggers if you can't manipulate the interrupted program's state at all.
- carbonBased
- Member

- Posts: 389
- Joined: Sat Nov 20, 2004 12:00 am
- Location: Wellesley, Ontario, Canada
- Contact:
Re: Modifying the stack (multitasking) using x86_64 crate
My intention was to use the PIT interrupt as the trigger to swap tasks. This is what i did in my previous OS (admittedly much older and 32-bit).
Is that not a reasonable thing to do?
Is that not a reasonable thing to do?
-
Octocontrabass
- Member

- Posts: 6275
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Modifying the stack (multitasking) using x86_64 crate
Using a timer interrupt as one way to swap tasks is reasonable. Using a timer interrupt as the only way to swap tasks is not reasonable. Tasks may block or yield at any time, with or without an interrupt.
The wiki has a pretty good example of how task switching works in a typical OS.
The wiki has a pretty good example of how task switching works in a typical OS.
Re: Modifying the stack (multitasking) using x86_64 crate
Yes, I've got a 64-bit Rust kernel called Venix. I've got a userspace running, and boot zsh successfully, so have experience with the exact issue you're trying to solve.. https://github.com/os-venix/VenixIs anyone using the x86_64 crate for 64-bit osdev in rust?
To answer your direct question, you can take a mutable reference to it, by calling the function you referenced in an unsafe block. You can't set it &mut in the signature, true, but you can call `as_mut()` on `stack_frame`.
However, I would, personally, argue you don't want to. Here's why. Traditional programming always stacks from the outside in - X calls Y, Y returns back to X. Nest as deeply as you like (unless, of course, `exit()` is called, or you're doing something odd).
That doesn't necessarily have to be true in kernel land. It's entirely possible to enter the kernel at point foo (a syscall handler, or an interrupt handler), do a bunch of stuff, then return to the user at point bar, so long as all the necessary registers to restore etc are saved.
What this means is it's possible to have a single kernel exit point, which sets up a the user everything, and then returns to it. In my case, I have a function called `context_switch`, which does precisely that - https://github.com/os-venix/kernel/blob ... od.rs#L247.
To achieve this, the kernel needs a fresh stack frame every time it gets called, that then gets discarded on exit.
The benefits of doing this is that, no matter what's going on and why the kernel is now running, your scheduler gets to be in charge of what process gets returned to. It knows if the thing that just happened is going to block the current thread, it knows how long the current thread has run for, it knows the relative priorities of everything. Eg, if a HDD interrupt comes in returning some data that a super high priority thread needs, that thread can just be resumed *now*. If a user presses a key, and the scheduler decides that happened close enough to a time boundary that switching tasks makes sense, it can do that (and, if you have it set up, not schedule the next tick, if that's all you need ticks for). So on and so forth. This gives you a much more powerful policy engine than your scheduler otherwise would be.
It also means that you don't need to duplicate "return to the user" all over the place too, for interrupts, syscalls, etc. I tried that and it was a nightmare, which is why I'm now doing it this way.
Hopefully something in there is useful advice. Pardon the ramblyness, I'm a bit tired today.
-
Octocontrabass
- Member

- Posts: 6275
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Modifying the stack (multitasking) using x86_64 crate
Doesn't this mean that it's impossible to have any tasks that stay in ring 0? How do you deal with a task that blocks in the kernel? (Or is that not possible in your kernel?)