Extracts kernel into a separated process

This commit is contained in:
Putta Khunchalee 2022-10-06 21:49:39 +07:00
parent d5d7f8c167
commit 18bc1ad606
14 changed files with 148 additions and 123 deletions

View file

@ -0,0 +1 @@
This file used for detection of development binary in src/main_window.cpp.

View file

@ -1,5 +1,8 @@
{
"cmake.configureOnOpen": true,
"cmake.debugConfig": {
"cwd": "${workspaceFolder}"
},
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true

View file

@ -6,19 +6,22 @@ find_package(Qt6 COMPONENTS Widgets REQUIRED)
find_package(Threads REQUIRED)
# Setup Rust target.
set(RUST_OUTPUTS $<IF:$<CONFIG:Debug>,${CMAKE_CURRENT_SOURCE_DIR}/target/debug,${CMAKE_CURRENT_SOURCE_DIR}/target/release>)
set(KERNEL ${RUST_OUTPUTS}/obliteration-kernel${CMAKE_EXECUTABLE_SUFFIX})
if(WIN32)
set(LIBCORE $<IF:$<CONFIG:Debug>,${CMAKE_CURRENT_SOURCE_DIR}/target/debug/core.lib,${CMAKE_CURRENT_SOURCE_DIR}/target/release/core.lib>)
set(LIBCORE ${RUST_OUTPUTS}/core.lib)
else()
set(LIBCORE $<IF:$<CONFIG:Debug>,${CMAKE_CURRENT_SOURCE_DIR}/target/debug/libcore.a,${CMAKE_CURRENT_SOURCE_DIR}/target/release/libcore.a>)
set(LIBCORE ${RUST_OUTPUTS}/libcore.a)
endif()
ExternalProject_Add(libcore
ExternalProject_Add(core
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
CONFIGURE_COMMAND ""
BUILD_COMMAND cargo build $<IF:$<CONFIG:Debug>,-v,--release>
BUILD_IN_SOURCE ON
BUILD_ALWAYS ON
BUILD_BYPRODUCTS ${LIBCORE}
BUILD_BYPRODUCTS ${KERNEL} ${LIBCORE}
INSTALL_COMMAND ""
TEST_COMMAND cargo test
TEST_EXCLUDE_FROM_MAIN ON)
@ -40,7 +43,7 @@ if(WIN32)
target_sources(obliteration PRIVATE resources.rc)
endif()
add_dependencies(obliteration libcore)
add_dependencies(obliteration core)
set_target_properties(obliteration PROPERTIES AUTOMOC ON AUTORCC ON)
@ -67,4 +70,5 @@ else()
install(TARGETS obliteration DESTINATION bin)
endif()
install(PROGRAMS ${KERNEL} TYPE BIN)
install(SCRIPT post-install.cmake)

1
src/Cargo.lock generated
View file

@ -100,7 +100,6 @@ version = "0.1.0"
dependencies = [
"context",
"error",
"kernel",
"pkg",
]

View file

@ -9,4 +9,4 @@ members = [
"util"
]
default-members = ["core"]
default-members = ["core", "kernel"]

View file

@ -9,5 +9,4 @@ crate-type = ["staticlib"]
[dependencies]
context = { path = "../context" }
error = { path = "../error" }
kernel = { path = "../kernel" }
pkg = { path = "../pkg" }

View file

@ -3,5 +3,4 @@
// of those APIs here.
pub use context::*;
pub use error::*;
pub use kernel::*;
pub use pkg::*;

View file

@ -1,20 +0,0 @@
#pragma once
#include "error.hpp"
struct kernel;
struct kernel_rootfs;
struct kernel_pfs;
typedef void (*kernel_logger_t) (int pid, int err, const char *msg, void *ud);
extern "C" {
// The returned kernel will take the ownership of rootfs.
kernel *kernel_new(kernel_rootfs *rootfs, error **err);
void kernel_shutdown(kernel *krn);
void kernel_set_logger(kernel *krn, kernel_logger_t logger, void *ud);
kernel_rootfs *kernel_rootfs_new(error **err);
void kernel_rootfs_free(kernel_rootfs *fs);
}

View file

@ -3,6 +3,10 @@ name = "kernel"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "obliteration-kernel"
path = "src/main.rs"
[dependencies]
error = { path = "../error" }
memmap2 = "0.5"

View file

@ -1,39 +0,0 @@
use self::rootfs::RootFs;
use std::ffi::c_void;
use std::os::raw::{c_char, c_int};
pub mod fs;
pub mod rootfs;
pub type Logger = extern "C" fn(c_int, c_int, *const c_char, ud: *mut c_void);
#[no_mangle]
pub extern "C" fn kernel_new(
rootfs: *mut RootFs<'static>,
_: *mut *mut error::Error,
) -> *mut Kernel {
let krn = Box::new(Kernel {
rootfs: unsafe { *Box::from_raw(rootfs) },
logger: None,
});
Box::into_raw(krn)
}
#[no_mangle]
pub extern "C" fn kernel_shutdown(krn: *mut Kernel) {
unsafe { Box::from_raw(krn) };
}
#[no_mangle]
pub extern "C" fn kernel_set_logger(krn: &mut Kernel, logger: Option<Logger>, ud: *mut c_void) {
krn.logger = match logger {
Some(v) => Some((v, ud)),
None => None,
};
}
pub struct Kernel {
rootfs: RootFs<'static>,
logger: Option<(Logger, *mut c_void)>,
}

3
src/kernel/src/main.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
loop {}
}

View file

@ -4,6 +4,7 @@
#include "pkg.hpp"
#include "progress_dialog.hpp"
#include "settings.hpp"
#include "string.hpp"
#include "util.hpp"
#include <QAction>
@ -12,6 +13,7 @@
#include <QDesktopServices>
#include <QGuiApplication>
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QIcon>
#include <QListView>
@ -25,6 +27,8 @@
#include <QToolBar>
#include <QUrl>
#include <filesystem>
MainWindow::MainWindow(context *context) :
m_context(context),
m_tab(nullptr),
@ -147,9 +151,7 @@ void MainWindow::closeEvent(QCloseEvent *event)
return;
}
// Shutdown.
kernel_shutdown(m_kernel);
m_kernel = nullptr;
killKernel();
}
// Save gometry.
@ -293,44 +295,6 @@ void MainWindow::aboutObliteration()
QMessageBox::about(this, "About Obliteration", "Obliteration is a free and open-source software for playing your PlayStation 4 titles on PC.");
}
void MainWindow::startGame(const QModelIndex &index)
{
if (!requireEmulatorStopped()) {
return;
}
// Get target game.
auto model = reinterpret_cast<GameListModel *>(m_games->model());
auto game = model->get(index.row()); // Qt already guaranteed the index is valid.
// Setup rootfs.
Error error;
auto rootfs = kernel_rootfs_new(&error);
if (!rootfs) {
QMessageBox::critical(this, "Error", QString("Failed to create rootfs: %1").arg(error.message()));
return;
}
// Setup kernel.
m_kernel = kernel_new(rootfs, &error);
if (!m_kernel) {
kernel_rootfs_free(rootfs);
QMessageBox::critical(this, "Error", QString("Failed to create kernel: %1").arg(error.message()));
return;
}
kernel_set_logger(m_kernel, [](int pid, int err, const char *msg, void *ud) {
reinterpret_cast<MainWindow *>(ud)->appendLog(pid, err, msg);
}, this);
// Clear previous log and switch to log view.
m_log->clear();
m_tab->setCurrentIndex(1);
}
void MainWindow::requestGamesContextMenu(const QPoint &pos)
{
// Get item index.
@ -362,6 +326,107 @@ void MainWindow::requestGamesContextMenu(const QPoint &pos)
}
}
void MainWindow::startGame(const QModelIndex &index)
{
if (!requireEmulatorStopped()) {
return;
}
// Get target game.
auto model = reinterpret_cast<GameListModel *>(m_games->model());
auto game = model->get(index.row()); // Qt already guaranteed the index is valid.
// Clear previous log and switch to log view.
m_log->clear();
m_tab->setCurrentIndex(1);
// Get full path to kernel binary.
QString path;
if (QFile::exists(".obliteration-development")) {
auto b = std::filesystem::current_path();
b /= S("src");
b /= S("target");
#ifdef NDEBUG
b /= S("release");
#else
b /= S("debug");
#endif
#ifdef _WIN32
b /= L"obliteration-kernel.exe";
path = QString::fromStdWString(b.wstring());
#else
b /= "obliteration-kernel";
path = QString::fromStdString(b.string());
#endif
} else {
#ifdef _WIN32
std::filesystem::path b(QCoreApplication::applicationDirPath().toStdString(), std::filesystem::path::native_format);
b /= L"bin";
b /= L"obliteration-kernel.exe";
path = QString::fromStdWString(b.wstring());
#else
std::filesystem::path b(QCoreApplication::applicationDirPath().toStdString(), std::filesystem::path::native_format);
b /= "obliteration-kernel";
path = QString::fromStdString(b.string());
#endif
}
// Prepare kernel launching.
m_kernel = new QProcess(this);
m_kernel->setProgram(path);
m_kernel->setProcessChannelMode(QProcess::MergedChannels);
connect(m_kernel, &QProcess::errorOccurred, this, &MainWindow::kernelError);
connect(m_kernel, &QIODevice::readyRead, this, &MainWindow::kernelOutput);
connect(m_kernel, &QProcess::finished, this, &MainWindow::kernelTerminated);
// Launch kernel.
m_kernel->start(QIODeviceBase::ReadOnly | QIODeviceBase::Text);
}
void MainWindow::kernelError(QProcess::ProcessError error)
{
// Display error.
QString msg;
switch (error) {
case QProcess::FailedToStart:
msg = QString("Failed to launch %1.").arg(m_kernel->program());
break;
case QProcess::Crashed:
msg = "The kernel crashed.";
break;
default:
msg = "An unknown error occurred on the kernel";
}
QMessageBox::critical(this, "Error", msg);
// Destroy object.
m_kernel->deleteLater();
m_kernel = nullptr;
}
void MainWindow::kernelOutput()
{
while (m_kernel->canReadLine()) {
auto line = QString::fromUtf8(m_kernel->readLine());
m_log->appendPlainText(line);
}
}
void MainWindow::kernelTerminated(int, QProcess::ExitStatus)
{
kernelOutput();
m_kernel->deleteLater();
m_kernel = nullptr;
}
bool MainWindow::loadGame(const QString &gameId)
{
auto gamesDirectory = readGamesDirectorySetting();
@ -391,19 +456,16 @@ bool MainWindow::loadGame(const QString &gameId)
return true;
}
void MainWindow::appendLog(int pid, int err, const char *msg)
void MainWindow::killKernel()
{
m_log->appendHtml(QString("<strong>[PID:%1]:</strong> ").arg(pid));
// We need to disconnect all slots first otherwise the application will be freezing.
disconnect(m_kernel, nullptr, nullptr, nullptr);
if (err) {
m_log->appendHtml(R"(<span style="color:red">)");
}
m_kernel->kill();
m_kernel->waitForFinished(-1);
m_log->appendPlainText(msg);
if (err) {
m_log->appendHtml(R"(</span>)");
}
delete m_kernel;
m_kernel = nullptr;
}
void MainWindow::restoreGeometry()

View file

@ -1,9 +1,9 @@
#pragma once
#include "context.hpp"
#include "kernel.hpp"
#include <QMainWindow>
#include <QProcess>
class QListView;
class QPlainTextEdit;
@ -23,12 +23,15 @@ private slots:
void installPkg();
void reportIssue();
void aboutObliteration();
void startGame(const QModelIndex &index);
void requestGamesContextMenu(const QPoint &pos);
void startGame(const QModelIndex &index);
void kernelError(QProcess::ProcessError error);
void kernelOutput();
void kernelTerminated(int exitCode, QProcess::ExitStatus exitStatus);
private:
bool loadGame(const QString &gameId);
void appendLog(int pid, int err, const char *msg);
void killKernel();
void restoreGeometry();
bool requireEmulatorStopped();
@ -37,5 +40,5 @@ private:
QTabWidget *m_tab;
QListView *m_games;
QPlainTextEdit *m_log;
kernel *m_kernel;
QProcess *m_kernel;
};

7
src/string.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#ifdef _WIN32
#define S(x) L##x
#else
#define S(x) x
#endif