Compare commits

...

66 Commits

Author SHA1 Message Date
Morph
87cfe5b1da time: Fix CalculateSpanBetween implementation
CalculateSpanBetween passes in the ClockSnapshots through 2 input buffers and not as raw arguments. Fix this by reading the 2 input buffers instead of popping raw arguments.

Partially fixes Super Smash Bros. Ultimate's Spirit Board
2021-03-10 11:42:59 -05:00
Rodrigo Locatti
daf5c5060b Merge pull request #5891 from ameerj/bgra-ogl
renderer_opengl: Use compute shaders to swizzle BGR textures on copy
2021-03-09 02:47:51 -03:00
bunnei
d1a7b2eca7 Merge pull request #6021 from ReinUsesLisp/skip-cache-heuristic
buffer_cache: Heuristically decide to skip cache on uniform buffers
2021-03-08 17:48:55 -08:00
bunnei
9c4c9f1e7d Merge pull request #5990 from german77/mousePanningV2
InputCommon: Mouse fixes
2021-03-08 14:50:58 -08:00
bunnei
69ce5e41eb Merge pull request #6041 from bunnei/fiber-leaks
common: fiber: Use weak_ptr when yielding.
2021-03-08 13:51:37 -08:00
Morph
9d77ae39de Merge pull request #6047 from lioncash/dynarmic
external: Update dynarmic
2021-03-08 00:52:19 -05:00
Lioncash
de21c9e330 external: Update dynarmic 2021-03-08 00:44:54 -05:00
bunnei
8f7eb194af common: Fiber: use a reference for YieldTo.
- Fixes another small leak.
2021-03-07 13:46:53 -08:00
bunnei
68ffac250a common: fiber: Use weak_ptr when yielding.
- Avoids a memory leak, as taking a strong reference of the fiber here causes a circular reference.
- Supersedes #6006 with a more narrow fix.
2021-03-05 22:10:03 -08:00
bunnei
4cf5b860bd Merge pull request #6036 from bunnei/thread-leak
hle: kernel: KThread: Rework dummy threads & fix memory leak.
2021-03-05 17:15:35 -08:00
bunnei
47af34003b hle: kernel: KThread: Rework dummy threads & fix memory leak.
- Dummy threads are created on thread local storage for all host threads.
- Fixes a leak by removing creation of fibers, which are not applicable here.
2021-03-05 17:10:57 -08:00
LC
97415ad07a Merge pull request #6029 from Morph1984/compile-utf8
CMakeLists: Add /utf-8 compile option for MSVC
2021-03-05 20:09:37 -05:00
bunnei
7b29a8ce4e Merge pull request #6039 from yuzu-emu/revert-6006-fiber-unique-ptr
Revert "core: Switch to unique_ptr for usage of Common::Fiber."
2021-03-05 17:08:48 -08:00
bunnei
a5ab85ac37 Revert "core: Switch to unique_ptr for usage of Common::Fiber." 2021-03-05 17:08:17 -08:00
bunnei
9d010be483 Merge pull request #6034 from Morph1984/mbedtls
externals: Update mbedtls to 2.16.9
2021-03-05 15:48:28 -08:00
bunnei
34a3ee1631 Merge pull request #6006 from bunnei/fiber-unique-ptr
core: Switch to unique_ptr for usage of Common::Fiber.
2021-03-04 23:59:06 -08:00
Morph
96c9e67b1b aes_util: Remove malformed mbedtls_cipher_finish function call 2021-03-05 02:05:05 -05:00
Morph
6faabd6d69 externals: Update mbedtls to 2.16.9
mbedtls 2.16 is the last version which has licensing for GPL 2.0. This updates mbedtls to our own fork of mbedtls 2.16
2021-03-05 02:05:05 -05:00
Morph
e7038344aa CMakeLists: Add /utf-8 compile option for MSVC
Ensures that the source and execution character sets are in UTF-8
2021-03-05 01:46:56 -05:00
ameerj
5213f70230 texture_cache: Blacklist BGRA8 copies and views on OpenGL
In order to force the BGRA8 conversion on Nvidia using OpenGL, we need to forbid texture copies and views with other formats.

This commit also adds a boolean relating to this, as this needs to be done only for the OpenGL api, Vulkan must remain unchanged.
2021-03-04 14:14:49 -05:00
ameerj
0639244d85 renderer_opengl: Swizzle BGR textures on copy
OpenGL does not natively support BGR internal formats, which causes many BGR textures to render incorrectly, with Red and Blue channels swapped.

This commit aims to address this by swizzling the blue and red channels on texture copies when a BGR format is encountered.
2021-03-04 14:14:19 -05:00
bunnei
b8b5891585 Merge pull request #5989 from ReinUsesLisp/cmdpool
vk_command_pool: Reduce the command pool size from 4096 to 4
2021-03-04 11:07:31 -08:00
bunnei
394475c4e3 Merge pull request #6004 from german77/udprandom
InputCommon: Use an unique client id for each udp socket instance
2021-03-03 15:45:32 -08:00
bunnei
f8bfec3109 Merge pull request #5815 from comex/net-error-reform
Network error handling reform
2021-03-02 17:08:47 -08:00
LC
4a45012f35 Merge pull request #6020 from bunnei/shutdown-crash-2
core: Shutdown: Move kernel cleanup to later in shutdown.
2021-03-02 09:28:28 -05:00
ReinUsesLisp
5ad62e7bfc buffer_cache: Heuristically decide to skip cache on uniform buffers
Some games benefit from skipping caches (Pokémon Sword), and others
don't (Animal Crossing: New Horizons). Add an heuristic to decide this
at runtime.

The cache hit ratio has to be ~98% or better to not skip the cache.
There are 16 frames of buffer.
2021-03-02 02:44:19 -03:00
bunnei
925671071c core: Shutdown: Move kernel cleanup to later in shutdown.
- Fixes a shutdown crash due to a race condition with GPU still accessing memory.
2021-03-01 21:42:06 -08:00
bunnei
cd25817938 Merge pull request #6019 from Kelebek1/bcat
[Service::nifm] Fix bcat_backend's default initialisation
2021-03-01 19:27:06 -08:00
Kelebek1
c7a7e47615 Fix default bcat_backend init 2021-03-02 03:20:16 +00:00
german
9b3af0027b inputCommon: Use an unique client id for each socket instance 2021-03-01 09:19:33 -06:00
Morph
ac8b1445ff Merge pull request #6016 from ameerj/remove-async-nvdec
gpu_thread: Remove Async NVDEC placeholders
2021-03-01 04:22:46 -05:00
ameerj
52e9d7fa49 gpu_thread: Remove Async NVDEC placeholders
This commit removes early placeholders for an implementation of async nvdec. With recent changes to the source code, the placeholders are no longer accurate, and can cause a nullptr dereference due to the nature of the cdma_pusher lifetime.
2021-02-28 22:03:00 -05:00
comex
2910aa77b2 [network] Error handling reform
`network.cpp` has several error paths which either:
- report "Unhandled host socket error=n" and return `SUCCESS`, or
- switch on a few possible errors, log them, and translate them to
  Errno; the same switch statement is copied and pasted in multiple
  places in the code

Convert these paths to use a helper function `GetAndLogLastError`, which
is roughly the equivalent of one of the switch statements, but:
- handling more cases (both ones that were already in `Errno`, and a few
  more I added), and
- using OS functions to convert the error to a string when logging, so
  it'll describe the error even if it's not one of the ones in the
  switch statement.
  - To handle this, refactor the logic in `GetLastErrorMsg` to expose a
    new function `NativeErrorToString` which takes the error number
    explicitly as an argument.  And improve the Windows version a bit.

Also, add a test which exercises two random error paths.
2021-02-28 17:25:31 -05:00
bunnei
9e9341f4b4 Merge pull request #6007 from bunnei/ldn-error
core: hle: ldn: Error out on call to Initialization.
2021-02-28 13:34:20 -08:00
Morph
ee9ebeeb80 Merge pull request #5276 from german77/gestures
HID: Implement gestures
2021-02-27 22:18:41 -05:00
german
e895ab7d6f Implements touch, pan, pinch and rotation gestures 2021-02-27 19:54:42 -06:00
german77
4738e14cb0 inputCommon: Mouse fixes 2021-02-27 17:53:10 -06:00
bunnei
55f556c53e Merge pull request #5984 from jbeich/gcc-freebsd
common,video-core: unbreak GCC 11 build on FreeBSD 13
2021-02-27 14:15:00 -07:00
bunnei
ab65cb499d core: hle: ldn: Error out on call to Initialization.
- Since we do not emulate LDN, returning an error here makes more sense.
2021-02-27 11:59:29 -08:00
bunnei
51fb0a6f96 core: Switch to unique_ptr for usage of Common::Fiber.
- With using unique_ptr instead of shared_ptr, we have more explicit ownership of the context.
- Fixes a memory leak due to circular reference of the shared pointer.
2021-02-27 11:56:04 -08:00
bunnei
09f7c355c6 Merge pull request #5953 from bunnei/memory-refactor-1
Kernel Rework: Memory updates and refactoring (Part 1)
2021-02-27 12:48:35 -07:00
bunnei
bfa1644464 Merge pull request #5944 from Morph1984/gc-vibrations
hid: Implement GameCube Controller Vibrations
2021-02-26 19:10:36 -07:00
bunnei
272bc4c3d6 Merge pull request #5997 from Kelebek1/Depth
[OpenGL] Implement glDepthRangeIndexeddNV
2021-02-26 15:06:55 -07:00
bunnei
1ba578c4aa Merge pull request #5977 from Morph1984/stub-acc
acc: Stub GetNintendoAccountUserResourceCacheForApplication
2021-02-24 17:46:15 -07:00
ReinUsesLisp
aae399c1a8 vk_command_pool: Reduce the command pool size from 4096 to 4
This allows drivers to reuse memory more easily and preallocate less.
The optimal number has been measured booting Pokémon Sword.
2021-02-23 19:08:24 -03:00
Jan Beich
1841ca4b9b video_core: add missing header after 468bd9c1b0
src/video_core/shader_notify.cpp: In member function 'void VideoCore::ShaderNotify::MarkShaderComplete()':
src/video_core/shader_notify.cpp:33:10: error: 'unique_lock' is not a member of 'std'
   33 |     std::unique_lock lock{mutex};
      |          ^~~~~~~~~~~
src/video_core/shader_notify.cpp:6:1: note: 'std::unique_lock' is defined in header '<mutex>'; did you forget to '#include <mutex>'?
    5 | #include "video_core/shader_notify.h"
  +++ |+#include <mutex>
    6 |
src/video_core/shader_notify.cpp: In member function 'void VideoCore::ShaderNotify::MarkSharderBuilding()':
src/video_core/shader_notify.cpp:38:10: error: 'unique_lock' is not a member of 'std'
   38 |     std::unique_lock lock{mutex};
      |          ^~~~~~~~~~~
src/video_core/shader_notify.cpp:38:10: note: 'std::unique_lock' is defined in header '<mutex>'; did you forget to '#include <mutex>'?
2021-02-23 00:04:36 +00:00
Jan Beich
71526ecfc7 common: add missing header after f3805376f7
In file included from src/video_core/dma_pusher.cpp:5:
src/./common/cityhash.h:69:47: error: 'size_t' has not been declared
   69 | [[nodiscard]] u64 CityHash64(const char* buf, size_t len);
      |                                               ^~~~~~
src/./common/cityhash.h:73:55: error: 'size_t' has not been declared
   73 | [[nodiscard]] u64 CityHash64WithSeed(const char* buf, size_t len, u64 seed);
      |                                                       ^~~~~~
src/./common/cityhash.h:77:56: error: 'size_t' has not been declared
   77 | [[nodiscard]] u64 CityHash64WithSeeds(const char* buf, size_t len, u64 seed0, u64 seed1);
      |                                                        ^~~~~~
src/./common/cityhash.h:80:47: error: 'size_t' has not been declared
   80 | [[nodiscard]] u128 CityHash128(const char* s, size_t len);
      |                                               ^~~~~~
src/./common/cityhash.h:84:55: error: 'size_t' has not been declared
   84 | [[nodiscard]] u128 CityHash128WithSeed(const char* s, size_t len, u128 seed);
      |                                                       ^~~~~~
2021-02-23 00:04:32 +00:00
Morph
ec19a85890 hid: Implement GameCube Controller Vibrations
Implements both SendVibrationGcErmCommand and GetActualVibrationGcErmCommand, and modifies GetVibrationDeviceInfo to account for additional controllers.
2021-02-21 10:32:59 -05:00
Morph
3de8e7a8f2 acc: Stub GetNintendoAccountUserResourceCacheForApplication
This command returns a Nintendo Account ID and writes 2 output buffers. The first output buffer is a NasUserBaseForApplication and the second output buffer is currently empty.

Used by:
- Pokken Tournament DX
- Super Smash Bros. Ultimate
- Super Nintendo Entertainment System - Nintendo Switch Online
- Mario Kart 8 Deluxe
2021-02-21 10:29:25 -05:00
bunnei
93e20867b0 hle: kernel: Migrate PageHeap/PageTable to KPageHeap/KPageTable. 2021-02-18 16:16:25 -08:00
bunnei
b1e27890e8 hle: kernel: Migrate MemoryManager to KMemoryManager. 2021-02-18 16:16:25 -08:00
bunnei
93109c870e hle: kernel: Migrate PageLinkedList to KPageLinkedList. 2021-02-18 16:16:25 -08:00
bunnei
65e0178cc0 hle: kernel: Migrate to KMemoryBlock, KMemoryBlockManager, and others. 2021-02-18 16:16:25 -08:00
bunnei
9e520e8f12 hle: kernel: Migrate SlabHeap to KSlabHeap. 2021-02-18 16:16:25 -08:00
bunnei
1d162f28d1 hle: kernel: Migrate MemoryLayout to KMemoryLayout. 2021-02-18 16:16:25 -08:00
bunnei
7ed5dd0d62 hle: kernel: Migrate AddressSpaceInfo to KAddressSpaceInfo. 2021-02-18 16:16:25 -08:00
bunnei
701ef616b2 hle: kernel: memory_manager: Rename AllocateContinuous to AllocateContinuous. 2021-02-18 16:16:24 -08:00
bunnei
f7a008d77f hle: kernel: KSystemControl does not belong in Memory namespace. 2021-02-18 16:16:24 -08:00
bunnei
6a19086001 hle: kernel: memory: PageHeap: Migrate to KPageBitmap class. 2021-02-18 16:16:24 -08:00
bunnei
a02566136c hle: kernel: Add KPageBitmap class. 2021-02-18 16:16:24 -08:00
bunnei
e7c33d1ad6 hle: kernel: system_control: Add function GenerateRandomU64. 2021-02-18 16:16:24 -08:00
bunnei
c9235764c7 common: Add implementation of TinyMT (Mersenne Twister RNG). 2021-02-18 16:16:24 -08:00
bunnei
6da91da08e hle: kernel: Add KSpinLock implementation. 2021-02-18 16:16:24 -08:00
bunnei
24e1e17a8a core: memory: Add templated GetPointer methods. 2021-02-18 16:16:24 -08:00
bunnei
b5b92fd1e5 common: alignment: Add DivideUp utility method. 2021-02-18 16:16:24 -08:00
bunnei
0d62f30b00 hle: kernel: Rename SharedMemory to KSharedMemory. 2021-02-18 16:16:12 -08:00
115 changed files with 2710 additions and 1542 deletions

2
.gitmodules vendored
View File

@@ -27,7 +27,7 @@
url = https://github.com/ReinUsesLisp/sirit
[submodule "mbedtls"]
path = externals/mbedtls
url = https://github.com/DarkLordZach/mbedtls
url = https://github.com/yuzu-emu/mbedtls
[submodule "libzip"]
path = externals/libzip/libzip
url = https://github.com/nih-at/libzip.git

View File

@@ -27,6 +27,7 @@ if (MSVC)
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /utf-8 - Set source and execution character sets to UTF-8
# /volatile:iso - Use strict standards-compliant volatile semantics.
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
# /Zc:inline - Let codegen omit inline functions in object files
@@ -38,6 +39,7 @@ if (MSVC)
/permissive-
/EHsc
/std:c++latest
/utf-8
/volatile:iso
/Zc:externConstexpr
/Zc:inline

View File

@@ -167,6 +167,7 @@ add_library(common STATIC
threadsafe_queue.h
time_zone.cpp
time_zone.h
tiny_mt.h
tree.h
uint128.h
uuid.cpp

View File

@@ -42,6 +42,11 @@ requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, si
return (value & mask) == 0;
}
template <typename T, typename U>
requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) {
return (x + (y - 1)) / y;
}
template <typename T, size_t Align = 16>
class AlignmentAllocator {
public:

View File

@@ -61,6 +61,7 @@
#pragma once
#include <cstddef>
#include "common/common_types.h"
namespace Common {

View File

@@ -52,9 +52,13 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
// Generic function to get last error message.
// Call directly after the command or use the error num.
// This function might change the error code.
// Defined in Misc.cpp.
// Defined in misc.cpp.
[[nodiscard]] std::string GetLastErrorMsg();
// Like GetLastErrorMsg(), but passing an explicit error code.
// Defined in misc.cpp.
[[nodiscard]] std::string NativeErrorToString(int e);
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \

View File

@@ -116,16 +116,19 @@ void Fiber::Rewind() {
boost::context::detail::jump_fcontext(impl->rewind_context, this);
}
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
to->impl->guard.lock();
to->impl->previous_fiber = from;
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
to.impl->guard.lock();
to.impl->previous_fiber = weak_from.lock();
auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
// "from" might no longer be valid if the thread was killed
if (auto from = weak_from.lock()) {
ASSERT(from->impl->previous_fiber != nullptr);
from->impl->previous_fiber->impl->context = transfer.fctx;
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {

View File

@@ -41,7 +41,7 @@ public:
/// Yields control from Fiber 'from' to Fiber 'to'
/// Fiber 'from' must be the currently running fiber.
static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);

View File

@@ -12,27 +12,41 @@
#include "common/common_funcs.h"
// Generic function to get last error message.
// Call directly after the command or use the error num.
// This function might change the error code.
std::string GetLastErrorMsg() {
static constexpr std::size_t buff_size = 255;
char err_str[buff_size];
std::string NativeErrorToString(int e) {
#ifdef _WIN32
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
return std::string(err_str, buff_size);
#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
LPSTR err_str;
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
if (!res) {
return "(FormatMessageA failed to format error)";
}
std::string ret(err_str);
LocalFree(err_str);
return ret;
#else
char err_str[255];
#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
// Thread safe (GNU-specific)
const char* str = strerror_r(errno, err_str, buff_size);
const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str);
#else
// Thread safe (XSI-compliant)
const int success = strerror_r(errno, err_str, buff_size);
if (success != 0) {
return {};
int second_err = strerror_r(e, err_str, sizeof(err_str));
if (second_err != 0) {
return "(strerror_r failed to format error)";
}
return std::string(err_str);
#endif // GLIBC etc.
#endif // _WIN32
}
std::string GetLastErrorMsg() {
#ifdef _WIN32
return NativeErrorToString(GetLastError());
#else
return NativeErrorToString(errno);
#endif
}

250
src/common/tiny_mt.h Normal file
View File

@@ -0,0 +1,250 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/alignment.h"
#include "common/common_types.h"
namespace Common {
// Implementation of TinyMT (mersenne twister RNG).
// Like Nintendo, we will use the sample parameters.
class TinyMT {
public:
static constexpr std::size_t NumStateWords = 4;
struct State {
std::array<u32, NumStateWords> data{};
};
private:
static constexpr u32 ParamMat1 = 0x8F7011EE;
static constexpr u32 ParamMat2 = 0xFC78FF1F;
static constexpr u32 ParamTmat = 0x3793FDFF;
static constexpr u32 ParamMult = 0x6C078965;
static constexpr u32 ParamPlus = 0x0019660D;
static constexpr u32 ParamXor = 0x5D588B65;
static constexpr u32 TopBitmask = 0x7FFFFFFF;
static constexpr int MinimumInitIterations = 8;
static constexpr int NumDiscardedInitOutputs = 8;
static constexpr u32 XorByShifted27(u32 value) {
return value ^ (value >> 27);
}
static constexpr u32 XorByShifted30(u32 value) {
return value ^ (value >> 30);
}
private:
State state{};
private:
// Internal API.
void FinalizeInitialization() {
const u32 state0 = this->state.data[0] & TopBitmask;
const u32 state1 = this->state.data[1];
const u32 state2 = this->state.data[2];
const u32 state3 = this->state.data[3];
if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) {
this->state.data[0] = 'T';
this->state.data[1] = 'I';
this->state.data[2] = 'N';
this->state.data[3] = 'Y';
}
for (int i = 0; i < NumDiscardedInitOutputs; i++) {
this->GenerateRandomU32();
}
}
u32 GenerateRandomU24() {
return (this->GenerateRandomU32() >> 8);
}
static void GenerateInitialValuePlus(TinyMT::State* state, int index, u32 value) {
u32& state0 = state->data[(index + 0) % NumStateWords];
u32& state1 = state->data[(index + 1) % NumStateWords];
u32& state2 = state->data[(index + 2) % NumStateWords];
u32& state3 = state->data[(index + 3) % NumStateWords];
const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus;
const u32 y = x + index + value;
state0 = y;
state1 += x;
state2 += y;
}
static void GenerateInitialValueXor(TinyMT::State* state, int index) {
u32& state0 = state->data[(index + 0) % NumStateWords];
u32& state1 = state->data[(index + 1) % NumStateWords];
u32& state2 = state->data[(index + 2) % NumStateWords];
u32& state3 = state->data[(index + 3) % NumStateWords];
const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor;
const u32 y = x - index;
state0 = y;
state1 ^= x;
state2 ^= y;
}
public:
constexpr TinyMT() = default;
// Public API.
// Initialization.
void Initialize(u32 seed) {
this->state.data[0] = seed;
this->state.data[1] = ParamMat1;
this->state.data[2] = ParamMat2;
this->state.data[3] = ParamTmat;
for (int i = 1; i < MinimumInitIterations; i++) {
const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]);
this->state.data[i % NumStateWords] ^= mixed * ParamMult + i;
}
this->FinalizeInitialization();
}
void Initialize(const u32* seed, int seed_count) {
this->state.data[0] = 0;
this->state.data[1] = ParamMat1;
this->state.data[2] = ParamMat2;
this->state.data[3] = ParamTmat;
{
const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1;
GenerateInitialValuePlus(&this->state, 0, seed_count);
for (int i = 0; i < num_init_iterations; i++) {
GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords,
(i < seed_count) ? seed[i] : 0);
}
for (int i = 0; i < static_cast<int>(NumStateWords); i++) {
GenerateInitialValueXor(&this->state,
(i + 1 + num_init_iterations) % NumStateWords);
}
}
this->FinalizeInitialization();
}
// State management.
void GetState(TinyMT::State& out) const {
out.data = this->state.data;
}
void SetState(const TinyMT::State& state_) {
this->state.data = state_.data;
}
// Random generation.
void GenerateRandomBytes(void* dst, std::size_t size) {
const uintptr_t start = reinterpret_cast<uintptr_t>(dst);
const uintptr_t end = start + size;
const uintptr_t aligned_start = Common::AlignUp(start, 4);
const uintptr_t aligned_end = Common::AlignDown(end, 4);
// Make sure we're aligned.
if (start < aligned_start) {
const u32 rnd = this->GenerateRandomU32();
std::memcpy(dst, &rnd, aligned_start - start);
}
// Write as many aligned u32s as we can.
{
u32* cur_dst = reinterpret_cast<u32*>(aligned_start);
u32* const end_dst = reinterpret_cast<u32*>(aligned_end);
while (cur_dst < end_dst) {
*(cur_dst++) = this->GenerateRandomU32();
}
}
// Handle any leftover unaligned data.
if (aligned_end < end) {
const u32 rnd = this->GenerateRandomU32();
std::memcpy(reinterpret_cast<void*>(aligned_end), &rnd, end - aligned_end);
}
}
u32 GenerateRandomU32() {
// Advance state.
const u32 x0 =
(this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2];
const u32 y0 = this->state.data[3];
const u32 x1 = x0 ^ (x0 << 1);
const u32 y1 = y0 ^ (y0 >> 1) ^ x1;
const u32 state0 = this->state.data[1];
u32 state1 = this->state.data[2];
u32 state2 = x1 ^ (y1 << 10);
const u32 state3 = y1;
if ((y1 & 1) != 0) {
state1 ^= ParamMat1;
state2 ^= ParamMat2;
}
this->state.data[0] = state0;
this->state.data[1] = state1;
this->state.data[2] = state2;
this->state.data[3] = state3;
// Temper.
const u32 t1 = state0 + (state2 >> 8);
u32 t0 = state3 ^ t1;
if ((t1 & 1) != 0) {
t0 ^= ParamTmat;
}
return t0;
}
u64 GenerateRandomU64() {
const u32 lo = this->GenerateRandomU32();
const u32 hi = this->GenerateRandomU32();
return (u64{hi} << 32) | u64{lo};
}
float GenerateRandomF32() {
// Floats have 24 bits of mantissa.
constexpr u32 MantissaBits = 24;
return static_cast<float>(GenerateRandomU24()) * (1.0f / (1U << MantissaBits));
}
double GenerateRandomF64() {
// Doubles have 53 bits of mantissa.
// The smart way to generate 53 bits of random would be to use 32 bits
// from the first rnd32() call, and then 21 from the second.
// Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32()
// call, and (32 - 6) bits from the second. We'll do what they do, but
// There's not a clear reason why.
constexpr u32 MantissaBits = 53;
constexpr u32 Shift1st = (64 - MantissaBits) / 2;
constexpr u32 Shift2nd = (64 - MantissaBits) - Shift1st;
const u32 first = (this->GenerateRandomU32() >> Shift1st);
const u32 second = (this->GenerateRandomU32() >> Shift2nd);
return (1.0 * first * (u64{1} << (32 - Shift2nd)) + second) *
(1.0 / (u64{1} << MantissaBits));
}
};
} // namespace Common

View File

@@ -156,6 +156,8 @@ add_library(core STATIC
hle/kernel/hle_ipc.h
hle/kernel/k_address_arbiter.cpp
hle/kernel/k_address_arbiter.h
hle/kernel/k_address_space_info.cpp
hle/kernel/k_address_space_info.h
hle/kernel/k_affinity_mask.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
@@ -164,6 +166,18 @@ add_library(core STATIC
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
hle/kernel/k_memory_block.h
hle/kernel/k_memory_block_manager.cpp
hle/kernel/k_memory_block_manager.h
hle/kernel/k_memory_layout.h
hle/kernel/k_memory_manager.cpp
hle/kernel/k_memory_manager.h
hle/kernel/k_page_bitmap.h
hle/kernel/k_page_heap.cpp
hle/kernel/k_page_heap.h
hle/kernel/k_page_linked_list.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h
hle/kernel/k_priority_queue.h
hle/kernel/k_readable_event.cpp
hle/kernel/k_readable_event.h
@@ -175,8 +189,15 @@ add_library(core STATIC
hle/kernel/k_scoped_lock.h
hle/kernel/k_scoped_resource_reservation.h
hle/kernel/k_scoped_scheduler_lock_and_sleep.h
hle/kernel/k_shared_memory.cpp
hle/kernel/k_shared_memory.h
hle/kernel/k_slab_heap.h
hle/kernel/k_spin_lock.cpp
hle/kernel/k_spin_lock.h
hle/kernel/k_synchronization_object.cpp
hle/kernel/k_synchronization_object.h
hle/kernel/k_system_control.cpp
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_queue.h
@@ -184,23 +205,7 @@ add_library(core STATIC
hle/kernel/k_writable_event.h
hle/kernel/kernel.cpp
hle/kernel/kernel.h
hle/kernel/memory/address_space_info.cpp
hle/kernel/memory/address_space_info.h
hle/kernel/memory/memory_block.h
hle/kernel/memory/memory_block_manager.cpp
hle/kernel/memory/memory_block_manager.h
hle/kernel/memory/memory_layout.h
hle/kernel/memory/memory_manager.cpp
hle/kernel/memory/memory_manager.h
hle/kernel/memory/memory_types.h
hle/kernel/memory/page_linked_list.h
hle/kernel/memory/page_heap.cpp
hle/kernel/memory/page_heap.h
hle/kernel/memory/page_table.cpp
hle/kernel/memory/page_table.h
hle/kernel/memory/slab_heap.h
hle/kernel/memory/system_control.cpp
hle/kernel/memory/system_control.h
hle/kernel/memory_types.h
hle/kernel/object.cpp
hle/kernel/object.h
hle/kernel/physical_core.cpp
@@ -218,8 +223,6 @@ add_library(core STATIC
hle/kernel/service_thread.h
hle/kernel/session.cpp
hle/kernel/session.h
hle/kernel/shared_memory.cpp
hle/kernel/shared_memory.h
hle/kernel/svc.cpp
hle/kernel/svc.h
hle/kernel/svc_common.h

View File

@@ -299,28 +299,17 @@ struct System::Impl {
gpu_core->WaitIdle();
}
// Shutdown emulation session
services.reset();
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
// Close all CPU/threading state
cpu_manager.Shutdown();
// Release the Time Manager's resources
time_manager.Shutdown();
// Shutdown kernel and core timing
core_timing.Shutdown();
kernel.Shutdown();
// Close app loader
app_loader.reset();
gpu_core.reset();
perf_stats.reset();
// Clear all applets
kernel.Shutdown();
applet_manager.ClearAll();
LOG_DEBUG(Core, "Shutdown OK");

View File

@@ -148,7 +148,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
scheduler.RescheduleCurrentCore();
@@ -245,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
scheduler.RescheduleCurrentCore();
@@ -271,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
scheduler.Unload(scheduler.GetCurrentThread());
auto& next_scheduler = kernel.Scheduler(current_core);
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
}
// May have changed scheduler
@@ -363,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) {
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
data.is_running = true;
Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
data.is_running = false;
data.is_paused = true;
data.exit_barrier->Wait();

View File

@@ -105,8 +105,6 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
}
}
}
mbedtls_cipher_finish(context, nullptr, nullptr);
}
template <typename Key, std::size_t KeySize>

View File

@@ -2,15 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#include <array>
#include "common/assert.h"
#include "core/hle/kernel/memory/address_space_info.h"
#include "core/hle/kernel/k_address_space_info.h"
namespace Kernel::Memory {
namespace Kernel {
namespace {
@@ -28,20 +25,20 @@ enum : u64 {
};
// clang-format off
constexpr std::array<AddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = AddressSpaceInfo::Type::Is32Bit, },
{ .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Is32Bit, },
{ .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Large64Bit, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Is32Bit },
{ .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = AddressSpaceInfo::Type::Stack, },
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall },
{ .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, },
}};
// clang-format on
@@ -49,7 +46,8 @@ constexpr bool IsAllowedIndexForAddress(std::size_t index) {
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Invalid;
}
using IndexArray = std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>;
using IndexArray =
std::array<std::size_t, static_cast<std::size_t>(KAddressSpaceInfo::Type::Count)>;
constexpr IndexArray AddressSpaceIndices32Bit{
0, 1, 0, 2, 0, 3,
@@ -63,23 +61,23 @@ constexpr IndexArray AddressSpaceIndices39Bit{
9, 8, 8, 10, 12, 11,
};
constexpr bool IsAllowed32BitType(AddressSpaceInfo::Type type) {
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
type != AddressSpaceInfo::Type::Stack;
constexpr bool IsAllowed32BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
type != KAddressSpaceInfo::Type::Stack;
}
constexpr bool IsAllowed36BitType(AddressSpaceInfo::Type type) {
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
type != AddressSpaceInfo::Type::Stack;
constexpr bool IsAllowed36BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
type != KAddressSpaceInfo::Type::Stack;
}
constexpr bool IsAllowed39BitType(AddressSpaceInfo::Type type) {
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Small64Bit;
constexpr bool IsAllowed39BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::MapLarge;
}
} // namespace
u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
const std::size_t index{static_cast<std::size_t>(type)};
switch (width) {
case 32:
@@ -99,7 +97,7 @@ u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
return 0;
}
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
const std::size_t index{static_cast<std::size_t>(type)};
switch (width) {
case 32:
@@ -116,4 +114,4 @@ std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
return 0;
}
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -2,20 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/common_types.h"
namespace Kernel::Memory {
namespace Kernel {
struct AddressSpaceInfo final {
struct KAddressSpaceInfo final {
enum class Type : u32 {
Is32Bit = 0,
Small64Bit = 1,
Large64Bit = 2,
MapSmall = 0,
MapLarge = 1,
Map39Bit = 2,
Heap = 3,
Stack = 4,
Alias = 5,
@@ -31,4 +28,4 @@ struct AddressSpaceInfo final {
const Type type{};
};
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -2,20 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/svc_types.h"
namespace Kernel::Memory {
namespace Kernel {
enum class MemoryState : u32 {
enum class KMemoryState : u32 {
None = 0,
Mask = 0xFF,
All = ~None,
@@ -97,31 +94,31 @@ enum class MemoryState : u32 {
FlagReferenceCounted | FlagCanDebug,
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
static_assert(static_cast<u32>(MemoryState::Free) == 0x00000000);
static_assert(static_cast<u32>(MemoryState::Io) == 0x00002001);
static_assert(static_cast<u32>(MemoryState::Static) == 0x00042002);
static_assert(static_cast<u32>(MemoryState::Code) == 0x00DC7E03);
static_assert(static_cast<u32>(MemoryState::CodeData) == 0x03FEBD04);
static_assert(static_cast<u32>(MemoryState::Normal) == 0x037EBD05);
static_assert(static_cast<u32>(MemoryState::Shared) == 0x00402006);
static_assert(static_cast<u32>(MemoryState::AliasCode) == 0x00DD7E08);
static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);
static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);
static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);
static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C);
static_assert(static_cast<u32>(MemoryState::Transferred) == 0x015C3C0D);
static_assert(static_cast<u32>(MemoryState::SharedTransferred) == 0x005C380E);
static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);
static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811);
static_assert(static_cast<u32>(MemoryState::NonDeviceIpc) == 0x004C2812);
static_assert(static_cast<u32>(MemoryState::Kernel) == 0x00002013);
static_assert(static_cast<u32>(MemoryState::GeneratedCode) == 0x00402214);
static_assert(static_cast<u32>(MemoryState::CodeOut) == 0x00402015);
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00002001);
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
static_assert(static_cast<u32>(KMemoryState::Code) == 0x00DC7E03);
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x03FEBD04);
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x037EBD05);
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x00402006);
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x00DD7E08);
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
enum class MemoryPermission : u8 {
enum class KMemoryPermission : u8 {
None = 0,
Mask = static_cast<u8>(~None),
@@ -135,9 +132,9 @@ enum class MemoryPermission : u8 {
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
Svc::MemoryPermission::Execute),
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
enum class MemoryAttribute : u8 {
enum class KMemoryAttribute : u8 {
None = 0x00,
Mask = 0x7F,
All = Mask,
@@ -152,18 +149,18 @@ enum class MemoryAttribute : u8 {
LockedAndIpcLocked = Locked | IpcLocked,
DeviceSharedAndUncached = DeviceShared | Uncached
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
static_assert((static_cast<u8>(MemoryAttribute::Mask) &
static_cast<u8>(MemoryAttribute::DontCareMask)) == 0);
static_assert((static_cast<u8>(KMemoryAttribute::Mask) &
static_cast<u8>(KMemoryAttribute::DontCareMask)) == 0);
struct MemoryInfo {
struct KMemoryInfo {
VAddr addr{};
std::size_t size{};
MemoryState state{};
MemoryPermission perm{};
MemoryAttribute attribute{};
MemoryPermission original_perm{};
KMemoryState state{};
KMemoryPermission perm{};
KMemoryAttribute attribute{};
KMemoryPermission original_perm{};
u16 ipc_lock_count{};
u16 device_use_count{};
@@ -171,9 +168,9 @@ struct MemoryInfo {
return {
addr,
size,
static_cast<Svc::MemoryState>(state & MemoryState::Mask),
static_cast<Svc::MemoryAttribute>(attribute & MemoryAttribute::Mask),
static_cast<Svc::MemoryPermission>(perm & MemoryPermission::UserMask),
static_cast<Svc::MemoryState>(state & KMemoryState::Mask),
static_cast<Svc::MemoryAttribute>(attribute & KMemoryAttribute::Mask),
static_cast<Svc::MemoryPermission>(perm & KMemoryPermission::UserMask),
ipc_lock_count,
device_use_count,
};
@@ -196,21 +193,21 @@ struct MemoryInfo {
}
};
class MemoryBlock final {
friend class MemoryBlockManager;
class KMemoryBlock final {
friend class KMemoryBlockManager;
private:
VAddr addr{};
std::size_t num_pages{};
MemoryState state{MemoryState::None};
KMemoryState state{KMemoryState::None};
u16 ipc_lock_count{};
u16 device_use_count{};
MemoryPermission perm{MemoryPermission::None};
MemoryPermission original_perm{MemoryPermission::None};
MemoryAttribute attribute{MemoryAttribute::None};
KMemoryPermission perm{KMemoryPermission::None};
KMemoryPermission original_perm{KMemoryPermission::None};
KMemoryAttribute attribute{KMemoryAttribute::None};
public:
static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) {
static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
@@ -221,9 +218,9 @@ public:
}
public:
constexpr MemoryBlock() = default;
constexpr MemoryBlock(VAddr addr_, std::size_t num_pages_, MemoryState state_,
MemoryPermission perm_, MemoryAttribute attribute_)
constexpr KMemoryBlock() = default;
constexpr KMemoryBlock(VAddr addr_, std::size_t num_pages_, KMemoryState state_,
KMemoryPermission perm_, KMemoryAttribute attribute_)
: addr{addr_}, num_pages(num_pages_), state{state_}, perm{perm_}, attribute{attribute_} {}
constexpr VAddr GetAddress() const {
@@ -246,40 +243,40 @@ public:
return GetEndAddress() - 1;
}
constexpr MemoryInfo GetMemoryInfo() const {
constexpr KMemoryInfo GetMemoryInfo() const {
return {
GetAddress(), GetSize(), state, perm,
attribute, original_perm, ipc_lock_count, device_use_count,
};
}
void ShareToDevice(MemoryPermission /*new_perm*/) {
ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared ||
void ShareToDevice(KMemoryPermission /*new_perm*/) {
ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared ||
device_use_count == 0);
attribute |= MemoryAttribute::DeviceShared;
attribute |= KMemoryAttribute::DeviceShared;
const u16 new_use_count{++device_use_count};
ASSERT(new_use_count > 0);
}
void UnshareToDevice(MemoryPermission /*new_perm*/) {
ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared);
void UnshareToDevice(KMemoryPermission /*new_perm*/) {
ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
const u16 prev_use_count{device_use_count--};
ASSERT(prev_use_count > 0);
if (prev_use_count == 1) {
attribute &= ~MemoryAttribute::DeviceShared;
attribute &= ~KMemoryAttribute::DeviceShared;
}
}
private:
constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const {
constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask |
MemoryAttribute::IpcLocked |
MemoryAttribute::DeviceShared};
constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const {
constexpr KMemoryAttribute AttributeIgnoreMask{KMemoryAttribute::DontCareMask |
KMemoryAttribute::IpcLocked |
KMemoryAttribute::DeviceShared};
return state == s && perm == p &&
(attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
}
constexpr bool HasSameProperties(const MemoryBlock& rhs) const {
constexpr bool HasSameProperties(const KMemoryBlock& rhs) const {
return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm &&
attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count &&
device_use_count == rhs.device_use_count;
@@ -296,25 +293,25 @@ private:
num_pages += count;
}
constexpr void Update(MemoryState new_state, MemoryPermission new_perm,
MemoryAttribute new_attribute) {
ASSERT(original_perm == MemoryPermission::None);
ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None);
constexpr void Update(KMemoryState new_state, KMemoryPermission new_perm,
KMemoryAttribute new_attribute) {
ASSERT(original_perm == KMemoryPermission::None);
ASSERT((attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None);
state = new_state;
perm = new_perm;
attribute = static_cast<MemoryAttribute>(
attribute = static_cast<KMemoryAttribute>(
new_attribute |
(attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared)));
(attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)));
}
constexpr MemoryBlock Split(VAddr split_addr) {
constexpr KMemoryBlock Split(VAddr split_addr) {
ASSERT(GetAddress() < split_addr);
ASSERT(Contains(split_addr));
ASSERT(Common::IsAligned(split_addr, PageSize));
MemoryBlock block;
KMemoryBlock block;
block.addr = addr;
block.num_pages = (split_addr - GetAddress()) / PageSize;
block.state = state;
@@ -330,6 +327,6 @@ private:
return block;
}
};
static_assert(std::is_trivially_destructible<MemoryBlock>::value);
static_assert(std::is_trivially_destructible<KMemoryBlock>::value);
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -2,19 +2,19 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/memory/memory_block_manager.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel::Memory {
namespace Kernel {
MemoryBlockManager::MemoryBlockManager(VAddr start_addr, VAddr end_addr)
KMemoryBlockManager::KMemoryBlockManager(VAddr start_addr, VAddr end_addr)
: start_addr{start_addr}, end_addr{end_addr} {
const u64 num_pages{(end_addr - start_addr) / PageSize};
memory_block_tree.emplace_back(start_addr, num_pages, MemoryState::Free, MemoryPermission::None,
MemoryAttribute::None);
memory_block_tree.emplace_back(start_addr, num_pages, KMemoryState::Free,
KMemoryPermission::None, KMemoryAttribute::None);
}
MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) {
KMemoryBlockManager::iterator KMemoryBlockManager::FindIterator(VAddr addr) {
auto node{memory_block_tree.begin()};
while (node != end()) {
const VAddr end_addr{node->GetNumPages() * PageSize + node->GetAddress()};
@@ -26,9 +26,9 @@ MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) {
return end();
}
VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
std::size_t num_pages, std::size_t align, std::size_t offset,
std::size_t guard_pages) {
VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
std::size_t num_pages, std::size_t align,
std::size_t offset, std::size_t guard_pages) {
if (num_pages == 0) {
return {};
}
@@ -41,7 +41,7 @@ VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_nu
break;
}
if (info.state != MemoryState::Free) {
if (info.state != KMemoryState::Free) {
continue;
}
@@ -63,17 +63,17 @@ VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_nu
return {};
}
void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState prev_state,
MemoryPermission prev_perm, MemoryAttribute prev_attribute,
MemoryState state, MemoryPermission perm,
MemoryAttribute attribute) {
void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state,
KMemoryPermission prev_perm, KMemoryAttribute prev_attribute,
KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attribute) {
const VAddr end_addr{addr + num_pages * PageSize};
iterator node{memory_block_tree.begin()};
prev_attribute |= MemoryAttribute::IpcAndDeviceMapped;
prev_attribute |= KMemoryAttribute::IpcAndDeviceMapped;
while (node != memory_block_tree.end()) {
MemoryBlock* block{&(*node)};
KMemoryBlock* block{&(*node)};
iterator next_node{std::next(node)};
const VAddr cur_addr{block->GetAddress()};
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
@@ -106,13 +106,13 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState p
}
}
void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState state,
MemoryPermission perm, MemoryAttribute attribute) {
void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState state,
KMemoryPermission perm, KMemoryAttribute attribute) {
const VAddr end_addr{addr + num_pages * PageSize};
iterator node{memory_block_tree.begin()};
while (node != memory_block_tree.end()) {
MemoryBlock* block{&(*node)};
KMemoryBlock* block{&(*node)};
iterator next_node{std::next(node)};
const VAddr cur_addr{block->GetAddress()};
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
@@ -141,13 +141,13 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState s
}
}
void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
MemoryPermission perm) {
void KMemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
KMemoryPermission perm) {
const VAddr end_addr{addr + num_pages * PageSize};
iterator node{memory_block_tree.begin()};
while (node != memory_block_tree.end()) {
MemoryBlock* block{&(*node)};
KMemoryBlock* block{&(*node)};
iterator next_node{std::next(node)};
const VAddr cur_addr{block->GetAddress()};
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
@@ -176,9 +176,9 @@ void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&
}
}
void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
void KMemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
const_iterator it{FindIterator(start)};
MemoryInfo info{};
KMemoryInfo info{};
do {
info = it->GetMemoryInfo();
func(info);
@@ -186,8 +186,8 @@ void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& f
} while (info.addr + info.size - 1 < end - 1 && it != cend());
}
void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
MemoryBlock* block{&(*it)};
void KMemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
KMemoryBlock* block{&(*it)};
auto EraseIt = [&](const iterator it_to_erase) {
if (next_it == it_to_erase) {
@@ -197,7 +197,7 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
};
if (it != memory_block_tree.begin()) {
MemoryBlock* prev{&(*std::prev(it))};
KMemoryBlock* prev{&(*std::prev(it))};
if (block->HasSameProperties(*prev)) {
const iterator prev_it{std::prev(it)};
@@ -211,7 +211,7 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
}
if (it != cend()) {
const MemoryBlock* const next{&(*std::next(it))};
const KMemoryBlock* const next{&(*std::next(it))};
if (block->HasSameProperties(*next)) {
block->Add(next->GetNumPages());
@@ -220,4 +220,4 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
}
}
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -8,18 +8,18 @@
#include <list>
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/k_memory_block.h"
namespace Kernel::Memory {
namespace Kernel {
class MemoryBlockManager final {
class KMemoryBlockManager final {
public:
using MemoryBlockTree = std::list<MemoryBlock>;
using MemoryBlockTree = std::list<KMemoryBlock>;
using iterator = MemoryBlockTree::iterator;
using const_iterator = MemoryBlockTree::const_iterator;
public:
MemoryBlockManager(VAddr start_addr, VAddr end_addr);
KMemoryBlockManager(VAddr start_addr, VAddr end_addr);
iterator end() {
return memory_block_tree.end();
@@ -36,21 +36,22 @@ public:
VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
std::size_t align, std::size_t offset, std::size_t guard_pages);
void Update(VAddr addr, std::size_t num_pages, MemoryState prev_state,
MemoryPermission prev_perm, MemoryAttribute prev_attribute, MemoryState state,
MemoryPermission perm, MemoryAttribute attribute);
void Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state,
KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, KMemoryState state,
KMemoryPermission perm, KMemoryAttribute attribute);
void Update(VAddr addr, std::size_t num_pages, MemoryState state,
MemoryPermission perm = MemoryPermission::None,
MemoryAttribute attribute = MemoryAttribute::None);
void Update(VAddr addr, std::size_t num_pages, KMemoryState state,
KMemoryPermission perm = KMemoryPermission::None,
KMemoryAttribute attribute = KMemoryAttribute::None);
using LockFunc = std::function<void(iterator, MemoryPermission)>;
void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, MemoryPermission perm);
using LockFunc = std::function<void(iterator, KMemoryPermission)>;
void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
KMemoryPermission perm);
using IterateFunc = std::function<void(const MemoryInfo&)>;
using IterateFunc = std::function<void(const KMemoryInfo&)>;
void IterateForRange(VAddr start, VAddr end, IterateFunc&& func);
MemoryBlock& FindBlock(VAddr addr) {
KMemoryBlock& FindBlock(VAddr addr) {
return *FindIterator(addr);
}
@@ -63,4 +64,4 @@ private:
MemoryBlockTree memory_block_tree;
};
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -7,7 +7,7 @@
#include "common/common_types.h"
#include "core/device_memory.h"
namespace Kernel::Memory {
namespace Kernel {
constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024;
constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
@@ -27,8 +27,8 @@ constexpr bool IsKernelAddress(VAddr address) {
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
}
class MemoryRegion final {
friend class MemoryLayout;
class KMemoryRegion final {
friend class KMemoryLayout;
public:
constexpr PAddr StartAddress() const {
@@ -40,29 +40,29 @@ public:
}
private:
constexpr MemoryRegion() = default;
constexpr MemoryRegion(PAddr start_address, PAddr end_address)
constexpr KMemoryRegion() = default;
constexpr KMemoryRegion(PAddr start_address, PAddr end_address)
: start_address{start_address}, end_address{end_address} {}
const PAddr start_address{};
const PAddr end_address{};
};
class MemoryLayout final {
class KMemoryLayout final {
public:
constexpr const MemoryRegion& Application() const {
constexpr const KMemoryRegion& Application() const {
return application;
}
constexpr const MemoryRegion& Applet() const {
constexpr const KMemoryRegion& Applet() const {
return applet;
}
constexpr const MemoryRegion& System() const {
constexpr const KMemoryRegion& System() const {
return system;
}
static constexpr MemoryLayout GetDefaultLayout() {
static constexpr KMemoryLayout GetDefaultLayout() {
constexpr std::size_t application_size{0xcd500000};
constexpr std::size_t applet_size{0x1fb00000};
constexpr PAddr application_start_address{Core::DramMemoryMap::End - application_size};
@@ -76,15 +76,15 @@ public:
}
private:
constexpr MemoryLayout(PAddr application_start_address, std::size_t application_size,
PAddr applet_start_address, std::size_t applet_size,
PAddr system_start_address, std::size_t system_size)
constexpr KMemoryLayout(PAddr application_start_address, std::size_t application_size,
PAddr applet_start_address, std::size_t applet_size,
PAddr system_start_address, std::size_t system_size)
: application{application_start_address, application_size},
applet{applet_start_address, applet_size}, system{system_start_address, system_size} {}
const MemoryRegion application;
const MemoryRegion applet;
const MemoryRegion system;
const KMemoryRegion application;
const KMemoryRegion applet;
const KMemoryRegion system;
};
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -8,20 +8,20 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/scope_exit.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/memory/page_linked_list.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Memory {
namespace Kernel {
std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
const auto size{end_address - start_address};
// Calculate metadata sizes
const auto ref_count_size{(size / PageSize) * sizeof(u16)};
const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)};
const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)};
const auto page_heap_size{PageHeap::CalculateMetadataOverheadSize(size)};
const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)};
const auto total_metadata_size{manager_size + page_heap_size};
ASSERT(manager_size <= total_metadata_size);
ASSERT(Common::IsAligned(total_metadata_size, PageSize));
@@ -41,29 +41,30 @@ std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u6
return total_metadata_size;
}
void MemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
ASSERT(pool < Pool::Count);
managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address);
}
VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool,
Direction dir) {
VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages,
u32 option) {
// Early return if we're allocating no pages
if (num_pages == 0) {
return {};
}
// Lock the pool that we're allocating from
const auto [pool, dir] = DecodeOption(option);
const auto pool_index{static_cast<std::size_t>(pool)};
std::lock_guard lock{pool_locks[pool_index]};
// Choose a heap based on our page size request
const s32 heap_index{PageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
// Loop, trying to iterate from each block
// TODO (bunnei): Support multiple managers
Impl& chosen_manager{managers[pool_index]};
VAddr allocated_block{chosen_manager.AllocateBlock(heap_index)};
VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)};
// If we failed to allocate, quit now
if (!allocated_block) {
@@ -71,7 +72,7 @@ VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align
}
// If we allocated more than we need, free some
const auto allocated_pages{PageHeap::GetBlockNumPages(heap_index)};
const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)};
if (allocated_pages > num_pages) {
chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
}
@@ -79,8 +80,8 @@ VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align
return allocated_block;
}
ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
ASSERT(page_list.GetNumPages() == 0);
// Early return if we're allocating no pages
@@ -93,7 +94,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
std::lock_guard lock{pool_locks[pool_index]};
// Choose a heap based on our page size request
const s32 heap_index{PageHeap::GetBlockIndex(num_pages)};
const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)};
if (heap_index < 0) {
return ResultOutOfMemory;
}
@@ -112,11 +113,11 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
// Keep allocating until we've allocated all our pages
for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) {
const auto pages_per_alloc{PageHeap::GetBlockNumPages(index)};
const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)};
while (num_pages >= pages_per_alloc) {
// Allocate a block
VAddr allocated_block{chosen_manager.AllocateBlock(index)};
VAddr allocated_block{chosen_manager.AllocateBlock(index, false)};
if (!allocated_block) {
break;
}
@@ -148,8 +149,8 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
return RESULT_SUCCESS;
}
ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir) {
// Early return if we're freeing no pages
if (!num_pages) {
return RESULT_SUCCESS;
@@ -172,4 +173,4 @@ ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages,
return RESULT_SUCCESS;
}
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -6,16 +6,18 @@
#include <array>
#include <mutex>
#include <tuple>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/page_heap.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/hle/result.h"
namespace Kernel::Memory {
namespace Kernel {
class PageLinkedList;
class KPageLinkedList;
class MemoryManager final : NonCopyable {
class KMemoryManager final : NonCopyable {
public:
enum class Pool : u32 {
Application = 0,
@@ -37,29 +39,50 @@ public:
Mask = (0xF << Shift),
};
MemoryManager() = default;
KMemoryManager() = default;
constexpr std::size_t GetSize(Pool pool) const {
return managers[static_cast<std::size_t>(pool)].GetSize();
}
void InitializeManager(Pool pool, u64 start_address, u64 end_address);
VAddr AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool,
Direction dir = Direction::FromFront);
ResultCode Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir = Direction::FromFront);
ResultCode Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
Direction dir = Direction::FromFront);
static constexpr std::size_t MaxManagerCount = 10;
public:
static constexpr u32 EncodeOption(Pool pool, Direction dir) {
return (static_cast<u32>(pool) << static_cast<u32>(Pool::Shift)) |
(static_cast<u32>(dir) << static_cast<u32>(Direction::Shift));
}
static constexpr Pool GetPool(u32 option) {
return static_cast<Pool>((static_cast<u32>(option) & static_cast<u32>(Pool::Mask)) >>
static_cast<u32>(Pool::Shift));
}
static constexpr Direction GetDirection(u32 option) {
return static_cast<Direction>(
(static_cast<u32>(option) & static_cast<u32>(Direction::Mask)) >>
static_cast<u32>(Direction::Shift));
}
static constexpr std::tuple<Pool, Direction> DecodeOption(u32 option) {
return std::make_tuple(GetPool(option), GetDirection(option));
}
private:
class Impl final : NonCopyable {
private:
using RefCount = u16;
private:
PageHeap heap;
KPageHeap heap;
Pool pool{};
public:
@@ -67,8 +90,8 @@ private:
std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
VAddr AllocateBlock(s32 index) {
return heap.AllocateBlock(index);
VAddr AllocateBlock(s32 index, bool random) {
return heap.AllocateBlock(index, random);
}
void Free(VAddr addr, std::size_t num_pages) {
@@ -93,4 +116,4 @@ private:
std::array<Impl, MaxManagerCount> managers;
};
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -0,0 +1,279 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <bit>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/tiny_mt.h"
#include "core/hle/kernel/k_system_control.h"
namespace Kernel {
class KPageBitmap {
private:
class RandomBitGenerator {
private:
Common::TinyMT rng{};
u32 entropy{};
u32 bits_available{};
private:
void RefreshEntropy() {
entropy = rng.GenerateRandomU32();
bits_available = static_cast<u32>(Common::BitSize<decltype(entropy)>());
}
bool GenerateRandomBit() {
if (bits_available == 0) {
this->RefreshEntropy();
}
const bool rnd_bit = (entropy & 1) != 0;
entropy >>= 1;
--bits_available;
return rnd_bit;
}
public:
RandomBitGenerator() {
rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
}
std::size_t SelectRandomBit(u64 bitmap) {
u64 selected = 0;
u64 cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2;
u64 cur_mask = (1ULL << cur_num_bits) - 1;
while (cur_num_bits) {
const u64 low = (bitmap >> 0) & cur_mask;
const u64 high = (bitmap >> cur_num_bits) & cur_mask;
bool choose_low;
if (high == 0) {
// If only low val is set, choose low.
choose_low = true;
} else if (low == 0) {
// If only high val is set, choose high.
choose_low = false;
} else {
// If both are set, choose random.
choose_low = this->GenerateRandomBit();
}
// If we chose low, proceed with low.
if (choose_low) {
bitmap = low;
selected += 0;
} else {
bitmap = high;
selected += cur_num_bits;
}
// Proceed.
cur_num_bits /= 2;
cur_mask >>= cur_num_bits;
}
return selected;
}
};
public:
static constexpr std::size_t MaxDepth = 4;
private:
std::array<u64*, MaxDepth> bit_storages{};
RandomBitGenerator rng{};
std::size_t num_bits{};
std::size_t used_depths{};
public:
KPageBitmap() = default;
constexpr std::size_t GetNumBits() const {
return num_bits;
}
constexpr s32 GetHighestDepthIndex() const {
return static_cast<s32>(used_depths) - 1;
}
u64* Initialize(u64* storage, std::size_t size) {
// Initially, everything is un-set.
num_bits = 0;
// Calculate the needed bitmap depth.
used_depths = static_cast<std::size_t>(GetRequiredDepth(size));
ASSERT(used_depths <= MaxDepth);
// Set the bitmap pointers.
for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) {
bit_storages[depth] = storage;
size = Common::AlignUp(size, Common::BitSize<u64>()) / Common::BitSize<u64>();
storage += size;
}
return storage;
}
s64 FindFreeBlock(bool random) {
uintptr_t offset = 0;
s32 depth = 0;
if (random) {
do {
const u64 v = bit_storages[depth][offset];
if (v == 0) {
// If depth is bigger than zero, then a previous level indicated a block was
// free.
ASSERT(depth == 0);
return -1;
}
offset = offset * Common::BitSize<u64>() + rng.SelectRandomBit(v);
++depth;
} while (depth < static_cast<s32>(used_depths));
} else {
do {
const u64 v = bit_storages[depth][offset];
if (v == 0) {
// If depth is bigger than zero, then a previous level indicated a block was
// free.
ASSERT(depth == 0);
return -1;
}
offset = offset * Common::BitSize<u64>() + std::countr_zero(v);
++depth;
} while (depth < static_cast<s32>(used_depths));
}
return static_cast<s64>(offset);
}
void SetBit(std::size_t offset) {
this->SetBit(this->GetHighestDepthIndex(), offset);
num_bits++;
}
void ClearBit(std::size_t offset) {
this->ClearBit(this->GetHighestDepthIndex(), offset);
num_bits--;
}
bool ClearRange(std::size_t offset, std::size_t count) {
s32 depth = this->GetHighestDepthIndex();
u64* bits = bit_storages[depth];
std::size_t bit_ind = offset / Common::BitSize<u64>();
if (count < Common::BitSize<u64>()) {
const std::size_t shift = offset % Common::BitSize<u64>();
ASSERT(shift + count <= Common::BitSize<u64>());
// Check that all the bits are set.
const u64 mask = ((u64(1) << count) - 1) << shift;
u64 v = bits[bit_ind];
if ((v & mask) != mask) {
return false;
}
// Clear the bits.
v &= ~mask;
bits[bit_ind] = v;
if (v == 0) {
this->ClearBit(depth - 1, bit_ind);
}
} else {
ASSERT(offset % Common::BitSize<u64>() == 0);
ASSERT(count % Common::BitSize<u64>() == 0);
// Check that all the bits are set.
std::size_t remaining = count;
std::size_t i = 0;
do {
if (bits[bit_ind + i++] != ~u64(0)) {
return false;
}
remaining -= Common::BitSize<u64>();
} while (remaining > 0);
// Clear the bits.
remaining = count;
i = 0;
do {
bits[bit_ind + i] = 0;
this->ClearBit(depth - 1, bit_ind + i);
i++;
remaining -= Common::BitSize<u64>();
} while (remaining > 0);
}
num_bits -= count;
return true;
}
private:
void SetBit(s32 depth, std::size_t offset) {
while (depth >= 0) {
std::size_t ind = offset / Common::BitSize<u64>();
std::size_t which = offset % Common::BitSize<u64>();
const u64 mask = u64(1) << which;
u64* bit = std::addressof(bit_storages[depth][ind]);
u64 v = *bit;
ASSERT((v & mask) == 0);
*bit = v | mask;
if (v) {
break;
}
offset = ind;
depth--;
}
}
void ClearBit(s32 depth, std::size_t offset) {
while (depth >= 0) {
std::size_t ind = offset / Common::BitSize<u64>();
std::size_t which = offset % Common::BitSize<u64>();
const u64 mask = u64(1) << which;
u64* bit = std::addressof(bit_storages[depth][ind]);
u64 v = *bit;
ASSERT((v & mask) != 0);
v &= ~mask;
*bit = v;
if (v) {
break;
}
offset = ind;
depth--;
}
}
private:
static constexpr s32 GetRequiredDepth(std::size_t region_size) {
s32 depth = 0;
while (true) {
region_size /= Common::BitSize<u64>();
depth++;
if (region_size == 0) {
return depth;
}
}
}
public:
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
std::size_t overhead_bits = 0;
for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) {
region_size =
Common::AlignUp(region_size, Common::BitSize<u64>()) / Common::BitSize<u64>();
overhead_bits += region_size;
}
return overhead_bits * sizeof(u64);
}
};
} // namespace Kernel

View File

@@ -2,16 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#include "core/core.h"
#include "core/hle/kernel/memory/page_heap.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/memory.h"
namespace Kernel::Memory {
namespace Kernel {
void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
// Check our assumptions
ASSERT(Common::IsAligned((address), PageSize));
ASSERT(Common::IsAligned(size, PageSize));
@@ -32,11 +29,11 @@ void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_
}
}
VAddr PageHeap::AllocateBlock(s32 index) {
VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
const std::size_t needed_size{blocks[index].GetSize()};
for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) {
if (const VAddr addr{blocks[i].PopBlock()}; addr) {
if (const VAddr addr{blocks[i].PopBlock(random)}; addr) {
if (const std::size_t allocated_size{blocks[i].GetSize()};
allocated_size > needed_size) {
Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
@@ -48,13 +45,13 @@ VAddr PageHeap::AllocateBlock(s32 index) {
return 0;
}
void PageHeap::FreeBlock(VAddr block, s32 index) {
void KPageHeap::FreeBlock(VAddr block, s32 index) {
do {
block = blocks[index++].PushBlock(block);
} while (block != 0);
}
void PageHeap::Free(VAddr addr, std::size_t num_pages) {
void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
// Freeing no pages is a no-op
if (num_pages == 0) {
return;
@@ -104,16 +101,16 @@ void PageHeap::Free(VAddr addr, std::size_t num_pages) {
}
}
std::size_t PageHeap::CalculateMetadataOverheadSize(std::size_t region_size) {
std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) {
std::size_t overhead_size = 0;
for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
const std::size_t next_block_shift{
(i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
overhead_size += PageHeap::Block::CalculateMetadataOverheadSize(
overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
region_size, cur_block_shift, next_block_shift);
}
return Common::AlignUp(overhead_size, PageSize);
}
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -0,0 +1,193 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <bit>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_page_bitmap.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
class KPageHeap final : NonCopyable {
public:
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
const auto target_pages{std::max(num_pages, align_pages)};
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <=
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
return -1;
}
static constexpr s32 GetBlockIndex(std::size_t num_pages) {
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
return -1;
}
static constexpr std::size_t GetBlockSize(std::size_t index) {
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
}
static constexpr std::size_t GetBlockNumPages(std::size_t index) {
return GetBlockSize(index) / PageSize;
}
private:
static constexpr std::size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
class Block final : NonCopyable {
private:
KPageBitmap bitmap;
VAddr heap_address{};
uintptr_t end_offset{};
std::size_t block_shift{};
std::size_t next_block_shift{};
public:
Block() = default;
constexpr std::size_t GetShift() const {
return block_shift;
}
constexpr std::size_t GetNextShift() const {
return next_block_shift;
}
constexpr std::size_t GetSize() const {
return static_cast<std::size_t>(1) << GetShift();
}
constexpr std::size_t GetNumPages() const {
return GetSize() / PageSize;
}
constexpr std::size_t GetNumFreeBlocks() const {
return bitmap.GetNumBits();
}
constexpr std::size_t GetNumFreePages() const {
return GetNumFreeBlocks() * GetNumPages();
}
u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
u64* bit_storage) {
// Set shifts
block_shift = bs;
next_block_shift = nbs;
// Align up the address
VAddr end{addr + size};
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
: (1ULL << block_shift)};
addr = Common::AlignDown((addr), align);
end = Common::AlignUp((end), align);
heap_address = addr;
end_offset = (end - addr) / (1ULL << block_shift);
return bitmap.Initialize(bit_storage, end_offset);
}
VAddr PushBlock(VAddr address) {
// Set the bit for the free block
std::size_t offset{(address - heap_address) >> GetShift()};
bitmap.SetBit(offset);
// If we have a next shift, try to clear the blocks below and return the address
if (GetNextShift()) {
const auto diff{1ULL << (GetNextShift() - GetShift())};
offset = Common::AlignDown(offset, diff);
if (bitmap.ClearRange(offset, diff)) {
return heap_address + (offset << GetShift());
}
}
// We couldn't coalesce, or we're already as big as possible
return 0;
}
VAddr PopBlock(bool random) {
// Find a free block
const s64 soffset{bitmap.FindFreeBlock(random)};
if (soffset < 0) {
return 0;
}
const auto offset{static_cast<std::size_t>(soffset)};
// Update our tracking and return it
bitmap.ClearBit(offset);
return heap_address + (offset << GetShift());
}
public:
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
std::size_t cur_block_shift,
std::size_t next_block_shift) {
const auto cur_block_size{(1ULL << cur_block_shift)};
const auto next_block_size{(1ULL << next_block_shift)};
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
return KPageBitmap::CalculateManagementOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
};
public:
KPageHeap() = default;
constexpr VAddr GetAddress() const {
return heap_address;
}
constexpr std::size_t GetSize() const {
return heap_size;
}
constexpr VAddr GetEndAddress() const {
return GetAddress() + GetSize();
}
constexpr std::size_t GetPageOffset(VAddr block) const {
return (block - GetAddress()) / PageSize;
}
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
VAddr AllocateBlock(s32 index, bool random);
void Free(VAddr addr, std::size_t num_pages);
void UpdateUsedSize() {
used_size = heap_size - (GetNumFreePages() * PageSize);
}
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
private:
constexpr std::size_t GetNumFreePages() const {
std::size_t num_free{};
for (const auto& block : blocks) {
num_free += block.GetNumFreePages();
}
return num_free;
}
void FreeBlock(VAddr block, s32 index);
VAddr heap_address{};
std::size_t heap_size{};
std::size_t used_size{};
std::array<Block, NumMemoryBlockPageShifts> blocks{};
std::vector<u64> metadata;
};
} // namespace Kernel

View File

@@ -8,12 +8,12 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/result.h"
namespace Kernel::Memory {
namespace Kernel {
class PageLinkedList final {
class KPageLinkedList final {
public:
class Node final {
public:
@@ -33,8 +33,8 @@ public:
};
public:
PageLinkedList() = default;
PageLinkedList(u64 address, u64 num_pages) {
KPageLinkedList() = default;
KPageLinkedList(u64 address, u64 num_pages) {
ASSERT(AddBlock(address, num_pages).IsSuccess());
}
@@ -54,7 +54,7 @@ public:
return num_pages;
}
bool IsEqual(PageLinkedList& other) const {
bool IsEqual(KPageLinkedList& other) const {
auto this_node = nodes.begin();
auto other_node = other.nodes.begin();
while (this_node != nodes.end() && other_node != other.nodes.end()) {
@@ -89,4 +89,4 @@ private:
std::list<Node> nodes;
};
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -10,27 +10,27 @@
#include "common/common_types.h"
#include "common/page_table.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel::Memory {
namespace Kernel {
class MemoryBlockManager;
class KMemoryBlockManager;
class PageTable final : NonCopyable {
class KPageTable final : NonCopyable {
public:
explicit PageTable(Core::System& system);
explicit KPageTable(Core::System& system);
ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
VAddr code_addr, std::size_t code_size,
Memory::MemoryManager::Pool pool);
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, MemoryState state,
MemoryPermission perm);
KMemoryManager::Pool pool);
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
KMemoryPermission perm);
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
@@ -38,20 +38,20 @@ public:
ResultCode UnmapMemory(VAddr addr, std::size_t size);
ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode MapPages(VAddr addr, PageLinkedList& page_linked_list, MemoryState state,
MemoryPermission perm);
ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, MemoryPermission perm);
MemoryInfo QueryInfo(VAddr addr);
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, MemoryPermission perm);
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
KMemoryPermission perm);
ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
KMemoryInfo QueryInfo(VAddr addr);
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, MemoryAttribute mask,
MemoryAttribute value);
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
KMemoryAttribute value);
ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
ResultVal<VAddr> SetHeapSize(std::size_t size);
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
bool is_map_only, VAddr region_start,
std::size_t region_num_pages, MemoryState state,
MemoryPermission perm, PAddr map_addr = 0);
std::size_t region_num_pages, KMemoryState state,
KMemoryPermission perm, PAddr map_addr = 0);
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
@@ -72,47 +72,49 @@ private:
ChangePermissionsAndRefresh,
};
static constexpr MemoryAttribute DefaultMemoryIgnoreAttr =
MemoryAttribute::DontCareMask | MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared;
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = KMemoryAttribute::DontCareMask |
KMemoryAttribute::IpcLocked |
KMemoryAttribute::DeviceShared;
ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
ResultCode MapPages(VAddr addr, const PageLinkedList& page_linked_list, MemoryPermission perm);
void MapPhysicalMemory(PageLinkedList& page_linked_list, VAddr start, VAddr end);
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
KMemoryPermission perm);
void MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end);
bool IsRegionMapped(VAddr address, u64 size);
bool IsRegionContiguous(VAddr addr, u64 size) const;
void AddRegionToPages(VAddr start, std::size_t num_pages, PageLinkedList& page_linked_list);
MemoryInfo QueryInfoImpl(VAddr addr);
void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list);
KMemoryInfo QueryInfoImpl(VAddr addr);
VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages,
std::size_t align);
ResultCode Operate(VAddr addr, std::size_t num_pages, const PageLinkedList& page_group,
ResultCode Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
OperationType operation);
ResultCode Operate(VAddr addr, std::size_t num_pages, MemoryPermission perm,
ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
OperationType operation, PAddr map_addr = 0);
constexpr VAddr GetRegionAddress(MemoryState state) const;
constexpr std::size_t GetRegionSize(MemoryState state) const;
constexpr bool CanContain(VAddr addr, std::size_t size, MemoryState state) const;
constexpr VAddr GetRegionAddress(KMemoryState state) const;
constexpr std::size_t GetRegionSize(KMemoryState state) const;
constexpr bool CanContain(VAddr addr, std::size_t size, KMemoryState state) const;
constexpr ResultCode CheckMemoryState(const MemoryInfo& info, MemoryState state_mask,
MemoryState state, MemoryPermission perm_mask,
MemoryPermission perm, MemoryAttribute attr_mask,
MemoryAttribute attr) const;
ResultCode CheckMemoryState(MemoryState* out_state, MemoryPermission* out_perm,
MemoryAttribute* out_attr, VAddr addr, std::size_t size,
MemoryState state_mask, MemoryState state,
MemoryPermission perm_mask, MemoryPermission perm,
MemoryAttribute attr_mask, MemoryAttribute attr,
MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
ResultCode CheckMemoryState(VAddr addr, std::size_t size, MemoryState state_mask,
MemoryState state, MemoryPermission perm_mask,
MemoryPermission perm, MemoryAttribute attr_mask,
MemoryAttribute attr,
MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const;
ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr, ignore_attr);
}
std::recursive_mutex page_table_lock;
std::unique_ptr<MemoryBlockManager> block_manager;
std::unique_ptr<KMemoryBlockManager> block_manager;
public:
constexpr VAddr GetAddressSpaceStart() const {
@@ -212,7 +214,7 @@ public:
return !IsOutsideASLRRegion(address, size);
}
constexpr PAddr GetPhysicalAddr(VAddr addr) {
return page_table_impl.backing_addr[addr >> Memory::PageBits] + addr;
return page_table_impl.backing_addr[addr >> PageBits] + addr;
}
private:
@@ -267,11 +269,11 @@ private:
bool is_kernel{};
bool is_aslr_enabled{};
MemoryManager::Pool memory_pool{MemoryManager::Pool::Application};
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
Common::PageTable page_table_impl;
Core::System& system;
};
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -734,7 +734,7 @@ void KScheduler::ScheduleImpl() {
}
guard.unlock();
Common::Fiber::YieldTo(*old_context, switch_fiber);
Common::Fiber::YieldTo(*old_context, *switch_fiber);
/// When a thread wakes up, the scheduler may have changed to other in another core.
auto& next_scheduler = *system.Kernel().CurrentScheduler();
next_scheduler.SwitchContextStep2();
@@ -769,13 +769,8 @@ void KScheduler::SwitchToCurrent() {
break;
}
}
std::shared_ptr<Common::Fiber>* next_context;
if (next_thread != nullptr) {
next_context = &next_thread->GetHostContext();
} else {
next_context = &idle_thread->GetHostContext();
}
Common::Fiber::YieldTo(switch_fiber, *next_context);
auto thread = next_thread ? next_thread : idle_thread;
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
} while (!is_switch_pending());
}
}
@@ -800,9 +795,9 @@ void KScheduler::Initialize() {
std::string name = "Idle Thread Id:" + std::to_string(core_id);
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
nullptr, std::move(init_func), init_func_parameter);
auto thread_res = KThread::CreateThread(
system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0,
static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);
idle_thread = thread_res.Unwrap().get();
}

View File

@@ -4,33 +4,32 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/shared_memory.h"
namespace Kernel {
SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
KSharedMemory::KSharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
: Object{kernel}, device_memory{device_memory} {}
SharedMemory::~SharedMemory() {
KSharedMemory::~KSharedMemory() {
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
}
std::shared_ptr<SharedMemory> SharedMemory::Create(
std::shared_ptr<KSharedMemory> KSharedMemory::Create(
KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission,
Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
std::string name) {
KPageLinkedList&& page_list, KMemoryPermission owner_permission,
KMemoryPermission user_permission, PAddr physical_address, std::size_t size, std::string name) {
const auto resource_limit = kernel.GetSystemResourceLimit();
KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
size);
ASSERT(memory_reservation.Succeeded());
std::shared_ptr<SharedMemory> shared_memory{
std::make_shared<SharedMemory>(kernel, device_memory)};
std::shared_ptr<KSharedMemory> shared_memory{
std::make_shared<KSharedMemory>(kernel, device_memory)};
shared_memory->owner_process = owner_process;
shared_memory->page_list = std::move(page_list);
@@ -44,22 +43,22 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
return shared_memory;
}
ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
Memory::MemoryPermission permissions) {
const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize};
ResultCode KSharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
KMemoryPermission permissions) {
const u64 page_count{(size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
UNIMPLEMENTED_MSG("Page count does not match");
}
const Memory::MemoryPermission expected =
const KMemoryPermission expected =
&target_process == owner_process ? owner_permission : user_permission;
if (permissions != expected) {
UNIMPLEMENTED_MSG("Permission does not match");
}
return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared,
return target_process.PageTable().MapPages(address, page_list, KMemoryState::Shared,
permissions);
}

View File

@@ -9,8 +9,8 @@
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/page_linked_list.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
@@ -19,15 +19,15 @@ namespace Kernel {
class KernelCore;
class SharedMemory final : public Object {
class KSharedMemory final : public Object {
public:
explicit SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);
~SharedMemory() override;
explicit KSharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);
~KSharedMemory() override;
static std::shared_ptr<SharedMemory> Create(
static std::shared_ptr<KSharedMemory> Create(
KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission,
Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
KPageLinkedList&& page_list, KMemoryPermission owner_permission,
KMemoryPermission user_permission, PAddr physical_address, std::size_t size,
std::string name);
std::string GetTypeName() const override {
@@ -51,7 +51,7 @@ public:
* @param permissions Memory block map permissions (specified by SVC field)
*/
ResultCode Map(Process& target_process, VAddr address, std::size_t size,
Memory::MemoryPermission permissions);
KMemoryPermission permissions);
/**
* Gets a pointer to the shared memory block
@@ -76,9 +76,9 @@ public:
private:
Core::DeviceMemory& device_memory;
Process* owner_process{};
Memory::PageLinkedList page_list;
Memory::MemoryPermission owner_permission{};
Memory::MemoryPermission user_permission{};
KPageLinkedList page_list;
KMemoryPermission owner_permission{};
KMemoryPermission user_permission{};
PAddr physical_address{};
std::size_t size{};
std::string name;

View File

@@ -2,9 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include <atomic>
@@ -12,17 +9,17 @@
#include "common/assert.h"
#include "common/common_types.h"
namespace Kernel::Memory {
namespace Kernel {
namespace impl {
class SlabHeapImpl final : NonCopyable {
class KSlabHeapImpl final : NonCopyable {
public:
struct Node {
Node* next{};
};
constexpr SlabHeapImpl() = default;
constexpr KSlabHeapImpl() = default;
void Initialize(std::size_t size) {
ASSERT(head == nullptr);
@@ -65,9 +62,9 @@ private:
} // namespace impl
class SlabHeapBase : NonCopyable {
class KSlabHeapBase : NonCopyable {
public:
constexpr SlabHeapBase() = default;
constexpr KSlabHeapBase() = default;
constexpr bool Contains(uintptr_t addr) const {
return start <= addr && addr < end;
@@ -126,7 +123,7 @@ public:
}
private:
using Impl = impl::SlabHeapImpl;
using Impl = impl::KSlabHeapImpl;
Impl impl;
uintptr_t peak{};
@@ -135,9 +132,9 @@ private:
};
template <typename T>
class SlabHeap final : public SlabHeapBase {
class KSlabHeap final : public KSlabHeapBase {
public:
constexpr SlabHeap() : SlabHeapBase() {}
constexpr KSlabHeap() : KSlabHeapBase() {}
void Initialize(void* memory, std::size_t memory_size) {
InitializeImpl(sizeof(T), memory, memory_size);
@@ -160,4 +157,4 @@ public:
}
};
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -0,0 +1,54 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_spin_lock.h"
#if _MSC_VER
#include <intrin.h>
#if _M_AMD64
#define __x86_64__ 1
#endif
#if _M_ARM64
#define __aarch64__ 1
#endif
#else
#if __x86_64__
#include <xmmintrin.h>
#endif
#endif
namespace {
void ThreadPause() {
#if __x86_64__
_mm_pause();
#elif __aarch64__ && _MSC_VER
__yield();
#elif __aarch64__
asm("yield");
#endif
}
} // namespace
namespace Kernel {
void KSpinLock::Lock() {
while (lck.test_and_set(std::memory_order_acquire)) {
ThreadPause();
}
}
void KSpinLock::Unlock() {
lck.clear(std::memory_order_release);
}
bool KSpinLock::TryLock() {
if (lck.test_and_set(std::memory_order_acquire)) {
return false;
}
return true;
}
} // namespace Kernel

View File

@@ -0,0 +1,33 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include "core/hle/kernel/k_scoped_lock.h"
namespace Kernel {
class KSpinLock {
public:
KSpinLock() = default;
KSpinLock(const KSpinLock&) = delete;
KSpinLock& operator=(const KSpinLock&) = delete;
KSpinLock(KSpinLock&&) = delete;
KSpinLock& operator=(KSpinLock&&) = delete;
void Lock();
void Unlock();
[[nodiscard]] bool TryLock();
private:
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
using KScopedSpinLock = KScopedLock<KSpinLock>;
} // namespace Kernel

View File

@@ -1,12 +1,13 @@
// Copyright 2020 yuzu Emulator Project
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <random>
#include "core/hle/kernel/memory/system_control.h"
#include "core/hle/kernel/k_system_control.h"
namespace Kernel {
namespace Kernel::Memory::SystemControl {
namespace {
template <typename F>
u64 GenerateUniformRange(u64 min, u64 max, F f) {
@@ -25,16 +26,17 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
}
}
u64 GenerateRandomU64ForInit() {
} // Anonymous namespace
u64 KSystemControl::GenerateRandomU64() {
static std::random_device device;
static std::mt19937 gen(device());
static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return distribution(gen);
}
} // Anonymous namespace
u64 GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64);
}
} // namespace Kernel::Memory::SystemControl
} // namespace Kernel

View File

@@ -0,0 +1,19 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Kernel {
class KSystemControl {
public:
KSystemControl() = default;
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
};
} // namespace Kernel

View File

@@ -20,13 +20,13 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
@@ -782,7 +782,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
}
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters++) >= 0);
}
@@ -795,7 +795,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
}
@@ -870,7 +870,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
KThread* thread = std::addressof(*it);
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
}
it = waiter_list.erase(it);
@@ -995,22 +995,11 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
return host_context;
}
ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, Process* owner_process) {
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
owner_process, std::move(init_func), init_func_parameter);
}
ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags,
std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func,
void* thread_start_parameter) {
ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(Core::System& system,
ThreadType type_flags, std::string name,
VAddr entry_point, u32 priority, u64 arg,
s32 processor_id, VAddr stack_top,
Process* owner_process) {
auto& kernel = system.Kernel();
std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel);
@@ -1027,12 +1016,35 @@ ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, Thread
auto& scheduler = kernel.GlobalSchedulerContext();
scheduler.AddThread(thread);
thread->host_context =
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
return MakeResult<std::shared_ptr<KThread>>(std::move(thread));
}
ResultVal<std::shared_ptr<KThread>> KThread::CreateThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter) {
auto thread_result = CreateThread(system, type_flags, name, entry_point, priority, arg,
processor_id, stack_top, owner_process);
if (thread_result.Succeeded()) {
(*thread_result)->host_context =
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
}
return thread_result;
}
ResultVal<std::shared_ptr<KThread>> KThread::CreateUserThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process) {
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
return CreateThread(system, type_flags, name, entry_point, priority, arg, processor_id,
stack_top, owner_process, std::move(init_func), init_func_parameter);
}
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
return kernel.GetCurrentEmuThread();
}

View File

@@ -116,7 +116,7 @@ public:
using WaiterList = boost::intrusive::list<KThread>;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* Creates and returns a new thread.
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
@@ -127,12 +127,12 @@ public:
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* Creates and returns a new thread, with a specified entry point.
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
@@ -145,11 +145,27 @@ public:
* @param thread_start_parameter The parameter which will passed to host context on init
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
/**
* Creates and returns a new thread for the emulated "user" process.
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> CreateUserThread(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
[[nodiscard]] std::string GetName() const override {
return name;
}

View File

@@ -27,17 +27,17 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/service_thread.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/lock.h"
@@ -181,9 +181,9 @@ struct KernelCore::Impl {
std::string name = "Suspend Thread Id:" + std::to_string(i);
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0,
0, 0, static_cast<u32>(i), 0, nullptr,
std::move(init_func), init_func_parameter);
auto thread_res = KThread::CreateThread(
system, ThreadType::HighPriority, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
nullptr, std::move(init_func), init_func_parameter);
suspend_threads[i] = std::move(thread_res).Unwrap();
}
@@ -221,10 +221,9 @@ struct KernelCore::Impl {
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
const thread_local auto thread =
KThread::Create(
KThread::CreateThread(
system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr,
[]([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr)
.Unwrap();
return thread.get();
}
@@ -271,7 +270,7 @@ struct KernelCore::Impl {
void InitializeMemoryLayout() {
// Initialize memory layout
constexpr Memory::MemoryLayout layout{Memory::MemoryLayout::GetDefaultLayout()};
constexpr KMemoryLayout layout{KMemoryLayout::GetDefaultLayout()};
constexpr std::size_t hid_size{0x40000};
constexpr std::size_t font_size{0x1100000};
constexpr std::size_t irs_size{0x8000};
@@ -282,36 +281,36 @@ struct KernelCore::Impl {
constexpr PAddr time_addr{layout.System().StartAddress() + hid_size + font_size + irs_size};
// Initialize memory manager
memory_manager = std::make_unique<Memory::MemoryManager>();
memory_manager->InitializeManager(Memory::MemoryManager::Pool::Application,
memory_manager = std::make_unique<KMemoryManager>();
memory_manager->InitializeManager(KMemoryManager::Pool::Application,
layout.Application().StartAddress(),
layout.Application().EndAddress());
memory_manager->InitializeManager(Memory::MemoryManager::Pool::Applet,
memory_manager->InitializeManager(KMemoryManager::Pool::Applet,
layout.Applet().StartAddress(),
layout.Applet().EndAddress());
memory_manager->InitializeManager(Memory::MemoryManager::Pool::System,
memory_manager->InitializeManager(KMemoryManager::Pool::System,
layout.System().StartAddress(),
layout.System().EndAddress());
hid_shared_mem = Kernel::SharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr,
{hid_addr, hid_size / Memory::PageSize}, Memory::MemoryPermission::None,
Memory::MemoryPermission::Read, hid_addr, hid_size, "HID:SharedMemory");
font_shared_mem = Kernel::SharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr,
{font_pa, font_size / Memory::PageSize}, Memory::MemoryPermission::None,
Memory::MemoryPermission::Read, font_pa, font_size, "Font:SharedMemory");
irs_shared_mem = Kernel::SharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr,
{irs_addr, irs_size / Memory::PageSize}, Memory::MemoryPermission::None,
Memory::MemoryPermission::Read, irs_addr, irs_size, "IRS:SharedMemory");
time_shared_mem = Kernel::SharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr,
{time_addr, time_size / Memory::PageSize}, Memory::MemoryPermission::None,
Memory::MemoryPermission::Read, time_addr, time_size, "Time:SharedMemory");
hid_shared_mem = Kernel::KSharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr, {hid_addr, hid_size / PageSize},
KMemoryPermission::None, KMemoryPermission::Read, hid_addr, hid_size,
"HID:SharedMemory");
font_shared_mem = Kernel::KSharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr, {font_pa, font_size / PageSize},
KMemoryPermission::None, KMemoryPermission::Read, font_pa, font_size,
"Font:SharedMemory");
irs_shared_mem = Kernel::KSharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr, {irs_addr, irs_size / PageSize},
KMemoryPermission::None, KMemoryPermission::Read, irs_addr, irs_size,
"IRS:SharedMemory");
time_shared_mem = Kernel::KSharedMemory::Create(
system.Kernel(), system.DeviceMemory(), nullptr, {time_addr, time_size / PageSize},
KMemoryPermission::None, KMemoryPermission::Read, time_addr, time_size,
"Time:SharedMemory");
// Allocate slab heaps
user_slab_heap_pages = std::make_unique<Memory::SlabHeap<Memory::Page>>();
user_slab_heap_pages = std::make_unique<KSlabHeap<Page>>();
constexpr u64 user_slab_heap_size{0x1ef000};
// Reserve slab heaps
@@ -353,14 +352,14 @@ struct KernelCore::Impl {
std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
// Kernel memory management
std::unique_ptr<Memory::MemoryManager> memory_manager;
std::unique_ptr<Memory::SlabHeap<Memory::Page>> user_slab_heap_pages;
std::unique_ptr<KMemoryManager> memory_manager;
std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
// Shared memory for services
std::shared_ptr<Kernel::SharedMemory> hid_shared_mem;
std::shared_ptr<Kernel::SharedMemory> font_shared_mem;
std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
std::shared_ptr<Kernel::KSharedMemory> hid_shared_mem;
std::shared_ptr<Kernel::KSharedMemory> font_shared_mem;
std::shared_ptr<Kernel::KSharedMemory> irs_shared_mem;
std::shared_ptr<Kernel::KSharedMemory> time_shared_mem;
// Threads used for services
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
@@ -578,51 +577,51 @@ KThread* KernelCore::GetCurrentEmuThread() const {
return impl->GetCurrentEmuThread();
}
Memory::MemoryManager& KernelCore::MemoryManager() {
KMemoryManager& KernelCore::MemoryManager() {
return *impl->memory_manager;
}
const Memory::MemoryManager& KernelCore::MemoryManager() const {
const KMemoryManager& KernelCore::MemoryManager() const {
return *impl->memory_manager;
}
Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() {
KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
return *impl->user_slab_heap_pages;
}
const Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() const {
const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
return *impl->user_slab_heap_pages;
}
Kernel::SharedMemory& KernelCore::GetHidSharedMem() {
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
return *impl->hid_shared_mem;
}
const Kernel::SharedMemory& KernelCore::GetHidSharedMem() const {
const Kernel::KSharedMemory& KernelCore::GetHidSharedMem() const {
return *impl->hid_shared_mem;
}
Kernel::SharedMemory& KernelCore::GetFontSharedMem() {
Kernel::KSharedMemory& KernelCore::GetFontSharedMem() {
return *impl->font_shared_mem;
}
const Kernel::SharedMemory& KernelCore::GetFontSharedMem() const {
const Kernel::KSharedMemory& KernelCore::GetFontSharedMem() const {
return *impl->font_shared_mem;
}
Kernel::SharedMemory& KernelCore::GetIrsSharedMem() {
Kernel::KSharedMemory& KernelCore::GetIrsSharedMem() {
return *impl->irs_shared_mem;
}
const Kernel::SharedMemory& KernelCore::GetIrsSharedMem() const {
const Kernel::KSharedMemory& KernelCore::GetIrsSharedMem() const {
return *impl->irs_shared_mem;
}
Kernel::SharedMemory& KernelCore::GetTimeSharedMem() {
Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() {
return *impl->time_shared_mem;
}
const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const {
const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
return *impl->time_shared_mem;
}

View File

@@ -11,7 +11,7 @@
#include <vector>
#include "core/arm/cpu_interrupt_handler.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/object.h"
namespace Core {
@@ -27,25 +27,23 @@ struct EventType;
namespace Kernel {
namespace Memory {
class MemoryManager;
template <typename T>
class SlabHeap;
} // namespace Memory
class ClientPort;
class GlobalSchedulerContext;
class HandleTable;
class PhysicalCore;
class Process;
class KMemoryManager;
class KResourceLimit;
class KScheduler;
class SharedMemory;
class KSharedMemory;
class KThread;
class PhysicalCore;
class Process;
class ServiceThread;
class Synchronization;
class KThread;
class TimeManager;
template <typename T>
class KSlabHeap;
using EmuThreadHandle = uintptr_t;
constexpr EmuThreadHandle EmuThreadHandleInvalid{};
constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
@@ -178,40 +176,40 @@ public:
void RegisterHostThread();
/// Gets the virtual memory manager for the kernel.
Memory::MemoryManager& MemoryManager();
KMemoryManager& MemoryManager();
/// Gets the virtual memory manager for the kernel.
const Memory::MemoryManager& MemoryManager() const;
const KMemoryManager& MemoryManager() const;
/// Gets the slab heap allocated for user space pages.
Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages();
KSlabHeap<Page>& GetUserSlabHeapPages();
/// Gets the slab heap allocated for user space pages.
const Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages() const;
const KSlabHeap<Page>& GetUserSlabHeapPages() const;
/// Gets the shared memory object for HID services.
Kernel::SharedMemory& GetHidSharedMem();
Kernel::KSharedMemory& GetHidSharedMem();
/// Gets the shared memory object for HID services.
const Kernel::SharedMemory& GetHidSharedMem() const;
const Kernel::KSharedMemory& GetHidSharedMem() const;
/// Gets the shared memory object for font services.
Kernel::SharedMemory& GetFontSharedMem();
Kernel::KSharedMemory& GetFontSharedMem();
/// Gets the shared memory object for font services.
const Kernel::SharedMemory& GetFontSharedMem() const;
const Kernel::KSharedMemory& GetFontSharedMem() const;
/// Gets the shared memory object for IRS services.
Kernel::SharedMemory& GetIrsSharedMem();
Kernel::KSharedMemory& GetIrsSharedMem();
/// Gets the shared memory object for IRS services.
const Kernel::SharedMemory& GetIrsSharedMem() const;
const Kernel::KSharedMemory& GetIrsSharedMem() const;
/// Gets the shared memory object for Time services.
Kernel::SharedMemory& GetTimeSharedMem();
Kernel::KSharedMemory& GetTimeSharedMem();
/// Gets the shared memory object for Time services.
const Kernel::SharedMemory& GetTimeSharedMem() const;
const Kernel::KSharedMemory& GetTimeSharedMem() const;
/// Suspend/unsuspend the OS.
void Suspend(bool in_suspention);

View File

@@ -1,370 +0,0 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include <array>
#include <bit>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory/memory_types.h"
namespace Kernel::Memory {
class PageHeap final : NonCopyable {
public:
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
const auto target_pages{std::max(num_pages, align_pages)};
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <=
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
return -1;
}
static constexpr s32 GetBlockIndex(std::size_t num_pages) {
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
return -1;
}
static constexpr std::size_t GetBlockSize(std::size_t index) {
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
}
static constexpr std::size_t GetBlockNumPages(std::size_t index) {
return GetBlockSize(index) / PageSize;
}
private:
static constexpr std::size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
class Block final : NonCopyable {
private:
class Bitmap final : NonCopyable {
public:
static constexpr std::size_t MaxDepth{4};
private:
std::array<u64*, MaxDepth> bit_storages{};
std::size_t num_bits{};
std::size_t used_depths{};
public:
constexpr Bitmap() = default;
constexpr std::size_t GetNumBits() const {
return num_bits;
}
constexpr s32 GetHighestDepthIndex() const {
return static_cast<s32>(used_depths) - 1;
}
constexpr u64* Initialize(u64* storage, std::size_t size) {
//* Initially, everything is un-set
num_bits = 0;
// Calculate the needed bitmap depth
used_depths = static_cast<std::size_t>(GetRequiredDepth(size));
ASSERT(used_depths <= MaxDepth);
// Set the bitmap pointers
for (s32 depth{GetHighestDepthIndex()}; depth >= 0; depth--) {
bit_storages[depth] = storage;
size = Common::AlignUp(size, 64) / 64;
storage += size;
}
return storage;
}
s64 FindFreeBlock() const {
uintptr_t offset{};
s32 depth{};
do {
const u64 v{bit_storages[depth][offset]};
if (v == 0) {
// Non-zero depth indicates that a previous level had a free block
ASSERT(depth == 0);
return -1;
}
offset = offset * 64 + static_cast<u32>(std::countr_zero(v));
++depth;
} while (depth < static_cast<s32>(used_depths));
return static_cast<s64>(offset);
}
constexpr void SetBit(std::size_t offset) {
SetBit(GetHighestDepthIndex(), offset);
num_bits++;
}
constexpr void ClearBit(std::size_t offset) {
ClearBit(GetHighestDepthIndex(), offset);
num_bits--;
}
constexpr bool ClearRange(std::size_t offset, std::size_t count) {
const s32 depth{GetHighestDepthIndex()};
const auto bit_ind{offset / 64};
u64* bits{bit_storages[depth]};
if (count < 64) {
const auto shift{offset % 64};
ASSERT(shift + count <= 64);
// Check that all the bits are set
const u64 mask{((1ULL << count) - 1) << shift};
u64 v{bits[bit_ind]};
if ((v & mask) != mask) {
return false;
}
// Clear the bits
v &= ~mask;
bits[bit_ind] = v;
if (v == 0) {
ClearBit(depth - 1, bit_ind);
}
} else {
ASSERT(offset % 64 == 0);
ASSERT(count % 64 == 0);
// Check that all the bits are set
std::size_t remaining{count};
std::size_t i = 0;
do {
if (bits[bit_ind + i++] != ~u64(0)) {
return false;
}
remaining -= 64;
} while (remaining > 0);
// Clear the bits
remaining = count;
i = 0;
do {
bits[bit_ind + i] = 0;
ClearBit(depth - 1, bit_ind + i);
i++;
remaining -= 64;
} while (remaining > 0);
}
num_bits -= count;
return true;
}
private:
constexpr void SetBit(s32 depth, std::size_t offset) {
while (depth >= 0) {
const auto ind{offset / 64};
const auto which{offset % 64};
const u64 mask{1ULL << which};
u64* bit{std::addressof(bit_storages[depth][ind])};
const u64 v{*bit};
ASSERT((v & mask) == 0);
*bit = v | mask;
if (v) {
break;
}
offset = ind;
depth--;
}
}
constexpr void ClearBit(s32 depth, std::size_t offset) {
while (depth >= 0) {
const auto ind{offset / 64};
const auto which{offset % 64};
const u64 mask{1ULL << which};
u64* bit{std::addressof(bit_storages[depth][ind])};
u64 v{*bit};
ASSERT((v & mask) != 0);
v &= ~mask;
*bit = v;
if (v) {
break;
}
offset = ind;
depth--;
}
}
private:
static constexpr s32 GetRequiredDepth(std::size_t region_size) {
s32 depth = 0;
while (true) {
region_size /= 64;
depth++;
if (region_size == 0) {
return depth;
}
}
}
public:
static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size) {
std::size_t overhead_bits = 0;
for (s32 depth{GetRequiredDepth(region_size) - 1}; depth >= 0; depth--) {
region_size = Common::AlignUp(region_size, 64) / 64;
overhead_bits += region_size;
}
return overhead_bits * sizeof(u64);
}
};
private:
Bitmap bitmap;
VAddr heap_address{};
uintptr_t end_offset{};
std::size_t block_shift{};
std::size_t next_block_shift{};
public:
constexpr Block() = default;
constexpr std::size_t GetShift() const {
return block_shift;
}
constexpr std::size_t GetNextShift() const {
return next_block_shift;
}
constexpr std::size_t GetSize() const {
return static_cast<std::size_t>(1) << GetShift();
}
constexpr std::size_t GetNumPages() const {
return GetSize() / PageSize;
}
constexpr std::size_t GetNumFreeBlocks() const {
return bitmap.GetNumBits();
}
constexpr std::size_t GetNumFreePages() const {
return GetNumFreeBlocks() * GetNumPages();
}
constexpr u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
u64* bit_storage) {
// Set shifts
block_shift = bs;
next_block_shift = nbs;
// Align up the address
VAddr end{addr + size};
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
: (1ULL << block_shift)};
addr = Common::AlignDown((addr), align);
end = Common::AlignUp((end), align);
heap_address = addr;
end_offset = (end - addr) / (1ULL << block_shift);
return bitmap.Initialize(bit_storage, end_offset);
}
constexpr VAddr PushBlock(VAddr address) {
// Set the bit for the free block
std::size_t offset{(address - heap_address) >> GetShift()};
bitmap.SetBit(offset);
// If we have a next shift, try to clear the blocks below and return the address
if (GetNextShift()) {
const auto diff{1ULL << (GetNextShift() - GetShift())};
offset = Common::AlignDown(offset, diff);
if (bitmap.ClearRange(offset, diff)) {
return heap_address + (offset << GetShift());
}
}
// We couldn't coalesce, or we're already as big as possible
return 0;
}
VAddr PopBlock() {
// Find a free block
const s64 soffset{bitmap.FindFreeBlock()};
if (soffset < 0) {
return 0;
}
const auto offset{static_cast<std::size_t>(soffset)};
// Update our tracking and return it
bitmap.ClearBit(offset);
return heap_address + (offset << GetShift());
}
public:
static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size,
std::size_t cur_block_shift,
std::size_t next_block_shift) {
const auto cur_block_size{(1ULL << cur_block_shift)};
const auto next_block_size{(1ULL << next_block_shift)};
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
return Bitmap::CalculateMetadataOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
};
public:
PageHeap() = default;
constexpr VAddr GetAddress() const {
return heap_address;
}
constexpr std::size_t GetSize() const {
return heap_size;
}
constexpr VAddr GetEndAddress() const {
return GetAddress() + GetSize();
}
constexpr std::size_t GetPageOffset(VAddr block) const {
return (block - GetAddress()) / PageSize;
}
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
VAddr AllocateBlock(s32 index);
void Free(VAddr addr, std::size_t num_pages);
void UpdateUsedSize() {
used_size = heap_size - (GetNumFreePages() * PageSize);
}
static std::size_t CalculateMetadataOverheadSize(std::size_t region_size);
private:
constexpr std::size_t GetNumFreePages() const {
std::size_t num_free{};
for (const auto& block : blocks) {
num_free += block.GetNumFreePages();
}
return num_free;
}
void FreeBlock(VAddr block, s32 index);
VAddr heap_address{};
std::size_t heap_size{};
std::size_t used_size{};
std::array<Block, NumMemoryBlockPageShifts> blocks{};
std::vector<u64> metadata;
};
} // namespace Kernel::Memory

View File

@@ -1,13 +0,0 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Kernel::Memory::SystemControl {
u64 GenerateRandomRange(u64 min, u64 max);
} // namespace Kernel::Memory::SystemControl

View File

@@ -8,11 +8,11 @@
#include "common/common_types.h"
namespace Kernel::Memory {
namespace Kernel {
constexpr std::size_t PageBits{12};
constexpr std::size_t PageSize{1 << PageBits};
using Page = std::array<u8, PageSize>;
} // namespace Kernel::Memory
} // namespace Kernel

View File

@@ -14,14 +14,14 @@
#include "core/device_memory.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block_manager.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/lock.h"
@@ -40,8 +40,9 @@ namespace {
void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
owner_process.GetIdealCoreId(), stack_top, &owner_process);
auto thread_res =
KThread::CreateUserThread(system, ThreadType::User, "main", entry_point, priority, 0,
owner_process.GetIdealCoreId(), stack_top, &owner_process);
std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
@@ -274,7 +275,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
// Set initial resource limits
resource_limit->SetLimitValue(
LimitableResource::PhysicalMemory,
kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application));
kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application));
KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
code_size + system_resource_size);
if (!memory_reservation.Succeeded()) {
@@ -285,15 +286,15 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
// Initialize proces address space
if (const ResultCode result{
page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000,
code_size, Memory::MemoryManager::Pool::Application)};
code_size, KMemoryManager::Pool::Application)};
result.IsError()) {
return result;
}
// Map process code region
if (const ResultCode result{page_table->MapProcessCode(
page_table->GetCodeRegionStart(), code_size / Memory::PageSize,
Memory::MemoryState::Code, Memory::MemoryPermission::None)};
if (const ResultCode result{page_table->MapProcessCode(page_table->GetCodeRegionStart(),
code_size / PageSize, KMemoryState::Code,
KMemoryPermission::None)};
result.IsError()) {
return result;
}
@@ -323,6 +324,11 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
UNREACHABLE();
}
// Set initial resource limits
resource_limit->SetLimitValue(
LimitableResource::PhysicalMemory,
kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application));
resource_limit->SetLimitValue(LimitableResource::Threads, 608);
resource_limit->SetLimitValue(LimitableResource::Events, 700);
resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128);
@@ -400,22 +406,22 @@ VAddr Process::CreateTLSRegion() {
return *tls_page_iter->ReserveSlot();
}
Memory::Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
ASSERT(tls_page_ptr);
const VAddr start{page_table->GetKernelMapRegionStart()};
const VAddr size{page_table->GetKernelMapRegionEnd() - start};
const PAddr tls_map_addr{system.DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
const VAddr tls_page_addr{
page_table
->AllocateAndMapMemory(1, Memory::PageSize, true, start, size / Memory::PageSize,
Memory::MemoryState::ThreadLocal,
Memory::MemoryPermission::ReadAndWrite, tls_map_addr)
.ValueOr(0)};
const VAddr tls_page_addr{page_table
->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
KMemoryState::ThreadLocal,
KMemoryPermission::ReadAndWrite,
tls_map_addr)
.ValueOr(0)};
ASSERT(tls_page_addr);
std::memset(tls_page_ptr, 0, Memory::PageSize);
std::memset(tls_page_ptr, 0, PageSize);
tls_pages.emplace_back(tls_page_addr);
const auto reserve_result{tls_pages.back().ReserveSlot()};
@@ -442,15 +448,15 @@ void Process::FreeTLSRegion(VAddr tls_address) {
void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
std::lock_guard lock{HLE::g_hle_lock};
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
Memory::MemoryPermission permission) {
KMemoryPermission permission) {
page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission);
};
system.Memory().WriteBlock(*this, base_addr, code_set.memory.data(), code_set.memory.size());
ReprotectSegment(code_set.CodeSegment(), Memory::MemoryPermission::ReadAndExecute);
ReprotectSegment(code_set.RODataSegment(), Memory::MemoryPermission::Read);
ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite);
ReprotectSegment(code_set.CodeSegment(), KMemoryPermission::ReadAndExecute);
ReprotectSegment(code_set.RODataSegment(), KMemoryPermission::Read);
ReprotectSegment(code_set.DataSegment(), KMemoryPermission::ReadAndWrite);
}
bool Process::IsSignaled() const {
@@ -459,9 +465,9 @@ bool Process::IsSignaled() const {
}
Process::Process(Core::System& system)
: KSynchronizationObject{system.Kernel()},
page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()},
address_arbiter{system}, condition_var{system}, state_lock{system.Kernel()}, system{system} {}
: KSynchronizationObject{system.Kernel()}, page_table{std::make_unique<KPageTable>(system)},
handle_table{system.Kernel()}, address_arbiter{system}, condition_var{system},
state_lock{system.Kernel()}, system{system} {}
Process::~Process() = default;
@@ -479,16 +485,15 @@ ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) {
ASSERT(stack_size);
// The kernel always ensures that the given stack size is page aligned.
main_thread_stack_size = Common::AlignUp(stack_size, Memory::PageSize);
main_thread_stack_size = Common::AlignUp(stack_size, PageSize);
const VAddr start{page_table->GetStackRegionStart()};
const std::size_t size{page_table->GetStackRegionEnd() - start};
CASCADE_RESULT(main_thread_stack_top,
page_table->AllocateAndMapMemory(
main_thread_stack_size / Memory::PageSize, Memory::PageSize, false, start,
size / Memory::PageSize, Memory::MemoryState::Stack,
Memory::MemoryPermission::ReadAndWrite));
main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize,
KMemoryState::Stack, KMemoryPermission::ReadAndWrite));
main_thread_stack_top += main_thread_stack_size;

View File

@@ -29,16 +29,13 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
class KPageTable;
class KResourceLimit;
class KThread;
class TLSPage;
struct CodeSet;
namespace Memory {
class PageTable;
}
enum class MemoryRegion : u16 {
APPLICATION = 1,
SYSTEM = 2,
@@ -104,12 +101,12 @@ public:
}
/// Gets a reference to the process' page table.
Memory::PageTable& PageTable() {
KPageTable& PageTable() {
return *page_table;
}
/// Gets const a reference to the process' page table.
const Memory::PageTable& PageTable() const {
const KPageTable& PageTable() const {
return *page_table;
}
@@ -385,7 +382,7 @@ private:
ResultCode AllocateMainThreadStack(std::size_t stack_size);
/// Memory manager for this process
std::unique_ptr<Memory::PageTable> page_table;
std::unique_ptr<KPageTable> page_table;
/// Current status of the process
ProcessStatus status{};

View File

@@ -7,7 +7,7 @@
#include "common/bit_util.h"
#include "common/logging/log.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/svc_results.h"
@@ -69,7 +69,7 @@ u32 GetFlagBitOffset(CapabilityType type) {
ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
std::size_t num_capabilities,
Memory::PageTable& page_table) {
KPageTable& page_table) {
Clear();
// Allow all cores and priorities.
@@ -82,7 +82,7 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti
ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
std::size_t num_capabilities,
Memory::PageTable& page_table) {
KPageTable& page_table) {
Clear();
return ParseCapabilities(capabilities, num_capabilities, page_table);
@@ -108,7 +108,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
std::size_t num_capabilities,
Memory::PageTable& page_table) {
KPageTable& page_table) {
u32 set_flags = 0;
u32 set_svc_bits = 0;
@@ -155,7 +155,7 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
}
ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
u32 flag, Memory::PageTable& page_table) {
u32 flag, KPageTable& page_table) {
const auto type = GetCapabilityType(flag);
if (type == CapabilityType::Unset) {
@@ -293,12 +293,12 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
}
ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
Memory::PageTable& page_table) {
KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return RESULT_SUCCESS;
}
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, Memory::PageTable& page_table) {
ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return RESULT_SUCCESS;
}

View File

@@ -12,9 +12,7 @@ union ResultCode;
namespace Kernel {
namespace Memory {
class PageTable;
}
class KPageTable;
/// The possible types of programs that may be indicated
/// by the program type capability descriptor.
@@ -90,7 +88,7 @@ public:
/// otherwise, an error code upon failure.
///
ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
Memory::PageTable& page_table);
KPageTable& page_table);
/// Initializes this process capabilities instance for a userland process.
///
@@ -103,7 +101,7 @@ public:
/// otherwise, an error code upon failure.
///
ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
Memory::PageTable& page_table);
KPageTable& page_table);
/// Initializes this process capabilities instance for a process that does not
/// have any metadata to parse.
@@ -189,7 +187,7 @@ private:
/// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
///
ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
Memory::PageTable& page_table);
KPageTable& page_table);
/// Attempts to parse a capability descriptor that is only represented by a
/// single flag set.
@@ -204,7 +202,7 @@ private:
/// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
///
ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
Memory::PageTable& page_table);
KPageTable& page_table);
/// Clears the internal state of this process capability instance. Necessary,
/// to have a sane starting point due to us allowing running executables without
@@ -228,10 +226,10 @@ private:
ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
/// Handles flags related to mapping physical memory pages.
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, Memory::PageTable& page_table);
ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
/// Handles flags related to mapping IO pages.
ResultCode HandleMapIOFlags(u32 flags, Memory::PageTable& page_table);
ResultCode HandleMapIOFlags(u32 flags, KPageTable& page_table);
/// Handles flags related to the interrupt capability flags.
ResultCode HandleInterruptFlags(u32 flags);

View File

@@ -27,21 +27,21 @@
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"
@@ -67,8 +67,8 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr dst_addr,
VAddr src_addr, u64 size) {
ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
u64 size) {
if (!Common::Is4KBAligned(dst_addr)) {
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
return ResultInvalidAddress;
@@ -230,9 +230,9 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
return ResultInvalidCurrentMemory;
}
const auto attributes{static_cast<Memory::MemoryAttribute>(mask | attribute)};
if (attributes != static_cast<Memory::MemoryAttribute>(mask) ||
(attributes | Memory::MemoryAttribute::Uncached) != Memory::MemoryAttribute::Uncached) {
const auto attributes{static_cast<MemoryAttribute>(mask | attribute)};
if (attributes != static_cast<MemoryAttribute>(mask) ||
(attributes | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
LOG_ERROR(Kernel_SVC,
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
attribute, mask);
@@ -241,8 +241,8 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
return page_table.SetMemoryAttribute(address, size, static_cast<Memory::MemoryAttribute>(mask),
static_cast<Memory::MemoryAttribute>(attribute));
return page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
static_cast<KMemoryAttribute>(attribute));
}
static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
@@ -508,7 +508,7 @@ static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAdd
thread_handle, address, tag);
// Validate the input address.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
address);
return ResultInvalidCurrentMemory;
@@ -531,8 +531,7 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
// Validate the input address.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC,
"Attempting to arbitrate an unlock on a kernel address (address={:08X})",
address);
@@ -1232,9 +1231,8 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
return ResultInvalidCurrentMemory;
}
const auto permission_type = static_cast<Memory::MemoryPermission>(permissions);
if ((permission_type | Memory::MemoryPermission::Write) !=
Memory::MemoryPermission::ReadAndWrite) {
const auto permission_type = static_cast<MemoryPermission>(permissions);
if ((permission_type | MemoryPermission::Write) != MemoryPermission::ReadWrite) {
LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
permissions);
return ResultInvalidMemoryPermissions;
@@ -1267,14 +1265,15 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
return ResultInvalidMemoryRange;
}
auto shared_memory{current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle)};
auto shared_memory{current_process->GetHandleTable().Get<KSharedMemory>(shared_memory_handle)};
if (!shared_memory) {
LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
shared_memory_handle);
return ResultInvalidHandle;
}
return shared_memory->Map(*current_process, addr, size, permission_type);
return shared_memory->Map(*current_process, addr, size,
static_cast<KMemoryPermission>(permission_type));
}
static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr,
@@ -1533,8 +1532,9 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
std::shared_ptr<KThread> thread;
{
KScopedLightLock lk{process.GetStateLock()};
CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority,
arg, core_id, stack_bottom, &process));
CASCADE_RESULT(thread,
KThread::CreateUserThread(system, ThreadType::User, "", entry_point,
priority, arg, core_id, stack_bottom, &process));
}
const auto new_thread_handle = process.GetHandleTable().Create(thread);
@@ -1638,7 +1638,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address,
cv_key, tag, timeout_ns);
// Validate input.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
return ResultInvalidCurrentMemory;
}
@@ -1720,7 +1720,7 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::Arbit
address, arb_type, value, timeout_ns);
// Validate input.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
return ResultInvalidCurrentMemory;
}
@@ -1765,7 +1765,7 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign
address, signal_type, value, count);
// Validate input.
if (Memory::IsKernelAddress(address)) {
if (IsKernelAddress(address)) {
LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
return ResultInvalidCurrentMemory;
}
@@ -1887,9 +1887,8 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
return ResultInvalidCurrentMemory;
}
const auto perms{static_cast<Memory::MemoryPermission>(permissions)};
if (perms > Memory::MemoryPermission::ReadAndWrite ||
perms == Memory::MemoryPermission::Write) {
const auto perms{static_cast<MemoryPermission>(permissions)};
if (perms > MemoryPermission::ReadWrite || perms == MemoryPermission::Write) {
LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
permissions);
return ResultInvalidMemoryPermissions;
@@ -1903,7 +1902,8 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
LOG_ERROR(Kernel_SVC, "Could not reserve a new transfer memory");
return ResultResourceLimitedExceeded;
}
auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms);
auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size,
static_cast<KMemoryPermission>(perms));
if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
return reserve_result;

View File

@@ -2,9 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/result.h"
@@ -24,7 +24,7 @@ TransferMemory::~TransferMemory() {
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel,
Core::Memory::Memory& memory,
VAddr base_address, std::size_t size,
Memory::MemoryPermission permissions) {
KMemoryPermission permissions) {
std::shared_ptr<TransferMemory> transfer_memory{
std::make_shared<TransferMemory>(kernel, memory)};

View File

@@ -6,7 +6,7 @@
#include <memory>
#include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/physical_memory.h"
@@ -36,7 +36,7 @@ public:
static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Core::Memory::Memory& memory,
VAddr base_address, std::size_t size,
Memory::MemoryPermission permissions);
KMemoryPermission permissions);
TransferMemory(const TransferMemory&) = delete;
TransferMemory& operator=(const TransferMemory&) = delete;
@@ -82,7 +82,7 @@ private:
std::size_t size{};
/// The memory permissions that are applied to this instance.
Memory::MemoryPermission owner_permissions{};
KMemoryPermission owner_permissions{};
/// The process that this transfer memory instance was created under.
Process* owner_process{};

View File

@@ -508,7 +508,7 @@ public:
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
{2, nullptr, "EnsureIdTokenCacheAsync"},
{3, nullptr, "LoadIdTokenCache"},
{130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
{130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"},
{150, nullptr, "CreateAuthorizationRequest"},
{160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"},
{170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
@@ -534,6 +534,22 @@ private:
rb.PushRaw<u64>(user_id.GetNintendoID());
}
void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
std::vector<u8> nas_user_base_for_application(0x68);
ctx.WriteBuffer(nas_user_base_for_application, 0);
if (ctx.CanWriteBuffer(1)) {
std::vector<u8> unknown_out_buffer(ctx.GetWriteBufferSize(1));
ctx.WriteBuffer(unknown_out_buffer, 1);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(user_id.GetNintendoID());
}
void StoreOpenContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};

View File

@@ -5,15 +5,25 @@
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hle/service/hid/controllers/gesture.h"
#include "core/settings.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
constexpr f32 angle_threshold = 0.08f;
constexpr f32 pinch_threshold = 100.0f;
Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {}
void Controller_Gesture::OnInit() {
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
mouse_finger_id[id] = MAX_FINGERS;
keyboard_finger_id[id] = MAX_FINGERS;
udp_finger_id[id] = MAX_FINGERS;
}
}
void Controller_Gesture::OnRelease() {}
@@ -35,10 +45,153 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
// TODO(ogniK): Update gesture states
// TODO(german77): Implement all gesture types
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
}
if (Settings::values.use_touch_from_button) {
const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
keyboard_finger_id[id] =
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
}
}
TouchType type = TouchType::Idle;
Attribute attributes{};
GestureProperties gesture = GetGestureProperties();
if (last_gesture.active_points != gesture.active_points) {
++last_gesture.detection_count;
}
if (gesture.active_points > 0) {
if (last_gesture.active_points == 0) {
attributes.is_new_touch.Assign(true);
last_gesture.average_distance = gesture.average_distance;
last_gesture.angle = gesture.angle;
}
type = TouchType::Touch;
if (gesture.mid_point.x != last_entry.x || gesture.mid_point.y != last_entry.y) {
type = TouchType::Pan;
}
if (std::abs(gesture.average_distance - last_gesture.average_distance) > pinch_threshold) {
type = TouchType::Pinch;
}
if (std::abs(gesture.angle - last_gesture.angle) > angle_threshold) {
type = TouchType::Rotate;
}
cur_entry.delta_x = gesture.mid_point.x - last_entry.x;
cur_entry.delta_y = gesture.mid_point.y - last_entry.y;
// TODO: Find how velocities are calculated
cur_entry.vel_x = static_cast<float>(cur_entry.delta_x) * 150.1f;
cur_entry.vel_y = static_cast<float>(cur_entry.delta_y) * 150.1f;
// Slowdown the rate of change for less flapping
last_gesture.average_distance =
(last_gesture.average_distance * 0.9f) + (gesture.average_distance * 0.1f);
last_gesture.angle = (last_gesture.angle * 0.9f) + (gesture.angle * 0.1f);
} else {
cur_entry.delta_x = 0;
cur_entry.delta_y = 0;
cur_entry.vel_x = 0;
cur_entry.vel_y = 0;
}
last_gesture.active_points = gesture.active_points;
cur_entry.detection_count = last_gesture.detection_count;
cur_entry.type = type;
cur_entry.attributes = attributes;
cur_entry.x = gesture.mid_point.x;
cur_entry.y = gesture.mid_point.y;
cur_entry.point_count = static_cast<s32>(gesture.active_points);
for (size_t id = 0; id < MAX_POINTS; id++) {
cur_entry.points[id].x = gesture.points[id].x;
cur_entry.points[id].y = gesture.points[id].y;
}
cur_entry.rotation_angle = 0;
cur_entry.scale = 0;
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
void Controller_Gesture::OnLoadInputDevices() {}
void Controller_Gesture::OnLoadInputDevices() {
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
}
std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
std::size_t first_free_id = 0;
while (first_free_id < MAX_POINTS) {
if (!fingers[first_free_id].pressed) {
return first_free_id;
} else {
first_free_id++;
}
}
return std::nullopt;
}
std::size_t Controller_Gesture::UpdateTouchInputEvent(
const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
const auto& [x, y, pressed] = touch_input;
if (pressed) {
if (finger_id == MAX_POINTS) {
const auto first_free_id = GetUnusedFingerID();
if (!first_free_id) {
// Invalid finger id do nothing
return MAX_POINTS;
}
finger_id = first_free_id.value();
fingers[finger_id].pressed = true;
}
fingers[finger_id].x = x;
fingers[finger_id].y = y;
return finger_id;
}
if (finger_id != MAX_POINTS) {
fingers[finger_id].pressed = false;
}
return MAX_POINTS;
}
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
GestureProperties gesture;
std::array<Finger, MAX_POINTS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
gesture.active_points =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
for (size_t id = 0; id < gesture.active_points; ++id) {
gesture.points[id].x =
static_cast<int>(active_fingers[id].x * Layout::ScreenUndocked::Width);
gesture.points[id].y =
static_cast<int>(active_fingers[id].y * Layout::ScreenUndocked::Height);
gesture.mid_point.x += static_cast<int>(gesture.points[id].x / gesture.active_points);
gesture.mid_point.y += static_cast<int>(gesture.points[id].y / gesture.active_points);
}
for (size_t id = 0; id < gesture.active_points; ++id) {
const double distance =
std::pow(static_cast<float>(gesture.mid_point.x - gesture.points[id].x), 2) +
std::pow(static_cast<float>(gesture.mid_point.y - gesture.points[id].y), 2);
gesture.average_distance +=
static_cast<float>(distance) / static_cast<float>(gesture.active_points);
}
gesture.angle = std::atan2(static_cast<float>(gesture.mid_point.y - gesture.points[0].y),
static_cast<float>(gesture.mid_point.x - gesture.points[0].x));
return gesture;
}
} // namespace Service::HID

View File

@@ -5,8 +5,10 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
@@ -28,29 +30,64 @@ public:
void OnLoadInputDevices() override;
private:
struct Locations {
static constexpr size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
enum class TouchType : u32 {
Idle, // Nothing touching the screen
Complete, // Unknown. End of touch?
Cancel, // Never triggered
Touch, // Pressing without movement
Press, // Never triggered
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
Pinch, // All points moving away/closer to the midpoint
Rotate, // All points rotating from the midpoint
};
enum class Direction : u32 {
None,
Left,
Up,
Right,
Down,
};
struct Attribute {
union {
u32_le raw{};
BitField<0, 1, u32> is_new_touch;
BitField<1, 1, u32> is_double_tap;
};
};
static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size");
struct Points {
s32_le x;
s32_le y;
};
static_assert(sizeof(Points) == 8, "Points is an invalid size");
struct GestureState {
s64_le sampling_number;
s64_le sampling_number2;
s64_le detection_count;
s32_le type;
s32_le dir;
TouchType type;
Direction dir;
s32_le x;
s32_le y;
s32_le delta_x;
s32_le delta_y;
f32 vel_x;
f32 vel_y;
s32_le attributes;
f32 scale;
f32 rotation;
s32_le location_count;
std::array<Locations, 4> locations;
Attribute attributes;
u32 scale;
u32 rotation_angle;
s32_le point_count;
std::array<Points, 4> points;
};
static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
@@ -58,6 +95,45 @@ private:
CommonHeader header;
std::array<GestureState, 17> gesture_states;
};
static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
struct Finger {
f32 x{};
f32 y{};
bool pressed{};
};
struct GestureProperties {
std::array<Points, MAX_POINTS> points{};
std::size_t active_points{};
Points mid_point{};
s64_le detection_count{};
u64_le delta_time{};
float average_distance{};
float angle{};
};
// Returns an unused finger id, if there is no fingers avaliable MAX_FINGERS will be returned
std::optional<size_t> GetUnusedFingerID() const;
/** If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
* changes will be made. Updates the coordinates if the finger id it's already set. If the touch
* ends delays the output by one frame to set the end_touch flag before finally freeing the
* finger id */
size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
size_t finger_id);
// Returns the average distance, angle and middle point of the active fingers
GestureProperties GetGestureProperties();
SharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_mouse_device;
std::unique_ptr<Input::TouchDevice> touch_udp_device;
std::unique_ptr<Input::TouchDevice> touch_btn_device;
std::array<size_t, MAX_FINGERS> mouse_finger_id;
std::array<size_t, MAX_FINGERS> keyboard_finger_id;
std::array<size_t, MAX_FINGERS> udp_finger_id;
std::array<Finger, MAX_POINTS> fingers;
GestureProperties last_gesture;
};
} // namespace Service::HID

View File

@@ -15,9 +15,9 @@
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/irs.h"
@@ -273,8 +273,8 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
{204, &Hid::PermitVibration, "PermitVibration"},
{205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"},
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
{207, nullptr, "SendVibrationGcErmCommand"},
{208, nullptr, "GetActualVibrationGcErmCommand"},
{207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
{208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
{209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
{210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
{211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
@@ -1093,7 +1093,22 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
VibrationDeviceInfo vibration_device_info;
vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
switch (vibration_device_handle.npad_type) {
case Controller_NPad::NpadType::ProController:
case Controller_NPad::NpadType::Handheld:
case Controller_NPad::NpadType::JoyconDual:
case Controller_NPad::NpadType::JoyconLeft:
case Controller_NPad::NpadType::JoyconRight:
default:
vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
break;
case Controller_NPad::NpadType::GameCube:
vibration_device_info.type = VibrationDeviceType::GcErm;
break;
case Controller_NPad::NpadType::Pokeball:
vibration_device_info.type = VibrationDeviceType::Unknown;
break;
}
switch (vibration_device_handle.device_index) {
case Controller_NPad::DeviceIndex::Left:
@@ -1215,6 +1230,108 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle vibration_device_handle;
u64 applet_resource_user_id;
VibrationGcErmCommand gc_erm_command;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
/**
* Note: This uses yuzu-specific behavior such that the StopHard command produces
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
* in order to differentiate between Stop and StopHard commands.
* This is done to reuse the controller vibration functions made for regular controllers.
*/
const auto vibration_value = [parameters] {
switch (parameters.gc_erm_command) {
case VibrationGcErmCommand::Stop:
return Controller_NPad::VibrationValue{
.amp_low = 0.0f,
.freq_low = 160.0f,
.amp_high = 0.0f,
.freq_high = 320.0f,
};
case VibrationGcErmCommand::Start:
return Controller_NPad::VibrationValue{
.amp_low = 1.0f,
.freq_low = 160.0f,
.amp_high = 1.0f,
.freq_high = 320.0f,
};
case VibrationGcErmCommand::StopHard:
return Controller_NPad::VibrationValue{
.amp_low = 0.0f,
.freq_low = 0.0f,
.amp_high = 0.0f,
.freq_high = 0.0f,
};
default:
return Controller_NPad::DEFAULT_VIBRATION_VALUE;
}
}();
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateController(parameters.vibration_device_handle, vibration_value);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
"gc_erm_command={}",
parameters.vibration_device_handle.npad_type,
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
parameters.gc_erm_command);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::DeviceHandle vibration_device_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
const auto parameters{rp.PopRaw<Parameters>()};
const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetLastVibration(parameters.vibration_device_handle);
const auto gc_erm_command = [last_vibration] {
if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) {
return VibrationGcErmCommand::Start;
}
/**
* Note: This uses yuzu-specific behavior such that the StopHard command produces
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
* SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
* This is done to reuse the controller vibration functions made for regular controllers.
*/
if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) {
return VibrationGcErmCommand::StopHard;
}
return VibrationGcErmCommand::Stop;
}();
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.vibration_device_handle.npad_type,
parameters.vibration_device_handle.npad_id,
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(gc_erm_command);
}
void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};

View File

@@ -14,7 +14,7 @@ struct EventType;
}
namespace Kernel {
class SharedMemory;
class KSharedMemory;
}
namespace Service::SM {
@@ -69,7 +69,7 @@ private:
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
std::shared_ptr<Kernel::SharedMemory> shared_mem;
std::shared_ptr<Kernel::KSharedMemory> shared_mem;
std::shared_ptr<Core::Timing::EventType> pad_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
@@ -136,6 +136,8 @@ private:
void PermitVibration(Kernel::HLERequestContext& ctx);
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
void GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
@@ -154,7 +156,9 @@ private:
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
enum class VibrationDeviceType : u32 {
Unknown = 0,
LinearResonantActuator = 1,
GcErm = 2,
};
enum class VibrationDevicePosition : u32 {
@@ -163,6 +167,12 @@ private:
Right = 2,
};
enum class VibrationGcErmCommand : u64 {
Stop = 0,
Start = 1,
StopHard = 2,
};
struct VibrationDeviceInfo {
VibrationDeviceType type{};
VibrationDevicePosition position{};

View File

@@ -6,8 +6,8 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/irs.h"
namespace Service::HID {

View File

@@ -12,7 +12,7 @@ class System;
}
namespace Kernel {
class SharedMemory;
class KSharedMemory;
}
namespace Service::HID {
@@ -42,7 +42,7 @@ private:
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
std::shared_ptr<Kernel::SharedMemory> shared_mem;
std::shared_ptr<Kernel::KSharedMemory> shared_mem;
const u32 device_handle{0xABCD};
};

View File

@@ -156,7 +156,7 @@ public:
is_initialized = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
rb.Push(ERROR_DISABLED);
}
private:

View File

@@ -11,8 +11,8 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/system_control.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/service/ldr/ldr.h"
@@ -287,12 +287,11 @@ public:
rb.Push(RESULT_SUCCESS);
}
bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start,
std::size_t size) const {
constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize};
bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const {
constexpr std::size_t padding_size{4 * Kernel::PageSize};
const auto start_info{page_table.QueryInfo(start - 1)};
if (start_info.state != Kernel::Memory::MemoryState::Free) {
if (start_info.state != Kernel::KMemoryState::Free) {
return {};
}
@@ -302,21 +301,20 @@ public:
const auto end_info{page_table.QueryInfo(start + size)};
if (end_info.state != Kernel::Memory::MemoryState::Free) {
if (end_info.state != Kernel::KMemoryState::Free) {
return {};
}
return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
}
VAddr GetRandomMapRegion(const Kernel::Memory::PageTable& page_table, std::size_t size) const {
VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const {
VAddr addr{};
const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >>
Kernel::Memory::PageBits};
Kernel::PageBits};
do {
addr = page_table.GetAliasCodeRegionStart() +
(Kernel::Memory::SystemControl::GenerateRandomRange(0, end_pages)
<< Kernel::Memory::PageBits);
(Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits);
} while (!page_table.IsInsideAddressSpace(addr, size) ||
page_table.IsInsideHeapRegion(addr, size) ||
page_table.IsInsideAliasRegion(addr, size));
@@ -387,7 +385,7 @@ public:
const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset};
const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size};
const VAddr bss_end_addr{
Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)};
Common::AlignUp(bss_start + nro_header.bss_size, Kernel::PageSize)};
auto CopyCode{[&](VAddr src_addr, VAddr dst_addr, u64 size) {
std::vector<u8> source_data(size);
@@ -402,12 +400,12 @@ public:
nro_header.segment_headers[DATA_INDEX].memory_size);
CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute));
CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
ro_start, data_start - ro_start, Kernel::Memory::MemoryPermission::Read));
text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute));
CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(ro_start, data_start - ro_start,
Kernel::KMemoryPermission::Read));
return process->PageTable().SetCodeMemoryPermission(
data_start, bss_end_addr - data_start, Kernel::Memory::MemoryPermission::ReadAndWrite);
data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite);
}
void LoadNro(Kernel::HLERequestContext& ctx) {

View File

@@ -19,9 +19,9 @@
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/pl_u.h"
@@ -131,7 +131,7 @@ struct PL_U::Impl {
}
/// Handle to shared memory region designated for a shared font
std::shared_ptr<Kernel::SharedMemory> shared_font_mem;
std::shared_ptr<Kernel::KSharedMemory> shared_font_mem;
/// Backing memory for the shared font data
std::shared_ptr<Kernel::PhysicalMemory> shared_font;

View File

@@ -341,12 +341,18 @@ void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::RequestParser rp{ctx};
const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>();
const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>();
Clock::ClockSnapshot snapshot_a;
Clock::ClockSnapshot snapshot_b;
const auto snapshot_a_data = ctx.ReadBuffer(0);
const auto snapshot_b_data = ctx.ReadBuffer(1);
std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot));
std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot));
Clock::TimeSpanType time_span_type{};
s64 span{};
if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween(
snapshot_b.steady_clock_time_point, span)};
result != RESULT_SUCCESS) {

View File

@@ -22,7 +22,7 @@ SharedMemory::SharedMemory(Core::System& system) : system(system) {
SharedMemory::~SharedMemory() = default;
std::shared_ptr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() const {
std::shared_ptr<Kernel::KSharedMemory> SharedMemory::GetSharedMemoryHolder() const {
return shared_memory_holder;
}

View File

@@ -6,8 +6,8 @@
#include "common/common_types.h"
#include "common/uuid.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::Time {
@@ -18,7 +18,7 @@ public:
~SharedMemory();
// Return the shared memory handle
std::shared_ptr<Kernel::SharedMemory> GetSharedMemoryHolder() const;
std::shared_ptr<Kernel::KSharedMemory> GetSharedMemoryHolder() const;
// TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
template <typename T, std::size_t Offset>
@@ -63,7 +63,7 @@ public:
void SetAutomaticCorrectionEnabled(bool is_enabled);
private:
std::shared_ptr<Kernel::SharedMemory> shared_memory_holder;
std::shared_ptr<Kernel::KSharedMemory> shared_memory_holder;
Core::System& system;
Format shared_memory_format{};
};

View File

@@ -12,8 +12,8 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/deconstructed_rom_directory.h"

View File

@@ -10,7 +10,7 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/process.h"
#include "core/loader/elf.h"
#include "core/memory.h"

View File

@@ -6,7 +6,7 @@
#include "core/file_sys/kernel_executable.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/process.h"
#include "core/loader/kip.h"
#include "core/memory.h"

View File

@@ -15,8 +15,8 @@
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_offset.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/nro.h"

View File

@@ -15,8 +15,8 @@
#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
#include "core/loader/nso.h"
#include "core/memory.h"

View File

@@ -16,7 +16,7 @@
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/physical_memory.h"
#include "core/hle/kernel/process.h"
#include "core/memory.h"

View File

@@ -116,6 +116,11 @@ public:
*/
u8* GetPointer(VAddr vaddr);
template <typename T>
T* GetPointer(VAddr vaddr) {
return reinterpret_cast<T*>(GetPointer(vaddr));
}
/**
* Gets a pointer to the given address.
*
@@ -126,6 +131,11 @@ public:
*/
const u8* GetPointer(VAddr vaddr) const;
template <typename T>
const T* GetPointer(VAddr vaddr) const {
return reinterpret_cast<T*>(GetPointer(vaddr));
}
/**
* Reads an 8-bit unsigned value from the current process' address space
* at the given virtual address.

View File

@@ -10,7 +10,7 @@
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"

View File

@@ -7,6 +7,7 @@
#include <limits>
#include <utility>
#include <vector>
#include "common/common_funcs.h"
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
@@ -90,15 +91,36 @@ LINGER MakeLinger(bool enable, u32 linger_value) {
return value;
}
int LastError() {
return WSAGetLastError();
}
bool EnableNonBlock(SOCKET fd, bool enable) {
u_long value = enable ? 1 : 0;
return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
}
Errno TranslateNativeError(int e) {
switch (e) {
case WSAEBADF:
return Errno::BADF;
case WSAEINVAL:
return Errno::INVAL;
case WSAEMFILE:
return Errno::MFILE;
case WSAENOTCONN:
return Errno::NOTCONN;
case WSAEWOULDBLOCK:
return Errno::AGAIN;
case WSAECONNREFUSED:
return Errno::CONNREFUSED;
case WSAEHOSTUNREACH:
return Errno::HOSTUNREACH;
case WSAENETDOWN:
return Errno::NETDOWN;
case WSAENETUNREACH:
return Errno::NETUNREACH;
default:
return Errno::OTHER;
}
}
#elif YUZU_UNIX // ^ _WIN32 v YUZU_UNIX
using SOCKET = int;
@@ -108,9 +130,6 @@ using ULONG = u64;
constexpr SOCKET INVALID_SOCKET = -1;
constexpr SOCKET SOCKET_ERROR = -1;
constexpr int WSAEWOULDBLOCK = EAGAIN;
constexpr int WSAENOTCONN = ENOTCONN;
constexpr int SD_RECEIVE = SHUT_RD;
constexpr int SD_SEND = SHUT_WR;
constexpr int SD_BOTH = SHUT_RDWR;
@@ -162,10 +181,6 @@ linger MakeLinger(bool enable, u32 linger_value) {
return value;
}
int LastError() {
return errno;
}
bool EnableNonBlock(int fd, bool enable) {
int flags = fcntl(fd, F_GETFD);
if (flags == -1) {
@@ -179,8 +194,43 @@ bool EnableNonBlock(int fd, bool enable) {
return fcntl(fd, F_SETFD, flags) == 0;
}
Errno TranslateNativeError(int e) {
switch (e) {
case EBADF:
return Errno::BADF;
case EINVAL:
return Errno::INVAL;
case EMFILE:
return Errno::MFILE;
case ENOTCONN:
return Errno::NOTCONN;
case EAGAIN:
return Errno::AGAIN;
case ECONNREFUSED:
return Errno::CONNREFUSED;
case EHOSTUNREACH:
return Errno::HOSTUNREACH;
case ENETDOWN:
return Errno::NETDOWN;
case ENETUNREACH:
return Errno::NETUNREACH;
default:
return Errno::OTHER;
}
}
#endif
Errno GetAndLogLastError() {
#ifdef _WIN32
int e = WSAGetLastError();
#else
int e = errno;
#endif
LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
return TranslateNativeError(e);
}
int TranslateDomain(Domain domain) {
switch (domain) {
case Domain::INET:
@@ -290,9 +340,7 @@ Errno SetSockOpt(SOCKET fd, int option, T value) {
if (result != SOCKET_ERROR) {
return Errno::SUCCESS;
}
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return Errno::SUCCESS;
return GetAndLogLastError();
}
} // Anonymous namespace
@@ -308,14 +356,12 @@ NetworkInstance::~NetworkInstance() {
std::pair<IPv4Address, Errno> GetHostIPv4Address() {
std::array<char, 256> name{};
if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) {
UNIMPLEMENTED_MSG("Unhandled gethostname error");
return {IPv4Address{}, Errno::SUCCESS};
return {IPv4Address{}, GetAndLogLastError()};
}
hostent* const ent = gethostbyname(name.data());
if (!ent) {
UNIMPLEMENTED_MSG("Unhandled gethostbyname error");
return {IPv4Address{}, Errno::SUCCESS};
return {IPv4Address{}, GetAndLogLastError()};
}
if (ent->h_addr_list == nullptr) {
UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
@@ -359,9 +405,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
ASSERT(result == SOCKET_ERROR);
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return {-1, Errno::SUCCESS};
return {-1, GetAndLogLastError()};
}
Socket::~Socket() {
@@ -380,9 +424,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
return Errno::SUCCESS;
}
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return Errno::SUCCESS;
return GetAndLogLastError();
}
std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
@@ -391,9 +433,7 @@ std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
const SOCKET new_socket = accept(fd, &addr, &addrlen);
if (new_socket == INVALID_SOCKET) {
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return {AcceptResult{}, Errno::SUCCESS};
return {AcceptResult{}, GetAndLogLastError()};
}
AcceptResult result;
@@ -412,23 +452,14 @@ Errno Socket::Connect(SockAddrIn addr_in) {
return Errno::SUCCESS;
}
switch (const int ec = LastError()) {
case WSAEWOULDBLOCK:
LOG_DEBUG(Service, "EAGAIN generated");
return Errno::AGAIN;
default:
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return Errno::SUCCESS;
}
return GetAndLogLastError();
}
std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
sockaddr addr;
socklen_t addrlen = sizeof(addr);
if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) {
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return {SockAddrIn{}, Errno::SUCCESS};
return {SockAddrIn{}, GetAndLogLastError()};
}
ASSERT(addrlen == sizeof(sockaddr_in));
@@ -439,9 +470,7 @@ std::pair<SockAddrIn, Errno> Socket::GetSockName() {
sockaddr addr;
socklen_t addrlen = sizeof(addr);
if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) {
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return {SockAddrIn{}, Errno::SUCCESS};
return {SockAddrIn{}, GetAndLogLastError()};
}
ASSERT(addrlen == sizeof(sockaddr_in));
@@ -454,9 +483,7 @@ Errno Socket::Bind(SockAddrIn addr) {
return Errno::SUCCESS;
}
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return Errno::SUCCESS;
return GetAndLogLastError();
}
Errno Socket::Listen(s32 backlog) {
@@ -464,9 +491,7 @@ Errno Socket::Listen(s32 backlog) {
return Errno::SUCCESS;
}
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return Errno::SUCCESS;
return GetAndLogLastError();
}
Errno Socket::Shutdown(ShutdownHow how) {
@@ -489,14 +514,7 @@ Errno Socket::Shutdown(ShutdownHow how) {
return Errno::SUCCESS;
}
switch (const int ec = LastError()) {
case WSAENOTCONN:
LOG_ERROR(Service, "ENOTCONN generated");
return Errno::NOTCONN;
default:
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return Errno::SUCCESS;
}
return GetAndLogLastError();
}
std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
@@ -509,17 +527,7 @@ std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
return {static_cast<s32>(result), Errno::SUCCESS};
}
switch (const int ec = LastError()) {
case WSAEWOULDBLOCK:
LOG_DEBUG(Service, "EAGAIN generated");
return {-1, Errno::AGAIN};
case WSAENOTCONN:
LOG_ERROR(Service, "ENOTCONN generated");
return {-1, Errno::NOTCONN};
default:
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return {0, Errno::SUCCESS};
}
return {-1, GetAndLogLastError()};
}
std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
@@ -541,17 +549,7 @@ std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, Sock
return {static_cast<s32>(result), Errno::SUCCESS};
}
switch (const int ec = LastError()) {
case WSAEWOULDBLOCK:
LOG_DEBUG(Service, "EAGAIN generated");
return {-1, Errno::AGAIN};
case WSAENOTCONN:
LOG_ERROR(Service, "ENOTCONN generated");
return {-1, Errno::NOTCONN};
default:
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return {-1, Errno::SUCCESS};
}
return {-1, GetAndLogLastError()};
}
std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
@@ -564,18 +562,7 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
return {static_cast<s32>(result), Errno::SUCCESS};
}
const int ec = LastError();
switch (ec) {
case WSAEWOULDBLOCK:
LOG_DEBUG(Service, "EAGAIN generated");
return {-1, Errno::AGAIN};
case WSAENOTCONN:
LOG_ERROR(Service, "ENOTCONN generated");
return {-1, Errno::NOTCONN};
default:
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return {-1, Errno::SUCCESS};
}
return {-1, GetAndLogLastError()};
}
std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
@@ -597,9 +584,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
return {static_cast<s32>(result), Errno::SUCCESS};
}
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return {-1, Errno::SUCCESS};
return {-1, GetAndLogLastError()};
}
Errno Socket::Close() {
@@ -642,9 +627,7 @@ Errno Socket::SetNonBlock(bool enable) {
if (EnableNonBlock(fd, enable)) {
return Errno::SUCCESS;
}
const int ec = LastError();
UNREACHABLE_MSG("Unhandled host socket error={}", ec);
return Errno::SUCCESS;
return GetAndLogLastError();
}
bool Socket::IsOpened() const {

View File

@@ -7,6 +7,7 @@
#include <array>
#include <utility>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Network {
@@ -21,6 +22,11 @@ enum class Errno {
MFILE,
NOTCONN,
AGAIN,
CONNREFUSED,
HOSTUNREACH,
NETDOWN,
NETUNREACH,
OTHER,
};
/// Address families

View File

@@ -17,7 +17,7 @@
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
#include "core/memory.h"

View File

@@ -59,7 +59,7 @@ void Mouse::UpdateYuzuSettings() {
});
}
void Mouse::PressButton(int x, int y, int button_) {
void Mouse::PressButton(int x, int y, MouseButton button_) {
const auto button_index = static_cast<std::size_t>(button_);
if (button_index >= mouse_info.size()) {
return;
@@ -67,7 +67,7 @@ void Mouse::PressButton(int x, int y, int button_) {
const auto button = 1U << button_index;
buttons |= static_cast<u16>(button);
last_button = static_cast<MouseButton>(button_index);
last_button = button_;
mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
@@ -129,7 +129,7 @@ void Mouse::MouseMove(int x, int y, int center_x, int center_y) {
}
}
void Mouse::ReleaseButton(int button_) {
void Mouse::ReleaseButton(MouseButton button_) {
const auto button_index = static_cast<std::size_t>(button_);
if (button_index >= mouse_info.size()) {
return;
@@ -152,6 +152,11 @@ void Mouse::BeginConfiguration() {
void Mouse::EndConfiguration() {
buttons = 0;
for (MouseInfo& info : mouse_info) {
info.tilt_speed = 0;
info.data.pressed = false;
info.data.axis = {0, 0};
}
last_button = MouseButton::Undefined;
mouse_queue.Clear();
configuring = false;

View File

@@ -18,10 +18,12 @@ namespace MouseInput {
enum class MouseButton {
Left,
Wheel,
Right,
Forward,
Wheel,
Backward,
Forward,
Task,
Extra,
Undefined,
};
@@ -51,7 +53,7 @@ public:
* @param y the y-coordinate of the cursor
* @param button_ the button pressed
*/
void PressButton(int x, int y, int button_);
void PressButton(int x, int y, MouseButton button_);
/**
* Signals that mouse has moved.
@@ -65,7 +67,7 @@ public:
/**
* Signals that a motion sensor tilt has ended.
*/
void ReleaseButton(int button_);
void ReleaseButton(MouseButton button_);
[[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
[[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
@@ -94,7 +96,7 @@ private:
u16 buttons{};
std::thread update_thread;
MouseButton last_button{MouseButton::Undefined};
std::array<MouseInfo, 5> mouse_info;
std::array<MouseInfo, 7> mouse_info;
Common::SPSCQueue<MouseStatus> mouse_queue;
bool configuring{false};
bool update_thread_running{true};

View File

@@ -5,6 +5,7 @@
#include <chrono>
#include <cstring>
#include <functional>
#include <random>
#include <thread>
#include <boost/asio.hpp>
#include "common/logging/log.h"
@@ -26,10 +27,10 @@ class Socket {
public:
using clock = std::chrono::system_clock;
explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_,
explicit Socket(const std::string& host, u16 port, std::size_t pad_index_,
SocketCallback callback_)
: callback(std::move(callback_)), timer(io_service),
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_),
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()),
pad_index(pad_index_) {
boost::system::error_code ec{};
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
@@ -63,6 +64,11 @@ public:
}
private:
u32 GenerateRandomClientId() const {
std::random_device device;
return device();
}
void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
switch (*type) {
@@ -115,7 +121,7 @@ private:
boost::asio::basic_waitable_timer<clock> timer;
udp::socket socket;
u32 client_id{};
const u32 client_id;
std::size_t pad_index{};
static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
@@ -203,7 +209,7 @@ void Client::ReloadSockets() {
LOG_ERROR(Input, "Duplicated UDP servers found");
continue;
}
StartCommunication(client++, udp_input_address, udp_input_port, pad, 24872);
StartCommunication(client++, udp_input_address, udp_input_port, pad);
}
}
}
@@ -277,7 +283,7 @@ void Client::OnPadData(Response::PadData data, std::size_t client) {
}
void Client::StartCommunication(std::size_t client, const std::string& host, u16 port,
std::size_t pad_index, u32 client_id) {
std::size_t pad_index) {
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
[this](Response::PortInfo info) { OnPortInfo(info); },
[this, client](Response::PadData data) { OnPadData(data, client); }};
@@ -287,7 +293,7 @@ void Client::StartCommunication(std::size_t client, const std::string& host, u16
clients[client].port = port;
clients[client].pad_index = pad_index;
clients[client].active = 0;
clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
clients[client].socket = std::make_unique<Socket>(host, port, pad_index, callback);
clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
// Set motion parameters
// SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
@@ -416,7 +422,7 @@ const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
return pad_queue;
}
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
const std::function<void()>& success_callback,
const std::function<void()>& failure_callback) {
std::thread([=] {
@@ -426,7 +432,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
.port_info = [](Response::PortInfo) {},
.pad_data = [&](Response::PadData) { success_event.Set(); },
};
Socket socket{host, port, pad_index, client_id, std::move(callback)};
Socket socket{host, port, pad_index, std::move(callback)};
std::thread worker_thread{SocketLoop, &socket};
const bool result = success_event.WaitFor(std::chrono::seconds(5));
socket.Stop();
@@ -440,7 +446,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
}
CalibrationConfigurationJob::CalibrationConfigurationJob(
const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
const std::string& host, u16 port, std::size_t pad_index,
std::function<void(Status)> status_callback,
std::function<void(u16, u16, u16, u16)> data_callback) {
@@ -485,7 +491,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
complete_event.Set();
}
}};
Socket socket{host, port, pad_index, client_id, std::move(callback)};
Socket socket{host, port, pad_index, std::move(callback)};
std::thread worker_thread{SocketLoop, &socket};
complete_event.Wait();
socket.Stop();

View File

@@ -126,7 +126,7 @@ private:
void OnPortInfo(Response::PortInfo);
void OnPadData(Response::PadData, std::size_t client);
void StartCommunication(std::size_t client, const std::string& host, u16 port,
std::size_t pad_index, u32 client_id);
std::size_t pad_index);
void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
const Common::Vec3<float>& gyro);
@@ -165,7 +165,7 @@ public:
* @param data_callback Called when calibration data is ready
*/
explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index,
u32 client_id, std::function<void(Status)> status_callback,
std::function<void(Status)> status_callback,
std::function<void(u16, u16, u16, u16)> data_callback);
~CalibrationConfigurationJob();
void Stop();
@@ -174,7 +174,7 @@ private:
Common::Event complete_event;
};
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
const std::function<void()>& success_callback,
const std::function<void()>& failure_callback);

View File

@@ -5,6 +5,7 @@ add_executable(tests
common/param_package.cpp
common/ring_buffer.cpp
core/core_timing.cpp
core/network/network.cpp
tests.cpp
video_core/buffer_base.cpp
)

View File

@@ -67,7 +67,7 @@ void TestControl1::DoWork() {
value++;
}
results[id] = value;
Fiber::YieldTo(work_fibers[id], thread_fibers[id]);
Fiber::YieldTo(work_fibers[id], *thread_fibers[id]);
}
void TestControl1::ExecuteThread(u32 id) {
@@ -76,7 +76,7 @@ void TestControl1::ExecuteThread(u32 id) {
thread_fibers[id] = thread_fiber;
work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
items[id] = rand() % 256;
Fiber::YieldTo(thread_fibers[id], work_fibers[id]);
Fiber::YieldTo(thread_fibers[id], *work_fibers[id]);
thread_fibers[id]->Exit();
}
@@ -117,11 +117,11 @@ public:
for (u32 i = 0; i < 12000; i++) {
value1 += i;
}
Fiber::YieldTo(fiber1, fiber3);
Fiber::YieldTo(fiber1, *fiber3);
const u32 id = thread_ids.Get();
assert1 = id == 1;
value2 += 5000;
Fiber::YieldTo(fiber1, thread_fibers[id]);
Fiber::YieldTo(fiber1, *thread_fibers[id]);
}
void DoWork2() {
@@ -129,7 +129,7 @@ public:
;
value2 = 2000;
trap = false;
Fiber::YieldTo(fiber2, fiber1);
Fiber::YieldTo(fiber2, *fiber1);
assert3 = false;
}
@@ -137,19 +137,19 @@ public:
const u32 id = thread_ids.Get();
assert2 = id == 0;
value1 += 1000;
Fiber::YieldTo(fiber3, thread_fibers[id]);
Fiber::YieldTo(fiber3, *thread_fibers[id]);
}
void ExecuteThread(u32 id);
void CallFiber1() {
const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], fiber1);
Fiber::YieldTo(thread_fibers[id], *fiber1);
}
void CallFiber2() {
const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], fiber2);
Fiber::YieldTo(thread_fibers[id], *fiber2);
}
void Exit();
@@ -241,23 +241,23 @@ public:
void DoWork1() {
value1 += 1;
Fiber::YieldTo(fiber1, fiber2);
Fiber::YieldTo(fiber1, *fiber2);
const u32 id = thread_ids.Get();
value3 += 1;
Fiber::YieldTo(fiber1, thread_fibers[id]);
Fiber::YieldTo(fiber1, *thread_fibers[id]);
}
void DoWork2() {
value2 += 1;
const u32 id = thread_ids.Get();
Fiber::YieldTo(fiber2, thread_fibers[id]);
Fiber::YieldTo(fiber2, *thread_fibers[id]);
}
void ExecuteThread(u32 id);
void CallFiber1() {
const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], fiber1);
Fiber::YieldTo(thread_fibers[id], *fiber1);
}
void Exit();
@@ -332,7 +332,7 @@ public:
void Execute() {
thread_fiber = Fiber::ThreadToFiber();
Fiber::YieldTo(thread_fiber, fiber1);
Fiber::YieldTo(thread_fiber, *fiber1);
thread_fiber->Exit();
}
@@ -340,7 +340,7 @@ public:
fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this);
if (rewinded) {
goal_reached = true;
Fiber::YieldTo(fiber1, thread_fiber);
Fiber::YieldTo(fiber1, *thread_fiber);
}
rewinded = true;
fiber1->Rewind();

View File

@@ -0,0 +1,28 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <catch2/catch.hpp>
#include "core/network/network.h"
#include "core/network/sockets.h"
TEST_CASE("Network::Errors", "[core]") {
Network::NetworkInstance network_instance; // initialize network
Network::Socket socks[2];
for (Network::Socket& sock : socks) {
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
Network::Protocol::TCP) == Network::Errno::SUCCESS);
}
Network::SockAddrIn addr{
Network::Domain::INET,
{127, 0, 0, 1},
1, // hopefully nobody running this test has something listening on port 1
};
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
std::vector<u8> message{1, 2, 3, 4};
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN);
}

View File

@@ -9,6 +9,7 @@
#include <deque>
#include <memory>
#include <mutex>
#include <numeric>
#include <span>
#include <unordered_map>
#include <vector>
@@ -91,7 +92,7 @@ class BufferCache {
};
public:
static constexpr u32 SKIP_CACHE_SIZE = 4096;
static constexpr u32 DEFAULT_SKIP_CACHE_SIZE = 4096;
explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
Tegra::Engines::Maxwell3D& maxwell3d_,
@@ -240,9 +241,9 @@ private:
template <bool insert>
void ChangeRegister(BufferId buffer_id);
void SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
void SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
std::span<BufferCopy> copies);
@@ -297,6 +298,11 @@ private:
std::array<u32, NUM_STAGES> fast_bound_uniform_buffers{};
std::array<u32, 16> uniform_cache_hits{};
std::array<u32, 16> uniform_cache_shots{};
u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
bool has_deleted_buffers = false;
std::conditional_t<HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS, std::array<u32, NUM_STAGES>, Empty>
@@ -328,6 +334,19 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
template <class P>
void BufferCache<P>::TickFrame() {
// Calculate hits and shots and move hit bits to the right
const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end());
const u32 shots = std::reduce(uniform_cache_shots.begin(), uniform_cache_shots.end());
std::copy_n(uniform_cache_hits.begin(), uniform_cache_hits.size() - 1,
uniform_cache_hits.begin() + 1);
std::copy_n(uniform_cache_shots.begin(), uniform_cache_shots.size() - 1,
uniform_cache_shots.begin() + 1);
uniform_cache_hits[0] = 0;
uniform_cache_shots[0] = 0;
const bool skip_preferred = hits * 256 < shots * 251;
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
delayed_destruction_ring.Tick();
}
@@ -671,7 +690,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
const VAddr cpu_addr = binding.cpu_addr;
const u32 size = binding.size;
Buffer& buffer = slot_buffers[binding.buffer_id];
if (size <= SKIP_CACHE_SIZE && !buffer.IsRegionGpuModified(cpu_addr, size)) {
if (size <= uniform_buffer_skip_cache_size && !buffer.IsRegionGpuModified(cpu_addr, size)) {
if constexpr (IS_OPENGL) {
if (runtime.HasFastBufferSubData()) {
// Fast path for Nvidia
@@ -692,7 +711,12 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
return;
}
// Classic cached path
SynchronizeBuffer(buffer, cpu_addr, size);
const bool sync_cached = SynchronizeBuffer(buffer, cpu_addr, size);
if (sync_cached) {
++uniform_cache_hits[0];
}
++uniform_cache_shots[0];
if (!needs_bind && !HasFastUniformBufferBound(stage, binding_index)) {
// Skip binding if it's not needed and if the bound buffer is not the fast version
// This exists to avoid instances where the fast buffer is bound and a GPU write happens
@@ -1106,15 +1130,15 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
}
template <class P>
void BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
if (buffer.CpuAddr() == 0) {
return;
return true;
}
SynchronizeBufferImpl(buffer, cpu_addr, size);
return SynchronizeBufferImpl(buffer, cpu_addr, size);
}
template <class P>
void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
boost::container::small_vector<BufferCopy, 4> copies;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
@@ -1128,10 +1152,11 @@ void BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
largest_copy = std::max(largest_copy, range_size);
});
if (total_size_bytes == 0) {
return;
return true;
}
const std::span<BufferCopy> copies_span(copies.data(), copies.size());
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
return false;
}
template <class P>

View File

@@ -48,6 +48,15 @@ constexpr std::array VIEW_CLASS_32_BITS{
PixelFormat::A2B10G10R10_UINT,
};
constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{
PixelFormat::R16G16_FLOAT, PixelFormat::B10G11R11_FLOAT, PixelFormat::R32_FLOAT,
PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT,
PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM,
PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM,
PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::A8B8G8R8_UINT,
PixelFormat::A8B8G8R8_SINT, PixelFormat::A2B10G10R10_UINT,
};
// TODO: How should we handle 24 bits?
constexpr std::array VIEW_CLASS_16_BITS{
@@ -205,7 +214,6 @@ constexpr Table MakeViewTable() {
EnableRange(view, VIEW_CLASS_128_BITS);
EnableRange(view, VIEW_CLASS_96_BITS);
EnableRange(view, VIEW_CLASS_64_BITS);
EnableRange(view, VIEW_CLASS_32_BITS);
EnableRange(view, VIEW_CLASS_16_BITS);
EnableRange(view, VIEW_CLASS_8_BITS);
EnableRange(view, VIEW_CLASS_RGTC1_RED);
@@ -231,20 +239,47 @@ constexpr Table MakeCopyTable() {
EnableRange(copy, COPY_CLASS_64_BITS);
return copy;
}
constexpr Table MakeNativeBgrViewTable() {
Table copy = MakeViewTable();
EnableRange(copy, VIEW_CLASS_32_BITS);
return copy;
}
constexpr Table MakeNonNativeBgrViewTable() {
Table copy = MakeViewTable();
EnableRange(copy, VIEW_CLASS_32_BITS_NO_BGR);
return copy;
}
constexpr Table MakeNativeBgrCopyTable() {
Table copy = MakeCopyTable();
EnableRange(copy, VIEW_CLASS_32_BITS);
return copy;
}
constexpr Table MakeNonNativeBgrCopyTable() {
Table copy = MakeCopyTable();
EnableRange(copy, VIEW_CLASS_32_BITS);
return copy;
}
} // Anonymous namespace
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) {
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
bool native_bgr) {
if (broken_views) {
// If format views are broken, only accept formats that are identical.
return format_a == format_b;
}
static constexpr Table TABLE = MakeViewTable();
return IsSupported(TABLE, format_a, format_b);
static constexpr Table BGR_TABLE = MakeNativeBgrViewTable();
static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrViewTable();
return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
}
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b) {
static constexpr Table TABLE = MakeCopyTable();
return IsSupported(TABLE, format_a, format_b);
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) {
static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable();
static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable();
return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
}
} // namespace VideoCore::Surface

View File

@@ -8,8 +8,9 @@
namespace VideoCore::Surface {
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views);
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
bool native_bgr);
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b);
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr);
} // namespace VideoCore::Surface

View File

@@ -459,7 +459,7 @@ void GPU::ProcessSemaphoreAcquire() {
}
void GPU::Start() {
gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher);
cpu_context = renderer->GetRenderWindow().CreateSharedContext();
cpu_context->MakeCurrent();
}

View File

@@ -19,7 +19,7 @@ namespace VideoCommon::GPUThread {
/// Runs the GPU thread
static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
SynchState& state, Tegra::CDmaPusher& cdma_pusher) {
SynchState& state) {
std::string name = "yuzu:GPU";
MicroProfileOnThreadCreate(name.c_str());
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
@@ -46,9 +46,6 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
dma_pusher.Push(std::move(submit_list->entries));
dma_pusher.DispatchCalls();
} else if (auto* command_list = std::get_if<SubmitChCommandEntries>(&next.data)) {
// NVDEC
cdma_pusher.ProcessEntries(std::move(command_list->entries));
} else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) {
renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
} else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
@@ -83,20 +80,16 @@ ThreadManager::~ThreadManager() {
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher, Tegra::CDmaPusher& cdma_pusher) {
Tegra::DmaPusher& dma_pusher) {
rasterizer = renderer.ReadRasterizer();
thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
std::ref(dma_pusher), std::ref(state), std::ref(cdma_pusher));
std::ref(dma_pusher), std::ref(state));
}
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
PushCommand(SubmitListCommand(std::move(entries)));
}
void ThreadManager::SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries) {
PushCommand(SubmitChCommandEntries(std::move(entries)));
}
void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt));
}

View File

@@ -43,14 +43,6 @@ struct SubmitListCommand final {
Tegra::CommandList entries;
};
/// Command to signal to the GPU thread that a cdma command list is ready for processing
struct SubmitChCommandEntries final {
explicit SubmitChCommandEntries(Tegra::ChCommandHeaderList&& entries_)
: entries{std::move(entries_)} {}
Tegra::ChCommandHeaderList entries;
};
/// Command to signal to the GPU thread that a swap buffers is pending
struct SwapBuffersCommand final {
explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer_)
@@ -91,9 +83,9 @@ struct OnCommandListEndCommand final {};
struct GPUTickCommand final {};
using CommandData =
std::variant<EndProcessingCommand, SubmitListCommand, SubmitChCommandEntries,
SwapBuffersCommand, FlushRegionCommand, InvalidateRegionCommand,
FlushAndInvalidateRegionCommand, OnCommandListEndCommand, GPUTickCommand>;
std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
GPUTickCommand>;
struct CommandDataContainer {
CommandDataContainer() = default;
@@ -123,14 +115,11 @@ public:
/// Creates and starts the GPU thread.
void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher, Tegra::CDmaPusher& cdma_pusher);
Tegra::DmaPusher& dma_pusher);
/// Push GPU command entries to be processed
void SubmitList(Tegra::CommandList&& entries);
/// Push GPU CDMA command buffer entries to be processed
void SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries);
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);

View File

@@ -5,6 +5,7 @@ set(SHADER_FILES
convert_float_to_depth.frag
full_screen_triangle.vert
opengl_copy_bc4.comp
opengl_copy_bgra.comp
opengl_present.frag
opengl_present.vert
pitch_unswizzle.comp

View File

@@ -0,0 +1,15 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 430 core
layout (local_size_x = 4, local_size_y = 4) in;
layout(binding = 0, rgba8) readonly uniform image2DArray bgr_input;
layout(binding = 1, rgba8) writeonly uniform image2DArray bgr_output;
void main() {
vec4 color = imageLoad(bgr_input, ivec3(gl_GlobalInvocationID));
imageStore(bgr_output, ivec3(gl_GlobalInvocationID), color.bgra);
}

View File

@@ -6,7 +6,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/process.h"
#include "core/memory.h"
#include "video_core/gpu.h"

View File

@@ -73,7 +73,8 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_)
for (auto& stage_uniforms : fast_uniforms) {
for (OGLBuffer& buffer : stage_uniforms) {
buffer.Create();
glNamedBufferData(buffer.handle, BufferCache::SKIP_CACHE_SIZE, nullptr, GL_STREAM_DRAW);
glNamedBufferData(buffer.handle, BufferCache::DEFAULT_SKIP_CACHE_SIZE, nullptr,
GL_STREAM_DRAW);
}
}
for (auto& stage_uniforms : copy_uniforms) {

View File

@@ -96,7 +96,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT
{GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
{GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT
{GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT
{GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT
@@ -125,7 +125,7 @@ constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{
{GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM
{GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM
{GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM
{GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE}, // B8G8R8A8_SRGB
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
@@ -396,6 +396,17 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) {
}
}
[[nodiscard]] bool IsPixelFormatBGR(PixelFormat format) {
switch (format) {
case PixelFormat::B5G6R5_UNORM:
case PixelFormat::B8G8R8A8_UNORM:
case PixelFormat::B8G8R8A8_SRGB:
return true;
default:
return false;
}
}
} // Anonymous namespace
ImageBufferMap::~ImageBufferMap() {
@@ -512,6 +523,9 @@ bool TextureCacheRuntime::CanImageBeCopied(const Image& dst, const Image& src) {
if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
return false;
}
if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
return false;
}
return true;
}
@@ -520,6 +534,8 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
if (dst.info.type == ImageType::e3D && dst.info.format == PixelFormat::BC4_UNORM) {
ASSERT(src.info.type == ImageType::e3D);
util_shaders.CopyBC4(dst, src, copies);
} else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
util_shaders.CopyBGR(dst, src, copies);
} else {
UNREACHABLE();
}

View File

@@ -86,6 +86,11 @@ public:
FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const;
bool HasNativeBgr() const noexcept {
// OpenGL does not have native support for the BGR internal format
return false;
}
bool HasBrokenTextureViewFormats() const noexcept {
return has_broken_texture_view_formats;
}

View File

@@ -14,6 +14,7 @@
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
#include "video_core/host_shaders/opengl_copy_bgra_comp.h"
#include "video_core/host_shaders/pitch_unswizzle_comp.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -48,6 +49,11 @@ OGLProgram MakeProgram(std::string_view source) {
return program;
}
size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) {
return static_cast<size_t>(copy.extent.width * copy.extent.height *
copy.src_subresource.num_layers);
}
} // Anonymous namespace
UtilShaders::UtilShaders(ProgramManager& program_manager_)
@@ -55,6 +61,7 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)),
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
swizzle_table_buffer.Create();
@@ -205,6 +212,43 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
program_manager.RestoreGuestCompute();
}
void UtilShaders::CopyBGR(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies) {
static constexpr GLuint BINDING_INPUT_IMAGE = 0;
static constexpr GLuint BINDING_OUTPUT_IMAGE = 1;
static constexpr VideoCommon::Offset3D zero_offset{0, 0, 0};
const u32 bytes_per_block = BytesPerBlock(dst_image.info.format);
switch (bytes_per_block) {
case 2:
// BGR565 copy
for (const ImageCopy& copy : copies) {
ASSERT(copy.src_offset == zero_offset);
ASSERT(copy.dst_offset == zero_offset);
bgr_copy_pass.Execute(dst_image, src_image, copy);
}
break;
case 4: {
// BGRA8 copy
program_manager.BindHostCompute(copy_bgra_program.handle);
constexpr GLenum FORMAT = GL_RGBA8;
for (const ImageCopy& copy : copies) {
ASSERT(copy.src_offset == zero_offset);
ASSERT(copy.dst_offset == zero_offset);
glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(),
copy.src_subresource.base_level, GL_FALSE, 0, GL_READ_ONLY, FORMAT);
glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(),
copy.dst_subresource.base_level, GL_FALSE, 0, GL_WRITE_ONLY, FORMAT);
glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth);
}
program_manager.RestoreGuestCompute();
break;
}
default:
UNREACHABLE();
break;
}
}
GLenum StoreFormat(u32 bytes_per_block) {
switch (bytes_per_block) {
case 1:
@@ -222,4 +266,36 @@ GLenum StoreFormat(u32 bytes_per_block) {
return GL_R8UI;
}
void Bgr565CopyPass::Execute(const Image& dst_image, const Image& src_image,
const ImageCopy& copy) {
if (CopyBufferCreationNeeded(copy)) {
CreateNewCopyBuffer(copy, GL_TEXTURE_2D_ARRAY, GL_RGB565);
}
// Copy from source to PBO
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ROW_LENGTH, copy.extent.width);
glBindBuffer(GL_PIXEL_PACK_BUFFER, bgr16_pbo.handle);
glGetTextureSubImage(src_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
copy.src_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
static_cast<GLsizei>(bgr16_pbo_size), nullptr);
// Copy from PBO to destination in reverse order
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, copy.extent.width);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bgr16_pbo.handle);
glTextureSubImage3D(dst_image.Handle(), 0, 0, 0, 0, copy.extent.width, copy.extent.height,
copy.dst_subresource.num_layers, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV,
nullptr);
}
bool Bgr565CopyPass::CopyBufferCreationNeeded(const ImageCopy& copy) {
return bgr16_pbo_size < NumPixelsInCopy(copy) * sizeof(u16);
}
void Bgr565CopyPass::CreateNewCopyBuffer(const ImageCopy& copy, GLenum target, GLuint format) {
bgr16_pbo.Create();
bgr16_pbo_size = NumPixelsInCopy(copy) * sizeof(u16);
glNamedBufferData(bgr16_pbo.handle, bgr16_pbo_size, nullptr, GL_STREAM_COPY);
}
} // namespace OpenGL

View File

@@ -19,6 +19,22 @@ class ProgramManager;
struct ImageBufferMap;
class Bgr565CopyPass {
public:
Bgr565CopyPass() = default;
~Bgr565CopyPass() = default;
void Execute(const Image& dst_image, const Image& src_image,
const VideoCommon::ImageCopy& copy);
private:
[[nodiscard]] bool CopyBufferCreationNeeded(const VideoCommon::ImageCopy& copy);
void CreateNewCopyBuffer(const VideoCommon::ImageCopy& copy, GLenum target, GLuint format);
OGLBuffer bgr16_pbo;
size_t bgr16_pbo_size{};
};
class UtilShaders {
public:
explicit UtilShaders(ProgramManager& program_manager);
@@ -36,6 +52,9 @@ public:
void CopyBC4(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies);
void CopyBGR(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies);
private:
ProgramManager& program_manager;
@@ -44,7 +63,10 @@ private:
OGLProgram block_linear_unswizzle_2d_program;
OGLProgram block_linear_unswizzle_3d_program;
OGLProgram pitch_unswizzle_program;
OGLProgram copy_bgra_program;
OGLProgram copy_bc4_program;
Bgr565CopyPass bgr_copy_pass;
};
GLenum StoreFormat(u32 bytes_per_block);

View File

@@ -10,7 +10,7 @@
namespace Vulkan {
constexpr size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
constexpr size_t COMMAND_BUFFER_POOL_SIZE = 4;
struct CommandPool::Pool {
vk::CommandPool handle;

View File

@@ -93,6 +93,11 @@ struct TextureCacheRuntime {
// No known Vulkan driver has broken image views
return false;
}
bool HasNativeBgr() const noexcept {
// All known Vulkan drivers can natively handle BGR textures
return true;
}
};
class Image : public VideoCommon::ImageBase {

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <mutex>
#include "video_core/shader_notify.h"
using namespace std::chrono_literals;

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