refactor: extract ports/drivers/bins layers — clean dependency inversion
Separate abstractions from implementations following DIP:
servers/ drivers/
│ │
▼ ▼
ports/ (trait-only crates, zero deps)
New crates:
ports/ (abstractions):
- port-framebuffer: Framebuffer trait
- port-block-device: BlockDevice trait + SectorId
- port-input-device: InputDevice trait + InputEvent types
drivers/ (implementations depend on ports/):
- driver-framebuffer-virtio: VirtIO GPU framebuffer
- driver-framebuffer-mock: test double
- driver-block-virtio: VirtIO block device
bins/ (composition roots wire server + driver):
- display-qemu: DisplayServer<VirtioFramebuffer>
- storage-qemu: storage server with VirtIO block
Servers no longer own their hardware traits or driver code.
Neither server nor driver depends on the other — both point
inward to the port abstraction.
This commit is contained in:
60
Cargo.lock
generated
60
Cargo.lock
generated
@@ -49,14 +49,25 @@ version = "0.1.0"
|
|||||||
name = "contacts-server"
|
name = "contacts-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "display-qemu"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"display-server",
|
||||||
|
"driver-framebuffer-virtio",
|
||||||
|
"server-framework",
|
||||||
|
"userspace",
|
||||||
|
"userspace-rt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "display-server"
|
name = "display-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"driver-framebuffer-mock",
|
||||||
|
"port-framebuffer",
|
||||||
"server-framework",
|
"server-framework",
|
||||||
"types",
|
"types",
|
||||||
"userspace",
|
|
||||||
"userspace-rt",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -66,6 +77,27 @@ dependencies = [
|
|||||||
"types",
|
"types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "driver-block-virtio"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"port-block-device",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "driver-framebuffer-mock"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"port-framebuffer",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "driver-framebuffer-virtio"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"port-framebuffer",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "echo"
|
name = "echo"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -107,6 +139,7 @@ dependencies = [
|
|||||||
name = "input-server"
|
name = "input-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"port-input-device",
|
||||||
"server-framework",
|
"server-framework",
|
||||||
"types",
|
"types",
|
||||||
"userspace",
|
"userspace",
|
||||||
@@ -170,6 +203,18 @@ dependencies = [
|
|||||||
"types",
|
"types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "port-block-device"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "port-framebuffer"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "port-input-device"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "power-server"
|
name = "power-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -205,14 +250,23 @@ dependencies = [
|
|||||||
"userspace-rt",
|
"userspace-rt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "storage-qemu"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"storage-server",
|
||||||
|
"userspace-rt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "storage-server"
|
name = "storage-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"driver-block-virtio",
|
||||||
|
"port-block-device",
|
||||||
"server-framework",
|
"server-framework",
|
||||||
"types",
|
"types",
|
||||||
"userspace",
|
"userspace",
|
||||||
"userspace-rt",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
18
Cargo.toml
18
Cargo.toml
@@ -41,6 +41,14 @@ members = [
|
|||||||
"crates/types",
|
"crates/types",
|
||||||
"crates/userspace",
|
"crates/userspace",
|
||||||
"crates/userspace-rt",
|
"crates/userspace-rt",
|
||||||
|
"crates/ports/framebuffer",
|
||||||
|
"crates/ports/block-device",
|
||||||
|
"crates/ports/input-device",
|
||||||
|
"crates/drivers/framebuffer/virtio",
|
||||||
|
"crates/drivers/framebuffer/mock",
|
||||||
|
"crates/drivers/block/virtio",
|
||||||
|
"crates/bins/display-qemu",
|
||||||
|
"crates/bins/storage-qemu",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
@@ -84,6 +92,16 @@ telephony-server = { path = "crates/servers/telephony-server" }
|
|||||||
usb-server = { path = "crates/servers/usb-server" }
|
usb-server = { path = "crates/servers/usb-server" }
|
||||||
user-manager = { path = "crates/servers/user-manager" }
|
user-manager = { path = "crates/servers/user-manager" }
|
||||||
|
|
||||||
|
# ports
|
||||||
|
port-framebuffer = { path = "crates/ports/framebuffer" }
|
||||||
|
port-block-device = { path = "crates/ports/block-device" }
|
||||||
|
port-input-device = { path = "crates/ports/input-device" }
|
||||||
|
|
||||||
|
# drivers
|
||||||
|
driver-framebuffer-virtio = { path = "crates/drivers/framebuffer/virtio" }
|
||||||
|
driver-framebuffer-mock = { path = "crates/drivers/framebuffer/mock" }
|
||||||
|
driver-block-virtio = { path = "crates/drivers/block/virtio" }
|
||||||
|
|
||||||
# apps
|
# apps
|
||||||
snake = { path = "crates/apps/snake" }
|
snake = { path = "crates/apps/snake" }
|
||||||
|
|
||||||
|
|||||||
5
Makefile
5
Makefile
@@ -29,7 +29,10 @@ kani-kernel:
|
|||||||
kani: kani-types kani-domain kani-kernel
|
kani: kani-types kani-domain kani-kernel
|
||||||
|
|
||||||
build:
|
build:
|
||||||
cargo build -p adapter-riscv --target $(RISCV_TARGET) --release
|
cargo build --target $(RISCV_TARGET) --release \
|
||||||
|
-p adapter-riscv -p init -p echo -p snake \
|
||||||
|
-p display-qemu -p storage-qemu \
|
||||||
|
-p input-server -p log-server -p permission-manager -p session-server
|
||||||
|
|
||||||
image: build
|
image: build
|
||||||
scripts/make-disk-image.sh
|
scripts/make-disk-image.sh
|
||||||
|
|||||||
16
crates/bins/display-qemu/Cargo.toml
Normal file
16
crates/bins/display-qemu/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "display-qemu"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "display-qemu"
|
||||||
|
test = false
|
||||||
|
bench = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
userspace-rt = { workspace = true }
|
||||||
|
userspace = { workspace = true }
|
||||||
|
server-framework = { workspace = true }
|
||||||
|
display-server = { workspace = true }
|
||||||
|
driver-framebuffer-virtio = { workspace = true }
|
||||||
14
crates/bins/display-qemu/src/main.rs
Normal file
14
crates/bins/display-qemu/src/main.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate userspace_rt;
|
||||||
|
|
||||||
|
use driver_framebuffer_virtio::VirtioFramebuffer;
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "C" fn main() {
|
||||||
|
let fb = VirtioFramebuffer::new();
|
||||||
|
let mut srv = display_server::server::DisplayServer::new(fb);
|
||||||
|
server_framework::serve(&mut srv, 0);
|
||||||
|
loop { userspace::syscall::yield_now(); }
|
||||||
|
}
|
||||||
13
crates/bins/storage-qemu/Cargo.toml
Normal file
13
crates/bins/storage-qemu/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "storage-qemu"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "storage-qemu"
|
||||||
|
test = false
|
||||||
|
bench = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
userspace-rt = { workspace = true }
|
||||||
|
storage-server = { workspace = true }
|
||||||
7
crates/drivers/block/virtio/Cargo.toml
Normal file
7
crates/drivers/block/virtio/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "driver-block-virtio"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
port-block-device = { workspace = true }
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::block_device::{BlockDevice, SectorId};
|
#![no_std]
|
||||||
|
|
||||||
|
use port_block_device::{BlockDevice, SectorId};
|
||||||
|
|
||||||
const QUEUE_SIZE: usize = 8;
|
const QUEUE_SIZE: usize = 8;
|
||||||
const SECTOR_SIZE: usize = 512;
|
const SECTOR_SIZE: usize = 512;
|
||||||
@@ -6,9 +8,6 @@ const SECTOR_SIZE: usize = 512;
|
|||||||
const MMIO_BASE: usize = 0x20000;
|
const MMIO_BASE: usize = 0x20000;
|
||||||
const VQUEUE_VA: usize = 0x21000;
|
const VQUEUE_VA: usize = 0x21000;
|
||||||
const REQ_HDR_VA: usize = 0x23000;
|
const REQ_HDR_VA: usize = 0x23000;
|
||||||
|
|
||||||
// Physical addresses are written to a known location by the adapter at boot
|
|
||||||
// Layout at 0x24000: [req_hdr_pa: u64, status_pa: u64, sector_buf_pa: u64]
|
|
||||||
const PHYS_INFO_VA: usize = 0x24000;
|
const PHYS_INFO_VA: usize = 0x24000;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -49,33 +48,15 @@ struct VirtioBlkReqHeader {
|
|||||||
sector: u64,
|
sector: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mmio_read(offset: usize) -> u32 {
|
|
||||||
unsafe { core::ptr::read_volatile((MMIO_BASE + offset) as *const u32) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mmio_write(offset: usize, val: u32) {
|
fn mmio_write(offset: usize, val: u32) {
|
||||||
unsafe { core::ptr::write_volatile((MMIO_BASE + offset) as *mut u32, val) }
|
unsafe { core::ptr::write_volatile((MMIO_BASE + offset) as *mut u32, val) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn descs() -> *mut VirtqDesc {
|
fn descs() -> *mut VirtqDesc { VQUEUE_VA as *mut VirtqDesc }
|
||||||
VQUEUE_VA as *mut VirtqDesc
|
fn avail() -> *mut VirtqAvail { (VQUEUE_VA + QUEUE_SIZE * 16) as *mut VirtqAvail }
|
||||||
}
|
fn used() -> *mut VirtqUsed { (VQUEUE_VA + 0x1000) as *mut VirtqUsed }
|
||||||
|
fn req_header() -> *mut VirtioBlkReqHeader { REQ_HDR_VA as *mut VirtioBlkReqHeader }
|
||||||
fn avail() -> *mut VirtqAvail {
|
fn req_status() -> *mut u8 { (REQ_HDR_VA + 16) as *mut u8 }
|
||||||
(VQUEUE_VA + QUEUE_SIZE * 16) as *mut VirtqAvail
|
|
||||||
}
|
|
||||||
|
|
||||||
fn used() -> *mut VirtqUsed {
|
|
||||||
(VQUEUE_VA + 0x1000) as *mut VirtqUsed
|
|
||||||
}
|
|
||||||
|
|
||||||
fn req_header() -> *mut VirtioBlkReqHeader {
|
|
||||||
REQ_HDR_VA as *mut VirtioBlkReqHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
fn req_status() -> *mut u8 {
|
|
||||||
(REQ_HDR_VA + 16) as *mut u8
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PhysAddrs {
|
struct PhysAddrs {
|
||||||
req_hdr: u64,
|
req_hdr: u64,
|
||||||
@@ -105,18 +86,12 @@ impl VirtioBlockDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlockDevice for VirtioBlockDevice {
|
impl BlockDevice for VirtioBlockDevice {
|
||||||
fn sector_size(&self) -> usize {
|
fn sector_size(&self) -> usize { SECTOR_SIZE }
|
||||||
SECTOR_SIZE
|
fn sector_count(&self) -> usize { 0xFFFF }
|
||||||
}
|
|
||||||
|
|
||||||
fn sector_count(&self) -> usize {
|
|
||||||
0xFFFF
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_sector(&mut self, sector: SectorId, buf: &mut [u8]) -> bool {
|
fn read_sector(&mut self, sector: SectorId, buf: &mut [u8]) -> bool {
|
||||||
if buf.len() < SECTOR_SIZE { return false; }
|
if buf.len() < SECTOR_SIZE { return false; }
|
||||||
|
|
||||||
// Write to the local VA — device reads via physical address
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let hdr = req_header();
|
let hdr = req_header();
|
||||||
(*hdr).req_type = 0;
|
(*hdr).req_type = 0;
|
||||||
@@ -125,28 +100,12 @@ impl BlockDevice for VirtioBlockDevice {
|
|||||||
|
|
||||||
core::ptr::write_volatile(req_status(), 0xFF);
|
core::ptr::write_volatile(req_status(), 0xFF);
|
||||||
|
|
||||||
// Sector buffer: use a fixed area at REQ_HDR_VA + 32
|
|
||||||
let sector_buf_va = (REQ_HDR_VA + 32) as *mut u8;
|
let sector_buf_va = (REQ_HDR_VA + 32) as *mut u8;
|
||||||
|
|
||||||
let d = descs();
|
let d = descs();
|
||||||
(*d.add(0)) = VirtqDesc {
|
(*d.add(0)) = VirtqDesc { addr: self.phys.req_hdr, len: 16, flags: 1, next: 1 };
|
||||||
addr: self.phys.req_hdr,
|
(*d.add(1)) = VirtqDesc { addr: self.phys.sector_buf, len: SECTOR_SIZE as u32, flags: 2 | 1, next: 2 };
|
||||||
len: 16,
|
(*d.add(2)) = VirtqDesc { addr: self.phys.status, len: 1, flags: 2, next: 0 };
|
||||||
flags: 1,
|
|
||||||
next: 1,
|
|
||||||
};
|
|
||||||
(*d.add(1)) = VirtqDesc {
|
|
||||||
addr: self.phys.sector_buf,
|
|
||||||
len: SECTOR_SIZE as u32,
|
|
||||||
flags: 2 | 1,
|
|
||||||
next: 2,
|
|
||||||
};
|
|
||||||
(*d.add(2)) = VirtqDesc {
|
|
||||||
addr: self.phys.status,
|
|
||||||
len: 1,
|
|
||||||
flags: 2,
|
|
||||||
next: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let a = avail();
|
let a = avail();
|
||||||
let avail_idx = core::ptr::read_volatile(&(*a).idx);
|
let avail_idx = core::ptr::read_volatile(&(*a).idx);
|
||||||
@@ -166,11 +125,8 @@ impl BlockDevice for VirtioBlockDevice {
|
|||||||
if timeout == 0 { return false; }
|
if timeout == 0 { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if core::ptr::read_volatile(req_status()) != 0 {
|
if core::ptr::read_volatile(req_status()) != 0 { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy from sector buffer VA to caller's buffer
|
|
||||||
core::ptr::copy_nonoverlapping(sector_buf_va, buf.as_mut_ptr(), SECTOR_SIZE);
|
core::ptr::copy_nonoverlapping(sector_buf_va, buf.as_mut_ptr(), SECTOR_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
7
crates/drivers/framebuffer/mock/Cargo.toml
Normal file
7
crates/drivers/framebuffer/mock/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "driver-framebuffer-mock"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
port-framebuffer = { workspace = true }
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::framebuffer::Framebuffer;
|
#![no_std]
|
||||||
|
|
||||||
|
use port_framebuffer::Framebuffer;
|
||||||
|
|
||||||
pub struct MockFramebuffer {
|
pub struct MockFramebuffer {
|
||||||
width: u16,
|
width: u16,
|
||||||
@@ -33,13 +35,8 @@ impl MockFramebuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Framebuffer for MockFramebuffer {
|
impl Framebuffer for MockFramebuffer {
|
||||||
fn width(&self) -> u16 {
|
fn width(&self) -> u16 { self.width }
|
||||||
self.width
|
fn height(&self) -> u16 { self.height }
|
||||||
}
|
|
||||||
|
|
||||||
fn height(&self) -> u16 {
|
|
||||||
self.height
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_pixel(&mut self, x: u16, y: u16, color: u32) {
|
fn write_pixel(&mut self, x: u16, y: u16, color: u32) {
|
||||||
if self.pixel_count < self.pixels.len() {
|
if self.pixel_count < self.pixels.len() {
|
||||||
7
crates/drivers/framebuffer/virtio/Cargo.toml
Normal file
7
crates/drivers/framebuffer/virtio/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "driver-framebuffer-virtio"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
port-framebuffer = { workspace = true }
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::framebuffer::Framebuffer;
|
#![no_std]
|
||||||
|
|
||||||
|
use port_framebuffer::Framebuffer;
|
||||||
|
|
||||||
const GPU_MMIO_VA: usize = 0x20000;
|
const GPU_MMIO_VA: usize = 0x20000;
|
||||||
const VQUEUE_VA: usize = 0x21000;
|
const VQUEUE_VA: usize = 0x21000;
|
||||||
@@ -11,7 +13,6 @@ const QUEUE_SIZE: usize = 8;
|
|||||||
struct PhysInfo {
|
struct PhysInfo {
|
||||||
cmd_pa: u64,
|
cmd_pa: u64,
|
||||||
resp_pa: u64,
|
resp_pa: u64,
|
||||||
fb_pa: u64,
|
|
||||||
width: u64,
|
width: u64,
|
||||||
height: u64,
|
height: u64,
|
||||||
}
|
}
|
||||||
@@ -22,7 +23,6 @@ fn read_phys_info() -> PhysInfo {
|
|||||||
PhysInfo {
|
PhysInfo {
|
||||||
cmd_pa: core::ptr::read_volatile(p),
|
cmd_pa: core::ptr::read_volatile(p),
|
||||||
resp_pa: core::ptr::read_volatile(p.add(1)),
|
resp_pa: core::ptr::read_volatile(p.add(1)),
|
||||||
fb_pa: core::ptr::read_volatile(p.add(2)),
|
|
||||||
width: core::ptr::read_volatile(p.add(3)),
|
width: core::ptr::read_volatile(p.add(3)),
|
||||||
height: core::ptr::read_volatile(p.add(4)),
|
height: core::ptr::read_volatile(p.add(4)),
|
||||||
}
|
}
|
||||||
@@ -41,12 +41,11 @@ pub struct VirtioFramebuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VirtioFramebuffer {
|
impl VirtioFramebuffer {
|
||||||
pub fn new(_buffer_addr: usize) -> Self {
|
pub fn new() -> Self {
|
||||||
let phys = read_phys_info();
|
let phys = read_phys_info();
|
||||||
let width = phys.width as u16;
|
let width = phys.width as u16;
|
||||||
let height = phys.height as u16;
|
let height = phys.height as u16;
|
||||||
let stride = width as usize * FB_BPP;
|
let stride = width as usize * FB_BPP;
|
||||||
|
|
||||||
Self { width, height, stride, phys }
|
Self { width, height, stride, phys }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,13 +54,13 @@ impl VirtioFramebuffer {
|
|||||||
let desc0 = VQUEUE_VA as *mut u8;
|
let desc0 = VQUEUE_VA as *mut u8;
|
||||||
core::ptr::write_volatile(desc0 as *mut u64, self.phys.cmd_pa);
|
core::ptr::write_volatile(desc0 as *mut u64, self.phys.cmd_pa);
|
||||||
core::ptr::write_volatile(desc0.add(8) as *mut u32, cmd_len);
|
core::ptr::write_volatile(desc0.add(8) as *mut u32, cmd_len);
|
||||||
core::ptr::write_volatile(desc0.add(12) as *mut u16, 1); // NEXT
|
core::ptr::write_volatile(desc0.add(12) as *mut u16, 1);
|
||||||
core::ptr::write_volatile(desc0.add(14) as *mut u16, 1);
|
core::ptr::write_volatile(desc0.add(14) as *mut u16, 1);
|
||||||
|
|
||||||
let desc1 = desc0.add(16);
|
let desc1 = desc0.add(16);
|
||||||
core::ptr::write_volatile(desc1 as *mut u64, self.phys.resp_pa);
|
core::ptr::write_volatile(desc1 as *mut u64, self.phys.resp_pa);
|
||||||
core::ptr::write_volatile(desc1.add(8) as *mut u32, resp_len);
|
core::ptr::write_volatile(desc1.add(8) as *mut u32, resp_len);
|
||||||
core::ptr::write_volatile(desc1.add(12) as *mut u16, 2); // WRITE
|
core::ptr::write_volatile(desc1.add(12) as *mut u16, 2);
|
||||||
core::ptr::write_volatile(desc1.add(14) as *mut u16, 0);
|
core::ptr::write_volatile(desc1.add(14) as *mut u16, 0);
|
||||||
|
|
||||||
let avail = VQUEUE_VA + QUEUE_SIZE * 16;
|
let avail = VQUEUE_VA + QUEUE_SIZE * 16;
|
||||||
@@ -88,54 +87,41 @@ impl VirtioFramebuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transfer_and_flush(&self, x: u32, y: u32, w: u32, h: u32) {
|
fn transfer_and_flush(&self, x: u32, y: u32, w: u32, h: u32) {
|
||||||
// TRANSFER_TO_HOST_2D
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = CMDBUF_VA as *mut u8;
|
let p = CMDBUF_VA as *mut u8;
|
||||||
core::ptr::write_bytes(p, 0, 512);
|
core::ptr::write_bytes(p, 0, 512);
|
||||||
core::ptr::write_volatile(p as *mut u32, 0x0105); // TRANSFER_TO_HOST_2D
|
core::ptr::write_volatile(p as *mut u32, 0x0105);
|
||||||
// rect at offset 24
|
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, x);
|
core::ptr::write_volatile(p.add(24) as *mut u32, x);
|
||||||
core::ptr::write_volatile(p.add(28) as *mut u32, y);
|
core::ptr::write_volatile(p.add(28) as *mut u32, y);
|
||||||
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
||||||
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
||||||
// offset at 40
|
|
||||||
core::ptr::write_volatile(p.add(40) as *mut u64, 0);
|
core::ptr::write_volatile(p.add(40) as *mut u64, 0);
|
||||||
// resource_id at 48
|
|
||||||
core::ptr::write_volatile(p.add(48) as *mut u32, 1);
|
core::ptr::write_volatile(p.add(48) as *mut u32, 1);
|
||||||
}
|
}
|
||||||
self.send_gpu_cmd(56, 24);
|
self.send_gpu_cmd(56, 24);
|
||||||
|
|
||||||
// RESOURCE_FLUSH
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = CMDBUF_VA as *mut u8;
|
let p = CMDBUF_VA as *mut u8;
|
||||||
core::ptr::write_bytes(p, 0, 512);
|
core::ptr::write_bytes(p, 0, 512);
|
||||||
core::ptr::write_volatile(p as *mut u32, 0x0106); // RESOURCE_FLUSH
|
core::ptr::write_volatile(p as *mut u32, 0x0106);
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, x);
|
core::ptr::write_volatile(p.add(24) as *mut u32, x);
|
||||||
core::ptr::write_volatile(p.add(28) as *mut u32, y);
|
core::ptr::write_volatile(p.add(28) as *mut u32, y);
|
||||||
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
||||||
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
||||||
core::ptr::write_volatile(p.add(40) as *mut u32, 1); // resource_id
|
core::ptr::write_volatile(p.add(40) as *mut u32, 1);
|
||||||
}
|
}
|
||||||
self.send_gpu_cmd(48, 24);
|
self.send_gpu_cmd(48, 24);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Framebuffer for VirtioFramebuffer {
|
impl Framebuffer for VirtioFramebuffer {
|
||||||
fn width(&self) -> u16 {
|
fn width(&self) -> u16 { self.width }
|
||||||
self.width
|
fn height(&self) -> u16 { self.height }
|
||||||
}
|
|
||||||
|
|
||||||
fn height(&self) -> u16 {
|
|
||||||
self.height
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_pixel(&mut self, x: u16, y: u16, color: u32) {
|
fn write_pixel(&mut self, x: u16, y: u16, color: u32) {
|
||||||
if x >= self.width || y >= self.height { return; }
|
if x >= self.width || y >= self.height { return; }
|
||||||
let offset = (y as usize) * self.stride + (x as usize) * FB_BPP;
|
let offset = (y as usize) * self.stride + (x as usize) * FB_BPP;
|
||||||
let buf = FB_VA + offset;
|
unsafe { core::ptr::write_volatile((FB_VA + offset) as *mut u32, color); }
|
||||||
unsafe {
|
|
||||||
core::ptr::write_volatile(buf as *mut u32, color);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: u32) {
|
fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: u32) {
|
||||||
6
crates/ports/block-device/Cargo.toml
Normal file
6
crates/ports/block-device/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "port-block-device"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct SectorId(u32);
|
pub struct SectorId(u32);
|
||||||
|
|
||||||
6
crates/ports/framebuffer/Cargo.toml
Normal file
6
crates/ports/framebuffer/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "port-framebuffer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
pub trait Framebuffer {
|
pub trait Framebuffer {
|
||||||
fn width(&self) -> u16;
|
fn width(&self) -> u16;
|
||||||
fn height(&self) -> u16;
|
fn height(&self) -> u16;
|
||||||
6
crates/ports/input-device/Cargo.toml
Normal file
6
crates/ports/input-device/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "port-input-device"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
54
crates/ports/input-device/src/lib.rs
Normal file
54
crates/ports/input-device/src/lib.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Key {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Enter,
|
||||||
|
Escape,
|
||||||
|
Space,
|
||||||
|
Backspace,
|
||||||
|
Tab,
|
||||||
|
Char(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum GamepadButton {
|
||||||
|
South,
|
||||||
|
East,
|
||||||
|
North,
|
||||||
|
West,
|
||||||
|
DpadUp,
|
||||||
|
DpadDown,
|
||||||
|
DpadLeft,
|
||||||
|
DpadRight,
|
||||||
|
Start,
|
||||||
|
Select,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Axis {
|
||||||
|
LeftX,
|
||||||
|
LeftY,
|
||||||
|
RightX,
|
||||||
|
RightY,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum InputEvent {
|
||||||
|
KeyDown(Key),
|
||||||
|
KeyUp(Key),
|
||||||
|
PointerDown { id: u8, x: u16, y: u16 },
|
||||||
|
PointerMove { id: u8, x: u16, y: u16 },
|
||||||
|
PointerUp { id: u8, x: u16, y: u16 },
|
||||||
|
Scroll { dx: i16, dy: i16 },
|
||||||
|
GamepadButton { button: GamepadButton, pressed: bool },
|
||||||
|
GamepadAxis { axis: Axis, value: i16 },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InputDevice {
|
||||||
|
fn poll(&mut self) -> Option<InputEvent>;
|
||||||
|
fn has_event(&self) -> bool;
|
||||||
|
}
|
||||||
@@ -3,17 +3,10 @@ name = "display-server"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "display-server"
|
|
||||||
test = false
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["qemu-virt"]
|
|
||||||
qemu-virt = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
types = { workspace = true }
|
types = { workspace = true }
|
||||||
userspace = { workspace = true }
|
|
||||||
userspace-rt = { workspace = true }
|
|
||||||
server-framework = { workspace = true }
|
server-framework = { workspace = true }
|
||||||
|
port-framebuffer = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
driver-framebuffer-mock = { workspace = true }
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
pub mod mock;
|
|
||||||
pub mod qemu_virtio;
|
|
||||||
@@ -1,22 +1,8 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
pub mod adapters;
|
|
||||||
pub mod framebuffer;
|
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "tests/mod.rs"]
|
#[path = "tests/mod.rs"]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub fn run() -> ! {
|
|
||||||
#[cfg(feature = "qemu-virt")]
|
|
||||||
let fb = adapters::qemu_virtio::VirtioFramebuffer::new(0x10008000);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "qemu-virt"))]
|
|
||||||
let fb = adapters::mock::MockFramebuffer::new(320, 240);
|
|
||||||
|
|
||||||
let mut srv = server::DisplayServer::new(fb);
|
|
||||||
server_framework::serve(&mut srv, 0);
|
|
||||||
loop { userspace::syscall::yield_now(); }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
|
|
||||||
extern crate userspace_rt;
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn main() {
|
|
||||||
display_server::run();
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use server_framework::Server;
|
use server_framework::Server;
|
||||||
|
|
||||||
use crate::framebuffer::Framebuffer;
|
use port_framebuffer::Framebuffer;
|
||||||
use crate::protocol::{DisplayRequest, DisplayResponse};
|
use crate::protocol::{DisplayRequest, DisplayResponse};
|
||||||
|
|
||||||
pub struct DisplayServer<F: Framebuffer> {
|
pub struct DisplayServer<F: Framebuffer> {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::adapters::mock::MockFramebuffer;
|
use driver_framebuffer_mock::MockFramebuffer;
|
||||||
use crate::protocol::{DisplayRequest, DisplayResponse};
|
use crate::protocol::{DisplayRequest, DisplayResponse};
|
||||||
use crate::server::DisplayServer;
|
use crate::server::DisplayServer;
|
||||||
use server_framework::Server;
|
use server_framework::Server;
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ types = { workspace = true }
|
|||||||
userspace = { workspace = true }
|
userspace = { workspace = true }
|
||||||
userspace-rt = { workspace = true }
|
userspace-rt = { workspace = true }
|
||||||
server-framework = { workspace = true }
|
server-framework = { workspace = true }
|
||||||
|
port-input-device = { workspace = true }
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::input_device::InputDevice;
|
use crate::input_device::{InputDevice, InputEvent};
|
||||||
use crate::protocol::InputEvent;
|
|
||||||
|
|
||||||
pub struct MockInputDevice {
|
pub struct MockInputDevice {
|
||||||
events: [Option<InputEvent>; 16],
|
events: [Option<InputEvent>; 16],
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::input_device::InputDevice;
|
use crate::input_device::{InputDevice, InputEvent, Key};
|
||||||
use crate::protocol::{InputEvent, Key};
|
|
||||||
|
|
||||||
const UART_BASE: usize = 0x10000000;
|
const UART_BASE: usize = 0x10000000;
|
||||||
const UART_LSR: usize = 5;
|
const UART_LSR: usize = 5;
|
||||||
|
|||||||
@@ -1,6 +1 @@
|
|||||||
use crate::protocol::InputEvent;
|
pub use port_input_device::{InputDevice, InputEvent, Key, GamepadButton, Axis};
|
||||||
|
|
||||||
pub trait InputDevice {
|
|
||||||
fn poll(&mut self) -> Option<InputEvent>;
|
|
||||||
fn has_event(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,55 +1,5 @@
|
|||||||
use server_framework::{Decode, Encode};
|
use server_framework::{Decode, Encode};
|
||||||
|
pub use port_input_device::{Key, GamepadButton, Axis, InputEvent};
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Key {
|
|
||||||
Up,
|
|
||||||
Down,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Enter,
|
|
||||||
Escape,
|
|
||||||
Space,
|
|
||||||
Backspace,
|
|
||||||
Tab,
|
|
||||||
Char(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum GamepadButton {
|
|
||||||
South,
|
|
||||||
East,
|
|
||||||
North,
|
|
||||||
West,
|
|
||||||
DpadUp,
|
|
||||||
DpadDown,
|
|
||||||
DpadLeft,
|
|
||||||
DpadRight,
|
|
||||||
Start,
|
|
||||||
Select,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Axis {
|
|
||||||
LeftX,
|
|
||||||
LeftY,
|
|
||||||
RightX,
|
|
||||||
RightY,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum InputEvent {
|
|
||||||
KeyDown(Key),
|
|
||||||
KeyUp(Key),
|
|
||||||
|
|
||||||
PointerDown { id: u8, x: u16, y: u16 },
|
|
||||||
PointerMove { id: u8, x: u16, y: u16 },
|
|
||||||
PointerUp { id: u8, x: u16, y: u16 },
|
|
||||||
|
|
||||||
Scroll { dx: i16, dy: i16 },
|
|
||||||
|
|
||||||
GamepadButton { button: GamepadButton, pressed: bool },
|
|
||||||
GamepadAxis { axis: Axis, value: i16 },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum InputRequest {
|
pub enum InputRequest {
|
||||||
@@ -82,7 +32,6 @@ impl Decode for InputRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event type tags for encoding
|
|
||||||
const EV_KEY_DOWN: usize = 1;
|
const EV_KEY_DOWN: usize = 1;
|
||||||
const EV_KEY_UP: usize = 2;
|
const EV_KEY_UP: usize = 2;
|
||||||
const EV_PTR_DOWN: usize = 3;
|
const EV_PTR_DOWN: usize = 3;
|
||||||
|
|||||||
@@ -3,17 +3,9 @@ name = "storage-server"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "storage-server"
|
|
||||||
test = false
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["qemu-virt"]
|
|
||||||
qemu-virt = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
types = { workspace = true }
|
types = { workspace = true }
|
||||||
userspace = { workspace = true }
|
userspace = { workspace = true }
|
||||||
userspace-rt = { workspace = true }
|
|
||||||
server-framework = { workspace = true }
|
server-framework = { workspace = true }
|
||||||
|
port-block-device = { workspace = true }
|
||||||
|
driver-block-virtio = { workspace = true }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::block_device::{BlockDevice, SectorId};
|
use port_block_device::{BlockDevice, SectorId};
|
||||||
|
|
||||||
const SECTOR_SIZE: usize = 512;
|
const SECTOR_SIZE: usize = 512;
|
||||||
const MAX_SECTORS: usize = 64;
|
const MAX_SECTORS: usize = 64;
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
pub mod mock_block;
|
pub mod mock_block;
|
||||||
pub mod mock_fs;
|
pub mod mock_fs;
|
||||||
pub mod virtio_blk;
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::block_device::{BlockDevice, SectorId};
|
use port_block_device::{BlockDevice, SectorId};
|
||||||
use crate::filesystem::{DirEntry, FileHandle, FileName, FileSystem};
|
use crate::filesystem::{DirEntry, FileHandle, FileName, FileSystem};
|
||||||
|
|
||||||
const SECTOR_SIZE: usize = 512;
|
const SECTOR_SIZE: usize = 512;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::block_device::{BlockDevice, SectorId};
|
use port_block_device::{BlockDevice, SectorId};
|
||||||
use crate::filesystem::{DirEntry, FileHandle, FileName, FileSystem};
|
use crate::filesystem::{DirEntry, FileHandle, FileName, FileSystem};
|
||||||
|
|
||||||
const SECTOR_SIZE: usize = 512;
|
const SECTOR_SIZE: usize = 512;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
pub mod adapters;
|
pub mod adapters;
|
||||||
pub mod block_device;
|
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
@@ -30,7 +29,7 @@ fn debug_print(s: &[u8]) {
|
|||||||
pub fn run() -> ! {
|
pub fn run() -> ! {
|
||||||
debug_print(b"storage: started\n");
|
debug_print(b"storage: started\n");
|
||||||
|
|
||||||
let device = adapters::virtio_blk::VirtioBlockDevice::new();
|
let device = driver_block_virtio::VirtioBlockDevice::new();
|
||||||
let fat16 = fs::fat16::Fat16::mount(device);
|
let fat16 = fs::fat16::Fat16::mount(device);
|
||||||
if fat16.is_none() {
|
if fat16.is_none() {
|
||||||
debug_print(b"storage: FAT16 mount failed\n");
|
debug_print(b"storage: FAT16 mount failed\n");
|
||||||
@@ -71,7 +70,7 @@ pub fn run() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_entire_file<B: block_device::BlockDevice>(
|
fn read_entire_file<B: port_block_device::BlockDevice>(
|
||||||
fat: &mut fs::fat16::Fat16<B>,
|
fat: &mut fs::fat16::Fat16<B>,
|
||||||
filename: &[u8],
|
filename: &[u8],
|
||||||
out: &mut [u8],
|
out: &mut [u8],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::adapters::mock_block::MockBlockDevice;
|
use crate::adapters::mock_block::MockBlockDevice;
|
||||||
use crate::block_device::{BlockDevice, SectorId};
|
use port_block_device::{BlockDevice, SectorId};
|
||||||
use crate::filesystem::{DirEntry, FileSystem};
|
use crate::filesystem::{DirEntry, FileSystem};
|
||||||
use crate::fs::ext4::Ext4;
|
use crate::fs::ext4::Ext4;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::adapters::mock_block::MockBlockDevice;
|
use crate::adapters::mock_block::MockBlockDevice;
|
||||||
use crate::block_device::{BlockDevice, SectorId};
|
use port_block_device::{BlockDevice, SectorId};
|
||||||
use crate::filesystem::{DirEntry, FileSystem};
|
use crate::filesystem::{DirEntry, FileSystem};
|
||||||
use crate::fs::fat16::Fat16;
|
use crate::fs::fat16::Fat16;
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ copy() {
|
|||||||
copy init "init.elf"
|
copy init "init.elf"
|
||||||
copy echo "echo.elf"
|
copy echo "echo.elf"
|
||||||
copy snake "snake.elf"
|
copy snake "snake.elf"
|
||||||
copy display-server "display.elf"
|
copy display-qemu "display.elf"
|
||||||
copy input-server "input.elf"
|
copy input-server "input.elf"
|
||||||
copy log-server "log.elf"
|
copy log-server "log.elf"
|
||||||
copy permission-manager "permis.elf"
|
copy permission-manager "permis.elf"
|
||||||
copy storage-server "storage.elf"
|
copy storage-qemu "storage.elf"
|
||||||
copy session-server "session.elf"
|
copy session-server "session.elf"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user