Compare commits

...

13 Commits

Author SHA1 Message Date
lizzie ac86f96efa 2026-09-14 00:57:14
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-17 13:53:07 +02:00
lizzie 1203082a8f [msvc] fix build errors (#4451)
MSVC doesn't consider atomic<u64> to be trivially copyable on x86 (why?).
And other chary idiosyncrasies from constructing `std::optional<uint8_t>`

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4451
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 08:16:18 +02:00
lizzie 14235dc0d0 [ips] fix .pchtxt with >256 bytes on a single line (#4445)
It wasn't correct to assume pchtxt would've been less than 256 bytes per line.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4445
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 08:15:58 +02:00
lizzie 6374f7f51f Partial revert: [dynarmic] fix sign extension and optimize vaddr lookups for 64bit on x64 systems (#4446) (#4448)
Breaks Mario Kart 8 Deluxe.

#4446

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4448
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 05:52:36 +02:00
lizzie 73e004de6e [common/page_table] unmark non-constexpr functions (#4433)
`std::atomic` member functions don't translate unto `constexpr`.

Trivial change, fixes CI warning.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4433
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 05:51:23 +02:00
lizzie f7b8ade40e [hle] stpl:sys, stpl:u initial implementation (#4435)
These cmds are hardcoded to use 0x20b, also the vftable is the same for `nn::sf::hipc`.

Streamplay is probably not gonna be implemented fully, but some future updates *may* hang when querying for the service, thus registering it at once will avoid such issues.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4435
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 05:45:37 +02:00
lizzie 8c1194474e [hle/ssl] Implement Peek(),{Get,Set}RenegotiationMode,Pending,Poll and stub miscellany (#4428)
Mainly because of Pending() as it's used in some games. May as well implement the rest.

Skyline implemented ISslContext::RegisterInternalPki which is just a success stub (for now) for JJBA:ASB

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4428
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 05:43:59 +02:00
lizzie 90aafeedc1 [k_scheduler] fix for libc++ that lazily initializes mutexes (#4447)
`__m_` is nullptr when `unlock();` is called, so `try_lock();` will force materialization of the backing mutex.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4447
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-17 00:28:37 +02:00
Exverge 4fe5f62c38 [dynarmic] fix sign extension and optimize vaddr lookups for 64bit on x64 systems (#4446)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Same as #4415 + #4444 just ported to A64 because I amazingly forgot

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4446
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-16 22:47:16 +02:00
Feng Chen c3f1e6562b [common/fs] Preserve Windows roots during path sanitization (#4437)
Commit 5b4c29b123 made SanitizePath rebuild paths from split components after resolving traversal. That reconstruction retained only one leading separator, collapsing UNC paths and breaking network-backed directories.

Parse the path root separately from normal components. Preserve UNC prefixes, drive-absolute and drive-relative forms, and extended UNC/device namespaces while normalizing separators and dot components.

Treat the UNC server/share portion as part of the root so parent traversal cannot escape into another share. Also preserve root-only results such as / and C:\\ instead of stripping their final separator.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4437
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-16 22:12:01 +02:00
lizzie ac35358b3f [common] Replace Common::SpinLock with std::mutex (#4436)
`std::mutex` can be better than the spin-lock specially since we don't waste CPU cycles for no reason.

Test less stuttering/heat consumption.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4436
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-16 15:35:13 +02:00
lizzie 7bf95be2c2 [dynarmic] fix A32 crash due to wrong LEA encoding (#4444)
LEA 1<<n, not just n

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4444
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-16 09:29:12 +02:00
lizzie 4ce45b3b37 [hle] Fix crash on MK8D and SM3DW LDN (#4417)
Crash is due to SIGILL due to mismatched N and span<> ctor().
Also some astray nullptr references.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4417
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-16 08:10:29 +02:00
25 changed files with 553 additions and 360 deletions
+34
View File
@@ -0,0 +1,34 @@
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 2859ee1..766961f 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -1688,10 +1688,12 @@ static int setup_queue_families(AVHWDeviceContext *ctx, VkDeviceCreateInfo *cd)
hwctx->nb_qf = 0;
hwctx->queue_flags = 0;
+/* SD gamemode vkroots crash
#ifdef VK_KHR_internally_synchronized_queues
if (p->vkctx.extensions & FF_VK_EXT_INTERNAL_QUEUE_SYNC)
hwctx->queue_flags |= VK_DEVICE_QUEUE_CREATE_INTERNALLY_SYNCHRONIZED_BIT_KHR;
#endif
+*/
/* Pick each queue family to use. */
#define PICK_QF(type, vid_op) \
diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c
index b2e575c..5b3c9cf 100644
--- a/libavutil/vulkan.c
+++ b/libavutil/vulkan.c
@@ -574,10 +574,12 @@ int ff_vk_exec_pool_init(FFVulkanContext *s, AVVulkanDeviceQueueFamily *qf,
e->qf = qf->idx;
VkDeviceQueueInfo2 qinfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2,
+/* SD gamemode vkroots crash
#ifdef VK_KHR_internally_synchronized_queues
.flags = internal_queue_sync ?
VK_DEVICE_QUEUE_CREATE_INTERNALLY_SYNCHRONIZED_BIT_KHR : 0,
#endif
+*/
.queueFamilyIndex = qf->idx,
.queueIndex = e->qi,
};
+2 -2
View File
@@ -70,9 +70,9 @@
"version": "v1.3.18" "version": "v1.3.18"
}, },
"ffmpeg": { "ffmpeg": {
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a", "hash": "68f46abf0d5dc47ab95083d59e273131a8df7948b82e62db00f00fa416e1d0bd24b799671b5a60752209bb2ac0f75295055ed0085c89a98113323cde23b69710",
"repo": "FFmpeg/FFmpeg", "repo": "FFmpeg/FFmpeg",
"version": "c7b5f1537d" "version": "6efe500d2e"
}, },
"ffmpeg-ci": { "ffmpeg-ci": {
"ci": true, "ci": true,
-1
View File
@@ -110,7 +110,6 @@ 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
+66 -13
View File
@@ -5,6 +5,7 @@
// 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"
@@ -480,28 +481,80 @@ 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());
const bool absolute = !path.empty() && path[0] == type2; std::string root;
std::vector<std::string_view> parts; std::string_view components{path};
bool drive_relative = false;
for (const auto part : SplitPathComponents(path)) #ifdef _WIN32
{ const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
if (part.empty() || part == ".") const bool drive =
continue; path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
if (part == ".." && !parts.empty() && parts.back() != "..")
parts.pop_back(); if (network) {
else if (part != "..") parts.push_back(part); 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);
} }
std::string resolved = absolute ? std::string(1, type2) : std::string{}; const auto path_parts = SplitPathComponents(components);
for (std::size_t i = 0; i < parts.size(); ++i) std::size_t root_component_count = 0;
{ #ifdef _WIN32
if (i != 0) 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;
for (std::size_t i = 0; i < path_parts.size(); ++i) {
const auto part = path_parts[i];
if (i < root_component_count) {
parts.push_back(part);
} else if (part.empty() || part == ".") {
continue;
} else if (part == "..") {
if (parts.size() > root_component_count) {
parts.pop_back();
}
} else {
parts.push_back(part);
}
}
const std::size_t root_length = root.size();
std::string resolved = std::move(root);
for (std::size_t i = 0; i < parts.size(); ++i) {
if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative))
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));
} }
+3 -2
View File
@@ -347,8 +347,9 @@ 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);
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' // Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows // '.' and '..' components without traversing above the path root. Windows drive and UNC roots are
// 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
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept { inline 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}));
} }
constexpr void MarkRasterizerCached() noexcept { inline void MarkRasterizerCached() noexcept {
data_raw.fetch_or(0b111); data_raw.fetch_or(0b111);
} }
constexpr void MarkDebug(u64 ptr, u16 block) noexcept { inline void MarkDebug(u64 ptr, u16 block) noexcept {
Store(true, PageType::DebugMemory, block, ptr); Store(true, PageType::DebugMemory, block, ptr);
} }
+2 -1
View File
@@ -38,7 +38,8 @@ 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>
requires std::is_trivially_copyable_v<T> // MSVC doesn't regard structs with atomics as trivially copyable
// requires std::is_trivially_copyable_v<T>
class SparseLargeVector final { class SparseLargeVector final {
public: public:
constexpr SparseLargeVector() = default; constexpr SparseLargeVector() = default;
-50
View File
@@ -1,50 +0,0 @@
// 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
+2 -2
View File
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
return uuid; return uuid;
} }
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) { UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) {
UUID uuid{}; UUID uuid{};
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID)); std::memcpy(&uuid.uuid, sha1.data(), sha1.size());
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, 20> sha1); [[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> 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
@@ -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 = 0; config.page_table_marked_bit = uint8_t(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 = Common::PageTable::SIGN_BIT; config.page_table_sign_extension = std::uint8_t(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 = 0; config.page_table_marked_bit = uint8_t(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 = Common::PageTable::SIGN_BIT; config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
} }
} }
+18 -18
View File
@@ -100,8 +100,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
struct IPSwitchRecord { struct IPSwitchRecord {
std::array<uint8_t, 256 - sizeof(size_t)> data; std::vector<uint8_t> data;
size_t count;
}; };
struct IPSwitchCompiler::IPSwitchPatch { struct IPSwitchCompiler::IPSwitchPatch {
::Common::unordered_map<u32, IPSwitchRecord> records; ::Common::unordered_map<u32, IPSwitchRecord> records;
@@ -122,22 +121,23 @@ 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()) {
switch (it[1]) { r.data.push_back([it]() {
case 'a': r.data[r.count] = '\a'; break; switch (it[1]) {
case 'b': r.data[r.count] = '\b'; break; case 'a': return '\a';
case 'e': r.data[r.count] = '\e'; break; case 'b': return '\b';
case 'f': r.data[r.count] = '\f'; break; case 'e': return '\e';
case 'n': r.data[r.count] = '\n'; break; case 'f': return '\f';
case 'r': r.data[r.count] = '\r'; break; case 'n': return '\n';
case 't': r.data[r.count] = '\t'; break; case 'r': return '\r';
case 'v': r.data[r.count] = '\v'; break; case 't': return '\t';
case '?': r.data[r.count] = '\?'; break; case 'v': return '\v';
default: r.data[r.count] = it[1]; break; case '?': return '\?';
} default: return it[1];
++r.count; }
}());
it += 2; it += 2;
} else { } else {
++r.count; r.data.push_back(*it);
++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.count; auto replace_size = record.second.data.size();
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
View File
@@ -465,6 +465,10 @@ 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();
@@ -496,6 +500,10 @@ 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();
} }
+3 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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,7 +12,6 @@
#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 {
@@ -30,7 +29,7 @@ public:
}; };
public: public:
constexpr KSlabHeapImpl() = default; KSlabHeapImpl() = default;
void Initialize() { void Initialize() {
ASSERT(m_head == nullptr); ASSERT(m_head == nullptr);
@@ -68,7 +67,7 @@ public:
private: private:
std::atomic<Node*> m_head{}; std::atomic<Node*> m_head{};
Common::SpinLock m_lock; std::mutex m_lock;
}; };
} // namespace impl } // namespace impl
+1 -2
View File
@@ -19,7 +19,6 @@
#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"
@@ -920,7 +919,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{};
Common::SpinLock m_context_guard{}; std::mutex m_context_guard{};
// For emulation // For emulation
std::shared_ptr<Common::Fiber> m_host_context{}; std::shared_ptr<Common::Fiber> m_host_context{};
@@ -23,6 +23,12 @@ 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,21 +347,22 @@ 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, ResultUnknown); R_UNLESS(out_pseudo_device_id != nullptr, 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);
u8 hash[EVP_MAX_MD_SIZE]; R_UNLESS(res.first != nullptr, ResultUnknown);
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, &hash_len); EVP_DigestFinal_ex(ctx, hash.data(), &hash_len);
EVP_MD_CTX_free(ctx); EVP_MD_CTX_free(ctx);
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)}); *out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 16>{hash.begin(), hash.begin() + 16});
R_SUCCEED(); R_SUCCEED();
} }
@@ -154,6 +154,9 @@ 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,6 +22,7 @@
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,10 +6,16 @@
#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 {
@@ -166,12 +172,123 @@ 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));
} }
+218 -244
View File
@@ -48,6 +48,12 @@ 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 {
@@ -75,34 +81,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, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"}, {0, D<&ISslConnection::SetSocketDescriptor>, "SetSocketDescriptor"},
{1, &ISslConnection::SetHostName, "SetHostName"}, {1, D<&ISslConnection::SetHostName>, "SetHostName"},
{2, &ISslConnection::SetVerifyOption, "SetVerifyOption"}, {2, D<&ISslConnection::SetVerifyOption>, "SetVerifyOption"},
{3, &ISslConnection::SetIoMode, "SetIoMode"}, {3, D<&ISslConnection::SetIoMode>, "SetIoMode"},
{4, nullptr, "GetSocketDescriptor"}, {4, D<&ISslConnection::GetSocketDescriptor>, "GetSocketDescriptor"},
{5, nullptr, "GetHostName"}, {5, D<&ISslConnection::GetHostName>, "GetHostName"},
{6, nullptr, "GetVerifyOption"}, {6, nullptr, "GetVerifyOption"},
{7, nullptr, "GetIoMode"}, {7, D<&ISslConnection::GetIoMode>, "GetIoMode"},
{8, &ISslConnection::DoHandshake, "DoHandshake"}, {8, D<&ISslConnection::DoHandshake>, "DoHandshake"},
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"}, {9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
{10, &ISslConnection::Read, "Read"}, {10, D<&ISslConnection::Read>, "Read"},
{11, &ISslConnection::Write, "Write"}, {11, D<&ISslConnection::Write>, "Write"},
{12, &ISslConnection::Pending, "Pending"}, {12, D<&ISslConnection::Pending>, "Pending"},
{13, nullptr, "Peek"}, {13, D<&ISslConnection::Peek>, "Peek"},
{14, nullptr, "Poll"}, {14, D<&ISslConnection::Poll>, "Poll"},
{15, nullptr, "GetVerifyCertError"}, {15, D<&ISslConnection::GetVerifyCertError>, "GetVerifyCertError"},
{16, nullptr, "GetNeededServerCertBufferSize"}, {16, D<&ISslConnection::GetNeededServerCertBufferSize>, "GetNeededServerCertBufferSize"},
{17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"}, {17, D<&ISslConnection::SetSessionCacheMode>, "SetSessionCacheMode"},
{18, nullptr, "GetSessionCacheMode"}, {18, D<&ISslConnection::GetSessionCacheMode>, "GetSessionCacheMode"},
{19, nullptr, "FlushSessionCache"}, {19, D<&ISslConnection::FlushSessionCache>, "FlushSessionCache"},
{20, nullptr, "SetRenegotiationMode"}, {20, D<&ISslConnection::SetRenegotiationMode>, "SetRenegotiationMode"},
{21, nullptr, "GetRenegotiationMode"}, {21, D<&ISslConnection::GetRenegotiationMode>, "GetRenegotiationMode"},
{22, &ISslConnection::SetOption, "SetOption"}, {22, D<&ISslConnection::SetOption>, "SetOption"},
{23, &ISslConnection::GetOption, "GetOption"}, {23, D<&ISslConnection::GetOption>, "GetOption"},
{24, nullptr, "GetVerifyCertErrors"}, {24, nullptr, "GetVerifyCertErrors"},
{25, nullptr, "GetCipherInfo"}, {25, nullptr, "GetCipherInfo"},
{26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"}, {26, D<&ISslConnection::SetNextAlpnProto>, "SetNextAlpnProto"},
{27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"}, {27, D<&ISslConnection::GetNextAlpnProto>, "GetNextAlpnProto"},
{28, nullptr, "SetDtlsSocketDescriptor"}, {28, nullptr, "SetDtlsSocketDescriptor"},
{29, nullptr, "GetDtlsHandshakeTimeout"}, {29, nullptr, "GetDtlsHandshakeTimeout"},
{30, nullptr, "SetPrivateOption"}, {30, nullptr, "SetPrivateOption"},
@@ -141,80 +147,6 @@ 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();
@@ -234,19 +166,17 @@ 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( ASSERT_OR_EXECUTE_MSG(!certs.empty(), { return {}; }, "Should be at least one server cert");
!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, static_cast<u32>(certs.size()), 0}; Header header{0x4E4D684374726543, 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{static_cast<u32>(cert.size()), static_cast<u32>(data_offset)}; EntryHeader entry_header{u32(cert.size()), u32(data_offset)};
data_offset += cert.size(); data_offset += cert.size();
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), reinterpret_cast<u8*>(&entry_header + 1));
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());
@@ -254,65 +184,77 @@ private:
return ret; return ret;
} }
Result ReadImpl(std::vector<u8>* out_data) { Result SetSocketDescriptor(s32 in_fd, Out<s32> out_fd) {
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); LOG_DEBUG(Service_SSL, "called, fd={}", in_fd);
size_t actual_size{}; ASSERT(!did_handshake);
Result res = backend->Read(&actual_size, *out_data); auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
if (res != ResultSuccess) { ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
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;
} }
out_data->resize(actual_size); LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", in_fd);
return res; return ResultInvalidSocket;
} }
Result WriteImpl(size_t* out_size, std::span<const u8> data) { Result SetHostName(InBuffer<BufferAttr_HipcMapAlias> buf) {
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); auto const hostname = Common::StringFromBuffer(buf);
return backend->Write(out_size, data); LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
ASSERT(!did_handshake);
return backend->SetHostName(hostname.c_str());
} }
Result PendingImpl(s32* out_pending) { Result SetVerifyOption(u32 option) {
LOG_WARNING(Service_SSL, "(STUBBED) called."); LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
*out_pending = 0; ASSERT(!did_handshake);
return ResultSuccess; verify_option = 0;
backend->SetVerifyOption(0);
R_SUCCEED();
} }
void SetSocketDescriptor(HLERequestContext& ctx) { Result SetIoMode(u32 input_mode) {
IPC::RequestParser rp{ctx}; auto mode = IoMode(input_mode);
const s32 in_fd = rp.Pop<s32>(); ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
s32 out_fd{-1}; R_UNLESS(socket, ResultNoSocket);
const Result res = SetSocketDescriptorImpl(&out_fd, in_fd); const bool non_block = mode == IoMode::NonBlocking;
IPC::ResponseBuilder rb{ctx, 3}; const Network::Errno error = socket->SetNonBlock(non_block);
rb.Push(res); if (error != Network::Errno::SUCCESS) {
rb.Push<s32>(out_fd); LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
}
R_SUCCEED();
} }
void SetHostName(HLERequestContext& ctx) { Result GetSocketDescriptor(Out<u32> out_fd) {
const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer()); LOG_WARNING(Service_SSL, "(STUBBED)");
const Result res = SetHostNameImpl(hostname); *out_fd = uint32_t(socket->GetFD());
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(res);
} }
void SetVerifyOption(HLERequestContext& ctx) { Result GetHostName(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_SSL, "(STUBBED)");
const u32 option = rp.Pop<u32>(); ASSERT(!did_handshake);
const Result res = SetVerifyOptionImpl(option); return backend->GetHostName(std::span<u8>{data.begin(), data.end()}, out_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
} }
void SetIoMode(HLERequestContext& ctx) { Result GetIoMode(Out<u32> out_mode) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_SSL, "(STUBBED)");
const u32 mode = rp.Pop<u32>(); R_SUCCEED();
const Result res = SetIoModeImpl(mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
} }
void DoHandshake(HLERequestContext& ctx) { Result DoHandshake() {
const Result res = DoHandshakeImpl(); return DoHandshakeImpl();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
} }
void DoHandshakeGetServerCert(HLERequestContext& ctx) { void DoHandshakeGetServerCert(HLERequestContext& ctx) {
@@ -351,131 +293,158 @@ private:
rb.PushRaw(out); rb.PushRaw(out);
} }
void Read(HLERequestContext& ctx) { Result Read(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
std::vector<u8> output_bytes(ctx.GetWriteBufferSize()); R_UNLESS(did_handshake, ResultInternalError);
const Result res = ReadImpl(&output_bytes); size_t tmp{};
IPC::ResponseBuilder rb{ctx, 3}; auto const res = backend->Read(&tmp, data);
rb.Push(res); *out_size = u32(tmp);
if (res == ResultSuccess) { return res;
rb.Push(static_cast<u32>(output_bytes.size()));
ctx.WriteBuffer(output_bytes);
} else {
rb.Push(static_cast<u32>(0));
}
} }
void Write(HLERequestContext& ctx) { Result Write(InBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
size_t write_size{0}; R_UNLESS(did_handshake, ResultInternalError);
const Result res = WriteImpl(&write_size, ctx.ReadBuffer()); size_t tmp{};
IPC::ResponseBuilder rb{ctx, 3}; auto const res = backend->Write(&tmp, data);
rb.Push(res); *out_size = u32(tmp);
rb.Push(static_cast<u32>(write_size)); return res;
} }
void Pending(HLERequestContext& ctx) { Result Pending(Out<s32> out_pending_size) {
s32 pending_size{0}; LOG_WARNING(Service_SSL, "(STUBBED)");
const Result res = PendingImpl(&pending_size); *out_pending_size = s32(backend->Pending());
IPC::ResponseBuilder rb{ctx, 3}; R_SUCCEED();
rb.Push(res);
rb.Push<s32>(pending_size);
} }
void SetSessionCacheMode(HLERequestContext& ctx) { Result Peek(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_SSL, "(STUBBED)");
const u32 mode = rp.Pop<u32>(); size_t tmp{};
const Result res = SetSessionCacheModeImpl(mode); auto const res = backend->Peek(&tmp, data);
IPC::ResponseBuilder rb{ctx, 2}; *out_size = u32(tmp);
rb.Push(res); return res;
} }
void SetOption(HLERequestContext& ctx) { Result Poll(u32 in_pollevent, u32 timer, Out<u32> out_pollevent) {
struct Parameters { LOG_WARNING(Service_SSL, "(STUBBED)");
OptionType option; R_SUCCEED();
s32 value;
};
static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size");
IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
switch (parameters.option) {
case OptionType::DoNotCloseSocket:
do_not_close_socket = static_cast<bool>(parameters.value);
break;
case OptionType::GetServerCertChain:
get_server_cert_chain = static_cast<bool>(parameters.value);
break;
case OptionType::SkipDefaultVerify:
skip_default_verify = static_cast<bool>(parameters.value);
break;
case OptionType::EnableAlpn:
enable_alpn = static_cast<bool>(parameters.value);
break;
default:
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
parameters.value);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void GetOption(HLERequestContext& ctx) { Result GetVerifyCertError() {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_SSL, "(STUBBED)");
const auto option = rp.PopRaw<OptionType>(); R_SUCCEED();
}
u8 value = 0; Result GetNeededServerCertBufferSize(Out<u32> out_needed_buffer_size) {
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) { switch (option) {
case OptionType::DoNotCloseSocket: case OptionType::DoNotCloseSocket:
value = static_cast<u8>(do_not_close_socket); do_not_close_socket = bool(value);
break; break;
case OptionType::GetServerCertChain: case OptionType::GetServerCertChain:
value = static_cast<u8>(get_server_cert_chain); get_server_cert_chain = bool(value);
break; break;
case OptionType::SkipDefaultVerify: case OptionType::SkipDefaultVerify:
value = static_cast<u8>(skip_default_verify); skip_default_verify = bool(value);
break; break;
case OptionType::EnableAlpn: case OptionType::EnableAlpn:
value = static_cast<u8>(enable_alpn); enable_alpn = bool(value);
break; break;
default: default:
LOG_WARNING(Service_SSL, "Unknown option={}", option); LOG_WARNING(Service_SSL, "Unknown option={}, value={}", option, value);
value = 0; }
R_SUCCEED();
}
Result GetOption(OptionType option, Out<u8> value) {
switch (option) {
case OptionType::DoNotCloseSocket:
*value = u8(do_not_close_socket);
break;
case OptionType::GetServerCertChain:
*value = u8(get_server_cert_chain);
break;
case OptionType::SkipDefaultVerify:
*value = u8(skip_default_verify);
break;
case OptionType::EnableAlpn:
*value = u8(enable_alpn);
break;
default:
LOG_WARNING(Service_SSL, "Unknown option={}", u32(option));
*value = 0;
break; break;
} }
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", u32(option), *value);
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value); R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(value);
} }
void SetNextAlpnProto(HLERequestContext& ctx) { Result SetNextAlpnProto(InBuffer<BufferAttr_HipcMapAlias> data) {
const auto data = ctx.ReadBuffer(0); auto const to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
next_alpn_proto.assign(data.begin(), data.end()); next_alpn_proto.assign(data.begin(), data.begin() + to_write);
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);
} }
void GetNextAlpnProto(HLERequestContext& ctx) { Result GetNextAlpnProto(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> to_write) {
const size_t writable = ctx.GetWriteBufferSize(); *to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
const size_t to_write = (std::min)(next_alpn_proto.size(), writable); next_alpn_proto.assign(data.begin(), data.begin() + *to_write);
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", *to_write);
if (to_write != 0) { R_SUCCEED();
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> {
@@ -492,7 +461,7 @@ public:
{5, &ISslContext::ImportClientPki, "ImportClientPki"}, {5, &ISslContext::ImportClientPki, "ImportClientPki"},
{6, nullptr, "RemoveServerPki"}, {6, nullptr, "RemoveServerPki"},
{7, nullptr, "RemoveClientPki"}, {7, nullptr, "RemoveClientPki"},
{8, nullptr, "RegisterInternalPki"}, {8, D<&ISslContext::RegisterInternalPki>, "RegisterInternalPki"},
{9, nullptr, "AddPolicyOid"}, {9, nullptr, "AddPolicyOid"},
{10, nullptr, "ImportCrl"}, {10, nullptr, "ImportCrl"},
{11, nullptr, "RemoveCrl"}, {11, nullptr, "RemoveCrl"},
@@ -587,6 +556,11 @@ 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> {
+6 -1
View File
@@ -36,12 +36,17 @@ 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,18 +157,30 @@ public:
socket = std::move(socket_in); socket = std::move(socket_in);
} }
Result SetHostName(const std::string& hostname) override { Result SetHostName(const char* hostname) override {
if (!skip_cert_verification) { if (!skip_cert_verification) {
if (!SSL_set1_host(ssl, hostname.c_str())) { if (!SSL_set1_host(ssl, hostname)) {
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.c_str())) { // hostname for SNI if (!SSL_set_tlsext_host_name(ssl, hostname)) { // 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();
} }
return ResultSuccess; R_SUCCEED();
}
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 {
@@ -213,6 +225,13 @@ 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);
@@ -263,7 +282,25 @@ public:
out_certs->emplace_back(buf, buf + len); out_certs->emplace_back(buf, buf + len);
OPENSSL_free(buf); OPENSSL_free(buf);
} }
return ResultSuccess; R_SUCCEED();
}
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(ctx.conf.page_table_log2_stride)]); 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
@@ -161,8 +161,12 @@ template<>
code.jnz(abort, code.T_NEAR); code.jnz(abort, code.T_NEAR);
} }
code.shl(tmp, int(ctx.conf.page_table_log2_stride)); if (ctx.conf.page_table_log2_stride > 3) {
code.mov(page, qword[r14 + tmp]); code.shl(tmp, int(ctx.conf.page_table_log2_stride));
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) {
@@ -178,6 +182,7 @@ 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);