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

Multiple improvements to the inline assembly code #251

Merged
merged 10 commits into from
May 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -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))
Expand Down
47 changes: 35 additions & 12 deletions src/asm/asm.s
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -135,11 +139,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
Expand Down Expand Up @@ -223,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
Expand All @@ -252,6 +251,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:
Expand All @@ -275,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
20 changes: 19 additions & 1 deletion src/asm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -208,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"),
Expand Down Expand Up @@ -245,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);
}
36 changes: 11 additions & 25 deletions src/instructions/interrupts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Expand All @@ -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();
}
}
Expand Down Expand Up @@ -129,26 +126,23 @@ 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();
}
}

/// 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();
}
}
Expand All @@ -159,18 +153,10 @@ pub fn int3() {
/// 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(any(feature = "nightly", feature = "inline_asm"))))]
#[macro_export]
macro_rules! software_interrupt {
($x:expr) => {{
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");
}};
}
26 changes: 12 additions & 14 deletions src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ pub mod tlb;
/// Halts the CPU until the next interrupt arrives.
#[inline]
pub fn hlt() {
#[cfg(feature = "inline_asm")]
unsafe {
asm!("hlt", options(nomem, nostack));
}
#[cfg(feature = "inline_asm")]
asm!("hlt", options(nomem, nostack, preserves_flags));

#[cfg(not(feature = "inline_asm"))]
unsafe {
#[cfg(not(feature = "inline_asm"))]
crate::asm::x86_64_asm_hlt();
}
}
Expand All @@ -31,37 +29,37 @@ 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();
}
}

/// 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 {
asm!("xchg bx, bx", options(nomem, nostack));
#[cfg(feature = "inline_asm")]
asm!("xchg bx, bx", options(nomem, nostack, preserves_flags));

#[cfg(not(feature = "inline_asm"))]
crate::asm::x86_64_asm_bochs();
}
}

/// 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(any(feature = "nightly", feature = "inline_asm"))))]
#[inline(always)]
pub fn read_rip() -> crate::VirtAddr {
let rip: u64;
unsafe {
asm!(
"lea {}, [rip]", out(reg) rip, options(nostack, nomem)
);
asm!("lea {}, [rip]", out(reg) rip, options(nostack, nomem, preserves_flags));
}
crate::VirtAddr::new(rip)
}
Loading