Compare commits
37 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ba7fe4ab1 | ||
|
|
964154ce44 | ||
|
|
53a219f163 | ||
|
|
3ac736c003 | ||
|
|
f5e87f4ce1 | ||
|
|
9533875eeb | ||
|
|
d95a1a3742 | ||
|
|
5c49e56d41 | ||
|
|
d66b43dadf | ||
|
|
34d6a1349c | ||
|
|
41660c8923 | ||
|
|
973fdce79b | ||
|
|
0f20fa5a1e | ||
|
|
de7cb91995 | ||
|
|
867ba1ceee | ||
|
|
1c7c1347d8 | ||
|
|
ff8754f921 | ||
|
|
89cc8c1617 | ||
|
|
552aac7e6c | ||
|
|
fe2498a650 | ||
|
|
3d938b8c60 | ||
|
|
1519ce7eab | ||
|
|
d85cfc94e2 | ||
|
|
459e158340 | ||
|
|
3e0727df1b | ||
|
|
894b0de0f2 | ||
|
|
dd09439fee | ||
|
|
05231d8b08 | ||
|
|
966874e357 | ||
|
|
d84eb9dac6 | ||
|
|
ae09adfcb3 | ||
|
|
d5bc9aef4e | ||
|
|
863579736c | ||
|
|
bb960c8cb4 | ||
|
|
48733744bb | ||
|
|
a8bb1eb39f | ||
|
|
a44475207c |
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 5a91c94dca...7ea1241953
@@ -63,7 +63,6 @@ add_library(common STATIC
|
||||
string_util.cpp
|
||||
string_util.h
|
||||
swap.h
|
||||
synchronized_wrapper.h
|
||||
telemetry.cpp
|
||||
telemetry.h
|
||||
thread.cpp
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define USA_DIR "USA"
|
||||
#define JAP_DIR "JAP"
|
||||
|
||||
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
|
||||
// Subdirs in the User dir returned by GetUserPath(UserPath::UserDir)
|
||||
#define CONFIG_DIR "config"
|
||||
#define CACHE_DIR "cache"
|
||||
#define SDMC_DIR "sdmc"
|
||||
@@ -35,11 +35,11 @@
|
||||
#define LOG_DIR "log"
|
||||
|
||||
// Filenames
|
||||
// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
|
||||
// Files in the directory returned by GetUserPath(UserPath::ConfigDir)
|
||||
#define EMU_CONFIG "emu.ini"
|
||||
#define DEBUGGER_CONFIG "debugger.ini"
|
||||
#define LOGGER_CONFIG "logger.ini"
|
||||
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
|
||||
// Files in the directory returned by GetUserPath(UserPath::LogDir)
|
||||
#define LOG_FILE "yuzu_log.txt"
|
||||
|
||||
// Sys files
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_paths.h"
|
||||
@@ -395,12 +396,12 @@ bool CreateEmptyFile(const std::string& filename) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
|
||||
bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory,
|
||||
DirectoryEntryCallable callback) {
|
||||
LOG_TRACE(Common_Filesystem, "directory {}", directory);
|
||||
|
||||
// How many files + directories we found
|
||||
unsigned found_entries = 0;
|
||||
u64 found_entries = 0;
|
||||
|
||||
// Save the status of callback function
|
||||
bool callback_error = false;
|
||||
@@ -430,7 +431,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directo
|
||||
if (virtual_name == "." || virtual_name == "..")
|
||||
continue;
|
||||
|
||||
unsigned ret_entries = 0;
|
||||
u64 ret_entries = 0;
|
||||
if (!callback(&ret_entries, directory, virtual_name)) {
|
||||
callback_error = true;
|
||||
break;
|
||||
@@ -454,9 +455,9 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directo
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
unsigned int recursion) {
|
||||
const auto callback = [recursion, &parent_entry](unsigned* num_entries_out,
|
||||
u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
unsigned int recursion) {
|
||||
const auto callback = [recursion, &parent_entry](u64* num_entries_out,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
FSTEntry entry;
|
||||
@@ -468,7 +469,7 @@ unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
// is a directory, lets go inside if we didn't recurse to often
|
||||
if (recursion > 0) {
|
||||
entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1);
|
||||
*num_entries_out += (int)entry.size;
|
||||
*num_entries_out += entry.size;
|
||||
} else {
|
||||
entry.size = 0;
|
||||
}
|
||||
@@ -479,16 +480,16 @@ unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
(*num_entries_out)++;
|
||||
|
||||
// Push into the tree
|
||||
parent_entry.children.push_back(entry);
|
||||
parent_entry.children.push_back(std::move(entry));
|
||||
return true;
|
||||
};
|
||||
|
||||
unsigned num_entries;
|
||||
u64 num_entries;
|
||||
return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0;
|
||||
}
|
||||
|
||||
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
|
||||
const auto callback = [recursion](unsigned* num_entries_out, const std::string& directory,
|
||||
const auto callback = [recursion](u64* num_entries_out, const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
|
||||
|
||||
@@ -681,67 +682,68 @@ std::string GetSysDirectory() {
|
||||
|
||||
// Returns a string with a yuzu data dir or file in the user's home
|
||||
// directory. To be used in "multi-user" mode (that is, installed).
|
||||
const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) {
|
||||
static std::string paths[NUM_PATH_INDICES];
|
||||
const std::string& GetUserPath(UserPath path, const std::string& new_path) {
|
||||
static std::unordered_map<UserPath, std::string> paths;
|
||||
auto& user_path = paths[UserPath::UserDir];
|
||||
|
||||
// Set up all paths and files on the first run
|
||||
if (paths[D_USER_IDX].empty()) {
|
||||
if (user_path.empty()) {
|
||||
#ifdef _WIN32
|
||||
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
|
||||
if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
|
||||
paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
|
||||
if (!FileUtil::IsDirectory(user_path)) {
|
||||
user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
} else {
|
||||
LOG_INFO(Common_Filesystem, "Using the local user directory");
|
||||
}
|
||||
|
||||
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
|
||||
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
||||
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
|
||||
#else
|
||||
if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
|
||||
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
|
||||
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
|
||||
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
||||
user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
|
||||
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
|
||||
} else {
|
||||
std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
|
||||
std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
|
||||
std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
|
||||
|
||||
paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
user_path = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
paths.emplace(UserPath::ConfigDir, config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::CacheDir, cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
|
||||
}
|
||||
#endif
|
||||
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
||||
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
|
||||
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
|
||||
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
|
||||
// TODO: Put the logs in a better location for each OS
|
||||
paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP;
|
||||
paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
|
||||
}
|
||||
|
||||
if (!newPath.empty()) {
|
||||
if (!FileUtil::IsDirectory(newPath)) {
|
||||
LOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath);
|
||||
return paths[DirIDX];
|
||||
if (!new_path.empty()) {
|
||||
if (!FileUtil::IsDirectory(new_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
|
||||
return paths[path];
|
||||
} else {
|
||||
paths[DirIDX] = newPath;
|
||||
paths[path] = new_path;
|
||||
}
|
||||
|
||||
switch (DirIDX) {
|
||||
case D_ROOT_IDX:
|
||||
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
|
||||
switch (path) {
|
||||
case UserPath::RootDir:
|
||||
user_path = paths[UserPath::RootDir] + DIR_SEP;
|
||||
break;
|
||||
|
||||
case D_USER_IDX:
|
||||
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
|
||||
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
|
||||
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
|
||||
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
||||
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
|
||||
case UserPath::UserDir:
|
||||
user_path = paths[UserPath::RootDir] + DIR_SEP;
|
||||
paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP;
|
||||
paths[UserPath::CacheDir] = user_path + CACHE_DIR DIR_SEP;
|
||||
paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP;
|
||||
paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return paths[DirIDX];
|
||||
return paths[path];
|
||||
}
|
||||
|
||||
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
@@ -836,8 +838,7 @@ std::string GetPathWithoutTop(std::string path) {
|
||||
}
|
||||
const auto name_bck_index = path.find_first_of('\\');
|
||||
const auto name_fwd_index = path.find_first_of('/');
|
||||
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
|
||||
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
|
||||
return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
|
||||
}
|
||||
|
||||
std::string GetFilename(std::string path) {
|
||||
|
||||
@@ -16,21 +16,20 @@
|
||||
#include "common/string_util.h"
|
||||
#endif
|
||||
|
||||
// User directory indices for GetUserPath
|
||||
enum {
|
||||
D_USER_IDX,
|
||||
D_ROOT_IDX,
|
||||
D_CONFIG_IDX,
|
||||
D_CACHE_IDX,
|
||||
D_SDMC_IDX,
|
||||
D_NAND_IDX,
|
||||
D_SYSDATA_IDX,
|
||||
D_LOGS_IDX,
|
||||
NUM_PATH_INDICES
|
||||
};
|
||||
|
||||
namespace FileUtil {
|
||||
|
||||
// User paths for GetUserPath
|
||||
enum class UserPath {
|
||||
CacheDir,
|
||||
ConfigDir,
|
||||
LogDir,
|
||||
NANDDir,
|
||||
RootDir,
|
||||
SDMCDir,
|
||||
SysDataDir,
|
||||
UserDir,
|
||||
};
|
||||
|
||||
// FileSystem tree node/
|
||||
struct FSTEntry {
|
||||
bool isDirectory;
|
||||
@@ -85,7 +84,7 @@ bool CreateEmptyFile(const std::string& filename);
|
||||
* @return whether handling the entry succeeded
|
||||
*/
|
||||
using DirectoryEntryCallable = std::function<bool(
|
||||
unsigned* num_entries_out, const std::string& directory, const std::string& virtual_name)>;
|
||||
u64* num_entries_out, const std::string& directory, const std::string& virtual_name)>;
|
||||
|
||||
/**
|
||||
* Scans a directory, calling the callback for each file/directory contained within.
|
||||
@@ -96,7 +95,7 @@ using DirectoryEntryCallable = std::function<bool(
|
||||
* @param callback The callback which will be called for each entry
|
||||
* @return whether scanning the directory succeeded
|
||||
*/
|
||||
bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory,
|
||||
bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory,
|
||||
DirectoryEntryCallable callback);
|
||||
|
||||
/**
|
||||
@@ -106,8 +105,8 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directo
|
||||
* @param recursion Number of children directories to read before giving up.
|
||||
* @return the total number of files/directories found
|
||||
*/
|
||||
unsigned ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
unsigned int recursion = 0);
|
||||
u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
unsigned int recursion = 0);
|
||||
|
||||
// deletes the given directory and anything under it. Returns true on success.
|
||||
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
|
||||
@@ -123,7 +122,7 @@ bool SetCurrentDir(const std::string& directory);
|
||||
|
||||
// Returns a pointer to a string with a yuzu data dir in the user's home
|
||||
// directory. To be used in "multi-user" mode (that is, installed).
|
||||
const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath = "");
|
||||
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
|
||||
|
||||
// Returns the path to where the sys file are
|
||||
std::string GetSysDirectory();
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
class SynchronizedWrapper;
|
||||
|
||||
/**
|
||||
* Synchronized reference, that keeps a SynchronizedWrapper's mutex locked during its lifetime. This
|
||||
* greatly reduces the chance that someone will access the wrapped resource without locking the
|
||||
* mutex.
|
||||
*/
|
||||
template <typename T>
|
||||
class SynchronizedRef {
|
||||
public:
|
||||
SynchronizedRef(SynchronizedWrapper<T>& wrapper) : wrapper(&wrapper) {
|
||||
wrapper.mutex.lock();
|
||||
}
|
||||
|
||||
SynchronizedRef(SynchronizedRef&) = delete;
|
||||
SynchronizedRef(SynchronizedRef&& o) : wrapper(o.wrapper) {
|
||||
o.wrapper = nullptr;
|
||||
}
|
||||
|
||||
~SynchronizedRef() {
|
||||
if (wrapper)
|
||||
wrapper->mutex.unlock();
|
||||
}
|
||||
|
||||
SynchronizedRef& operator=(SynchronizedRef&) = delete;
|
||||
SynchronizedRef& operator=(SynchronizedRef&& o) {
|
||||
std::swap(wrapper, o.wrapper);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return wrapper->data;
|
||||
}
|
||||
const T& operator*() const {
|
||||
return wrapper->data;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return &wrapper->data;
|
||||
}
|
||||
const T* operator->() const {
|
||||
return &wrapper->data;
|
||||
}
|
||||
|
||||
private:
|
||||
SynchronizedWrapper<T>* wrapper;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps an object, only allowing access to it via a locking reference wrapper. Good to ensure no
|
||||
* one forgets to lock a mutex before acessing an object. To access the wrapped object construct a
|
||||
* SyncronizedRef on this wrapper. Inspired by Rust's Mutex type
|
||||
* (http://doc.rust-lang.org/std/sync/struct.Mutex.html).
|
||||
*/
|
||||
template <typename T>
|
||||
class SynchronizedWrapper {
|
||||
public:
|
||||
template <typename... Args>
|
||||
SynchronizedWrapper(Args&&... args) : data(std::forward<Args>(args)...) {}
|
||||
|
||||
SynchronizedRef<T> Lock() {
|
||||
return {*this};
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U>
|
||||
friend class SynchronizedRef;
|
||||
|
||||
std::mutex mutex;
|
||||
T data;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -20,9 +20,6 @@ public:
|
||||
u64 cpsr;
|
||||
std::array<u128, 32> fpu_registers;
|
||||
u64 fpscr;
|
||||
|
||||
// TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT
|
||||
VAddr tls_address;
|
||||
};
|
||||
|
||||
/// Runs the CPU until an event happens
|
||||
@@ -104,6 +101,10 @@ public:
|
||||
|
||||
virtual void SetTlsAddress(VAddr address) = 0;
|
||||
|
||||
virtual u64 GetTPIDR_EL0() const = 0;
|
||||
|
||||
virtual void SetTPIDR_EL0(u64 value) = 0;
|
||||
|
||||
/**
|
||||
* Saves the current CPU context
|
||||
* @param ctx Thread context to save
|
||||
|
||||
@@ -196,6 +196,14 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
|
||||
cb->tpidrro_el0 = address;
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetTPIDR_EL0() const {
|
||||
return cb->tpidr_el0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
|
||||
cb->tpidr_el0 = value;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
@@ -203,7 +211,6 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
|
||||
ctx.cpsr = jit->GetPstate();
|
||||
ctx.fpu_registers = jit->GetVectors();
|
||||
ctx.fpscr = jit->GetFpcr();
|
||||
ctx.tls_address = cb->tpidrro_el0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
|
||||
@@ -213,7 +220,6 @@ void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
|
||||
jit->SetPstate(static_cast<u32>(ctx.cpsr));
|
||||
jit->SetVectors(ctx.fpu_registers);
|
||||
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
|
||||
cb->tpidrro_el0 = ctx.tls_address;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PrepareReschedule() {
|
||||
|
||||
@@ -34,6 +34,8 @@ public:
|
||||
void SetCPSR(u32 cpsr) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
|
||||
@@ -169,6 +169,16 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
}
|
||||
|
||||
u64 ARM_Unicorn::GetTPIDR_EL0() const {
|
||||
u64 value{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
|
||||
return value;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
ExecuteInstructions(std::max(4000000, 0));
|
||||
@@ -220,8 +230,6 @@ void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
|
||||
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31));
|
||||
|
||||
ctx.tls_address = GetTlsAddress();
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = &ctx.fpu_registers[i];
|
||||
@@ -249,8 +257,6 @@ void ARM_Unicorn::LoadContext(const ARM_Interface::ThreadContext& ctx) {
|
||||
|
||||
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31));
|
||||
|
||||
SetTlsAddress(ctx.tls_address);
|
||||
|
||||
for (auto i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = (void*)&ctx.fpu_registers[i];
|
||||
|
||||
@@ -28,6 +28,8 @@ public:
|
||||
void SetCPSR(u32 cpsr) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void PrepareReschedule() override;
|
||||
|
||||
@@ -20,13 +20,13 @@ enum {
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
|
||||
|
||||
// TODO(bunnei): Replace these with correct errors for Switch OS
|
||||
constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_INVALID_PATH(-1);
|
||||
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
|
||||
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -42,7 +42,7 @@ bool VfsFile::WriteByte(u8 data, size_t offset) {
|
||||
return Write(&data, 1, offset) == 1;
|
||||
}
|
||||
|
||||
size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) {
|
||||
size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
|
||||
return Write(data.data(), data.size(), offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,7 @@ struct VfsFile : NonCopyable {
|
||||
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
|
||||
template <typename T>
|
||||
size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"Data type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
|
||||
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
|
||||
}
|
||||
@@ -69,8 +68,7 @@ struct VfsFile : NonCopyable {
|
||||
// Returns the number of bytes read successfully.
|
||||
template <typename T>
|
||||
size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"Data type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Read(reinterpret_cast<u8*>(data), size, offset);
|
||||
}
|
||||
|
||||
@@ -78,8 +76,7 @@ struct VfsFile : NonCopyable {
|
||||
// Returns the number of bytes read successfully (sizeof(T)).
|
||||
template <typename T>
|
||||
size_t ReadObject(T* data, size_t offset = 0) const {
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"Data type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
|
||||
}
|
||||
|
||||
@@ -88,33 +85,29 @@ struct VfsFile : NonCopyable {
|
||||
virtual bool WriteByte(u8 data, size_t offset = 0);
|
||||
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
|
||||
// written.
|
||||
virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0);
|
||||
virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0);
|
||||
|
||||
// Writes an array of type T, size number_elements to offset in file.
|
||||
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
|
||||
template <typename T>
|
||||
size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"Data type must be trivially copyable.");
|
||||
|
||||
size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Write(data, number_elements * sizeof(T), offset);
|
||||
}
|
||||
|
||||
// Writes size bytes starting at memory location data to offset in file.
|
||||
// Returns the number of bytes written successfully.
|
||||
template <typename T>
|
||||
size_t WriteBytes(T* data, size_t size, size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"Data type must be trivially copyable.");
|
||||
return Write(reinterpret_cast<u8*>(data), size, offset);
|
||||
size_t WriteBytes(const T* data, size_t size, size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Write(reinterpret_cast<const u8*>(data), size, offset);
|
||||
}
|
||||
|
||||
// Writes one object of type T to offset in file.
|
||||
// Returns the number of bytes written successfully (sizeof(T)).
|
||||
template <typename T>
|
||||
size_t WriteObject(const T& data, size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"Data type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Write(&data, sizeof(T), offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
@@ -75,7 +76,7 @@ bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) {
|
||||
size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
|
||||
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ size_t OffsetVfsFile::GetOffset() const {
|
||||
}
|
||||
|
||||
size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
|
||||
return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0);
|
||||
return std::clamp(r_size, size_t{0}, size - r_offset);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -28,7 +28,7 @@ struct OffsetVfsFile : public VfsFile {
|
||||
std::vector<u8> ReadBytes(size_t size, size_t offset) const override;
|
||||
std::vector<u8> ReadAllBytes() const override;
|
||||
bool WriteByte(u8 data, size_t offset) override;
|
||||
size_t WriteBytes(std::vector<u8> data, size_t offset) override;
|
||||
size_t WriteBytes(const std::vector<u8>& data, size_t offset) override;
|
||||
|
||||
bool Rename(const std::string& name) override;
|
||||
|
||||
|
||||
@@ -92,13 +92,13 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
|
||||
perms(perms_) {
|
||||
if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append))
|
||||
FileUtil::CreateDir(path);
|
||||
unsigned size;
|
||||
|
||||
if (perms == Mode::Append)
|
||||
return;
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(
|
||||
&size, path,
|
||||
[this](unsigned* entries_out, const std::string& directory, const std::string& filename) {
|
||||
nullptr, path,
|
||||
[this](u64* entries_out, const std::string& directory, const std::string& filename) {
|
||||
std::string full_path = directory + DIR_SEP + filename;
|
||||
if (FileUtil::IsDirectory(full_path))
|
||||
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms));
|
||||
|
||||
@@ -56,6 +56,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
if (previous_thread) {
|
||||
previous_thread->last_running_ticks = CoreTiming::GetTicks();
|
||||
cpu_core->SaveContext(previous_thread->context);
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0();
|
||||
|
||||
if (previous_thread->status == ThreadStatus::Running) {
|
||||
// This is only the case when a reschedule is triggered without the current thread
|
||||
@@ -87,6 +89,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
|
||||
cpu_core->LoadContext(new_thread->context);
|
||||
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
|
||||
cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
|
||||
cpu_core->ClearExclusiveState();
|
||||
} else {
|
||||
current_thread = nullptr;
|
||||
|
||||
@@ -312,6 +312,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||
thread->status = ThreadStatus::Dormant;
|
||||
thread->entry_point = entry_point;
|
||||
thread->stack_top = stack_top;
|
||||
thread->tpidr_el0 = 0;
|
||||
thread->nominal_priority = thread->current_priority = priority;
|
||||
thread->last_running_ticks = CoreTiming::GetTicks();
|
||||
thread->processor_id = processor_id;
|
||||
|
||||
@@ -182,6 +182,14 @@ public:
|
||||
return tls_address;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
|
||||
* @returns The value of the TPIDR_EL0 register.
|
||||
*/
|
||||
u64 GetTPIDR_EL0() const {
|
||||
return tpidr_el0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the address of the current thread's command buffer, located in the TLS.
|
||||
* @returns VAddr of the thread's command buffer.
|
||||
@@ -213,6 +221,7 @@ public:
|
||||
s32 processor_id;
|
||||
|
||||
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
|
||||
u64 tpidr_el0; ///< TPIDR_EL0 read/write system register.
|
||||
|
||||
SharedPtr<Process> owner_process; ///< Process that owns this thread
|
||||
|
||||
|
||||
@@ -272,9 +272,9 @@ void RegisterFileSystems() {
|
||||
sdmc_factory = nullptr;
|
||||
|
||||
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
||||
FileUtil::GetUserPath(D_NAND_IDX), FileSys::Mode::Write);
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::Write);
|
||||
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
||||
FileUtil::GetUserPath(D_SDMC_IDX), FileSys::Mode::Write);
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::Write);
|
||||
|
||||
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
||||
save_data_factory = std::move(savedata);
|
||||
|
||||
@@ -42,7 +42,7 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
|
||||
RegisterHandlers(functions);
|
||||
|
||||
// Attempt to load shared font data from disk
|
||||
const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT};
|
||||
const std::string filepath{FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SHARED_FONT};
|
||||
FileUtil::CreateFullPath(filepath); // Create path if not already created
|
||||
FileUtil::IOFile file(filepath, "rb");
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
|
||||
if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) {
|
||||
return SubmitGPFIFO(input, output);
|
||||
}
|
||||
if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
|
||||
return KickoffPB(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
@@ -127,14 +130,37 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
|
||||
params.gpfifo, params.num_entries, params.flags);
|
||||
params.address, params.num_entries, params.flags);
|
||||
|
||||
auto entries = std::vector<IoctlGpfifoEntry>();
|
||||
entries.resize(params.num_entries);
|
||||
std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)],
|
||||
params.num_entries * sizeof(IoctlGpfifoEntry));
|
||||
for (auto entry : entries) {
|
||||
VAddr va_addr = entry.Address();
|
||||
Tegra::GPUVAddr va_addr = entry.Address();
|
||||
Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
|
||||
}
|
||||
params.fence_out.id = 0;
|
||||
params.fence_out.value = 0;
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
|
||||
params.address, params.num_entries, params.flags);
|
||||
|
||||
std::vector<IoctlGpfifoEntry> entries(params.num_entries);
|
||||
Memory::ReadBlock(params.address, entries.data(),
|
||||
params.num_entries * sizeof(IoctlGpfifoEntry));
|
||||
|
||||
for (auto entry : entries) {
|
||||
Tegra::GPUVAddr va_addr = entry.Address();
|
||||
Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
|
||||
}
|
||||
params.fence_out.id = 0;
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Service::Nvidia::Devices {
|
||||
class nvmap;
|
||||
constexpr u32 NVGPU_IOCTL_MAGIC('H');
|
||||
constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
|
||||
constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
|
||||
|
||||
class nvhost_gpu final : public nvdevice {
|
||||
public:
|
||||
@@ -158,14 +159,14 @@ private:
|
||||
BitField<31, 1, u32_le> unk2;
|
||||
};
|
||||
|
||||
VAddr Address() const {
|
||||
return (static_cast<VAddr>(gpu_va_hi) << 32) | entry0;
|
||||
Tegra::GPUVAddr Address() const {
|
||||
return (static_cast<Tegra::GPUVAddr>(gpu_va_hi) << 32) | entry0;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size");
|
||||
|
||||
struct IoctlSubmitGpfifo {
|
||||
u64_le gpfifo; // (ignored) pointer to gpfifo fence structs
|
||||
u64_le address; // pointer to gpfifo entry structs
|
||||
u32_le num_entries; // number of fence objects being submitted
|
||||
u32_le flags;
|
||||
IoctlFence fence_out; // returned new fence object for others to wait on
|
||||
@@ -193,6 +194,7 @@ private:
|
||||
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
|
||||
{8, &NVDRV::SetClientPID, "SetClientPID"},
|
||||
{9, nullptr, "DumpGraphicsMemoryInfo"},
|
||||
{10, nullptr, "InitializeDevtools"},
|
||||
{11, nullptr, "Ioctl2"},
|
||||
{11, &NVDRV::Ioctl, "Ioctl2"},
|
||||
{12, nullptr, "Ioctl3"},
|
||||
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Loader {
|
||||
|
||||
static std::string FindRomFS(const std::string& directory) {
|
||||
std::string filepath_romfs;
|
||||
const auto callback = [&filepath_romfs](unsigned*, const std::string& directory,
|
||||
const auto callback = [&filepath_romfs](u64*, const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
const std::string physical_name = directory + virtual_name;
|
||||
if (FileUtil::IsDirectory(physical_name)) {
|
||||
@@ -83,16 +83,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
|
||||
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
|
||||
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
|
||||
const VAddr load_addr = next_load_addr;
|
||||
const FileSys::VirtualFile module_file = dir->GetFile(module);
|
||||
if (module_file != nullptr)
|
||||
if (module_file != nullptr) {
|
||||
const VAddr load_addr = next_load_addr;
|
||||
next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr);
|
||||
if (next_load_addr) {
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
|
||||
// Register module with GDBStub
|
||||
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
|
||||
} else {
|
||||
next_load_addr = load_addr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ static u64 GenerateTelemetryId() {
|
||||
|
||||
u64 GetTelemetryId() {
|
||||
u64 telemetry_id{};
|
||||
static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
|
||||
static const std::string& filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
|
||||
"telemetry_id"};
|
||||
|
||||
if (FileUtil::Exists(filename)) {
|
||||
FileUtil::IOFile file(filename, "rb");
|
||||
@@ -61,7 +62,8 @@ u64 GetTelemetryId() {
|
||||
|
||||
u64 RegenerateTelemetryId() {
|
||||
const u64 new_telemetry_id{GenerateTelemetryId()};
|
||||
static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
|
||||
static const std::string& filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
|
||||
"telemetry_id"};
|
||||
|
||||
FileUtil::IOFile file(filename, "wb");
|
||||
if (!file.IsOpen()) {
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
namespace ArmTests {
|
||||
|
||||
static Memory::PageTable* page_table = nullptr;
|
||||
|
||||
TestEnvironment::TestEnvironment(bool mutable_memory_)
|
||||
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
|
||||
|
||||
@@ -67,10 +65,13 @@ boost::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
|
||||
}
|
||||
|
||||
boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
|
||||
auto iter = data.find(addr);
|
||||
const auto iter = data.find(addr);
|
||||
|
||||
if (iter == data.end()) {
|
||||
return addr; // Some arbitrary data
|
||||
// Some arbitrary data
|
||||
return static_cast<u8>(addr);
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -9,6 +11,10 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/memory_hook.h"
|
||||
|
||||
namespace Memory {
|
||||
struct PageTable;
|
||||
}
|
||||
|
||||
namespace ArmTests {
|
||||
|
||||
struct WriteRecord {
|
||||
@@ -79,6 +85,7 @@ private:
|
||||
bool mutable_memory;
|
||||
std::shared_ptr<TestMemory> test_memory;
|
||||
std::vector<WriteRecord> write_records;
|
||||
Memory::PageTable* page_table = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ArmTests
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
namespace Tegra::Engines {
|
||||
|
||||
Fermi2D::Fermi2D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
|
||||
|
||||
@@ -69,5 +68,4 @@ void Fermi2D::HandleSurfaceCopy() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
namespace Tegra::Engines {
|
||||
|
||||
#define FERMI2D_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::Fermi2D::Regs, field_name) / sizeof(u32))
|
||||
@@ -110,5 +109,4 @@ ASSERT_REG_POSITION(operation, 0xAB);
|
||||
ASSERT_REG_POSITION(trigger, 0xB5);
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
namespace Tegra::Engines {
|
||||
|
||||
#define MAXWELL3D_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32))
|
||||
@@ -898,5 +897,4 @@ ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
namespace Tegra::Engines {
|
||||
|
||||
class MaxwellCompute final {
|
||||
public:
|
||||
@@ -18,5 +17,4 @@ public:
|
||||
void WriteReg(u32 method, u32 value);
|
||||
};
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
namespace Tegra::Engines {
|
||||
|
||||
class MaxwellDMA final {
|
||||
public:
|
||||
@@ -151,5 +150,4 @@ ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Shader {
|
||||
namespace Tegra::Shader {
|
||||
|
||||
struct Register {
|
||||
/// Number of registers
|
||||
@@ -109,8 +108,7 @@ union Sampler {
|
||||
u64 value{};
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Shader
|
||||
|
||||
namespace std {
|
||||
|
||||
@@ -127,8 +125,7 @@ struct make_unsigned<Tegra::Shader::Register> {
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace Tegra {
|
||||
namespace Shader {
|
||||
namespace Tegra::Shader {
|
||||
|
||||
enum class Pred : u64 {
|
||||
UnusedIndex = 0x7,
|
||||
@@ -784,5 +781,4 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Shader
|
||||
|
||||
@@ -20,7 +20,11 @@ GPU::GPU() {
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
const Tegra::Engines::Maxwell3D& GPU::Get3DEngine() const {
|
||||
const Engines::Maxwell3D& GPU::Maxwell3D() const {
|
||||
return *maxwell_3d;
|
||||
}
|
||||
|
||||
Engines::Maxwell3D& GPU::Maxwell3D() {
|
||||
return *maxwell_3d;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,15 +93,14 @@ public:
|
||||
/// Processes a command list stored at the specified address in GPU memory.
|
||||
void ProcessCommandList(GPUVAddr address, u32 size);
|
||||
|
||||
/// Returns a const reference to the Maxwell3D GPU engine.
|
||||
const Engines::Maxwell3D& Maxwell3D() const;
|
||||
|
||||
/// Returns a reference to the Maxwell3D GPU engine.
|
||||
const Engines::Maxwell3D& Get3DEngine() const;
|
||||
Engines::Maxwell3D& Maxwell3D();
|
||||
|
||||
std::unique_ptr<MemoryManager> memory_manager;
|
||||
|
||||
Engines::Maxwell3D& Maxwell3D() {
|
||||
return *maxwell_3d;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Writes a single register in the engine bound to the specified subchannel
|
||||
void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);
|
||||
|
||||
@@ -634,8 +634,8 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
|
||||
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program,
|
||||
u32 current_bindpoint,
|
||||
const std::vector<GLShader::ConstBufferEntry>& entries) {
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
auto& maxwell3d = gpu.Get3DEngine();
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
|
||||
// Reset all buffer draw state for this stage.
|
||||
for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) {
|
||||
@@ -644,7 +644,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
|
||||
}
|
||||
|
||||
// Upload only the enabled buffers from the 16 constbuffers of each shader stage
|
||||
auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
|
||||
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
|
||||
|
||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& used_buffer = entries[bindpoint];
|
||||
@@ -700,8 +700,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
|
||||
|
||||
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit,
|
||||
const std::vector<GLShader::SamplerEntry>& entries) {
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
auto& maxwell3d = gpu.Get3DEngine();
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
|
||||
ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
|
||||
"Exceeded the number of active textures.");
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
|
||||
namespace GLShader {
|
||||
namespace Decompiler {
|
||||
namespace GLShader::Decompiler {
|
||||
|
||||
using Tegra::Shader::Attribute;
|
||||
using Tegra::Shader::Instruction;
|
||||
@@ -1845,5 +1844,4 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
} // namespace Decompiler
|
||||
} // namespace GLShader
|
||||
} // namespace GLShader::Decompiler
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
|
||||
namespace GLShader {
|
||||
namespace Decompiler {
|
||||
namespace GLShader::Decompiler {
|
||||
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
|
||||
@@ -23,5 +22,4 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
|
||||
Maxwell3D::Regs::ShaderStage stage,
|
||||
const std::string& suffix);
|
||||
|
||||
} // namespace Decompiler
|
||||
} // namespace GLShader
|
||||
} // namespace GLShader::Decompiler
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
namespace GLShader {
|
||||
|
||||
namespace Impl {
|
||||
void SetShaderUniformBlockBinding(GLuint shader, const char* name,
|
||||
Maxwell3D::Regs::ShaderStage binding, size_t expected_size) {
|
||||
static void SetShaderUniformBlockBinding(GLuint shader, const char* name,
|
||||
Maxwell3D::Regs::ShaderStage binding,
|
||||
size_t expected_size) {
|
||||
GLuint ub_index = glGetUniformBlockIndex(shader, name);
|
||||
if (ub_index != GL_INVALID_INDEX) {
|
||||
GLint ub_size = 0;
|
||||
|
||||
@@ -21,7 +21,6 @@ using Tegra::Engines::Maxwell3D;
|
||||
|
||||
namespace Impl {
|
||||
void SetShaderUniformBlockBindings(GLuint shader);
|
||||
void SetShaderSamplerBindings(GLuint shader);
|
||||
} // namespace Impl
|
||||
|
||||
/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include "video_core/textures/decoders.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Texture {
|
||||
namespace Tegra::Texture {
|
||||
|
||||
/**
|
||||
* Calculates the offset of an (x, y) position within a swizzled texture.
|
||||
@@ -186,5 +185,4 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
return rgba_data;
|
||||
}
|
||||
|
||||
} // namespace Texture
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Texture
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Texture {
|
||||
namespace Tegra::Texture {
|
||||
|
||||
/**
|
||||
* Unswizzles a swizzled texture without changing its format.
|
||||
@@ -33,5 +32,4 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
|
||||
std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
|
||||
u32 height);
|
||||
|
||||
} // namespace Texture
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Texture
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Texture {
|
||||
namespace Tegra::Texture {
|
||||
|
||||
enum class TextureFormat : u32 {
|
||||
R32_G32_B32_A32 = 0x01,
|
||||
@@ -260,5 +259,4 @@ struct FullTextureInfo {
|
||||
/// Returns the number of bytes per pixel of the input texture format.
|
||||
u32 BytesPerPixel(TextureFormat format);
|
||||
|
||||
} // namespace Texture
|
||||
} // namespace Tegra
|
||||
} // namespace Tegra::Texture
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini";
|
||||
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
|
||||
FileUtil::CreateFullPath(qt_config_loc);
|
||||
qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
|
||||
ui->setupUi(this);
|
||||
this->setConfiguration();
|
||||
connect(ui->open_log_button, &QPushButton::pressed, []() {
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(D_LOGS_IDX));
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -336,9 +336,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
|
||||
// TODO: Store a reference to the registers in the debug context instead of accessing them
|
||||
// directly...
|
||||
|
||||
auto& registers = gpu.Get3DEngine().regs;
|
||||
auto& rt = registers.rt[static_cast<size_t>(surface_source) -
|
||||
static_cast<size_t>(Source::RenderTarget0)];
|
||||
const auto& registers = gpu.Maxwell3D().regs;
|
||||
const auto& rt = registers.rt[static_cast<size_t>(surface_source) -
|
||||
static_cast<size_t>(Source::RenderTarget0)];
|
||||
|
||||
surface_address = rt.Address();
|
||||
surface_width = rt.width;
|
||||
|
||||
@@ -394,7 +394,7 @@ void GameList::RefreshGameDirectory() {
|
||||
}
|
||||
|
||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
|
||||
const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
|
||||
const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
|
||||
|
||||
@@ -909,6 +909,16 @@ void GMainWindow::UpdateUITheme() {
|
||||
#undef main
|
||||
#endif
|
||||
|
||||
static void InitializeLogging() {
|
||||
Log::Filter log_filter;
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
|
||||
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
|
||||
FileUtil::CreateFullPath(log_dir);
|
||||
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
MicroProfileOnThreadCreate("Frontend");
|
||||
SCOPE_EXIT({ MicroProfileShutdown(); });
|
||||
@@ -927,13 +937,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
GMainWindow main_window;
|
||||
// After settings have been loaded by GMainWindow, apply the filter
|
||||
Log::Filter log_filter;
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
|
||||
Log::AddBackend(
|
||||
std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
|
||||
|
||||
InitializeLogging();
|
||||
main_window.show();
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini";
|
||||
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini";
|
||||
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
|
||||
|
||||
Reload();
|
||||
|
||||
@@ -56,6 +56,18 @@ static void PrintVersion() {
|
||||
std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
|
||||
}
|
||||
|
||||
static void InitializeLogging() {
|
||||
Log::Filter log_filter(Log::Level::Debug);
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
|
||||
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
|
||||
|
||||
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
|
||||
FileUtil::CreateFullPath(log_dir);
|
||||
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
|
||||
}
|
||||
|
||||
/// Application entry point
|
||||
int main(int argc, char** argv) {
|
||||
Config config;
|
||||
@@ -124,14 +136,7 @@ int main(int argc, char** argv) {
|
||||
LocalFree(argv_w);
|
||||
#endif
|
||||
|
||||
Log::Filter log_filter(Log::Level::Debug);
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
|
||||
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
|
||||
FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
|
||||
Log::AddBackend(
|
||||
std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
|
||||
InitializeLogging();
|
||||
|
||||
MicroProfileOnThreadCreate("EmuThread");
|
||||
SCOPE_EXIT({ MicroProfileShutdown(); });
|
||||
|
||||
Reference in New Issue
Block a user