[Teton] Move console/shell to rust. WIP
This commit is contained in:
parent
76f8795a46
commit
18e512cf1f
|
@ -5,6 +5,8 @@ compile_commands.json
|
||||||
|
|
||||||
sysroot/bin
|
sysroot/bin
|
||||||
sysroot/usr
|
sysroot/usr
|
||||||
|
sysroot/.crates.toml
|
||||||
|
sysroot/.crates2.json
|
||||||
|
|
||||||
rust/target
|
rust/target
|
||||||
yunq/venv
|
yunq/venv
|
||||||
|
|
|
@ -117,6 +117,15 @@ dependencies = [
|
||||||
"yunq",
|
"yunq",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "teton"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"mammoth",
|
||||||
|
"victoriafalls",
|
||||||
|
"yellowstone",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
|
@ -129,6 +138,17 @@ version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "victoriafalls"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"mammoth",
|
||||||
|
"yellowstone",
|
||||||
|
"yunq",
|
||||||
|
"yunq-derive",
|
||||||
|
"yunqc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yellowstone"
|
name = "yellowstone"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"lib/mammoth", "lib/yellowstone", "lib/yunq", "lib/yunq-derive", "usr/testbed",
|
"lib/mammoth", "lib/victoriafalls", "lib/yellowstone", "lib/yunq", "lib/yunq-derive", "sys/teton", "usr/testbed",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::syscall;
|
use crate::syscall;
|
||||||
|
use crate::zion::{z_cap_t, ZError};
|
||||||
|
use alloc::slice;
|
||||||
use linked_list_allocator::LockedHeap;
|
use linked_list_allocator::LockedHeap;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
|
@ -16,3 +18,57 @@ pub fn init_heap() {
|
||||||
CAN_ALLOC = true;
|
CAN_ALLOC = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct MemoryRegion {
|
||||||
|
mem_cap: z_cap_t,
|
||||||
|
virt_addr: u64,
|
||||||
|
size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryRegion {
|
||||||
|
pub fn direct_physical(paddr: u64, size: u64) -> Result<Self, ZError> {
|
||||||
|
let mem_cap = syscall::memory_object_direct_physical(paddr, size)?;
|
||||||
|
let virt_addr = syscall::address_space_map(mem_cap)?;
|
||||||
|
Ok(Self {
|
||||||
|
mem_cap,
|
||||||
|
virt_addr,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_cap(mem_cap: z_cap_t) -> Result<Self, ZError> {
|
||||||
|
let virt_addr = syscall::address_space_map(mem_cap)?;
|
||||||
|
let size = syscall::memory_object_inspect(mem_cap)?;
|
||||||
|
Ok(Self {
|
||||||
|
mem_cap,
|
||||||
|
virt_addr,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slice<T>(&self) -> &[T] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self.virt_addr as *const T,
|
||||||
|
self.size as usize / size_of::<T>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mut_slice<T>(&self) -> &mut [T] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self.virt_addr as *mut T,
|
||||||
|
self.size as usize / size_of::<T>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MemoryRegion {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
syscall::address_space_unmap(self.virt_addr, self.virt_addr + self.size)
|
||||||
|
.expect("Failed to unmap memory");
|
||||||
|
syscall::cap_release(self.mem_cap).expect("Failed to release memory cap");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -89,6 +89,31 @@ pub fn memory_object_create(size: u64) -> Result<z_cap_t, ZError> {
|
||||||
Ok(vmmo_cap)
|
Ok(vmmo_cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn memory_object_direct_physical(paddr: u64, size: u64) -> Result<z_cap_t, ZError> {
|
||||||
|
let mut vmmo_cap = 0;
|
||||||
|
syscall(
|
||||||
|
zion::kZionMemoryObjectCreatePhysical,
|
||||||
|
&zion::ZMemoryObjectCreatePhysicalReq {
|
||||||
|
paddr,
|
||||||
|
size,
|
||||||
|
vmmo_cap: &mut vmmo_cap,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(vmmo_cap)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_object_inspect(mem_cap: z_cap_t) -> Result<u64, ZError> {
|
||||||
|
let mut mem_size = 0;
|
||||||
|
syscall(
|
||||||
|
zion::kZionMemoryObjectInspect,
|
||||||
|
&zion::ZMemoryObjectInspectReq {
|
||||||
|
vmmo_cap: mem_cap,
|
||||||
|
size: &mut mem_size,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(mem_size)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn address_space_map(vmmo_cap: z_cap_t) -> Result<u64, ZError> {
|
pub fn address_space_map(vmmo_cap: z_cap_t) -> Result<u64, ZError> {
|
||||||
let mut vaddr: u64 = 0;
|
let mut vaddr: u64 = 0;
|
||||||
// FIXME: Allow caller to pass these options.
|
// FIXME: Allow caller to pass these options.
|
||||||
|
@ -104,6 +129,17 @@ pub fn address_space_map(vmmo_cap: z_cap_t) -> Result<u64, ZError> {
|
||||||
Ok(vaddr)
|
Ok(vaddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn address_space_unmap(lower_addr: u64, upper_addr: u64) -> Result<(), ZError> {
|
||||||
|
syscall(
|
||||||
|
zion::kZionAddressSpaceUnmap,
|
||||||
|
&zion::ZAddressSpaceUnmapReq {
|
||||||
|
vmas_cap: unsafe { crate::init::SELF_VMAS_CAP },
|
||||||
|
lower_addr,
|
||||||
|
upper_addr,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn port_poll(
|
pub fn port_poll(
|
||||||
port_cap: z_cap_t,
|
port_cap: z_cap_t,
|
||||||
bytes: &mut [u8],
|
bytes: &mut [u8],
|
||||||
|
@ -211,3 +247,7 @@ pub fn reply_port_recv(
|
||||||
|
|
||||||
Ok((num_bytes, num_caps))
|
Ok((num_bytes, num_caps))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cap_release(cap: z_cap_t) -> Result<(), ZError> {
|
||||||
|
syscall(zion::kZionCapRelease, &zion::ZCapReleaseReq { cap })
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "victoriafalls"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mammoth = { path = "../mammoth" }
|
||||||
|
yellowstone = { path = "../yellowstone" }
|
||||||
|
yunq = {path = "../yunq"}
|
||||||
|
yunq-derive = {path = "../yunq-derive"}
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
yunqc = {path = "../../../yunq/rust"}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input_file = "../../../sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq";
|
||||||
|
|
||||||
|
println!("cargo::rerun-if-changed={input_file}");
|
||||||
|
|
||||||
|
let input = fs::read_to_string(input_file).expect("Failed to read input file");
|
||||||
|
|
||||||
|
let code = yunqc::codegen(&input).expect("Failed to generate yunq code.");
|
||||||
|
|
||||||
|
let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs";
|
||||||
|
fs::write(out, code).expect("Failed to write generated code.");
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
use crate::OpenFileRequest;
|
||||||
|
use crate::VFSClient;
|
||||||
|
use alloc::string::ToString;
|
||||||
|
use mammoth::zion::ZError;
|
||||||
|
|
||||||
|
static mut VFS_CLIENT: Option<VFSClient> = None;
|
||||||
|
|
||||||
|
fn get_client() -> &'static mut VFSClient {
|
||||||
|
unsafe {
|
||||||
|
if let None = VFS_CLIENT {
|
||||||
|
let endpoint_cap = yellowstone::from_init_endpoint()
|
||||||
|
.get_endpoint(&yellowstone::GetEndpointRequest {
|
||||||
|
endpoint_name: "victoriafalls".to_string(),
|
||||||
|
})
|
||||||
|
.expect("Failed to get VFS endpoint");
|
||||||
|
|
||||||
|
VFS_CLIENT = Some(VFSClient::new(endpoint_cap.endpoint));
|
||||||
|
}
|
||||||
|
VFS_CLIENT.as_mut().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct File {
|
||||||
|
memory: mammoth::mem::MemoryRegion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn open(path: &str) -> Result<Self, ZError> {
|
||||||
|
let vfs = get_client();
|
||||||
|
let resp = vfs.open_file(&OpenFileRequest {
|
||||||
|
path: path.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
memory: mammoth::mem::MemoryRegion::from_cap(resp.memory)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slice(&self, offset: usize, len: usize) -> &[u8] {
|
||||||
|
&self.memory.slice()[offset..offset + len]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::include;
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/yunq.rs"));
|
||||||
|
|
||||||
|
pub mod file;
|
|
@ -3,3 +3,17 @@
|
||||||
use core::include;
|
use core::include;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/yunq.rs"));
|
include!(concat!(env!("OUT_DIR"), "/yunq.rs"));
|
||||||
|
|
||||||
|
use mammoth::init::INIT_ENDPOINT;
|
||||||
|
|
||||||
|
static mut YELLOWSTONE_INIT: Option<YellowstoneClient> = None;
|
||||||
|
|
||||||
|
pub fn from_init_endpoint() -> &'static mut YellowstoneClient {
|
||||||
|
unsafe {
|
||||||
|
if let None = YELLOWSTONE_INIT {
|
||||||
|
YELLOWSTONE_INIT = Some(YellowstoneClient::new(INIT_ENDPOINT));
|
||||||
|
}
|
||||||
|
|
||||||
|
YELLOWSTONE_INIT.as_mut().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ impl YunqMessage for Empty {
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
todo!()
|
Ok(Self {})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize<const N: usize>(
|
fn serialize<const N: usize>(
|
||||||
|
@ -88,6 +88,6 @@ impl YunqMessage for Empty {
|
||||||
_offset: usize,
|
_offset: usize,
|
||||||
_caps: &mut Vec<z_cap_t>,
|
_caps: &mut Vec<z_cap_t>,
|
||||||
) -> Result<usize, ZError> {
|
) -> Result<usize, ZError> {
|
||||||
todo!()
|
Ok(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "teton"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mammoth = { path = "../../lib/mammoth" }
|
||||||
|
victoriafalls = { path = "../../lib/victoriafalls" }
|
||||||
|
yellowstone = { path = "../../lib/yellowstone" }
|
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::framebuffer::Framebuffer;
|
||||||
|
use crate::psf::Psf;
|
||||||
|
|
||||||
|
pub struct Console {
|
||||||
|
framebuffer: Framebuffer,
|
||||||
|
psf: Psf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Console {
|
||||||
|
pub fn new(framebuffer: Framebuffer, psf: Psf) -> Self {
|
||||||
|
Self { framebuffer, psf }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_char(&self, c: char) {
|
||||||
|
let glyph = self.psf.glyph(c as u32);
|
||||||
|
self.framebuffer.draw_glyph(glyph, 0, 0)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
use mammoth::{mem::MemoryRegion, zion::ZError};
|
||||||
|
use yellowstone::FramebufferInfo;
|
||||||
|
|
||||||
|
pub struct Framebuffer {
|
||||||
|
fb_info: FramebufferInfo,
|
||||||
|
memory_region: MemoryRegion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Framebuffer {
|
||||||
|
pub fn from_info(fb_info: FramebufferInfo) -> Result<Self, ZError> {
|
||||||
|
let size = fb_info.height * fb_info.pitch;
|
||||||
|
let memory_region = MemoryRegion::direct_physical(fb_info.address_phys, size)?;
|
||||||
|
Ok(Self {
|
||||||
|
fb_info,
|
||||||
|
memory_region,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_pixel(&self, row: u32, col: u32, pixel: u32) {
|
||||||
|
let index = row * (self.fb_info.pitch as u32 / 4) + col;
|
||||||
|
self.memory_region.mut_slice()[index as usize] = pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_glyph(&self, glyph: &[u8], row: u32, col: u32) {
|
||||||
|
let gl_width = 8;
|
||||||
|
let gl_height = 16;
|
||||||
|
|
||||||
|
for r in 0..gl_height {
|
||||||
|
for c in 0..gl_width {
|
||||||
|
if ((glyph[r] >> c) % 2) == 1 {
|
||||||
|
self.draw_pixel(row + (r as u32), col + (gl_width - c - 1), 0xFFFFFFFF);
|
||||||
|
} else {
|
||||||
|
self.draw_pixel(row + (r as u32), col + (gl_width - c - 1), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
mod console;
|
||||||
|
mod framebuffer;
|
||||||
|
mod psf;
|
||||||
|
|
||||||
|
use mammoth::{debug, define_entry, zion::z_err_t};
|
||||||
|
|
||||||
|
define_entry!();
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn main() -> z_err_t {
|
||||||
|
debug!("Teton Starting");
|
||||||
|
|
||||||
|
let yellowstone = yellowstone::from_init_endpoint();
|
||||||
|
let framebuffer_info = yellowstone
|
||||||
|
.get_framebuffer_info()
|
||||||
|
.expect("Failed to get framebuffer info.");
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"FB addr {:#x}, bpp {}, width {} , height {}, pitch {}",
|
||||||
|
framebuffer_info.address_phys,
|
||||||
|
framebuffer_info.bpp,
|
||||||
|
framebuffer_info.width,
|
||||||
|
framebuffer_info.height,
|
||||||
|
framebuffer_info.pitch
|
||||||
|
);
|
||||||
|
|
||||||
|
let framebuffer = framebuffer::Framebuffer::from_info(framebuffer_info)
|
||||||
|
.expect("Failed to create framebuffer");
|
||||||
|
|
||||||
|
let psf = psf::Psf::new("/default8x16.psfu").expect("Failed to open font file.");
|
||||||
|
let console = console::Console::new(framebuffer, psf);
|
||||||
|
console.write_char('>');
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Terminal terminal(console);
|
||||||
|
terminal.Register();
|
||||||
|
|
||||||
|
Thread lthread = terminal.Listen();
|
||||||
|
|
||||||
|
check(lthread.Join());
|
||||||
|
*/
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
use mammoth::debug;
|
||||||
|
use mammoth::zion::ZError;
|
||||||
|
use victoriafalls::file::File;
|
||||||
|
|
||||||
|
const MAGIC_HEADER: u32 = 0x864AB572;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct PsfHeader {
|
||||||
|
magic: u32, /* magic bytes to identify PSF */
|
||||||
|
version: u32, /* zero */
|
||||||
|
headersize: u32, /* offset of bitmaps in file, 32 */
|
||||||
|
flags: u32, /* 0 if there's no unicode table */
|
||||||
|
numglyph: u32, /* number of glyphs */
|
||||||
|
bytes_per_glyph: u32, /* size of each glyph */
|
||||||
|
height: u32, /* height in pixels */
|
||||||
|
width: u32, /* width in pixels */
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Psf {
|
||||||
|
file: File,
|
||||||
|
header: &'static PsfHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Psf {
|
||||||
|
pub fn new(path: &str) -> Result<Self, ZError> {
|
||||||
|
let file = File::open(&path)?;
|
||||||
|
|
||||||
|
let header = file
|
||||||
|
.slice(0, core::mem::size_of::<PsfHeader>())
|
||||||
|
.as_ptr()
|
||||||
|
.cast();
|
||||||
|
let psf = Self {
|
||||||
|
file,
|
||||||
|
header: unsafe { &*header },
|
||||||
|
};
|
||||||
|
|
||||||
|
psf.validate()?;
|
||||||
|
|
||||||
|
Ok(psf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(&self) -> Result<(), ZError> {
|
||||||
|
if self.header.magic != MAGIC_HEADER {
|
||||||
|
debug!("PSF: Magic value: {:x}", self.header.magic);
|
||||||
|
return Err(ZError::INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.header.version != 0 {
|
||||||
|
debug!("PSF non-zero version");
|
||||||
|
return Err(ZError::INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.header.height != 0x10 {
|
||||||
|
debug!("PSF height other than 16 not handled");
|
||||||
|
return Err(ZError::UNIMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.header.width != 0x8 {
|
||||||
|
debug!("PSF width other than 8 not handled");
|
||||||
|
return Err(ZError::UNIMPLEMENTED);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn glyph(&self, index: u32) -> &[u8] {
|
||||||
|
let offset: usize =
|
||||||
|
(self.header.headersize + (index * self.header.bytes_per_glyph)) as usize;
|
||||||
|
let len: usize = self.header.bytes_per_glyph as usize;
|
||||||
|
&self.file.slice(offset, len)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,11 +12,19 @@ pushd $BUILD_DIR
|
||||||
ninja
|
ninja
|
||||||
ninja install
|
ninja install
|
||||||
|
|
||||||
export CARGO_INSTALL_ROOT="${DIR}/../sysroot/usr/"
|
CARGO_USR_ROOT="${DIR}/../sysroot/usr/"
|
||||||
|
CARGO_SYS_ROOT="${DIR}/../sysroot/"
|
||||||
|
|
||||||
# Need to pushd so rustup gets the toolchain from rust/rust_toolchain.toml
|
# Need to pushd so rustup gets the toolchain from rust/rust_toolchain.toml
|
||||||
pushd "${DIR}/../rust"
|
pushd "${DIR}/../rust"
|
||||||
cargo install --force --path "${DIR}/../rust/usr/testbed/"
|
|
||||||
|
for BIN in ${DIR}/../rust/usr/*/; do
|
||||||
|
cargo install --force --path "${BIN}" --root $CARGO_USR_ROOT
|
||||||
|
done
|
||||||
|
|
||||||
|
for BIN in ${DIR}/../rust/sys/*/; do
|
||||||
|
cargo install --force --path "${BIN}" --root $CARGO_SYS_ROOT
|
||||||
|
done
|
||||||
popd
|
popd
|
||||||
|
|
||||||
sudo sh ${DIR}/build_image.sh disk.img
|
sudo sh ${DIR}/build_image.sh disk.img
|
||||||
|
|
Loading…
Reference in New Issue