mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-04 08:22:32 +00:00
add stdin/out/err as virtual devices
This commit is contained in:
parent
b9c63b4ae3
commit
d0f168b0e5
@ -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
|
||||
|
64
src/core/devices/logger.cpp
Normal file
64
src/core/devices/logger.cpp
Normal 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
37
src/core/devices/logger.h
Normal 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
|
55
src/core/devices/nop_device.h
Normal file
55
src/core/devices/nop_device.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,14 +92,15 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
|
||||
u32 handle = h->CreateHandle();
|
||||
auto* file = h->GetFile(handle);
|
||||
|
||||
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)};
|
||||
return 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 = factory(handle, path.data(), flags, mode);
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user