add stdin/out/err as virtual devices

This commit is contained in:
Vinicius Rangel 2024-11-27 16:18:50 -03:00
parent b9c63b4ae3
commit d0f168b0e5
No known key found for this signature in database
GPG Key ID: A5B154D904B761D9
9 changed files with 226 additions and 38 deletions

View File

@ -499,6 +499,10 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/crypto/keys.h
src/core/devices/base_device.cpp
src/core/devices/base_device.h
src/core/devices/ioccom.h
src/core/devices/logger.cpp
src/core/devices/logger.h
src/core/devices/nop_device.h
src/core/file_format/pfs.h
src/core/file_format/pkg.cpp
src/core/file_format/pkg.h

View File

@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/kernel/file_system.h"
#include "logger.h"
namespace Core::Devices {
Logger::Logger(std::string prefix, bool is_err) : prefix(std::move(prefix)), is_err(is_err) {}
Logger::~Logger() = default;
s64 Logger::write(const void* buf, size_t nbytes) {
log(static_cast<const char*>(buf), nbytes);
return nbytes;
}
size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
for (int i = 0; i < iovcnt; i++) {
log(static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
}
return iovcnt;
}
s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) {
log(static_cast<const char*>(buf), nbytes);
return nbytes;
}
s32 Logger::fsync() {
log_flush();
return 0;
}
void Logger::log(const char* buf, size_t nbytes) {
std::scoped_lock lock{mtx};
const char* end = buf + nbytes;
for (const char* it = buf; it < end; ++it) {
char c = *it;
if (c == '\r') {
continue;
}
if (c == '\n') {
log_flush();
continue;
}
buffer.push_back(c);
}
}
void Logger::log_flush() {
std::scoped_lock lock{mtx};
if (buffer.empty()) {
return;
}
if (is_err) {
LOG_ERROR(Tty, "[{}] {}", prefix, std::string_view{buffer});
} else {
LOG_INFO(Tty, "[{}] {}", prefix, std::string_view{buffer});
}
buffer.clear();
}
} // namespace Core::Devices

37
src/core/devices/logger.h Normal file
View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "base_device.h"
#include <mutex>
#include <string>
#include <vector>
namespace Core::Devices {
class Logger final : BaseDevice {
std::string prefix;
bool is_err;
std::recursive_mutex mtx;
std::vector<char> buffer;
public:
explicit Logger(std::string prefix, bool is_err);
~Logger() override;
s64 write(const void* buf, size_t nbytes) override;
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
s32 fsync() override;
private:
void log(const char* buf, size_t nbytes);
void log_flush();
};
} // namespace Core::Devices

View File

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "base_device.h"
namespace Core::Devices {
class NopDevice final : BaseDevice {
u32 handle;
public:
explicit NopDevice(u32 handle) : handle(handle) {}
~NopDevice() override = default;
int ioctl(u64 cmd, Common::VaCtx* args) override {
return 0;
}
s64 write(const void* buf, size_t nbytes) override {
return 0;
}
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
return 0;
}
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
return 0;
}
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override {
return 0;
}
s64 lseek(s64 offset, int whence) override {
return 0;
}
s64 read(void* buf, size_t nbytes) override {
return 0;
}
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override {
return 0;
}
s32 fsync() override {
return 0;
}
int ftruncate(s64 length) override {
return 0;
}
int getdents(void* buf, u32 nbytes, s64* basep) override {
return 0;
}
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override {
return 0;
}
};
} // namespace Core::Devices

View File

@ -4,12 +4,12 @@
#include <algorithm>
#include "common/config.h"
#include "common/string_util.h"
#include "core/devices/logger.h"
#include "core/devices/nop_device.h"
#include "core/file_sys/fs.h"
namespace Core::FileSys {
constexpr int RESERVED_HANDLES = 3; // First 3 handles are stdin,stdout,stderr
void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder,
bool read_only) {
std::scoped_lock lock{m_mutex};
@ -142,23 +142,23 @@ int HandleTable::CreateHandle() {
for (int index = 0; index < existingFilesNum; index++) {
if (m_files.at(index) == nullptr) {
m_files[index] = file;
return index + RESERVED_HANDLES;
return index;
}
}
m_files.push_back(file);
return m_files.size() + RESERVED_HANDLES - 1;
return m_files.size() - 1;
}
void HandleTable::DeleteHandle(int d) {
std::scoped_lock lock{m_mutex};
delete m_files.at(d - RESERVED_HANDLES);
m_files[d - RESERVED_HANDLES] = nullptr;
delete m_files.at(d);
m_files[d] = nullptr;
}
File* HandleTable::GetFile(int d) {
std::scoped_lock lock{m_mutex};
return m_files.at(d - RESERVED_HANDLES);
return m_files.at(d);
}
File* HandleTable::GetFile(const std::filesystem::path& host_name) {
@ -170,4 +170,20 @@ File* HandleTable::GetFile(const std::filesystem::path& host_name) {
return nullptr;
}
void HandleTable::CreateStdHandles() {
auto setup = [this](const char* path, auto* device) {
int fd = CreateHandle();
auto* file = GetFile(fd);
file->is_opened = true;
file->type = FileType::Device;
file->m_guest_name = path;
file->device =
std::shared_ptr<Devices::BaseDevice>{reinterpret_cast<Devices::BaseDevice*>(device)};
};
// order matters
setup("/dev/stdin", new Devices::NopDevice(0)); // stdin
setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout
setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr
}
} // namespace Core::FileSys

View File

@ -71,7 +71,7 @@ struct File {
std::vector<DirEntry> dirents;
u32 dirents_index;
std::mutex m_mutex;
std::unique_ptr<Devices::BaseDevice> device; // only valid for type == Device
std::shared_ptr<Devices::BaseDevice> device; // only valid for type == Device
};
class HandleTable {
@ -84,6 +84,8 @@ public:
File* GetFile(int d);
File* GetFile(const std::filesystem::path& host_name);
void CreateStdHandles();
private:
std::vector<File*> m_files;
std::mutex m_mutex;

View File

@ -14,10 +14,35 @@
#include <map>
#include <ranges>
using FactoryDevice = std::function<Core::Devices::BaseDevice*(u32, const char*, int, u16)>;
#include "core/devices/logger.h"
#include "core/devices/nop_device.h"
// prefix path
static std::map<std::string, FactoryDevice> available_device = {};
namespace D = Core::Devices;
using FactoryDevice = std::function<std::shared_ptr<D::BaseDevice>(u32, const char*, int, u16)>;
#define GET_DEVICE_FD(fd) \
[](u32, const char*, int, u16) { \
return Common::Singleton<Core::FileSys::HandleTable>::Instance()->GetFile(fd)->device; \
}
// prefix path, only dev devices
static std::map<std::string, FactoryDevice> available_device = {
// clang-format off
{"/dev/stdin", GET_DEVICE_FD(0)},
{"/dev/stdout", GET_DEVICE_FD(1)},
{"/dev/stderr", GET_DEVICE_FD(2)},
{"/dev/fd/0", GET_DEVICE_FD(0)},
{"/dev/fd/1", GET_DEVICE_FD(1)},
{"/dev/fd/2", GET_DEVICE_FD(2)},
{"/dev/deci_stdin", GET_DEVICE_FD(0)},
{"/dev/deci_stdout", GET_DEVICE_FD(1)},
{"/dev/deci_stderr", GET_DEVICE_FD(2)},
{"/dev/null", GET_DEVICE_FD(0)}, // fd0 (stdin) is a nop device
// clang-format on
};
namespace Libraries::Kernel {
@ -60,9 +85,6 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
if (path == "/dev/deci_tty6") {
return 2001;
}
if (path == "/dev/stdout") {
return 2002;
}
if (path == "/dev/urandom") {
return 2003;
}
@ -70,16 +92,17 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
u32 handle = h->CreateHandle();
auto* file = h->GetFile(handle);
if (path.starts_with("/dev/")) {
for (const auto& [prefix, factory] : available_device) {
if (path.starts_with(prefix)) {
file->is_opened = true;
file->type = Core::FileSys::FileType::Device;
file->m_guest_name = path;
file->device = std::unique_ptr<Core::Devices::BaseDevice>{
factory(handle, path.data(), flags, mode)};
file->device = factory(handle, path.data(), flags, mode);
return handle;
}
}
}
if (directory) {
file->type = Core::FileSys::FileType::Directory;
@ -179,14 +202,6 @@ int PS4_SYSV_ABI posix_close(int d) {
}
size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) {
if (d <= 2) { // stdin,stdout,stderr
char* str = strdup((const char*)buf);
if (str[nbytes - 1] == '\n')
str[nbytes - 1] = 0;
LOG_INFO(Tty, "{}", str);
free(str);
return nbytes;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
if (file == nullptr) {

View File

@ -143,14 +143,6 @@ void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
}
s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) {
if (d <= 2) { // stdin,stdout,stderr
std::string_view str{buf};
if (str[nbytes - 1] == '\n') {
str = str.substr(0, nbytes - 1);
}
LOG_INFO(Tty, "{}", str);
return nbytes;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
if (file == nullptr) {

View File

@ -75,6 +75,9 @@ Emulator::Emulator() {
LOG_INFO(Config, "Vulkan rdocMarkersEnable: {}", Config::vkMarkersEnabled());
LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::vkCrashDiagnosticEnabled());
// Create stdin/stdout/stderr
Common::Singleton<FileSys::HandleTable>::Instance()->CreateStdHandles();
// Defer until after logging is initialized.
memory = Core::Memory::Instance();
controller = Common::Singleton<Input::GameController>::Instance();