From ff6cbc0544bb12ded95129ac4fe54cf8d9998778 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 5 May 2021 21:55:43 -0700 Subject: [PATCH 01/10] Fix Clippy warnings Signed-off-by: Joe Richey --- src/registers/rflags.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/registers/rflags.rs b/src/registers/rflags.rs index ae967e260..018c203dc 100644 --- a/src/registers/rflags.rs +++ b/src/registers/rflags.rs @@ -119,16 +119,12 @@ mod x86_64 { /// flags also used by Rust/LLVM can result in undefined behavior too. #[inline] pub unsafe fn write_raw(val: u64) { + // FIXME - There's probably a better way than saying we preserve the flags even though we actually don't #[cfg(feature = "inline_asm")] - { - // FIXME - There's probably a better way than saying we preserve the flags even though we actually don't - asm!("push {}; popf", in(reg) val, options(preserves_flags)) - }; + asm!("push {}; popf", in(reg) val, options(preserves_flags)); #[cfg(not(feature = "inline_asm"))] - { - crate::asm::x86_64_asm_write_rflags(val) - } + crate::asm::x86_64_asm_write_rflags(val); } #[cfg(test)] From cc7f2865a27b9cf82a44ee3e7dbe84555e948c2d Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 5 May 2021 23:17:06 -0700 Subject: [PATCH 02/10] Consistently format ASM blocks This mostly makes the code shorter and easier to read Signed-off-by: Joe Richey --- src/instructions/interrupts.rs | 26 ++++------ src/instructions/mod.rs | 12 ++--- src/instructions/port.rs | 75 +++++++++++---------------- src/instructions/segmentation.rs | 88 ++++++++++---------------------- src/instructions/tables.rs | 10 ++-- src/instructions/tlb.rs | 21 +++----- src/registers/control.rs | 4 -- src/registers/rflags.rs | 8 +-- 8 files changed, 85 insertions(+), 159 deletions(-) diff --git a/src/instructions/interrupts.rs b/src/instructions/interrupts.rs index bdc9046b6..c3827bd39 100644 --- a/src/instructions/interrupts.rs +++ b/src/instructions/interrupts.rs @@ -13,12 +13,11 @@ pub fn are_enabled() -> bool { /// This is a wrapper around the `sti` instruction. #[inline] pub fn enable() { - #[cfg(feature = "inline_asm")] unsafe { + #[cfg(feature = "inline_asm")] asm!("sti", options(nomem, nostack)); - } - #[cfg(not(feature = "inline_asm"))] - unsafe { + + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_interrupt_enable(); } } @@ -28,13 +27,11 @@ pub fn enable() { /// This is a wrapper around the `cli` instruction. #[inline] pub fn disable() { - #[cfg(feature = "inline_asm")] unsafe { + #[cfg(feature = "inline_asm")] asm!("cli", options(nomem, nostack)); - } - #[cfg(not(feature = "inline_asm"))] - unsafe { + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_interrupt_disable(); } } @@ -129,12 +126,11 @@ where /// information. #[inline] pub fn enable_and_hlt() { - #[cfg(feature = "inline_asm")] unsafe { + #[cfg(feature = "inline_asm")] asm!("sti; hlt", options(nomem, nostack)); - } - #[cfg(not(feature = "inline_asm"))] - unsafe { + + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_interrupt_enable_and_hlt(); } } @@ -142,13 +138,11 @@ pub fn enable_and_hlt() { /// Cause a breakpoint exception by invoking the `int3` instruction. #[inline] pub fn int3() { - #[cfg(feature = "inline_asm")] unsafe { + #[cfg(feature = "inline_asm")] asm!("int3", options(nomem, nostack)); - } - #[cfg(not(feature = "inline_asm"))] - unsafe { + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_int3(); } } diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index e1bd9c1c7..c4e0f440d 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -12,13 +12,11 @@ pub mod tlb; /// Halts the CPU until the next interrupt arrives. #[inline] pub fn hlt() { - #[cfg(feature = "inline_asm")] unsafe { + #[cfg(feature = "inline_asm")] asm!("hlt", options(nomem, nostack)); - } - #[cfg(not(feature = "inline_asm"))] - unsafe { + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_hlt(); } } @@ -31,13 +29,11 @@ pub fn hlt() { /// endless loop away. #[inline] pub fn nop() { - #[cfg(feature = "inline_asm")] unsafe { + #[cfg(feature = "inline_asm")] asm!("nop", options(nomem, nostack, preserves_flags)); - } - #[cfg(not(feature = "inline_asm"))] - unsafe { + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_nop(); } } diff --git a/src/instructions/port.rs b/src/instructions/port.rs index 01e1aafdf..ad630dc57 100644 --- a/src/instructions/port.rs +++ b/src/instructions/port.rs @@ -6,92 +6,77 @@ use core::marker::PhantomData; pub use crate::structures::port::{PortRead, PortWrite}; impl PortRead for u8 { - #[cfg(feature = "inline_asm")] - #[inline] - unsafe fn read_from_port(port: u16) -> u8 { - let value: u8; - asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack)); - value - } - - #[cfg(not(feature = "inline_asm"))] #[inline] unsafe fn read_from_port(port: u16) -> u8 { + #[cfg(feature = "inline_asm")] + { + let value: u8; + asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack)); + value + } + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_read_from_port_u8(port) } } impl PortRead for u16 { - #[cfg(feature = "inline_asm")] - #[inline] - unsafe fn read_from_port(port: u16) -> u16 { - let value: u16; - asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack)); - value - } - - #[cfg(not(feature = "inline_asm"))] #[inline] unsafe fn read_from_port(port: u16) -> u16 { + #[cfg(feature = "inline_asm")] + { + let value: u16; + asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack)); + value + } + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_read_from_port_u16(port) } } impl PortRead for u32 { - #[cfg(feature = "inline_asm")] - #[inline] - unsafe fn read_from_port(port: u16) -> u32 { - let value: u32; - asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack)); - value - } - - #[cfg(not(feature = "inline_asm"))] #[inline] unsafe fn read_from_port(port: u16) -> u32 { + #[cfg(feature = "inline_asm")] + { + let value: u32; + asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack)); + value + } + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_read_from_port_u32(port) } } impl PortWrite for u8 { - #[cfg(feature = "inline_asm")] #[inline] unsafe fn write_to_port(port: u16, value: u8) { + #[cfg(feature = "inline_asm")] asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack)); - } - #[cfg(not(feature = "inline_asm"))] - #[inline] - unsafe fn write_to_port(port: u16, value: u8) { - crate::asm::x86_64_asm_write_to_port_u8(port, value) + #[cfg(not(feature = "inline_asm"))] + crate::asm::x86_64_asm_write_to_port_u8(port, value); } } impl PortWrite for u16 { - #[cfg(feature = "inline_asm")] #[inline] unsafe fn write_to_port(port: u16, value: u16) { + #[cfg(feature = "inline_asm")] asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack)); - } - #[cfg(not(feature = "inline_asm"))] - #[inline] - unsafe fn write_to_port(port: u16, value: u16) { - crate::asm::x86_64_asm_write_to_port_u16(port, value) + #[cfg(not(feature = "inline_asm"))] + crate::asm::x86_64_asm_write_to_port_u16(port, value); } } impl PortWrite for u32 { - #[cfg(feature = "inline_asm")] #[inline] unsafe fn write_to_port(port: u16, value: u32) { + #[cfg(feature = "inline_asm")] asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack)); - } - #[cfg(not(feature = "inline_asm"))] - #[inline] - unsafe fn write_to_port(port: u16, value: u32) { - crate::asm::x86_64_asm_write_to_port_u32(port, value) + #[cfg(not(feature = "inline_asm"))] + crate::asm::x86_64_asm_write_to_port_u32(port, value); } } diff --git a/src/instructions/segmentation.rs b/src/instructions/segmentation.rs index a7b7bbc2a..fd34750ee 100644 --- a/src/instructions/segmentation.rs +++ b/src/instructions/segmentation.rs @@ -16,26 +16,18 @@ use crate::structures::gdt::SegmentSelector; #[inline] pub unsafe fn set_cs(sel: SegmentSelector) { #[cfg(feature = "inline_asm")] - #[inline(always)] - unsafe fn inner(sel: SegmentSelector) { - asm!( - "push {sel}", - "lea {tmp}, [1f + rip]", - "push {tmp}", - "retfq", - "1:", - sel = in(reg) u64::from(sel.0), - tmp = lateout(reg) _, - ); - } + asm!( + "push {sel}", + "lea {tmp}, [1f + rip]", + "push {tmp}", + "retfq", + "1:", + sel = in(reg) u64::from(sel.0), + tmp = lateout(reg) _, + ); #[cfg(not(feature = "inline_asm"))] - #[inline(always)] - unsafe fn inner(sel: SegmentSelector) { - crate::asm::x86_64_asm_set_cs(u64::from(sel.0)) - } - - inner(sel) + crate::asm::x86_64_asm_set_cs(u64::from(sel.0)); } /// Reload stack segment register. @@ -131,18 +123,18 @@ pub unsafe fn swap_gs() { /// Returns the current value of the code segment register. #[inline] pub fn cs() -> SegmentSelector { + let segment: u16; + #[cfg(feature = "inline_asm")] - { - let segment: u16; - unsafe { asm!("mov {0:x}, cs", out(reg) segment, options(nostack, nomem)) }; - SegmentSelector(segment) + unsafe { + asm!("mov {0:x}, cs", out(reg) segment, options(nostack, nomem)); } - #[cfg(not(feature = "inline_asm"))] - { - let segment: u16 = unsafe { crate::asm::x86_64_asm_get_cs() }; - SegmentSelector(segment) + unsafe { + segment = crate::asm::x86_64_asm_get_cs(); } + + SegmentSelector(segment) } /// Writes the FS segment base address @@ -157,18 +149,10 @@ pub fn cs() -> SegmentSelector { #[inline] pub unsafe fn wrfsbase(val: u64) { #[cfg(feature = "inline_asm")] - #[inline(always)] - unsafe fn inner(val: u64) { - asm!("wrfsbase {}", in(reg) val, options(nomem, nostack)); - } + asm!("wrfsbase {}", in(reg) val, options(nomem, nostack)); #[cfg(not(feature = "inline_asm"))] - #[inline(always)] - unsafe fn inner(val: u64) { - crate::asm::x86_64_asm_wrfsbase(val) - } - - inner(val) + crate::asm::x86_64_asm_wrfsbase(val); } /// Reads the FS segment base address @@ -179,20 +163,14 @@ pub unsafe fn wrfsbase(val: u64) { #[inline] pub unsafe fn rdfsbase() -> u64 { #[cfg(feature = "inline_asm")] - #[inline(always)] - unsafe fn inner() -> u64 { + { let val: u64; asm!("rdfsbase {}", out(reg) val, options(nomem, nostack)); val } #[cfg(not(feature = "inline_asm"))] - #[inline(always)] - unsafe fn inner() -> u64 { - crate::asm::x86_64_asm_rdfsbase() - } - - inner() + crate::asm::x86_64_asm_rdfsbase() } /// Writes the GS segment base address @@ -206,18 +184,10 @@ pub unsafe fn rdfsbase() -> u64 { #[inline] pub unsafe fn wrgsbase(val: u64) { #[cfg(feature = "inline_asm")] - #[inline(always)] - unsafe fn inner(val: u64) { - asm!("wrgsbase {}", in(reg) val, options(nomem, nostack)) - } + asm!("wrgsbase {}", in(reg) val, options(nomem, nostack)); #[cfg(not(feature = "inline_asm"))] - #[inline(always)] - unsafe fn inner(val: u64) { - crate::asm::x86_64_asm_wrgsbase(val) - } - - inner(val) + crate::asm::x86_64_asm_wrgsbase(val); } /// Reads the GS segment base address @@ -228,18 +198,12 @@ pub unsafe fn wrgsbase(val: u64) { #[inline] pub unsafe fn rdgsbase() -> u64 { #[cfg(feature = "inline_asm")] - #[inline(always)] - unsafe fn inner() -> u64 { + { let val: u64; asm!("rdgsbase {}", out(reg) val, options(nomem, nostack)); val } #[cfg(not(feature = "inline_asm"))] - #[inline(always)] - unsafe fn inner() -> u64 { - crate::asm::x86_64_asm_rdgsbase() - } - - inner() + crate::asm::x86_64_asm_rdgsbase() } diff --git a/src/instructions/tables.rs b/src/instructions/tables.rs index 22a2f4610..5c409a2e6 100644 --- a/src/instructions/tables.rs +++ b/src/instructions/tables.rs @@ -52,15 +52,13 @@ pub fn sidt() -> DescriptorTablePointer { limit: 0, base: VirtAddr::new(0), }; - #[cfg(feature = "inline_asm")] unsafe { + #[cfg(feature = "inline_asm")] asm!("sidt [{}]", in(reg) &mut idt, options(nostack)); - } - #[cfg(not(feature = "inline_asm"))] - unsafe { + + #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_sidt(&mut idt as *mut _); } - idt } @@ -77,5 +75,5 @@ pub unsafe fn load_tss(sel: SegmentSelector) { asm!("ltr {0:x}", in(reg) sel.0, options(nostack, nomem)); #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_ltr(sel.0) + crate::asm::x86_64_asm_ltr(sel.0); } diff --git a/src/instructions/tlb.rs b/src/instructions/tlb.rs index 0a97e7540..33497f41a 100644 --- a/src/instructions/tlb.rs +++ b/src/instructions/tlb.rs @@ -5,15 +5,13 @@ use crate::VirtAddr; /// Invalidate the given address in the TLB using the `invlpg` instruction. #[inline] pub fn flush(addr: VirtAddr) { - #[cfg(feature = "inline_asm")] unsafe { - asm!("invlpg [{}]", in(reg) addr.as_u64(), options(nostack)) - }; + #[cfg(feature = "inline_asm")] + asm!("invlpg [{}]", in(reg) addr.as_u64(), options(nostack)); - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_invlpg(addr.as_u64()) - }; + #[cfg(not(feature = "inline_asm"))] + crate::asm::x86_64_asm_invlpg(addr.as_u64()); + } } /// Invalidate the TLB completely by reloading the CR3 register. @@ -98,13 +96,8 @@ pub unsafe fn flush_pcid(command: InvPicdCommand) { } #[cfg(feature = "inline_asm")] - { - let desc_value = &desc as *const InvpcidDescriptor as u64; - asm!("invpcid {1}, [{0}]", in(reg) desc_value, in(reg) kind); - }; + asm!("invpcid {1}, [{0}]", in(reg) &desc, in(reg) kind); #[cfg(not(feature = "inline_asm"))] - { - crate::asm::x86_64_asm_invpcid(kind, &desc as *const InvpcidDescriptor as u64) - }; + crate::asm::x86_64_asm_invpcid(kind, &desc as *const _ as u64); } diff --git a/src/registers/control.rs b/src/registers/control.rs index 97abb027e..a862a62a6 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -146,7 +146,6 @@ mod x86_64 { unsafe { asm!("mov {}, cr0", out(reg) value, options(nomem)); } - #[cfg(not(feature = "inline_asm"))] unsafe { value = crate::asm::x86_64_asm_read_cr0(); @@ -218,7 +217,6 @@ mod x86_64 { unsafe { asm!("mov {}, cr2", out(reg) value, options(nomem)); } - #[cfg(not(feature = "inline_asm"))] unsafe { value = crate::asm::x86_64_asm_read_cr2(); @@ -246,7 +244,6 @@ mod x86_64 { unsafe { asm!("mov {}, cr3", out(reg) value, options(nomem)); } - #[cfg(not(feature = "inline_asm"))] unsafe { value = crate::asm::x86_64_asm_read_cr3(); @@ -321,7 +318,6 @@ mod x86_64 { unsafe { asm!("mov {}, cr4", out(reg) value, options(nostack)); } - #[cfg(not(feature = "inline_asm"))] unsafe { value = crate::asm::x86_64_asm_read_cr4(); diff --git a/src/registers/rflags.rs b/src/registers/rflags.rs index 018c203dc..bb80ee7de 100644 --- a/src/registers/rflags.rs +++ b/src/registers/rflags.rs @@ -78,15 +78,15 @@ mod x86_64 { #[inline] pub fn read_raw() -> u64 { let r: u64; + #[cfg(feature = "inline_asm")] unsafe { - asm!("pushf; pop {}", out(reg) r) - }; - + asm!("pushf; pop {}", out(reg) r); + } #[cfg(not(feature = "inline_asm"))] unsafe { r = crate::asm::x86_64_asm_read_rflags(); - }; + } r } From 92920d52f33611ec3952c13931a894c5a0d6c9f6 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 May 2021 00:46:02 -0700 Subject: [PATCH 03/10] Use correct asm! options This adds some previously omitted options to our inline asm! Note that instuctions that just push/pop the stack (but otherwise don't touch memory) can be marked `nomem`. We also don't mark instuctions as `nomem` if they could modify memory translation: This includes - writing segment registers - invlpg/invpcid - writing cr2/cr3/cr4 - writing any MSR (we still mark these as `nostack` though. The following are still marked `nomem`: - Reading any MSR (as this cannot have a side-effect) - Writing XCR0 Signed-off-by: Joe Richey --- src/instructions/mod.rs | 6 +++--- src/instructions/port.rs | 12 ++++++------ src/instructions/segmentation.rs | 23 +++++++++++----------- src/instructions/tables.rs | 8 ++++---- src/instructions/tlb.rs | 4 ++-- src/registers/control.rs | 14 +++++++------- src/registers/model_specific.rs | 14 ++++++++++++-- src/registers/rflags.rs | 7 ++++--- src/registers/xcontrol.rs | 33 +++++++++++++------------------- 9 files changed, 63 insertions(+), 58 deletions(-) diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index c4e0f440d..3ba9dffd3 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -14,7 +14,7 @@ pub mod tlb; pub fn hlt() { unsafe { #[cfg(feature = "inline_asm")] - asm!("hlt", options(nomem, nostack)); + asm!("hlt", options(nomem, nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_hlt(); @@ -44,7 +44,7 @@ pub fn nop() { #[inline] pub fn bochs_breakpoint() { unsafe { - asm!("xchg bx, bx", options(nomem, nostack)); + asm!("xchg bx, bx", options(nomem, nostack, preserves_flags)); } } @@ -56,7 +56,7 @@ pub fn read_rip() -> crate::VirtAddr { let rip: u64; unsafe { asm!( - "lea {}, [rip]", out(reg) rip, options(nostack, nomem) + "lea {}, [rip]", out(reg) rip, options(nostack, nomem, preserves_flags) ); } crate::VirtAddr::new(rip) diff --git a/src/instructions/port.rs b/src/instructions/port.rs index ad630dc57..5d792f098 100644 --- a/src/instructions/port.rs +++ b/src/instructions/port.rs @@ -11,7 +11,7 @@ impl PortRead for u8 { #[cfg(feature = "inline_asm")] { let value: u8; - asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack)); + asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack, preserves_flags)); value } #[cfg(not(feature = "inline_asm"))] @@ -25,7 +25,7 @@ impl PortRead for u16 { #[cfg(feature = "inline_asm")] { let value: u16; - asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack)); + asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack, preserves_flags)); value } #[cfg(not(feature = "inline_asm"))] @@ -39,7 +39,7 @@ impl PortRead for u32 { #[cfg(feature = "inline_asm")] { let value: u32; - asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack)); + asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack, preserves_flags)); value } #[cfg(not(feature = "inline_asm"))] @@ -51,7 +51,7 @@ impl PortWrite for u8 { #[inline] unsafe fn write_to_port(port: u16, value: u8) { #[cfg(feature = "inline_asm")] - asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack)); + asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_write_to_port_u8(port, value); @@ -62,7 +62,7 @@ impl PortWrite for u16 { #[inline] unsafe fn write_to_port(port: u16, value: u16) { #[cfg(feature = "inline_asm")] - asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack)); + asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_write_to_port_u16(port, value); @@ -73,7 +73,7 @@ impl PortWrite for u32 { #[inline] unsafe fn write_to_port(port: u16, value: u32) { #[cfg(feature = "inline_asm")] - asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack)); + asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_write_to_port_u32(port, value); diff --git a/src/instructions/segmentation.rs b/src/instructions/segmentation.rs index fd34750ee..7de0fbb59 100644 --- a/src/instructions/segmentation.rs +++ b/src/instructions/segmentation.rs @@ -24,6 +24,7 @@ pub unsafe fn set_cs(sel: SegmentSelector) { "1:", sel = in(reg) u64::from(sel.0), tmp = lateout(reg) _, + options(preserves_flags), ); #[cfg(not(feature = "inline_asm"))] @@ -39,7 +40,7 @@ pub unsafe fn set_cs(sel: SegmentSelector) { #[inline] pub unsafe fn load_ss(sel: SegmentSelector) { #[cfg(feature = "inline_asm")] - asm!("mov ss, {0:x}", in(reg) sel.0, options(nostack)); + asm!("mov ss, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_load_ss(sel.0); @@ -54,7 +55,7 @@ pub unsafe fn load_ss(sel: SegmentSelector) { #[inline] pub unsafe fn load_ds(sel: SegmentSelector) { #[cfg(feature = "inline_asm")] - asm!("mov ds, {0:x}", in(reg) sel.0, options(nostack)); + asm!("mov ds, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_load_ds(sel.0); @@ -69,7 +70,7 @@ pub unsafe fn load_ds(sel: SegmentSelector) { #[inline] pub unsafe fn load_es(sel: SegmentSelector) { #[cfg(feature = "inline_asm")] - asm!("mov es, {0:x}", in(reg) sel.0, options(nostack)); + asm!("mov es, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_load_es(sel.0); @@ -84,7 +85,7 @@ pub unsafe fn load_es(sel: SegmentSelector) { #[inline] pub unsafe fn load_fs(sel: SegmentSelector) { #[cfg(feature = "inline_asm")] - asm!("mov fs, {0:x}", in(reg) sel.0, options(nostack)); + asm!("mov fs, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_load_fs(sel.0); @@ -99,7 +100,7 @@ pub unsafe fn load_fs(sel: SegmentSelector) { #[inline] pub unsafe fn load_gs(sel: SegmentSelector) { #[cfg(feature = "inline_asm")] - asm!("mov gs, {0:x}", in(reg) sel.0, options(nostack)); + asm!("mov gs, {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_load_gs(sel.0); @@ -114,7 +115,7 @@ pub unsafe fn load_gs(sel: SegmentSelector) { #[inline] pub unsafe fn swap_gs() { #[cfg(feature = "inline_asm")] - asm!("swapgs", options(nostack)); + asm!("swapgs", options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_swapgs(); @@ -127,7 +128,7 @@ pub fn cs() -> SegmentSelector { #[cfg(feature = "inline_asm")] unsafe { - asm!("mov {0:x}, cs", out(reg) segment, options(nostack, nomem)); + asm!("mov {0:x}, cs", out(reg) segment, options(nomem, nostack, preserves_flags)); } #[cfg(not(feature = "inline_asm"))] unsafe { @@ -149,7 +150,7 @@ pub fn cs() -> SegmentSelector { #[inline] pub unsafe fn wrfsbase(val: u64) { #[cfg(feature = "inline_asm")] - asm!("wrfsbase {}", in(reg) val, options(nomem, nostack)); + asm!("wrfsbase {}", in(reg) val, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_wrfsbase(val); @@ -165,7 +166,7 @@ pub unsafe fn rdfsbase() -> u64 { #[cfg(feature = "inline_asm")] { let val: u64; - asm!("rdfsbase {}", out(reg) val, options(nomem, nostack)); + asm!("rdfsbase {}", out(reg) val, options(nomem, nostack, preserves_flags)); val } @@ -184,7 +185,7 @@ pub unsafe fn rdfsbase() -> u64 { #[inline] pub unsafe fn wrgsbase(val: u64) { #[cfg(feature = "inline_asm")] - asm!("wrgsbase {}", in(reg) val, options(nomem, nostack)); + asm!("wrgsbase {}", in(reg) val, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_wrgsbase(val); @@ -200,7 +201,7 @@ pub unsafe fn rdgsbase() -> u64 { #[cfg(feature = "inline_asm")] { let val: u64; - asm!("rdgsbase {}", out(reg) val, options(nomem, nostack)); + asm!("rdgsbase {}", out(reg) val, options(nomem, nostack, preserves_flags)); val } diff --git a/src/instructions/tables.rs b/src/instructions/tables.rs index 5c409a2e6..27d79f920 100644 --- a/src/instructions/tables.rs +++ b/src/instructions/tables.rs @@ -19,7 +19,7 @@ pub use crate::structures::DescriptorTablePointer; #[inline] pub unsafe fn lgdt(gdt: &DescriptorTablePointer) { #[cfg(feature = "inline_asm")] - asm!("lgdt [{}]", in(reg) gdt, options(nostack)); + asm!("lgdt [{}]", in(reg) gdt, options(readonly, nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_lgdt(gdt as *const _); @@ -39,7 +39,7 @@ pub unsafe fn lgdt(gdt: &DescriptorTablePointer) { #[inline] pub unsafe fn lidt(idt: &DescriptorTablePointer) { #[cfg(feature = "inline_asm")] - asm!("lidt [{}]", in(reg) idt, options(nostack)); + asm!("lidt [{}]", in(reg) idt, options(readonly, nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_lidt(idt as *const _); @@ -54,7 +54,7 @@ pub fn sidt() -> DescriptorTablePointer { }; unsafe { #[cfg(feature = "inline_asm")] - asm!("sidt [{}]", in(reg) &mut idt, options(nostack)); + asm!("sidt [{}]", in(reg) &mut idt, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_sidt(&mut idt as *mut _); @@ -72,7 +72,7 @@ pub fn sidt() -> DescriptorTablePointer { #[inline] pub unsafe fn load_tss(sel: SegmentSelector) { #[cfg(feature = "inline_asm")] - asm!("ltr {0:x}", in(reg) sel.0, options(nostack, nomem)); + asm!("ltr {0:x}", in(reg) sel.0, options(nomem, nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_ltr(sel.0); diff --git a/src/instructions/tlb.rs b/src/instructions/tlb.rs index 33497f41a..ea980863d 100644 --- a/src/instructions/tlb.rs +++ b/src/instructions/tlb.rs @@ -7,7 +7,7 @@ use crate::VirtAddr; pub fn flush(addr: VirtAddr) { unsafe { #[cfg(feature = "inline_asm")] - asm!("invlpg [{}]", in(reg) addr.as_u64(), options(nostack)); + asm!("invlpg [{}]", in(reg) addr.as_u64(), options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_invlpg(addr.as_u64()); @@ -96,7 +96,7 @@ pub unsafe fn flush_pcid(command: InvPicdCommand) { } #[cfg(feature = "inline_asm")] - asm!("invpcid {1}, [{0}]", in(reg) &desc, in(reg) kind); + asm!("invpcid {0}, [{1}]", in(reg) kind, in(reg) &desc, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_invpcid(kind, &desc as *const _ as u64); diff --git a/src/registers/control.rs b/src/registers/control.rs index a862a62a6..c09dd0d7e 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -144,7 +144,7 @@ mod x86_64 { #[cfg(feature = "inline_asm")] unsafe { - asm!("mov {}, cr0", out(reg) value, options(nomem)); + asm!("mov {}, cr0", out(reg) value, options(nomem, nostack, preserves_flags)); } #[cfg(not(feature = "inline_asm"))] unsafe { @@ -182,7 +182,7 @@ mod x86_64 { #[inline] pub unsafe fn write_raw(value: u64) { #[cfg(feature = "inline_asm")] - asm!("mov cr0, {}", in(reg) value, options(nostack)); + asm!("mov cr0, {}", in(reg) value, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_write_cr0(value); @@ -215,7 +215,7 @@ mod x86_64 { #[cfg(feature = "inline_asm")] unsafe { - asm!("mov {}, cr2", out(reg) value, options(nomem)); + asm!("mov {}, cr2", out(reg) value, options(nomem, nostack, preserves_flags)); } #[cfg(not(feature = "inline_asm"))] unsafe { @@ -242,7 +242,7 @@ mod x86_64 { #[cfg(feature = "inline_asm")] unsafe { - asm!("mov {}, cr3", out(reg) value, options(nomem)); + asm!("mov {}, cr3", out(reg) value, options(nomem, nostack, preserves_flags)); } #[cfg(not(feature = "inline_asm"))] unsafe { @@ -295,7 +295,7 @@ mod x86_64 { let value = addr.as_u64() | val as u64; #[cfg(feature = "inline_asm")] - asm!("mov cr3, {}", in(reg) value, options(nostack)); + asm!("mov cr3, {}", in(reg) value, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_write_cr3(value) @@ -316,7 +316,7 @@ mod x86_64 { #[cfg(feature = "inline_asm")] unsafe { - asm!("mov {}, cr4", out(reg) value, options(nostack)); + asm!("mov {}, cr4", out(reg) value, options(nomem, nostack, preserves_flags)); } #[cfg(not(feature = "inline_asm"))] unsafe { @@ -356,7 +356,7 @@ mod x86_64 { #[inline] pub unsafe fn write_raw(value: u64) { #[cfg(feature = "inline_asm")] - asm!("mov cr4, {}", in(reg) value, options(nostack)); + asm!("mov cr4, {}", in(reg) value, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_write_cr4(value); diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index adc402f2b..6dda29bf6 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -121,7 +121,12 @@ mod x86_64 { #[cfg(feature = "inline_asm")] { let (high, low): (u32, u32); - asm!("rdmsr", out("eax") low, out("edx") high, in("ecx") self.0, options(nostack)); + asm!( + "rdmsr", + in("ecx") self.0, + out("eax") low, out("edx") high, + options(nomem, nostack, preserves_flags), + ); ((high as u64) << 32) | (low as u64) } @@ -141,7 +146,12 @@ mod x86_64 { { let low = value as u32; let high = (value >> 32) as u32; - asm!("wrmsr", in("ecx") self.0, in("eax") low, in("edx") high, options(nostack)) + asm!( + "wrmsr", + in("ecx") self.0, + in("eax") low, in("edx") high, + options(nostack, preserves_flags), + ); } #[cfg(not(feature = "inline_asm"))] diff --git a/src/registers/rflags.rs b/src/registers/rflags.rs index bb80ee7de..b863a289a 100644 --- a/src/registers/rflags.rs +++ b/src/registers/rflags.rs @@ -81,7 +81,7 @@ mod x86_64 { #[cfg(feature = "inline_asm")] unsafe { - asm!("pushf; pop {}", out(reg) r); + asm!("pushf; pop {}", out(reg) r, options(nomem, preserves_flags)); } #[cfg(not(feature = "inline_asm"))] unsafe { @@ -119,9 +119,10 @@ mod x86_64 { /// flags also used by Rust/LLVM can result in undefined behavior too. #[inline] pub unsafe fn write_raw(val: u64) { - // FIXME - There's probably a better way than saying we preserve the flags even though we actually don't + // HACK: we mark this function as preserves_flags to prevent Rust from restoring + // saved flags after the "popf" below. See above note on safety. #[cfg(feature = "inline_asm")] - asm!("push {}; popf", in(reg) val, options(preserves_flags)); + asm!("push {}; popf", in(reg) val, options(nomem, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_write_rflags(val); diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 54198bc7e..c74147d72 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -37,20 +37,15 @@ mod x86_64 { /// Read the current raw XCR0 value. #[inline] pub fn read_raw() -> u64 { - let low: u32; - let high: u32; - + let (low, high): (u32, u32); unsafe { - asm!(" - xor rcx, rcx - xgetbv - ", - lateout("rdx") high, - lateout("rax") low, - lateout("rcx") _, + asm!( + "xgetbv", + in("ecx") 0, + out("rax") low, out("rdx") high, + options(nomem, nostack, preserves_flags), ); } - (high as u64) << 32 | (low as u64) } @@ -81,15 +76,13 @@ mod x86_64 { /// enable features that are not supported by the architecture #[inline] pub unsafe fn write_raw(value: u64) { - let high: u32 = (value >> 32) as u32; - let low: u32 = (value) as u32; - asm!(" - xor ecx, ecx - xsetbv - ", - in("edx") high, - in("eax") low, - lateout("ecx") _, + let low = value as u32; + let high = (value >> 32) as u32; + asm!( + "xsetbv", + in("ecx") 0, + in("rax") low, in("rdx") high, + options(nomem, nostack, preserves_flags), ); } } From fe15d6038cc689c6b51c108b9a54a32ef2b7addf Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 May 2021 01:11:20 -0700 Subject: [PATCH 04/10] Simplify _x86_64_asm_read_rflags Signed-off-by: Joe Richey --- src/asm/asm.s | 3 --- src/registers/rflags.rs | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/asm/asm.s b/src/asm/asm.s index 32742d172..b656ba62f 100644 --- a/src/asm/asm.s +++ b/src/asm/asm.s @@ -135,11 +135,8 @@ _x86_64_asm_write_rflags: .global _x86_64_asm_read_rflags .p2align 4 _x86_64_asm_read_rflags: - pushq %rbp - movq %rsp, %rbp pushfq popq %rax - popq %rbp retq .global _x86_64_asm_load_ss diff --git a/src/registers/rflags.rs b/src/registers/rflags.rs index b863a289a..cb133bf3b 100644 --- a/src/registers/rflags.rs +++ b/src/registers/rflags.rs @@ -81,7 +81,7 @@ mod x86_64 { #[cfg(feature = "inline_asm")] unsafe { - asm!("pushf; pop {}", out(reg) r, options(nomem, preserves_flags)); + asm!("pushfq; pop {}", out(reg) r, options(nomem, preserves_flags)); } #[cfg(not(feature = "inline_asm"))] unsafe { @@ -122,7 +122,7 @@ mod x86_64 { // HACK: we mark this function as preserves_flags to prevent Rust from restoring // saved flags after the "popf" below. See above note on safety. #[cfg(feature = "inline_asm")] - asm!("push {}; popf", in(reg) val, options(nomem, preserves_flags)); + asm!("push {}; popfq", in(reg) val, options(nomem, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_write_rflags(val); From 24b9a454bc73c62b61d80511a44bbcc4dbc50c1f Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 May 2021 01:12:54 -0700 Subject: [PATCH 05/10] Add non "inline_asm" impl for bochs_breakpoint Signed-off-by: Joe Richey --- src/asm/asm.s | 10 ++++++++++ src/asm/mod.rs | 6 ++++++ src/instructions/mod.rs | 5 ++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/asm/asm.s b/src/asm/asm.s index b656ba62f..a3566a7ac 100644 --- a/src/asm/asm.s +++ b/src/asm/asm.s @@ -1,6 +1,10 @@ .text .code64 +# REMEMBER: This code uses the AMD64 calling convention: +# Arguments: RDI, RSI, RDX, RCX +# Return: RAX + .global _x86_64_asm_interrupt_enable .p2align 4 _x86_64_asm_interrupt_enable: @@ -249,6 +253,12 @@ _x86_64_asm_nop: nop retq +.global _x86_64_asm_bochs +.p2align 4 +_x86_64_asm_bochs: + xchgw %bx, %bx + retq + .global _x86_64_asm_rdfsbase .p2align 4 _x86_64_asm_rdfsbase: diff --git a/src/asm/mod.rs b/src/asm/mod.rs index 74b7051d4..df097ebba 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -36,6 +36,12 @@ extern "C" { )] pub(crate) fn x86_64_asm_nop(); + #[cfg_attr( + any(target_env = "gnu", target_env = "musl"), + link_name = "_x86_64_asm_bochs" + )] + pub(crate) fn x86_64_asm_bochs(); + #[cfg_attr( any(target_env = "gnu", target_env = "musl"), link_name = "_x86_64_asm_read_from_port_u8" diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 3ba9dffd3..a92a5a776 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -40,11 +40,14 @@ pub fn nop() { /// Emits a '[magic breakpoint](https://wiki.osdev.org/Bochs#Magic_Breakpoint)' instruction for the [Bochs](http://bochs.sourceforge.net/) CPU /// emulator. Make sure to set `magic_break: enabled=1` in your `.bochsrc` file. -#[cfg(feature = "inline_asm")] #[inline] pub fn bochs_breakpoint() { unsafe { + #[cfg(feature = "inline_asm")] asm!("xchg bx, bx", options(nomem, nostack, preserves_flags)); + + #[cfg(not(feature = "inline_asm"))] + crate::asm::x86_64_asm_bochs(); } } From dd0b37751345117650dfccffcdb33a9dc6331c35 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 May 2021 01:32:26 -0700 Subject: [PATCH 06/10] Do wrmsr bit-twiddling in Rust Signed-off-by: Joe Richey --- src/asm/asm.s | 16 +++++++--------- src/asm/mod.rs | 2 +- src/registers/model_specific.rs | 21 ++++++++++----------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/asm/asm.s b/src/asm/asm.s index a3566a7ac..df7506f8b 100644 --- a/src/asm/asm.s +++ b/src/asm/asm.s @@ -224,21 +224,19 @@ _x86_64_asm_write_cr4: .global _x86_64_asm_rdmsr .p2align 4 _x86_64_asm_rdmsr: - mov %edi,%ecx + mov %edi, %ecx # First param is the MSR number rdmsr - shl $0x20,%rdx # shift edx to upper 32bit - mov %eax,%eax # clear upper 32bit of rax - or %rdx,%rax # or with rdx + shl $32, %rdx # shift edx to upper 32bit + mov %eax, %eax # clear upper 32bit of rax + or %rdx, %rax # or with rdx retq .global _x86_64_asm_wrmsr .p2align 4 _x86_64_asm_wrmsr: - mov %edi,%ecx - movq %rsi,%rax - movq %rsi,%rdx - shr $0x20,%rdx - wrmsr + movl %edi, %ecx # First param is the MSR number + movl %esi, %eax # Second param is the low 32-bits + wrmsr # Third param (high 32-bits) is already in %edx retq .global _x86_64_asm_hlt diff --git a/src/asm/mod.rs b/src/asm/mod.rs index df097ebba..0e5fb92df 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -214,7 +214,7 @@ extern "C" { any(target_env = "gnu", target_env = "musl"), link_name = "_x86_64_asm_wrmsr" )] - pub(crate) fn x86_64_asm_wrmsr(msr: u32, value: u64); + pub(crate) fn x86_64_asm_wrmsr(msr: u32, low: u32, high: u32); #[cfg_attr( any(target_env = "gnu", target_env = "musl"), diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 6dda29bf6..acebb1a5a 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -142,20 +142,19 @@ mod x86_64 { /// effects. #[inline] pub unsafe fn write(&mut self, value: u64) { + let low = value as u32; + let high = (value >> 32) as u32; + #[cfg(feature = "inline_asm")] - { - let low = value as u32; - let high = (value >> 32) as u32; - asm!( - "wrmsr", - in("ecx") self.0, - in("eax") low, in("edx") high, - options(nostack, preserves_flags), - ); - } + asm!( + "wrmsr", + in("ecx") self.0, + in("eax") low, in("edx") high, + options(nostack, preserves_flags), + ); #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_wrmsr(self.0, value); + crate::asm::x86_64_asm_wrmsr(self.0, low, high); } } From 8d61248faef84ce8166d66271a7739c911d38f56 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 May 2021 01:45:44 -0700 Subject: [PATCH 07/10] Add non inline_asm XCr0 implementation Signed-off-by: Joe Richey --- src/asm/asm.s | 18 ++++++++++++++++++ src/asm/mod.rs | 12 ++++++++++++ src/registers/xcontrol.rs | 17 ++++++++++++++--- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/asm/asm.s b/src/asm/asm.s index df7506f8b..16f11c6c1 100644 --- a/src/asm/asm.s +++ b/src/asm/asm.s @@ -280,3 +280,21 @@ _x86_64_asm_rdgsbase: _x86_64_asm_wrgsbase: wrgsbase %rdi retq + +.global _x86_64_asm_xgetbv +.p2align 4 +_x86_64_asm_xgetbv: + mov %edi, %ecx # First param is the XCR number + xgetbv + shl $32, %rdx # shift edx to upper 32bit + mov %eax, %eax # clear upper 32bit of rax + or %rdx, %rax # or with rdx + retq + +.global _x86_64_asm_xsetbv +.p2align 4 +_x86_64_asm_xsetbv: + movl %edi, %ecx # First param is the XCR number + movl %esi, %eax # Second param is the low 32-bits + xsetbv # Third param (high 32-bits) is already in %edx + retq diff --git a/src/asm/mod.rs b/src/asm/mod.rs index 0e5fb92df..57ec996b8 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -251,4 +251,16 @@ extern "C" { link_name = "_x86_64_asm_wrgsbase" )] pub(crate) fn x86_64_asm_wrgsbase(val: u64); + + #[cfg_attr( + any(target_env = "gnu", target_env = "musl"), + link_name = "_x86_64_asm_xgetbv" + )] + pub(crate) fn x86_64_asm_xgetbv(xcr: u32) -> u64; + + #[cfg_attr( + any(target_env = "gnu", target_env = "musl"), + link_name = "_x86_64_asm_xsetbv" + )] + pub(crate) fn x86_64_asm_xsetbv(xcr: u32, low: u32, high: u32); } diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index c74147d72..8d2dfb789 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -24,7 +24,7 @@ bitflags! { } } -#[cfg(all(feature = "instructions", feature = "inline_asm"))] +#[cfg(feature = "instructions")] mod x86_64 { use super::*; impl XCr0 { @@ -37,16 +37,22 @@ mod x86_64 { /// Read the current raw XCR0 value. #[inline] pub fn read_raw() -> u64 { - let (low, high): (u32, u32); + #[cfg(feature = "inline_asm")] unsafe { + let (low, high): (u32, u32); asm!( "xgetbv", in("ecx") 0, out("rax") low, out("rdx") high, options(nomem, nostack, preserves_flags), ); + (high as u64) << 32 | (low as u64) + } + + #[cfg(not(feature = "inline_asm"))] + unsafe { + crate::asm::x86_64_asm_xgetbv(0) } - (high as u64) << 32 | (low as u64) } /// Write XCR0 flags. @@ -78,12 +84,17 @@ mod x86_64 { pub unsafe fn write_raw(value: u64) { let low = value as u32; let high = (value >> 32) as u32; + + #[cfg(feature = "inline_asm")] asm!( "xsetbv", in("ecx") 0, in("rax") low, in("rdx") high, options(nomem, nostack, preserves_flags), ); + + #[cfg(not(feature = "inline_asm"))] + crate::asm::x86_64_asm_xsetbv(0, low, high); } } } From 951d2badbb5c1dad4ae05aa9e931ebd403f22718 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 May 2021 02:07:07 -0700 Subject: [PATCH 08/10] Add doc(cfg()) noting that some functions need nightly Signed-off-by: Joe Richey --- Cargo.toml | 3 +++ src/instructions/interrupts.rs | 13 ++++--------- src/instructions/mod.rs | 10 ++++++---- src/lib.rs | 1 + 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 42c3233cf..f4d845bf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,9 @@ inline_asm = [] abi_x86_interrupt = [] const_fn = [] +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] + [package.metadata.release] no-dev-version = true pre-release-replacements = [ diff --git a/src/instructions/interrupts.rs b/src/instructions/interrupts.rs index c3827bd39..df34a2e7e 100644 --- a/src/instructions/interrupts.rs +++ b/src/instructions/interrupts.rs @@ -152,19 +152,14 @@ pub fn int3() { /// This currently needs to be a macro because the `int` argument needs to be an /// immediate. This macro will be replaced by a generic function when support for /// const generics is implemented in Rust. -#[cfg(feature = "inline_asm")] +#[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] #[macro_export] macro_rules! software_interrupt { ($x:expr) => {{ + #[cfg(feature = "inline_asm")] asm!("int {id}", id = const $x, options(nomem, nostack)); - }}; -} -/// Not implemented -#[cfg(not(feature = "inline_asm"))] -#[macro_export] -macro_rules! software_interrupt { - ($x:expr) => {{ - compile_error!("software_interrupt not implemented for non-nightly"); + #[cfg(not(feature = "inline_asm"))] + compile_error!("software_interrupt!() requires \"nightly\" feature"); }}; } diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index a92a5a776..51c4d9f2e 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -53,14 +53,16 @@ pub fn bochs_breakpoint() { /// Gets the current instruction pointer. Note that this is only approximate as it requires a few /// instructions to execute. -#[cfg(feature = "inline_asm")] +#[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] #[inline(always)] pub fn read_rip() -> crate::VirtAddr { let rip: u64; + #[cfg(feature = "inline_asm")] unsafe { - asm!( - "lea {}, [rip]", out(reg) rip, options(nostack, nomem, preserves_flags) - ); + asm!("lea {}, [rip]", out(reg) rip, options(nostack, nomem, preserves_flags)); } + + #[cfg(not(feature = "inline_asm"))] + compile_error!("read_rip() requires \"nightly\" feature"); crate::VirtAddr::new(rip) } diff --git a/src/lib.rs b/src/lib.rs index c3f8616da..d5cc3632e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ #![cfg_attr(feature = "const_fn", feature(const_fn_trait_bound))] #![cfg_attr(feature = "inline_asm", feature(asm))] #![cfg_attr(feature = "abi_x86_interrupt", feature(abi_x86_interrupt))] +#![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![deny(missing_debug_implementations)] From 8f7e5d6e353c7b2e073474c2fada890db8342819 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 May 2021 14:47:42 -0700 Subject: [PATCH 09/10] Don't use compile_error! Also, update the docs attrube to point to both relevant features. Signed-off-by: Joe Richey --- src/instructions/interrupts.rs | 7 ++----- src/instructions/mod.rs | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/instructions/interrupts.rs b/src/instructions/interrupts.rs index df34a2e7e..f1d171cf8 100644 --- a/src/instructions/interrupts.rs +++ b/src/instructions/interrupts.rs @@ -152,14 +152,11 @@ pub fn int3() { /// This currently needs to be a macro because the `int` argument needs to be an /// immediate. This macro will be replaced by a generic function when support for /// const generics is implemented in Rust. -#[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] +#[cfg(feature = "inline_asm")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "nightly", feature = "inline_asm"))))] #[macro_export] macro_rules! software_interrupt { ($x:expr) => {{ - #[cfg(feature = "inline_asm")] asm!("int {id}", id = const $x, options(nomem, nostack)); - - #[cfg(not(feature = "inline_asm"))] - compile_error!("software_interrupt!() requires \"nightly\" feature"); }}; } diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 51c4d9f2e..de1c4c3fa 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -53,16 +53,13 @@ pub fn bochs_breakpoint() { /// Gets the current instruction pointer. Note that this is only approximate as it requires a few /// instructions to execute. -#[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] +#[cfg(feature = "inline_asm")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "nightly", feature = "inline_asm"))))] #[inline(always)] pub fn read_rip() -> crate::VirtAddr { let rip: u64; - #[cfg(feature = "inline_asm")] unsafe { asm!("lea {}, [rip]", out(reg) rip, options(nostack, nomem, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - compile_error!("read_rip() requires \"nightly\" feature"); crate::VirtAddr::new(rip) } From 2d1e4a269dbbcf52e6f2442fa4cfc62fb6aee2ce Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 6 May 2021 23:45:41 -0700 Subject: [PATCH 10/10] Update Changelog Signed-off-by: Joe Richey --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index e52d742f3..85608ca98 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,10 @@ # Unreleased +- Multiple improvements to assembly code ([#251](https://github.com/rust-osdev/x86_64/pull/251)) + - Added `external_asm` implementations for `bochs_breakpoint` and `XCr0` + - Updated `options` for `asm!` blocks (to improve performance) + - Updated docs to use [`doc_cfg`](https://doc.rust-lang.org/unstable-book/language-features/doc-cfg.html) + # 0.14.1 – 2021-05-06 - Use new `const_fn_trait_bound` feature to fix build on latest nightly ([#250](https://github.com/rust-osdev/x86_64/pull/250))