mirror of
https://github.com/obhq/obliteration.git
synced 2024-10-06 16:03:22 +00:00
Revises console (#946)
This commit is contained in:
parent
c44798de47
commit
0f85fbb946
2
.github/workflows/ci-linux.yml
vendored
2
.github/workflows/ci-linux.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install Flatpak runtimes
|
||||
run: |
|
||||
flatpak install --noninteractive flathub \
|
||||
org.kde.Platform//6.6 org.kde.Sdk//6.6 \
|
||||
org.kde.Platform//6.7 org.kde.Sdk//6.7 \
|
||||
org.freedesktop.Sdk.Extension.rust-stable//23.08
|
||||
if: ${{ steps.flatpak-runtime.outputs.cache-hit != 'true' }}
|
||||
- name: Restore build files
|
||||
|
6
.github/workflows/ci-mac.yml
vendored
6
.github/workflows/ci-mac.yml
vendored
@ -21,9 +21,9 @@ jobs:
|
||||
name: ${{ inputs.name }}
|
||||
runs-on: ${{ inputs.macos }}
|
||||
env:
|
||||
CMAKE_PREFIX_PATH: qt/6.6.0/macos
|
||||
QT_URL_BASE: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_660/qt.qt6.660.clang_64/6.6.0-0-202310040910qtbase-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z
|
||||
QT_URL_SVG: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_660/qt.qt6.660.clang_64/6.6.0-0-202310040910qtsvg-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z
|
||||
CMAKE_PREFIX_PATH: qt/6.7.2/macos
|
||||
QT_URL_BASE: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_672/qt.qt6.672.clang_64/6.7.2-0-202406110330qtbase-MacOS-MacOS_13-Clang-MacOS-MacOS_13-X86_64-ARM64.7z
|
||||
QT_URL_SVG: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_672/qt.qt6.672.clang_64/6.7.2-0-202406110330qtsvg-MacOS-MacOS_13-Clang-MacOS-MacOS_13-X86_64-ARM64.7z
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v4
|
||||
|
@ -1,7 +1,7 @@
|
||||
app-id: io.github.obhq.Obliteration
|
||||
default-branch: stable
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: '6.6'
|
||||
runtime-version: '6.7'
|
||||
platform-extensions:
|
||||
- org.freedesktop.Platform.GL.default
|
||||
sdk: org.kde.Sdk
|
||||
|
35
src/core.h
35
src/core.h
@ -13,6 +13,13 @@
|
||||
struct Param;
|
||||
struct Pkg;
|
||||
|
||||
/**
|
||||
* Log category.
|
||||
*/
|
||||
enum VmmLog {
|
||||
VmmLog_Info,
|
||||
};
|
||||
|
||||
/**
|
||||
* Error object managed by Rust side.
|
||||
*/
|
||||
@ -41,6 +48,26 @@ struct VmmScreen {
|
||||
;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains VMM event information.
|
||||
*/
|
||||
enum VmmEvent_Tag {
|
||||
VmmEvent_Log,
|
||||
};
|
||||
|
||||
struct VmmEvent_Log_Body {
|
||||
enum VmmLog ty;
|
||||
const char *data;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct VmmEvent {
|
||||
enum VmmEvent_Tag tag;
|
||||
union {
|
||||
struct VmmEvent_Log_Body log;
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
@ -89,12 +116,14 @@ struct RustError *update_firmware(const char *root,
|
||||
|
||||
void vmm_free(struct Vmm *vmm);
|
||||
|
||||
struct Vmm *vmm_run(const char *kernel, const struct VmmScreen *screen, struct RustError **err);
|
||||
struct Vmm *vmm_run(const char *kernel,
|
||||
const struct VmmScreen *screen,
|
||||
bool (*event)(const struct VmmEvent*, void*),
|
||||
void *cx,
|
||||
struct RustError **err);
|
||||
|
||||
struct RustError *vmm_draw(struct Vmm *vmm);
|
||||
|
||||
void vmm_logs(const struct Vmm *vmm, void *cx, void (*cb)(uint8_t, const char*, size_t, void*));
|
||||
|
||||
#if defined(__linux__)
|
||||
extern int kvm_check_version(int kvm, bool *compat);
|
||||
#endif
|
||||
|
@ -40,6 +40,7 @@ fn main() {
|
||||
conf.export
|
||||
.rename
|
||||
.insert("KvmTranslation".into(), "kvm_translation".into());
|
||||
conf.enumeration.prefix_with_name = true;
|
||||
conf.defines
|
||||
.insert("target_os = linux".into(), "__linux__".into());
|
||||
conf.defines
|
||||
|
@ -1,7 +1,8 @@
|
||||
use super::{Console, Log};
|
||||
use super::Console;
|
||||
use crate::vmm::hv::{CpuIo, IoBuf};
|
||||
use crate::vmm::hw::{DeviceContext, Ram};
|
||||
use obvirt::console::{Commit, Memory};
|
||||
use crate::vmm::VmmEvent;
|
||||
use obvirt::console::{Memory, MsgType};
|
||||
use std::error::Error;
|
||||
use std::mem::offset_of;
|
||||
use thiserror::Error;
|
||||
@ -10,8 +11,6 @@ use thiserror::Error;
|
||||
pub struct Context<'a> {
|
||||
dev: &'a Console,
|
||||
ram: &'a Ram,
|
||||
file_len: usize,
|
||||
file: String,
|
||||
msg_len: usize,
|
||||
msg: String,
|
||||
}
|
||||
@ -21,14 +20,12 @@ impl<'a> Context<'a> {
|
||||
Self {
|
||||
dev,
|
||||
ram,
|
||||
file_len: 0,
|
||||
file: String::new(),
|
||||
msg_len: 0,
|
||||
msg: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u32(&self, off: usize, exit: &mut dyn CpuIo) -> Result<u32, ExecError> {
|
||||
fn read_u8(&self, off: usize, exit: &mut dyn CpuIo) -> Result<u8, ExecError> {
|
||||
// Get data.
|
||||
let data = match exit.buffer() {
|
||||
IoBuf::Write(v) => v,
|
||||
@ -36,9 +33,11 @@ impl<'a> Context<'a> {
|
||||
};
|
||||
|
||||
// Parse data.
|
||||
data.try_into()
|
||||
.map(|v| u32::from_ne_bytes(v))
|
||||
.map_err(|_| ExecError::InvalidData(off))
|
||||
if data.len() != 1 {
|
||||
Err(ExecError::InvalidData(off))
|
||||
} else {
|
||||
Ok(data[0])
|
||||
}
|
||||
}
|
||||
|
||||
fn read_usize(&self, off: usize, exit: &mut dyn CpuIo) -> Result<usize, ExecError> {
|
||||
@ -84,7 +83,7 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
|
||||
impl<'a> DeviceContext for Context<'a> {
|
||||
fn exec(&mut self, exit: &mut dyn CpuIo) -> Result<(), Box<dyn Error>> {
|
||||
fn exec(&mut self, exit: &mut dyn CpuIo) -> Result<bool, Box<dyn Error>> {
|
||||
// Check field.
|
||||
let off = exit.addr() - self.dev.addr;
|
||||
|
||||
@ -92,36 +91,34 @@ impl<'a> DeviceContext for Context<'a> {
|
||||
self.msg_len = self.read_usize(off, exit)?;
|
||||
} else if off == offset_of!(Memory, msg_addr) {
|
||||
self.msg.push_str(self.read_str(off, exit, self.msg_len)?);
|
||||
} else if off == offset_of!(Memory, file_len) {
|
||||
self.file_len = self.read_usize(off, exit)?;
|
||||
} else if off == offset_of!(Memory, file_addr) {
|
||||
self.file = self.read_str(off, exit, self.file_len)?.to_owned();
|
||||
} else if off == offset_of!(Memory, commit) {
|
||||
// Parse data.
|
||||
let commit = self.read_u32(off, exit)?;
|
||||
let (ty, line) = match Commit::parse(commit) {
|
||||
let commit = self.read_u8(off, exit)?;
|
||||
let ty = match MsgType::from_u8(commit) {
|
||||
Some(v) => v,
|
||||
None => return Err(Box::new(ExecError::InvalidCommit(commit))),
|
||||
};
|
||||
|
||||
// Push log.
|
||||
let mut logs = self.dev.logs.lock().unwrap();
|
||||
// Trigger event.
|
||||
let msg = std::mem::take(&mut self.msg);
|
||||
let status = match ty {
|
||||
MsgType::Info => unsafe {
|
||||
self.dev.event.invoke(VmmEvent::Log {
|
||||
ty: ty.into(),
|
||||
data: msg.as_ptr().cast(),
|
||||
len: msg.len(),
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
logs.push_back(Log {
|
||||
ty,
|
||||
file: std::mem::take(&mut self.file),
|
||||
line,
|
||||
msg: std::mem::take(&mut self.msg),
|
||||
});
|
||||
|
||||
while logs.len() > 10000 {
|
||||
logs.pop_front();
|
||||
if !status {
|
||||
return Ok(false);
|
||||
}
|
||||
} else {
|
||||
return Err(Box::new(ExecError::UnknownField(off)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,5 +138,5 @@ enum ExecError {
|
||||
TranslateVaddrFailed(usize, #[source] Box<dyn Error>),
|
||||
|
||||
#[error("{0:#} is not a valid commit")]
|
||||
InvalidCommit(u32),
|
||||
InvalidCommit(u8),
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
use self::context::Context;
|
||||
use super::{Device, DeviceContext, Ram};
|
||||
use obvirt::console::{Memory, MsgType};
|
||||
use std::collections::VecDeque;
|
||||
use crate::vmm::VmmEventHandler;
|
||||
use obvirt::console::Memory;
|
||||
use std::num::NonZero;
|
||||
use std::sync::Mutex;
|
||||
|
||||
mod context;
|
||||
|
||||
@ -11,11 +10,11 @@ mod context;
|
||||
pub struct Console {
|
||||
addr: usize,
|
||||
len: NonZero<usize>,
|
||||
logs: Mutex<VecDeque<Log>>,
|
||||
event: VmmEventHandler,
|
||||
}
|
||||
|
||||
impl Console {
|
||||
pub fn new(addr: usize, vm_page_size: NonZero<usize>) -> Self {
|
||||
pub fn new(addr: usize, vm_page_size: NonZero<usize>, event: VmmEventHandler) -> Self {
|
||||
let len = size_of::<Memory>()
|
||||
.checked_next_multiple_of(vm_page_size.get())
|
||||
.and_then(NonZero::new)
|
||||
@ -23,11 +22,7 @@ impl Console {
|
||||
|
||||
addr.checked_add(len.get()).unwrap();
|
||||
|
||||
Self {
|
||||
addr,
|
||||
len,
|
||||
logs: Mutex::default(),
|
||||
}
|
||||
Self { addr, len, event }
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,11 +39,3 @@ impl Device for Console {
|
||||
Box::new(Context::new(self, ram))
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains data for each logging entry.
|
||||
struct Log {
|
||||
ty: MsgType,
|
||||
file: String,
|
||||
line: u32,
|
||||
msg: String,
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use super::VmmEventHandler;
|
||||
use crate::vmm::hv::CpuIo;
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error;
|
||||
@ -10,12 +11,16 @@ pub use self::ram::*;
|
||||
mod console;
|
||||
mod ram;
|
||||
|
||||
pub fn setup_devices(start_addr: usize, vm_page_size: NonZero<usize>) -> DeviceTree {
|
||||
pub fn setup_devices(
|
||||
start_addr: usize,
|
||||
vm_page_size: NonZero<usize>,
|
||||
event: VmmEventHandler,
|
||||
) -> DeviceTree {
|
||||
let mut map = BTreeMap::<usize, Arc<dyn Device>>::new();
|
||||
|
||||
// Console.
|
||||
let addr = start_addr;
|
||||
let console = Arc::new(Console::new(addr, vm_page_size));
|
||||
let console = Arc::new(Console::new(addr, vm_page_size, event));
|
||||
|
||||
assert!(map.insert(console.addr(), console.clone()).is_none());
|
||||
|
||||
@ -60,5 +65,5 @@ pub trait Device: Send + Sync {
|
||||
|
||||
/// Context to execute memory-mapped I/O operations on a virtual device.
|
||||
pub trait DeviceContext {
|
||||
fn exec(&mut self, exit: &mut dyn CpuIo) -> Result<(), Box<dyn Error>>;
|
||||
fn exec(&mut self, exit: &mut dyn CpuIo) -> Result<bool, Box<dyn Error>>;
|
||||
}
|
||||
|
@ -5,16 +5,15 @@ use self::screen::Screen;
|
||||
use crate::error::RustError;
|
||||
use obconf::{BootEnv, Vm};
|
||||
use obvirt::console::MsgType;
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error;
|
||||
use std::ffi::{c_char, c_void, CStr};
|
||||
use std::io::Read;
|
||||
use std::num::NonZero;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use std::thread::JoinHandle;
|
||||
use thiserror::Error;
|
||||
|
||||
@ -32,6 +31,8 @@ pub unsafe extern "C" fn vmm_free(vmm: *mut Vmm) {
|
||||
pub unsafe extern "C" fn vmm_run(
|
||||
kernel: *const c_char,
|
||||
screen: *const VmmScreen,
|
||||
event: unsafe extern "C" fn(*const VmmEvent, *mut c_void) -> bool,
|
||||
cx: *mut c_void,
|
||||
err: *mut *mut RustError,
|
||||
) -> *mut Vmm {
|
||||
// Check if path UTF-8.
|
||||
@ -359,7 +360,8 @@ pub unsafe extern "C" fn vmm_run(
|
||||
}
|
||||
|
||||
// Allocate arguments.
|
||||
let devices = Arc::new(setup_devices(Ram::SIZE, vm_page_size));
|
||||
let event = VmmEventHandler { fp: event, cx };
|
||||
let devices = Arc::new(setup_devices(Ram::SIZE, vm_page_size, event));
|
||||
let env = BootEnv::Vm(Vm {
|
||||
console: devices.console().addr(),
|
||||
});
|
||||
@ -398,7 +400,6 @@ pub unsafe extern "C" fn vmm_run(
|
||||
};
|
||||
|
||||
// Setup arguments for main CPU.
|
||||
let logs = Arc::new(Mutex::new(VecDeque::new()));
|
||||
let shutdown = Arc::new(AtomicBool::new(false));
|
||||
let args = CpuArgs {
|
||||
hv,
|
||||
@ -440,7 +441,6 @@ pub unsafe extern "C" fn vmm_run(
|
||||
let vmm = Vmm {
|
||||
cpus: vec![main],
|
||||
screen,
|
||||
logs,
|
||||
shutdown,
|
||||
};
|
||||
|
||||
@ -455,19 +455,6 @@ pub unsafe extern "C" fn vmm_draw(vmm: *mut Vmm) -> *mut RustError {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn vmm_logs(
|
||||
vmm: *const Vmm,
|
||||
cx: *mut c_void,
|
||||
cb: unsafe extern "C" fn(u8, *const c_char, usize, *mut c_void),
|
||||
) {
|
||||
let logs = (*vmm).logs.lock().unwrap();
|
||||
|
||||
for (ty, msg) in logs.deref() {
|
||||
cb(*ty as u8, msg.as_ptr().cast(), msg.len(), cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn main_cpu(args: &CpuArgs, entry: usize, map: RamMap, status: Sender<Result<(), MainCpuError>>) {
|
||||
// Create vCPU.
|
||||
let mut cpu = match args.hv.create_cpu(0) {
|
||||
@ -616,7 +603,13 @@ fn run_cpu(mut cpu: impl Cpu, args: &CpuArgs) {
|
||||
// Check if I/O.
|
||||
match exit.into_io() {
|
||||
Ok(io) => match exec_io(&mut devices, io) {
|
||||
Ok(_) => continue,
|
||||
Ok(status) => {
|
||||
if !status {
|
||||
args.shutdown.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
},
|
||||
Err(_) => todo!(),
|
||||
@ -627,7 +620,7 @@ fn run_cpu(mut cpu: impl Cpu, args: &CpuArgs) {
|
||||
fn exec_io<'a>(
|
||||
devices: &mut BTreeMap<usize, (Box<dyn DeviceContext + 'a>, NonZero<usize>)>,
|
||||
mut io: impl CpuIo,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
) -> Result<bool, Box<dyn Error>> {
|
||||
// Get target device.
|
||||
let addr = io.addr();
|
||||
let (_, (dev, end)) = devices.range_mut(..=addr).last().unwrap();
|
||||
@ -663,7 +656,6 @@ fn get_page_size() -> Result<NonZero<usize>, std::io::Error> {
|
||||
pub struct Vmm {
|
||||
cpus: Vec<JoinHandle<()>>,
|
||||
screen: self::screen::Default,
|
||||
logs: Arc<Mutex<VecDeque<(MsgType, String)>>>,
|
||||
shutdown: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
@ -689,6 +681,48 @@ pub struct VmmScreen {
|
||||
pub view: usize,
|
||||
}
|
||||
|
||||
/// Encapsulates a function to handle VMM events.
|
||||
#[derive(Clone, Copy)]
|
||||
struct VmmEventHandler {
|
||||
fp: unsafe extern "C" fn(*const VmmEvent, *mut c_void) -> bool,
|
||||
cx: *mut c_void,
|
||||
}
|
||||
|
||||
impl VmmEventHandler {
|
||||
unsafe fn invoke(self, e: VmmEvent) -> bool {
|
||||
(self.fp)(&e, self.cx)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for VmmEventHandler {}
|
||||
unsafe impl Sync for VmmEventHandler {}
|
||||
|
||||
/// Contains VMM event information.
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)] // TODO: Figure out why Rust think fields in each enum are not used.
|
||||
pub enum VmmEvent {
|
||||
Log {
|
||||
ty: VmmLog,
|
||||
data: *const c_char,
|
||||
len: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// Log category.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum VmmLog {
|
||||
Info,
|
||||
}
|
||||
|
||||
impl From<MsgType> for VmmLog {
|
||||
fn from(value: MsgType) -> Self {
|
||||
match value {
|
||||
MsgType::Info => Self::Info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encapsulates arguments for a function to run a CPU.
|
||||
struct CpuArgs {
|
||||
hv: Arc<self::hv::Default>,
|
||||
|
11
src/main.cpp
11
src/main.cpp
@ -1,4 +1,3 @@
|
||||
#include "core.hpp"
|
||||
#include "initialize_wizard.hpp"
|
||||
#include "main_window.hpp"
|
||||
#include "settings.hpp"
|
||||
@ -10,6 +9,7 @@
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaObject>
|
||||
#include <QThread>
|
||||
#ifndef __APPLE__
|
||||
#include <QVersionNumber>
|
||||
#include <QVulkanInstance>
|
||||
@ -27,14 +27,19 @@ static void panicHook(
|
||||
size_t mlen,
|
||||
void *cx)
|
||||
{
|
||||
QMetaObject::invokeMethod(reinterpret_cast<QObject *>(cx), [=]() {
|
||||
auto main = reinterpret_cast<QObject *>(cx);
|
||||
auto type = QThread::currentThread() == main->thread()
|
||||
? Qt::DirectConnection
|
||||
: Qt::BlockingQueuedConnection;
|
||||
|
||||
QMetaObject::invokeMethod(main, [=]() {
|
||||
auto text = QString("An unexpected error occurred at %1:%2: %3")
|
||||
.arg(QString::fromUtf8(file, flen))
|
||||
.arg(line)
|
||||
.arg(QString::fromUtf8(msg, mlen));
|
||||
|
||||
QMessageBox::critical(nullptr, "Fatal Error", text);
|
||||
});
|
||||
}, type);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -30,6 +30,7 @@
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
#include <string.h>
|
||||
@ -234,25 +235,14 @@ void MainWindow::openSystemFolder()
|
||||
|
||||
void MainWindow::viewLogs()
|
||||
{
|
||||
// Check for previous window.
|
||||
if (m_logs) {
|
||||
m_logs->activateWindow();
|
||||
m_logs->raise();
|
||||
return;
|
||||
} else if (!m_kernel) {
|
||||
QMessageBox::information(this, "Information", "The kernel is not running.");
|
||||
return;
|
||||
} else {
|
||||
m_logs = new LogsViewer();
|
||||
m_logs->setAttribute(Qt::WA_DeleteOnClose);
|
||||
m_logs->show();
|
||||
}
|
||||
|
||||
// Create a window.
|
||||
m_logs = new LogsViewer();
|
||||
|
||||
vmm_logs(m_kernel, m_logs.get(), [](uint8_t, const char *msg, size_t len, void *cx) {
|
||||
reinterpret_cast<LogsViewer *>(cx)->append(QString::fromUtf8(msg, len));
|
||||
});
|
||||
|
||||
m_logs->setAttribute(Qt::WA_DeleteOnClose);
|
||||
m_logs->show();
|
||||
}
|
||||
|
||||
void MainWindow::reportIssue()
|
||||
@ -332,7 +322,7 @@ void MainWindow::startKernel()
|
||||
}
|
||||
#endif
|
||||
|
||||
vmm = vmm_run(kernel.c_str(), &screen, &error);
|
||||
vmm = vmm_run(kernel.c_str(), &screen, MainWindow::vmmHandler, this, &error);
|
||||
|
||||
if (!vmm) {
|
||||
m_main->setCurrentIndex(0);
|
||||
@ -373,6 +363,19 @@ void MainWindow::updateScreen()
|
||||
m_screen->requestUpdate();
|
||||
}
|
||||
|
||||
void MainWindow::log(VmmLog type, const QString &msg)
|
||||
{
|
||||
if (m_logs) {
|
||||
m_logs->append(msg);
|
||||
} else {
|
||||
switch (type) {
|
||||
case VmmLog_Info:
|
||||
std::cout << msg.toStdString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::loadGame(const QString &gameId)
|
||||
{
|
||||
auto gamesDirectory = readGamesDirectorySetting();
|
||||
@ -453,3 +456,21 @@ bool MainWindow::requireEmulatorStopped()
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MainWindow::vmmHandler(const VmmEvent *ev, void *cx)
|
||||
{
|
||||
auto w = reinterpret_cast<MainWindow *>(cx);
|
||||
|
||||
switch (ev->tag) {
|
||||
case VmmEvent_Log:
|
||||
QMetaObject::invokeMethod(
|
||||
w,
|
||||
&MainWindow::log,
|
||||
Qt::QueuedConnection,
|
||||
ev->log.ty,
|
||||
QString::fromUtf8(ev->log.data, ev->log.len));
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -37,14 +37,17 @@ private slots:
|
||||
void updateScreen();
|
||||
|
||||
private:
|
||||
void log(VmmLog type, const QString &msg);
|
||||
bool loadGame(const QString &gameId);
|
||||
void restoreGeometry();
|
||||
bool requireEmulatorStopped();
|
||||
|
||||
static bool vmmHandler(const VmmEvent *ev, void *cx);
|
||||
|
||||
QStackedWidget *m_main;
|
||||
GameListModel *m_games;
|
||||
LaunchSettings *m_launch;
|
||||
Screen *m_screen;
|
||||
QPointer<LogsViewer> m_logs;
|
||||
Rust<Vmm> m_kernel;
|
||||
Rust<Vmm> m_kernel; // Destroy first.
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anstyle = { version = "1.0.8", default-features = false }
|
||||
macros = { path = "../macros" }
|
||||
obconf = { path = "../obconf" }
|
||||
obvirt = { path = "../obvirt" }
|
||||
|
@ -1,16 +1,17 @@
|
||||
use crate::config::boot_env;
|
||||
use core::fmt::Arguments;
|
||||
use anstyle::{AnsiColor, Color, Style};
|
||||
use core::fmt::{Arguments, Display, Formatter};
|
||||
use obconf::BootEnv;
|
||||
use obvirt::console::MsgType;
|
||||
|
||||
mod vm;
|
||||
|
||||
/// Write single line of information log.
|
||||
/// Write information log.
|
||||
///
|
||||
/// When running inside a VM each call will cause a VM to exit multiple times so don't do this in a
|
||||
/// performance critical path.
|
||||
///
|
||||
/// The line should not contains LF character.
|
||||
/// The LF character will be automatically appended.
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($args:tt)*) => {
|
||||
@ -18,9 +19,37 @@ macro_rules! info {
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn info(file: &str, line: u32, msg: Arguments) {
|
||||
let log = Log {
|
||||
style: Style::new().fg_color(Some(Color::Ansi(AnsiColor::BrightCyan))),
|
||||
cat: 'I',
|
||||
file,
|
||||
line,
|
||||
msg,
|
||||
};
|
||||
|
||||
match boot_env() {
|
||||
BootEnv::Vm(env) => self::vm::print(env, MsgType::Info, file, line, msg),
|
||||
BootEnv::Vm(env) => self::vm::print(env, MsgType::Info, log),
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Display`] implementation to format each log.
|
||||
struct Log<'a> {
|
||||
style: Style,
|
||||
cat: char,
|
||||
file: &'a str,
|
||||
line: u32,
|
||||
msg: Arguments<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Display for Log<'a> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"{}++++++++++++++++++ {} {}:{}{0:#}",
|
||||
self.style, self.cat, self.file, self.line
|
||||
)?;
|
||||
self.msg.fmt(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
use core::fmt::{Arguments, Write};
|
||||
use core::fmt::{Display, Write};
|
||||
use core::ptr::{addr_of_mut, write_volatile};
|
||||
use obconf::Vm;
|
||||
use obvirt::console::{Commit, Memory, MsgType};
|
||||
use obvirt::console::{Memory, MsgType};
|
||||
|
||||
pub fn print(env: &Vm, ty: MsgType, file: &str, line: u32, msg: Arguments) {
|
||||
pub fn print(env: &Vm, ty: MsgType, msg: impl Display) {
|
||||
let c = env.console as *mut Memory;
|
||||
let mut w = Writer(c);
|
||||
|
||||
unsafe { write_volatile(addr_of_mut!((*c).file_len), file.len()) };
|
||||
unsafe { write_volatile(addr_of_mut!((*c).file_addr), file.as_ptr() as usize) };
|
||||
writeln!(w, "{msg}").unwrap();
|
||||
|
||||
Writer(c).write_fmt(msg).unwrap();
|
||||
|
||||
unsafe { write_volatile(addr_of_mut!((*c).commit), Commit::new(ty, line)) };
|
||||
unsafe { write_volatile(addr_of_mut!((*c).commit), ty) };
|
||||
}
|
||||
|
||||
struct Writer(*mut Memory);
|
||||
|
@ -2,41 +2,14 @@
|
||||
///
|
||||
/// The sequence of operations on a console memory is per-cpu. The kernel will start each log by:
|
||||
///
|
||||
/// 1. Write [`Self::file_len`] then [`Self::file_addr`].
|
||||
/// 2. Write [`Self::msg_len`] then [`Self::msg_addr`].
|
||||
/// 3. Repeat step 2 until the whole message has been written.
|
||||
/// 4. Write [`Self::commit`].
|
||||
/// 1. Write [`Self::msg_len`] then [`Self::msg_addr`].
|
||||
/// 2. Repeat step 1 until the whole message has been written.
|
||||
/// 3. Write [`Self::commit`].
|
||||
#[repr(C)]
|
||||
pub struct Memory {
|
||||
pub file_len: usize,
|
||||
pub file_addr: usize,
|
||||
pub msg_len: usize,
|
||||
pub msg_addr: usize,
|
||||
pub commit: Commit,
|
||||
}
|
||||
|
||||
/// Struct to commit a log.
|
||||
#[repr(transparent)]
|
||||
pub struct Commit(u32);
|
||||
|
||||
impl Commit {
|
||||
/// # Panics
|
||||
/// If `line` greater than 0xffffff.
|
||||
pub fn new(ty: MsgType, line: u32) -> Self {
|
||||
assert!(line <= 0xffffff);
|
||||
|
||||
Self((ty as u32) << 24 | line)
|
||||
}
|
||||
|
||||
pub fn parse(raw: u32) -> Option<(MsgType, u32)> {
|
||||
let line = raw & 0xffffff;
|
||||
let ty = match (raw >> 24) as u8 {
|
||||
v if v == MsgType::Info as u8 => MsgType::Info,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some((ty, line))
|
||||
}
|
||||
pub commit: MsgType,
|
||||
}
|
||||
|
||||
/// Type of console message.
|
||||
@ -45,3 +18,14 @@ impl Commit {
|
||||
pub enum MsgType {
|
||||
Info,
|
||||
}
|
||||
|
||||
impl MsgType {
|
||||
pub fn from_u8(v: u8) -> Option<Self> {
|
||||
let v = match v {
|
||||
v if v == MsgType::Info as u8 => MsgType::Info,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user