virtual-machine 0.0.4 → 0.0.14
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/build/{chunk-V6I6APCK.mjs → chunk-H6WI4NRK.mjs} +122 -123
- package/build/cli.js +590 -610
- package/build/index.d.ts +5 -5
- package/build/index.js +117 -118
- package/build/index.mjs +4 -4
- package/build/{riscv_vm-QIJGD3E2.mjs → riscv_vm-MAZK3LFN.mjs} +1 -1
- package/package.json +12 -5
- package/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +0 -1
- package/Cargo.toml +0 -49
- package/build.sh +0 -25
- package/cli.ts +0 -268
- package/index.ts +0 -16
- package/src/bus.rs +0 -558
- package/src/clint.rs +0 -132
- package/src/console.rs +0 -83
- package/src/cpu.rs +0 -1913
- package/src/csr.rs +0 -67
- package/src/decoder.rs +0 -789
- package/src/dram.rs +0 -146
- package/src/emulator.rs +0 -603
- package/src/lib.rs +0 -249
- package/src/main.rs +0 -449
- package/src/mmu.rs +0 -331
- package/src/net.rs +0 -121
- package/src/net_webtransport.rs +0 -446
- package/src/plic.rs +0 -261
- package/src/uart.rs +0 -231
- package/src/virtio.rs +0 -1074
- package/tsconfig.json +0 -19
- package/tsup/index.ts +0 -79
- package/tsup/tsup.cli.ts +0 -8
- package/tsup/tsup.core.cjs.ts +0 -7
- package/tsup/tsup.core.esm.ts +0 -8
package/src/main.rs
DELETED
|
@@ -1,449 +0,0 @@
|
|
|
1
|
-
use clap::Parser;
|
|
2
|
-
use goblin::elf::{program_header::PT_LOAD, Elf};
|
|
3
|
-
use riscv_vm::bus::{Bus, SystemBus};
|
|
4
|
-
use riscv_vm::cpu::Cpu;
|
|
5
|
-
use riscv_vm::Trap;
|
|
6
|
-
use riscv_vm::csr::{CSR_MCAUSE, CSR_MEPC, CSR_MTVAL, CSR_MTVEC, CSR_SCAUSE, CSR_SEPC, CSR_STVAL, CSR_STVEC};
|
|
7
|
-
use std::fs::File;
|
|
8
|
-
use std::io::Read;
|
|
9
|
-
use std::path::PathBuf;
|
|
10
|
-
|
|
11
|
-
use riscv_vm::console::Console;
|
|
12
|
-
|
|
13
|
-
#[derive(Parser, Debug)]
|
|
14
|
-
#[command(author, version, about, long_about = None)]
|
|
15
|
-
struct Args {
|
|
16
|
-
/// Path or URL to binary to load
|
|
17
|
-
#[arg(short, long)]
|
|
18
|
-
kernel: String,
|
|
19
|
-
|
|
20
|
-
/// Address to load kernel at (default 0x8000_0000)
|
|
21
|
-
#[arg(long, default_value_t = 0x8000_0000)]
|
|
22
|
-
load_addr: u64,
|
|
23
|
-
|
|
24
|
-
/// DRAM size in MiB
|
|
25
|
-
#[arg(long, default_value_t = 512)]
|
|
26
|
-
mem_mib: usize,
|
|
27
|
-
|
|
28
|
-
/// Optional path to a VirtIO Block disk image (e.g. xv6 fs.img)
|
|
29
|
-
#[arg(long)]
|
|
30
|
-
disk: Option<PathBuf>,
|
|
31
|
-
|
|
32
|
-
/// Optional TAP interface name for VirtIO network device (e.g. tap0)
|
|
33
|
-
/// Requires the interface to exist: sudo ip tuntap add dev tap0 mode tap
|
|
34
|
-
#[arg(long)]
|
|
35
|
-
net_tap: Option<String>,
|
|
36
|
-
|
|
37
|
-
/// Enable VirtIO network device with a dummy backend (for testing, no actual packets)
|
|
38
|
-
#[arg(long)]
|
|
39
|
-
net_dummy: bool,
|
|
40
|
-
|
|
41
|
-
/// Connect to a WebSocket server for networking (e.g. ws://localhost:8765)
|
|
42
|
-
/// Works on macOS and in browser/WASM
|
|
43
|
-
#[arg(long)]
|
|
44
|
-
net_ws: Option<String>,
|
|
45
|
-
|
|
46
|
-
/// Connect to a WebTransport relay for networking (e.g. https://127.0.0.1:4433)
|
|
47
|
-
/// Supports NAT traversal and peer-to-peer connections
|
|
48
|
-
#[arg(long)]
|
|
49
|
-
net_webtransport: Option<String>,
|
|
50
|
-
|
|
51
|
-
/// Certificate hash for WebTransport (hex string)
|
|
52
|
-
/// Required for self-signed certificates
|
|
53
|
-
#[arg(long)]
|
|
54
|
-
net_cert_hash: Option<String>,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Debug helper: dump VirtIO MMIO identity registers expected by xv6.
|
|
58
|
-
fn dump_virtio_id(bus: &mut SystemBus) {
|
|
59
|
-
const VIRTIO0_BASE: u64 = 0x1000_1000;
|
|
60
|
-
fn r32(bus: &mut SystemBus, off: u64) -> u32 {
|
|
61
|
-
bus.read32(VIRTIO0_BASE + off).unwrap_or(0)
|
|
62
|
-
}
|
|
63
|
-
let magic = r32(bus, 0x000);
|
|
64
|
-
let ver = r32(bus, 0x004);
|
|
65
|
-
let devid = r32(bus, 0x008);
|
|
66
|
-
let vendor = r32(bus, 0x00c);
|
|
67
|
-
eprintln!(
|
|
68
|
-
"VirtIO ID: MAGIC=0x{:08x} VERSION={} DEVICE_ID={} VENDOR=0x{:08x}",
|
|
69
|
-
magic, ver, devid, vendor
|
|
70
|
-
);
|
|
71
|
-
}
|
|
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
|
-
|
|
108
|
-
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
109
|
-
env_logger::init();
|
|
110
|
-
print_vm_banner();
|
|
111
|
-
|
|
112
|
-
let args = Args::parse();
|
|
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");
|
|
139
|
-
let buffer = if args.kernel.starts_with("http://") || args.kernel.starts_with("https://") {
|
|
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");
|
|
143
|
-
let response = reqwest::blocking::get(&args.kernel)?;
|
|
144
|
-
if !response.status().is_success() {
|
|
145
|
-
print_status("Download", "FAILED", false);
|
|
146
|
-
return Err(format!("Failed to download kernel: {}", response.status()).into());
|
|
147
|
-
}
|
|
148
|
-
let bytes = response.bytes()?.to_vec();
|
|
149
|
-
print_status("Download", "COMPLETE", true);
|
|
150
|
-
bytes
|
|
151
|
-
} else {
|
|
152
|
-
print_info("Source", "Local filesystem");
|
|
153
|
-
print_info("Path", &args.kernel);
|
|
154
|
-
let mut file = File::open(&args.kernel)?;
|
|
155
|
-
let mut buffer = Vec::new();
|
|
156
|
-
file.read_to_end(&mut buffer)?;
|
|
157
|
-
buffer
|
|
158
|
-
};
|
|
159
|
-
print_info("Kernel Size", &format!("{} bytes ({:.2} KiB)", buffer.len(), buffer.len() as f64 / 1024.0));
|
|
160
|
-
|
|
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
|
-
|
|
168
|
-
// If a disk image is provided, wire up VirtIO Block at 0x1000_1000
|
|
169
|
-
if let Some(disk_path) = &args.disk {
|
|
170
|
-
let mut disk_file = File::open(disk_path)?;
|
|
171
|
-
let mut disk_buf = Vec::new();
|
|
172
|
-
disk_file.read_to_end(&mut disk_buf)?;
|
|
173
|
-
let disk_size_mib = disk_buf.len() / (1024 * 1024);
|
|
174
|
-
let vblk = riscv_vm::virtio::VirtioBlock::new(disk_buf);
|
|
175
|
-
bus.virtio_devices.push(Box::new(vblk));
|
|
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);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// If WebTransport is provided, wire up VirtIO Net
|
|
187
|
-
if let Some(wt_url) = &args.net_webtransport {
|
|
188
|
-
let wt_backend = riscv_vm::net_webtransport::WebTransportBackend::new(wt_url, args.net_cert_hash.clone());
|
|
189
|
-
let vnet = riscv_vm::virtio::VirtioNet::new(Box::new(wt_backend));
|
|
190
|
-
let device_idx = bus.virtio_devices.len();
|
|
191
|
-
let irq = 1 + device_idx;
|
|
192
|
-
bus.virtio_devices.push(Box::new(vnet));
|
|
193
|
-
let base_addr = 0x1000_1000 + (device_idx as u64) * 0x1000;
|
|
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
|
-
}
|
|
203
|
-
|
|
204
|
-
let entry_pc = if buffer.starts_with(b"\x7FELF") {
|
|
205
|
-
print_info("Format", "ELF64 Executable");
|
|
206
|
-
load_elf_into_dram(&buffer, &mut bus)?
|
|
207
|
-
} else {
|
|
208
|
-
if args.load_addr < dram_base {
|
|
209
|
-
print_status("Load Address", "INVALID (below DRAM)", false);
|
|
210
|
-
return Ok(());
|
|
211
|
-
}
|
|
212
|
-
let offset = args.load_addr - dram_base;
|
|
213
|
-
print_info("Format", "Raw Binary");
|
|
214
|
-
print_info("Load Address", &format!("0x{:08X}", args.load_addr));
|
|
215
|
-
bus.dram
|
|
216
|
-
.load(&buffer, offset)
|
|
217
|
-
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
|
218
|
-
args.load_addr
|
|
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);
|
|
233
|
-
|
|
234
|
-
let mut cpu = Cpu::new(entry_pc);
|
|
235
|
-
|
|
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!();
|
|
250
|
-
|
|
251
|
-
let mut step_count = 0u64;
|
|
252
|
-
let mut last_report_step = 0u64;
|
|
253
|
-
|
|
254
|
-
// Initialize console for host input
|
|
255
|
-
let console = Console::new();
|
|
256
|
-
let mut escaped = false;
|
|
257
|
-
|
|
258
|
-
loop {
|
|
259
|
-
// Poll console input
|
|
260
|
-
if let Some(b) = console.poll() {
|
|
261
|
-
if escaped {
|
|
262
|
-
if b == b'x' {
|
|
263
|
-
println!("\nTerminated by user.");
|
|
264
|
-
break;
|
|
265
|
-
} else if b == 1 {
|
|
266
|
-
// Ctrl-A twice -> send Ctrl-A to guest
|
|
267
|
-
bus.uart.push_input(1);
|
|
268
|
-
} else {
|
|
269
|
-
// Ctrl-A then something else -> send that something else
|
|
270
|
-
// (Ctrl-A is swallowed)
|
|
271
|
-
bus.uart.push_input(b);
|
|
272
|
-
}
|
|
273
|
-
escaped = false;
|
|
274
|
-
} else {
|
|
275
|
-
if b == 1 { // Ctrl-A
|
|
276
|
-
escaped = true;
|
|
277
|
-
} else {
|
|
278
|
-
bus.uart.push_input(b);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
let step_result = cpu.step(&mut bus);
|
|
284
|
-
step_count += 1;
|
|
285
|
-
|
|
286
|
-
// Poll VirtIO devices for incoming network packets every 100 instructions
|
|
287
|
-
// More frequent polling improves network responsiveness for interactive protocols
|
|
288
|
-
if step_count % 100 == 0 {
|
|
289
|
-
bus.poll_virtio();
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Progress report every 10M instructions (not every instruction!)
|
|
293
|
-
if step_count - last_report_step >= 10_000_000 {
|
|
294
|
-
// eprinteln!("[{} M insns] pc=0x{:x} mode={:?}", step_count / 1_000_000, cpu.pc, cpu.mode);
|
|
295
|
-
last_report_step = step_count;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if let Err(trap) = step_result {
|
|
300
|
-
match trap {
|
|
301
|
-
// Test finisher / explicit host stop requested by the guest.
|
|
302
|
-
Trap::RequestedTrap(_) => {
|
|
303
|
-
break;
|
|
304
|
-
}
|
|
305
|
-
// Non-recoverable emulator error: dump state and exit.
|
|
306
|
-
Trap::Fatal(msg) => {
|
|
307
|
-
eprintln!("Fatal emulator error: {msg}");
|
|
308
|
-
println!("PC: 0x{:x}", cpu.pc);
|
|
309
|
-
for i in 0..32 {
|
|
310
|
-
if i % 4 == 0 {
|
|
311
|
-
println!();
|
|
312
|
-
}
|
|
313
|
-
print!("x{:<2}: 0x{:<16x} ", i, cpu.regs[i]);
|
|
314
|
-
}
|
|
315
|
-
println!();
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
318
|
-
// Architectural traps (interrupts, page faults, ecalls, etc.)
|
|
319
|
-
// are fully handled inside Cpu::handle_trap by updating CSRs
|
|
320
|
-
// and redirecting PC to mtvec/stvec. We simply continue
|
|
321
|
-
// stepping so that the guest handler can run.
|
|
322
|
-
_other => {
|
|
323
|
-
// Traps are handled inside cpu.step() - just continue execution.
|
|
324
|
-
// Use RUST_LOG=debug to see trap details.
|
|
325
|
-
if log::log_enabled!(log::Level::Debug) {
|
|
326
|
-
let mepc = cpu.read_csr(CSR_MEPC).unwrap_or(0);
|
|
327
|
-
let mcause = cpu.read_csr(CSR_MCAUSE).unwrap_or(0);
|
|
328
|
-
let mtval = cpu.read_csr(CSR_MTVAL).unwrap_or(0);
|
|
329
|
-
let mtvec = cpu.read_csr(CSR_MTVEC).unwrap_or(0);
|
|
330
|
-
log::debug!(
|
|
331
|
-
"Trap: {:?} pc=0x{:x} mepc=0x{:x} mcause=0x{:x} mtval=0x{:x} mtvec=0x{:x}",
|
|
332
|
-
_other, cpu.pc, mepc, mcause, mtval, mtvec
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Check UART output - handle raw mode by converting \n to \r\n
|
|
340
|
-
use std::io::Write;
|
|
341
|
-
let stdout = std::io::stdout();
|
|
342
|
-
let mut stdout_lock = stdout.lock();
|
|
343
|
-
while let Some(byte) = bus.uart.pop_output() {
|
|
344
|
-
// In raw terminal mode, \n alone doesn't return cursor to column 0.
|
|
345
|
-
// We need to emit \r\n for proper line breaks.
|
|
346
|
-
if byte == b'\n' {
|
|
347
|
-
let _ = stdout_lock.write_all(b"\r\n");
|
|
348
|
-
} else if byte == b'\r' {
|
|
349
|
-
// Carriage return - just emit it
|
|
350
|
-
let _ = stdout_lock.write_all(b"\r");
|
|
351
|
-
} else {
|
|
352
|
-
let _ = stdout_lock.write_all(&[byte]);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
let _ = stdout_lock.flush();
|
|
356
|
-
|
|
357
|
-
// Stop if PC is 0 in Machine/Supervisor mode (likely trap to unmapped vector).
|
|
358
|
-
// User mode PC=0 is valid (xv6 initcode).
|
|
359
|
-
if cpu.pc == 0 && cpu.mode != riscv_vm::csr::Mode::User {
|
|
360
|
-
let mepc = cpu.read_csr(CSR_MEPC).unwrap_or(0);
|
|
361
|
-
let mcause = cpu.read_csr(CSR_MCAUSE).unwrap_or(0);
|
|
362
|
-
let mtval = cpu.read_csr(CSR_MTVAL).unwrap_or(0);
|
|
363
|
-
let mtvec = cpu.read_csr(CSR_MTVEC).unwrap_or(0);
|
|
364
|
-
let sepc = cpu.read_csr(CSR_SEPC).unwrap_or(0);
|
|
365
|
-
let scause = cpu.read_csr(CSR_SCAUSE).unwrap_or(0);
|
|
366
|
-
let stval = cpu.read_csr(CSR_STVAL).unwrap_or(0);
|
|
367
|
-
let stvec = cpu.read_csr(CSR_STVEC).unwrap_or(0);
|
|
368
|
-
println!("PC reached 0, stopping.");
|
|
369
|
-
println!(
|
|
370
|
-
"Final state:\n pc=0x{:016x} mode={:?}\n M: mepc=0x{:016x} mcause=0x{:016x} mtval=0x{:016x} mtvec=0x{:016x}\n S: sepc=0x{:016x} scause=0x{:016x} stval=0x{:016x} stvec=0x{:016x}",
|
|
371
|
-
cpu.pc, cpu.mode, mepc, mcause, mtval, mtvec, sepc, scause, stval, stvec
|
|
372
|
-
);
|
|
373
|
-
break;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
Ok(())
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
fn load_elf_into_dram(
|
|
381
|
-
buffer: &[u8],
|
|
382
|
-
bus: &mut SystemBus,
|
|
383
|
-
) -> Result<u64, Box<dyn std::error::Error>> {
|
|
384
|
-
let elf = Elf::parse(buffer)?;
|
|
385
|
-
let base = bus.dram_base();
|
|
386
|
-
let dram_end = base + bus.dram_size() as u64;
|
|
387
|
-
|
|
388
|
-
for ph in &elf.program_headers {
|
|
389
|
-
if ph.p_type != PT_LOAD || ph.p_memsz == 0 {
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
let file_size = ph.p_filesz as usize;
|
|
394
|
-
let mem_size = ph.p_memsz as usize;
|
|
395
|
-
let file_offset = ph.p_offset as usize;
|
|
396
|
-
if file_offset + file_size > buffer.len() {
|
|
397
|
-
return Err(format!(
|
|
398
|
-
"ELF segment exceeds file bounds (offset 0x{:x})",
|
|
399
|
-
file_offset
|
|
400
|
-
)
|
|
401
|
-
.into());
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
let target_addr = if ph.p_paddr != 0 {
|
|
405
|
-
ph.p_paddr
|
|
406
|
-
} else {
|
|
407
|
-
ph.p_vaddr
|
|
408
|
-
};
|
|
409
|
-
if target_addr < base {
|
|
410
|
-
return Err(format!(
|
|
411
|
-
"Segment start 0x{:x} lies below DRAM base 0x{:x}",
|
|
412
|
-
target_addr, base
|
|
413
|
-
)
|
|
414
|
-
.into());
|
|
415
|
-
}
|
|
416
|
-
let seg_end = target_addr
|
|
417
|
-
.checked_add(mem_size as u64)
|
|
418
|
-
.ok_or_else(|| "Segment end overflow".to_string())?;
|
|
419
|
-
if seg_end > dram_end {
|
|
420
|
-
return Err(format!(
|
|
421
|
-
"Segment 0x{:x}-0x{:x} exceeds DRAM (end 0x{:x})",
|
|
422
|
-
target_addr, seg_end, dram_end
|
|
423
|
-
)
|
|
424
|
-
.into());
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
let dram_offset = (target_addr - base) as u64;
|
|
428
|
-
if file_size > 0 {
|
|
429
|
-
let end = file_offset + file_size;
|
|
430
|
-
bus.dram
|
|
431
|
-
.load(&buffer[file_offset..end], dram_offset)
|
|
432
|
-
.map_err(|e| format!("Failed to load segment: {}", e))?;
|
|
433
|
-
}
|
|
434
|
-
if mem_size > file_size {
|
|
435
|
-
let zero_start = dram_offset as usize + file_size;
|
|
436
|
-
bus.dram
|
|
437
|
-
.zero_range(zero_start, mem_size - file_size)
|
|
438
|
-
.map_err(|e| format!("Failed to zero bss: {}", e))?;
|
|
439
|
-
}
|
|
440
|
-
log::debug!(
|
|
441
|
-
"Loaded segment: addr=0x{:x}, filesz=0x{:x}, memsz=0x{:x}",
|
|
442
|
-
target_addr,
|
|
443
|
-
file_size,
|
|
444
|
-
mem_size
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
Ok(elf.entry)
|
|
449
|
-
}
|