Skip to content
This repository was archived by the owner on Nov 28, 2023. It is now read-only.
Closed
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
24 changes: 24 additions & 0 deletions asm.S
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ _abs_start:
.cfi_startproc
.cfi_undefined ra

// Save boot arguments to RAM if on hartid 0
csrr t0, mhartid
bnez t0, 1f
la t1, boot_args
STORE a0, 0*REGBYTES(t1)
STORE a1, 1*REGBYTES(t1)
STORE a2, 2*REGBYTES(t1)
STORE a3, 3*REGBYTES(t1)
STORE a4, 4*REGBYTES(t1)
STORE a5, 5*REGBYTES(t1)
STORE a6, 6*REGBYTES(t1)
STORE a7, 7*REGBYTES(t1)
fence rw, rw

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not confident about this (single) fence, or about using plain reads in fn boot_arguments.

At first I started emulating the behavior of AtomicUsize: https://rust.godbolt.org/z/3djWjhPcG. But why is the fence instruction placed before stores and after loads? I’d expected fences to be between things in order to separate them.

On the Rust size I ended up with this, since AtomicUsize is disabled on targets without the A extension. The portable-atomic crate would be another option for doing the same.

/// Simulate AtomicUsize::load(Ordering::Acquire)
unsafe fn atomic_load(ptr: *const usize) -> usize {
    let value;

    // Only lw v.s. ld changes but asm! wants a string literal
    #[cfg(target_pointer_width = "32")]
    core::arch::asm!(
        "fence r, rw",
        "lw {value}, 0({ptr})",
        ptr = in(reg) ptr,
        value = out(reg) value,
    );
    #[cfg(not(target_pointer_width = "32"))]
    core::arch::asm!(
        "fence r, rw",
        "ld {value}, 0({ptr})",
        ptr = in(reg) ptr,
        value = out(reg) value,
    );
    value
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Position of fence instructions depends on the ordering. With SeqCst you get fences before and after the load.

1:

csrw mie, 0
csrw mip, 0

Expand Down Expand Up @@ -117,6 +132,15 @@ _abs_start:

.cfi_endproc

/*
Space to save the value of argument registers at boot
*/
.section .data
.global boot_args
boot_args:
#define ARGUMENT_REGISTER_COUNT 8 // a0 to a7
.skip REGBYTES * ARGUMENT_REGISTER_COUNT

/*
Trap entry point (_start_trap)

Expand Down
Binary file modified bin/riscv32i-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32ic-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32if-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32ifc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32ifd-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32ifdc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32im-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32imc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32imf-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32imfc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32imfd-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv32imfdc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64i-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64ic-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64if-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64ifc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64ifd-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64ifdc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64im-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64imc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64imf-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64imfc-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64imfd-unknown-none-elf.a
Binary file not shown.
Binary file modified bin/riscv64imfdc-unknown-none-elf.a
Binary file not shown.
17 changes: 17 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,13 @@ extern "C" {

// Initial values of the .data section (stored in Flash)
static _sidata: u32;

static boot_args: [usize; ARGUMENT_REGISTERS_COUNT];
}

/// a0 to a7
const ARGUMENT_REGISTERS_COUNT: usize = 8;

/// Rust entry point (_start_rust)
///
/// Zeros bss section, initializes data section and calls main. This function
Expand Down Expand Up @@ -389,6 +394,18 @@ pub unsafe extern "C" fn start_rust() -> ! {
main();
}

/// Returns the value an argument register had when this firmware was first booted.
///
/// The argument registers are `a0` to `a7`, indexed 0 to 7.
/// This returns `None` for an out-of-bounds index.
///
/// Some of these registers may have had values passed by the previous boot stage:
/// <https://doc.coreboot.org/arch/riscv/index.html#stage-handoff-protocol>.
/// Otherwise, the returned `usize` value is undefined.
pub fn boot_argument(index: usize) -> Option<usize> {
unsafe { boot_args.get(index).copied() }
}

/// Registers saved in trap handler
#[allow(missing_docs)]
#[repr(C)]
Expand Down