Skip to content
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
1 change: 1 addition & 0 deletions riscv-rt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ targets = [

[build-dependencies]
riscv-target-parser = { path = "../riscv-target-parser", version = "0.1.2" }
minilink = "0.2"

[dependencies]
riscv = { path = "../riscv", version = "0.14.0" }
Expand Down
49 changes: 10 additions & 39 deletions riscv-rt/build.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,19 @@
// NOTE: Adapted from cortex-m/build.rs

use riscv_target_parser::RiscvTarget;
use std::{env, fs, io, path::PathBuf};
use riscv_target_parser::{RiscvTarget, Width};
use std::env;

// List of all possible RISC-V configurations to check for in risv-rt
const RISCV_CFG: [&str; 4] = ["riscvi", "riscvm", "riscvf", "riscvd"];

fn add_linker_script(arch_width: u32) -> io::Result<()> {
// Read the file to a string and replace all occurrences of ${ARCH_WIDTH} with the arch width
let mut content = fs::read_to_string("link.x.in")?;
content = content.replace("${ARCH_WIDTH}", &arch_width.to_string());

// Get target-dependent linker configuration and replace ${INCLUDE_LINKER_FILES} with it
let mut include_content = String::new();

// If no-exceptions is disabled, include the exceptions.x files
if env::var_os("CARGO_FEATURE_NO_EXCEPTIONS").is_none() {
let exceptions_content = fs::read_to_string("exceptions.x")?;
include_content.push_str(&(exceptions_content + "\n"));
}
// If no-interrupts is disabled, include the interrupts.x files
if env::var_os("CARGO_FEATURE_NO_INTERRUPTS").is_none() {
let interrupts_content = fs::read_to_string("interrupts.x")?;
include_content.push_str(&(interrupts_content + "\n"));
}
// If device is enabled, include the device.x file (usually, provided by PACs)
if env::var_os("CARGO_FEATURE_DEVICE").is_some() {
include_content.push_str("/* Device-specific exception and interrupt handlers */\n");
include_content.push_str("INCLUDE device.x\n");
fn add_linker_script(arch_width: Width) {
// `CARGO_CFG_TARGET_POINTER_WIDTH` technically be used, but instruction
// alignment and pointer width aren't necessarily the same things.
unsafe {
std::env::set_var("CARGO_CFG_ARCH_WIDTH", arch_width.to_string());
}
// If memory is enabled, include the memory.x file (usually, provided by BSPs)
if env::var_os("CARGO_FEATURE_MEMORY").is_some() {
include_content.push_str("/* Device-specific memory layout */\n");
include_content.push_str("INCLUDE memory.x\n");
}

content = content.replace("${INCLUDE_LINKER_FILES}", &include_content);

// Put the linker script somewhere the linker can find it
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
fs::write(out_dir.join("link.x"), content)?;
println!("cargo:rustc-link-search={}", out_dir.display());
println!("cargo:rerun-if-changed=link.x");

Ok(())
minilink::register_template("./link.x.in", "link.x");
}

fn main() {
Expand Down Expand Up @@ -86,6 +56,7 @@ fn main() {
println!("cargo:rustc-cfg={flag}");
}
}
add_linker_script(width.into()).unwrap();

add_linker_script(width);
}
}
23 changes: 0 additions & 23 deletions riscv-rt/exceptions.x

This file was deleted.

25 changes: 0 additions & 25 deletions riscv-rt/interrupts.x

This file was deleted.

98 changes: 75 additions & 23 deletions riscv-rt/link.x.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@

- `PROVIDE` is used to provide default values that can be overridden by a user linker script

- In this linker script, you may find symbols that look like `${...}` (e.g., `${ARCH_WIDTH}`).
These are wildcards used by the `build.rs` script to adapt to different target particularities.
Check `build.rs` for more details about these symbols.

- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
the LMA of .data are all `${ARCH_WIDTH}`-byte aligned. These alignments are assumed by the RAM
initialization routine. There's also a second benefit: `${ARCH_WIDTH}`-byte aligned boundaries
the LMA of .data are all `{{ cfg.arch_width }}`-byte aligned. These alignments are assumed by the RAM
initialization routine. There's also a second benefit: `{{ cfg.arch_width }}`-byte aligned boundaries
means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`.
*/

Expand Down Expand Up @@ -67,7 +63,63 @@ PROVIDE(DefaultHandler = abort);
to avoid compilation errors in direct mode, not to allow users to overwrite the symbol. */
PROVIDE(_start_DefaultHandler_trap = _start_trap);

${INCLUDE_LINKER_FILES}
{% if not contains(cfg.feature, "no-exceptions") %}
/* # EXCEPTION HANDLERS DESCRIBED IN THE STANDARD RISC-V ISA

It is possible to define a special handler for each exception type.
By default, all exceptions are handled by ExceptionHandler. However,
users can override these alias by defining the symbol themselves
*/
PROVIDE(InstructionMisaligned = ExceptionHandler);
PROVIDE(InstructionFault = ExceptionHandler);
PROVIDE(IllegalInstruction = ExceptionHandler);
PROVIDE(Breakpoint = ExceptionHandler);
PROVIDE(LoadMisaligned = ExceptionHandler);
PROVIDE(LoadFault = ExceptionHandler);
PROVIDE(StoreMisaligned = ExceptionHandler);
PROVIDE(StoreFault = ExceptionHandler);
PROVIDE(UserEnvCall = ExceptionHandler);
PROVIDE(SupervisorEnvCall = ExceptionHandler);
PROVIDE(MachineEnvCall = ExceptionHandler);
PROVIDE(InstructionPageFault = ExceptionHandler);
PROVIDE(LoadPageFault = ExceptionHandler);
PROVIDE(StorePageFault = ExceptionHandler);
{% endif %}

{% if not contains(cfg.feature, "no-interrupts") %}
/* # CORE INTERRUPT HANDLERS DESCRIBED IN THE STANDARD RISC-V ISA

It is possible to define a special handler for each interrupt type.
By default, all interrupts are handled by DefaultHandler. However, users can
override these alias by defining the symbol themselves
*/
PROVIDE(SupervisorSoft = DefaultHandler);
PROVIDE(MachineSoft = DefaultHandler);
PROVIDE(SupervisorTimer = DefaultHandler);
PROVIDE(MachineTimer = DefaultHandler);
PROVIDE(SupervisorExternal = DefaultHandler);
PROVIDE(MachineExternal = DefaultHandler);

/* When vectored trap mode is enabled, each interrupt source must implement its own
trap entry point. By default, all interrupts start in _DefaultHandler_trap.
However, users can override these alias by defining the symbol themselves */
PROVIDE(_start_SupervisorSoft_trap = _start_DefaultHandler_trap);
PROVIDE(_start_MachineSoft_trap = _start_DefaultHandler_trap);
PROVIDE(_start_SupervisorTimer_trap = _start_DefaultHandler_trap);
PROVIDE(_start_MachineTimer_trap = _start_DefaultHandler_trap);
PROVIDE(_start_SupervisorExternal_trap = _start_DefaultHandler_trap);
PROVIDE(_start_MachineExternal_trap = _start_DefaultHandler_trap);
{% endif %}

{% if contains(cfg.feature, "device")%}
/* Device-specific exception and interrupt handlers, usually provided by PACs */
INCLUDE device.x
{% endif %}

{% if contains(cfg.feature, "memory")%}
/* Device-specific memory layout, usually provided by BSPs */
INCLUDE memory.x
{% endif %}

PROVIDE(_stext = ORIGIN(REGION_TEXT));
PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
Expand Down Expand Up @@ -108,16 +160,16 @@ SECTIONS
*(.srodata .srodata.*);
*(.rodata .rodata.*);

/* ${ARCH_WIDTH}-byte align the end (VMA) of this section.
/* {{ cfg.arch_width }}-byte align the end (VMA) of this section.
This is required by LLD to ensure the LMA of the following .data
section will have the correct alignment. */
. = ALIGN(${ARCH_WIDTH});
. = ALIGN({{ cfg.arch_width }});
__erodata = .;
} > REGION_RODATA

.data : ALIGN(${ARCH_WIDTH})
.data : ALIGN({{ cfg.arch_width }})
{
. = ALIGN(${ARCH_WIDTH});
. = ALIGN({{ cfg.arch_width }});
__sdata = .;

/* Must be called __global_pointer$ for linker relaxations to work. */
Expand All @@ -130,15 +182,15 @@ SECTIONS
/* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to
* use the .data loading mechanism by pushing __edata. Note: do not change
* output region or load region in those user sections! */
. = ALIGN(${ARCH_WIDTH});
. = ALIGN({{ cfg.arch_width }});
__edata = .;

/* LMA of .data */
__sidata = LOADADDR(.data);

.bss (NOLOAD) : ALIGN(${ARCH_WIDTH})
.bss (NOLOAD) : ALIGN({{ cfg.arch_width }})
{
. = ALIGN(${ARCH_WIDTH});
. = ALIGN({{ cfg.arch_width }});
__sbss = .;

*(.sbss .sbss.* .bss .bss.*);
Expand All @@ -147,7 +199,7 @@ SECTIONS
/* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to
* use the .bss zeroing mechanism by pushing __ebss. Note: do not change
* output region or load region in those user sections! */
. = ALIGN(${ARCH_WIDTH});
. = ALIGN({{ cfg.arch_width }});
__ebss = .;

/* fictitious region that represents the memory available for the heap */
Expand Down Expand Up @@ -184,8 +236,8 @@ ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned");

ASSERT(ORIGIN(REGION_DATA) % ${ARCH_WIDTH} == 0, "
ERROR(riscv-rt): the start of the REGION_DATA must be ${ARCH_WIDTH}-byte aligned");
ASSERT(ORIGIN(REGION_DATA) % {{ cfg.arch_width }} == 0, "
ERROR(riscv-rt): the start of the REGION_DATA must be {{ cfg.arch_width }}-byte aligned");

ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned");
Expand All @@ -196,14 +248,14 @@ ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned");
ASSERT(_stext % 4 == 0, "
ERROR(riscv-rt): `_stext` must be 4-byte aligned");

ASSERT(__sdata % ${ARCH_WIDTH} == 0 && __edata % ${ARCH_WIDTH} == 0, "
BUG(riscv-rt): .data is not ${ARCH_WIDTH}-byte aligned");
ASSERT(__sdata % {{ cfg.arch_width }} == 0 && __edata % {{ cfg.arch_width }} == 0, "
BUG(riscv-rt): .data is not {{ cfg.arch_width }}-byte aligned");

ASSERT(__sidata % ${ARCH_WIDTH} == 0, "
BUG(riscv-rt): the LMA of .data is not ${ARCH_WIDTH}-byte aligned");
ASSERT(__sidata % {{ cfg.arch_width }} == 0, "
BUG(riscv-rt): the LMA of .data is not {{ cfg.arch_width }}-byte aligned");

ASSERT(__sbss % ${ARCH_WIDTH} == 0 && __ebss % ${ARCH_WIDTH} == 0, "
BUG(riscv-rt): .bss is not ${ARCH_WIDTH}-byte aligned");
ASSERT(__sbss % {{ cfg.arch_width }} == 0 && __ebss % {{ cfg.arch_width }} == 0, "
BUG(riscv-rt): .bss is not {{ cfg.arch_width }}-byte aligned");

ASSERT(__sheap % 4 == 0, "
BUG(riscv-rt): start of .heap is not 4-byte aligned");
Expand Down
3 changes: 3 additions & 0 deletions tests-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ panic-halt = "1.0"
riscv = { path = "../riscv" }
riscv-rt = { path = "../riscv-rt" }

[build-dependencies]
minilink = "0.2"

[features]
pre-init = ["riscv-rt/pre-init"]
single-hart = ["riscv-rt/single-hart"]
Expand Down
20 changes: 2 additions & 18 deletions tests-build/build.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
use std::{env, fs::File, io::Write, path::PathBuf};

fn main() {
// Put device.x somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("device.x"))
.unwrap()
.write_all(include_bytes!("device.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=device.x");

// Put memory.x somewhere the linker can find it
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=memory.x");

println!("cargo:rerun-if-changed=build.rs");
minilink::register_template("device.x", "device.x");
minilink::register_template("memory.x", "memory.x");
}
Loading