Instant double fault on syscall
instruction - need help
#1380
-
I followed https://nfil.dev/kernel/rust/coding/rust-kernel-to-userspace-and-back/#final-code-to-return-to-usermode and /~https://github.com/nikofil/rust-os. Entering ring 3 works. But as soon as ring 3 does I set the following idt entries (so none of these interrupts get called):
The full code is here: /~https://github.com/ChocolateLoverRaj/code-runner/tree/3c2e6cf730ee7d4616e007a51558b946936e4971. I can remove unused parts needed for easier debugging. Here are the main parts of the code: pub unsafe fn jmp_to_usermode(gdt: &Gdt, code: VirtAddr, stack_end: VirtAddr) {
let cs_idx = {
let mut code_selector = gdt.user_code_selector.clone();
code_selector.set_rpl(PrivilegeLevel::Ring3);
code_selector.0
};
let ds_idx = {
let mut data_selector = gdt.user_data_selector.clone();
data_selector.set_rpl(PrivilegeLevel::Ring3);
DS::set_reg(data_selector);
data_selector.0
};
flush_all();
asm!("\
push rax // stack segment
push rsi // rsp
push 0x200 // rflags (only interrupt bit set)
push rdx // code segment
push rdi // ret to virtual addr
iretq",
in("rdi") code.as_u64(), in("rsi") stack_end.as_u64(), in("dx") cs_idx, in("ax") ds_idx);
}
unsafe fn userspace_prog_1() {
// asm!("nop");
// asm!("nop");
asm!("syscall", options(nostack));
// asm!("nop");
// asm!("nop");
}
pub struct Gdt {
gdt: GlobalDescriptorTable,
kernel_code_selector: SegmentSelector,
kernel_data_selector: SegmentSelector,
pub user_code_selector: SegmentSelector,
pub user_data_selector: SegmentSelector,
tss_selector: SegmentSelector,
}
impl Gdt {
pub fn new(tss: &'static TaskStateSegment) -> Self {
let mut gdt = GlobalDescriptorTable::new();
let kernel_code_selector = gdt.append(Descriptor::kernel_code_segment());
let kernel_data_selector = gdt.append(Descriptor::kernel_data_segment());
let tss_selector = gdt.append(Descriptor::tss_segment(&tss));
let user_code_selector = gdt.append(Descriptor::user_code_segment());
let user_data_selector = gdt.append(Descriptor::user_data_segment());
info!(
"kernel code: {:?}\nkernel data: {:?}\nuser code: {:?}\nuser data: {:?}\nTSS: {:?}",
kernel_code_selector,
kernel_data_selector,
user_code_selector,
user_data_selector,
tss_selector
);
Self {
gdt,
kernel_code_selector,
kernel_data_selector,
user_code_selector,
user_data_selector,
tss_selector,
}
}
pub fn init(&self) {
unsafe { self.gdt.load_unsafe() };
unsafe {
// /~https://github.com/rust-osdev/bootloader/blob/5d318bfc8afa4fb116a2c7923d5411febbe7266c/docs/migration/v0.9.md#kernel
SS::set_reg(self.kernel_data_selector);
ES::set_reg(SegmentSelector::NULL);
FS::set_reg(SegmentSelector::NULL);
GS::set_reg(SegmentSelector::NULL);
CS::set_reg(self.kernel_code_selector);
DS::set_reg(self.kernel_data_selector);
load_tss(self.tss_selector);
}
}
} Serial output:
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 16 replies
-
Start QEMU with I would assume that it is You can check with Ideally, you would load an executable (ELF or whatever) in a separate address space (different |
Beta Was this translation helpful? Give feedback.
-
So I was missing 2 things:
let mut ia32_efer = Msr::new(0xC0000080);
let mut value = ia32_efer.read();
value |= 0b1;
ia32_efer.write(value); And This is how I realized that the invalid opcode was caused by not setting the efer thing: https://shell-storm.org/x86doc/SYSCALL.html |
Beta Was this translation helpful? Give feedback.
So I was missing 2 things:
sycall
s by setting bit 0 to 1 inIA32_EFER
:And
syscall
works now. No need to have kernel in higher half addresses.This is how I realized that the invalid opcode was caused by not setting the efer thing: https://shell-storm.org/x86doc/SYSCALL.html