Skip to content

Commit

Permalink
Merge pull request #35 from ion098/feat/hot-cold
Browse files Browse the repository at this point in the history
feat: ✨ Add hot/cold support
  • Loading branch information
Tropix126 authored Nov 13, 2024
2 parents 879ab42 + 97be0fb commit f9a57b3
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 42 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/client-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ vex-v5-qemu-host = { path = "../host" }
thiserror = "1.0.63"
log = "0.4.22"
simplelog = "0.12.2"
clap-num = "1.1.1"
74 changes: 61 additions & 13 deletions packages/client-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
use std::path::PathBuf;
use std::{option::Option, ffi::OsStr, fs::File, io::Read, num::NonZeroU32, path::{Path, PathBuf}};

use anyhow::Context;
use anyhow::{bail, Context};
use clap_num::maybe_hex;
use log::LevelFilter;
use simplelog::{ColorChoice, ConfigBuilder, TermLogger, TerminalMode};
use tokio::{
io::{stdout, AsyncReadExt, AsyncWriteExt, BufReader},
process::Command,
};
use vex_v5_qemu_host::brain::Brain;
use vex_v5_qemu_host::brain::{Brain, Program};

// TODO: fix this cursedness
const DEFAULT_KERNEL: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../target/armv7a-none-eabi/debug/kernel"
);

/// Simulate the VEX V5 robot program at <BINARY>.
/// Simulate a VEX V5 robot program
#[derive(clap::Parser)]
struct Opt {
/// Start the simulator in a paused state and open a GDB server.
///
/// When enabled, the simulator will make a GDB server available on port
/// 1234, allowing a debugger to set breakpoints in and step through the
/// kernel or user code.
#[clap(short, long, env = "V5_SIM_GDB")]
#[clap(short, long, global = true, env = "V5_SIM_GDB")]
gdb: bool,

/// Override the kernel image.
Expand All @@ -32,20 +33,49 @@ struct Opt {
/// and set up the virtual machine before running the robot code.
/// This option defaults to a kernel designed to replicate the behavior
/// of programs under VEXos.
#[clap(short, long, default_value = DEFAULT_KERNEL, env = "V5_SIM_KERNEL_PATH")]
#[clap(short, long, global = true, default_value = DEFAULT_KERNEL, env = "V5_SIM_KERNEL_PATH")]
kernel: PathBuf,

/// Override the QEMU executable to a custom version of `qemu-system-arm`.
#[clap(short, long, default_value = "qemu-system-arm", env = "V5_SIM_QEMU")]
#[clap(short, long, global = true, default_value = "qemu-system-arm", env = "V5_SIM_QEMU")]
qemu: PathBuf,

#[clap(env = "V5_SIM_BINARY_PATH")]
binary: PathBuf,
/// What type of program to simulate
#[clap(subcommand)]
program_type: Subcommands,

/// Extra arguments to pass to QEMU.
qemu_args: Vec<String>,
}

#[derive(clap::Subcommand)]
enum Subcommands {
/// Simulate a monolith binary at <BIN>
Monolith {
/// The binary to run
#[clap(env = "V5_SIM_BINARY_PATH")]
bin: PathBuf,
},
/// Simulate a hot/cold binary at <HOT_BIN> and <COLD_BIN>
HotCold {
/// The hot binary to run
#[clap(env = "V5_SIM_HOT_BINARY_PATH")]
hot_bin: PathBuf,

/// The cold binary to run
#[clap(env = "V5_SIM_COLD_BINARY_PATH")]
cold_bin: PathBuf,

/// Override the address to load the hot binary at
#[clap(long, global = true, default_value = "0x07800000", value_parser=maybe_hex::<u32>, env = "V5_SIM_HOT_ADDR")]
hot_addr: u32,

/// Override the address to load the cold binary at
#[clap(long, global = true, default_value = "0x03800000", value_parser=maybe_hex::<u32>, env = "V5_SIM_COLD_ADDR")]
cold_addr: u32,
}
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let opt = <Opt as clap::Parser>::parse();
Expand All @@ -68,10 +98,28 @@ async fn main() -> anyhow::Result<()> {
qemu.args(["-S", "-s"]);
}

brain
.run_program(qemu, opt.kernel, opt.binary)
.await
.context("Failed to start QEMU.")?;
match opt.program_type {
Subcommands::Monolith {bin} => {
brain
.run_program(qemu, opt.kernel, Program{path: bin, load_addr: NonZeroU32::new(0x03800000).unwrap()}, None)
.await
.context("Failed to start QEMU.")?;
}
Subcommands::HotCold {hot_bin, cold_bin, hot_addr, cold_addr} => {
if hot_addr == 0 {
bail!("Hot load address cannot be zero!");
}
if cold_addr == 0 {
bail!("Cold load address cannot be zero!");
}
let hot_addr = NonZeroU32::new(hot_addr).unwrap();
let cold_addr = NonZeroU32::new(cold_addr).unwrap();
brain
.run_program(qemu, opt.kernel, Program{path: hot_bin, load_addr: hot_addr}, Some(Program{path: cold_bin, load_addr: cold_addr}))
.await
.context("Failed to start QEMU.")?;
}
}

// brain.kill_program().await.unwrap();

Expand Down
44 changes: 38 additions & 6 deletions packages/host/src/brain.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::{
ffi::OsString,
io,
num::NonZeroU32,
option::Option,
path::PathBuf,
process::{ExitStatus, Stdio},
sync::Arc,
sync::{Arc, atomic::{AtomicU32, Ordering}},
vec::Vec
};

use tokio::{
Expand All @@ -23,19 +27,27 @@ use crate::{
},
};

#[derive(Debug, Clone)]
pub struct Program {
pub path: PathBuf,
pub load_addr: NonZeroU32
}

#[derive(Debug)]
pub struct Brain {
pub peripherals: Option<Peripherals>,
connection: Arc<Mutex<Option<QemuConnection>>>,
task: AbortHandle,
barrier: Arc<Barrier>,
link_addr: Arc<AtomicU32>
}

impl Brain {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let connection = Arc::new(Mutex::new(None));
let barrier = Arc::new(Barrier::new(2));
let link_addr = Arc::new(AtomicU32::new(0));

let (peripherals_tx, peripherals_rx) = mpsc::channel::<KernelBoundPacket>(1024);

Expand Down Expand Up @@ -70,6 +82,7 @@ impl Brain {
Self {
connection: connection.clone(),
barrier: barrier.clone(),
link_addr: link_addr.clone(),
task: tokio::task::spawn(async move {
let mut peripherals_rx = peripherals_rx;
let smartport_senders: [Sender<SmartPortCommand>; 21] = [
Expand Down Expand Up @@ -118,6 +131,9 @@ impl Brain {
log::info!("Kernel exited with code {code}.");
}

HostBoundPacket::LinkAddressRequest => {
}

// The kernel has sent a device command packet to a specific smartport,
// so we must forward that packet to the respective smartport's
// receiver.
Expand Down Expand Up @@ -178,23 +194,39 @@ impl Brain {
&mut self,
mut qemu_command: Command,
kernel: PathBuf,
binary: PathBuf,
main_binary: Program,
linked_binary: Option<Program>,
) -> io::Result<()> {
qemu_command
let link_addr : u32 = linked_binary.clone().map_or(0, |v| v.load_addr.into());
let qemu_command = qemu_command
.args(["-machine", "xilinx-zynq-a9,memory-backend=mem"])
.args(["-cpu", "cortex-a9"])
.args(["-object", "memory-backend-ram,id=mem,size=256M"])
.args([
"-device",
&format!("loader,addr=0x200,data={},data-len=4,cpu-num=0", link_addr),
])
.args([
"-device",
&format!("loader,file={},addr=0x100000,cpu-num=0", kernel.display()),
])
.args([
"-device",
&format!(
"loader,file={},force-raw=on,addr=0x03800000",
binary.display()
"loader,file={},force-raw=on,addr={}",
main_binary.path.display(),
main_binary.load_addr
),
])
]);
if let Some(linked_binary) = linked_binary {
qemu_command.arg("-device");
qemu_command.arg(format!(
"loader,file={},force-raw=on,addr={}",
linked_binary.path.display(),
linked_binary.load_addr
));
}
qemu_command
.args(["-display", "none"])
.args(["-chardev", "stdio,id=char0"])
.args(["-serial", "null"])
Expand Down
1 change: 1 addition & 0 deletions packages/kernel/link/kernel.ld
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ __heap_end = _jump_table_start - __stack_size - __irq_stack_size - __fiq_stack_s

SECTIONS {
_vex_startup = _user_memory_start + 0x20;
_link_addr = 0x200;

.mmu_pages : {
KEEP(*(.mmu_pages))
Expand Down
27 changes: 18 additions & 9 deletions packages/kernel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ pub mod sync;
pub mod vectors;
pub mod xil;

use alloc::format;
use core::{num::NonZeroU32, sync::atomic::Ordering};
use crate::protocol::exit;
use log::LevelFilter;
use logger::KernelLogger;
use peripherals::{GIC, PRIVATE_TIMER, UART1, WATCHDOG_TIMER};
use sdk::vexSystemTimeGet;
use vex_v5_qemu_protocol::{code_signature::CodeSignature, HostBoundPacket};
use sdk::{vexSystemTimeGet, vexSystemLinkAddrGet};
use vex_v5_qemu_protocol::{code_signature::CodeSignature, HostBoundPacket, KernelBoundPacket};

extern "C" {
/// Entrypoint of the user program. (located at 0x03800020)
Expand Down Expand Up @@ -97,19 +100,25 @@ pub extern "C" fn _start() -> ! {
// FreeRTOS if needed.
peripherals::setup_private_timer().unwrap();

// Send user code signature to host.
log::debug!("Sending code signature to host.");
protocol::send_packet(HostBoundPacket::CodeSignature(CodeSignature::from(
let code_header = unsafe {
core::ptr::read(core::ptr::addr_of!(USER_MEMORY_START) as *const u32)
};
if code_header == u32::from_le_bytes(*b"\x7FELF") {
log::error!("Invalid user program! Hint: did you use the elf instead of the bin file?");
exit(102);
}
let code_signature = CodeSignature::try_from(
unsafe {
core::ptr::read(core::ptr::addr_of!(USER_MEMORY_START) as *const vex_sdk::vcodesig)
},
)))
.unwrap();

).unwrap_or_else(|()| {log::error!("Invalid user program!"); exit(102); });
// Send user code signature to host.
log::debug!("Sending code signature to host.");
protocol::send_packet(HostBoundPacket::CodeSignature(code_signature)).unwrap();
// Execute user program's entrypoint function.
//
// This is located 32 bytes after the code signature at 0x03800020.
log::debug!("Calling user code.");
log::debug!("Link address is {:#02x}. Calling user code.", vexSystemLinkAddrGet());
unsafe {
vexStartup();
}
Expand Down
8 changes: 6 additions & 2 deletions packages/kernel/src/sdk/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ use crate::{
},
};

extern "C" {
#[link_name = "_link_addr"]
static LINK_ADDR: u32;
}

pub extern "C" fn vexPrivateApiDisable(sig: u32) {}
pub extern "C" fn vexStdlibMismatchError(param_1: u32, param_2: u32) {}
pub extern "C" fn vexScratchMemoryPtr(ptr: *mut *mut core::ffi::c_void) -> i32 {
Expand Down Expand Up @@ -59,8 +64,7 @@ pub extern "C" fn vexSystemPowerupTimeGet() -> u64 {
vexSystemHighResTimeGet()
}
pub extern "C" fn vexSystemLinkAddrGet() -> u32 {
// no multi-file support yet
0x0
unsafe { LINK_ADDR }
}
pub extern "C" fn vexSystemTimerGet(param_1: u32) -> u32 {
Default::default()
Expand Down
30 changes: 18 additions & 12 deletions packages/protocol/src/code_signature.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use bincode::{Decode, Encode};
use bitflags::bitflags;
use core::result::Result;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use vex_sdk::vcodesig;
use vex_sdk::{vcodesig, V5_SIG_MAGIC};

use crate::impl_bincode_bitflags;

Expand All @@ -21,17 +22,22 @@ pub struct CodeSignature {
}

// TODO: This impl should probably be TryFrom and not have unreachables
impl From<vcodesig> for CodeSignature {
fn from(vcodesig: vcodesig) -> Self {
Self {
magic: vcodesig.magic,
program_type: ProgramType::User,
owner: match vcodesig.owner {
1 => ProgramOwner::Vex,
2 => ProgramOwner::Partner,
_ => ProgramOwner::System,
},
flags: ProgramFlags::from_bits_retain(vcodesig.options),
impl TryFrom<vcodesig> for CodeSignature {
type Error = ();
fn try_from(vcodesig: vcodesig) -> Result<Self, Self::Error> {
if vcodesig.magic != V5_SIG_MAGIC {
Err(())
} else {
Ok(Self {
magic: vcodesig.magic,
program_type: ProgramType::User,
owner: match vcodesig.owner {
1 => ProgramOwner::Vex,
2 => ProgramOwner::Partner,
_ => ProgramOwner::System,
},
flags: ProgramFlags::from_bits_retain(vcodesig.options),
})
}
}
}
Expand Down
Loading

0 comments on commit f9a57b3

Please sign in to comment.