Compare commits

..

7 Commits

Author SHA1 Message Date
ReinUsesLisp
bc699ace15 service/bsd: Handle Poll with no entries accurately
Testing shows that Poll called with zero entries returns -1 and signals
an errno of zero.
2020-07-28 01:51:47 -03:00
ReinUsesLisp
f7d59f3e0e services/bsd: Implement most of bsd:s
This implements: Socket, Poll, Accept, Bind, Connect, GetPeerName,
GetSockName, Listen, Fcntl, SetSockOpt, Shutdown, Recv, RecvFrom,
Send, SendTo, Write, and Close

The implementation was done referencing: SwIPC, switchbrew, testing
with libnx and inspecting its code, general information about bsd
sockets online, and analysing official software.

Not everything from these service calls is implemented, but everything
that is not implemented will be logged in some way.
2020-07-28 01:48:42 -03:00
ReinUsesLisp
2c67bbf609 service/sockets: Add worker pool abstraction
Manage worker threads with an easy to use abstraction.
We can expand this to support thread deletion in the future.
2020-07-28 01:47:03 -03:00
ReinUsesLisp
5692c48ab7 service/sockets: Add worker abstraction to execute blocking calls asynchronously
This abstraction allows executing blocking functions (like recvfrom on a
socket configured for blocking) without blocking the service thread.
It is intended to be used with SleepClientThread.
2020-07-28 01:47:03 -03:00
ReinUsesLisp
80b4bd3583 service/sockets: Add translate functions
These functions translate from Network enumerations/structures to guest
enumerations/structures and viceversa.
2020-07-28 01:47:03 -03:00
ReinUsesLisp
22263ccaa4 service/sockets: Add enumerations and structures
Add guest enumerations and structures used in socket services
2020-07-28 01:47:03 -03:00
ReinUsesLisp
ef8acc9c3d services/nifm: Implement GetCurrentIpAddress
This is trivially implemented using the Network abstraction

- Used by ftpd
2020-07-28 01:47:03 -03:00
120 changed files with 2889 additions and 1915 deletions

View File

@@ -7,7 +7,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis
fi
# Default clang-format points to default 3.5 version one
CLANG_FORMAT=clang-format-10.0
CLANG_FORMAT=clang-format-6.0
$CLANG_FORMAT --version
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then

View File

@@ -7,7 +7,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis*
fi
# Default clang-format points to default 3.5 version one
CLANG_FORMAT=clang-format-10.0
CLANG_FORMAT=clang-format-6.0
$CLANG_FORMAT --version
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then

View File

@@ -159,15 +159,15 @@ macro(yuzu_find_packages)
# Capitalization matters here. We need the naming to match the generated paths from Conan
set(REQUIRED_LIBS
# Cmake Pkg Prefix Version Conan Pkg
"Boost 1.73 boost/1.73.0"
"Catch2 2.13 catch2/2.13.0"
"Boost 1.71 boost/1.72.0"
"Catch2 2.11 catch2/2.11.0"
"fmt 7.0 fmt/7.0.1"
# can't use until https://github.com/bincrafters/community/issues/1173
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
"lz4 1.8 lz4/1.9.2"
"nlohmann_json 3.8 nlohmann_json/3.8.0"
"nlohmann_json 3.7 nlohmann_json/3.7.3"
"ZLIB 1.2 zlib/1.2.11"
"zstd 1.4 zstd/1.4.5"
"zstd 1.4 zstd/1.4.4"
)
foreach(PACKAGE ${REQUIRED_LIBS})

View File

@@ -62,12 +62,6 @@ else()
-Wno-unused-parameter
)
# TODO: Remove when we update to a GCC compiler that enables this
# by default (i.e. GCC 10 or newer).
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
add_compile_options(-fconcepts)
endif()
if (ARCHITECTURE_x86_64)
add_compile_options("-mcx16")
endif()

View File

@@ -110,7 +110,6 @@ add_library(common STATIC
common_funcs.h
common_paths.h
common_types.h
concepts.h
dynamic_library.cpp
dynamic_library.h
fiber.cpp

View File

@@ -14,55 +14,50 @@ namespace Common {
#if _MSC_VER
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
const u8 result =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) {
u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
const u16 result =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) {
u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
const u32 result =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) {
u32 result = _InterlockedCompareExchange((long*)pointer, value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
value, expected);
bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
value[0],
reinterpret_cast<__int64*>(expected.data())) != 0;
bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) {
return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0],
(__int64*)expected.data()) != 0;
}
#else
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
std::memcpy(&value_a, value.data(), sizeof(u128));

View File

@@ -8,10 +8,10 @@
namespace Common {
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected);
bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected);
bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected);
bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected);
bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected);
} // namespace Common

View File

@@ -35,7 +35,6 @@
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define DUMP_DIR "dump"
#define SCREENSHOTS_DIR "screenshots"
#define SHADER_DIR "shader"
#define LOG_DIR "log"

View File

@@ -1,34 +0,0 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Common {
#include <type_traits>
// Check if type is like an STL container
template <typename T>
concept IsSTLContainer = requires(T t) {
typename T::value_type;
typename T::iterator;
typename T::const_iterator;
// TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
t.begin();
t.end();
t.cbegin();
t.cend();
t.data();
t.size();
};
// TODO: Replace with std::derived_from when the <concepts> header
// is available on all supported platforms.
template <typename Derived, typename Base>
concept DerivedFrom = requires {
std::is_base_of_v<Base, Derived>;
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
};
} // namespace Common

View File

@@ -695,7 +695,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
paths.emplace(UserPath::ScreenshotsDir, user_path + SCREENSHOTS_DIR DIR_SEP);
paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);

View File

@@ -32,7 +32,6 @@ enum class UserPath {
SDMCDir,
LoadDir,
DumpDir,
ScreenshotsDir,
ShaderDir,
SysDataDir,
UserDir,

View File

@@ -3,9 +3,21 @@
// Refer to the license.txt file included.
#include "common/hex_util.h"
#include "common/logging/log.h"
namespace Common {
u8 ToHexNibble(char c1) {
if (c1 >= 65 && c1 <= 70)
return c1 - 55;
if (c1 >= 97 && c1 <= 102)
return c1 - 87;
if (c1 >= 48 && c1 <= 57)
return c1 - 48;
LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
return 0;
}
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
std::vector<u8> out(str.size() / 2);
if (little_endian) {
@@ -18,4 +30,26 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
return out;
}
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
if (len != 32) {
LOG_ERROR(Common,
"Attempting to parse string to array that is not of correct size (expected=32, "
"actual={}).",
len);
return {};
}
return HexStringToArray<16>(str);
}
std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
if (len != 64) {
LOG_ERROR(Common,
"Attempting to parse string to array that is not of correct size (expected=64, "
"actual={}).",
len);
return {};
}
return HexStringToArray<32>(str);
}
} // namespace Common

View File

@@ -14,31 +14,19 @@
namespace Common {
constexpr u8 ToHexNibble(char c) {
if (c >= 65 && c <= 70) {
return c - 55;
}
if (c >= 97 && c <= 102) {
return c - 87;
}
return c - 48;
}
u8 ToHexNibble(char c1);
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
template <std::size_t Size, bool le = false>
constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
if constexpr (le) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
} else {
for (std::size_t i = 0; i < 2 * Size; i += 2) {
for (std::size_t i = 0; i < 2 * Size; i += 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
}
return out;
}
@@ -60,12 +48,7 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) {
return out;
}
constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
return HexStringToArray<16>(data);
}
constexpr std::array<u8, 32> AsArray(const char (&data)[65]) {
return HexStringToArray<32>(data);
}
std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
} // namespace Common

View File

@@ -113,19 +113,19 @@ private:
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string message) const {
using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::steady_clock;
return {
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
.log_class = log_class,
.log_level = log_level,
.filename = filename,
.line_num = line_nr,
.function = function,
.message = std::move(message),
.final_entry = false,
};
Entry entry;
entry.timestamp =
duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
entry.log_class = log_class;
entry.log_level = log_level;
entry.filename = filename;
entry.line_num = line_nr;
entry.function = function;
entry.message = std::move(message);
return entry;
}
std::mutex writing_mutex;

View File

@@ -21,13 +21,19 @@ class Filter;
*/
struct Entry {
std::chrono::microseconds timestamp;
Class log_class{};
Level log_level{};
const char* filename = nullptr;
unsigned int line_num = 0;
Class log_class;
Level log_level;
const char* filename;
unsigned int line_num;
std::string function;
std::string message;
bool final_entry = false;
Entry() = default;
Entry(Entry&& o) = default;
Entry& operator=(Entry&& o) = default;
Entry& operator=(const Entry& o) = default;
};
/**

View File

@@ -491,6 +491,7 @@ add_library(core STATIC
hle/service/sm/controller.h
hle/service/sm/sm.cpp
hle/service/sm/sm.h
hle/service/sockets/blocking_worker.h
hle/service/sockets/bsd.cpp
hle/service/sockets/bsd.h
hle/service/sockets/ethc.cpp
@@ -501,6 +502,8 @@ add_library(core STATIC
hle/service/sockets/sfdnsres.h
hle/service/sockets/sockets.cpp
hle/service/sockets/sockets.h
hle/service/sockets/sockets_translate.cpp
hle/service/sockets/sockets_translate.h
hle/service/spl/csrng.cpp
hle/service/spl/csrng.h
hle/service/spl/module.cpp

View File

@@ -146,7 +146,7 @@ struct System::Impl {
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
device_memory = std::make_unique<Core::DeviceMemory>();
device_memory = std::make_unique<Core::DeviceMemory>(system);
is_multicore = Settings::values.use_multi_core.GetValue();
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();

View File

@@ -52,15 +52,15 @@ void CpuManager::Shutdown() {
}
std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
return GuestThreadFunction;
return std::function<void(void*)>(GuestThreadFunction);
}
std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
return IdleThreadFunction;
return std::function<void(void*)>(IdleThreadFunction);
}
std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
return SuspendThreadFunction;
return std::function<void(void*)>(SuspendThreadFunction);
}
void CpuManager::GuestThreadFunction(void* cpu_manager_) {

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <mbedtls/cipher.h>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -11,10 +10,8 @@
namespace Core::Crypto {
namespace {
using NintendoTweak = std::array<u8, 16>;
NintendoTweak CalculateNintendoTweak(std::size_t sector_id) {
NintendoTweak out{};
std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) {
std::vector<u8> out(0x10);
for (std::size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
@@ -66,6 +63,13 @@ AESCipher<Key, KeySize>::~AESCipher() {
mbedtls_cipher_free(&ctx->decryption_context);
}
template <typename Key, std::size_t KeySize>
void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) ||
mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
"Failed to set IV on mbedtls ciphers.");
}
template <typename Key, std::size_t KeySize>
void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const {
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
@@ -120,13 +124,6 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
}
}
template <typename Key, std::size_t KeySize>
void AESCipher<Key, KeySize>::SetIVImpl(const u8* data, std::size_t size) {
ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, data, size) ||
mbedtls_cipher_set_iv(&ctx->decryption_context, data, size)) == 0,
"Failed to set IV on mbedtls ciphers.");
}
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto

View File

@@ -6,6 +6,7 @@
#include <memory>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
@@ -31,12 +32,10 @@ class AESCipher {
public:
AESCipher(Key key, Mode mode);
~AESCipher();
template <typename ContiguousContainer>
void SetIV(const ContiguousContainer& container) {
SetIVImpl(std::data(container), std::size(container));
}
void SetIV(std::vector<u8> iv);
template <typename Source, typename Dest>
void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const {
@@ -60,8 +59,6 @@ public:
std::size_t sector_size, Op op);
private:
void SetIVImpl(const u8* data, std::size_t size);
std::unique_ptr<CipherContext> ctx;
};
} // namespace Core::Crypto

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "core/crypto/ctr_encryption_layer.h"
@@ -11,7 +10,8 @@ namespace Core::Crypto {
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_,
std::size_t base_offset)
: EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR) {}
: EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
iv(16, 0) {}
std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
if (length == 0)
@@ -39,8 +39,9 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
return read + Read(data + read, length - read, offset + read);
}
void CTREncryptionLayer::SetIV(const IVData& iv_) {
iv = iv_;
void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
const auto length = std::min(iv_.size(), iv.size());
iv.assign(iv_.cbegin(), iv_.cbegin() + length);
}
void CTREncryptionLayer::UpdateIV(std::size_t offset) const {

View File

@@ -4,8 +4,7 @@
#pragma once
#include <array>
#include <vector>
#include "core/crypto/aes_util.h"
#include "core/crypto/encryption_layer.h"
#include "core/crypto/key_manager.h"
@@ -15,20 +14,18 @@ namespace Core::Crypto {
// Sits on top of a VirtualFile and provides CTR-mode AES decription.
class CTREncryptionLayer : public EncryptionLayer {
public:
using IVData = std::array<u8, 16>;
CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset);
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
void SetIV(const IVData& iv);
void SetIV(const std::vector<u8>& iv);
private:
std::size_t base_offset;
// Must be mutable as operations modify cipher contexts.
mutable AESCipher<Key128> cipher;
mutable IVData iv{};
mutable std::vector<u8> iv;
void UpdateIV(std::size_t offset) const;
};

View File

@@ -40,14 +40,12 @@ namespace Core::Crypto {
constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
constexpr u64 FULL_TICKET_SIZE = 0x400;
using Common::AsArray;
using namespace Common;
// clang-format off
constexpr std::array eticket_source_hashes{
AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source
AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source
const std::array<SHA256Hash, 2> eticket_source_hashes{
"B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
"E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
};
// clang-format on
const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
{{S128KeyType::Master, 0}, "master_key_"},

View File

@@ -27,7 +27,7 @@
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
using Common::AsArray;
using namespace Common;
namespace Core::Crypto {
@@ -47,123 +47,105 @@ struct Package2Header {
};
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
// clang-format off
constexpr std::array source_hashes{
AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source
AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source
AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source
AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source
AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source
AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source
AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source
AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source
AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source
AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source
AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source
AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source
AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source
AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source
AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3
AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0
const std::array<SHA256Hash, 0x10> source_hashes{
"B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
"7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
"21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
"FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
"FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
"C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
"04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
"FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
"1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
"6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
"D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
"2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
"1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
"8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
"D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
"FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
};
// clang-format on
// clang-format off
constexpr std::array keyblob_source_hashes{
AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00
AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01
AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02
AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03
AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04
AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07
const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
"8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
"2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
"61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
"8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
"95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
"3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
};
// clang-format on
// clang-format off
constexpr std::array master_key_hashes{
AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00
AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01
AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02
AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03
AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04
AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05
AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06
AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07
const std::array<SHA256Hash, 0x20> master_key_hashes{
"0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
"4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
"79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
"4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
"75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
"EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08
AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09
AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E
AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
};
// clang-format on
static constexpr u8 CalculateMaxKeyblobSourceHash() {
const auto is_zero = [](const auto& data) {
// TODO: Replace with std::all_of whenever mingw decides to update their
// libraries to include the constexpr variant of it.
for (const auto element : data) {
if (element != 0) {
return false;
}
}
return true;
};
static u8 CalculateMaxKeyblobSourceHash() {
for (s8 i = 0x1F; i >= 0; --i) {
if (!is_zero(keyblob_source_hashes[i])) {
if (keyblob_source_hashes[i] != SHA256Hash{})
return static_cast<u8>(i + 1);
}
}
return 0;
@@ -364,9 +346,10 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con
}
static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
Package2Header temp = header;
AESCipher<Key128> cipher(key, Mode::CTR);
cipher.SetIV(header.header_ctr);
cipher.SetIV(iv);
cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
Op::Decrypt);
if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
@@ -405,7 +388,7 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
auto c = a->ReadAllBytes();
AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
cipher.SetIV(header.section_ctr[1]);
cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);

View File

@@ -2,11 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/device_memory.h"
#include "core/memory.h"
namespace Core {
DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {}
DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {}
DeviceMemory::~DeviceMemory() = default;
} // namespace Core

View File

@@ -4,11 +4,14 @@
#pragma once
#include "common/common_types.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/virtual_buffer.h"
namespace Core {
class System;
namespace DramMemoryMap {
enum : u64 {
Base = 0x80000000ULL,
@@ -23,7 +26,7 @@ enum : u64 {
class DeviceMemory : NonCopyable {
public:
explicit DeviceMemory();
explicit DeviceMemory(Core::System& system);
~DeviceMemory();
template <typename T>
@@ -42,6 +45,7 @@ public:
private:
Common::VirtualBuffer<u8> buffer;
Core::System& system;
};
} // namespace Core

View File

@@ -495,10 +495,9 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
starting_offset);
Core::Crypto::CTREncryptionLayer::IVData iv{};
for (std::size_t i = 0; i < 8; ++i) {
iv[i] = s_header.raw.section_ctr[8 - i - 1];
}
std::vector<u8> iv(16);
for (u8 i = 0; i < 8; ++i)
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
out->SetIV(iv);
return std::static_pointer_cast<VfsFile>(out);
}

View File

@@ -4,7 +4,6 @@
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace FileSys {
@@ -12,11 +11,13 @@ namespace FileSys {
enum class Mode : u32 {
Read = 1,
Write = 2,
ReadWrite = Read | Write,
ReadWrite = 3,
Append = 4,
WriteAppend = Write | Append,
WriteAppend = 6,
};
DECLARE_ENUM_FLAG_OPERATORS(Mode)
inline u32 operator&(Mode lhs, Mode rhs) {
return static_cast<u32>(lhs) & static_cast<u32>(rhs);
}
} // namespace FileSys

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstring>
@@ -67,7 +66,7 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR);
// Calculate AES IV
std::array<u8, 16> iv{};
std::vector<u8> iv(16);
auto subsection_ctr = subsection.ctr;
auto offset_iv = section_offset + base_offset;
for (std::size_t i = 0; i < section_ctr.size(); ++i)

View File

@@ -288,8 +288,8 @@ std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
}
Core::Memory::TextCheatParser parser;
return parser.Parse(system,
std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
return parser.Parse(
system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));
}
} // Anonymous namespace

View File

@@ -344,18 +344,15 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
ContentRecordType type) {
const auto cmnt_iter = map.find(title_id);
if (cmnt_iter == map.cend()) {
return std::nullopt;
}
if (map.find(title_id) == map.end())
return {};
const auto& cnmt = cmnt_iter->second;
const auto& content_records = cnmt.GetContentRecords();
const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
const auto& cnmt = map.at(title_id);
const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(),
[type](const ContentRecord& rec) { return rec.type == type; });
if (iter == content_records.cend()) {
return std::nullopt;
}
if (iter == cnmt.GetContentRecords().end())
return {};
return std::make_optional(iter->nca_id);
}
@@ -470,16 +467,14 @@ VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType ty
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
const auto meta_iter = meta.find(title_id);
if (meta_iter != meta.cend()) {
if (meta_iter != meta.end())
return meta_iter->second.GetTitleVersion();
}
const auto yuzu_meta_iter = yuzu_meta.find(title_id);
if (yuzu_meta_iter != yuzu_meta.cend()) {
if (yuzu_meta_iter != yuzu_meta.end())
return yuzu_meta_iter->second.GetTitleVersion();
}
return std::nullopt;
return {};
}
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
@@ -552,6 +547,56 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
}
bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
const auto delete_nca = [this](const NcaID& id) {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
if (dir->GetFileRelative(path) == nullptr) {
return false;
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
const auto dirname = fmt::format("000000{:02X}", hash[0]);
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
return res;
};
// If an entry exists in the registered cache, remove it
if (HasEntry(title_id, ContentRecordType::Meta)) {
LOG_INFO(Loader,
"Previously installed entry (v{}) for title_id={:016X} detected! "
"Attempting to remove...",
GetEntryVersion(title_id).value_or(0), title_id);
// Get all the ncas associated with the current CNMT and delete them
const auto meta_old_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
const auto program_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
const auto data_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
const auto control_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
const auto html_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
const auto legal_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
delete_nca(meta_old_id);
delete_nca(program_id);
delete_nca(data_id);
delete_nca(control_id);
delete_nca(html_id);
delete_nca(legal_id);
return true;
}
return false;
}
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
const auto ncas = nsp.GetNCAsCollapsed();
@@ -647,57 +692,6 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
}
bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
const auto delete_nca = [this](const NcaID& id) {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const bool isFile = dir->GetFileRelative(path) != nullptr;
const bool isDir = dir->GetDirectoryRelative(path) != nullptr;
if (isFile) {
return dir->DeleteFile(path);
} else if (isDir) {
return dir->DeleteSubdirectoryRecursive(path);
}
return false;
};
// If an entry exists in the registered cache, remove it
if (HasEntry(title_id, ContentRecordType::Meta)) {
LOG_INFO(Loader,
"Previously installed entry (v{}) for title_id={:016X} detected! "
"Attempting to remove...",
GetEntryVersion(title_id).value_or(0), title_id);
// Get all the ncas associated with the current CNMT and delete them
const auto meta_old_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
const auto program_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
const auto data_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
const auto control_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
const auto html_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
const auto legal_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
const auto deleted_meta = delete_nca(meta_old_id);
const auto deleted_program = delete_nca(program_id);
const auto deleted_data = delete_nca(data_id);
const auto deleted_control = delete_nca(control_id);
const auto deleted_html = delete_nca(html_id);
const auto deleted_legal = delete_nca(legal_id);
return deleted_meta && (deleted_meta || deleted_program || deleted_data ||
deleted_control || deleted_html || deleted_legal);
}
return false;
}
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
std::optional<NcaID> override_id) {

View File

@@ -155,6 +155,9 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const override;
// Removes an existing entry based on title id
bool RemoveExistingEntry(u64 title_id);
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
@@ -169,9 +172,6 @@ public:
InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
// Removes an existing entry based on title id
bool RemoveExistingEntry(u64 title_id) const;
private:
template <typename T>
void IterateAllMetadata(std::vector<T>& out,

View File

@@ -17,23 +17,23 @@ constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
namespace {
void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) {
void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataAttribute, type is "
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).",
meta.zero_1);
}
if (meta.zero_2 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataAttribute, type is "
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).",
meta.zero_2);
}
if (meta.zero_3 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataAttribute, type is "
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).",
meta.zero_3);
}
@@ -41,32 +41,33 @@ void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) {
if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataAttribute, type is SystemSaveData but title_id is "
"Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is "
"non-zero ({:016X}).",
meta.title_id);
}
if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataAttribute, type is DeviceSaveData but user_id is "
"Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
"non-zero ({:016X}{:016X})",
meta.user_id[1], meta.user_id[0]);
}
}
bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataAttribute& attr) {
return attr.type == SaveDataType::CacheStorage || attr.type == SaveDataType::TemporaryStorage ||
bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) {
return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage ||
(space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
(attr.type == SaveDataType::SaveData || attr.type == SaveDataType::DeviceSaveData) &&
attr.title_id == 0 && attr.save_id == 0);
(desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) &&
desc.title_id == 0 && desc.save_id == 0);
}
} // Anonymous namespace
std::string SaveDataAttribute::DebugInfo() const {
return fmt::format("[title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, type={:02X}, "
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
"save_id={:016X}, "
"rank={}, index={}]",
title_id, user_id[1], user_id[0], save_id, static_cast<u8>(type),
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
static_cast<u8>(rank), index);
}
@@ -79,8 +80,8 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
const SaveDataAttribute& meta) const {
PrintSaveDataAttributeWarnings(meta);
const SaveDataDescriptor& meta) const {
PrintSaveDataDescriptorWarnings(meta);
const auto save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
@@ -97,7 +98,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
}
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
const SaveDataAttribute& meta) const {
const SaveDataDescriptor& meta) const {
const auto save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);

View File

@@ -21,7 +21,6 @@ enum class SaveDataSpaceId : u8 {
TemporaryStorage = 3,
SdCardUser = 4,
ProperSystem = 100,
SafeMode = 101,
};
enum class SaveDataType : u8 {
@@ -31,50 +30,28 @@ enum class SaveDataType : u8 {
DeviceSaveData = 3,
TemporaryStorage = 4,
CacheStorage = 5,
SystemBcat = 6,
};
enum class SaveDataRank : u8 {
Primary = 0,
Secondary = 1,
Primary,
Secondary,
};
enum class SaveDataFlags : u32 {
None = (0 << 0),
KeepAfterResettingSystemSaveData = (1 << 0),
KeepAfterRefurbishment = (1 << 1),
KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2),
NeedsSecureDelete = (1 << 3),
};
struct SaveDataAttribute {
u64 title_id;
struct SaveDataDescriptor {
u64_le title_id;
u128 user_id;
u64 save_id;
u64_le save_id;
SaveDataType type;
SaveDataRank rank;
u16 index;
u16_le index;
INSERT_PADDING_BYTES(4);
u64 zero_1;
u64 zero_2;
u64 zero_3;
u64_le zero_1;
u64_le zero_2;
u64_le zero_3;
std::string DebugInfo() const;
};
static_assert(sizeof(SaveDataAttribute) == 0x40, "SaveDataAttribute has incorrect size.");
struct SaveDataExtraData {
SaveDataAttribute attr;
u64 owner_id;
s64 timestamp;
SaveDataFlags flags;
INSERT_PADDING_BYTES(4);
s64 available_size;
s64 journal_size;
s64 commit_id;
std::array<u8, 0x190> unused;
};
static_assert(sizeof(SaveDataExtraData) == 0x200, "SaveDataExtraData has incorrect size.");
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
struct SaveDataSize {
u64 normal;
@@ -87,8 +64,8 @@ public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;

View File

@@ -27,12 +27,18 @@ VirtualDir MiiModel() {
auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{},
std::vector<VirtualDir>{}, "data");
out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
out->AddFile(MakeArrayFile(MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
out->AddFile(MakeArrayFile(MiiModelData::SHAPE_MID, "ShapeMid.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>(
MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>(
MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>(
MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>(
MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>(
MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>(
MiiModelData::SHAPE_MID, "ShapeMid.dat"));
return out;
}

View File

@@ -24,18 +24,19 @@ constexpr std::array<u8, 30> WORD_TXT{
} // namespace NgWord1Data
VirtualDir NgWord1() {
std::vector<VirtualFile> files;
files.reserve(NgWord1Data::NUMBER_WORD_TXT_FILES);
std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES);
for (std::size_t i = 0; i < files.size(); ++i) {
files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)));
files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
}
files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, "common.txt"));
files.push_back(MakeArrayFile(NgWord1Data::VERSION_DAT, "version.dat"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
NgWord1Data::WORD_TXT, "common.txt"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>(
NgWord1Data::VERSION_DAT, "version.dat"));
return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
"data");
return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
}
namespace NgWord2Data {
@@ -54,22 +55,27 @@ constexpr std::array<u8, 0x2C> AC_NX_DATA{
} // namespace NgWord2Data
VirtualDir NgWord2() {
std::vector<VirtualFile> files;
files.reserve(NgWord2Data::NUMBER_AC_NX_FILES * 3);
std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3);
for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) {
files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)));
files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)));
files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)));
files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i));
files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i));
files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i));
}
files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
files.push_back(MakeArrayFile(NgWord2Data::VERSION_DAT, "version.dat"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>(
NgWord2Data::VERSION_DAT, "version.dat"));
return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{},
"data");
return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
}
} // namespace FileSys::SystemArchive

View File

@@ -2,9 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <vector>
#include "common/swap.h"
#include "core/file_sys/system_archive/time_zone_binary.h"
#include "core/file_sys/vfs_vector.h"
@@ -618,49 +615,43 @@ static constexpr std::array<u8, 9633> LOCATION_NAMES{
0x0a};
static VirtualFile GenerateDefaultTimeZoneFile() {
struct TimeZoneInfo {
struct {
s64_be at;
std::array<u8, 7> padding1;
INSERT_PADDING_BYTES(7);
std::array<char, 4> time_zone_chars;
std::array<u8, 2> padding2;
INSERT_PADDING_BYTES(2);
std::array<char, 6> time_zone_name;
};
} time_zone_info{};
VirtualFile file{std::make_shared<VectorVfsFile>(
std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(TimeZoneInfo)),
const VirtualFile file{std::make_shared<VectorVfsFile>(
std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(time_zone_info)),
"GMT")};
const Service::Time::TimeZone::TzifHeader header{
.magic = 0x545a6966,
.version = 0x32,
.ttis_gmt_count = 1,
.ttis_std_count = 1,
.time_count = 1,
.type_count = 1,
.char_count = 4,
};
Service::Time::TimeZone::TzifHeader header{};
header.magic = 0x545a6966;
header.version = 0x32;
header.ttis_gmt_count = 0x1;
header.ttis_std_count = 0x1;
header.time_count = 0x1;
header.type_count = 0x1;
header.char_count = 0x4;
file->WriteObject(header, 0);
const TimeZoneInfo time_zone_info{
.at = 0xf8,
.padding1 = {},
.time_zone_chars = {'G', 'M', 'T', '\0'},
.padding2 = {},
.time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'},
};
time_zone_info.at = 0xf8;
time_zone_info.time_zone_chars = {'G', 'M', 'T', '\0'};
time_zone_info.time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'};
file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader));
return file;
}
VirtualDir TimeZoneBinary() {
std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{},
"zoneinfo")};
std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")};
return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs),
"data");
const std::vector<VirtualFile> root_files{
std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")};
return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data");
}
} // namespace FileSys::SystemArchive

View File

@@ -18,22 +18,20 @@ static std::string ModeFlagsToString(Mode mode) {
std::string mode_str;
// Calculate the correct open mode for the file.
if (True(mode & Mode::Read) && True(mode & Mode::Write)) {
if (True(mode & Mode::Append)) {
if (mode & Mode::Read && mode & Mode::Write) {
if (mode & Mode::Append)
mode_str = "a+";
} else {
else
mode_str = "r+";
}
} else {
if (True(mode & Mode::Read)) {
if (mode & Mode::Read)
mode_str = "r";
} else if (True(mode & Mode::Append)) {
else if (mode & Mode::Append)
mode_str = "a";
} else if (True(mode & Mode::Write)) {
else if (mode & Mode::Write)
mode_str = "w";
} else {
else
UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
}
}
mode_str += "b";
@@ -75,9 +73,8 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
}
}
if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) {
if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0)
FileUtil::CreateEmptyFile(path);
}
auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
cache[path] = backing;
@@ -250,11 +247,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
}
bool RealVfsFile::IsWritable() const {
return True(perms & Mode::WriteAppend);
return (perms & Mode::WriteAppend) != 0;
}
bool RealVfsFile::IsReadable() const {
return True(perms & Mode::ReadWrite);
return (perms & Mode::ReadWrite) != 0;
}
std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
@@ -322,9 +319,8 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
path_components(FileUtil::SplitPathComponents(path)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) {
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
FileUtil::CreateDir(path);
}
}
RealVfsDirectory::~RealVfsDirectory() = default;
@@ -375,11 +371,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories()
}
bool RealVfsDirectory::IsWritable() const {
return True(perms & Mode::WriteAppend);
return (perms & Mode::WriteAppend) != 0;
}
bool RealVfsDirectory::IsReadable() const {
return True(perms & Mode::ReadWrite);
return (perms & Mode::ReadWrite) != 0;
}
std::string RealVfsDirectory::GetName() const {

View File

@@ -4,11 +4,7 @@
#pragma once
#include <array>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -17,8 +13,7 @@ namespace FileSys {
template <std::size_t size>
class ArrayVfsFile : public VfsFile {
public:
explicit ArrayVfsFile(const std::array<u8, size>& data, std::string name = "",
VirtualDir parent = nullptr)
ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr)
: data(data), name(std::move(name)), parent(std::move(parent)) {}
std::string GetName() const override {
@@ -66,12 +61,6 @@ private:
VirtualDir parent;
};
template <std::size_t Size, typename... Args>
std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data,
Args&&... args) {
return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...);
}
// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
class VectorVfsFile : public VfsFile {
public:

View File

@@ -70,18 +70,14 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
NAX::~NAX() = default;
Loader::ResultStatus NAX::Parse(std::string_view path) {
if (file == nullptr) {
return Loader::ResultStatus::ErrorNullFile;
}
if (file->ReadObject(header.get()) != sizeof(NAXHeader)) {
if (file->ReadObject(header.get()) != sizeof(NAXHeader))
return Loader::ResultStatus::ErrorBadNAXHeader;
}
if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) {
if (header->magic != Common::MakeMagic('N', 'A', 'X', '0'))
return Loader::ResultStatus::ErrorBadNAXHeader;
}
if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) {
if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size)
return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
}
keys.DeriveSDSeedLazy();
std::array<Core::Crypto::Key256, 2> sd_keys{};

View File

@@ -229,8 +229,6 @@ inline void ResponseBuilder::Push(u32 value) {
template <typename T>
void ResponseBuilder::PushRaw(const T& value) {
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
@@ -386,8 +384,6 @@ inline s32 RequestParser::Pop() {
template <typename T>
void RequestParser::PopRaw(T& value) {
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}

View File

@@ -81,7 +81,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
do {
current_value = monitor.ExclusiveRead32(current_core, address);
if (current_value != static_cast<u32>(value)) {
if (current_value != value) {
return ERR_INVALID_STATE;
}
current_value++;

View File

@@ -13,7 +13,6 @@
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
#include "common/concepts.h"
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/object.h"
@@ -194,24 +193,23 @@ public:
/* Helper function to write a buffer using the appropriate buffer descriptor
*
* @tparam T an arbitrary container that satisfies the
* ContiguousContainer concept in the C++ standard library or a trivially copyable type.
* @tparam ContiguousContainer an arbitrary container that satisfies the
* ContiguousContainer concept in the C++ standard library.
*
* @param data The container/data to write into a buffer.
* @param container The container to write the data of into a buffer.
* @param buffer_index The buffer in particular to write to.
*/
template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
if constexpr (Common::IsSTLContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Container to WriteBuffer must contain trivially copyable objects");
return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType),
buffer_index);
} else {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return WriteBuffer(&data, sizeof(T), buffer_index);
}
template <typename ContiguousContainer,
typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>>
std::size_t WriteBuffer(const ContiguousContainer& container,
std::size_t buffer_index = 0) const {
using ContiguousType = typename ContiguousContainer::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Container to WriteBuffer must contain trivially copyable objects");
return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType),
buffer_index);
}
/// Helper function to get the size of the input buffer

View File

@@ -604,6 +604,7 @@ ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_lis
if (const auto result{
Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
result.IsError()) {
const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()};
const std::size_t num_pages{(addr - cur_addr) / PageSize};
ASSERT(

View File

@@ -131,8 +131,7 @@ u32 GlobalScheduler::SelectThreads() {
u32 cores_needing_context_switch{};
for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
Scheduler& sched = kernel.Scheduler(core);
ASSERT(top_threads[core] == nullptr ||
static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core);
if (update_thread(top_threads[core], sched)) {
cores_needing_context_switch |= (1ul << core);
}
@@ -664,26 +663,32 @@ void Scheduler::Reload() {
}
void Scheduler::SwitchContextStep2() {
Thread* previous_thread = current_thread_prev.get();
Thread* new_thread = selected_thread.get();
// Load context of new thread
if (selected_thread) {
ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
Process* const previous_process =
previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr;
if (new_thread) {
ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
"Thread must be runnable.");
// Cancel any outstanding wakeup events for this thread
selected_thread->SetIsRunning(true);
selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
selected_thread->SetWasRunning(false);
new_thread->SetIsRunning(true);
new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
new_thread->SetWasRunning(false);
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (thread_owner_process != nullptr) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
}
if (!selected_thread->IsHLEThread()) {
Core::ARM_Interface& cpu_core = selected_thread->ArmInterface();
cpu_core.LoadContext(selected_thread->GetContext32());
cpu_core.LoadContext(selected_thread->GetContext64());
cpu_core.SetTlsAddress(selected_thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0());
if (!new_thread->IsHLEThread()) {
Core::ARM_Interface& cpu_core = new_thread->ArmInterface();
cpu_core.LoadContext(new_thread->GetContext32());
cpu_core.LoadContext(new_thread->GetContext64());
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core.ChangeProcessorID(this->core_id);
cpu_core.ClearExclusiveState();
}

View File

@@ -286,7 +286,9 @@ protected:
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
ctx.WriteBuffer(data);
std::array<u8, sizeof(ProfileData)> raw_data;
std::memcpy(raw_data.data(), &data, sizeof(ProfileData));
ctx.WriteBuffer(raw_data);
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
@@ -331,7 +333,7 @@ protected:
std::vector<u8> buffer(size);
image.ReadBytes(buffer.data(), buffer.size());
ctx.WriteBuffer(buffer);
ctx.WriteBuffer(buffer.data(), buffer.size());
rb.Push<u32>(size);
}

View File

@@ -58,7 +58,7 @@ ProfileManager::~ProfileManager() {
/// internal management of the users profiles
std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
if (user_count >= MAX_USERS) {
return std::nullopt;
return {};
}
profiles[user_count] = profile;
return user_count++;
@@ -101,14 +101,13 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
[&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) {
return ERROR_USER_ALREADY_EXISTS;
}
return AddUser({
.user_uuid = uuid,
.username = username,
.creation_time = 0,
.data = {},
.is_open = false,
});
ProfileInfo profile;
profile.user_uuid = uuid;
profile.username = username;
profile.data = {};
profile.creation_time = 0x0;
profile.is_open = false;
return AddUser(profile);
}
/// Creates a new user on the system. This function allows a much simpler method of registration
@@ -127,7 +126,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
if (index >= MAX_USERS) {
return std::nullopt;
return {};
}
return profiles[index].user_uuid;
@@ -136,13 +135,13 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
/// Returns a users profile index based on their user id.
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
return std::nullopt;
return {};
}
const auto iter = std::find_if(profiles.begin(), profiles.end(),
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
if (iter == profiles.end()) {
return std::nullopt;
return {};
}
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
@@ -340,13 +339,7 @@ void ProfileManager::ParseUserSaveFile() {
continue;
}
AddUser({
.user_uuid = user.uuid,
.username = user.username,
.creation_time = user.timestamp,
.data = user.extra_data,
.is_open = false,
});
AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false});
}
std::stable_partition(profiles.begin(), profiles.end(),
@@ -357,13 +350,11 @@ void ProfileManager::WriteUserSaveFile() {
ProfileDataRaw raw{};
for (std::size_t i = 0; i < MAX_USERS; ++i) {
raw.users[i] = {
.uuid = profiles[i].user_uuid,
.uuid2 = profiles[i].user_uuid,
.timestamp = profiles[i].creation_time,
.username = profiles[i].username,
.extra_data = profiles[i].data,
};
raw.users[i].username = profiles[i].username;
raw.users[i].uuid2 = profiles[i].user_uuid;
raw.users[i].uuid = profiles[i].user_uuid;
raw.users[i].timestamp = profiles[i].creation_time;
raw.users[i].extra_data = profiles[i].data;
}
const auto raw_path =

View File

@@ -378,11 +378,7 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
}
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto permission = rp.PopEnum<ScreenshotPermission>();
LOG_DEBUG(Service_AM, "called, permission={}", permission);
screenshot_permission = permission;
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -1346,12 +1342,12 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
FileSys::SaveDataAttribute attribute{};
attribute.title_id = system.CurrentProcess()->GetTitleID();
attribute.user_id = user_id;
attribute.type = FileSys::SaveDataType::SaveData;
FileSys::SaveDataDescriptor descriptor{};
descriptor.title_id = system.CurrentProcess()->GetTitleID();
descriptor.user_id = user_id;
descriptor.type = FileSys::SaveDataType::SaveData;
const auto res = system.GetFileSystemController().CreateSaveData(
FileSys::SaveDataSpaceId::NandUser, attribute);
FileSys::SaveDataSpaceId::NandUser, descriptor);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res.Code());

View File

@@ -149,12 +149,6 @@ private:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
enum class ScreenshotPermission : u32 {
Inherit = 0,
Enable = 1,
Disable = 2,
};
Core::System& system;
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
@@ -163,7 +157,6 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
bool is_auto_sleep_disabled = false;
ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {

View File

@@ -71,7 +71,7 @@ public:
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
audio_params.channel_count, std::move(unique_name),
[this] { buffer_event.writable->Signal(); });
[=]() { buffer_event.writable->Signal(); });
}
private:

View File

@@ -92,7 +92,7 @@ private:
if (performance) {
rb.Push<u64>(*performance);
}
ctx.WriteBuffer(samples);
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}
bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,

View File

@@ -112,7 +112,7 @@ private:
void GetImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
ctx.WriteBuffer(impl);
ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -160,7 +160,7 @@ private:
return;
}
ctx.WriteBuffer(key);
ctx.WriteBuffer(key.data(), key.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -311,7 +311,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
}
ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const {
FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const {
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
static_cast<u8>(space), save_struct.DebugInfo());
@@ -323,15 +323,15 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
}
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const {
FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
static_cast<u8>(space), attribute.DebugInfo());
static_cast<u8>(space), descriptor.DebugInfo());
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
return save_data_factory->Open(space, attribute);
return save_data_factory->Open(space, descriptor);
}
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(

View File

@@ -31,7 +31,7 @@ enum class SaveDataSpaceId : u8;
enum class SaveDataType : u8;
enum class StorageId : u8;
struct SaveDataAttribute;
struct SaveDataDescriptor;
struct SaveDataSize;
} // namespace FileSys
@@ -69,9 +69,9 @@ public:
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const;
ResultVal<FileSys::VirtualDir> CreateSaveData(
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
ResultVal<FileSys::VirtualDir> OpenSaveData(
FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
ResultVal<FileSys::VirtualDir> OpenSDMC() const;
ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;

View File

@@ -696,8 +696,8 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
{67, nullptr, "FindSaveDataWithFilter"},
{68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
{69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"},
{70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
{71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
{70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
{71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
{80, nullptr, "OpenSaveDataMetaFile"},
{81, nullptr, "OpenSaveDataTransferManager"},
{82, nullptr, "OpenSaveDataTransferManagerVersion2"},
@@ -812,7 +812,7 @@ void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>();
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
@@ -826,18 +826,17 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
}
void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
FileSys::SaveDataSpaceId space_id;
FileSys::SaveDataAttribute attribute;
};
const auto parameters = rp.PopRaw<Parameters>();
LOG_INFO(Service_FS, "called.");
auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute);
struct Parameters {
FileSys::SaveDataSpaceId save_data_space_id;
FileSys::SaveDataDescriptor descriptor;
};
IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
if (dir.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
@@ -845,18 +844,13 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
}
FileSys::StorageId id;
switch (parameters.space_id) {
case FileSys::SaveDataSpaceId::NandUser:
if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
id = FileSys::StorageId::NandUser;
break;
case FileSys::SaveDataSpaceId::SdCardSystem:
case FileSys::SaveDataSpaceId::SdCardUser:
} else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
id = FileSys::StorageId::SdCard;
break;
case FileSys::SaveDataSpaceId::NandSystem:
} else {
id = FileSys::StorageId::NandSystem;
break;
}
auto filesystem =
@@ -882,31 +876,22 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
}
void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called.");
void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
log_mode = rp.PopEnum<LogMode>();
LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
FileSys::SaveDataSpaceId space_id;
FileSys::SaveDataAttribute attribute;
};
const auto parameters = rp.PopRaw<Parameters>();
// Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData
constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None);
LOG_WARNING(Service_FS, "(STUBBED) called, flags={}", flags);
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(flags);
rb.PushEnum(log_mode);
}
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
@@ -981,24 +966,6 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
}
void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
log_mode = rp.PopEnum<LogMode>();
LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(log_mode);
}
void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
const auto raw = ctx.ReadBuffer();
auto log = Common::StringFromFixedZeroTerminatedBuffer(

View File

@@ -43,13 +43,11 @@ private:
void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx);
void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(Kernel::HLERequestContext& ctx);
void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);

View File

@@ -310,7 +310,7 @@ public:
ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress,
u64 size) const {
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->PageTable()};
const VAddr addr{GetRandomMapRegion(page_table, size)};
const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)};
@@ -331,7 +331,8 @@ public:
ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size,
VAddr bss_addr, std::size_t bss_size, std::size_t size) const {
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->PageTable()};
VAddr addr{};

View File

@@ -127,7 +127,7 @@ private:
const u32 array_size = rp.Pop<u32>();
LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
ctx.WriteBuffer(device_handle);
ctx.WriteBuffer(&device_handle, sizeof(device_handle));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -220,7 +220,7 @@ private:
tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
tag_info.tag_type = 2;
ctx.WriteBuffer(tag_info);
ctx.WriteBuffer(&tag_info, sizeof(TagInfo));
rb.Push(RESULT_SUCCESS);
}
@@ -237,7 +237,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
auto amiibo = nfp_interface.GetAmiiboBuffer();
ctx.WriteBuffer(amiibo.model_info);
ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info));
rb.Push(RESULT_SUCCESS);
}
@@ -283,7 +283,7 @@ private:
CommonInfo common_info{};
common_info.application_area_size = 0;
ctx.WriteBuffer(common_info);
ctx.WriteBuffer(&common_info, sizeof(CommonInfo));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -9,6 +9,7 @@
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/service.h"
#include "core/network/network.h"
#include "core/settings.h"
namespace Service::NIFM {
@@ -174,6 +175,16 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
const auto [ipv4, error] = Network::GetHostIPv4Address();
UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(ipv4);
}
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
@@ -235,7 +246,7 @@ IGeneralService::IGeneralService(Core::System& system)
{9, nullptr, "SetNetworkProfile"},
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
{11, nullptr, "GetScanDataOld"},
{12, nullptr, "GetCurrentIpAddress"},
{12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"},
{13, nullptr, "GetCurrentAccessPointOld"},
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
{15, nullptr, "GetCurrentIpConfigInfo"},

View File

@@ -16,12 +16,11 @@
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
namespace NvErrCodes {
constexpr u32 Success{};
constexpr u32 OutOfMemory{static_cast<u32>(-12)};
constexpr u32 InvalidInput{static_cast<u32>(-22)};
} // namespace NvErrCodes
enum {
InvalidNmapHandle = -22,
};
}
nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
@@ -50,9 +49,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
break;
}
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) {
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
return Remap(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl command");
return 0;
@@ -61,7 +59,6 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlInitalizeEx params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
return 0;
@@ -70,61 +67,53 @@ u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& ou
u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocSpace params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
params.page_size, params.flags);
const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) {
params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size);
auto& gpu = system.GPU();
const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
if (params.flags & 1) {
params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1);
} else {
params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
}
auto result{NvErrCodes::Success};
if (!params.offset) {
LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
result = NvErrCodes::OutOfMemory;
params.offset = gpu.MemoryManager().AllocateSpace(size, params.align);
}
std::memcpy(output.data(), &params, output.size());
return result;
return 0;
}
u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry);
LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries);
auto result{NvErrCodes::Success};
std::vector<IoctlRemapEntry> entries(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
auto& gpu = system.GPU();
for (const auto& entry : entries) {
LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
entry.offset, entry.nvmap_handle, entry.pages);
const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
entry.offset, entry.nvmap_handle, entry.pages);
GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10;
auto object = nvmap_dev->GetObject(entry.nvmap_handle);
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
result = NvErrCodes::InvalidInput;
break;
LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
std::memcpy(output.data(), entries.data(), output.size());
return static_cast<u32>(NvErrCodes::InvalidNmapHandle);
}
const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10};
const auto size{static_cast<u64>(entry.pages) << 0x10};
const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10};
const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)};
ASSERT(object->status == nvmap::Object::Status::Allocated);
if (!addr) {
LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
result = NvErrCodes::InvalidInput;
break;
}
const u64 size = static_cast<u64>(entry.pages) << 0x10;
ASSERT(size <= object->size);
const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10;
const GPUVAddr returned =
gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size);
ASSERT(returned == offset);
}
std::memcpy(output.data(), entries.data(), output.size());
return result;
return 0;
}
u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -137,76 +126,44 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
params.offset);
const auto object{nvmap_dev->GetObject(params.nvmap_handle)};
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
if (!params.nvmap_handle) {
return 0;
}
auto object = nvmap_dev->GetObject(params.nvmap_handle);
ASSERT(object);
// We can only map objects that have already been assigned a CPU address.
ASSERT(object->status == nvmap::Object::Status::Allocated);
ASSERT(params.buffer_offset == 0);
// The real nvservices doesn't make a distinction between handles and ids, and
// object can only have one handle and it will be the same as its id. Assert that this is the
// case to prevent unexpected behavior.
ASSERT(object->id == params.nvmap_handle);
auto& gpu = system.GPU();
u64 page_size{params.page_size};
if (!page_size) {
page_size = object->align;
}
if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) {
if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) {
const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)};
const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)};
if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) {
LOG_CRITICAL(Service_NVDRV,
"remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, "
"mapping_size = {}, offset={}",
params.flags, params.nvmap_handle, params.buffer_offset,
params.mapping_size, params.offset);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
}
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::Success;
} else {
LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::InvalidInput;
}
}
// We can only map objects that have already been assigned a CPU address.
ASSERT(object->status == nvmap::Object::Status::Allocated);
const auto physical_address{object->addr + params.buffer_offset};
u64 size{params.mapping_size};
if (!size) {
size = object->size;
}
const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None};
if (is_alloc) {
params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size);
if (params.flags & 1) {
params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size);
} else {
params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
params.offset = gpu.MemoryManager().MapBufferEx(object->addr, object->size);
}
auto result{NvErrCodes::Success};
if (!params.offset) {
LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
result = NvErrCodes::InvalidInput;
} else {
AddBufferMap(params.offset, size, physical_address, is_alloc);
}
// Create a new mapping entry for this operation.
ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(),
"Offset is already mapped");
BufferMapping mapping{};
mapping.nvmap_handle = params.nvmap_handle;
mapping.offset = params.offset;
mapping.size = object->size;
buffer_mappings[params.offset] = mapping;
std::memcpy(output.data(), &params, output.size());
return result;
return 0;
}
u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -215,20 +172,24 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
if (const auto size{RemoveBufferMap(params.offset)}; size) {
system.GPU().MemoryManager().Unmap(params.offset, *size);
} else {
LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset);
const auto itr = buffer_mappings.find(params.offset);
if (itr == buffer_mappings.end()) {
LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset);
// Hardware tests shows that unmapping an already unmapped buffer always returns successful
// and doesn't fail.
return 0;
}
params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);
std::memcpy(output.data(), &params, output.size());
return NvErrCodes::Success;
return 0;
}
u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
channel = params.fd;
@@ -238,7 +199,6 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou
u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size);
@@ -250,43 +210,9 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
params.regions[1].offset = 0x04000000;
params.regions[1].page_size = 0x10000;
params.regions[1].pages = 0x1bffff;
// TODO(ogniK): This probably can stay stubbed but should add support way way later
std::memcpy(output.data(), &params, output.size());
return 0;
}
std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {
const auto end{buffer_mappings.upper_bound(gpu_addr)};
for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) {
if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) {
return iter->second;
}
}
return {};
}
void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
bool is_allocated) {
buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated};
}
std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) {
std::size_t size{};
if (iter->second.IsAllocated()) {
size = iter->second.Size();
}
buffer_mappings.erase(iter);
return size;
}
return {};
}
} // namespace Service::Nvidia::Devices

View File

@@ -4,12 +4,9 @@
#pragma once
#include <map>
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -18,13 +15,6 @@ namespace Service::Nvidia::Devices {
class nvmap;
enum class AddressSpaceFlags : u32 {
None = 0x0,
FixedOffset = 0x1,
Remap = 0x100,
};
DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags);
class nvhost_as_gpu final : public nvdevice {
public:
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
@@ -35,45 +25,6 @@ public:
IoctlVersion version) override;
private:
class BufferMap final {
public:
constexpr BufferMap() = default;
constexpr BufferMap(GPUVAddr start_addr, std::size_t size)
: start_addr{start_addr}, end_addr{start_addr + size} {}
constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr,
bool is_allocated)
: start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr},
is_allocated{is_allocated} {}
constexpr VAddr StartAddr() const {
return start_addr;
}
constexpr VAddr EndAddr() const {
return end_addr;
}
constexpr std::size_t Size() const {
return end_addr - start_addr;
}
constexpr VAddr CpuAddr() const {
return cpu_addr;
}
constexpr bool IsAllocated() const {
return is_allocated;
}
private:
GPUVAddr start_addr{};
GPUVAddr end_addr{};
VAddr cpu_addr{};
bool is_allocated{};
};
enum class IoctlCommand : u32_le {
IocInitalizeExCommand = 0x40284109,
IocAllocateSpaceCommand = 0xC0184102,
@@ -98,7 +49,7 @@ private:
struct IoctlAllocSpace {
u32_le pages;
u32_le page_size;
AddressSpaceFlags flags;
u32_le flags;
INSERT_PADDING_WORDS(1);
union {
u64_le offset;
@@ -118,18 +69,18 @@ private:
static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
struct IoctlMapBufferEx {
AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable
u32_le kind; // -1 is default
u32_le flags; // bit0: fixed_offset, bit2: cacheable
u32_le kind; // -1 is default
u32_le nvmap_handle;
u32_le page_size; // 0 means don't care
s64_le buffer_offset;
u64_le buffer_offset;
u64_le mapping_size;
s64_le offset;
u64_le offset;
};
static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
struct IoctlUnmapBuffer {
s64_le offset;
u64_le offset;
};
static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
@@ -155,6 +106,15 @@ private:
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
"IoctlGetVaRegions is incorrect size");
struct BufferMapping {
u64 offset;
u64 size;
u32 nvmap_handle;
};
/// Map containing the nvmap object mappings in GPU memory.
std::unordered_map<u64, BufferMapping> buffer_mappings;
u32 channel{};
u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
@@ -165,14 +125,7 @@ private:
u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
std::shared_ptr<nvmap> nvmap_dev;
// This is expected to be ordered, therefore we must use a map, not unordered_map
std::map<GPUVAddr, BufferMap> buffer_mappings;
};
} // namespace Service::Nvidia::Devices

View File

@@ -18,12 +18,7 @@ enum {
};
}
nvmap::nvmap(Core::System& system) : nvdevice(system) {
// Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
// represent this.
CreateObject(0);
}
nvmap::nvmap(Core::System& system) : nvdevice(system) {}
nvmap::~nvmap() = default;
VAddr nvmap::GetObjectAddress(u32 handle) const {
@@ -55,21 +50,6 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<
return 0;
}
u32 nvmap::CreateObject(u32 size) {
// Create a new nvmap object and obtain a handle to it.
auto object = std::make_shared<Object>();
object->id = next_id++;
object->size = size;
object->status = Object::Status::Created;
object->refcount = 1;
const u32 handle = next_handle++;
handles.insert_or_assign(handle, std::move(object));
return handle;
}
u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -79,8 +59,17 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_ERROR(Service_NVDRV, "Size is 0");
return static_cast<u32>(NvErrCodes::InvalidValue);
}
// Create a new nvmap object and obtain a handle to it.
auto object = std::make_shared<Object>();
object->id = next_id++;
object->size = params.size;
object->status = Object::Status::Created;
object->refcount = 1;
params.handle = CreateObject(params.size);
u32 handle = next_handle++;
handles[handle] = std::move(object);
params.handle = handle;
std::memcpy(output.data(), &params, sizeof(params));
return 0;

View File

@@ -49,10 +49,10 @@ public:
private:
/// Id to use for the next handle that is created.
u32 next_handle = 0;
u32 next_handle = 1;
/// Id to use for the next object that is created.
u32 next_id = 0;
u32 next_id = 1;
/// Mapping of currently allocated handles to the objects they represent.
std::unordered_map<u32, std::shared_ptr<Object>> handles;
@@ -119,8 +119,6 @@ private:
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
u32 CreateObject(u32 size);
u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);

View File

@@ -60,24 +60,24 @@ void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
if (ctrl.must_delay) {
ctrl.fresh_call = false;
ctx.SleepClientThread(
"NVServices::DelayedResponse", ctrl.timeout,
[=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
Kernel::ThreadWakeupReason reason) {
IoctlCtrl ctrl2{ctrl};
std::vector<u8> tmp_output = output;
std::vector<u8> tmp_output2 = output2;
const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
tmp_output2, ctrl2, version);
ctx_.WriteBuffer(tmp_output, 0);
if (version == IoctlVersion::Version3) {
ctx_.WriteBuffer(tmp_output2, 1);
}
IPC::ResponseBuilder rb{ctx_, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(ioctl_result);
},
nvdrv->GetEventWriteable(ctrl.event_id));
ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout,
[=](std::shared_ptr<Kernel::Thread> thread,
Kernel::HLERequestContext& ctx,
Kernel::ThreadWakeupReason reason) {
IoctlCtrl ctrl2{ctrl};
std::vector<u8> tmp_output = output;
std::vector<u8> tmp_output2 = output2;
u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
tmp_output2, ctrl2, version);
ctx.WriteBuffer(tmp_output, 0);
if (version == IoctlVersion::Version3) {
ctx.WriteBuffer(tmp_output2, 1);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(result);
},
nvdrv->GetEventWriteable(ctrl.event_id));
} else {
ctx.WriteBuffer(output);
if (version == IoctlVersion::Version3) {

View File

@@ -24,13 +24,13 @@ BufferQueue::~BufferQueue() = default;
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
Buffer buffer{};
buffer.slot = slot;
buffer.igbp_buffer = igbp_buffer;
buffer.status = Buffer::Status::Free;
free_buffers.push_back(slot);
queue.push_back({
.slot = slot,
.status = Buffer::Status::Free,
.igbp_buffer = igbp_buffer,
});
queue.emplace_back(buffer);
buffer_wait_event.writable->Signal();
}
@@ -38,7 +38,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
u32 height) {
if (free_buffers.empty()) {
return std::nullopt;
return {};
}
auto f_itr = free_buffers.begin();
@@ -69,7 +69,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
}
if (itr == queue.end()) {
return std::nullopt;
return {};
}
itr->status = Buffer::Status::Dequeued;
@@ -103,15 +103,14 @@ std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::Ac
auto itr = queue.end();
// Iterate to find a queued buffer matching the requested slot.
while (itr == queue.end() && !queue_sequence.empty()) {
const u32 slot = queue_sequence.front();
u32 slot = queue_sequence.front();
itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
});
queue_sequence.pop_front();
}
if (itr == queue.end()) {
return std::nullopt;
}
if (itr == queue.end())
return {};
itr->status = Buffer::Status::Acquired;
return *itr;
}

View File

@@ -246,7 +246,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
PSC::InstallInterfaces(*sm);
PSM::InstallInterfaces(*sm);
Set::InstallInterfaces(*sm);
Sockets::InstallInterfaces(*sm);
Sockets::InstallInterfaces(*sm, system);
SPL::InstallInterfaces(*sm);
SSL::InstallInterfaces(*sm);
Time::InstallInterfaces(system);

View File

@@ -106,7 +106,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
ctx.WriteBuffer(layout);
ctx.WriteBuffer(&layout, sizeof(KeyboardLayout));
}
} // Anonymous namespace

View File

@@ -9,7 +9,6 @@
#include <type_traits>
#include <unordered_map>
#include "common/concepts.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
@@ -57,8 +56,10 @@ public:
ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
template <typename T>
std::shared_ptr<T> GetService(const std::string& service_name) const {
static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
"Not a base of ServiceFrameworkBase");
auto service = registered_services.find(service_name);
if (service == registered_services.end()) {
LOG_DEBUG(Service, "Can't find service: {}", service_name);

View File

@@ -0,0 +1,162 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <variant>
#include <vector>
#include <fmt/format.h>
#include "common/assert.h"
#include "common/microprofile.h"
#include "common/thread.h"
#include "core/core.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h"
namespace Service::Sockets {
/**
* Worker abstraction to execute blocking calls on host without blocking the guest thread
*
* @tparam Service Service where the work is executed
* @tparam ...Types Types of work to execute
*/
template <class Service, class... Types>
class BlockingWorker {
using This = BlockingWorker<Service, Types...>;
using WorkVariant = std::variant<std::monostate, Types...>;
public:
/// Create a new worker
static std::unique_ptr<This> Create(Core::System& system, Service* service,
std::string_view name) {
return std::unique_ptr<This>(new This(system, service, name));
}
~BlockingWorker() {
while (!is_available.load(std::memory_order_relaxed)) {
// Busy wait until work is finished
std::this_thread::yield();
}
// Monostate means to exit the thread
work = std::monostate{};
work_event.Set();
thread.join();
}
/**
* Try to capture the worker to send work after a success
* @returns True when the worker has been successfully captured
*/
bool TryCapture() {
bool expected = true;
return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed,
std::memory_order_relaxed);
}
/**
* Send work to this worker abstraction
* @see TryCapture must be called before attempting to call this function
*/
template <class Work>
void SendWork(Work new_work) {
ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured");
work = std::move(new_work);
work_event.Set();
}
/// Generate a callback for @see SleepClientThread
template <class Work>
auto Callback() {
return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx,
Kernel::ThreadWakeupReason reason) {
ASSERT(reason == Kernel::ThreadWakeupReason::Signal);
std::get<Work>(work).Response(ctx);
is_available.store(true);
};
}
/// Get kernel event that will be signalled by the worker when the host operation finishes
std::shared_ptr<Kernel::WritableEvent> KernelEvent() const {
return kernel_event;
}
private:
explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) {
auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name));
kernel_event = std::move(pair.writable);
thread = std::thread([this, &system, service, name] { Run(system, service, name); });
}
void Run(Core::System& system, Service* service, std::string_view name) {
system.RegisterHostThread();
const std::string thread_name = fmt::format("yuzu:{}", name);
MicroProfileOnThreadCreate(thread_name.c_str());
Common::SetCurrentThreadName(thread_name.c_str());
bool keep_running = true;
while (keep_running) {
work_event.Wait();
const auto visit_fn = [service, &keep_running](auto&& w) {
using T = std::decay_t<decltype(w)>;
if constexpr (std::is_same_v<T, std::monostate>) {
keep_running = false;
} else {
w.Execute(service);
}
};
std::visit(visit_fn, work);
kernel_event->Signal();
}
}
std::thread thread;
WorkVariant work;
Common::Event work_event;
std::shared_ptr<Kernel::WritableEvent> kernel_event;
std::atomic_bool is_available{true};
};
template <class Service, class... Types>
class BlockingWorkerPool {
using Worker = BlockingWorker<Service, Types...>;
public:
explicit BlockingWorkerPool(Core::System& system_, Service* service_)
: system{system_}, service{service_} {}
/// Returns a captured worker thread, creating new ones if necessary
Worker* CaptureWorker() {
for (auto& worker : workers) {
if (worker->TryCapture()) {
return worker.get();
}
}
auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size()));
[[maybe_unused]] const bool success = new_worker->TryCapture();
ASSERT(success);
return workers.emplace_back(std::move(new_worker)).get();
}
private:
Core::System& system;
Service* const service;
std::vector<std::unique_ptr<Worker>> workers;
};
} // namespace Service::Sockets

View File

@@ -2,18 +2,138 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <fmt/format.h>
#include "common/microprofile.h"
#include "common/thread.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/network/network.h"
#include "core/network/sockets.h"
namespace Service::Sockets {
namespace {
bool IsConnectionBased(Type type) {
switch (type) {
case Type::STREAM:
return true;
case Type::DGRAM:
return false;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
return false;
}
}
} // Anonymous namespace
void BSD::PollWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout);
}
void BSD::PollWork::Response(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(ret);
rb.PushEnum(bsd_errno);
}
void BSD::AcceptWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer);
}
void BSD::AcceptWork::Response(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(ret);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
void BSD::ConnectWork::Execute(BSD* bsd) {
bsd_errno = bsd->ConnectImpl(fd, addr);
}
void BSD::ConnectWork::Response(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
void BSD::RecvWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message);
}
void BSD::RecvWork::Response(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(message);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(ret);
rb.PushEnum(bsd_errno);
}
void BSD::RecvFromWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr);
}
void BSD::RecvFromWork::Response(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(message, 0);
if (!addr.empty()) {
ctx.WriteBuffer(addr, 1);
}
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(ret);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(addr.size()));
}
void BSD::SendWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message);
}
void BSD::SendWork::Response(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(ret);
rb.PushEnum(bsd_errno);
}
void BSD::SendToWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr);
}
void BSD::SendToWork::Response(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(ret);
rb.PushEnum(bsd_errno);
}
void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // bsd errno
rb.Push<s32>(0); // bsd errno
}
void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
@@ -26,20 +146,19 @@ void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
void BSD::Socket(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 domain = rp.Pop<u32>();
const u32 type = rp.Pop<u32>();
const u32 protocol = rp.Pop<u32>();
u32 domain = rp.Pop<u32>();
u32 type = rp.Pop<u32>();
u32 protocol = rp.Pop<u32>();
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
LOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, protocol);
u32 fd = next_fd++;
const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type),
static_cast<Protocol>(protocol));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(fd);
rb.Push<u32>(0); // bsd errno
rb.Push<s32>(fd);
rb.PushEnum(bsd_errno);
}
void BSD::Select(Kernel::HLERequestContext& ctx) {
@@ -52,67 +171,663 @@ void BSD::Select(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno
}
void BSD::Poll(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 nfds = rp.Pop<s32>();
const s32 timeout = rp.Pop<s32>();
LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
ExecuteWork(ctx, "BSD:Poll", timeout != 0,
PollWork{
.nfds = nfds,
.timeout = timeout,
.read_buffer = ctx.ReadBuffer(),
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
});
}
void BSD::Accept(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={}", fd);
ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd),
AcceptWork{
.fd = fd,
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
});
}
void BSD::Bind(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
IPC::ResponseBuilder rb{ctx, 4};
LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // ret
rb.Push<u32>(0); // bsd errno
BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
}
void BSD::Connect(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
IPC::ResponseBuilder rb{ctx, 4};
LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd),
ConnectWork{
.fd = fd,
.addr = ctx.ReadBuffer(),
});
}
void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // ret
rb.Push<u32>(0); // bsd errno
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
void BSD::GetSockName(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
void BSD::Listen(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const s32 backlog = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={} backlog={}", fd, backlog);
BuildErrnoResponse(ctx, ListenImpl(fd, backlog));
}
void BSD::Fcntl(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const s32 cmd = rp.Pop<s32>();
const s32 arg = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // ret
rb.Push<u32>(0); // bsd errno
rb.Push<s32>(ret);
rb.PushEnum(bsd_errno);
}
void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestParser rp{ctx};
IPC::ResponseBuilder rb{ctx, 4};
const s32 fd = rp.Pop<s32>();
const u32 level = rp.Pop<u32>();
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // ret
rb.Push<u32>(0); // bsd errno
const std::vector<u8> buffer = ctx.ReadBuffer();
const u8* optval = buffer.empty() ? nullptr : buffer.data();
size_t optlen = buffer.size();
std::array<u64, 2> values;
if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) {
std::memcpy(values.data(), buffer.data(), sizeof(values));
optlen = sizeof(values);
optval = reinterpret_cast<const u8*>(values.data());
}
LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
static_cast<u32>(optname), optlen);
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval));
}
void BSD::Shutdown(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const s32 how = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={} how={}", fd, how);
BuildErrnoResponse(ctx, ShutdownImpl(fd, how));
}
void BSD::Recv(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd),
RecvWork{
.fd = fd,
.flags = flags,
.message = std::vector<u8>(ctx.GetWriteBufferSize()),
});
}
void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd),
RecvFromWork{
.fd = fd,
.flags = flags,
.message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
.addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
});
}
void BSD::Send(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd),
SendWork{
.fd = fd,
.flags = flags,
.message = ctx.ReadBuffer(),
});
}
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const u32 flags = rp.Pop<u32>();
IPC::ResponseBuilder rb{ctx, 4};
LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // ret
rb.Push<u32>(0); // bsd errno
ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd),
SendToWork{
.fd = fd,
.flags = flags,
.message = ctx.ReadBuffer(0),
.addr = ctx.ReadBuffer(1),
});
}
void BSD::Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd),
SendWork{
.fd = fd,
.flags = 0,
.message = ctx.ReadBuffer(),
});
}
void BSD::Close(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={}", fd);
BuildErrnoResponse(ctx, CloseImpl(fd));
}
template <typename Work>
void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
bool is_blocking, Work work) {
if (!is_blocking) {
work.Execute(this);
work.Response(ctx);
return;
}
// Signal a dummy response to make IPC validation happy
// This will be overwritten by the SleepClientThread callback
work.Response(ctx);
auto worker = worker_pool.CaptureWorker();
ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(),
worker->Callback<Work>(), worker->KernelEvent());
worker->SendWork(std::move(work));
}
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
if (type == Type::SEQPACKET) {
UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management");
} else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) {
UNIMPLEMENTED_MSG("SOCK_RAW errno management");
}
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
const s32 fd = FindFreeFileDescriptorHandle();
if (fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return {-1, Errno::MFILE};
}
FileDescriptor& descriptor = file_descriptors[fd].emplace();
// ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={}", fd);
descriptor.socket = std::make_unique<Network::Socket>();
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
descriptor.is_connection_based = IsConnectionBased(type);
return {fd, Errno::SUCCESS};
}
std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
s32 nfds, s32 timeout) {
if (write_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
}
if (nfds == 0) {
// When no entries are provided, -1 is returned with errno zero
return {-1, Errno::SUCCESS};
}
const size_t length = std::min(read_buffer.size(), write_buffer.size());
std::vector<PollFD> fds(nfds);
std::memcpy(fds.data(), read_buffer.data(), length);
if (timeout >= 0) {
const s64 seconds = timeout / 1000;
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
if (seconds < 0) {
return {-1, Errno::INVAL};
}
if (nanoseconds > 999'999'999) {
return {-1, Errno::INVAL};
}
} else if (timeout != -1) {
return {-1, Errno::INVAL};
}
for (PollFD& pollfd : fds) {
ASSERT(pollfd.revents == 0);
if (pollfd.fd > MAX_FD || pollfd.fd < 0) {
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
pollfd.revents = 0;
return {0, Errno::SUCCESS};
}
std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
if (!descriptor) {
LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd);
pollfd.revents = POLL_NVAL;
return {0, Errno::SUCCESS};
}
}
std::vector<Network::PollFD> host_pollfds(fds.size());
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) {
Network::PollFD result;
result.socket = file_descriptors[pollfd.fd]->socket.get();
result.events = TranslatePollEventsToHost(pollfd.events);
result.revents = 0;
return result;
});
const auto result = Network::Poll(host_pollfds, timeout);
const size_t num = host_pollfds.size();
for (size_t i = 0; i < num; ++i) {
fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents);
}
std::memcpy(write_buffer.data(), fds.data(), length);
return Translate(result);
}
std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return {-1, Errno::MFILE};
}
FileDescriptor& descriptor = *file_descriptors[fd];
auto [result, bsd_errno] = descriptor.socket->Accept();
if (bsd_errno != Network::Errno::SUCCESS) {
return {-1, Translate(bsd_errno)};
}
FileDescriptor& new_descriptor = file_descriptors[new_fd].emplace();
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;
ASSERT(write_buffer.size() == sizeof(SockAddrIn));
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in));
return {new_fd, Errno::SUCCESS};
}
Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
ASSERT(addr.size() == sizeof(SockAddrIn));
SockAddrIn addr_in;
std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
}
Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
SockAddrIn addr_in;
std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
}
Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
if (bsd_errno != Network::Errno::SUCCESS) {
return Translate(bsd_errno);
}
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() == sizeof(guest_addrin));
std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
return Translate(bsd_errno);
}
Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
if (bsd_errno != Network::Errno::SUCCESS) {
return Translate(bsd_errno);
}
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() == sizeof(guest_addrin));
std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
return Translate(bsd_errno);
}
Errno BSD::ListenImpl(s32 fd, s32 backlog) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
return Translate(file_descriptors[fd]->socket->Listen(backlog));
}
std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
switch (cmd) {
case FcntlCmd::GETFL:
ASSERT(arg == 0);
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
const bool enable = (arg & FLAG_O_NONBLOCK) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
if (bsd_errno != Errno::SUCCESS) {
return {-1, bsd_errno};
}
descriptor.flags = arg;
return {0, Errno::SUCCESS};
}
default:
UNIMPLEMENTED_MSG("Unimplemented cmd={}", static_cast<int>(cmd));
return {-1, Errno::SUCCESS};
}
}
Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) {
UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
Network::Socket* const socket = file_descriptors[fd]->socket.get();
if (optname == OptName::LINGER) {
ASSERT(optlen == sizeof(Linger));
Linger linger;
std::memcpy(&linger, optval, sizeof(linger));
ASSERT(linger.onoff == 0 || linger.onoff == 1);
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
}
ASSERT(optlen == sizeof(u32));
u32 value;
std::memcpy(&value, optval, sizeof(value));
switch (optname) {
case OptName::REUSEADDR:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetReuseAddr(value != 0));
case OptName::BROADCAST:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetBroadcast(value != 0));
case OptName::SNDBUF:
return Translate(socket->SetSndBuf(value));
case OptName::RCVBUF:
return Translate(socket->SetRcvBuf(value));
case OptName::SNDTIMEO:
return Translate(socket->SetSndTimeo(value));
case OptName::RCVTIMEO:
return Translate(socket->SetRcvTimeo(value));
default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", static_cast<int>(optname));
return Errno::SUCCESS;
}
}
Errno BSD::ShutdownImpl(s32 fd, s32 how) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
}
std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
return Translate(file_descriptors[fd]->socket->Recv(flags, message));
}
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
Network::SockAddrIn addr_in{};
Network::SockAddrIn* p_addr_in = nullptr;
if (descriptor.is_connection_based) {
// Connection based file descriptors (e.g. TCP) zero addr
addr.clear();
} else {
p_addr_in = &addr_in;
}
// Apply flags
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
// Restore original state
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(false);
}
if (p_addr_in) {
if (ret < 0) {
addr.clear();
} else {
ASSERT(addr.size() == sizeof(SockAddrIn));
const SockAddrIn result = Translate(addr_in);
std::memcpy(addr.data(), &result, sizeof(result));
}
}
return {ret, bsd_errno};
}
std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
return Translate(file_descriptors[fd]->socket->Send(message, flags));
}
std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
const std::vector<u8>& addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
Network::SockAddrIn addr_in;
Network::SockAddrIn* p_addr_in = nullptr;
if (!addr.empty()) {
ASSERT(addr.size() == sizeof(SockAddrIn));
SockAddrIn guest_addr_in;
std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in));
addr_in = Translate(guest_addr_in);
}
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
}
Errno BSD::CloseImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
if (bsd_errno != Errno::SUCCESS) {
return bsd_errno;
}
LOG_INFO(Service, "Close socket fd={}", fd);
file_descriptors[fd].reset();
return bsd_errno;
}
s32 BSD::FindFreeFileDescriptorHandle() noexcept {
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
if (!file_descriptors[fd]) {
return fd;
}
}
return -1;
}
bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
if (fd > MAX_FD || fd < 0) {
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
return false;
}
if (!file_descriptors[fd]) {
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
return false;
}
return true;
}
bool BSD::IsBlockingSocket(s32 fd) const noexcept {
// Inform invalid sockets as non-blocking
// This way we avoid using a worker thread as it will fail without blocking host
if (fd > MAX_FD || fd < 0) {
return false;
}
if (!file_descriptors[fd]) {
return false;
}
return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0;
}
void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // ret
rb.Push<u32>(0); // bsd errno
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
BSD::BSD(const char* name) : ServiceFramework(name) {
BSD::BSD(Core::System& system, const char* name)
: ServiceFramework(name), worker_pool{system, this} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
@@ -121,25 +836,25 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
{3, nullptr, "SocketExempt"},
{4, nullptr, "Open"},
{5, &BSD::Select, "Select"},
{6, nullptr, "Poll"},
{6, &BSD::Poll, "Poll"},
{7, nullptr, "Sysctl"},
{8, nullptr, "Recv"},
{9, nullptr, "RecvFrom"},
{10, nullptr, "Send"},
{8, &BSD::Recv, "Recv"},
{9, &BSD::RecvFrom, "RecvFrom"},
{10, &BSD::Send, "Send"},
{11, &BSD::SendTo, "SendTo"},
{12, nullptr, "Accept"},
{12, &BSD::Accept, "Accept"},
{13, &BSD::Bind, "Bind"},
{14, &BSD::Connect, "Connect"},
{15, nullptr, "GetPeerName"},
{16, nullptr, "GetSockName"},
{15, &BSD::GetPeerName, "GetPeerName"},
{16, &BSD::GetSockName, "GetSockName"},
{17, nullptr, "GetSockOpt"},
{18, &BSD::Listen, "Listen"},
{19, nullptr, "Ioctl"},
{20, nullptr, "Fcntl"},
{20, &BSD::Fcntl, "Fcntl"},
{21, &BSD::SetSockOpt, "SetSockOpt"},
{22, nullptr, "Shutdown"},
{22, &BSD::Shutdown, "Shutdown"},
{23, nullptr, "ShutdownAllSockets"},
{24, nullptr, "Write"},
{24, &BSD::Write, "Write"},
{25, nullptr, "Read"},
{26, &BSD::Close, "Close"},
{27, nullptr, "DuplicateSocket"},

View File

@@ -4,30 +4,174 @@
#pragma once
#include <memory>
#include <string_view>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/blocking_worker.h"
#include "core/hle/service/sockets/sockets.h"
namespace Core {
class System;
}
namespace Network {
class Socket;
}
namespace Service::Sockets {
class BSD final : public ServiceFramework<BSD> {
public:
explicit BSD(const char* name);
explicit BSD(Core::System& system, const char* name);
~BSD() override;
private:
/// Maximum number of file descriptors
static constexpr size_t MAX_FD = 128;
struct FileDescriptor {
std::unique_ptr<Network::Socket> socket;
s32 flags = 0;
bool is_connection_based = false;
};
struct PollWork {
void Execute(BSD* bsd);
void Response(Kernel::HLERequestContext& ctx);
s32 nfds;
s32 timeout;
std::vector<u8> read_buffer;
std::vector<u8> write_buffer;
s32 ret{};
Errno bsd_errno{};
};
struct AcceptWork {
void Execute(BSD* bsd);
void Response(Kernel::HLERequestContext& ctx);
s32 fd;
std::vector<u8> write_buffer;
s32 ret{};
Errno bsd_errno{};
};
struct ConnectWork {
void Execute(BSD* bsd);
void Response(Kernel::HLERequestContext& ctx);
s32 fd;
std::vector<u8> addr;
Errno bsd_errno{};
};
struct RecvWork {
void Execute(BSD* bsd);
void Response(Kernel::HLERequestContext& ctx);
s32 fd;
u32 flags;
std::vector<u8> message;
s32 ret{};
Errno bsd_errno{};
};
struct RecvFromWork {
void Execute(BSD* bsd);
void Response(Kernel::HLERequestContext& ctx);
s32 fd;
u32 flags;
std::vector<u8> message;
std::vector<u8> addr;
s32 ret{};
Errno bsd_errno{};
};
struct SendWork {
void Execute(BSD* bsd);
void Response(Kernel::HLERequestContext& ctx);
s32 fd;
u32 flags;
std::vector<u8> message;
s32 ret{};
Errno bsd_errno{};
};
struct SendToWork {
void Execute(BSD* bsd);
void Response(Kernel::HLERequestContext& ctx);
s32 fd;
u32 flags;
std::vector<u8> message;
std::vector<u8> addr;
s32 ret{};
Errno bsd_errno{};
};
void RegisterClient(Kernel::HLERequestContext& ctx);
void StartMonitoring(Kernel::HLERequestContext& ctx);
void Socket(Kernel::HLERequestContext& ctx);
void Select(Kernel::HLERequestContext& ctx);
void Poll(Kernel::HLERequestContext& ctx);
void Accept(Kernel::HLERequestContext& ctx);
void Bind(Kernel::HLERequestContext& ctx);
void Connect(Kernel::HLERequestContext& ctx);
void GetPeerName(Kernel::HLERequestContext& ctx);
void GetSockName(Kernel::HLERequestContext& ctx);
void Listen(Kernel::HLERequestContext& ctx);
void Fcntl(Kernel::HLERequestContext& ctx);
void SetSockOpt(Kernel::HLERequestContext& ctx);
void Shutdown(Kernel::HLERequestContext& ctx);
void Recv(Kernel::HLERequestContext& ctx);
void RecvFrom(Kernel::HLERequestContext& ctx);
void Send(Kernel::HLERequestContext& ctx);
void SendTo(Kernel::HLERequestContext& ctx);
void Write(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
/// Id to use for the next open file descriptor.
u32 next_fd = 1;
template <typename Work>
void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
bool is_blocking, Work work);
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
s32 nfds, s32 timeout);
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Errno BindImpl(s32 fd, const std::vector<u8>& addr);
Errno ConnectImpl(s32 fd, const std::vector<u8>& addr);
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval);
Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr);
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message);
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
const std::vector<u8>& addr);
Errno CloseImpl(s32 fd);
s32 FindFreeFileDescriptorHandle() noexcept;
bool IsFileDescriptorValid(s32 fd) const noexcept;
bool IsBlockingSocket(s32 fd) const noexcept;
void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork,
SendToWork>
worker_pool;
};
class BSDCFG final : public ServiceFramework<BSDCFG> {

View File

@@ -10,9 +10,9 @@
namespace Service::Sockets {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager);
std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager);
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<BSD>(system, "bsd:s")->InstallAsService(service_manager);
std::make_shared<BSD>(system, "bsd:u")->InstallAsService(service_manager);
std::make_shared<BSDCFG>()->InstallAsService(service_manager);
std::make_shared<ETHC_C>()->InstallAsService(service_manager);

View File

@@ -4,11 +4,94 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::Sockets {
enum class Errno : u32 {
SUCCESS = 0,
BADF = 9,
AGAIN = 11,
INVAL = 22,
MFILE = 24,
NOTCONN = 107,
};
enum class Domain : u32 {
INET = 2,
};
enum class Type : u32 {
STREAM = 1,
DGRAM = 2,
RAW = 3,
SEQPACKET = 5,
};
enum class Protocol : u32 {
UNSPECIFIED = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
};
enum class OptName : u32 {
REUSEADDR = 0x4,
BROADCAST = 0x20,
LINGER = 0x80,
SNDBUF = 0x1001,
RCVBUF = 0x1002,
SNDTIMEO = 0x1005,
RCVTIMEO = 0x1006,
};
enum class ShutdownHow : s32 {
RD = 0,
WR = 1,
RDWR = 2,
};
enum class FcntlCmd : s32 {
GETFL = 3,
SETFL = 4,
};
struct SockAddrIn {
u8 len;
u8 family;
u16 portno;
std::array<u8, 4> ip;
std::array<u8, 8> zeroes;
};
struct PollFD {
s32 fd;
u16 events;
u16 revents;
};
struct Linger {
u32 onoff;
u32 linger;
};
constexpr u16 POLL_IN = 0x01;
constexpr u16 POLL_PRI = 0x02;
constexpr u16 POLL_OUT = 0x04;
constexpr u16 POLL_ERR = 0x08;
constexpr u16 POLL_HUP = 0x10;
constexpr u16 POLL_NVAL = 0x20;
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
constexpr u32 FLAG_O_NONBLOCK = 0x800;
/// Registers all Sockets services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::Sockets

View File

@@ -0,0 +1,165 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/network/network.h"
namespace Service::Sockets {
Errno Translate(Network::Errno value) {
switch (value) {
case Network::Errno::SUCCESS:
return Errno::SUCCESS;
case Network::Errno::BADF:
return Errno::BADF;
case Network::Errno::AGAIN:
return Errno::AGAIN;
case Network::Errno::INVAL:
return Errno::INVAL;
case Network::Errno::MFILE:
return Errno::MFILE;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
default:
UNIMPLEMENTED_MSG("Unimplemented errno={}", static_cast<int>(value));
return Errno::SUCCESS;
}
}
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
return {value.first, Translate(value.second)};
}
Network::Domain Translate(Domain domain) {
switch (domain) {
case Domain::INET:
return Network::Domain::INET;
default:
UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
return {};
}
}
Domain Translate(Network::Domain domain) {
switch (domain) {
case Network::Domain::INET:
return Domain::INET;
default:
UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
return {};
}
}
Network::Type Translate(Type type) {
switch (type) {
case Type::STREAM:
return Network::Type::STREAM;
case Type::DGRAM:
return Network::Type::DGRAM;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
}
}
Network::Protocol Translate(Type type, Protocol protocol) {
switch (protocol) {
case Protocol::UNSPECIFIED:
LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type");
switch (type) {
case Type::DGRAM:
return Network::Protocol::UDP;
case Type::STREAM:
return Network::Protocol::TCP;
default:
return Network::Protocol::TCP;
}
case Protocol::TCP:
return Network::Protocol::TCP;
case Protocol::UDP:
return Network::Protocol::UDP;
default:
UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol));
return Network::Protocol::TCP;
}
}
u16 TranslatePollEventsToHost(u16 flags) {
u16 result = 0;
const auto translate = [&result, &flags](u16 from, u16 to) {
if ((flags & from) != 0) {
flags &= ~from;
result |= to;
}
};
translate(POLL_IN, Network::POLL_IN);
translate(POLL_PRI, Network::POLL_PRI);
translate(POLL_OUT, Network::POLL_OUT);
translate(POLL_ERR, Network::POLL_ERR);
translate(POLL_HUP, Network::POLL_HUP);
translate(POLL_NVAL, Network::POLL_NVAL);
UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
return result;
}
u16 TranslatePollEventsToGuest(u16 flags) {
u16 result = 0;
const auto translate = [&result, &flags](u16 from, u16 to) {
if ((flags & from) != 0) {
flags &= ~from;
result |= to;
}
};
translate(Network::POLL_IN, POLL_IN);
translate(Network::POLL_PRI, POLL_PRI);
translate(Network::POLL_OUT, POLL_OUT);
translate(Network::POLL_ERR, POLL_ERR);
translate(Network::POLL_HUP, POLL_HUP);
translate(Network::POLL_NVAL, POLL_NVAL);
UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
return result;
}
Network::SockAddrIn Translate(SockAddrIn value) {
ASSERT(value.len == 0 || value.len == sizeof(value));
Network::SockAddrIn result;
result.family = Translate(static_cast<Domain>(value.family));
result.ip = value.ip;
result.portno = value.portno >> 8 | value.portno << 8;
return result;
}
SockAddrIn Translate(Network::SockAddrIn value) {
SockAddrIn result;
result.len = sizeof(result);
result.family = static_cast<u8>(Translate(value.family));
result.portno = value.portno >> 8 | value.portno << 8;
result.ip = value.ip;
result.zeroes = {};
return result;
}
Network::ShutdownHow Translate(ShutdownHow how) {
switch (how) {
case ShutdownHow::RD:
return Network::ShutdownHow::RD;
case ShutdownHow::WR:
return Network::ShutdownHow::WR;
case ShutdownHow::RDWR:
return Network::ShutdownHow::RDWR;
default:
UNIMPLEMENTED_MSG("Unimplemented how={}", static_cast<int>(how));
return {};
}
}
} // namespace Service::Sockets

View File

@@ -0,0 +1,48 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <utility>
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/network/network.h"
namespace Service::Sockets {
/// Translate abstract errno to guest errno
Errno Translate(Network::Errno value);
/// Translate abstract return value errno pair to guest return value errno pair
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
/// Translate guest domain to abstract domain
Network::Domain Translate(Domain domain);
/// Translate abstract domain to guest domain
Domain Translate(Network::Domain domain);
/// Translate guest type to abstract type
Network::Type Translate(Type type);
/// Translate guest protocol to abstract protocol
Network::Protocol Translate(Type type, Protocol protocol);
/// Translate abstract poll event flags to guest poll event flags
u16 TranslatePollEventsToHost(u16 flags);
/// Translate guest poll event flags to abstract poll event flags
u16 TranslatePollEventsToGuest(u16 flags);
/// Translate guest socket address structure to abstract socket address structure
Network::SockAddrIn Translate(SockAddrIn value);
/// Translate abstract socket address structure to guest socket address structure
SockAddrIn Translate(Network::SockAddrIn value);
/// Translate guest shutdown mode to abstract shutdown mode
Network::ShutdownHow Translate(ShutdownHow how);
} // namespace Service::Sockets

View File

@@ -290,7 +290,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
ctx.WriteBuffer(clock_snapshot);
ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
}
void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) {
@@ -313,7 +313,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
ctx.WriteBuffer(clock_snapshot);
ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
}
void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(

View File

@@ -142,7 +142,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(1); // Number of times we're returning
ctx.WriteBuffer(posix_time);
ctx.WriteBuffer(&posix_time, sizeof(s64));
}
void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
@@ -164,7 +164,7 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(1); // Number of times we're returning
ctx.WriteBuffer(posix_time);
ctx.WriteBuffer(&posix_time, sizeof(s64));
}
} // namespace Service::Time

View File

@@ -548,8 +548,8 @@ private:
// Wait the current thread until a buffer becomes available
ctx.SleepClientThread(
"IHOSBinderDriver::DequeueBuffer", UINT64_MAX,
[=, this](std::shared_ptr<Kernel::Thread> thread,
Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
[=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
const auto guard = nv_flinger->Lock();
auto& buffer_queue = nv_flinger->FindBufferQueue(id);
@@ -1199,23 +1199,6 @@ private:
}
}
void GetIndirectLayerImageRequiredMemoryInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto width = rp.Pop<u64>();
const auto height = rp.Pop<u64>();
LOG_DEBUG(Service_VI, "called width={}, height={}", width, height);
constexpr std::size_t base_size = 0x20000;
constexpr std::size_t alignment = 0x1000;
const auto texture_size = width * height * 4;
const auto out_size = (texture_size + base_size - 1) / base_size * base_size;
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.Push(out_size);
rb.Push(alignment);
}
static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) {
switch (mode) {
case NintendoScaleMode::None:
@@ -1260,8 +1243,7 @@ IApplicationDisplayService::IApplicationDisplayService(
{2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
{2450, nullptr, "GetIndirectLayerImageMap"},
{2451, nullptr, "GetIndirectLayerImageCropMap"},
{2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
"GetIndirectLayerImageRequiredMemoryInfo"},
{2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"},
{5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
{5203, nullptr, "GetDisplayVsyncEventForDebug"},
};

View File

@@ -3,10 +3,8 @@
// Refer to the license.txt file included.
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include "common/concepts.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -23,41 +21,27 @@
namespace Loader {
namespace {
template <Common::DerivedFrom<AppLoader> T>
std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
const auto file_type = T::IdentifyType(file);
if (file_type != FileType::Error) {
return file_type;
}
return std::nullopt;
}
} // namespace
FileType IdentifyFile(FileSys::VirtualFile file) {
if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
return *romdir_type;
} else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
return *elf_type;
} else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
return *nso_type;
} else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
return *nro_type;
} else if (const auto nca_type = IdentifyFileLoader<AppLoader_NCA>(file)) {
return *nca_type;
} else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) {
return *xci_type;
} else if (const auto nax_type = IdentifyFileLoader<AppLoader_NAX>(file)) {
return *nax_type;
} else if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) {
return *nsp_type;
} else if (const auto kip_type = IdentifyFileLoader<AppLoader_KIP>(file)) {
return *kip_type;
} else {
return FileType::Unknown;
}
FileType type;
#define CHECK_TYPE(loader) \
type = AppLoader_##loader::IdentifyType(file); \
if (FileType::Error != type) \
return type;
CHECK_TYPE(DeconstructedRomDirectory)
CHECK_TYPE(ELF)
CHECK_TYPE(NSO)
CHECK_TYPE(NRO)
CHECK_TYPE(NCA)
CHECK_TYPE(XCI)
CHECK_TYPE(NAX)
CHECK_TYPE(NSP)
CHECK_TYPE(KIP)
#undef CHECK_TYPE
return FileType::Unknown;
}
FileType GuessFromFilename(const std::string& name) {

View File

@@ -704,7 +704,7 @@ struct Memory::Impl {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer != nullptr) {
// NOTE: Avoid adding any extra logic to this fast-path block
auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]);
T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]);
return Common::AtomicCompareAndSwap(pointer, data, expected);
}
@@ -720,8 +720,9 @@ struct Memory::Impl {
case Common::PageType::RasterizerCachedMemory: {
u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
system.GPU().InvalidateRegion(vaddr, sizeof(T));
auto* pointer = reinterpret_cast<volatile T*>(&host_ptr);
T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr);
return Common::AtomicCompareAndSwap(pointer, data, expected);
break;
}
default:
UNREACHABLE();
@@ -733,7 +734,7 @@ struct Memory::Impl {
u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer != nullptr) {
// NOTE: Avoid adding any extra logic to this fast-path block
auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]);
u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]);
return Common::AtomicCompareAndSwap(pointer, data, expected);
}
@@ -749,8 +750,9 @@ struct Memory::Impl {
case Common::PageType::RasterizerCachedMemory: {
u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
system.GPU().InvalidateRegion(vaddr, sizeof(u128));
auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr);
u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr);
return Common::AtomicCompareAndSwap(pointer, data, expected);
break;
}
default:
UNREACHABLE();

View File

@@ -199,29 +199,17 @@ void CheatEngine::Initialize() {
metadata.title_id = system.CurrentProcess()->GetTitleID();
const auto& page_table = system.CurrentProcess()->PageTable();
metadata.heap_extents = {
.base = page_table.GetHeapRegionStart(),
.size = page_table.GetHeapRegionSize(),
};
metadata.address_space_extents = {
.base = page_table.GetAddressSpaceStart(),
.size = page_table.GetAddressSpaceSize(),
};
metadata.alias_extents = {
.base = page_table.GetAliasCodeRegionStart(),
.size = page_table.GetAliasCodeRegionSize(),
};
metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()};
metadata.address_space_extents = {page_table.GetAddressSpaceStart(),
page_table.GetAddressSpaceSize()};
metadata.alias_extents = {page_table.GetAliasCodeRegionStart(),
page_table.GetAliasCodeRegionSize()};
is_pending_reload.exchange(true);
}
void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
metadata.main_nso_extents = {
.base = main_region_begin,
.size = main_region_size,
};
metadata.main_nso_extents = {main_region_begin, main_region_size};
}
void CheatEngine::Reload(std::vector<CheatEntry> cheats) {

View File

@@ -313,32 +313,30 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
switch (opcode_type) {
case CheatVmOpcodeType::StoreStatic: {
StoreStaticOpcode store_static{};
// 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
// Read additional words.
const u32 second_dword = GetNextDword();
const u32 bit_width = (first_dword >> 24) & 0xF;
opcode.opcode = StoreStaticOpcode{
.bit_width = bit_width,
.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
.offset_register = (first_dword >> 16) & 0xF,
.rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
.value = GetNextVmInt(bit_width),
};
store_static.bit_width = (first_dword >> 24) & 0xF;
store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
store_static.offset_register = ((first_dword >> 16) & 0xF);
store_static.rel_address =
(static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
store_static.value = GetNextVmInt(store_static.bit_width);
opcode.opcode = store_static;
} break;
case CheatVmOpcodeType::BeginConditionalBlock: {
BeginConditionalOpcode begin_cond{};
// 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
// Read additional words.
const u32 second_dword = GetNextDword();
const u32 bit_width = (first_dword >> 24) & 0xF;
opcode.opcode = BeginConditionalOpcode{
.bit_width = bit_width,
.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF),
.rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
.value = GetNextVmInt(bit_width),
};
begin_cond.bit_width = (first_dword >> 24) & 0xF;
begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
begin_cond.rel_address =
(static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
begin_cond.value = GetNextVmInt(begin_cond.bit_width);
opcode.opcode = begin_cond;
} break;
case CheatVmOpcodeType::EndConditionalBlock: {
// 20000000
@@ -346,14 +344,12 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
opcode.opcode = EndConditionalOpcode{};
} break;
case CheatVmOpcodeType::ControlLoop: {
ControlLoopOpcode ctrl_loop{};
// 300R0000 VVVVVVVV
// 310R0000
// Parse register, whether loop start or loop end.
ControlLoopOpcode ctrl_loop{
.start_loop = ((first_dword >> 24) & 0xF) == 0,
.reg_index = (first_dword >> 20) & 0xF,
.num_iters = 0,
};
ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
// Read number of iters if loop start.
if (ctrl_loop.start_loop) {
@@ -362,65 +358,66 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
opcode.opcode = ctrl_loop;
} break;
case CheatVmOpcodeType::LoadRegisterStatic: {
LoadRegisterStaticOpcode ldr_static{};
// 400R0000 VVVVVVVV VVVVVVVV
// Read additional words.
opcode.opcode = LoadRegisterStaticOpcode{
.reg_index = (first_dword >> 16) & 0xF,
.value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(),
};
ldr_static.reg_index = ((first_dword >> 16) & 0xF);
ldr_static.value =
(static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
opcode.opcode = ldr_static;
} break;
case CheatVmOpcodeType::LoadRegisterMemory: {
LoadRegisterMemoryOpcode ldr_memory{};
// 5TMRI0AA AAAAAAAA
// Read additional words.
const u32 second_dword = GetNextDword();
opcode.opcode = LoadRegisterMemoryOpcode{
.bit_width = (first_dword >> 24) & 0xF,
.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
.reg_index = ((first_dword >> 16) & 0xF),
.load_from_reg = ((first_dword >> 12) & 0xF) != 0,
.rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
};
ldr_memory.bit_width = (first_dword >> 24) & 0xF;
ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
ldr_memory.rel_address =
(static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
opcode.opcode = ldr_memory;
} break;
case CheatVmOpcodeType::StoreStaticToAddress: {
StoreStaticToAddressOpcode str_static{};
// 6T0RIor0 VVVVVVVV VVVVVVVV
// Read additional words.
opcode.opcode = StoreStaticToAddressOpcode{
.bit_width = (first_dword >> 24) & 0xF,
.reg_index = (first_dword >> 16) & 0xF,
.increment_reg = ((first_dword >> 12) & 0xF) != 0,
.add_offset_reg = ((first_dword >> 8) & 0xF) != 0,
.offset_reg_index = (first_dword >> 4) & 0xF,
.value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(),
};
str_static.bit_width = (first_dword >> 24) & 0xF;
str_static.reg_index = ((first_dword >> 16) & 0xF);
str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
str_static.value =
(static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
opcode.opcode = str_static;
} break;
case CheatVmOpcodeType::PerformArithmeticStatic: {
PerformArithmeticStaticOpcode perform_math_static{};
// 7T0RC000 VVVVVVVV
// Read additional words.
opcode.opcode = PerformArithmeticStaticOpcode{
.bit_width = (first_dword >> 24) & 0xF,
.reg_index = ((first_dword >> 16) & 0xF),
.math_type = static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF),
.value = GetNextDword(),
};
perform_math_static.bit_width = (first_dword >> 24) & 0xF;
perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
perform_math_static.math_type =
static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF);
perform_math_static.value = GetNextDword();
opcode.opcode = perform_math_static;
} break;
case CheatVmOpcodeType::BeginKeypressConditionalBlock: {
BeginKeypressConditionalOpcode begin_keypress_cond{};
// 8kkkkkkk
// Just parse the mask.
opcode.opcode = BeginKeypressConditionalOpcode{
.key_mask = first_dword & 0x0FFFFFFF,
};
begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
opcode.opcode = begin_keypress_cond;
} break;
case CheatVmOpcodeType::PerformArithmeticRegister: {
PerformArithmeticRegisterOpcode perform_math_reg{};
// 9TCRSIs0 (VVVVVVVV (VVVVVVVV))
PerformArithmeticRegisterOpcode perform_math_reg{
.bit_width = (first_dword >> 24) & 0xF,
.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF),
.dst_reg_index = (first_dword >> 16) & 0xF,
.src_reg_1_index = (first_dword >> 12) & 0xF,
.src_reg_2_index = 0,
.has_immediate = ((first_dword >> 8) & 0xF) != 0,
.value = {},
};
perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF);
perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
if (perform_math_reg.has_immediate) {
perform_math_reg.src_reg_2_index = 0;
perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width);
@@ -430,6 +427,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
opcode.opcode = perform_math_reg;
} break;
case CheatVmOpcodeType::StoreRegisterToAddress: {
StoreRegisterToAddressOpcode str_register{};
// ATSRIOxa (aaaaaaaa)
// A = opcode 10
// T = bit width
@@ -441,23 +439,20 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// Relative Address
// x = offset register (for offset type 1), memory type (for offset type 3)
// a = relative address (for offset type 2+3)
StoreRegisterToAddressOpcode str_register{
.bit_width = (first_dword >> 24) & 0xF,
.str_reg_index = (first_dword >> 20) & 0xF,
.addr_reg_index = (first_dword >> 16) & 0xF,
.increment_reg = ((first_dword >> 12) & 0xF) != 0,
.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)),
.mem_type = MemoryAccessType::MainNso,
.ofs_reg_index = (first_dword >> 4) & 0xF,
.rel_address = 0,
};
str_register.bit_width = (first_dword >> 24) & 0xF;
str_register.str_reg_index = ((first_dword >> 20) & 0xF);
str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF));
str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
switch (str_register.ofs_type) {
case StoreRegisterOffsetType::None:
case StoreRegisterOffsetType::Reg:
// Nothing more to do
break;
case StoreRegisterOffsetType::Imm:
str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
str_register.rel_address =
((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
break;
case StoreRegisterOffsetType::MemReg:
str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
@@ -465,7 +460,8 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
case StoreRegisterOffsetType::MemImm:
case StoreRegisterOffsetType::MemImmReg:
str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
str_register.rel_address =
((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
break;
default:
str_register.ofs_type = StoreRegisterOffsetType::None;
@@ -474,6 +470,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
opcode.opcode = str_register;
} break;
case CheatVmOpcodeType::BeginRegisterConditionalBlock: {
BeginRegisterConditionalOpcode begin_reg_cond{};
// C0TcSX##
// C0TcS0Ma aaaaaaaa
// C0TcS1Mr
@@ -495,19 +492,11 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// r = offset register.
// X = other register.
// V = value.
BeginRegisterConditionalOpcode begin_reg_cond{
.bit_width = (first_dword >> 20) & 0xF,
.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF),
.val_reg_index = (first_dword >> 12) & 0xF,
.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF),
.mem_type = MemoryAccessType::MainNso,
.addr_reg_index = 0,
.other_reg_index = 0,
.ofs_reg_index = 0,
.rel_address = 0,
.value = {},
};
begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
begin_reg_cond.cond_type =
static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF);
switch (begin_reg_cond.comp_type) {
case CompareRegisterValueType::StaticValue:
@@ -519,25 +508,26 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
case CompareRegisterValueType::MemoryRelAddr:
begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
begin_reg_cond.rel_address =
(static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
break;
case CompareRegisterValueType::MemoryOfsReg:
begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
break;
case CompareRegisterValueType::RegisterRelAddr:
begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF;
begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
begin_reg_cond.rel_address =
(static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
break;
case CompareRegisterValueType::RegisterOfsReg:
begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF;
begin_reg_cond.ofs_reg_index = first_dword & 0xF;
begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
break;
}
opcode.opcode = begin_reg_cond;
} break;
case CheatVmOpcodeType::SaveRestoreRegister: {
SaveRestoreRegisterOpcode save_restore_reg{};
// C10D0Sx0
// C1 = opcode 0xC1
// D = destination index.
@@ -545,37 +535,36 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
// a register.
// NOTE: If we add more save slots later, current encoding is backwards compatible.
opcode.opcode = SaveRestoreRegisterOpcode{
.dst_index = (first_dword >> 16) & 0xF,
.src_index = (first_dword >> 8) & 0xF,
.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF),
};
save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
save_restore_reg.src_index = (first_dword >> 8) & 0xF;
save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF);
opcode.opcode = save_restore_reg;
} break;
case CheatVmOpcodeType::SaveRestoreRegisterMask: {
SaveRestoreRegisterMaskOpcode save_restore_regmask{};
// C2x0XXXX
// C2 = opcode 0xC2
// x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring.
// X = 16-bit bitmask, bit i --> save or restore register i.
SaveRestoreRegisterMaskOpcode save_restore_regmask{
.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF),
.should_operate = {},
};
save_restore_regmask.op_type =
static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF);
for (std::size_t i = 0; i < NumRegisters; i++) {
save_restore_regmask.should_operate[i] = (first_dword & (1U << i)) != 0;
save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
}
opcode.opcode = save_restore_regmask;
} break;
case CheatVmOpcodeType::ReadWriteStaticRegister: {
ReadWriteStaticRegisterOpcode rw_static_reg{};
// C3000XXx
// C3 = opcode 0xC3.
// XX = static register index.
// x = register index.
opcode.opcode = ReadWriteStaticRegisterOpcode{
.static_idx = (first_dword >> 4) & 0xFF,
.idx = first_dword & 0xF,
};
rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF);
rw_static_reg.idx = (first_dword & 0xF);
opcode.opcode = rw_static_reg;
} break;
case CheatVmOpcodeType::DebugLog: {
DebugLogOpcode debug_log{};
// FFFTIX##
// FFFTI0Ma aaaaaaaa
// FFFTI1Mr
@@ -594,36 +583,31 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// a = relative address.
// r = offset register.
// X = value register.
DebugLogOpcode debug_log{
.bit_width = (first_dword >> 16) & 0xF,
.log_id = (first_dword >> 12) & 0xF,
.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF),
.mem_type = MemoryAccessType::MainNso,
.addr_reg_index = 0,
.val_reg_index = 0,
.ofs_reg_index = 0,
.rel_address = 0,
};
debug_log.bit_width = (first_dword >> 16) & 0xF;
debug_log.log_id = ((first_dword >> 12) & 0xF);
debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF);
switch (debug_log.val_type) {
case DebugLogValueType::RegisterValue:
debug_log.val_reg_index = (first_dword >> 4) & 0xF;
debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
break;
case DebugLogValueType::MemoryRelAddr:
debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
debug_log.rel_address =
((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
break;
case DebugLogValueType::MemoryOfsReg:
debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
debug_log.ofs_reg_index = first_dword & 0xF;
debug_log.ofs_reg_index = (first_dword & 0xF);
break;
case DebugLogValueType::RegisterRelAddr:
debug_log.addr_reg_index = (first_dword >> 4) & 0xF;
debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
debug_log.rel_address =
((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
break;
case DebugLogValueType::RegisterOfsReg:
debug_log.addr_reg_index = (first_dword >> 4) & 0xF;
debug_log.ofs_reg_index = first_dword & 0xF;
debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
debug_log.ofs_reg_index = (first_dword & 0xF);
break;
}
opcode.opcode = debug_log;

View File

@@ -74,16 +74,15 @@ void PerfStats::EndGameFrame() {
game_frames += 1;
}
double PerfStats::GetMeanFrametime() const {
double PerfStats::GetMeanFrametime() {
std::lock_guard lock{object_mutex};
if (current_index <= IgnoreFrames) {
return 0;
}
const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
perf_history.begin() + current_index, 0.0);
return sum / static_cast<double>(current_index - IgnoreFrames);
return sum / (current_index - IgnoreFrames);
}
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
@@ -95,13 +94,12 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
const PerfStatsResults results{
.system_fps = static_cast<double>(system_frames) / interval,
.game_fps = static_cast<double>(game_frames) / interval,
.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
static_cast<double>(system_frames),
.emulation_speed = system_us_per_second.count() / 1'000'000.0,
};
PerfStatsResults results{};
results.system_fps = static_cast<double>(system_frames) / interval;
results.game_fps = static_cast<double>(game_frames) / interval;
results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
static_cast<double>(system_frames);
results.emulation_speed = system_us_per_second.count() / 1'000'000.0;
// Reset counters
reset_point = now;
@@ -113,7 +111,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
return results;
}
double PerfStats::GetLastFrameTimeScale() const {
double PerfStats::GetLastFrameTimeScale() {
std::lock_guard lock{object_mutex};
constexpr double FRAME_LENGTH = 1.0 / 60;

View File

@@ -30,6 +30,7 @@ struct PerfStatsResults {
class PerfStats {
public:
explicit PerfStats(u64 title_id);
~PerfStats();
using Clock = std::chrono::high_resolution_clock;
@@ -41,18 +42,18 @@ public:
PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
/**
* Returns the arithmetic mean of all frametime values stored in the performance history.
* Returns the Arthimetic Mean of all frametime values stored in the performance history.
*/
double GetMeanFrametime() const;
double GetMeanFrametime();
/**
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
* useful for scaling inputs or outputs moving between the two time domains.
*/
double GetLastFrameTimeScale() const;
double GetLastFrameTimeScale();
private:
mutable std::mutex object_mutex;
std::mutex object_mutex{};
/// Title ID for the game that is running. 0 if there is no game running yet
u64 title_id{0};
@@ -60,7 +61,7 @@ private:
std::size_t current_index{0};
/// Stores an hour of historical frametime data useful for processing and tracking performance
/// regressions with code changes.
std::array<double, 216000> perf_history{};
std::array<double, 216000> perf_history = {};
/// Point when the cumulative counters were reset
Clock::time_point reset_point = Clock::now();

View File

@@ -173,6 +173,7 @@ void RestoreGlobalState() {
values.use_assembly_shaders.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.force_30fps_mode.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);

View File

@@ -435,6 +435,7 @@ struct Values {
Setting<bool> use_vsync;
Setting<bool> use_assembly_shaders;
Setting<bool> use_asynchronous_shaders;
Setting<bool> force_30fps_mode;
Setting<bool> use_fast_gpu_time;
Setting<float> bg_red;

View File

@@ -148,17 +148,19 @@ void GCButtonFactory::EndConfiguration() {
class GCAnalog final : public Input::AnalogDevice {
public:
GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter,
float range_)
GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter)
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
origin_value_x(adapter->GetOriginValue(port_, axis_x_)),
origin_value_y(adapter->GetOriginValue(port_, axis_y_)), range(range_) {}
origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {}
float GetAxis(int axis) const {
if (gcadapter->DeviceConnected(port)) {
std::lock_guard lock{mutex};
const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range);
// division is not by a perfect 128 to account for some variance in center location
// e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
// [20-230]
return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
}
return 0.0f;
}
@@ -213,7 +215,6 @@ private:
GCAdapter::Adapter* gcadapter;
const float origin_value_x;
const float origin_value_y;
const float range;
mutable std::mutex mutex;
};
@@ -233,9 +234,8 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get());
}
void GCAnalogFactory::BeginConfiguration() {

View File

@@ -66,14 +66,14 @@ public:
state.axes.insert_or_assign(axis, value);
}
float GetAxis(int axis, float range) const {
float GetAxis(int axis) const {
std::lock_guard lock{mutex};
return state.axes.at(axis) / (32767.0f * range);
return state.axes.at(axis) / 32767.0f;
}
std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
float x = GetAxis(axis_x, range);
float y = GetAxis(axis_y, range);
std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
float x = GetAxis(axis_x);
float y = GetAxis(axis_y);
y = -y; // 3DS uses an y-axis inverse from SDL
// Make sure the coordinates are in the unit circle,
@@ -313,7 +313,7 @@ public:
trigger_if_greater(trigger_if_greater_) {}
bool GetStatus() const override {
const float axis_value = joystick->GetAxis(axis, 1.0f);
const float axis_value = joystick->GetAxis(axis);
if (trigger_if_greater) {
return axis_value > threshold;
}
@@ -329,13 +329,11 @@ private:
class SDLAnalog final : public Input::AnalogDevice {
public:
SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_,
float range_)
: joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_),
range(range_) {}
SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_)
: joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {}
std::tuple<float, float> GetStatus() const override {
const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
const auto [x, y] = joystick->GetAnalog(axis_x, axis_y);
const float r = std::sqrt((x * x) + (y * y));
if (r > deadzone) {
return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
@@ -365,7 +363,6 @@ private:
const int axis_x;
const int axis_y;
const float deadzone;
const float range;
};
/// A button device factory that creates button devices from SDL joystick
@@ -461,13 +458,13 @@ public:
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
// This is necessary so accessing GetAxis with axis_x and axis_y won't crash
joystick->SetAxis(axis_x, 0);
joystick->SetAxis(axis_y, 0);
return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range);
return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone);
}
private:

View File

@@ -46,16 +46,20 @@ struct ScopeInit final {
Core::Timing::CoreTiming core_timing;
};
#pragma optimize("", off)
u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) {
const u64 start = core_timing.GetGlobalTimeNs().count();
volatile u64 placebo = 0;
u64 start = core_timing.GetGlobalTimeNs().count();
u64 placebo = 0;
for (std::size_t i = 0; i < 1000; i++) {
placebo = placebo + core_timing.GetGlobalTimeNs().count();
placebo += core_timing.GetGlobalTimeNs().count();
}
const u64 end = core_timing.GetGlobalTimeNs().count();
return end - start;
u64 end = core_timing.GetGlobalTimeNs().count();
return (end - start);
}
#pragma optimize("", on)
} // Anonymous namespace
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <bitset>
#include <cstddef>

View File

@@ -81,7 +81,7 @@ void GPU::WaitFence(u32 syncpoint_id, u32 value) {
}
MICROPROFILE_SCOPE(GPU_wait);
std::unique_lock lock{sync_mutex};
sync_cv.wait(lock, [=, this] { return syncpoints[syncpoint_id].load() >= value; });
sync_cv.wait(lock, [=]() { return syncpoints[syncpoint_id].load() >= value; });
}
void GPU::IncrementSyncPoint(const u32 syncpoint_id) {

View File

@@ -4,6 +4,7 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
@@ -15,137 +16,121 @@
namespace Tegra {
MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
: system{system}, rasterizer{rasterizer}, page_table(page_table_size) {}
: rasterizer{rasterizer}, system{system} {
page_table.Resize(address_space_width, page_bits, false);
// Initialize the map with a single free region covering the entire managed space.
VirtualMemoryArea initial_vma;
initial_vma.size = address_space_end;
vma_map.emplace(initial_vma.base, initial_vma);
UpdatePageTableForVMA(initial_vma);
}
MemoryManager::~MemoryManager() = default;
GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
u64 remaining_size{size};
for (u64 offset{}; offset < size; offset += page_size) {
if (remaining_size < page_size) {
SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size);
} else {
SetPageEntry(gpu_addr + offset, page_entry + offset);
}
remaining_size -= page_size;
}
GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
const u64 aligned_size{Common::AlignUp(size, page_size)};
const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
AllocateMemory(gpu_addr, 0, aligned_size);
return gpu_addr;
}
GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
return UpdateRange(gpu_addr, cpu_addr, size);
GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
const u64 aligned_size{Common::AlignUp(size, page_size)};
AllocateMemory(gpu_addr, 0, aligned_size);
return gpu_addr;
}
GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) {
return Map(cpu_addr, *FindFreeRange(size, align), size);
GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
const u64 aligned_size{Common::AlignUp(size, page_size)};
const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
ASSERT(
system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess());
return gpu_addr;
}
void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
if (!size) {
return;
}
GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) {
ASSERT((gpu_addr & page_mask) == 0);
const u64 aligned_size{Common::AlignUp(size, page_size)};
MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
ASSERT(
system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess());
return gpu_addr;
}
GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
ASSERT((gpu_addr & page_mask) == 0);
const u64 aligned_size{Common::AlignUp(size, page_size)};
const auto cpu_addr = GpuToCpuAddress(gpu_addr);
ASSERT(cpu_addr);
// Flush and invalidate through the GPU interface, to be asynchronous if possible.
system.GPU().FlushAndInvalidateRegion(*GpuToCpuAddress(gpu_addr), size);
UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
}
std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
for (u64 offset{}; offset < size; offset += page_size) {
if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
return {};
}
}
return UpdateRange(gpu_addr, PageEntry::State::Allocated, size);
}
GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) {
return *AllocateFixed(*FindFreeRange(size, align), size);
}
void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) {
if (!page_entry.IsValid()) {
return;
}
system.GPU().FlushAndInvalidateRegion(*cpu_addr, aligned_size);
UnmapRange(gpu_addr, aligned_size);
ASSERT(system.CurrentProcess()
->PageTable()
.LockForDeviceAddressSpace(page_entry.ToAddress(), size)
.UnlockForDeviceAddressSpace(cpu_addr.value(), size)
.IsSuccess());
return gpu_addr;
}
void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
if (!page_entry.IsValid()) {
return;
GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) const {
// Find the first Free VMA.
const VMAHandle vma_handle{
std::find_if(vma_map.begin(), vma_map.end(), [region_start, size](const auto& vma) {
if (vma.second.type != VirtualMemoryArea::Type::Unmapped) {
return false;
}
const VAddr vma_end{vma.second.base + vma.second.size};
return vma_end > region_start && vma_end >= region_start + size;
})};
if (vma_handle == vma_map.end()) {
return {};
}
ASSERT(system.CurrentProcess()
->PageTable()
.UnlockForDeviceAddressSpace(page_entry.ToAddress(), size)
.IsSuccess());
return std::max(region_start, vma_handle->second.base);
}
PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
return page_table[PageEntryIndex(gpu_addr)];
bool MemoryManager::IsAddressValid(GPUVAddr addr) const {
return (addr >> page_bits) < page_table.pointers.size();
}
void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
// TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to
// improper tracking, but should be fixed in the future.
//// Unlock the old page
// TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size);
//// Lock the new page
// TryLockPage(page_entry, size);
page_table[PageEntryIndex(gpu_addr)] = page_entry;
}
std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align) const {
if (!align) {
align = page_size;
} else {
align = Common::AlignUp(align, page_size);
std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) const {
if (!IsAddressValid(addr)) {
return {};
}
u64 available_size{};
GPUVAddr gpu_addr{address_space_start};
while (gpu_addr + available_size < address_space_size) {
if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) {
available_size += page_size;
if (available_size >= size) {
return gpu_addr;
}
} else {
gpu_addr += available_size + page_size;
available_size = 0;
const auto remainder{gpu_addr % align};
if (remainder) {
gpu_addr = (gpu_addr - remainder) + align;
}
}
const VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]};
if (cpu_addr) {
return cpu_addr + (addr & page_mask);
}
return {};
}
std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
const auto page_entry{GetPageEntry(gpu_addr)};
if (!page_entry.IsValid()) {
template <typename T>
T MemoryManager::Read(GPUVAddr addr) const {
if (!IsAddressValid(addr)) {
return {};
}
return page_entry.ToAddress() + (gpu_addr & page_mask);
}
template <typename T>
T MemoryManager::Read(GPUVAddr addr) const {
if (auto page_pointer{GetPointer(addr)}; page_pointer) {
const u8* page_pointer{GetPointer(addr)};
if (page_pointer) {
// NOTE: Avoid adding any extra logic to this fast-path block
T value;
std::memcpy(&value, page_pointer, sizeof(T));
@@ -159,7 +144,12 @@ T MemoryManager::Read(GPUVAddr addr) const {
template <typename T>
void MemoryManager::Write(GPUVAddr addr, T data) {
if (auto page_pointer{GetPointer(addr)}; page_pointer) {
if (!IsAddressValid(addr)) {
return;
}
u8* page_pointer{GetPointer(addr)};
if (page_pointer) {
// NOTE: Avoid adding any extra logic to this fast-path block
std::memcpy(page_pointer, &data, sizeof(T));
return;
@@ -177,49 +167,66 @@ template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) {
if (!GetPageEntry(gpu_addr).IsValid()) {
u8* MemoryManager::GetPointer(GPUVAddr addr) {
if (!IsAddressValid(addr)) {
return {};
}
const auto address{GpuToCpuAddress(gpu_addr)};
if (!address) {
return {};
auto& memory = system.Memory();
const VAddr page_addr{page_table.backing_addr[addr >> page_bits]};
if (page_addr != 0) {
return memory.GetPointer(page_addr + (addr & page_mask));
}
return system.Memory().GetPointer(*address);
LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
return {};
}
const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
if (!GetPageEntry(gpu_addr).IsValid()) {
const u8* MemoryManager::GetPointer(GPUVAddr addr) const {
if (!IsAddressValid(addr)) {
return {};
}
const auto address{GpuToCpuAddress(gpu_addr)};
if (!address) {
return {};
const auto& memory = system.Memory();
const VAddr page_addr{page_table.backing_addr[addr >> page_bits]};
if (page_addr != 0) {
return memory.GetPointer(page_addr + (addr & page_mask));
}
return system.Memory().GetPointer(*address);
LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
return {};
}
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
bool MemoryManager::IsBlockContinuous(const GPUVAddr start, const std::size_t size) const {
const std::size_t inner_size = size - 1;
const GPUVAddr end = start + inner_size;
const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start));
const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end));
const auto range = static_cast<std::size_t>(host_ptr_end - host_ptr_start);
return range == inner_size;
}
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer,
const std::size_t size) const {
std::size_t remaining_size{size};
std::size_t page_index{gpu_src_addr >> page_bits};
std::size_t page_offset{gpu_src_addr & page_mask};
auto& memory = system.Memory();
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
const auto src_addr{*page_addr + page_offset};
// Flush must happen on the rasterizer interface, such that memory is always synchronous
// when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
rasterizer.FlushRegion(src_addr, copy_amount);
system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
}
const VAddr src_addr{page_table.backing_addr[page_index] + page_offset};
// Flush must happen on the rasterizer interface, such that memory is always synchronous
// when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
rasterizer.FlushRegion(src_addr, copy_amount);
memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
page_index++;
page_offset = 0;
@@ -234,17 +241,18 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
std::size_t page_index{gpu_src_addr >> page_bits};
std::size_t page_offset{gpu_src_addr & page_mask};
auto& memory = system.Memory();
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
const auto src_addr{*page_addr + page_offset};
system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
const u8* page_pointer = page_table.pointers[page_index];
if (page_pointer) {
const VAddr src_addr{page_table.backing_addr[page_index] + page_offset};
memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
} else {
std::memset(dest_buffer, 0, copy_amount);
}
page_index++;
page_offset = 0;
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
@@ -252,23 +260,23 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
}
}
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer,
const std::size_t size) {
std::size_t remaining_size{size};
std::size_t page_index{gpu_dest_addr >> page_bits};
std::size_t page_offset{gpu_dest_addr & page_mask};
auto& memory = system.Memory();
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
const auto dest_addr{*page_addr + page_offset};
// Invalidate must happen on the rasterizer interface, such that memory is always
// synchronous when it is written (even when in asynchronous GPU mode).
rasterizer.InvalidateRegion(dest_addr, copy_amount);
system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
}
const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset};
// Invalidate must happen on the rasterizer interface, such that memory is always
// synchronous when it is written (even when in asynchronous GPU mode).
rasterizer.InvalidateRegion(dest_addr, copy_amount);
memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
page_index++;
page_offset = 0;
@@ -278,20 +286,21 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, s
}
void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
std::size_t size) {
const std::size_t size) {
std::size_t remaining_size{size};
std::size_t page_index{gpu_dest_addr >> page_bits};
std::size_t page_offset{gpu_dest_addr & page_mask};
auto& memory = system.Memory();
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
const auto dest_addr{*page_addr + page_offset};
system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
u8* page_pointer = page_table.pointers[page_index];
if (page_pointer) {
const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset};
memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
}
page_index++;
page_offset = 0;
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
@@ -299,26 +308,273 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buf
}
}
void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr,
const std::size_t size) {
std::vector<u8> tmp_buffer(size);
ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
}
void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr,
std::size_t size) {
const std::size_t size) {
std::vector<u8> tmp_buffer(size);
ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size);
WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
}
bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) {
const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
if (!cpu_addr) {
return {};
}
const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
const VAddr addr = page_table.backing_addr[gpu_addr >> page_bits];
const std::size_t page = (addr & Core::Memory::PAGE_MASK) + size;
return page <= Core::Memory::PAGE_SIZE;
}
void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
VAddr backing_addr) {
LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size,
(base + size) * page_size);
const VAddr end{base + size};
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
base + page_table.pointers.size());
if (memory == nullptr) {
while (base != end) {
page_table.pointers[base] = nullptr;
page_table.backing_addr[base] = 0;
base += 1;
}
} else {
while (base != end) {
page_table.pointers[base] = memory;
page_table.backing_addr[base] = backing_addr;
base += 1;
memory += page_size;
backing_addr += page_size;
}
}
}
void MemoryManager::MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr) {
ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base);
MapPages(base / page_size, size / page_size, target, Common::PageType::Memory, backing_addr);
}
void MemoryManager::UnmapRegion(GPUVAddr base, u64 size) {
ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base);
MapPages(base / page_size, size / page_size, nullptr, Common::PageType::Unmapped);
}
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
ASSERT(base + size == next.base);
if (type != next.type) {
return {};
}
if (type == VirtualMemoryArea::Type::Allocated && (offset + size != next.offset)) {
return {};
}
if (type == VirtualMemoryArea::Type::Mapped && backing_memory + size != next.backing_memory) {
return {};
}
return true;
}
MemoryManager::VMAHandle MemoryManager::FindVMA(GPUVAddr target) const {
if (target >= address_space_end) {
return vma_map.end();
} else {
return std::prev(vma_map.upper_bound(target));
}
}
MemoryManager::VMAIter MemoryManager::Allocate(VMAIter vma_handle) {
VirtualMemoryArea& vma{vma_handle->second};
vma.type = VirtualMemoryArea::Type::Allocated;
vma.backing_addr = 0;
vma.backing_memory = {};
UpdatePageTableForVMA(vma);
return MergeAdjacent(vma_handle);
}
MemoryManager::VMAHandle MemoryManager::AllocateMemory(GPUVAddr target, std::size_t offset,
u64 size) {
// This is the appropriately sized VMA that will turn into our allocation.
VMAIter vma_handle{CarveVMA(target, size)};
VirtualMemoryArea& vma{vma_handle->second};
ASSERT(vma.size == size);
vma.offset = offset;
return Allocate(vma_handle);
}
MemoryManager::VMAHandle MemoryManager::MapBackingMemory(GPUVAddr target, u8* memory, u64 size,
VAddr backing_addr) {
// This is the appropriately sized VMA that will turn into our allocation.
VMAIter vma_handle{CarveVMA(target, size)};
VirtualMemoryArea& vma{vma_handle->second};
ASSERT(vma.size == size);
vma.type = VirtualMemoryArea::Type::Mapped;
vma.backing_memory = memory;
vma.backing_addr = backing_addr;
UpdatePageTableForVMA(vma);
return MergeAdjacent(vma_handle);
}
void MemoryManager::UnmapRange(GPUVAddr target, u64 size) {
VMAIter vma{CarveVMARange(target, size)};
const VAddr target_end{target + size};
const VMAIter end{vma_map.end()};
// The comparison against the end of the range must be done using addresses since VMAs can be
// merged during this process, causing invalidation of the iterators.
while (vma != end && vma->second.base < target_end) {
// Unmapped ranges return to allocated state and can be reused
// This behavior is used by Super Mario Odyssey, Sonic Forces, and likely other games
vma = std::next(Allocate(vma));
}
ASSERT(FindVMA(target)->second.size >= size);
}
MemoryManager::VMAIter MemoryManager::StripIterConstness(const VMAHandle& iter) {
// This uses a neat C++ trick to convert a const_iterator to a regular iterator, given
// non-const access to its container.
return vma_map.erase(iter, iter); // Erases an empty range of elements
}
MemoryManager::VMAIter MemoryManager::CarveVMA(GPUVAddr base, u64 size) {
ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size);
ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: 0x{:016X}", base);
VMAIter vma_handle{StripIterConstness(FindVMA(base))};
if (vma_handle == vma_map.end()) {
// Target address is outside the managed range
return {};
}
const VirtualMemoryArea& vma{vma_handle->second};
if (vma.type == VirtualMemoryArea::Type::Mapped) {
// Region is already allocated
return vma_handle;
}
const VAddr start_in_vma{base - vma.base};
const VAddr end_in_vma{start_in_vma + size};
ASSERT_MSG(end_in_vma <= vma.size, "region size 0x{:016X} is less than required size 0x{:016X}",
vma.size, end_in_vma);
if (end_in_vma < vma.size) {
// Split VMA at the end of the allocated region
SplitVMA(vma_handle, end_in_vma);
}
if (start_in_vma != 0) {
// Split VMA at the start of the allocated region
vma_handle = SplitVMA(vma_handle, start_in_vma);
}
return vma_handle;
}
MemoryManager::VMAIter MemoryManager::CarveVMARange(GPUVAddr target, u64 size) {
ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size);
ASSERT_MSG((target & page_mask) == 0, "non-page aligned base: 0x{:016X}", target);
const VAddr target_end{target + size};
ASSERT(target_end >= target);
ASSERT(size > 0);
VMAIter begin_vma{StripIterConstness(FindVMA(target))};
const VMAIter i_end{vma_map.lower_bound(target_end)};
if (std::any_of(begin_vma, i_end, [](const auto& entry) {
return entry.second.type == VirtualMemoryArea::Type::Unmapped;
})) {
return {};
}
if (target != begin_vma->second.base) {
begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base);
}
VMAIter end_vma{StripIterConstness(FindVMA(target_end))};
if (end_vma != vma_map.end() && target_end != end_vma->second.base) {
end_vma = SplitVMA(end_vma, target_end - end_vma->second.base);
}
return begin_vma;
}
MemoryManager::VMAIter MemoryManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
VirtualMemoryArea& old_vma{vma_handle->second};
VirtualMemoryArea new_vma{old_vma}; // Make a copy of the VMA
// For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably
// a bug. This restriction might be removed later.
ASSERT(offset_in_vma < old_vma.size);
ASSERT(offset_in_vma > 0);
old_vma.size = offset_in_vma;
new_vma.base += offset_in_vma;
new_vma.size -= offset_in_vma;
switch (new_vma.type) {
case VirtualMemoryArea::Type::Unmapped:
break;
case VirtualMemoryArea::Type::Allocated:
new_vma.offset += offset_in_vma;
break;
case VirtualMemoryArea::Type::Mapped:
new_vma.backing_memory += offset_in_vma;
break;
}
ASSERT(old_vma.CanBeMergedWith(new_vma));
return vma_map.emplace_hint(std::next(vma_handle), new_vma.base, new_vma);
}
MemoryManager::VMAIter MemoryManager::MergeAdjacent(VMAIter iter) {
const VMAIter next_vma{std::next(iter)};
if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
iter->second.size += next_vma->second.size;
vma_map.erase(next_vma);
}
if (iter != vma_map.begin()) {
VMAIter prev_vma{std::prev(iter)};
if (prev_vma->second.CanBeMergedWith(iter->second)) {
prev_vma->second.size += iter->second.size;
vma_map.erase(iter);
iter = prev_vma;
}
}
return iter;
}
void MemoryManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
switch (vma.type) {
case VirtualMemoryArea::Type::Unmapped:
UnmapRegion(vma.base, vma.size);
break;
case VirtualMemoryArea::Type::Allocated:
MapMemoryRegion(vma.base, vma.size, nullptr, vma.backing_addr);
break;
case VirtualMemoryArea::Type::Mapped:
MapMemoryRegion(vma.base, vma.size, vma.backing_memory, vma.backing_addr);
break;
}
}
} // namespace Tegra

View File

@@ -6,9 +6,9 @@
#include <map>
#include <optional>
#include <vector>
#include "common/common_types.h"
#include "common/page_table.h"
namespace VideoCore {
class RasterizerInterface;
@@ -20,57 +20,45 @@ class System;
namespace Tegra {
class PageEntry final {
public:
enum class State : u32 {
Unmapped = static_cast<u32>(-1),
Allocated = static_cast<u32>(-2),
/**
* Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space
* with homogeneous attributes across its extents. In this particular implementation each VMA is
* also backed by a single host memory allocation.
*/
struct VirtualMemoryArea {
enum class Type : u8 {
Unmapped,
Allocated,
Mapped,
};
constexpr PageEntry() = default;
constexpr PageEntry(State state) : state{state} {}
constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
/// Virtual base address of the region.
GPUVAddr base{};
/// Size of the region.
u64 size{};
/// Memory area mapping type.
Type type{Type::Unmapped};
/// CPU memory mapped address corresponding to this memory area.
VAddr backing_addr{};
/// Offset into the backing_memory the mapping starts from.
std::size_t offset{};
/// Pointer backing this VMA.
u8* backing_memory{};
constexpr bool IsUnmapped() const {
return state == State::Unmapped;
}
constexpr bool IsAllocated() const {
return state == State::Allocated;
}
constexpr bool IsValid() const {
return !IsUnmapped() && !IsAllocated();
}
constexpr VAddr ToAddress() const {
if (!IsValid()) {
return {};
}
return static_cast<VAddr>(state) << ShiftBits;
}
constexpr PageEntry operator+(u64 offset) {
// If this is a reserved value, offsets do not apply
if (!IsValid()) {
return *this;
}
return PageEntry{(static_cast<VAddr>(state) << ShiftBits) + offset};
}
private:
static constexpr std::size_t ShiftBits{12};
State state{State::Unmapped};
/// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
};
static_assert(sizeof(PageEntry) == 4, "PageEntry is too large");
class MemoryManager final {
public:
explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
~MemoryManager();
GPUVAddr AllocateSpace(u64 size, u64 align);
GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align);
GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size);
GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size);
std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
template <typename T>
@@ -82,6 +70,9 @@ public:
u8* GetPointer(GPUVAddr addr);
const u8* GetPointer(GPUVAddr addr) const;
/// Returns true if the block is continuous in host memory, false otherwise
bool IsBlockContinuous(GPUVAddr start, std::size_t size) const;
/**
* ReadBlock and WriteBlock are full read and write operations over virtual
* GPU Memory. It's important to use these when GPU memory may not be continuous
@@ -107,43 +98,92 @@ public:
void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size);
/**
* IsGranularRange checks if a gpu region can be simply read with a pointer.
* IsGranularRange checks if a gpu region can be simply read with a pointer
*/
bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size);
GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
GPUVAddr Allocate(std::size_t size, std::size_t align);
void Unmap(GPUVAddr gpu_addr, std::size_t size);
private:
using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>;
using VMAHandle = VMAMap::const_iterator;
using VMAIter = VMAMap::iterator;
bool IsAddressValid(GPUVAddr addr) const;
void MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
VAddr backing_addr = 0);
void MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr);
void UnmapRegion(GPUVAddr base, u64 size);
/// Finds the VMA in which the given address is included in, or `vma_map.end()`.
VMAHandle FindVMA(GPUVAddr target) const;
VMAHandle AllocateMemory(GPUVAddr target, std::size_t offset, u64 size);
/**
* Maps an unmanaged host memory pointer at a given address.
*
* @param target The guest address to start the mapping at.
* @param memory The memory to be mapped.
* @param size Size of the mapping in bytes.
* @param backing_addr The base address of the range to back this mapping.
*/
VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr);
/// Unmaps a range of addresses, splitting VMAs as necessary.
void UnmapRange(GPUVAddr target, u64 size);
/// Converts a VMAHandle to a mutable VMAIter.
VMAIter StripIterConstness(const VMAHandle& iter);
/// Marks as the specified VMA as allocated.
VMAIter Allocate(VMAIter vma);
/**
* Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing
* the appropriate error checking.
*/
VMAIter CarveVMA(GPUVAddr base, u64 size);
/**
* Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each
* end of the range.
*/
VMAIter CarveVMARange(GPUVAddr base, u64 size);
/**
* Splits a VMA in two, at the specified offset.
* @returns the right side of the split, with the original iterator becoming the left side.
*/
VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma);
/**
* Checks for and merges the specified VMA with adjacent ones if possible.
* @returns the merged VMA or the original if no merging was possible.
*/
VMAIter MergeAdjacent(VMAIter vma);
/// Updates the pages corresponding to this VMA so they match the VMA's attributes.
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
/// Finds a free (unmapped region) of the specified size starting at the specified address.
GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size) const;
private:
PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
void TryLockPage(PageEntry page_entry, std::size_t size);
void TryUnlockPage(PageEntry page_entry, std::size_t size);
static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
return (gpu_addr >> page_bits) & page_table_mask;
}
static constexpr u64 address_space_size = 1ULL << 40;
static constexpr u64 address_space_start = 1ULL << 32;
static constexpr u64 page_bits{16};
static constexpr u64 page_size{1 << page_bits};
static constexpr u64 page_mask{page_size - 1};
static constexpr u64 page_table_bits{24};
static constexpr u64 page_table_size{1 << page_table_bits};
static constexpr u64 page_table_mask{page_table_size - 1};
Core::System& system;
/// Address space in bits, according to Tegra X1 TRM
static constexpr u32 address_space_width{40};
/// Start address for mapping, this is fairly arbitrary but must be non-zero.
static constexpr GPUVAddr address_space_base{0x100000};
/// End of address space, based on address space in bits.
static constexpr GPUVAddr address_space_end{1ULL << address_space_width};
Common::PageTable page_table;
VMAMap vma_map;
VideoCore::RasterizerInterface& rasterizer;
std::vector<PageEntry> page_table;
Core::System& system;
};
} // namespace Tegra

View File

@@ -1463,8 +1463,9 @@ void ARBDecompiler::Exit() {
}
const auto safe_get_register = [this](u32 reg) -> std::string {
// TODO(Rodrigo): Replace with contains once C++20 releases
const auto& used_registers = ir.GetRegisters();
if (used_registers.contains(reg)) {
if (used_registers.find(reg) != used_registers.end()) {
return fmt::format("R{}.x", reg);
}
return "{0, 0, 0, 0}.x";

View File

@@ -178,11 +178,16 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
if (device.UseAsynchronousShaders()) {
// Max worker threads we should allow
constexpr u32 MAX_THREADS = 4;
// Deduce how many threads we can use
const u32 threads_used = std::thread::hardware_concurrency() / 4;
constexpr auto MAX_THREADS = 2u;
// Amount of threads we should reserve for other parts of yuzu
constexpr auto RESERVED_THREADS = 6u;
// Get the amount of threads we can use(this can return zero)
const auto cpu_thread_count =
std::max(RESERVED_THREADS, std::thread::hardware_concurrency());
// Deduce how many "extra" threads we have to use.
const auto max_threads_unused = cpu_thread_count - RESERVED_THREADS;
// Always allow at least 1 thread regardless of our settings
const auto max_worker_count = std::max(1U, threads_used);
const auto max_worker_count = std::max(1u, max_threads_unused);
// Don't use more than MAX_THREADS
const auto worker_count = std::min(max_worker_count, MAX_THREADS);
async_shaders.AllocateWorkers(worker_count);

View File

@@ -2265,7 +2265,8 @@ private:
}
const auto& used_registers = ir.GetRegisters();
const auto SafeGetRegister = [&](u32 reg) -> Expression {
if (used_registers.contains(reg)) {
// TODO(Rodrigo): Replace with contains once C++20 releases
if (used_registers.find(reg) != used_registers.end()) {
return {GetRegister(reg), Type::Float};
}
return {"0.0f", Type::Float};

View File

@@ -696,7 +696,6 @@ void VKBlitScreen::CreateFramebuffers() {
.flags = 0,
.renderPass = *renderpass,
.attachmentCount = 1,
.pAttachments = nullptr,
.width = size.width,
.height = size.height,
.layers = 1,

View File

@@ -771,9 +771,8 @@ std::vector<VkDeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = queue_family,
.queueCount = 1,
.pQueuePriorities = nullptr,
});
ci.queueCount = 1;
ci.pQueuePriorities = &QUEUE_PRIORITY;
}

View File

@@ -261,13 +261,8 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
}
const Specialization specialization{
.base_binding = 0,
.workgroup_size = key.workgroup_size,
.shared_memory_size = key.shared_memory_size,
.point_size = std::nullopt,
.enabled_attributes = {},
.attribute_types = {},
.ndc_minus_one_to_one = false,
};
const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute,
shader->GetRegistry(), specialization),

Some files were not shown because too many files have changed in this diff Show More