Compare commits

..

3 Commits

Author SHA1 Message Date
lizzie c9d5ddab57 2026-09-15 19:47:11
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 19:47:11 +00:00
lizzie 1eb44e3ea6 2026-09-15 19:39:00
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 19:39:00 +00:00
lizzie 6c96d9f43d 2026-09-15 07:59:31
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 07:59:31 +00:00
37 changed files with 408 additions and 690 deletions
+1 -2
View File
@@ -110,12 +110,11 @@ add_library(
socket_types.h socket_types.h
sparse_large_vector.cpp sparse_large_vector.cpp
sparse_large_vector.h sparse_large_vector.h
spin_lock.h
stb.cpp stb.cpp
stb.h stb.h
steady_clock.cpp steady_clock.cpp
steady_clock.h steady_clock.h
stream.cpp
stream.h
string_util.cpp string_util.cpp
string_util.h string_util.h
swap.h swap.h
+3 -4
View File
@@ -244,8 +244,7 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0} , ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0} , us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0} , ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
, rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1} , rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0} , cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0} , gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
, invariant{invariant_} , invariant{invariant_}
@@ -292,7 +291,7 @@ bool WallClock::IsNative() const {
} }
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const { u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor); return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
} }
#elif defined(HAS_NCE) #elif defined(HAS_NCE)
namespace { namespace {
@@ -417,7 +416,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
const WallClock g_wall_clock = [] { const WallClock g_wall_clock = [] {
#if defined(ARCHITECTURE_x86_64) #if defined(ARCHITECTURE_x86_64)
auto const& caps = Common::g_cpu_caps; auto const& caps = Common::g_cpu_caps;
return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency); return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
#elif defined(HAS_NCE) #elif defined(HAS_NCE)
return WallClock(false, 1); return WallClock(false, 1);
#else #else
-1
View File
@@ -96,7 +96,6 @@ public:
u64 ns_rdtsc_factor; u64 ns_rdtsc_factor;
u64 us_rdtsc_factor; u64 us_rdtsc_factor;
u64 ms_rdtsc_factor; u64 ms_rdtsc_factor;
u64 rdtsc_ns_integer;
u64 rdtsc_ns_factor; u64 rdtsc_ns_factor;
u64 cntpct_rdtsc_factor; u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor; u64 gputick_rdtsc_factor;
+12 -65
View File
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <cctype>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "common/container/unordered_map.h" #include "common/container/unordered_map.h"
@@ -481,80 +480,28 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end()); path.end());
std::string root; const bool absolute = !path.empty() && path[0] == type2;
std::string_view components{path};
bool drive_relative = false;
#ifdef _WIN32
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
const bool drive =
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
if (network) {
root.assign(2, type2);
components.remove_prefix(2);
} else if (drive) {
root.assign(path.data(), 2);
components.remove_prefix(2);
if (!components.empty() && components.front() == type2) {
root += type2;
components.remove_prefix(1);
} else {
drive_relative = true;
}
}
#endif
if (root.empty() && !components.empty() && components.front() == type2) {
root += type2;
components.remove_prefix(1);
}
const auto path_parts = SplitPathComponents(components);
std::size_t root_component_count = 0;
#ifdef _WIN32
if (network) {
root_component_count = 2;
const auto is_unc = [](std::string_view part) {
return part.size() == 3 && (part[0] == 'U' || part[0] == 'u') &&
(part[1] == 'N' || part[1] == 'n') && (part[2] == 'C' || part[2] == 'c');
};
if (path_parts.size() >= 2 && path_parts[0] == "?" && is_unc(path_parts[1])) {
root_component_count = 4;
}
}
#endif
std::vector<std::string_view> parts; std::vector<std::string_view> parts;
for (std::size_t i = 0; i < path_parts.size(); ++i) {
const auto part = path_parts[i]; for (const auto part : SplitPathComponents(path))
if (i < root_component_count) { {
parts.push_back(part); if (part.empty() || part == ".")
} else if (part.empty() || part == ".") {
continue; continue;
} else if (part == "..") { if (part == ".." && !parts.empty() && parts.back() != "..")
if (parts.size() > root_component_count) { parts.pop_back();
parts.pop_back(); else if (part != "..") parts.push_back(part);
}
} else {
parts.push_back(part);
}
} }
const std::size_t root_length = root.size(); std::string resolved = absolute ? std::string(1, type2) : std::string{};
std::string resolved = std::move(root); for (std::size_t i = 0; i < parts.size(); ++i)
for (std::size_t i = 0; i < parts.size(); ++i) { {
if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative)) if (i != 0)
resolved += type2; resolved += type2;
resolved.append(parts[i].data(), parts[i].size()); resolved.append(parts[i].data(), parts[i].size());
} }
path = std::move(resolved); path = std::move(resolved);
if (!path.empty() && path.size() == root_length) {
return path;
}
return std::string(RemoveTrailingSlash(path)); return std::string(RemoveTrailingSlash(path));
} }
+2 -3
View File
@@ -347,9 +347,8 @@ enum class DirectorySeparator {
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } // i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename); [[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves // Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are // depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
// preserved.
[[nodiscard]] std::string SanitizePath( [[nodiscard]] std::string SanitizePath(
std::string_view path, std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
+3 -3
View File
@@ -96,15 +96,15 @@ struct PageTable {
} }
/// Write page info atomically /// Write page info atomically
inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept { constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer})); data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
} }
inline void MarkRasterizerCached() noexcept { constexpr void MarkRasterizerCached() noexcept {
data_raw.fetch_or(0b111); data_raw.fetch_or(0b111);
} }
inline void MarkDebug(u64 ptr, u16 block) noexcept { constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
Store(true, PageType::DebugMemory, block, ptr); Store(true, PageType::DebugMemory, block, ptr);
} }
+1 -2
View File
@@ -38,8 +38,7 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
/// A large page-aligned buffer that has optimized memory usage for zero-writes. /// A large page-aligned buffer that has optimized memory usage for zero-writes.
template <typename T> template <typename T>
// MSVC doesn't regard structs with atomics as trivially copyable requires std::is_trivially_copyable_v<T>
// requires std::is_trivially_copyable_v<T>
class SparseLargeVector final { class SparseLargeVector final {
public: public:
constexpr SparseLargeVector() = default; constexpr SparseLargeVector() = default;
+50
View File
@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _MSC_VER
#include <intrin.h>
#elif defined(ARCHITECTURE_x86_64)
#include <xmmintrin.h>
#endif
#include <atomic>
namespace Common {
/// @brief A lock similar to mutex that forces a thread to spin wait instead calling the
/// supervisor. Should be used on short sequences of code.
struct SpinLock {
SpinLock() noexcept = default;
SpinLock(const SpinLock&) noexcept = delete;
SpinLock& operator=(const SpinLock&) noexcept = delete;
SpinLock(SpinLock&&) noexcept = delete;
SpinLock& operator=(SpinLock&&) noexcept = delete;
inline void lock() noexcept {
while (lck.test_and_set(std::memory_order_acquire)) {
#if defined(ARCHITECTURE_x86_64)
_mm_pause();
#elif defined(ARCHITECTURE_arm64) && defined(_MSC_VER)
__yield();
#elif defined(ARCHITECTURE_arm64)
asm("yield");
#endif
}
}
inline void unlock() noexcept {
lck.clear(std::memory_order_release);
}
[[nodiscard]] inline bool try_lock() noexcept {
return !lck.test_and_set(std::memory_order_acquire);
}
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
} // namespace Common
-46
View File
@@ -1,46 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stdexcept>
#include "common/common_types.h"
#include "common/stream.h"
namespace Common {
Stream::Stream() = default;
Stream::~Stream() = default;
void Stream::Seek(s32 offset, SeekOrigin origin) {
if (origin == SeekOrigin::SetOrigin) {
if (offset < 0) {
position = 0;
} else if (position >= buffer.size()) {
position = buffer.size();
} else {
position = offset;
}
} else if (origin == SeekOrigin::FromCurrentPos) {
Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
} else if (origin == SeekOrigin::FromEnd) {
Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
}
}
u8 Stream::ReadByte() {
if (position < buffer.size()) {
return buffer[position++];
} else {
throw std::out_of_range("Attempting to read a byte not within the buffer range");
}
}
void Stream::WriteByte(u8 byte) {
if (position == buffer.size()) {
buffer.push_back(byte);
position++;
} else {
buffer.insert(buffer.begin() + position, byte);
}
}
} // namespace Common
-55
View File
@@ -1,55 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Common {
enum class SeekOrigin {
SetOrigin,
FromCurrentPos,
FromEnd,
};
class Stream {
public:
/// Stream creates a bitstream and provides common functionality on the stream.
explicit Stream();
~Stream();
Stream(const Stream&) = delete;
Stream& operator=(const Stream&) = delete;
Stream(Stream&&) = default;
Stream& operator=(Stream&&) = default;
/// Reposition bitstream "cursor" to the specified offset from origin
void Seek(s32 offset, SeekOrigin origin);
/// Reads next byte in the stream buffer and increments position
u8 ReadByte();
/// Writes byte at current position
void WriteByte(u8 byte);
[[nodiscard]] std::size_t GetPosition() const {
return position;
}
[[nodiscard]] std::vector<u8>& GetBuffer() {
return buffer;
}
[[nodiscard]] const std::vector<u8>& GetBuffer() const {
return buffer;
}
private:
std::vector<u8> buffer;
std::size_t position{0};
};
} // namespace Common
+2 -2
View File
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
return uuid; return uuid;
} }
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) { UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
UUID uuid{}; UUID uuid{};
std::memcpy(&uuid.uuid, sha1.data(), sha1.size()); std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F); uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF); uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
return uuid; return uuid;
+1 -1
View File
@@ -104,7 +104,7 @@ struct UUID {
/// @returns A random UUID that is RFC 4122 Version 4 compliant. /// @returns A random UUID that is RFC 4122 Version 4 compliant.
[[nodiscard]] static UUID MakeRandomRFC4122V4(); [[nodiscard]] static UUID MakeRandomRFC4122V4();
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> sha1); [[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default; friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
}; };
+2 -2
View File
@@ -796,8 +796,6 @@ add_library(core STATIC
hle/service/ns/application_manager_interface.h hle/service/ns/application_manager_interface.h
hle/service/ns/application_version_interface.cpp hle/service/ns/application_version_interface.cpp
hle/service/ns/application_version_interface.h hle/service/ns/application_version_interface.h
hle/service/ns/async_result.cpp
hle/service/ns/async_result.h
hle/service/ns/content_management_interface.cpp hle/service/ns/content_management_interface.cpp
hle/service/ns/content_management_interface.h hle/service/ns/content_management_interface.h
hle/service/ns/develop_interface.cpp hle/service/ns/develop_interface.cpp
@@ -812,6 +810,8 @@ add_library(core STATIC
hle/service/ns/ecommerce_interface.h hle/service/ns/ecommerce_interface.h
hle/service/ns/factory_reset_interface.cpp hle/service/ns/factory_reset_interface.cpp
hle/service/ns/factory_reset_interface.h hle/service/ns/factory_reset_interface.h
hle/service/ns/i_async_result.cpp
hle/service/ns/i_async_result.h
hle/service/ns/language.cpp hle/service/ns/language.cpp
hle/service/ns/language.h hle/service/ns/language.h
hle/service/ns/ns.cpp hle/service/ns/ns.cpp
+2 -2
View File
@@ -177,7 +177,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>( config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data())); const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK; config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = uint8_t(0); config.page_table_marked_bit = 0;
config.absolute_offset_page_table = true; config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
config.only_detect_misalignment_via_page_table_on_page_boundary = true; config.only_detect_misalignment_via_page_table_on_page_boundary = true;
@@ -193,7 +193,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) { Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries, // Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative. // we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT); config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
} }
} }
+2 -2
View File
@@ -216,7 +216,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data())); const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table_address_space_bits = std::uint32_t(address_space_bits); config.page_table_address_space_bits = std::uint32_t(address_space_bits);
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK; config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = uint8_t(0); config.page_table_marked_bit = 0;
config.silently_mirror_page_table = false; config.silently_mirror_page_table = false;
config.absolute_offset_page_table = true; config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
@@ -235,7 +235,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) { Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries, // Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative. // we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT); config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
} }
} }
+18 -18
View File
@@ -100,7 +100,8 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
struct IPSwitchRecord { struct IPSwitchRecord {
std::vector<uint8_t> data; std::array<uint8_t, 256 - sizeof(size_t)> data;
size_t count;
}; };
struct IPSwitchCompiler::IPSwitchPatch { struct IPSwitchCompiler::IPSwitchPatch {
::Common::unordered_map<u32, IPSwitchRecord> records; ::Common::unordered_map<u32, IPSwitchRecord> records;
@@ -121,23 +122,22 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
IPSwitchRecord r{}; IPSwitchRecord r{};
for (auto it = sv.cbegin(); it < sv.cend(); ) { for (auto it = sv.cbegin(); it < sv.cend(); ) {
if (*it == '\\' && it + 1 < sv.cend()) { if (*it == '\\' && it + 1 < sv.cend()) {
r.data.push_back([it]() { switch (it[1]) {
switch (it[1]) { case 'a': r.data[r.count] = '\a'; break;
case 'a': return '\a'; case 'b': r.data[r.count] = '\b'; break;
case 'b': return '\b'; case 'e': r.data[r.count] = '\e'; break;
case 'e': return '\e'; case 'f': r.data[r.count] = '\f'; break;
case 'f': return '\f'; case 'n': r.data[r.count] = '\n'; break;
case 'n': return '\n'; case 'r': r.data[r.count] = '\r'; break;
case 'r': return '\r'; case 't': r.data[r.count] = '\t'; break;
case 't': return '\t'; case 'v': r.data[r.count] = '\v'; break;
case 'v': return '\v'; case '?': r.data[r.count] = '\?'; break;
case '?': return '\?'; default: r.data[r.count] = it[1]; break;
default: return it[1]; }
} ++r.count;
}());
it += 2; it += 2;
} else { } else {
r.data.push_back(*it); ++r.count;
++it; ++it;
} }
} }
@@ -223,8 +223,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
if (start <= line.cend() && end <= line.cend()) { if (start <= line.cend() && end <= line.cend()) {
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun! // Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
auto const hs = Common::HexStringToVector({start, end}, is_little_endian); auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
r.data.resize(hs.size());
std::memcpy(r.data.data(), hs.data(), hs.size()); std::memcpy(r.data.data(), hs.data(), hs.size());
r.count = hs.size();
LOG_INFO(Loader, "[H] value @ {:#08X}", offset); LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
patches.back().records.insert_or_assign(u32(offset), std::move(r)); patches.back().records.insert_or_assign(u32(offset), std::move(r));
} else { } else {
@@ -293,7 +293,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
if (patch.enabled) { if (patch.enabled) {
for (const auto& record : patch.records) { for (const auto& record : patch.records) {
if (record.first < in_data.size()) { if (record.first < in_data.size()) {
auto replace_size = record.second.data.size(); auto replace_size = record.second.count;
if (record.first + replace_size > in_data.size()) if (record.first + replace_size > in_data.size())
replace_size = in_data.size() - record.first; replace_size = in_data.size() - record.first;
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size); std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
+8 -25
View File
@@ -12,7 +12,6 @@
#include "hid_core/frontend/emulated_controller.h" #include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h" #include "hid_core/hid_core.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include <array>
namespace Core::Frontend { namespace Core::Frontend {
@@ -30,39 +29,23 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
const std::size_t min_supported_players = const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players; parameters.enable_single_mode ? 1 : parameters.min_players;
using Core::HID::NpadStyleIndex;
const std::size_t max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
std::size_t num_selected_players = 0;
std::array<bool, HID::HIDCore::available_controllers> keep_connected{};
// reserve existing AND valid players before filling slots. include Handheld, but not other // Disconnect Handheld first.
for (std::size_t index = 0; index < hid_core.available_controllers - 1; ++index) {
const auto* controller = hid_core.GetEmulatedControllerByIndex(index);
if (!parameters.keep_controllers_connected || !controller->IsConnected() || num_selected_players >= max_supported_players) continue;
const auto style = controller->GetNpadStyleIndex();
keep_connected[index] =
(style == NpadStyleIndex::Fullkey && parameters.allow_pro_controller) ||
(style == NpadStyleIndex::JoyconDual && parameters.allow_dual_joycons) ||
(style == NpadStyleIndex::JoyconLeft && parameters.allow_left_joycon) ||
(style == NpadStyleIndex::JoyconRight && parameters.allow_right_joycon) ||
(style == NpadStyleIndex::Handheld && parameters.enable_single_mode && parameters.allow_handheld && !Settings::IsDockedMode()) ||
(style == NpadStyleIndex::GameCube && parameters.allow_gamecube_controller);
num_selected_players += keep_connected[index];
}
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (!keep_connected[hid_core.available_controllers - 2]) handheld->Disconnect(); handheld->Disconnect();
// Deduce the best configuration based on the input parameters. // Deduce the best configuration based on the input parameters.
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) { for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
auto* controller = hid_core.GetEmulatedControllerByIndex(index); auto* controller = hid_core.GetEmulatedControllerByIndex(index);
if (keep_connected[index]) continue; // First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
controller->Disconnect(); controller->Disconnect();
// only add players still needed to reach the minimum // Only connect the minimum number of required players.
if (num_selected_players >= min_supported_players) continue; if (index >= min_supported_players) {
++num_selected_players; continue;
}
// Connect controllers based on the following priority list from highest to lowest priority: // Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
-8
View File
@@ -465,10 +465,6 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) {
// Check if we need scheduling. If we do, then we can't complete the switch and should // Check if we need scheduling. If we do, then we can't complete the switch and should
// retry. // retry.
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) { if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
// Some libc++ lazily init mutex
[[maybe_unused]] auto const can_lock = highest_priority_thread->m_context_guard.try_lock();
DEBUG_ASSERT(!can_lock);
// Our switch failed. // Our switch failed.
// We should unlock the thread context, and then retry. // We should unlock the thread context, and then retry.
highest_priority_thread->m_context_guard.unlock(); highest_priority_thread->m_context_guard.unlock();
@@ -500,10 +496,6 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) {
// Check if the thread is terminated by checking the DPC flags. // Check if the thread is terminated by checking the DPC flags.
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) { if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
// Some libc++ lazily init mutex
[[maybe_unused]] auto const can_lock = thread->m_context_guard.try_lock();
DEBUG_ASSERT(!can_lock);
// The thread isn't terminated, so we want to unlock it. // The thread isn't terminated, so we want to unlock it.
thread->m_context_guard.unlock(); thread->m_context_guard.unlock();
} }
+4 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -12,6 +12,7 @@
#include "common/atomic_ops.h" #include "common/atomic_ops.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/spin_lock.h"
namespace Kernel { namespace Kernel {
@@ -29,7 +30,7 @@ public:
}; };
public: public:
KSlabHeapImpl() = default; constexpr KSlabHeapImpl() = default;
void Initialize() { void Initialize() {
ASSERT(m_head == nullptr); ASSERT(m_head == nullptr);
@@ -67,7 +68,7 @@ public:
private: private:
std::atomic<Node*> m_head{}; std::atomic<Node*> m_head{};
std::mutex m_lock; Common::SpinLock m_lock;
}; };
} // namespace impl } // namespace impl
+2 -1
View File
@@ -19,6 +19,7 @@
#include "common/intrusive_red_black_tree.h" #include "common/intrusive_red_black_tree.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/spin_lock.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h" #include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_light_lock.h"
@@ -919,7 +920,7 @@ private:
bool m_resource_limit_release_hint{}; bool m_resource_limit_release_hint{};
bool m_is_kernel_address_key{}; bool m_is_kernel_address_key{};
StackParameters m_stack_parameters{}; StackParameters m_stack_parameters{};
std::mutex m_context_guard{}; Common::SpinLock m_context_guard{};
// For emulation // For emulation
std::shared_ptr<Common::Fiber> m_host_context{}; std::shared_ptr<Common::Fiber> m_host_context{};
@@ -23,12 +23,6 @@ constexpr std::size_t profile_username_size{32};
using ProfileUsername = std::array<u8, profile_username_size>; using ProfileUsername = std::array<u8, profile_username_size>;
using UserIDArray = std::array<Common::UUID, MAX_USERS>; using UserIDArray = std::array<Common::UUID, MAX_USERS>;
// This is nn::account::Uid
struct Uid {
std::array<u8, 0x10> unk0;
};
static_assert(sizeof(Uid) == 0x10);
/// Contains extra data related to a user. /// Contains extra data related to a user.
/// TODO: RE this structure /// TODO: RE this structure
struct UserData { struct UserData {
@@ -347,22 +347,21 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) { Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
LOG_WARNING(Service_AM, "(stubbed)"); LOG_WARNING(Service_AM, "(stubbed)");
R_UNLESS(out_pseudo_device_id != nullptr, ResultUnknown); R_UNLESS(out_pseudo_device_id, ResultUnknown);
// This should be hashed with the device specific hash // This should be hashed with the device specific hash
// for now this will do // for now this will do
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id); const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
R_UNLESS(res.first != nullptr, ResultUnknown); u8 hash[EVP_MAX_MD_SIZE];
std::array<u8, EVP_MAX_MD_SIZE> hash;
unsigned int hash_len = 0; unsigned int hash_len = 0;
auto const seed = res.first->raw.seed_for_pseudo_device_id; auto const seed = res.first->raw.seed_for_pseudo_device_id;
EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_MD_CTX *ctx = EVP_MD_CTX_new();
auto const algorithm = EVP_sha1(); auto const algorithm = EVP_sha1();
EVP_DigestInit_ex(ctx, algorithm, nullptr); EVP_DigestInit_ex(ctx, algorithm, nullptr);
EVP_DigestUpdate(ctx, &seed, sizeof(seed)); EVP_DigestUpdate(ctx, &seed, sizeof(seed));
EVP_DigestFinal_ex(ctx, hash.data(), &hash_len); EVP_DigestFinal_ex(ctx, hash, &hash_len);
EVP_MD_CTX_free(ctx); EVP_MD_CTX_free(ctx);
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 16>{hash.begin(), hash.begin() + 16}); *out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
R_SUCCEED(); R_SUCCEED();
} }
@@ -154,9 +154,6 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{720, nullptr, "AbandonAccessFailure"}, {720, nullptr, "AbandonAccessFailure"},
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
{810, nullptr, "RegisterProgramIndexMapInfo"}, {810, nullptr, "RegisterProgramIndexMapInfo"},
{820, nullptr, "GetContentStorageInfoIndex"},
{830, nullptr, "EncryptStreamPlaySaveData"},
{831, nullptr, "DecryptStreamPlaySaveData"},
{1000, nullptr, "SetBisRootForHost"}, {1000, nullptr, "SetBisRootForHost"},
{1001, nullptr, "SetSaveDataSize"}, {1001, nullptr, "SetSaveDataSize"},
{1002, nullptr, "SetSaveDataRootPath"}, {1002, nullptr, "SetSaveDataRootPath"},
-1
View File
@@ -22,7 +22,6 @@
namespace IPC { namespace IPC {
constexpr Result ResultNotSupported{ErrorModule::HIPC, 1};
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301}; constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
struct ResponseBuilder { struct ResponseBuilder {
-117
View File
@@ -6,16 +6,10 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/result.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ngc/ngc.h" #include "core/hle/service/ngc/ngc.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "frontend_common/firmware_manager.h"
namespace Service::NGC { namespace Service::NGC {
@@ -172,123 +166,12 @@ public:
} }
}; };
struct SaveDataHandle {
u64 unk0;
};
static_assert(sizeof(SaveDataHandle) == 0x08);
class IUserShimScopedObject final : public ServiceFramework<IUserShimScopedObject> {
public:
explicit IUserShimScopedObject(Core::System& system_) : ServiceFramework(system_, "IUserShimScopedObject") {
// clang-format off
static const FunctionInfo functions[] = {
{450, nullptr, "InitializeForSaveData"},
{451, nullptr, "FinalizeForSaveData"},
{452, D<&IUserShimScopedObject::OpenSaveData>, "OpenSaveData"},
{453, nullptr, "CloseSaveData"},
{454, D<&IUserShimScopedObject::ReadSaveSlot>, "ReadSaveSlot"},
{455, D<&IUserShimScopedObject::WriteSaveSlot>, "WriteSaveSlot"},
{456, nullptr, "FlushSaveSlot"},
{457, nullptr, "CommitSaveData"},
};
// clang-format on
RegisterHandlers(functions);
}
Result OpenSaveData(Account::Uid unk0, Out<SaveDataHandle> unk1) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result ReadSaveSlot(s32 offset, SaveDataHandle handle, OutBuffer<BufferAttr_HipcAutoSelect> out_data, Out<u32> out_size) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result WriteSaveSlot(s32 offset, SaveDataHandle handle, InBuffer<BufferAttr_HipcAutoSelect> out_data) {
LOG_WARNING(Service_NGC, "stubbed");
// to implement
R_SUCCEED();
}
};
class IUserService final : public ServiceFramework<IUserService> {
public:
explicit IUserService(Core::System& system_) : ServiceFramework(system_, "stpl:u") {
// clang-format off
static const FunctionInfo functions[] = {
{0 , D<&IUserService::Cmd0>, "Cmd0"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd0(u32 unk0, OutInterface<IUserShimScopedObject> out_interface) {
LOG_WARNING(Service_NGC, "stubbed");
*out_interface = std::make_shared<IUserShimScopedObject>(system);
R_SUCCEED();
}
};
class ISystemShimScopedObject final : public ServiceFramework<ISystemShimScopedObject> {
public:
explicit ISystemShimScopedObject(Core::System& system_) : ServiceFramework(system_, "ISystemShimScopedObject") {
// clang-format off
static const FunctionInfo functions[] = {
{106, nullptr, "Cmd106"},
{107, nullptr, "Cmd107"},
{108, D<&ISystemShimScopedObject::Cmd108>, "Cmd108"},
{207, nullptr, "Cmd207"},
{208, D<&ISystemShimScopedObject::Cmd208>, "Cmd208"},
{209, nullptr, "Cmd209"},
{210, nullptr, "Cmd210"},
{211, nullptr, "Cmd211"},
{212, nullptr, "Cmd212"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd108() {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result Cmd208(Out<std::array<u8, 0x20>> unk0) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
};
class ISystemService final : public ServiceFramework<ISystemService> {
public:
explicit ISystemService(Core::System& system_) : ServiceFramework(system_, "stpl:sys") {
// clang-format off
static const FunctionInfo functions[] = {
{0 , D<&ISystemService::Cmd0>, "Cmd0"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd0(OutInterface<ISystemShimScopedObject> out_interface) {
LOG_WARNING(Service_NGC, "stubbed");
*out_interface = std::make_shared<ISystemShimScopedObject>(system);
R_SUCCEED();
}
};
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4); server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4); server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4); server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4);
// +23.0.0
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 23) {
server_manager->RegisterNamedService("stpl:u", std::make_shared<IUserService>(system), 4);
server_manager->RegisterNamedService("stpl:sys", std::make_shared<ISystemService>(system), 4);
}
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
@@ -7,7 +7,7 @@
#pragma once #pragma once
#include "core/hle/service/cmif_types.h" #include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/async_result.h" #include "core/hle/service/ns/i_async_result.h"
#include "core/hle/service/ns/language.h" #include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_types.h" #include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/os/event.h" #include "core/hle/service/os/event.h"
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/async_result.h" #include "core/hle/service/ns/i_async_result.h"
#include <cstring> #include <cstring>
@@ -32,4 +32,4 @@ Result IAsyncResult::Cancel() {
R_SUCCEED(); R_SUCCEED();
} }
} // namespace Service::NS } // namespace Service::NS
@@ -311,30 +311,27 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
// u64 - app count // u64 - app count
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64)); memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
out_length += sizeof(u64); out_length += sizeof(u64);
ASSERT(out_length <= t_mem->GetSize());
// [list of u64] - size of icons // [list of u64] - size of icons
for (size_t i = 0; i < app_count; ++i) { for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i]; const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()}; const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
if (const auto control = pm.GetControlMetadata(); control.second) { const auto control = pm.GetControlMetadata();
u64 full_size = control.second->GetSize(); u64 full_size = control.second->GetSize();
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64)); memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
}
out_length += sizeof(u64); out_length += sizeof(u64);
ASSERT(out_length <= t_mem->GetSize());
} }
// [list of raw icon data] // [list of raw icon data]
std::vector<u8> full_icon_data;
for (size_t i = 0; i < app_count; ++i) { for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i]; const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()}; const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
if (const auto control = pm.GetControlMetadata(); control.second) { const auto control = pm.GetControlMetadata();
if (auto const full_size = control.second->GetSize(); full_size > 0) { auto const full_size = control.second->GetSize();
std::vector<u8> full_icon_data(full_size); if (full_size > 0) {
control.second->Read(full_icon_data.data(), full_size, 0); full_icon_data.resize(full_size);
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size); control.second->Read(full_icon_data.data(), full_size, 0);
out_length += full_size; memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
ASSERT(out_length <= t_mem->GetSize()); out_length += full_size;
}
} }
} }
} }
@@ -348,12 +345,6 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) { void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
const auto app_ids_buffer = ctx.ReadBuffer(); const auto app_ids_buffer = ctx.ReadBuffer();
const size_t app_count = app_ids_buffer.size() / sizeof(u64); const size_t app_count = app_ids_buffer.size() / sizeof(u64);
std::vector<u64> application_ids(app_count);
if (app_count > 0) {
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
}
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0)); auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
auto* t_mem = t_mem_obj.GetPointerUnsafe(); auto* t_mem = t_mem_obj.GetPointerUnsafe();
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry); constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
@@ -363,9 +354,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
auto& memory = system.ApplicationMemory(); auto& memory = system.ApplicationMemory();
const auto t_mem_address = t_mem->GetSourceAddress(); const auto t_mem_address = t_mem->GetSourceAddress();
for (size_t i = 0; i < app_count; ++i) { for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = application_ids[i]; const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
system.GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
FileSys::LanguageEntry entry{}; FileSys::LanguageEntry entry{};
if (control.first != nullptr) { if (control.first != nullptr) {
+234 -208
View File
@@ -48,12 +48,6 @@ enum class OptionType : u32 {
EnableAlpn = 3, EnableAlpn = 3,
}; };
// This is nn::ssl::sf::RenegotiationMode
enum RenegotiationMode : u32 {
None = 0, ///< None
Secure = 1, ///< Secure
};
// This is nn::ssl::sf::SslVersion // This is nn::ssl::sf::SslVersion
struct SslVersion { struct SslVersion {
union { union {
@@ -81,34 +75,34 @@ public:
shared_data{shared_data_in}, backend{std::move(backend_in)} { shared_data{shared_data_in}, backend{std::move(backend_in)} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&ISslConnection::SetSocketDescriptor>, "SetSocketDescriptor"}, {0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"},
{1, D<&ISslConnection::SetHostName>, "SetHostName"}, {1, &ISslConnection::SetHostName, "SetHostName"},
{2, D<&ISslConnection::SetVerifyOption>, "SetVerifyOption"}, {2, &ISslConnection::SetVerifyOption, "SetVerifyOption"},
{3, D<&ISslConnection::SetIoMode>, "SetIoMode"}, {3, &ISslConnection::SetIoMode, "SetIoMode"},
{4, D<&ISslConnection::GetSocketDescriptor>, "GetSocketDescriptor"}, {4, nullptr, "GetSocketDescriptor"},
{5, D<&ISslConnection::GetHostName>, "GetHostName"}, {5, nullptr, "GetHostName"},
{6, nullptr, "GetVerifyOption"}, {6, nullptr, "GetVerifyOption"},
{7, D<&ISslConnection::GetIoMode>, "GetIoMode"}, {7, nullptr, "GetIoMode"},
{8, D<&ISslConnection::DoHandshake>, "DoHandshake"}, {8, &ISslConnection::DoHandshake, "DoHandshake"},
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"}, {9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
{10, D<&ISslConnection::Read>, "Read"}, {10, &ISslConnection::Read, "Read"},
{11, D<&ISslConnection::Write>, "Write"}, {11, &ISslConnection::Write, "Write"},
{12, D<&ISslConnection::Pending>, "Pending"}, {12, &ISslConnection::Pending, "Pending"},
{13, D<&ISslConnection::Peek>, "Peek"}, {13, nullptr, "Peek"},
{14, D<&ISslConnection::Poll>, "Poll"}, {14, nullptr, "Poll"},
{15, D<&ISslConnection::GetVerifyCertError>, "GetVerifyCertError"}, {15, nullptr, "GetVerifyCertError"},
{16, D<&ISslConnection::GetNeededServerCertBufferSize>, "GetNeededServerCertBufferSize"}, {16, nullptr, "GetNeededServerCertBufferSize"},
{17, D<&ISslConnection::SetSessionCacheMode>, "SetSessionCacheMode"}, {17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"},
{18, D<&ISslConnection::GetSessionCacheMode>, "GetSessionCacheMode"}, {18, nullptr, "GetSessionCacheMode"},
{19, D<&ISslConnection::FlushSessionCache>, "FlushSessionCache"}, {19, nullptr, "FlushSessionCache"},
{20, D<&ISslConnection::SetRenegotiationMode>, "SetRenegotiationMode"}, {20, nullptr, "SetRenegotiationMode"},
{21, D<&ISslConnection::GetRenegotiationMode>, "GetRenegotiationMode"}, {21, nullptr, "GetRenegotiationMode"},
{22, D<&ISslConnection::SetOption>, "SetOption"}, {22, &ISslConnection::SetOption, "SetOption"},
{23, D<&ISslConnection::GetOption>, "GetOption"}, {23, &ISslConnection::GetOption, "GetOption"},
{24, nullptr, "GetVerifyCertErrors"}, {24, nullptr, "GetVerifyCertErrors"},
{25, nullptr, "GetCipherInfo"}, {25, nullptr, "GetCipherInfo"},
{26, D<&ISslConnection::SetNextAlpnProto>, "SetNextAlpnProto"}, {26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"},
{27, D<&ISslConnection::GetNextAlpnProto>, "GetNextAlpnProto"}, {27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"},
{28, nullptr, "SetDtlsSocketDescriptor"}, {28, nullptr, "SetDtlsSocketDescriptor"},
{29, nullptr, "GetDtlsHandshakeTimeout"}, {29, nullptr, "GetDtlsHandshakeTimeout"},
{30, nullptr, "SetPrivateOption"}, {30, nullptr, "SetPrivateOption"},
@@ -147,6 +141,80 @@ public:
} }
private: private:
SslVersion ssl_version;
std::shared_ptr<SslContextSharedData> shared_data;
std::unique_ptr<SSLConnectionBackend> backend;
std::optional<int> fd_to_close;
bool do_not_close_socket = false;
bool get_server_cert_chain = false;
bool skip_default_verify = false;
bool enable_alpn = false;
std::shared_ptr<Network::SocketBase> socket;
std::vector<u8> next_alpn_proto;
bool did_handshake = false;
u32 verify_option = 0;
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
LOG_DEBUG(Service_SSL, "called, fd={}", fd);
ASSERT(!did_handshake);
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
auto const res_v = bsd->DuplicateSocketImpl(fd);
if (auto *res = std::get_if<s32>(&res_v)) {
const s32 duplicated_fd = *res;
if (do_not_close_socket) {
*out_fd = duplicated_fd;
} else {
*out_fd = -1;
fd_to_close = duplicated_fd;
}
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(duplicated_fd);
if (!sock.has_value()) {
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd);
return ResultInvalidSocket;
}
socket = std::move(*sock);
backend->SetSocket(socket);
return ResultSuccess;
}
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
return ResultInvalidSocket;
}
Result SetHostNameImpl(const std::string& hostname) {
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
ASSERT(!did_handshake);
return backend->SetHostName(hostname);
}
Result SetVerifyOptionImpl(u32 option) {
ASSERT(!did_handshake);
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
verify_option = 0;
backend->SetVerifyOption(0);
return ResultSuccess;
}
Result SetIoModeImpl(u32 input_mode) {
auto mode = static_cast<IoMode>(input_mode);
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
ASSERT_OR_EXECUTE(socket, { return ResultNoSocket; });
const bool non_block = mode == IoMode::NonBlocking;
const Network::Errno error = socket->SetNonBlock(non_block);
if (error != Network::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
}
return ResultSuccess;
}
Result SetSessionCacheModeImpl(u32 mode) {
ASSERT(!did_handshake);
LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
return ResultSuccess;
}
Result DoHandshakeImpl() { Result DoHandshakeImpl() {
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; }); ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
Result res = backend->DoHandshake(); Result res = backend->DoHandshake();
@@ -166,17 +234,19 @@ private:
}; };
if (!get_server_cert_chain) { if (!get_server_cert_chain) {
// Just return the first one, unencoded. // Just return the first one, unencoded.
ASSERT_OR_EXECUTE_MSG(!certs.empty(), { return {}; }, "Should be at least one server cert"); ASSERT_OR_EXECUTE_MSG(
!certs.empty(), { return {}; }, "Should be at least one server cert");
return certs[0]; return certs[0];
} }
std::vector<u8> ret; std::vector<u8> ret;
Header header{0x4E4D684374726543, u32(certs.size()), 0}; Header header{0x4E4D684374726543, static_cast<u32>(certs.size()), 0};
ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1)); ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1));
size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader); size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
for (auto& cert : certs) { for (auto& cert : certs) {
EntryHeader entry_header{u32(cert.size()), u32(data_offset)}; EntryHeader entry_header{static_cast<u32>(cert.size()), static_cast<u32>(data_offset)};
data_offset += cert.size(); data_offset += cert.size();
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), reinterpret_cast<u8*>(&entry_header + 1)); ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header),
reinterpret_cast<u8*>(&entry_header + 1));
} }
for (auto& cert : certs) { for (auto& cert : certs) {
ret.insert(ret.end(), cert.begin(), cert.end()); ret.insert(ret.end(), cert.begin(), cert.end());
@@ -184,77 +254,65 @@ private:
return ret; return ret;
} }
Result SetSocketDescriptor(s32 in_fd, Out<s32> out_fd) { Result ReadImpl(std::vector<u8>* out_data) {
LOG_DEBUG(Service_SSL, "called, fd={}", in_fd); ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
ASSERT(!did_handshake); size_t actual_size{};
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u"); Result res = backend->Read(&actual_size, *out_data);
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; }); if (res != ResultSuccess) {
return res;
auto const res_v = bsd->DuplicateSocketImpl(in_fd);
if (auto *res = std::get_if<s32>(&res_v)) {
const s32 dup_fd = *res;
*out_fd = do_not_close_socket ? dup_fd : -1;
if (!do_not_close_socket)
fd_to_close = dup_fd;
auto const sock = bsd->GetSocket(dup_fd);
if (!sock.has_value()) {
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", dup_fd);
return ResultInvalidSocket;
}
socket = std::move(*sock);
backend->SetSocket(std::move(socket));
return ResultSuccess;
} }
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", in_fd); out_data->resize(actual_size);
return ResultInvalidSocket; return res;
} }
Result SetHostName(InBuffer<BufferAttr_HipcMapAlias> buf) { Result WriteImpl(size_t* out_size, std::span<const u8> data) {
auto const hostname = Common::StringFromBuffer(buf); ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname); return backend->Write(out_size, data);
ASSERT(!did_handshake);
return backend->SetHostName(hostname.c_str());
} }
Result SetVerifyOption(u32 option) { Result PendingImpl(s32* out_pending) {
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option); LOG_WARNING(Service_SSL, "(STUBBED) called.");
ASSERT(!did_handshake); *out_pending = 0;
verify_option = 0; return ResultSuccess;
backend->SetVerifyOption(0);
R_SUCCEED();
} }
Result SetIoMode(u32 input_mode) { void SetSocketDescriptor(HLERequestContext& ctx) {
auto mode = IoMode(input_mode); IPC::RequestParser rp{ctx};
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking); const s32 in_fd = rp.Pop<s32>();
R_UNLESS(socket, ResultNoSocket); s32 out_fd{-1};
const bool non_block = mode == IoMode::NonBlocking; const Result res = SetSocketDescriptorImpl(&out_fd, in_fd);
const Network::Errno error = socket->SetNonBlock(non_block); IPC::ResponseBuilder rb{ctx, 3};
if (error != Network::Errno::SUCCESS) { rb.Push(res);
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block); rb.Push<s32>(out_fd);
}
R_SUCCEED();
} }
Result GetSocketDescriptor(Out<u32> out_fd) { void SetHostName(HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED)"); const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer());
*out_fd = uint32_t(socket->GetFD()); const Result res = SetHostNameImpl(hostname);
R_SUCCEED(); IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
} }
Result GetHostName(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) { void SetVerifyOption(HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED)"); IPC::RequestParser rp{ctx};
ASSERT(!did_handshake); const u32 option = rp.Pop<u32>();
return backend->GetHostName(std::span<u8>{data.begin(), data.end()}, out_size); const Result res = SetVerifyOptionImpl(option);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
} }
Result GetIoMode(Out<u32> out_mode) { void SetIoMode(HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED)"); IPC::RequestParser rp{ctx};
R_SUCCEED(); const u32 mode = rp.Pop<u32>();
const Result res = SetIoModeImpl(mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
} }
Result DoHandshake() { void DoHandshake(HLERequestContext& ctx) {
return DoHandshakeImpl(); const Result res = DoHandshakeImpl();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
} }
void DoHandshakeGetServerCert(HLERequestContext& ctx) { void DoHandshakeGetServerCert(HLERequestContext& ctx) {
@@ -293,158 +351,131 @@ private:
rb.PushRaw(out); rb.PushRaw(out);
} }
Result Read(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) { void Read(HLERequestContext& ctx) {
R_UNLESS(did_handshake, ResultInternalError); std::vector<u8> output_bytes(ctx.GetWriteBufferSize());
size_t tmp{}; const Result res = ReadImpl(&output_bytes);
auto const res = backend->Read(&tmp, data); IPC::ResponseBuilder rb{ctx, 3};
*out_size = u32(tmp); rb.Push(res);
return res; if (res == ResultSuccess) {
rb.Push(static_cast<u32>(output_bytes.size()));
ctx.WriteBuffer(output_bytes);
} else {
rb.Push(static_cast<u32>(0));
}
} }
Result Write(InBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) { void Write(HLERequestContext& ctx) {
R_UNLESS(did_handshake, ResultInternalError); size_t write_size{0};
size_t tmp{}; const Result res = WriteImpl(&write_size, ctx.ReadBuffer());
auto const res = backend->Write(&tmp, data); IPC::ResponseBuilder rb{ctx, 3};
*out_size = u32(tmp); rb.Push(res);
return res; rb.Push(static_cast<u32>(write_size));
} }
Result Pending(Out<s32> out_pending_size) { void Pending(HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED)"); s32 pending_size{0};
*out_pending_size = s32(backend->Pending()); const Result res = PendingImpl(&pending_size);
R_SUCCEED(); IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push<s32>(pending_size);
} }
Result Peek(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) { void SetSessionCacheMode(HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED)"); IPC::RequestParser rp{ctx};
size_t tmp{}; const u32 mode = rp.Pop<u32>();
auto const res = backend->Peek(&tmp, data); const Result res = SetSessionCacheModeImpl(mode);
*out_size = u32(tmp); IPC::ResponseBuilder rb{ctx, 2};
return res; rb.Push(res);
} }
Result Poll(u32 in_pollevent, u32 timer, Out<u32> out_pollevent) { void SetOption(HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED)"); struct Parameters {
R_SUCCEED(); OptionType option;
} s32 value;
};
static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size");
Result GetVerifyCertError() { IPC::RequestParser rp{ctx};
LOG_WARNING(Service_SSL, "(STUBBED)"); const auto parameters = rp.PopRaw<Parameters>();
R_SUCCEED();
}
Result GetNeededServerCertBufferSize(Out<u32> out_needed_buffer_size) { switch (parameters.option) {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_SUCCEED();
}
Result SetSessionCacheMode(u32 mode) {
LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
R_UNLESS(!did_handshake, ResultInternalError);
R_SUCCEED();
}
Result GetSessionCacheMode(Out<u32> mode) {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_UNLESS(!did_handshake, ResultInternalError);
R_SUCCEED();
}
Result FlushSessionCache() {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_UNLESS(!did_handshake, ResultInternalError);
R_SUCCEED();
}
Result SetRenegotiationMode(RenegotiationMode mode) {
LOG_WARNING(Service_SSL, "(STUBBED)");
backend->SetRenegotiationMode(u32(mode));
R_SUCCEED();
}
Result GetRenegotiationMode(Out<RenegotiationMode> mode) {
LOG_WARNING(Service_SSL, "(STUBBED)");
u32 tmp{};
auto const res = backend->GetRenegotiationMode(&tmp);
*mode = RenegotiationMode(tmp);
return res;
}
Result SetOption(OptionType option, s32 value) {
switch (option) {
case OptionType::DoNotCloseSocket: case OptionType::DoNotCloseSocket:
do_not_close_socket = bool(value); do_not_close_socket = static_cast<bool>(parameters.value);
break; break;
case OptionType::GetServerCertChain: case OptionType::GetServerCertChain:
get_server_cert_chain = bool(value); get_server_cert_chain = static_cast<bool>(parameters.value);
break; break;
case OptionType::SkipDefaultVerify: case OptionType::SkipDefaultVerify:
skip_default_verify = bool(value); skip_default_verify = static_cast<bool>(parameters.value);
break; break;
case OptionType::EnableAlpn: case OptionType::EnableAlpn:
enable_alpn = bool(value); enable_alpn = static_cast<bool>(parameters.value);
break; break;
default: default:
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", option, value); LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
parameters.value);
} }
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result GetOption(OptionType option, Out<u8> value) { void GetOption(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto option = rp.PopRaw<OptionType>();
u8 value = 0;
switch (option) { switch (option) {
case OptionType::DoNotCloseSocket: case OptionType::DoNotCloseSocket:
*value = u8(do_not_close_socket); value = static_cast<u8>(do_not_close_socket);
break; break;
case OptionType::GetServerCertChain: case OptionType::GetServerCertChain:
*value = u8(get_server_cert_chain); value = static_cast<u8>(get_server_cert_chain);
break; break;
case OptionType::SkipDefaultVerify: case OptionType::SkipDefaultVerify:
*value = u8(skip_default_verify); value = static_cast<u8>(skip_default_verify);
break; break;
case OptionType::EnableAlpn: case OptionType::EnableAlpn:
*value = u8(enable_alpn); value = static_cast<u8>(enable_alpn);
break; break;
default: default:
LOG_WARNING(Service_SSL, "Unknown option={}", u32(option)); LOG_WARNING(Service_SSL, "Unknown option={}", option);
*value = 0; value = 0;
break; break;
} }
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", u32(option), *value);
R_SUCCEED(); LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(value);
} }
Result SetNextAlpnProto(InBuffer<BufferAttr_HipcMapAlias> data) { void SetNextAlpnProto(HLERequestContext& ctx) {
auto const to_write = u32((std::min)(next_alpn_proto.size(), data.size())); const auto data = ctx.ReadBuffer(0);
next_alpn_proto.assign(data.begin(), data.begin() + to_write); next_alpn_proto.assign(data.begin(), data.end());
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size()); LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result GetNextAlpnProto(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> to_write) { void GetNextAlpnProto(HLERequestContext& ctx) {
*to_write = u32((std::min)(next_alpn_proto.size(), data.size())); const size_t writable = ctx.GetWriteBufferSize();
next_alpn_proto.assign(data.begin(), data.begin() + *to_write); const size_t to_write = (std::min)(next_alpn_proto.size(), writable);
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", *to_write);
R_SUCCEED(); if (to_write != 0) {
ctx.WriteBuffer(std::span<const u8>(next_alpn_proto.data(), to_write));
}
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", to_write);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(to_write));
} }
Result GetVerifyCertErrors(OutBuffer<BufferAttr_HipcMapAlias> unk0, Out<u32> unk1, Out<u32> unk2) {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_SUCCEED();
}
SslVersion ssl_version;
std::shared_ptr<SslContextSharedData> shared_data;
std::unique_ptr<SSLConnectionBackend> backend;
std::optional<int> fd_to_close;
std::shared_ptr<Network::SocketBase> socket;
std::vector<u8> next_alpn_proto;
u32 verify_option = 0;
bool do_not_close_socket = false;
bool get_server_cert_chain = false;
bool skip_default_verify = false;
bool enable_alpn = false;
bool did_handshake = false;
}; };
class ISslContext final : public ServiceFramework<ISslContext> { class ISslContext final : public ServiceFramework<ISslContext> {
@@ -461,7 +492,7 @@ public:
{5, &ISslContext::ImportClientPki, "ImportClientPki"}, {5, &ISslContext::ImportClientPki, "ImportClientPki"},
{6, nullptr, "RemoveServerPki"}, {6, nullptr, "RemoveServerPki"},
{7, nullptr, "RemoveClientPki"}, {7, nullptr, "RemoveClientPki"},
{8, D<&ISslContext::RegisterInternalPki>, "RegisterInternalPki"}, {8, nullptr, "RegisterInternalPki"},
{9, nullptr, "AddPolicyOid"}, {9, nullptr, "AddPolicyOid"},
{10, nullptr, "ImportCrl"}, {10, nullptr, "ImportCrl"},
{11, nullptr, "RemoveCrl"}, {11, nullptr, "RemoveCrl"},
@@ -556,11 +587,6 @@ private:
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(client_id); rb.Push(client_id);
} }
Result RegisterInternalPki() {
LOG_WARNING(Service_SSL, "(STUBBED) called");
R_SUCCEED();
}
}; };
class ISslService final : public ServiceFramework<ISslService> { class ISslService final : public ServiceFramework<ISslService> {
+1 -6
View File
@@ -36,17 +36,12 @@ class SSLConnectionBackend {
public: public:
virtual ~SSLConnectionBackend() {} virtual ~SSLConnectionBackend() {}
virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0; virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0;
virtual Result SetHostName(const std::string& hostname) = 0;
virtual void SetVerifyOption(u32 option) = 0; virtual void SetVerifyOption(u32 option) = 0;
virtual Result DoHandshake() = 0; virtual Result DoHandshake() = 0;
virtual Result Read(size_t* out_size, std::span<u8> data) = 0; virtual Result Read(size_t* out_size, std::span<u8> data) = 0;
virtual Result Peek(size_t* out_size, std::span<u8> data) = 0;
virtual Result Write(size_t* out_size, std::span<const u8> data) = 0; virtual Result Write(size_t* out_size, std::span<const u8> data) = 0;
virtual Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) = 0; virtual Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) = 0;
virtual Result SetHostName(const char* hostname) = 0;
virtual Result GetHostName(std::span<u8> hostname, u32* out_size) = 0;
virtual int Pending() = 0;
virtual Result SetRenegotiationMode(u32 mode) = 0;
virtual Result GetRenegotiationMode(u32* mode) = 0;
}; };
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend); Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend);
@@ -157,30 +157,18 @@ public:
socket = std::move(socket_in); socket = std::move(socket_in);
} }
Result SetHostName(const char* hostname) override { Result SetHostName(const std::string& hostname) override {
if (!skip_cert_verification) { if (!skip_cert_verification) {
if (!SSL_set1_host(ssl, hostname)) { if (!SSL_set1_host(ssl, hostname.c_str())) {
LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname); LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname);
return CheckOpenSSLErrors(); return CheckOpenSSLErrors();
} }
} }
if (!SSL_set_tlsext_host_name(ssl, hostname)) { // hostname for SNI if (!SSL_set_tlsext_host_name(ssl, hostname.c_str())) { // hostname for SNI
LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname); LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname);
return CheckOpenSSLErrors(); return CheckOpenSSLErrors();
} }
R_SUCCEED(); return ResultSuccess;
}
Result GetHostName(std::span<u8> data, u32* out_size) override {
auto const peer_name = SSL_get0_peername(ssl);
if (peer_name == nullptr) {
LOG_ERROR(Service_SSL, "SSL_get0_peername()");
return CheckOpenSSLErrors();
}
auto const s = std::string{peer_name};
*out_size = u32(s.size());
std::memcpy(data.data(), s.data(), (std::min)(s.size(), data.size()));
R_SUCCEED();
} }
void SetVerifyOption(u32 option) override { void SetVerifyOption(u32 option) override {
@@ -225,13 +213,6 @@ public:
return HandleReturn("SSL_read_ex", out_size, ret); return HandleReturn("SSL_read_ex", out_size, ret);
} }
Result Peek(size_t* out_size, std::span<u8> data) override {
auto const n = (std::min)(data.size(), *out_size);
const int ret = SSL_peek(ssl, data.data(), int(n));
*out_size = n;
return HandleReturn("SSL_write_ex", out_size, ret);
}
Result Write(size_t* out_size, std::span<const u8> data) override { Result Write(size_t* out_size, std::span<const u8> data) override {
const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size); const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size);
return HandleReturn("SSL_write_ex", out_size, ret); return HandleReturn("SSL_write_ex", out_size, ret);
@@ -282,25 +263,7 @@ public:
out_certs->emplace_back(buf, buf + len); out_certs->emplace_back(buf, buf + len);
OPENSSL_free(buf); OPENSSL_free(buf);
} }
R_SUCCEED(); return ResultSuccess;
}
int Pending() override {
return SSL_pending(ssl);
}
Result SetRenegotiationMode(u32 mode) override {
if (mode == 0) {
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_RENEGOTIATION);
} else {
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALLOW_CLIENT_RENEGOTIATION);
}
R_SUCCEED();
}
Result GetRenegotiationMode(u32* mode) override {
*mode = SSL_get_secure_renegotiation_support(ssl) ? 1 : 0;
R_SUCCEED();
} }
~SSLConnectionBackendOpenSSL() { ~SSLConnectionBackendOpenSSL() {
@@ -90,7 +90,7 @@ template<>
code.shl(tmp, int(ctx.conf.page_table_log2_stride)); code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp.cvt64()]); code.mov(page, qword[r14 + tmp.cvt64()]);
} else { } else {
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]); code.mov(page, qword[r14 + tmp.cvt64() * int(ctx.conf.page_table_log2_stride)]);
} }
// check for marked bit, use as unmapped if marked // check for marked bit, use as unmapped if marked
@@ -161,12 +161,8 @@ template<>
code.jnz(abort, code.T_NEAR); code.jnz(abort, code.T_NEAR);
} }
if (ctx.conf.page_table_log2_stride > 3) { code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.shl(tmp, int(ctx.conf.page_table_log2_stride)); code.mov(page, qword[r14 + tmp]);
code.mov(page, qword[r14 + tmp.cvt64()]);
} else {
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
}
// check for marked bit, use as unmapped if marked // check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) { if (ctx.conf.page_table_marked_bit) {
@@ -182,7 +178,6 @@ template<>
code.mov(tmp, ctx.conf.page_table_pointer_mask); code.mov(tmp, ctx.conf.page_table_pointer_mask);
code.and_(page, tmp); code.and_(page, tmp);
} }
// check for sign bit, apply sign extension as needed
if (ctx.conf.page_table_sign_extension) { if (ctx.conf.page_table_sign_extension) {
code.shl(page, *ctx.conf.page_table_sign_extension); code.shl(page, *ctx.conf.page_table_sign_extension);
code.sar(page, *ctx.conf.page_table_sign_extension); code.sar(page, *ctx.conf.page_table_sign_extension);
+20 -19
View File
@@ -892,20 +892,28 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
count += shift; count += shift;
if (count >= 0) { if (count >= 0) {
const s32 offset = shift - count; auto const write_byte = [&](u8 byte) {
if (stream_pos == stream_data.size()) {
if (((low_value << (offset - 1)) >> 31) != 0) { stream_data.push_back(byte);
const s32 current_pos = static_cast<s32>(base_stream.GetPosition()); ++stream_pos;
base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos); } else {
while (PeekByte() == 0xff) { stream_data.insert(stream_data.begin() + stream_pos, byte);
base_stream.WriteByte(0);
base_stream.Seek(-2, Common::SeekOrigin::FromCurrentPos);
} }
base_stream.WriteByte(static_cast<u8>((PeekByte() + 1))); };
base_stream.Seek(current_pos, Common::SeekOrigin::SetOrigin);
const s32 offset = shift - count;
if (((low_value << (offset - 1)) >> 31) != 0) {
auto const current_pos = stream_pos;
--stream_pos;
while (stream_data[stream_pos] == 0xff) {
write_byte(0);
stream_pos -= 2;
}
write_byte(stream_data[stream_pos] + 1);
stream_pos = current_pos;
} }
base_stream.WriteByte(static_cast<u8>((low_value >> (24 - offset)))); write_byte(u8((low_value >> (24 - offset))));
++stream_pos;
low_value <<= offset; low_value <<= offset;
shift = count; shift = count;
@@ -923,13 +931,6 @@ void VpxRangeEncoder::End() {
} }
} }
u8 VpxRangeEncoder::PeekByte() {
const u8 value = base_stream.ReadByte();
base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos);
return value;
}
VpxBitStreamWriter::VpxBitStreamWriter() = default; VpxBitStreamWriter::VpxBitStreamWriter() = default;
VpxBitStreamWriter::~VpxBitStreamWriter() = default; VpxBitStreamWriter::~VpxBitStreamWriter() = default;
+6 -4
View File
@@ -12,7 +12,6 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/stream.h"
#include "video_core/host1x/codecs/decoder.h" #include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codec_types.h" #include "video_core/host1x/codec_types.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
@@ -52,16 +51,19 @@ public:
void End(); void End();
[[nodiscard]] std::vector<u8>& GetBuffer() { [[nodiscard]] std::vector<u8>& GetBuffer() {
return base_stream.GetBuffer(); return stream_data;
} }
[[nodiscard]] const std::vector<u8>& GetBuffer() const { [[nodiscard]] const std::vector<u8>& GetBuffer() const {
return base_stream.GetBuffer(); return stream_data;
} }
private: private:
u8 PeekByte(); u8 PeekByte();
Common::Stream base_stream{};
std::vector<u8> stream_data{};
size_t stream_pos;
u32 low_value{}; u32 low_value{};
u32 range{0xff}; u32 range{0xff};
s32 count{-24}; s32 count{-24};
@@ -2850,6 +2850,8 @@ Sampler::VariantKey Sampler::MakeKey(const ImageView& image_view, bool is_depth)
VariantKey key{}; VariantKey key{};
key.reduce_anisotropy = has_added_anisotropy && !image_view.SupportsAnisotropy(); key.reduce_anisotropy = has_added_anisotropy && !image_view.SupportsAnisotropy();
key.force_nearest = has_linear_filtering && IsPixelFormatInteger(image_view.format); key.force_nearest = has_linear_filtering && IsPixelFormatInteger(image_view.format);
key.drop_depth_comparison =
is_depth && has_depth_comparison && !image_view.SupportsDepthComparison();
key.drop_reduction = has_minmax_reduction && !image_view.SupportsMinmaxFilter(); key.drop_reduction = has_minmax_reduction && !image_view.SupportsMinmaxFilter();
key.drop_custom_border = has_custom_border_colors && image_view.RequiresBorderColorFormat(); key.drop_custom_border = has_custom_border_colors && image_view.RequiresBorderColorFormat();
key.srgb_border = has_srgb_border_color && IsPixelFormatSRGB(image_view.format); key.srgb_border = has_srgb_border_color && IsPixelFormatSRGB(image_view.format);
@@ -2927,6 +2929,9 @@ VkSampler Sampler::Emplace(VariantKey key) {
create_info.anisotropyEnable = static_cast<VkBool32>(default_anisotropy > 1.0f); create_info.anisotropyEnable = static_cast<VkBool32>(default_anisotropy > 1.0f);
create_info.maxAnisotropy = default_anisotropy; create_info.maxAnisotropy = default_anisotropy;
} }
if (key.drop_depth_comparison) {
create_info.compareEnable = VK_FALSE;
}
if (!custom_border) { if (!custom_border) {
create_info.borderColor = ConvertBorderColor(color); create_info.borderColor = ConvertBorderColor(color);
} }
@@ -536,6 +536,7 @@ private:
struct VariantKey { struct VariantKey {
bool reduce_anisotropy; bool reduce_anisotropy;
bool force_nearest; bool force_nearest;
bool drop_depth_comparison;
bool drop_reduction; bool drop_reduction;
bool drop_custom_border; bool drop_custom_border;
bool srgb_border; bool srgb_border;