Skip to content

Introduce x86 to Libtock-rs #569

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ rthumbv7em = "run --release --target=thumbv7em-none-eabi --example"
rtv7em = "rthumbv7em"

# Common settings for all embedded targets
[target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))']
[target.'cfg(any(target_arch = "arm", target_arch = "riscv32", target_arch = "x86"))']
rustflags = [
"-C", "relocation-model=static",
"-C", "link-arg=-icf=all",
Expand Down
2 changes: 2 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ pub struct TockSyscalls;
mod syscalls_impl_arm;
#[cfg(target_arch = "riscv32")]
mod syscalls_impl_riscv;
#[cfg(target_arch = "x86")]
mod syscalls_impl_x86;
120 changes: 120 additions & 0 deletions runtime/src/startup/asm_x86.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* rt_header is defined by the general linker script (libtock_layout.ld). It has
* the following layout:
*
* Field | Offset
* ------------------------------------
* Address of the start symbol | 0
* Initial process break | 4
* Top of the stack | 8
* Size of .data | 12
* Start of .data in flash | 16
* Start of .data in ram | 20
* Size of .bss | 24
* Start of .bss in ram | 28
*/

/* start is the entry point -- the first code executed by the kernel. The kernel
* passes the following arguments onto the stack:
*
* esp+4 Pointer to beginning of the process binary's code. The linker script
* locates rt_header at this address.
*
* +8 Address of the beginning of the process's usable memory region.
* +12 Size of the process' allocated memory region (including grant region)
* +16 Process break provided by the kernel.
*
* We currently only use the value in esp+4.
*/

/* int 0x03 is used to trigger a breakpoint which is promoted to a hard fault in the
absence of a debugger. This is useful to fault at failure cases where there is no
recovery path.
*/

/* Specify that the start code is allocated and executable (ax),
* and that it should be placed in the .start section of the binary.
*/

.section .start, "ax"
.globl start
start:
/*
* Verify that the binary was loaded to the correct
* address. We can do this by using the call command
* and grabbing the EIP off of the stack. The eip
* will be the "length of the call instruction" (5 bytes)
* ahead of the actual start of the program.
*/

call .Lget_eip // 1 byte for the opcode + 4 bytes for the relative offset
// = 5 byte long instruction
.Lget_eip:
popl %eax // eax = eip
subl $5, %eax // eax = eip - 5 byte instruction
movl 4(%esp), %ebx // ebx = rt_header (top of memory)
movl 0(%ebx), %ecx // ecx = rt_header.start
cmpl %ecx, %eax
je .Lset_brk
/* If the binary is not at the correct location, report the error via LowLevelDebug
* then exit. */
pushl %eax // eip, not consumed by the syscall, but is seen in trace
pushl $2 // Code 0x02 (app was not installed in the correct location)
pushl $1 // Minor number: Alert code
pushl $8 // Major number: LowLevelDebug driver
mov $2, %eax // Command syscall
int $0x40
addl $16, %esp
pushl $0
pushl $0
pushl $1 // Completion code: FAIL
pushl $0 // exit-terminate
mov $6, %eax // Exit syscall
int $0x40
addl $16, %esp
int $0x03 // If we return, trigger a fault


/* Set brk to rt_header initial break value */
.Lset_brk:
movl 4(%ebx), %ecx // ecx = initial process break
pushl $0
pushl $0
pushl %ecx // push initial process break
pushl $0
movl $5, %eax // memop
int $0x40

/* Set the stack pointer */
mov 8(%ebx), %esp

.Lzero_bss:
/* Zero out .bss */
movl 24(%ebx), %ecx // ecx = remaining = rt_header.bss_size
cmpl $0, %ecx
je .Lcopy_data // If there is no .bss, jump to copying .data
movl 28(%ebx), %edi // edi = dst = rt_header.bss_start
shrl $2, %ecx // ecx = remaining / 4 = number of words to zero
cld // Clear the direction flag
xorl %eax, %eax // eax = 0, value to set .bss to
rep stosl // Zero out the .bss_size
movl 24(%ebx), %ecx // ecx = remaining = rt_header.bss_size
andl $3, %ecx // ecx = remaining % 4 = number of bytes to zero
rep stosb // Zero out the remaining bytes

.Lcopy_data:
/* Copy .data into place */
movl 12(%ebx), %ecx // ecx = rt_header.data_size
cmpl $0, %ecx
je .Lcall_rust_start
movl 16(%ebx), %esi // esi = src = rt_header.data_flash_start
movl 20(%ebx), %edi // edi = dst = rt_header.data_ram_start
shrl $2, %ecx // ecx = rt_header.data_size / 4 = number of words to copy
cld // Clear the direction flag
rep movsl // Copy data from flash to ram
movl 12(%ebx), %ecx // ecx = rt_header.data_size
andl $3, %ecx // ecx = rt_header.data_size % 4 = number of bytes to copy
rep movsb // Copy the remaining bytes

.Lcall_rust_start:
jmp rust_start
int $0x03 // If we return, trigger a fault
2 changes: 2 additions & 0 deletions runtime/src/startup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use libtock_platform::{Syscalls, Termination};
core::arch::global_asm!(include_str!("asm_arm.s"));
#[cfg(target_arch = "riscv32")]
core::arch::global_asm!(include_str!("asm_riscv32.s"));
#[cfg(target_arch = "x86")]
core::arch::global_asm!(include_str!("asm_x86.s"), options(att_syntax));

/// `set_main!` is used to tell `libtock_runtime` where the process binary's
/// `main` function is. The process binary's `main` function must have the
Expand Down
150 changes: 150 additions & 0 deletions runtime/src/syscalls_impl_x86.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use core::arch::asm;
use libtock_platform::{syscall_class, RawSyscalls, Register};

unsafe impl RawSyscalls for crate::TockSyscalls {
// Yield 1 is used for yield_wait
unsafe fn yield1([Register(r0)]: [Register; 1]) {
unsafe {
asm!(
"pushl $0",
"pushl $0",
"pushl $0",
"pushl {0}", // r0
"movl $0, %eax",
"int $0x40",
"addl $16, %esp",

in(reg) r0,

// The following registers are clobbered by the syscall
out("eax") _,
out("ecx") _,
out("edx") _,
options(att_syntax),
);
}
}

// Yield 2 is used for yield_no_wait
unsafe fn yield2([Register(r0), Register(r1)]: [Register; 2]) {
unsafe {
asm!(
"pushl $0",
"pushl $0",
"pushl {0}", // r1
"pushl {1}", // r0
"movl $0, %eax",
"int $0x40",
"addl $16, %esp",

in(reg) r1,
in(reg) r0,

// The following registers are clobbered by the syscall
out("eax") _,
out("ecx") _,
out("edx") _,
options(att_syntax)
);
}
}

unsafe fn syscall1<const CLASS: usize>([Register(mut r0)]: [Register; 1]) -> [Register; 2] {
// This is memop, the only syscall class that syscall1 supports
let r1;
unsafe {
asm!(
"push $0",
"push $0",
"push $0",
"push {0}", // r0
"movl $5, %eax",
"int $0x40",
"popl {0:e}", // r1
"popl {1:e}", // r0
"addl $8, %esp",

inlateout(reg) r0,
out(reg) r1,

// The following registers are clobbered by the syscall
out("eax") _,
options(att_syntax),
);
}
[Register(r0), Register(r1)]
}

unsafe fn syscall2<const CLASS: usize>(
[Register(mut r0), Register(mut r1)]: [Register; 2],
) -> [Register; 2] {
let cmd: u32 = match CLASS {
syscall_class::MEMOP => 5,
syscall_class::EXIT => 6,
_ => unreachable!(),
};

unsafe {
asm!(
"pushl $0",
"pushl $0",
"pushl {0}", // r1
"pushl {1}", // r0
"movl {2}, %eax", // cmd
"int $0x40",
"popl {1:e}", // r0
"popl {0:e}", // r1
"addl $8, %esp",

inlateout(reg) r1,
inlateout(reg) r0,
in(reg) cmd,

// The following registers are clobbered by the syscall
out("eax") _,
options(att_syntax),
);
}

[Register(r0), Register(r1)]
}

unsafe fn syscall4<const CLASS: usize>(
[Register(mut r0), Register(mut r1), Register(mut r2), Register(mut r3)]: [Register; 4],
) -> [Register; 4] {
let cmd: u32 = match CLASS {
syscall_class::SUBSCRIBE => 1,
syscall_class::COMMAND => 2,
syscall_class::ALLOW_RW => 3,
syscall_class::ALLOW_RO => 4,
_ => unreachable!(),
};
unsafe {
asm!(
"pushl {3}", // r3
"pushl {2}", // r2
"pushl {1}", // r1
"pushl {0}", // r0
"movl {4:e}, %eax",
"int $0x40",
"popl {0:e}", // r0
"popl {1:e}", // r1
"popl {2:e}", // r2
"popl {3:e}", // r3

inlateout(reg) r0,
inlateout(reg) r1,
inlateout(reg) r2,
inlateout(reg) r3,

in(reg) cmd,

// The following registers are clobbered by the syscall
out("eax") _,
options(att_syntax),
);
}

[Register(r0), Register(r1), Register(r2), Register(r3)]
}
}
Loading