Skip to content
Draft
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
12 changes: 11 additions & 1 deletion loader/src/memory_mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use core::alloc::Layout;

pub type Result<T> = core::result::Result<T, &'static str>;

pub type MemoryWriter = fn(&mut MemoryMapper, usize, &[u8]) -> Result<()>;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct MemoryPermissions(u8);

Expand Down Expand Up @@ -79,11 +81,12 @@ pub struct MemoryMapper {
virtual_end: usize,
mem: Storage,
mode: MappingMode,
writer: Option<MemoryWriter>,
}

impl MemoryMapper {
#[inline]
pub fn new(regions: Option<&'static [MemoryRegion]>) -> Self {
pub fn new(regions: Option<&'static [MemoryRegion]>, writer: Option<MemoryWriter>) -> Self {
Self {
virtual_entry: 0,
virtual_start: usize::MAX,
Expand All @@ -93,6 +96,7 @@ impl MemoryMapper {
Some(regions) => MappingMode::Fixed(regions),
None => MappingMode::Allocated,
},
writer,
}
}

Expand Down Expand Up @@ -250,6 +254,12 @@ impl MemoryMapper {

pub fn write_slice_at(&mut self, vaddr: usize, data: &[u8]) -> Result<usize> {
let size = data.len();
if let Some(writer) = self.writer.take() {
let result = writer(self, vaddr, data);
self.writer = Some(writer);
result?;
return Ok(size);
}
if size == 0 {
return Ok(size);
}
Expand Down
31 changes: 30 additions & 1 deletion loader/tests/inputs/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import("//build/boards/${board}.gni")
import("//build/toolchain/blueos.gni")

if (defined(loader_test_relocation)) {
if (defined(ram_loader_test_relocation)) {
_loader_test_app = "exec_app:loader_test_app"
} else {
_loader_test_app = "no_std_app:loader_test_app"
Expand All @@ -26,6 +26,10 @@ group("loader_test_app") {

# Postlink actions need visibility to the target that generates the binary.
public_deps = [ _loader_test_app ]
if (defined(ram_loader_test_relocation) &&
defined(flash_loader_test_relocation)) {
public_deps += [ "exec_app:loader_test_uselibrs" ]
}
}

postlink_action("gen_loader_test_elf_path_symbol") {
Expand All @@ -46,10 +50,35 @@ static_library("loader_test_elf_path") {
sources = get_target_outputs(":gen_loader_test_elf_path_symbol")
}

if (defined(ram_loader_test_relocation) &&
defined(flash_loader_test_relocation)) {
postlink_action("gen_loader_test_uselibrs_elf_path_symbol") {
testonly = true
exe = "exec_app:loader_test_uselibrs"
script = "//kernel/loader/tests/scripts/gen_path_symbol.py"
outfile = "${target_gen_dir}/${target_name}.c"
args = [
"LOADER_TEST_USELIBRS_ELF_PATH",
rebase_path(outfile),
]
outputs = [ outfile ]
}

static_library("loader_test_uselibrs_elf_path") {
testonly = true
deps = [ ":gen_loader_test_uselibrs_elf_path_symbol" ]
sources = get_target_outputs(":gen_loader_test_uselibrs_elf_path_symbol")
}
}

group("inputs") {
testonly = true
deps = [
":loader_test_elf_path",
"malware:bomb_path",
]
if (defined(ram_loader_test_relocation) &&
defined(flash_loader_test_relocation)) {
deps += [ ":loader_test_uselibrs_elf_path" ]
}
}
99 changes: 83 additions & 16 deletions loader/tests/inputs/exec_app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,19 @@
import("//build/boards/${board}.gni")
import("//build/templates/rust.gni")

assert(defined(loader_test_relocation),
"loader_test_relocation must be defined for the EXEC loader test app")
assert(defined(loader_test_relocation.region),
"loader_test_relocation.region must be defined")
assert(defined(loader_test_relocation.origin),
"loader_test_relocation.origin must be defined")
assert(defined(loader_test_relocation.length),
"loader_test_relocation.length must be defined")
assert(defined(loader_test_relocation.permissions),
"loader_test_relocation.permissions must be defined")
assert(
defined(ram_loader_test_relocation),
"ram_loader_test_relocation must be defined for the EXEC loader test app")
assert(defined(ram_loader_test_relocation.region),
"ram_loader_test_relocation.region must be defined")
assert(defined(ram_loader_test_relocation.origin),
"ram_loader_test_relocation.origin must be defined")
assert(defined(ram_loader_test_relocation.length),
"ram_loader_test_relocation.length must be defined")
assert(defined(ram_loader_test_relocation.permissions),
"ram_loader_test_relocation.permissions must be defined")

_loader_test_linker_script_template = "link.x.template"
_loader_test_linker_script_template = "ram_link.x.template"
_loader_test_linker_script = "${target_gen_dir}/loader_test_relocation_link.x"

action("gen_loader_test_relocation") {
Expand All @@ -35,13 +36,13 @@ action("gen_loader_test_relocation") {
inputs = [ _loader_test_linker_script_template ]
args = [
"--region",
loader_test_relocation.region,
ram_loader_test_relocation.region,
"--origin",
loader_test_relocation.origin,
ram_loader_test_relocation.origin,
"--length",
loader_test_relocation.length,
ram_loader_test_relocation.length,
"--permissions",
loader_test_relocation.permissions,
ram_loader_test_relocation.permissions,
"--linker-script-template",
rebase_path(_loader_test_linker_script_template),
"--linker-script",
Expand All @@ -53,15 +54,81 @@ action("gen_loader_test_relocation") {
build_rust("loader_test_app") {
testonly = true
crate_type = "bin"
sources = [ "src/main.rs" ]
sources = [ "src/xor.rs" ]
build_deps = [ ":gen_loader_test_relocation" ]
configs += [ "//build/boards/${board}:app_config" ]
linker_script = _loader_test_linker_script
rustflags = [
"-Cpanic=abort",
"-Crelocation-model=static",
"-Cstrip=symbols",
"-Clink-arg=-Wl,-s",
"-Clink-arg=-Wl,-static",
"-Clink-arg=-Wl,--no-pie",
"-Clink-arg=-nostartfiles",
]
}

if (defined(flash_loader_test_relocation)) {
_irom = flash_loader_test_relocation.irom
_rodata = flash_loader_test_relocation.rodata
_rwdata = flash_loader_test_relocation.rwdata
_loader_test_xip_linker_script_template = "flash_link.x.template"
_loader_test_xip_linker_script = "${target_gen_dir}/loader_test_xip_link.x"

action("gen_loader_test_xip_relocation") {
testonly = true
script = "//kernel/loader/tests/scripts/gen_loader_test_relocation.py"
inputs = [ _loader_test_xip_linker_script_template ]
args = [
"--irom-origin",
_irom.origin,
"--irom-length",
_irom.length,
"--irom-permissions",
_irom.permissions,
"--rodata-origin",
_rodata.origin,
"--rodata-length",
_rodata.length,
"--rodata-permissions",
_rodata.permissions,
"--rwdata-origin",
_rwdata.origin,
"--rwdata-length",
_rwdata.length,
"--rwdata-permissions",
_rwdata.permissions,
"--flash-mmu-page-size",
flash_loader_test_relocation.page_size,
"--linker-script-template",
rebase_path(_loader_test_xip_linker_script_template),
"--linker-script",
rebase_path(_loader_test_xip_linker_script),
]
outputs = [ _loader_test_xip_linker_script ]
}

build_rust("loader_test_uselibrs") {
testonly = true
crate_type = "bin"
sources = [ "src/uselibrs.rs" ]
build_deps = [ ":gen_loader_test_xip_relocation" ]
configs += [ "//build/boards/${board}:app_config" ]
linker_script = _loader_test_xip_linker_script
rustflags = [
"-Cpanic=abort",
"-Crelocation-model=static",
"-Cstrip=symbols",
"-Clink-arg=-Wl,-s",
"-Clink-arg=-Wl,-static",
"-Clink-arg=-Wl,--no-pie",
"-Clink-arg=-nostartfiles",
]
deps = [
"//kernel/kernel:atomic",
"//libc",
"//librs:librs_swi",
]
}
}
74 changes: 74 additions & 0 deletions loader/tests/inputs/exec_app/flash_link.x.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2026 vivo Mobile Communication Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

ENTRY(_start)

MEMORY
{
IROM (@IROM_PERMISSIONS@) : ORIGIN = @IROM_ORIGIN@, LENGTH = @IROM_LENGTH@
RODATA (@RODATA_PERMISSIONS@) : ORIGIN = @RODATA_ORIGIN@, LENGTH = @RODATA_LENGTH@
RWDATA (@RWDATA_PERMISSIONS@) : ORIGIN = @RWDATA_ORIGIN@, LENGTH = @RWDATA_LENGTH@
}

PHDRS
{
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
}

SECTIONS
{
.text : ALIGN(4)
{
KEEP(*(.text._start))
*(.text .text.*)
} > IROM :text

.rodata : ALIGN(4)
{
*(.srodata .srodata.*)
*(.rodata .rodata.*)
} > RODATA :rodata

.data : ALIGN(4)
{
PROVIDE(__global_pointer$ = . + 0x800);
*(.sdata .sdata.*)
*(.data .data.*)
*(.tdata .tdata.*)
} > RWDATA :data

.bss (NOLOAD) : ALIGN(4)
{
*(.sbss .sbss.*)
*(.bss .bss.*)
*(.tbss .tbss.*)
*(COMMON)
} > RWDATA :data

/DISCARD/ :
{
*(.eh_frame*)
*(.comment*)
}

ASSERT(SIZEOF(.text) > 0, "EXEC payload has no text")
ASSERT(SIZEOF(.text) <= @FLASH_MMU_PAGE_SIZE@,
"EXEC payload text exceeds its flash MMU page")
ASSERT(SIZEOF(.rodata) <= @FLASH_MMU_PAGE_SIZE@,
"EXEC payload rodata exceeds its flash MMU page")
}
65 changes: 65 additions & 0 deletions loader/tests/inputs/exec_app/src/uselibrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) 2026 vivo Mobile Communication Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![no_main]
#![no_std]

use core::alloc::{GlobalAlloc, Layout};

// librs links liballoc even though this test does not allocate.
struct UnusedAllocator;

unsafe impl GlobalAlloc for UnusedAllocator {
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
core::ptr::null_mut()
}

unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}

#[global_allocator]
static ALLOCATOR: UnusedAllocator = UnusedAllocator;

// RV32IMC has no A extension. libatomic uses these hooks for its fallback.
#[no_mangle]
pub extern "C" fn disable_local_irq_save() -> usize {
let old: usize;
unsafe {
core::arch::asm!(
"csrrci {old}, mstatus, 8",
old = out(reg) old,
options(nostack),
);
}
old
}

#[no_mangle]
pub extern "C" fn enable_local_irq_restore(old: usize) {
unsafe {
core::arch::asm!("csrw mstatus, {old}", old = in(reg) old, options(nostack));
}
}

#[no_mangle]
pub extern "C" fn _start() -> i32 {
librs::time::msleep(1)
}

#[panic_handler]
fn panic(_: &core::panic::PanicInfo<'_>) -> ! {
loop {
core::hint::spin_loop();
}
}
Loading