Refactors RustError to supports passing it as argument (#1017)
Some checks are pending
Development Build / Build (push) Waiting to run
Development Build / Update PRs (push) Waiting to run

This commit is contained in:
Putta Khunchalee 2024-10-05 23:29:30 +07:00 committed by GitHub
parent 59356290e9
commit 5191975df8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 151 additions and 134 deletions

View File

@ -85,11 +85,16 @@ struct VmmScreen {
* Contains VMM event information.
*/
enum VmmEvent_Tag {
VmmEvent_Error,
VmmEvent_WaitingDebugger,
VmmEvent_Exiting,
VmmEvent_Log,
};
struct VmmEvent_Error_Body {
const struct RustError *reason;
};
struct VmmEvent_WaitingDebugger_Body {
const char *addr;
size_t len;
@ -108,6 +113,7 @@ struct VmmEvent_Log_Body {
struct VmmEvent {
enum VmmEvent_Tag tag;
union {
struct VmmEvent_Error_Body error;
struct VmmEvent_WaitingDebugger_Body waiting_debugger;
struct VmmEvent_Exiting_Body exiting;
struct VmmEvent_Log_Body log;
@ -182,7 +188,7 @@ struct Vmm *vmm_run(const char *kernel,
const struct VmmScreen *screen,
const struct Profile *profile,
const char *debug,
bool (*event)(const struct VmmEvent*, void*),
void (*event)(const struct VmmEvent*, void*),
void *cx,
struct RustError **err);

View File

@ -101,7 +101,7 @@ MainWindow::MainWindow(QVulkanInstance *vulkan, QList<VkPhysicalDevice> &&vkDevi
#endif
connect(m_launch, &LaunchSettings::saveClicked, this, &MainWindow::saveProfile);
connect(m_launch, &LaunchSettings::startClicked, this, &MainWindow::startKernel);
connect(m_launch, &LaunchSettings::startClicked, this, &MainWindow::startVmm);
m_main->addWidget(m_launch);
@ -213,7 +213,7 @@ void MainWindow::closeEvent(QCloseEvent *event)
event->ignore();
// Ask user to confirm.
if (m_kernel) {
if (m_vmm) {
QMessageBox confirm(this);
confirm.setText("Do you want to exit?");
@ -226,7 +226,7 @@ void MainWindow::closeEvent(QCloseEvent *event)
return;
}
m_kernel.free();
m_vmm.free();
}
// Close child windows.
@ -341,7 +341,7 @@ void MainWindow::saveProfile(Profile *p)
}
}
void MainWindow::startKernel(const QString &debugAddr)
void MainWindow::startVmm(const QString &debugAddr)
{
// Get full path to kernel binary.
std::string kernel;
@ -426,24 +426,24 @@ void MainWindow::startKernel(const QString &debugAddr)
return;
}
m_kernel = std::move(vmm);
m_vmm = std::move(vmm);
m_screen->requestUpdate();
}
void MainWindow::updateScreen()
{
// Do nothing if the kernel is not running.
if (!m_kernel) {
// Do nothing if the VMM is not running.
if (!m_vmm) {
return;
}
// Draw the screen.
Rust<RustError> error;
error = vmm_draw(m_kernel);
error = vmm_draw(m_vmm);
if (error) {
m_kernel.free();
m_vmm.free();
QMessageBox::critical(
this,
@ -456,17 +456,26 @@ void MainWindow::updateScreen()
m_screen->requestUpdate();
}
void MainWindow::vmmError(const QString &msg)
{
m_vmm.free();
QMessageBox::critical(this, "Error", msg);
m_main->setCurrentIndex(0);
}
void MainWindow::waitingDebugger(const QString &addr)
{
QMessageBox::information(
this,
"Debug",
QString("The kernel are waiting for a debugger at %1.").arg(addr));
QString("The VMM are waiting for a debugger at %1.").arg(addr));
}
void MainWindow::waitKernelExit(bool success)
{
m_kernel.free();
m_vmm.free();
if (!success) {
QMessageBox::critical(
@ -555,32 +564,40 @@ void MainWindow::restoreGeometry()
}
}
bool MainWindow::requireEmulatorStopped()
bool MainWindow::requireVmmStopped()
{
if (m_kernel) {
QMessageBox killPrompt(this);
if (m_vmm) {
QMessageBox prompt(this);
killPrompt.setText("Action requires kernel to be stopped to continue.");
killPrompt.setInformativeText("Do you want to kill the kernel?");
killPrompt.setStandardButtons(QMessageBox::Cancel | QMessageBox::Yes);
killPrompt.setDefaultButton(QMessageBox::Cancel);
killPrompt.setIcon(QMessageBox::Warning);
if (killPrompt.exec() != QMessageBox::Yes) {
return true;
}
m_kernel.free();
}
prompt.setText("Action requires VMM to be stopped to continue.");
prompt.setInformativeText("Do you want to kill the VMM?");
prompt.setStandardButtons(QMessageBox::Cancel | QMessageBox::Yes);
prompt.setDefaultButton(QMessageBox::Cancel);
prompt.setIcon(QMessageBox::Warning);
if (prompt.exec() != QMessageBox::Yes) {
return false;
}
bool MainWindow::vmmHandler(const VmmEvent *ev, void *cx)
m_vmm.free();
}
return true;
}
void MainWindow::vmmHandler(const VmmEvent *ev, void *cx)
{
// This method will be called from non-main thread.
auto w = reinterpret_cast<MainWindow *>(cx);
switch (ev->tag) {
case VmmEvent_Error:
QMetaObject::invokeMethod(
w,
&MainWindow::vmmError,
Qt::QueuedConnection,
QString(error_message(ev->error.reason)));
break;
case VmmEvent_WaitingDebugger:
QMetaObject::invokeMethod(
w,
@ -604,6 +621,4 @@ bool MainWindow::vmmHandler(const VmmEvent *ev, void *cx)
QString::fromUtf8(ev->log.data, ev->log.len));
break;
}
return true;
}

View File

@ -38,16 +38,17 @@ private slots:
void reportIssue();
void aboutObliteration();
void saveProfile(Profile *p);
void startKernel(const QString &debugAddr);
void startVmm(const QString &debugAddr);
void updateScreen();
private:
void vmmError(const QString &msg);
void waitingDebugger(const QString &addr);
void waitKernelExit(bool success);
void log(VmmLog type, const QString &msg);
bool loadGame(const QString &gameId);
bool requireEmulatorStopped();
bool requireVmmStopped();
static bool vmmHandler(const VmmEvent *ev, void *cx);
static void vmmHandler(const VmmEvent *ev, void *cx);
QStackedWidget *m_main;
ProfileList *m_profiles;
@ -55,5 +56,5 @@ private:
LaunchSettings *m_launch;
Screen *m_screen;
QPointer<LogsViewer> m_logs;
Rust<Vmm> m_kernel; // Destroy first.
Rust<Vmm> m_vmm; // Destroy first.
};

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use std::error::Error;
use std::ffi::{c_char, CString};
use std::fmt::{Display, Write};
@ -18,11 +19,11 @@ pub struct RustError(CString);
impl RustError {
/// # Panics
/// If `msg` contains NUL character.
pub fn new(msg: impl Into<Vec<u8>>) -> *mut Self {
Box::into_raw(Self(CString::new(msg).unwrap()).into())
pub fn new(msg: impl Into<Vec<u8>>) -> Self {
Self(CString::new(msg).unwrap())
}
pub fn with_source(msg: impl Display, src: impl Error) -> *mut Self {
pub fn with_source(msg: impl Display, src: impl Error) -> Self {
let mut msg = format!("{} -> {}", msg, src);
let mut src = src.source();
@ -31,10 +32,10 @@ impl RustError {
src = e.source();
}
Box::into_raw(Self(CString::new(msg).unwrap()).into())
Self(CString::new(msg).unwrap())
}
pub fn wrap(src: impl Error) -> *mut Self {
pub fn wrap(src: impl Error) -> Self {
let mut msg = src.to_string();
let mut src = src.source();
@ -43,6 +44,10 @@ impl RustError {
src = e.source();
}
Box::into_raw(Self(CString::new(msg).unwrap()).into())
Self(CString::new(msg).unwrap())
}
pub fn into_c(self) -> *mut Self {
Box::into_raw(self.into())
}
}

View File

@ -11,7 +11,7 @@ pub unsafe extern "C" fn param_open(file: *const c_char, error: *mut *mut RustEr
let file = match File::open(CStr::from_ptr(file).to_str().unwrap()) {
Ok(v) => v,
Err(e) => {
*error = RustError::with_source("couldn't open the specified file", e);
*error = RustError::with_source("couldn't open the specified file", e).into_c();
return null_mut();
}
};
@ -20,7 +20,7 @@ pub unsafe extern "C" fn param_open(file: *const c_char, error: *mut *mut RustEr
let param = match Param::read(file) {
Ok(v) => v,
Err(e) => {
*error = RustError::with_source("couldn't read the specified file", e);
*error = RustError::with_source("couldn't read the specified file", e).into_c();
return null_mut();
}
};

View File

@ -12,7 +12,7 @@ pub unsafe extern "C" fn pkg_open(file: *const c_char, error: *mut *mut RustErro
let pkg = match Pkg::open(path.to_str().unwrap()) {
Ok(v) => Box::new(v),
Err(e) => {
*error = RustError::wrap(e);
*error = RustError::wrap(e).into_c();
return null_mut();
}
};
@ -30,7 +30,7 @@ pub unsafe extern "C" fn pkg_get_param(pkg: *const Pkg, error: *mut *mut RustErr
let param = match (*pkg).get_param() {
Ok(v) => Box::new(v),
Err(e) => {
*error = RustError::wrap(e);
*error = RustError::wrap(e).into_c();
return null_mut();
}
};
@ -56,7 +56,7 @@ pub unsafe extern "C" fn pkg_extract(
match (*pkg).extract(root, progress) {
Ok(_) => null_mut(),
Err(e) => RustError::wrap(e),
Err(e) => RustError::wrap(e).into_c(),
}
}

View File

@ -27,7 +27,7 @@ pub unsafe extern "C" fn profile_load(
let root = match CStr::from_ptr(path).to_str() {
Ok(v) => Path::new(v),
Err(_) => {
*err = RustError::new("the specified path is not UTF-8");
*err = RustError::new("the specified path is not UTF-8").into_c();
return null_mut();
}
};
@ -37,7 +37,8 @@ pub unsafe extern "C" fn profile_load(
let file = match File::open(&path) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source(format_args!("couldn't open {}", path.display()), e);
*err = RustError::with_source(format_args!("couldn't open {}", path.display()), e)
.into_c();
return null_mut();
}
};
@ -46,7 +47,8 @@ pub unsafe extern "C" fn profile_load(
let p = match ciborium::from_reader(file) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source(format_args!("couldn't load {}", path.display()), e);
*err = RustError::with_source(format_args!("couldn't load {}", path.display()), e)
.into_c();
return null_mut();
}
};
@ -84,12 +86,12 @@ pub unsafe extern "C" fn profile_save(p: *const Profile, path: *const c_char) ->
// Check if path UTF-8.
let root = match CStr::from_ptr(path).to_str() {
Ok(v) => Path::new(v),
Err(_) => return RustError::new("the specified path is not UTF-8"),
Err(_) => return RustError::new("the specified path is not UTF-8").into_c(),
};
// Create a directory.
if let Err(e) = std::fs::create_dir_all(root) {
return RustError::with_source("couldn't create the specified path", e);
return RustError::with_source("couldn't create the specified path", e).into_c();
}
// Create profile.bin.
@ -98,12 +100,14 @@ pub unsafe extern "C" fn profile_save(p: *const Profile, path: *const c_char) ->
Ok(v) => v,
Err(e) => {
return RustError::with_source(format_args!("couldn't create {}", path.display()), e)
.into_c()
}
};
// Write profile.bin.
if let Err(e) = ciborium::into_writer(&*p, file) {
return RustError::with_source(format_args!("couldn't write {}", path.display()), e);
return RustError::with_source(format_args!("couldn't write {}", path.display()), e)
.into_c();
}
null_mut()

View File

@ -48,17 +48,14 @@ impl<'a, H: Hypervisor> DeviceContext for Context<'a, H> {
// Trigger event.
let msg = std::mem::take(&mut self.msg);
let status = unsafe {
unsafe {
self.dev.event.invoke(VmmEvent::Log {
ty: ty.into(),
data: msg.as_ptr().cast(),
len: msg.len(),
})
};
if !status {
return Ok(false);
}
} else {
return Err(Box::new(ExecError::UnknownField(off)));
}

View File

@ -18,7 +18,6 @@ use std::net::TcpListener;
use std::num::NonZero;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::sync::Arc;
use std::thread::{sleep, JoinHandle};
use std::time::Duration;
@ -45,7 +44,7 @@ pub unsafe extern "C" fn vmm_run(
screen: *const VmmScreen,
profile: *const Profile,
debug: *const c_char,
event: unsafe extern "C" fn(*const VmmEvent, *mut c_void) -> bool,
event: unsafe extern "C" fn(*const VmmEvent, *mut c_void),
cx: *mut c_void,
err: *mut *mut RustError,
) -> *mut Vmm {
@ -57,7 +56,7 @@ pub unsafe extern "C" fn vmm_run(
let addr = match CStr::from_ptr(debug).to_str() {
Ok(v) => v,
Err(_) => {
*err = RustError::new("address to listen for a debugger is not UTF-8");
*err = RustError::new("address to listen for a debugger is not UTF-8").into_c();
return null_mut();
}
};
@ -66,13 +65,14 @@ pub unsafe extern "C" fn vmm_run(
let sock = match TcpListener::bind(addr) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't listen for a debugger", e);
*err = RustError::with_source("couldn't listen for a debugger", e).into_c();
return null_mut();
}
};
if let Err(e) = sock.set_nonblocking(true) {
*err = RustError::with_source("couldn't enable non-blocking on a debug server", e);
*err = RustError::with_source("couldn't enable non-blocking on a debug server", e)
.into_c();
return null_mut();
}
@ -84,7 +84,7 @@ pub unsafe extern "C" fn vmm_run(
let path = match CStr::from_ptr(kernel).to_str() {
Ok(v) => v,
Err(_) => {
*err = RustError::new("path of the kernel is not UTF-8");
*err = RustError::new("path of the kernel is not UTF-8").into_c();
return null_mut();
}
};
@ -93,7 +93,7 @@ pub unsafe extern "C" fn vmm_run(
let mut file = match Kernel::open(path) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source(format_args!("couldn't open {path}"), e);
*err = RustError::with_source(format_args!("couldn't open {path}"), e).into_c();
return null_mut();
}
};
@ -105,7 +105,8 @@ pub unsafe extern "C" fn vmm_run(
*err = RustError::with_source(
format_args!("couldn't start enumerating program headers of {path}"),
e,
);
)
.into_c();
return null_mut();
}
@ -124,7 +125,8 @@ pub unsafe extern "C" fn vmm_run(
*err = RustError::with_source(
format_args!("couldn't read program header #{index} on {path}"),
e,
);
)
.into_c();
return null_mut();
}
@ -134,7 +136,8 @@ pub unsafe extern "C" fn vmm_run(
match hdr.p_type {
PT_LOAD => {
if hdr.p_filesz > TryInto::<u64>::try_into(hdr.p_memsz).unwrap() {
*err = RustError::new(format!("invalid p_filesz on on PT_LOAD {index}"));
*err =
RustError::new(format!("invalid p_filesz on on PT_LOAD {index}")).into_c();
return null_mut();
}
@ -142,7 +145,7 @@ pub unsafe extern "C" fn vmm_run(
}
PT_DYNAMIC => {
if dynamic.is_some() {
*err = RustError::new("multiple PT_DYNAMIC is not supported");
*err = RustError::new("multiple PT_DYNAMIC is not supported").into_c();
return null_mut();
}
@ -150,7 +153,7 @@ pub unsafe extern "C" fn vmm_run(
}
PT_NOTE => {
if note.is_some() {
*err = RustError::new("multiple PT_NOTE is not supported");
*err = RustError::new("multiple PT_NOTE is not supported").into_c();
return null_mut();
}
@ -158,7 +161,8 @@ pub unsafe extern "C" fn vmm_run(
}
PT_PHDR | PT_GNU_EH_FRAME | PT_GNU_STACK | PT_GNU_RELRO => {}
v => {
*err = RustError::new(format!("unknown p_type {v} on program header {index}"));
*err = RustError::new(format!("unknown p_type {v} on program header {index}"))
.into_c();
return null_mut();
}
}
@ -170,12 +174,12 @@ pub unsafe extern "C" fn vmm_run(
match segments.first() {
Some(hdr) => {
if hdr.p_offset != 0 {
*err = RustError::new("the first PT_LOAD does not includes ELF header");
*err = RustError::new("the first PT_LOAD does not includes ELF header").into_c();
return null_mut();
}
}
None => {
*err = RustError::new("no any PT_LOAD on the kernel");
*err = RustError::new("no any PT_LOAD on the kernel").into_c();
return null_mut();
}
}
@ -184,7 +188,7 @@ pub unsafe extern "C" fn vmm_run(
let dynamic = match dynamic {
Some(v) => v,
None => {
*err = RustError::new("no PT_DYNAMIC segment on the kernel");
*err = RustError::new("no PT_DYNAMIC segment on the kernel").into_c();
return null_mut();
}
};
@ -193,7 +197,7 @@ pub unsafe extern "C" fn vmm_run(
let note = match note {
Some(v) => v,
None => {
*err = RustError::new("no PT_NOTE segment on the kernel");
*err = RustError::new("no PT_NOTE segment on the kernel").into_c();
return null_mut();
}
};
@ -202,7 +206,8 @@ pub unsafe extern "C" fn vmm_run(
let mut data = match file.segment_data(&note) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source(format_args!("couldn't seek to PT_NOTE on {path}"), e);
*err = RustError::with_source(format_args!("couldn't seek to PT_NOTE on {path}"), e)
.into_c();
return null_mut();
}
};
@ -220,7 +225,8 @@ pub unsafe extern "C" fn vmm_run(
let mut buf = [0u8; 4 * 3];
if let Err(e) = data.read_exact(&mut buf) {
*err = RustError::with_source(format_args!("couldn't read kernel note #{i} header"), e);
*err = RustError::with_source(format_args!("couldn't read kernel note #{i} header"), e)
.into_c();
return null_mut();
}
@ -234,12 +240,12 @@ pub unsafe extern "C" fn vmm_run(
let ty = u32::from_ne_bytes(buf[8..].try_into().unwrap());
if nlen > 0xff {
*err = RustError::new(format!("name on kernel note #{i} is too large"));
*err = RustError::new(format!("name on kernel note #{i} is too large")).into_c();
return null_mut();
}
if dlen > 0xff {
*err = RustError::new(format!("description on kernel note #{i} is too large"));
*err = RustError::new(format!("description on kernel note #{i} is too large")).into_c();
return null_mut();
}
@ -248,7 +254,8 @@ pub unsafe extern "C" fn vmm_run(
let mut buf = vec![0u8; nalign + dlen];
if let Err(e) = data.read_exact(&mut buf) {
*err = RustError::with_source(format_args!("couldn't read kernel note #{i} data"), e);
*err = RustError::with_source(format_args!("couldn't read kernel note #{i} data"), e)
.into_c();
return null_mut();
}
@ -256,7 +263,7 @@ pub unsafe extern "C" fn vmm_run(
let name = match CStr::from_bytes_until_nul(&buf) {
Ok(v) if v.to_bytes_with_nul().len() == nlen => v,
_ => {
*err = RustError::new(format!("kernel note #{i} has invalid name"));
*err = RustError::new(format!("kernel note #{i} has invalid name")).into_c();
return null_mut();
}
};
@ -269,7 +276,7 @@ pub unsafe extern "C" fn vmm_run(
match ty {
0 => {
if vm_page_size.is_some() {
*err = RustError::new(format!("kernel note #{i} is duplicated"));
*err = RustError::new(format!("kernel note #{i} is duplicated")).into_c();
return null_mut();
}
@ -281,12 +288,13 @@ pub unsafe extern "C" fn vmm_run(
.filter(|v| v.is_power_of_two());
if vm_page_size.is_none() {
*err = RustError::new(format!("invalid description on kernel note #{i}"));
*err =
RustError::new(format!("invalid description on kernel note #{i}")).into_c();
return null_mut();
}
}
v => {
*err = RustError::new(format!("unknown type {v} on kernel note #{i}"));
*err = RustError::new(format!("unknown type {v} on kernel note #{i}")).into_c();
return null_mut();
}
}
@ -296,7 +304,7 @@ pub unsafe extern "C" fn vmm_run(
let vm_page_size = match vm_page_size {
Some(v) => v,
None => {
*err = RustError::new("no page size in kernel note");
*err = RustError::new("no page size in kernel note").into_c();
return null_mut();
}
};
@ -305,7 +313,7 @@ pub unsafe extern "C" fn vmm_run(
let host_page_size = match get_page_size() {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't get host page size", e);
*err = RustError::with_source("couldn't get host page size", e).into_c();
return null_mut();
}
};
@ -318,7 +326,8 @@ pub unsafe extern "C" fn vmm_run(
*err = RustError::new(format!(
"PT_LOAD at {:#x} is overlapped with the previous PT_LOAD",
hdr.p_vaddr
));
))
.into_c();
return null_mut();
}
@ -326,7 +335,8 @@ pub unsafe extern "C" fn vmm_run(
len = match hdr.p_vaddr.checked_add(hdr.p_memsz) {
Some(v) => v,
None => {
*err = RustError::new(format!("invalid p_memsz on PT_LOAD at {:#x}", hdr.p_vaddr));
*err = RustError::new(format!("invalid p_memsz on PT_LOAD at {:#x}", hdr.p_vaddr))
.into_c();
return null_mut();
}
};
@ -336,13 +346,13 @@ pub unsafe extern "C" fn vmm_run(
let block_size = max(vm_page_size, host_page_size);
let len = match len {
0 => {
*err = RustError::new("the kernel has PT_LOAD with zero length");
*err = RustError::new("the kernel has PT_LOAD with zero length").into_c();
return null_mut();
}
v => match v.checked_next_multiple_of(block_size.get()) {
Some(v) => NonZero::new_unchecked(v),
None => {
*err = RustError::new("total size of PT_LOAD is too large");
*err = RustError::new("total size of PT_LOAD is too large").into_c();
return null_mut();
}
},
@ -352,7 +362,7 @@ pub unsafe extern "C" fn vmm_run(
let ram = match Ram::new(block_size) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't create a RAM", e);
*err = RustError::with_source("couldn't create a RAM", e).into_c();
return null_mut();
}
};
@ -361,7 +371,7 @@ pub unsafe extern "C" fn vmm_run(
let mut hv = match self::hv::new(8, ram) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't setup a hypervisor", e);
*err = RustError::with_source("couldn't setup a hypervisor", e).into_c();
return null_mut();
}
};
@ -370,7 +380,7 @@ pub unsafe extern "C" fn vmm_run(
let feats = match hv.cpu_features() {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't get available vCPU features", e);
*err = RustError::with_source("couldn't get available vCPU features", e).into_c();
return null_mut();
}
};
@ -380,7 +390,7 @@ pub unsafe extern "C" fn vmm_run(
let kern = match ram.alloc_kernel(len) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't allocate RAM for the kernel", e);
*err = RustError::with_source("couldn't allocate RAM for the kernel", e).into_c();
return null_mut();
}
};
@ -393,7 +403,8 @@ pub unsafe extern "C" fn vmm_run(
*err = RustError::with_source(
format_args!("couldn't seek to offset {}", hdr.p_offset),
e,
);
)
.into_c();
return null_mut();
}
@ -405,7 +416,7 @@ pub unsafe extern "C" fn vmm_run(
match std::io::copy(&mut data, &mut seg) {
Ok(v) => {
if v != hdr.p_filesz {
*err = RustError::new(format!("{path} is incomplete"));
*err = RustError::new(format!("{path} is incomplete")).into_c();
return null_mut();
}
}
@ -413,7 +424,8 @@ pub unsafe extern "C" fn vmm_run(
*err = RustError::with_source(
format_args!("couldn't read kernet at offset {}", hdr.p_offset),
e,
);
)
.into_c();
return null_mut();
}
@ -422,7 +434,7 @@ pub unsafe extern "C" fn vmm_run(
// Allocate stack.
if let Err(e) = ram.alloc_stack(NonZero::new(1024 * 1024 * 2).unwrap()) {
*err = RustError::with_source("couldn't allocate RAM for stack", e);
*err = RustError::with_source("couldn't allocate RAM for stack", e).into_c();
return null_mut();
}
@ -436,7 +448,7 @@ pub unsafe extern "C" fn vmm_run(
});
if let Err(e) = ram.alloc_args(env, (*profile).kernel_config().clone()) {
*err = RustError::with_source("couldn't allocate RAM for arguments", e);
*err = RustError::with_source("couldn't allocate RAM for arguments", e).into_c();
return null_mut();
}
@ -444,7 +456,7 @@ pub unsafe extern "C" fn vmm_run(
let map = match ram.build(&feats, vm_page_size, &devices, dynamic) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't build RAM", e);
*err = RustError::with_source("couldn't build RAM", e).into_c();
return null_mut();
}
};
@ -453,7 +465,7 @@ pub unsafe extern "C" fn vmm_run(
let screen = match self::screen::Default::new(&*screen) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't setup a screen", e);
*err = RustError::with_source("couldn't setup a screen", e).into_c();
return null_mut();
}
};
@ -471,32 +483,15 @@ pub unsafe extern "C" fn vmm_run(
// Spawn a thread to drive main CPU.
let e_entry = file.entry();
let (tx, rx) = std::sync::mpsc::channel();
let main = move || main_cpu(&args, e_entry, map, debug, tx);
let main = move || main_cpu(&args, e_entry, map, debug);
let main = match std::thread::Builder::new().spawn(main) {
Ok(v) => v,
Err(e) => {
*err = RustError::with_source("couldn't spawn main CPU", e);
*err = RustError::with_source("couldn't spawn main CPU", e).into_c();
return null_mut();
}
};
// Wait for main CPU to enter event loop.
let r = match rx.recv() {
Ok(v) => v,
Err(_) => {
main.join().unwrap();
*err = RustError::new("main CPU stopped unexpectedly");
return null_mut();
}
};
if let Err(e) = r {
main.join().unwrap();
*err = RustError::with_source("couldn't start main CPU", e);
return null_mut();
}
// Create VMM.
let vmm = Vmm {
cpus: vec![main],
@ -511,7 +506,7 @@ pub unsafe extern "C" fn vmm_run(
pub unsafe extern "C" fn vmm_draw(vmm: *mut Vmm) -> *mut RustError {
match (*vmm).screen.update() {
Ok(_) => null_mut(),
Err(e) => RustError::wrap(e),
Err(e) => RustError::wrap(e).into_c(),
}
}
@ -520,38 +515,31 @@ fn main_cpu<H: Hypervisor>(
entry: usize,
map: RamMap,
debug: Option<TcpListener>,
status: Sender<Result<(), MainCpuError>>,
) {
// Create vCPU.
let mut cpu = match args.hv.create_cpu(0) {
Ok(v) => v,
Err(e) => {
status
.send(Err(MainCpuError::CreateCpuFailed(Box::new(e))))
.unwrap();
let e = RustError::with_source("couldn't create main CPU", e);
unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
return;
}
};
if let Err(e) = self::arch::setup_main_cpu(&mut cpu, entry, map, &args.feats) {
status.send(Err(e)).unwrap();
let e = RustError::with_source("couldn't setup main CPU", e);
unsafe { args.event.invoke(VmmEvent::Error { reason: &e }) };
return;
}
// Enter dispatch loop.
status.send(Ok(())).unwrap();
drop(status);
// Wait for debugger.
if let Some(debug) = debug {
// Tell the user to connect a debugger.
let addr = debug.local_addr().unwrap().to_string();
let len = addr.len();
let addr = addr.as_ptr().cast();
if !unsafe { args.event.invoke(VmmEvent::WaitingDebugger { addr, len }) } {
// This shutdown was initiate on the GUI so just return is enough.
return;
}
unsafe { args.event.invoke(VmmEvent::WaitingDebugger { addr, len }) };
// Wait for a debugger.
loop {
@ -571,6 +559,7 @@ fn main_cpu<H: Hypervisor>(
}
}
// Enter dispatch loop.
run_cpu(cpu, args);
}
@ -685,12 +674,12 @@ pub struct VmmScreen {
/// Encapsulates a function to handle VMM events.
#[derive(Clone, Copy)]
struct VmmEventHandler {
fp: unsafe extern "C" fn(*const VmmEvent, *mut c_void) -> bool,
fp: unsafe extern "C" fn(*const VmmEvent, *mut c_void),
cx: *mut c_void,
}
impl VmmEventHandler {
unsafe fn invoke(self, e: VmmEvent) -> bool {
unsafe fn invoke(self, e: VmmEvent) {
(self.fp)(&e, self.cx)
}
}
@ -702,6 +691,9 @@ unsafe impl Sync for VmmEventHandler {}
#[repr(C)]
#[allow(dead_code)] // TODO: Figure out why Rust think fields in each enum are not used.
pub enum VmmEvent {
Error {
reason: *const RustError,
},
WaitingDebugger {
addr: *const c_char,
len: usize,
@ -856,9 +848,6 @@ enum VmmError {
/// Represents an error when [`main_cpu()`] fails to reach event loop.
#[derive(Debug, Error)]
enum MainCpuError {
#[error("couldn't create vCPU")]
CreateCpuFailed(#[source] Box<dyn Error + Send>),
#[error("couldn't get vCPU states")]
GetCpuStatesFailed(#[source] Box<dyn Error + Send>),