Update Logger to use NDK Logger APIs + Improve Backing API + Fix FDSAN issues

This commit is contained in:
◱ PixelyIon 2020-09-29 18:16:17 +05:30 committed by ◱ PixelyIon
parent 4070686897
commit 97ac45d83b
26 changed files with 116 additions and 140 deletions

View File

@ -4,6 +4,7 @@
#include <csignal> #include <csignal>
#include <unistd.h> #include <unistd.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <android/log.h>
#include "skyline/loader/loader.h" #include "skyline/loader/loader.h"
#include "skyline/common.h" #include "skyline/common.h"
#include "skyline/os.h" #include "skyline/os.h"
@ -12,24 +13,18 @@
bool Halt; bool Halt;
jobject Surface; jobject Surface;
uint FaultCount;
skyline::GroupMutex JniMtx; skyline::GroupMutex JniMtx;
skyline::u16 fps; skyline::u16 fps;
skyline::u32 frametime; skyline::u32 frametime;
std::weak_ptr<skyline::input::Input> inputWeak; std::weak_ptr<skyline::input::Input> inputWeak;
void signalHandler(int signal) { void signalHandler(int signal) {
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal)); __android_log_print(ANDROID_LOG_FATAL, "emu-cpp", "Halting program due to signal: %s", strsignal(signal));
if (FaultCount > 2) exit(signal);
exit(SIGKILL);
else
Halt = true;
FaultCount++;
} }
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) { extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) {
Halt = false; Halt = false;
FaultCount = 0;
fps = 0; fps = 0;
frametime = 0; frametime = 0;
@ -44,6 +39,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto jvmManager{std::make_shared<skyline::JvmManager>(env, instance)}; auto jvmManager{std::make_shared<skyline::JvmManager>(env, instance)};
auto settings{std::make_shared<skyline::Settings>(preferenceFd)}; auto settings{std::make_shared<skyline::Settings>(preferenceFd)};
close(preferenceFd);
auto appFilesPath{env->GetStringUTFChars(appFilesPathJstring, nullptr)}; auto appFilesPath{env->GetStringUTFChars(appFilesPathJstring, nullptr)};
auto logger{std::make_shared<skyline::Logger>(std::string(appFilesPath) + "skyline.log", static_cast<skyline::Logger::LogLevel>(std::stoi(settings->GetString("log_level"))))}; auto logger{std::make_shared<skyline::Logger>(std::string(appFilesPath) + "skyline.log", static_cast<skyline::Logger::LogLevel>(std::stoi(settings->GetString("log_level"))))};
@ -74,6 +70,8 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto end{std::chrono::steady_clock::now()}; auto end{std::chrono::steady_clock::now()};
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count())); logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
close(romFd);
} }
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setHalt(JNIEnv *, jobject, jboolean halt) { extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setHalt(JNIEnv *, jobject, jboolean halt) {

View File

@ -2,6 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <tinyxml2.h> #include <tinyxml2.h>
#include <android/log.h>
#include "common.h" #include "common.h"
#include "nce.h" #include "nce.h"
#include "gpu.h" #include "gpu.h"
@ -68,11 +69,12 @@ namespace skyline {
Settings::Settings(int fd) { Settings::Settings(int fd) {
tinyxml2::XMLDocument pref; tinyxml2::XMLDocument pref;
if (pref.LoadFile(fdopen(fd, "r"))) auto fileDeleter = [](FILE *file) { fclose(file); };
std::unique_ptr<FILE, decltype(fileDeleter)> file{fdopen(fd, "r"), fileDeleter};
if (pref.LoadFile(file.get()))
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr())); throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
tinyxml2::XMLElement *elem{pref.LastChild()->FirstChild()->ToElement()}; tinyxml2::XMLElement *elem{pref.LastChild()->FirstChild()->ToElement()};
while (elem) { while (elem) {
switch (elem->Value()[0]) { switch (elem->Value()[0]) {
case 's': case 's':
@ -88,7 +90,7 @@ namespace skyline {
break; break;
default: default:
syslog(LOG_ALERT, "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value()); __android_log_print(ANDROID_LOG_WARN, "emu-cpp", "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
break; break;
}; };
@ -122,7 +124,7 @@ namespace skyline {
} }
Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) { Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) {
logFile.open(path, std::ios::app); logFile.open(path, std::ios::trunc);
WriteHeader("Logging started"); WriteHeader("Logging started");
} }
@ -132,14 +134,17 @@ namespace skyline {
} }
void Logger::WriteHeader(const std::string &str) { void Logger::WriteHeader(const std::string &str) {
syslog(LOG_ALERT, "%s", str.c_str()); __android_log_write(ANDROID_LOG_INFO, "emu-cpp", str.c_str());
std::lock_guard guard(mtx); std::lock_guard guard(mtx);
logFile << "0|" << str << "\n"; logFile << "0|" << str << "\n";
} }
void Logger::Write(LogLevel level, std::string str) { void Logger::Write(LogLevel level, std::string str) {
syslog(levelSyslog[static_cast<u8>(level)], "%s", str.c_str()); constexpr std::array<char, 4> levelCharacter{'0', '1', '2', '3'}; // The LogLevel as written out to a file
constexpr std::array<int, 4> levelAlog{ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG}; // This corresponds to LogLevel and provides it's equivalent for NDK Logging
__android_log_write(levelAlog[static_cast<u8>(level)], "emu-cpp", str.c_str());
for (auto &character : str) for (auto &character : str)
if (character == '\n') if (character == '\n')

View File

@ -17,7 +17,6 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <memory> #include <memory>
#include <syslog.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <frozen/unordered_map.h> #include <frozen/unordered_map.h>
@ -366,8 +365,6 @@ namespace skyline {
class Logger { class Logger {
private: private:
std::ofstream logFile; //!< An output stream to the log file std::ofstream logFile; //!< An output stream to the log file
std::array<char, 4> levelCharacter{'0', '1', '2', '3'}; //!< The LogLevel as written out to a file
static constexpr std::array<int, 4> levelSyslog{LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG}; //!< This corresponds to LogLevel and provides it's equivalent for syslog
Mutex mtx; //!< A mutex to lock before logging anything Mutex mtx; //!< A mutex to lock before logging anything
public: public:
@ -377,6 +374,7 @@ namespace skyline {
Info, Info,
Debug, Debug,
}; };
LogLevel configLevel; //!< The minimum level of logs to write LogLevel configLevel; //!< The minimum level of logs to write
/** /**
@ -457,9 +455,9 @@ namespace skyline {
*/ */
class Settings { class Settings {
private: private:
std::map<std::string, std::string> stringMap; //!< A mapping from all keys to their corresponding string value std::unordered_map<std::string, std::string> stringMap; //!< A mapping from all keys to their corresponding string value
std::map<std::string, bool> boolMap; //!< A mapping from all keys to their corresponding boolean value std::unordered_map<std::string, bool> boolMap; //!< A mapping from all keys to their corresponding boolean value
std::map<std::string, int> intMap; //!< A mapping from all keys to their corresponding integer value std::unordered_map<std::string, int> intMap; //!< A mapping from all keys to their corresponding integer value
public: public:
/** /**

View File

@ -15,7 +15,7 @@ namespace skyline::crypto {
void KeyStore::ReadPairs(const std::shared_ptr<vfs::Backing> &backing, ReadPairsCallback callback) { void KeyStore::ReadPairs(const std::shared_ptr<vfs::Backing> &backing, ReadPairsCallback callback) {
std::vector<char> fileContent(backing->size); std::vector<char> fileContent(backing->size);
backing->Read(fileContent.data(), 0, fileContent.size()); backing->Read(span(fileContent).cast<u8>());
auto lineStart{fileContent.begin()}; auto lineStart{fileContent.begin()};
std::vector<char>::iterator lineEnd; std::vector<char>::iterator lineEnd;

View File

@ -28,14 +28,14 @@ namespace skyline::crypto {
std::map<Key128, Key128> titleKeys; std::map<Key128, Key128> titleKeys;
std::unordered_map<std::string_view, std::optional<Key256> &> key256Names{ std::unordered_map<std::string_view, std::optional<Key256> &> key256Names{
{"header_key", headerKey} {"header_key", headerKey},
}; };
std::unordered_map<std::string_view, IndexedKeys128 &> indexedKey128Names{ std::unordered_map<std::string_view, IndexedKeys128 &> indexedKey128Names{
{"titlekek_", titleKek}, {"titlekek_", titleKek},
{"key_area_key_application_", areaKeyApplication}, {"key_area_key_application_", areaKeyApplication},
{"key_area_key_ocean_", areaKeyOcean}, {"key_area_key_ocean_", areaKeyOcean},
{"key_area_key_system_", areaKeySystem} {"key_area_key_system_", areaKeySystem},
}; };
using ReadPairsCallback = void (skyline::crypto::KeyStore::*)(std::string_view, std::string_view); using ReadPairsCallback = void (skyline::crypto::KeyStore::*)(std::string_view, std::string_view);

View File

@ -16,8 +16,8 @@ namespace skyline::kernel::type {
class KSession : public KSyncObject { class KSession : public KSyncObject {
public: public:
std::shared_ptr<service::BaseService> serviceObject; std::shared_ptr<service::BaseService> serviceObject;
std::unordered_map<KHandle, std::shared_ptr<service::BaseService>> domainTable; //!< A map from a virtual handle to it's service std::vector<std::shared_ptr<service::BaseService>> domains; //!< A vector of services that correspond to virtual handles
KHandle handleIndex{0x1}; //!< The currently allocated handle index KHandle handleIndex{}; //!< The currently allocated handle index
bool isOpen{true}; //!< If the session is open or not bool isOpen{true}; //!< If the session is open or not
bool isDomain{}; //!< If this is a domain session or not bool isDomain{}; //!< If this is a domain session or not
@ -33,7 +33,7 @@ namespace skyline::kernel::type {
*/ */
KHandle ConvertDomain() { KHandle ConvertDomain() {
isDomain = true; isDomain = true;
domainTable[handleIndex] = serviceObject; domains.push_back(serviceObject);
return handleIndex++; return handleIndex++;
} }
}; };

View File

@ -10,14 +10,14 @@
namespace skyline::loader { namespace skyline::loader {
NroLoader::NroLoader(const std::shared_ptr<vfs::Backing> &backing) : backing(backing) { NroLoader::NroLoader(const std::shared_ptr<vfs::Backing> &backing) : backing(backing) {
backing->Read(&header); header = backing->Read<NroHeader>();
if (header.magic != util::MakeMagic<u32>("NRO0")) if (header.magic != util::MakeMagic<u32>("NRO0"))
throw exception("Invalid NRO magic! 0x{0:X}", header.magic); throw exception("Invalid NRO magic! 0x{0:X}", header.magic);
// The homebrew asset section is appended to the end of an NRO file // The homebrew asset section is appended to the end of an NRO file
if (backing->size > header.size) { if (backing->size > header.size) {
backing->Read(&assetHeader, header.size); assetHeader = backing->Read<NroAssetHeader>(header.size);
if (assetHeader.magic != util::MakeMagic<u32>("ASET")) if (assetHeader.magic != util::MakeMagic<u32>("ASET"))
throw exception("Invalid ASET magic! 0x{0:X}", assetHeader.magic); throw exception("Invalid ASET magic! 0x{0:X}", assetHeader.magic);
@ -34,14 +34,14 @@ namespace skyline::loader {
NroAssetSection &segmentHeader{assetHeader.icon}; NroAssetSection &segmentHeader{assetHeader.icon};
std::vector<u8> buffer(segmentHeader.size); std::vector<u8> buffer(segmentHeader.size);
backing->Read(buffer.data(), header.size + segmentHeader.offset, segmentHeader.size); backing->Read(buffer, header.size + segmentHeader.offset);
return buffer; return buffer;
} }
std::vector<u8> NroLoader::GetSegment(const NroSegmentHeader &segment) { std::vector<u8> NroLoader::GetSegment(const NroSegmentHeader &segment) {
std::vector<u8> buffer(segment.size); std::vector<u8> buffer(segment.size);
backing->Read(buffer.data(), segment.offset, segment.size); backing->Read(buffer, segment.offset);
return buffer; return buffer;
} }

View File

@ -9,8 +9,7 @@
namespace skyline::loader { namespace skyline::loader {
NsoLoader::NsoLoader(const std::shared_ptr<vfs::Backing> &backing) : backing(backing) { NsoLoader::NsoLoader(const std::shared_ptr<vfs::Backing> &backing) : backing(backing) {
u32 magic{}; u32 magic{backing->Read<u32>()};
backing->Read(&magic);
if (magic != util::MakeMagic<u32>("NSO0")) if (magic != util::MakeMagic<u32>("NSO0"))
throw exception("Invalid NSO magic! 0x{0:X}", magic); throw exception("Invalid NSO magic! 0x{0:X}", magic);
@ -21,19 +20,18 @@ namespace skyline::loader {
if (compressedSize) { if (compressedSize) {
std::vector<u8> compressedBuffer(compressedSize); std::vector<u8> compressedBuffer(compressedSize);
backing->Read(compressedBuffer.data(), segment.fileOffset, compressedSize); backing->Read(compressedBuffer, segment.fileOffset);
LZ4_decompress_safe(reinterpret_cast<char *>(compressedBuffer.data()), reinterpret_cast<char *>(outputBuffer.data()), compressedSize, segment.decompressedSize); LZ4_decompress_safe(reinterpret_cast<char *>(compressedBuffer.data()), reinterpret_cast<char *>(outputBuffer.data()), compressedSize, segment.decompressedSize);
} else { } else {
backing->Read(outputBuffer.data(), segment.fileOffset, segment.decompressedSize); backing->Read(outputBuffer, segment.fileOffset);
} }
return outputBuffer; return outputBuffer;
} }
Loader::ExecutableLoadInfo NsoLoader::LoadNso(const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state, size_t offset) { Loader::ExecutableLoadInfo NsoLoader::LoadNso(const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state, size_t offset) {
NsoHeader header{}; auto header{backing->Read<NsoHeader>()};
backing->Read(&header);
if (header.magic != util::MakeMagic<u32>("NSO0")) if (header.magic != util::MakeMagic<u32>("NSO0"))
throw exception("Invalid NSO magic! 0x{0:X}", header.magic); throw exception("Invalid NSO magic! 0x{0:X}", header.magic);

View File

@ -57,8 +57,7 @@ namespace skyline::loader {
return std::vector<u8>(); return std::vector<u8>();
std::vector<u8> buffer(icon->size); std::vector<u8> buffer(icon->size);
icon->Read(buffer);
icon->Read(buffer.data(), 0, icon->size);
return buffer; return buffer;
} }
} }

View File

@ -23,7 +23,7 @@ namespace skyline::service::fssrv {
return result::InvalidSize; return result::InvalidSize;
} }
response.Push<u32>(static_cast<u32>(backing->Read(request.outputBuf.at(0).data(), offset, size))); response.Push<u32>(static_cast<u32>(backing->Read(request.outputBuf.at(0), offset)));
return {}; return {};
} }
@ -48,7 +48,7 @@ namespace skyline::service::fssrv {
return result::InvalidSize; return result::InvalidSize;
} }
if (backing->Write(request.inputBuf.at(0).data(), offset, request.inputBuf.at(0).size()) != size) { if (backing->Write(request.inputBuf.at(0), offset) != size) {
state.logger->Warn("Failed to write all data to the backing"); state.logger->Warn("Failed to write all data to the backing");
return result::UnexpectedFailure; return result::UnexpectedFailure;
} }

View File

@ -21,7 +21,7 @@ namespace skyline::service::fssrv {
return result::InvalidSize; return result::InvalidSize;
} }
backing->Read(request.outputBuf.at(0).data(), offset, size); backing->Read(request.outputBuf.at(0), offset);
return {}; return {};
} }

View File

@ -10,7 +10,9 @@ namespace skyline::service::nvdrv {
constexpr u32 VBlank0SyncpointId{26}; constexpr u32 VBlank0SyncpointId{26};
constexpr u32 VBlank1SyncpointId{27}; constexpr u32 VBlank1SyncpointId{27};
// Reserve both vblank syncpoints as client managed since the userspace driver has direct access to them // Reserve both vblank syncpoints as client managed as they use Continuous Mode
// Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode
// https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660
ReserveSyncpoint(VBlank0SyncpointId, true); ReserveSyncpoint(VBlank0SyncpointId, true);
ReserveSyncpoint(VBlank1SyncpointId, true); ReserveSyncpoint(VBlank1SyncpointId, true);
} }
@ -20,7 +22,7 @@ namespace skyline::service::nvdrv {
throw exception("Requested syncpoint is in use"); throw exception("Requested syncpoint is in use");
syncpoints.at(id).reserved = true; syncpoints.at(id).reserved = true;
syncpoints.at(id).clientManaged = clientManaged; syncpoints.at(id).interfaceManaged = clientManaged;
return id; return id;
} }
@ -44,7 +46,8 @@ namespace skyline::service::nvdrv {
if (!syncpoint.reserved) if (!syncpoint.reserved)
throw exception("Cannot check the expiry status of an unreserved syncpoint!"); throw exception("Cannot check the expiry status of an unreserved syncpoint!");
if (syncpoint.clientManaged) // If the interface manages counters then we don't keep track of the maximum value as it handles sanity checking the values then
if (syncpoint.interfaceManaged)
return static_cast<i32>(syncpoint.counterMin - threshold) >= 0; return static_cast<i32>(syncpoint.counterMin - threshold) >= 0;
else else
return (syncpoint.counterMax - threshold) >= (syncpoint.counterMin - threshold); return (syncpoint.counterMax - threshold) >= (syncpoint.counterMin - threshold);

View File

@ -7,16 +7,17 @@
namespace skyline::service::nvdrv { namespace skyline::service::nvdrv {
/** /**
* @brief NvHostSyncpoint handles allocating and accessing host1x syncpoints * @brief NvHostSyncpoint handles allocating and accessing host1x syncpoints, these are cached versions of the HW syncpoints which are intermittently synced
* @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them
* @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html
* @url https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c * @url https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c
*/ */
class NvHostSyncpoint { class NvHostSyncpoint {
private: private:
struct SyncpointInfo { struct SyncpointInfo {
std::atomic<u32> counterMin; //!< The least value the syncpoint can be (The value it was when it was last synchronized with the GPU for host managed syncpoints) std::atomic<u32> counterMin; //!< The least value the syncpoint can be (The value it was when it was last synchronized with host1x)
std::atomic<u32> counterMax; //!< The maximum value the syncpoint can reach according to the current usage std::atomic<u32> counterMax; //!< The maximum value the syncpoint can reach according to the current usage
bool clientManaged; //!< If the syncpoint is managed by the client (CPU) or host (GPU), it determines if the value is absolute or not bool interfaceManaged; //!< If the syncpoint is managed by a host1x client interface, a client interface is a HW block that can handle host1x transactions on behalf of a host1x client (Which would otherwise need to be manually synced using PIO which is synchronous and requires direct cooperation of the CPU)
bool reserved; bool reserved;
}; };

View File

@ -89,9 +89,9 @@ namespace skyline::service {
auto serviceObject{CreateService(name)}; auto serviceObject{CreateService(name)};
KHandle handle{}; KHandle handle{};
if (session.isDomain) { if (session.isDomain) {
session.domainTable[++session.handleIndex] = serviceObject; session.domains.push_back(serviceObject);
response.domainObjects.push_back(session.handleIndex); response.domainObjects.push_back(session.handleIndex);
handle = session.handleIndex; handle = session.handleIndex++;
} else { } else {
handle = state.process->NewHandle<type::KSession>(serviceObject).handle; handle = state.process->NewHandle<type::KSession>(serviceObject).handle;
response.moveHandles.push_back(handle); response.moveHandles.push_back(handle);
@ -105,7 +105,7 @@ namespace skyline::service {
KHandle handle{}; KHandle handle{};
if (session.isDomain) { if (session.isDomain) {
session.domainTable[session.handleIndex] = serviceObject; session.domains.push_back(serviceObject);
response.domainObjects.push_back(session.handleIndex); response.domainObjects.push_back(session.handleIndex);
handle = session.handleIndex++; handle = session.handleIndex++;
} else { } else {
@ -121,9 +121,9 @@ namespace skyline::service {
auto session{state.process->GetHandle<type::KSession>(handle)}; auto session{state.process->GetHandle<type::KSession>(handle)};
if (session->isOpen) { if (session->isOpen) {
if (session->isDomain) { if (session->isDomain) {
for (const auto &domainEntry : session->domainTable) for (const auto &domainService : session->domains)
std::erase_if(serviceMap, [domainEntry](const auto &entry) { std::erase_if(serviceMap, [domainService](const auto &entry) {
return entry.second == domainEntry.second; return entry.second == domainService;
}); });
} else { } else {
std::erase_if(serviceMap, [session](const auto &entry) { std::erase_if(serviceMap, [session](const auto &entry) {
@ -148,7 +148,9 @@ namespace skyline::service {
case ipc::CommandType::RequestWithContext: case ipc::CommandType::RequestWithContext:
if (session->isDomain) { if (session->isDomain) {
try { try {
auto service{session->domainTable.at(request.domain->objectId)}; auto service{session->domains.at(request.domain->objectId)};
if (service == nullptr)
throw exception("Domain request used an expired handle");
switch (request.domain->command) { switch (request.domain->command) {
case ipc::DomainCommand::SendMessage: case ipc::DomainCommand::SendMessage:
response.errorCode = service->HandleRequest(*session, request, response); response.errorCode = service->HandleRequest(*session, request, response);
@ -158,7 +160,7 @@ namespace skyline::service {
std::erase_if(serviceMap, [service](const auto &entry) { std::erase_if(serviceMap, [service](const auto &entry) {
return entry.second == service; return entry.second == service;
}); });
session->domainTable.erase(request.domain->objectId); session->domains.at(request.domain->objectId).reset();
break; break;
} }
} catch (std::out_of_range &) { } catch (std::out_of_range &) {

View File

@ -11,9 +11,6 @@ namespace skyline::vfs {
*/ */
class Backing { class Backing {
public: public:
/**
* @brief The capabilities of the Backing
*/
union Mode { union Mode {
struct { struct {
bool read : 1; //!< The backing is readable bool read : 1; //!< The backing is readable
@ -41,47 +38,44 @@ namespace skyline::vfs {
/** /**
* @brief Read bytes from the backing at a particular offset to a buffer * @brief Read bytes from the backing at a particular offset to a buffer
* @param output The object to write to * @param output The object to write the data read to
* @param offset The offset to start reading from * @param offset The offset to start reading from
* @param size The amount to read in bytes
* @return The amount of bytes read * @return The amount of bytes read
*/ */
virtual size_t Read(u8 *output, size_t offset, size_t size) = 0; virtual size_t Read(span<u8> output, size_t offset = 0) = 0;
/** /**
* @brief Read bytes from the backing at a particular offset to a buffer (template version) * @brief Read bytes from the backing at a particular offset into an object
* @param output The object to write to
* @param offset The offset to start reading from * @param offset The offset to start reading from
* @param size The amount to read in bytes * @return The object that was read
* @return The amount of bytes read
*/ */
template<typename T> template<typename T>
inline size_t Read(T *output, size_t offset = 0, size_t size = 0) { inline T Read(size_t offset = 0) {
return Read(reinterpret_cast<u8 *>(output), offset, size ? size : sizeof(T)); T object;
Read(span(reinterpret_cast<u8 *>(&object), sizeof(T)), offset);
return object;
} }
/** /**
* @brief Writes from a buffer to a particular offset in the backing * @brief Writes from a buffer to a particular offset in the backing
* @param input The object to write to the backing * @param input The data to write to the backing
* @param offset The offset where the input buffer should be written * @param offset The offset where the input buffer should be written
* @param size The amount to write
* @return The amount of bytes written * @return The amount of bytes written
*/ */
virtual size_t Write(u8 *input, size_t offset, size_t size) { virtual size_t Write(span<u8> input, size_t offset = 0) {
throw exception("This backing does not support being written to"); throw exception("This backing does not support being written to");
} }
/** /**
* @brief Writes from a buffer to a particular offset in the backing (template version) * @brief Writes from an object into a particular offset in the backing
* @tparam T The type of object to write * @param object The object to write to the backing
* @param input The object to write to the backing * @param offset The offset where the input should be written
* @param offset The offset where the input buffer should be written
* @param size The amount to write
* @return The amount of bytes written
*/ */
template<typename T> template<typename T>
inline size_t Write(T *output, size_t offset = 0, size_t size = 0) { inline void WriteObject(const T& object, size_t offset = 0) {
return Write(reinterpret_cast<u8 *>(output), offset, size ? size : sizeof(T)); size_t size;
if((size = Write(span(reinterpret_cast<u8 *>(&object), sizeof(T)), offset)) != sizeof(T))
throw exception("Object wasn't written fully into output backing: {}/{}", size, sizeof(T));
} }
/** /**

View File

@ -15,34 +15,34 @@ namespace skyline::vfs {
cipher.SetIV(ctr); cipher.SetIV(ctr);
} }
size_t CtrEncryptedBacking::Read(u8 *output, size_t offset, size_t size) { size_t CtrEncryptedBacking::Read(span<u8> output, size_t offset) {
if (size == 0) if (size == 0)
return 0; return 0;
size_t sectorOffset{offset % SectorSize}; size_t sectorOffset{offset % SectorSize};
if (sectorOffset == 0) { if (sectorOffset == 0) {
UpdateCtr(baseOffset + offset); UpdateCtr(baseOffset + offset);
size_t read{backing->Read(output, offset, size)}; size_t read{backing->Read(output, offset)};
if (read != size) if (read != size)
return 0; return 0;
cipher.Decrypt({output, size}); cipher.Decrypt(output);
return size; return size;
} }
size_t sectorStart{offset - sectorOffset}; size_t sectorStart{offset - sectorOffset};
std::vector<u8> blockBuf(SectorSize); std::vector<u8> blockBuf(SectorSize);
size_t read{backing->Read(blockBuf.data(), sectorStart, SectorSize)}; size_t read{backing->Read(blockBuf, sectorStart)};
if (read != SectorSize) if (read != SectorSize)
return 0; return 0;
UpdateCtr(baseOffset + sectorStart); UpdateCtr(baseOffset + sectorStart);
cipher.Decrypt(blockBuf); cipher.Decrypt(blockBuf);
if (size + sectorOffset < SectorSize) { if (size + sectorOffset < SectorSize) {
std::memcpy(output, blockBuf.data() + sectorOffset, size); std::memcpy(output.data(), blockBuf.data() + sectorOffset, size);
return size; return size;
} }
size_t readInBlock{SectorSize - sectorOffset}; size_t readInBlock{SectorSize - sectorOffset};
std::memcpy(output, blockBuf.data() + sectorOffset, readInBlock); std::memcpy(output.data(), blockBuf.data() + sectorOffset, readInBlock);
return readInBlock + Read(output + readInBlock, offset + readInBlock, size - readInBlock); return readInBlock + Read(output.subspan(readInBlock), offset + readInBlock);
} }
} }

View File

@ -26,6 +26,6 @@ namespace skyline::vfs {
public: public:
CtrEncryptedBacking(crypto::KeyStore::Key128 &ctr, crypto::KeyStore::Key128 &key, const std::shared_ptr<Backing> &backing, size_t baseOffset); CtrEncryptedBacking(crypto::KeyStore::Key128 &ctr, crypto::KeyStore::Key128 &key, const std::shared_ptr<Backing> &backing, size_t baseOffset);
size_t Read(u8 *output, size_t offset, size_t size) override; size_t Read(span<u8> output, size_t offset = 0) override;
}; };
} }

View File

@ -5,7 +5,7 @@
namespace skyline::vfs { namespace skyline::vfs {
NACP::NACP(const std::shared_ptr<vfs::Backing> &backing) { NACP::NACP(const std::shared_ptr<vfs::Backing> &backing) {
backing->Read(&nacpContents); nacpContents = backing->Read<NacpData>();
// TODO: Select based on language settings, complete struct, yada yada // TODO: Select based on language settings, complete struct, yada yada

View File

@ -14,7 +14,7 @@ namespace skyline::vfs {
using namespace loader; using namespace loader;
NCA::NCA(const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<crypto::KeyStore> &keyStore) : backing(backing), keyStore(keyStore) { NCA::NCA(const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<crypto::KeyStore> &keyStore) : backing(backing), keyStore(keyStore) {
backing->Read(&header); header = backing->Read<NcaHeader>();
if (header.magic != util::MakeMagic<u32>("NCA3")) { if (header.magic != util::MakeMagic<u32>("NCA3")) {
if (!keyStore->headerKey) if (!keyStore->headerKey)

View File

@ -20,22 +20,22 @@ namespace skyline::vfs {
close(fd); close(fd);
} }
size_t OsBacking::Read(u8 *output, size_t offset, size_t size) { size_t OsBacking::Read(span<u8> output, size_t offset) {
if (!mode.read) if (!mode.read)
throw exception("Attempting to read a backing that is not readable"); throw exception("Attempting to read a backing that is not readable");
auto ret{pread64(fd, output, size, offset)}; auto ret{pread64(fd, output.data(), output.size(), offset)};
if (ret < 0) if (ret < 0)
throw exception("Failed to read from fd: {}", strerror(errno)); throw exception("Failed to read from fd: {}", strerror(errno));
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
} }
size_t OsBacking::Write(u8 *output, size_t offset, size_t size) { size_t OsBacking::Write(span<u8> input, size_t offset) {
if (!mode.write) if (!mode.write)
throw exception("Attempting to write to a backing that is not writable"); throw exception("Attempting to write to a backing that is not writable");
auto ret{pwrite64(fd, output, size, offset)}; auto ret{pwrite64(fd, input.data(), input.size(), offset)};
if (ret < 0) if (ret < 0)
throw exception("Failed to write to fd: {}", strerror(errno)); throw exception("Failed to write to fd: {}", strerror(errno));

View File

@ -22,9 +22,9 @@ namespace skyline::vfs {
~OsBacking(); ~OsBacking();
size_t Read(u8 *output, size_t offset, size_t size); size_t Read(span<u8> output, size_t offset = 0);
size_t Write(u8 *output, size_t offset, size_t size); size_t Write(span<u8> input, size_t offset = 0);
void Resize(size_t size); void Resize(size_t size);
}; };

View File

@ -6,7 +6,7 @@
namespace skyline::vfs { namespace skyline::vfs {
PartitionFileSystem::PartitionFileSystem(std::shared_ptr<Backing> backing) : FileSystem(), backing(backing) { PartitionFileSystem::PartitionFileSystem(std::shared_ptr<Backing> backing) : FileSystem(), backing(backing) {
backing->Read(&header); header = backing->Read<FsHeader>();
if (header.magic == util::MakeMagic<u32>("PFS0")) if (header.magic == util::MakeMagic<u32>("PFS0"))
hashed = false; hashed = false;
@ -20,12 +20,11 @@ namespace skyline::vfs {
fileDataOffset = stringTableOffset + header.stringTableSize; fileDataOffset = stringTableOffset + header.stringTableSize;
std::vector<char> stringTable(header.stringTableSize + 1); std::vector<char> stringTable(header.stringTableSize + 1);
backing->Read(stringTable.data(), stringTableOffset, header.stringTableSize); backing->Read(span(stringTable).first(header.stringTableSize).cast<u8>(), stringTableOffset);
stringTable[header.stringTableSize] = 0; stringTable[header.stringTableSize] = 0;
for (u32 entryOffset{sizeof(FsHeader)}; entryOffset < header.numFiles * entrySize; entryOffset += entrySize) { for (u32 entryOffset{sizeof(FsHeader)}; entryOffset < header.numFiles * entrySize; entryOffset += entrySize) {
PartitionFileEntry entry; auto entry{backing->Read<PartitionFileEntry>(entryOffset)};
backing->Read(&entry, entryOffset);
std::string name(&stringTable[entry.stringTableOffset]); std::string name(&stringTable[entry.stringTableOffset]);
fileMap.emplace(name, std::move(entry)); fileMap.emplace(name, std::move(entry));

View File

@ -16,7 +16,7 @@ namespace skyline::vfs {
u32 numFiles; //!< The number of files in the filesystem u32 numFiles; //!< The number of files in the filesystem
u32 stringTableSize; //!< The size of the filesystem's string table u32 stringTableSize; //!< The size of the filesystem's string table
u32 _pad_; u32 _pad_;
} header{}; } header;
static_assert(sizeof(FsHeader) == 0x10); static_assert(sizeof(FsHeader) == 0x10);
struct PartitionFileEntry { struct PartitionFileEntry {

View File

@ -22,13 +22,12 @@ namespace skyline::vfs {
*/ */
RegionBacking(const std::shared_ptr<vfs::Backing> &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {}; RegionBacking(const std::shared_ptr<vfs::Backing> &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {};
inline size_t Read(u8 *output, size_t offset, size_t size) { virtual size_t Read(span<u8> output, size_t offset = 0) {
if (!mode.read) if (!mode.read)
throw exception("Attempting to read a backing that is not readable"); throw exception("Attempting to read a backing that is not readable");
if (size - offset < output.size())
size = std::min(offset + size, this->size) - offset; throw exception("Trying to read past the end of a region backing: 0x{:X}/0x{:X} (Offset: 0x{:X})", output.size(), size, offset);
return backing->Read(output, baseOffset + offset);
return backing->Read(output, baseOffset + offset, size);
} }
}; };
} }

View File

@ -6,20 +6,18 @@
namespace skyline::vfs { namespace skyline::vfs {
RomFileSystem::RomFileSystem(std::shared_ptr<Backing> backing) : FileSystem(), backing(backing) { RomFileSystem::RomFileSystem(std::shared_ptr<Backing> backing) : FileSystem(), backing(backing) {
backing->Read(&header); header = backing->Read<RomFsHeader>();
TraverseDirectory(0, ""); TraverseDirectory(0, "");
} }
void RomFileSystem::TraverseFiles(u32 offset, const std::string &path) { void RomFileSystem::TraverseFiles(u32 offset, const std::string &path) {
RomFsFileEntry entry{}; RomFsFileEntry entry;
do { do {
backing->Read(&entry, header.fileMetaTableOffset + offset); entry = backing->Read<RomFsFileEntry>(header.fileMetaTableOffset + offset);
if (entry.nameSize) { if (entry.nameSize) {
std::vector<char> name(entry.nameSize); std::vector<char> name(entry.nameSize);
backing->Read(name.data(), header.fileMetaTableOffset + offset + sizeof(RomFsFileEntry), entry.nameSize); backing->Read(span(name).cast<u8>(), header.fileMetaTableOffset + offset + sizeof(RomFsFileEntry));
std::string fullPath{path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize)}; std::string fullPath{path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize)};
fileMap.emplace(fullPath, entry); fileMap.emplace(fullPath, entry);
@ -30,13 +28,12 @@ namespace skyline::vfs {
} }
void RomFileSystem::TraverseDirectory(u32 offset, const std::string &path) { void RomFileSystem::TraverseDirectory(u32 offset, const std::string &path) {
RomFsDirectoryEntry entry{}; auto entry{backing->Read<RomFsDirectoryEntry>(header.dirMetaTableOffset + offset)};
backing->Read(&entry, header.dirMetaTableOffset + offset);
std::string childPath{path}; std::string childPath{path};
if (entry.nameSize) { if (entry.nameSize) {
std::vector<char> name(entry.nameSize); std::vector<char> name(entry.nameSize);
backing->Read(name.data(), header.dirMetaTableOffset + offset + sizeof(RomFsDirectoryEntry), entry.nameSize); backing->Read(span(name).cast<u8>(), header.dirMetaTableOffset + offset + sizeof(RomFsDirectoryEntry));
childPath = path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize); childPath = path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize);
} }
@ -89,11 +86,11 @@ namespace skyline::vfs {
u32 offset{ownEntry.fileOffset}; u32 offset{ownEntry.fileOffset};
do { do {
backing->Read(&romFsFileEntry, header.fileMetaTableOffset + offset); romFsFileEntry = backing->Read<RomFileSystem::RomFsFileEntry>(header.fileMetaTableOffset + offset);
if (romFsFileEntry.nameSize) { if (romFsFileEntry.nameSize) {
std::vector<char> name(romFsFileEntry.nameSize); std::vector<char> name(romFsFileEntry.nameSize);
backing->Read(name.data(), header.fileMetaTableOffset + offset + sizeof(RomFileSystem::RomFsFileEntry), romFsFileEntry.nameSize); backing->Read(span(name).cast<u8>(), header.fileMetaTableOffset + offset + sizeof(RomFileSystem::RomFsFileEntry));
contents.emplace_back(Entry{std::string(name.data(), romFsFileEntry.nameSize), EntryType::File}); contents.emplace_back(Entry{std::string(name.data(), romFsFileEntry.nameSize), EntryType::File});
} }
@ -107,11 +104,11 @@ namespace skyline::vfs {
u32 offset{ownEntry.childOffset}; u32 offset{ownEntry.childOffset};
do { do {
backing->Read(&romFsDirectoryEntry, header.dirMetaTableOffset + offset); romFsDirectoryEntry = backing->Read<RomFileSystem::RomFsDirectoryEntry>(header.dirMetaTableOffset + offset);
if (romFsDirectoryEntry.nameSize) { if (romFsDirectoryEntry.nameSize) {
std::vector<char> name(romFsDirectoryEntry.nameSize); std::vector<char> name(romFsDirectoryEntry.nameSize);
backing->Read(name.data(), header.dirMetaTableOffset + offset + sizeof(RomFileSystem::RomFsDirectoryEntry), romFsDirectoryEntry.nameSize); backing->Read(span(name).cast<u8>(), header.dirMetaTableOffset + offset + sizeof(RomFileSystem::RomFsDirectoryEntry));
contents.emplace_back(Entry{std::string(name.data(), romFsDirectoryEntry.nameSize), EntryType::Directory}); contents.emplace_back(Entry{std::string(name.data(), romFsDirectoryEntry.nameSize), EntryType::Directory});
} }

View File

@ -29,16 +29,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
System.loadLibrary("skyline") // libskyline.so System.loadLibrary("skyline") // libskyline.so
} }
/**
* The file descriptor of the ROM
*/
private lateinit var romFd : ParcelFileDescriptor
/**
* The file descriptor of the application Preference XML
*/
private lateinit var preferenceFd : ParcelFileDescriptor
/** /**
* The [InputManager] class handles loading/saving the input data * The [InputManager] class handles loading/saving the input data
*/ */
@ -187,12 +177,13 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
*/ */
private fun executeApplication(rom : Uri) { private fun executeApplication(rom : Uri) {
val romType = getRomFormat(rom, contentResolver).ordinal val romType = getRomFormat(rom, contentResolver).ordinal
romFd = contentResolver.openFileDescriptor(rom, "r")!! val romFd = contentResolver.openFileDescriptor(rom, "r")!!
val preferenceFd = ParcelFileDescriptor.open(File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml"), ParcelFileDescriptor.MODE_READ_WRITE)
emulationThread = Thread { emulationThread = Thread {
surfaceReady.block() surfaceReady.block()
executeApplication(rom.toString(), romType, romFd.fd, preferenceFd.fd, applicationContext.filesDir.canonicalPath + "/") executeApplication(rom.toString(), romType, romFd.detachFd(), preferenceFd.detachFd(), applicationContext.filesDir.canonicalPath + "/")
if (shouldFinish) if (shouldFinish)
runOnUiThread { finish() } runOnUiThread { finish() }
@ -225,9 +216,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
input = InputManager(this) input = InputManager(this)
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
game_view.holder.addCallback(this) game_view.holder.addCallback(this)
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
@ -262,8 +250,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
shouldFinish = true shouldFinish = true
romFd.close()
executeApplication(intent?.data!!) executeApplication(intent?.data!!)
super.onNewIntent(intent) super.onNewIntent(intent)
@ -281,9 +267,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
vibrators.forEach { (_, vibrator) -> vibrator.cancel() } vibrators.forEach { (_, vibrator) -> vibrator.cancel() }
vibrators.clear() vibrators.clear()
romFd.close()
preferenceFd.close()
super.onDestroy() super.onDestroy()
} }