virtual-machine 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +1 -1
- package/build/{chunk-GZ343GYI.mjs → chunk-V6I6APCK.mjs} +3 -101
- package/build/cli.js +4 -103
- package/build/index.d.ts +0 -9
- package/build/index.js +5 -103
- package/build/index.mjs +3 -3
- package/build/{riscv_vm-MHIWEZQY.mjs → riscv_vm-QIJGD3E2.mjs} +1 -1
- package/package.json +1 -1
- package/src/lib.rs +0 -21
- package/src/main.rs +130 -54
- package/src/net_webtransport.rs +1 -1
- package/src/uart.rs +0 -2
- package/build/chunk-LJUNPJTY.mjs +0 -670
- package/build/chunk-Q7TFYQ5G.mjs +0 -670
- package/build/riscv_vm-3CIEJ5K7.mjs +0 -12
- package/build/riscv_vm-VNML57ES.mjs +0 -12
- package/src/net_tap.rs +0 -164
- package/src/net_ws.rs +0 -396
package/package.json
CHANGED
package/src/lib.rs
CHANGED
|
@@ -8,7 +8,6 @@ pub mod clint;
|
|
|
8
8
|
pub mod plic;
|
|
9
9
|
pub mod uart;
|
|
10
10
|
pub mod net;
|
|
11
|
-
pub mod net_ws;
|
|
12
11
|
pub mod net_webtransport;
|
|
13
12
|
pub mod virtio;
|
|
14
13
|
pub mod emulator;
|
|
@@ -16,8 +15,6 @@ pub mod emulator;
|
|
|
16
15
|
#[cfg(not(target_arch = "wasm32"))]
|
|
17
16
|
pub mod console;
|
|
18
17
|
|
|
19
|
-
#[cfg(not(target_arch = "wasm32"))]
|
|
20
|
-
pub mod net_tap;
|
|
21
18
|
|
|
22
19
|
use serde::{Deserialize, Serialize};
|
|
23
20
|
|
|
@@ -90,24 +87,6 @@ impl WasmVm {
|
|
|
90
87
|
let vblk = virtio::VirtioBlock::new(disk_image.to_vec());
|
|
91
88
|
self.bus.virtio_devices.push(Box::new(vblk));
|
|
92
89
|
}
|
|
93
|
-
|
|
94
|
-
/// Connect to a WebSocket relay server for networking.
|
|
95
|
-
/// The URL should be like "ws://localhost:8765".
|
|
96
|
-
pub fn connect_network(&mut self, ws_url: &str) -> Result<(), JsValue> {
|
|
97
|
-
use crate::net_ws::WsBackend;
|
|
98
|
-
use crate::virtio::VirtioNet;
|
|
99
|
-
|
|
100
|
-
self.net_status = NetworkStatus::Connecting;
|
|
101
|
-
|
|
102
|
-
let backend = WsBackend::new(ws_url);
|
|
103
|
-
let mut vnet = VirtioNet::new(Box::new(backend));
|
|
104
|
-
vnet.debug = false; // Set to true for debugging
|
|
105
|
-
|
|
106
|
-
self.bus.virtio_devices.push(Box::new(vnet));
|
|
107
|
-
self.net_status = NetworkStatus::Connected;
|
|
108
|
-
|
|
109
|
-
Ok(())
|
|
110
|
-
}
|
|
111
90
|
|
|
112
91
|
/// Connect to a WebTransport relay server.
|
|
113
92
|
pub fn connect_webtransport(&mut self, url: &str, cert_hash: Option<String>) -> Result<(), JsValue> {
|
package/src/main.rs
CHANGED
|
@@ -70,106 +70,183 @@ fn dump_virtio_id(bus: &mut SystemBus) {
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
fn print_vm_banner() {
|
|
74
|
+
const BANNER: &str = r#"
|
|
75
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
76
|
+
│ │
|
|
77
|
+
│ ██████╗ ██╗███████╗██╗ ██╗ ██╗ ██╗ │
|
|
78
|
+
│ ██╔══██╗██║██╔════╝██║ ██╔╝ ██║ ██║ │
|
|
79
|
+
│ ██████╔╝██║███████╗█████╔╝ ██║ ██║ │
|
|
80
|
+
│ ██╔══██╗██║╚════██║██╔═██╗ ╚██╗ ██╔╝ │
|
|
81
|
+
│ ██║ ██║██║███████║██║ ██╗ ╚████╔╝ │
|
|
82
|
+
│ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝ ╚═╝ ╚═══╝ │
|
|
83
|
+
│ │
|
|
84
|
+
│ RISC-V Virtual Machine Hypervisor v0.1.0 │
|
|
85
|
+
│ 64-bit RISC-V Emulator with VirtIO Support │
|
|
86
|
+
│ │
|
|
87
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
88
|
+
"#;
|
|
89
|
+
println!("{}", BANNER);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fn print_section(title: &str) {
|
|
93
|
+
println!("\n\x1b[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m");
|
|
94
|
+
println!("\x1b[1;33m ▸ {}\x1b[0m", title);
|
|
95
|
+
println!("\x1b[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
fn print_status(component: &str, status: &str, ok: bool) {
|
|
99
|
+
let status_color = if ok { "\x1b[1;32m" } else { "\x1b[1;31m" };
|
|
100
|
+
let check = if ok { "✓" } else { "✗" };
|
|
101
|
+
println!(" \x1b[0;37m{:<40}\x1b[0m {}[{}] {}\x1b[0m", component, status_color, check, status);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn print_info(key: &str, value: &str) {
|
|
105
|
+
println!(" \x1b[0;90m├─\x1b[0m \x1b[0;37m{:<20}\x1b[0m \x1b[1;97m{}\x1b[0m", key, value);
|
|
106
|
+
}
|
|
107
|
+
|
|
73
108
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
74
109
|
env_logger::init();
|
|
110
|
+
print_vm_banner();
|
|
111
|
+
|
|
75
112
|
let args = Args::parse();
|
|
76
113
|
|
|
114
|
+
// ─── CPU INITIALIZATION ───────────────────────────────────────────────────
|
|
115
|
+
print_section("CPU INITIALIZATION");
|
|
116
|
+
print_info("Architecture", "RISC-V 64-bit (RV64GC)");
|
|
117
|
+
print_info("Extensions", "I, M, A, F, D, C, Zicsr, Zifencei");
|
|
118
|
+
print_info("Privilege Modes", "Machine, Supervisor, User");
|
|
119
|
+
print_status("CPU Core", "INITIALIZED", true);
|
|
120
|
+
|
|
121
|
+
// ─── MEMORY SUBSYSTEM ─────────────────────────────────────────────────────
|
|
122
|
+
print_section("MEMORY SUBSYSTEM");
|
|
123
|
+
let dram_size_bytes = args
|
|
124
|
+
.mem_mib
|
|
125
|
+
.checked_mul(1024 * 1024)
|
|
126
|
+
.ok_or("Requested memory size is too large")?;
|
|
127
|
+
let dram_base = 0x8000_0000u64;
|
|
128
|
+
|
|
129
|
+
print_info("DRAM Base", &format!("0x{:08X}", dram_base));
|
|
130
|
+
print_info("DRAM Size", &format!("{} MiB ({} bytes)", args.mem_mib, dram_size_bytes));
|
|
131
|
+
print_info("Address Range", &format!("0x{:08X} - 0x{:08X}", dram_base, dram_base + dram_size_bytes as u64));
|
|
132
|
+
|
|
133
|
+
let mut bus = SystemBus::new(dram_base, dram_size_bytes);
|
|
134
|
+
print_status("DRAM Controller", "ONLINE", true);
|
|
135
|
+
print_status("MMU (Sv39)", "READY", true);
|
|
136
|
+
|
|
137
|
+
// ─── KERNEL LOADING ───────────────────────────────────────────────────────
|
|
138
|
+
print_section("KERNEL LOADING");
|
|
77
139
|
let buffer = if args.kernel.starts_with("http://") || args.kernel.starts_with("https://") {
|
|
78
|
-
|
|
140
|
+
print_info("Source", "Remote (HTTP/HTTPS)");
|
|
141
|
+
print_info("URL", &args.kernel);
|
|
142
|
+
println!(" \x1b[0;90m├─\x1b[0m \x1b[0;33mDownloading...\x1b[0m");
|
|
79
143
|
let response = reqwest::blocking::get(&args.kernel)?;
|
|
80
144
|
if !response.status().is_success() {
|
|
145
|
+
print_status("Download", "FAILED", false);
|
|
81
146
|
return Err(format!("Failed to download kernel: {}", response.status()).into());
|
|
82
147
|
}
|
|
83
|
-
response.bytes()?.to_vec()
|
|
148
|
+
let bytes = response.bytes()?.to_vec();
|
|
149
|
+
print_status("Download", "COMPLETE", true);
|
|
150
|
+
bytes
|
|
84
151
|
} else {
|
|
152
|
+
print_info("Source", "Local filesystem");
|
|
153
|
+
print_info("Path", &args.kernel);
|
|
85
154
|
let mut file = File::open(&args.kernel)?;
|
|
86
155
|
let mut buffer = Vec::new();
|
|
87
156
|
file.read_to_end(&mut buffer)?;
|
|
88
157
|
buffer
|
|
89
158
|
};
|
|
159
|
+
print_info("Kernel Size", &format!("{} bytes ({:.2} KiB)", buffer.len(), buffer.len() as f64 / 1024.0));
|
|
90
160
|
|
|
91
|
-
let dram_size_bytes = args
|
|
92
|
-
.mem_mib
|
|
93
|
-
.checked_mul(1024 * 1024)
|
|
94
|
-
.ok_or("Requested memory size is too large")?;
|
|
95
|
-
|
|
96
|
-
// Initialize DRAM at 0x8000_0000
|
|
97
|
-
let dram_base = 0x8000_0000;
|
|
98
|
-
let mut bus = SystemBus::new(dram_base, dram_size_bytes);
|
|
99
161
|
|
|
162
|
+
// ─── VIRTIO DEVICE BUS ─────────────────────────────────────────────────────
|
|
163
|
+
print_section("VIRTIO DEVICE BUS");
|
|
164
|
+
print_info("Bus Type", "VirtIO MMIO v2");
|
|
165
|
+
print_info("Base Address", "0x10001000");
|
|
166
|
+
print_info("Device Spacing", "0x1000 (4 KiB)");
|
|
167
|
+
|
|
100
168
|
// If a disk image is provided, wire up VirtIO Block at 0x1000_1000
|
|
101
169
|
if let Some(disk_path) = &args.disk {
|
|
102
170
|
let mut disk_file = File::open(disk_path)?;
|
|
103
171
|
let mut disk_buf = Vec::new();
|
|
104
172
|
disk_file.read_to_end(&mut disk_buf)?;
|
|
173
|
+
let disk_size_mib = disk_buf.len() / (1024 * 1024);
|
|
105
174
|
let vblk = riscv_vm::virtio::VirtioBlock::new(disk_buf);
|
|
106
175
|
bus.virtio_devices.push(Box::new(vblk));
|
|
107
|
-
println!(
|
|
176
|
+
println!();
|
|
177
|
+
println!(" \x1b[1;35m┌─ VirtIO Block Device ─────────────────────────────────┐\x1b[0m");
|
|
178
|
+
println!(" \x1b[1;35m│\x1b[0m Address: \x1b[1;97m0x10001000\x1b[0m \x1b[1;35m│\x1b[0m");
|
|
179
|
+
println!(" \x1b[1;35m│\x1b[0m IRQ: \x1b[1;97m1\x1b[0m \x1b[1;35m│\x1b[0m");
|
|
180
|
+
println!(" \x1b[1;35m│\x1b[0m Disk Size: \x1b[1;97m{} MiB\x1b[0m \x1b[1;35m│\x1b[0m", disk_size_mib);
|
|
181
|
+
println!(" \x1b[1;35m│\x1b[0m Image: \x1b[0;90m{}\x1b[0m", disk_path.display());
|
|
182
|
+
println!(" \x1b[1;35m└────────────────────────────────────────────────────────┘\x1b[0m");
|
|
183
|
+
print_status("VirtIO Block", "ATTACHED", true);
|
|
108
184
|
}
|
|
109
185
|
|
|
110
|
-
// If
|
|
111
|
-
if let Some(
|
|
112
|
-
let tap_backend = riscv_vm::net_tap::TapBackend::new(tap_name);
|
|
113
|
-
let vnet = riscv_vm::virtio::VirtioNet::new(Box::new(tap_backend));
|
|
114
|
-
let device_idx = bus.virtio_devices.len();
|
|
115
|
-
let irq = 1 + device_idx; // IRQ 1 for first device, 2 for second, etc.
|
|
116
|
-
bus.virtio_devices.push(Box::new(vnet));
|
|
117
|
-
let base_addr = 0x1000_1000 + (device_idx as u64) * 0x1000;
|
|
118
|
-
println!("VirtIO Net device (TAP: {}) attached at 0x{:x} (IRQ {})", tap_name, base_addr, irq);
|
|
119
|
-
} else if let Some(ws_url) = &args.net_ws {
|
|
120
|
-
// Wire up VirtIO Net with WebSocket backend
|
|
121
|
-
let ws_backend = riscv_vm::net_ws::WsBackend::new(ws_url);
|
|
122
|
-
let vnet = riscv_vm::virtio::VirtioNet::new(Box::new(ws_backend));
|
|
123
|
-
let device_idx = bus.virtio_devices.len();
|
|
124
|
-
let irq = 1 + device_idx;
|
|
125
|
-
bus.virtio_devices.push(Box::new(vnet));
|
|
126
|
-
let base_addr = 0x1000_1000 + (device_idx as u64) * 0x1000;
|
|
127
|
-
println!("VirtIO Net device (WebSocket: {}) attached at 0x{:x} (IRQ {})", ws_url, base_addr, irq);
|
|
128
|
-
} else if let Some(wt_url) = &args.net_webtransport {
|
|
129
|
-
// Wire up VirtIO Net with WebTransport backend
|
|
186
|
+
// If WebTransport is provided, wire up VirtIO Net
|
|
187
|
+
if let Some(wt_url) = &args.net_webtransport {
|
|
130
188
|
let wt_backend = riscv_vm::net_webtransport::WebTransportBackend::new(wt_url, args.net_cert_hash.clone());
|
|
131
189
|
let vnet = riscv_vm::virtio::VirtioNet::new(Box::new(wt_backend));
|
|
132
190
|
let device_idx = bus.virtio_devices.len();
|
|
133
191
|
let irq = 1 + device_idx;
|
|
134
192
|
bus.virtio_devices.push(Box::new(vnet));
|
|
135
193
|
let base_addr = 0x1000_1000 + (device_idx as u64) * 0x1000;
|
|
136
|
-
println!(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
println!("VirtIO Net device (Dummy) attached at 0x{:x} (IRQ {})", base_addr, irq);
|
|
146
|
-
}
|
|
194
|
+
println!();
|
|
195
|
+
println!(" \x1b[1;34m┌─ VirtIO Network Device ───────────────────────────────┐\x1b[0m");
|
|
196
|
+
println!(" \x1b[1;34m│\x1b[0m Address: \x1b[1;97m0x{:08X}\x1b[0m \x1b[1;34m│\x1b[0m", base_addr);
|
|
197
|
+
println!(" \x1b[1;34m│\x1b[0m IRQ: \x1b[1;97m{}\x1b[0m \x1b[1;34m│\x1b[0m", irq);
|
|
198
|
+
println!(" \x1b[1;34m│\x1b[0m Backend: \x1b[1;97mWebTransport\x1b[0m \x1b[1;34m│\x1b[0m");
|
|
199
|
+
println!(" \x1b[1;34m│\x1b[0m Relay: \x1b[0;90m{}\x1b[0m", wt_url);
|
|
200
|
+
println!(" \x1b[1;34m└────────────────────────────────────────────────────────┘\x1b[0m");
|
|
201
|
+
print_status("VirtIO Network", "ATTACHED", true);
|
|
202
|
+
}
|
|
147
203
|
|
|
148
204
|
let entry_pc = if buffer.starts_with(b"\x7FELF") {
|
|
149
|
-
|
|
205
|
+
print_info("Format", "ELF64 Executable");
|
|
150
206
|
load_elf_into_dram(&buffer, &mut bus)?
|
|
151
207
|
} else {
|
|
152
208
|
if args.load_addr < dram_base {
|
|
153
|
-
|
|
209
|
+
print_status("Load Address", "INVALID (below DRAM)", false);
|
|
154
210
|
return Ok(());
|
|
155
211
|
}
|
|
156
212
|
let offset = args.load_addr - dram_base;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
buffer.len(),
|
|
160
|
-
args.load_addr
|
|
161
|
-
);
|
|
213
|
+
print_info("Format", "Raw Binary");
|
|
214
|
+
print_info("Load Address", &format!("0x{:08X}", args.load_addr));
|
|
162
215
|
bus.dram
|
|
163
216
|
.load(&buffer, offset)
|
|
164
217
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
|
165
218
|
args.load_addr
|
|
166
219
|
};
|
|
220
|
+
print_info("Entry Point", &format!("0x{:08X}", entry_pc));
|
|
221
|
+
print_status("Kernel Image", "LOADED", true);
|
|
222
|
+
|
|
223
|
+
// ─── SYSTEM PERIPHERALS ───────────────────────────────────────────────────
|
|
224
|
+
print_section("SYSTEM PERIPHERALS");
|
|
225
|
+
print_info("UART 16550", "0x10000000 (IRQ 10)");
|
|
226
|
+
print_info("CLINT", "0x02000000 (Machine Timer)");
|
|
227
|
+
print_info("PLIC", "0x0C000000 (IRQ Controller)");
|
|
228
|
+
print_status("UART Console", "READY", true);
|
|
229
|
+
print_status("Interrupt Controller", "CONFIGURED", true);
|
|
230
|
+
|
|
231
|
+
// Early probe dump (harmless if device absent)
|
|
232
|
+
dump_virtio_id(&mut bus);
|
|
167
233
|
|
|
168
234
|
let mut cpu = Cpu::new(entry_pc);
|
|
169
235
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
236
|
+
// ─── BOOT SEQUENCE COMPLETE ───────────────────────────────────────────────
|
|
237
|
+
print_section("BOOT SEQUENCE COMPLETE");
|
|
238
|
+
println!();
|
|
239
|
+
println!(" \x1b[1;32m╔══════════════════════════════════════════════════════════════════════╗\x1b[0m");
|
|
240
|
+
println!(" \x1b[1;32m║\x1b[0m \x1b[1;32m║\x1b[0m");
|
|
241
|
+
println!(" \x1b[1;32m║\x1b[0m \x1b[1;97mStarting RISC-V Kernel at 0x{:08X}\x1b[0m \x1b[1;32m║\x1b[0m", entry_pc);
|
|
242
|
+
println!(" \x1b[1;32m║\x1b[0m \x1b[0;90mPress Ctrl-A then 'x' to terminate\x1b[0m \x1b[1;32m║\x1b[0m");
|
|
243
|
+
println!(" \x1b[1;32m║\x1b[0m \x1b[1;32m║\x1b[0m");
|
|
244
|
+
println!(" \x1b[1;32m╚══════════════════════════════════════════════════════════════════════╝\x1b[0m");
|
|
245
|
+
println!();
|
|
246
|
+
println!("\x1b[1;36m══════════════════════════════════════════════════════════════════════════════\x1b[0m");
|
|
247
|
+
println!("\x1b[1;33m KERNEL OUTPUT BEGINS\x1b[0m");
|
|
248
|
+
println!("\x1b[1;36m══════════════════════════════════════════════════════════════════════════════\x1b[0m");
|
|
249
|
+
println!();
|
|
173
250
|
|
|
174
251
|
let mut step_count = 0u64;
|
|
175
252
|
let mut last_report_step = 0u64;
|
|
@@ -222,8 +299,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
222
299
|
if let Err(trap) = step_result {
|
|
223
300
|
match trap {
|
|
224
301
|
// Test finisher / explicit host stop requested by the guest.
|
|
225
|
-
Trap::RequestedTrap(
|
|
226
|
-
println!("Guest requested stop via test finisher: 0x{code:x}");
|
|
302
|
+
Trap::RequestedTrap(_) => {
|
|
227
303
|
break;
|
|
228
304
|
}
|
|
229
305
|
// Non-recoverable emulator error: dump state and exit.
|
package/src/net_webtransport.rs
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
//! - 0x01 prefix: Ethernet data frames
|
|
7
7
|
|
|
8
8
|
use crate::net::NetworkBackend;
|
|
9
|
-
use std::sync::Arc;
|
|
10
9
|
|
|
11
10
|
/// Message type prefix for control messages
|
|
12
11
|
const MSG_TYPE_CONTROL: u8 = 0x00;
|
|
@@ -68,6 +67,7 @@ fn decode_message(data: &[u8]) -> Option<Vec<u8>> {
|
|
|
68
67
|
#[cfg(not(target_arch = "wasm32"))]
|
|
69
68
|
mod native {
|
|
70
69
|
use super::*;
|
|
70
|
+
use std::sync::Arc;
|
|
71
71
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
72
72
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
|
73
73
|
use std::thread;
|
package/src/uart.rs
CHANGED
|
@@ -7,9 +7,7 @@ pub const UART_SIZE: u64 = 0x100;
|
|
|
7
7
|
// Registers (offset)
|
|
8
8
|
const RBR: u64 = 0x00; // Receiver Buffer (Read)
|
|
9
9
|
const THR: u64 = 0x00; // Transmitter Holding (Write)
|
|
10
|
-
const DLL: u64 = 0x00; // Divisor Latch LSB (Read/Write if DLAB=1)
|
|
11
10
|
const IER: u64 = 0x01; // Interrupt Enable
|
|
12
|
-
const DLM: u64 = 0x01; // Divisor Latch MSB (Read/Write if DLAB=1)
|
|
13
11
|
const IIR: u64 = 0x02; // Interrupt Identity (Read)
|
|
14
12
|
const FCR: u64 = 0x02; // FIFO Control (Write)
|
|
15
13
|
const LCR: u64 = 0x03; // Line Control
|