another try

This commit is contained in:
mgthepro
2022-11-05 13:58:44 +01:00
parent 4a9f2bbf2a
commit 9f63fbe700
2002 changed files with 671171 additions and 671092 deletions

View File

@@ -1,46 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
// This file contains yuzu's HLE API version constants.
namespace HLE::ApiVersion {
// Horizon OS version constants.
constexpr u8 HOS_VERSION_MAJOR = 12;
constexpr u8 HOS_VERSION_MINOR = 1;
constexpr u8 HOS_VERSION_MICRO = 0;
// NintendoSDK version constants.
constexpr u8 SDK_REVISION_MAJOR = 1;
constexpr u8 SDK_REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
constexpr char DISPLAY_VERSION[] = "12.1.0";
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
// Atmosphere version constants.
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 1;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 0;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 0;
constexpr u32 AtmosphereTargetFirmwareWithRevision(u8 major, u8 minor, u8 micro, u8 rev) {
return u32{major} << 24 | u32{minor} << 16 | u32{micro} << 8 | u32{rev};
}
constexpr u32 AtmosphereTargetFirmware(u8 major, u8 minor, u8 micro) {
return AtmosphereTargetFirmwareWithRevision(major, minor, micro, 0);
}
constexpr u32 GetTargetFirmware() {
return AtmosphereTargetFirmware(HOS_VERSION_MAJOR, HOS_VERSION_MINOR, HOS_VERSION_MICRO);
}
} // namespace HLE::ApiVersion
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
// This file contains yuzu's HLE API version constants.
namespace HLE::ApiVersion {
// Horizon OS version constants.
constexpr u8 HOS_VERSION_MAJOR = 12;
constexpr u8 HOS_VERSION_MINOR = 1;
constexpr u8 HOS_VERSION_MICRO = 0;
// NintendoSDK version constants.
constexpr u8 SDK_REVISION_MAJOR = 1;
constexpr u8 SDK_REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
constexpr char DISPLAY_VERSION[] = "12.1.0";
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
// Atmosphere version constants.
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 1;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 0;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 0;
constexpr u32 AtmosphereTargetFirmwareWithRevision(u8 major, u8 minor, u8 micro, u8 rev) {
return u32{major} << 24 | u32{minor} << 16 | u32{micro} << 8 | u32{rev};
}
constexpr u32 AtmosphereTargetFirmware(u8 major, u8 minor, u8 micro) {
return AtmosphereTargetFirmwareWithRevision(major, minor, micro, 0);
}
constexpr u32 GetTargetFirmware() {
return AtmosphereTargetFirmware(HOS_VERSION_MAJOR, HOS_VERSION_MINOR, HOS_VERSION_MICRO);
}
} // namespace HLE::ApiVersion

View File

@@ -1,196 +1,196 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
namespace IPC {
/// Size of the command buffer area, in 32-bit words.
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
enum class ControlCommand : u32 {
ConvertSessionToDomain = 0,
ConvertDomainToSession = 1,
DuplicateSession = 2,
QueryPointerBufferSize = 3,
DuplicateSessionEx = 4,
Unspecified,
};
enum class CommandType : u32 {
Invalid = 0,
LegacyRequest = 1,
Close = 2,
LegacyControl = 3,
Request = 4,
Control = 5,
RequestWithContext = 6,
ControlWithContext = 7,
TIPC_Close = 15,
TIPC_CommandRegion = 16, // Start of TIPC commands, this is an offset.
};
struct CommandHeader {
union {
u32_le raw_low;
BitField<0, 16, CommandType> type;
BitField<16, 4, u32> num_buf_x_descriptors;
BitField<20, 4, u32> num_buf_a_descriptors;
BitField<24, 4, u32> num_buf_b_descriptors;
BitField<28, 4, u32> num_buf_w_descriptors;
};
enum class BufferDescriptorCFlag : u32 {
Disabled = 0,
InlineDescriptor = 1,
OneDescriptor = 2,
};
union {
u32_le raw_high;
BitField<0, 10, u32> data_size;
BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags;
BitField<31, 1, u32> enable_handle_descriptor;
};
bool IsTipc() const {
return type.Value() >= CommandType::TIPC_CommandRegion;
}
bool IsCloseCommand() const {
switch (type.Value()) {
case CommandType::Close:
case CommandType::TIPC_Close:
return true;
default:
return false;
}
}
};
static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect");
union HandleDescriptorHeader {
u32_le raw_high;
BitField<0, 1, u32> send_current_pid;
BitField<1, 4, u32> num_handles_to_copy;
BitField<5, 4, u32> num_handles_to_move;
};
static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect");
struct BufferDescriptorX {
union {
BitField<0, 6, u32> counter_bits_0_5;
BitField<6, 3, u32> address_bits_36_38;
BitField<9, 3, u32> counter_bits_9_11;
BitField<12, 4, u32> address_bits_32_35;
BitField<16, 16, u32> size;
};
u32_le address_bits_0_31;
u32_le Counter() const {
u32_le counter{counter_bits_0_5};
counter |= counter_bits_9_11 << 9;
return counter;
}
VAddr Address() const {
VAddr address{address_bits_0_31};
address |= static_cast<VAddr>(address_bits_32_35) << 32;
address |= static_cast<VAddr>(address_bits_36_38) << 36;
return address;
}
u64 Size() const {
return static_cast<u64>(size);
}
};
static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect");
struct BufferDescriptorABW {
u32_le size_bits_0_31;
u32_le address_bits_0_31;
union {
BitField<0, 2, u32> flags;
BitField<2, 3, u32> address_bits_36_38;
BitField<24, 4, u32> size_bits_32_35;
BitField<28, 4, u32> address_bits_32_35;
};
VAddr Address() const {
VAddr address{address_bits_0_31};
address |= static_cast<VAddr>(address_bits_32_35) << 32;
address |= static_cast<VAddr>(address_bits_36_38) << 36;
return address;
}
u64 Size() const {
u64 size{size_bits_0_31};
size |= static_cast<u64>(size_bits_32_35) << 32;
return size;
}
};
static_assert(sizeof(BufferDescriptorABW) == 12, "BufferDescriptorABW size is incorrect");
struct BufferDescriptorC {
u32_le address_bits_0_31;
union {
BitField<0, 16, u32> address_bits_32_47;
BitField<16, 16, u32> size;
};
VAddr Address() const {
VAddr address{address_bits_0_31};
address |= static_cast<VAddr>(address_bits_32_47) << 32;
return address;
}
u64 Size() const {
return static_cast<u64>(size);
}
};
static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorrect");
struct DataPayloadHeader {
u32_le magic;
INSERT_PADDING_WORDS_NOINIT(1);
};
static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect");
struct DomainMessageHeader {
enum class CommandType : u32_le {
SendMessage = 1,
CloseVirtualHandle = 2,
};
union {
// Used when responding to an IPC request, Server -> Client.
struct {
u32_le num_objects;
INSERT_PADDING_WORDS_NOINIT(3);
};
// Used when performing an IPC request, Client -> Server.
struct {
union {
BitField<0, 8, CommandType> command;
BitField<8, 8, u32> input_object_count;
BitField<16, 16, u32> size;
};
u32_le object_id;
INSERT_PADDING_WORDS_NOINIT(2);
};
std::array<u32, 4> raw;
};
};
static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect");
} // namespace IPC
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
namespace IPC {
/// Size of the command buffer area, in 32-bit words.
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
enum class ControlCommand : u32 {
ConvertSessionToDomain = 0,
ConvertDomainToSession = 1,
DuplicateSession = 2,
QueryPointerBufferSize = 3,
DuplicateSessionEx = 4,
Unspecified,
};
enum class CommandType : u32 {
Invalid = 0,
LegacyRequest = 1,
Close = 2,
LegacyControl = 3,
Request = 4,
Control = 5,
RequestWithContext = 6,
ControlWithContext = 7,
TIPC_Close = 15,
TIPC_CommandRegion = 16, // Start of TIPC commands, this is an offset.
};
struct CommandHeader {
union {
u32_le raw_low;
BitField<0, 16, CommandType> type;
BitField<16, 4, u32> num_buf_x_descriptors;
BitField<20, 4, u32> num_buf_a_descriptors;
BitField<24, 4, u32> num_buf_b_descriptors;
BitField<28, 4, u32> num_buf_w_descriptors;
};
enum class BufferDescriptorCFlag : u32 {
Disabled = 0,
InlineDescriptor = 1,
OneDescriptor = 2,
};
union {
u32_le raw_high;
BitField<0, 10, u32> data_size;
BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags;
BitField<31, 1, u32> enable_handle_descriptor;
};
bool IsTipc() const {
return type.Value() >= CommandType::TIPC_CommandRegion;
}
bool IsCloseCommand() const {
switch (type.Value()) {
case CommandType::Close:
case CommandType::TIPC_Close:
return true;
default:
return false;
}
}
};
static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect");
union HandleDescriptorHeader {
u32_le raw_high;
BitField<0, 1, u32> send_current_pid;
BitField<1, 4, u32> num_handles_to_copy;
BitField<5, 4, u32> num_handles_to_move;
};
static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect");
struct BufferDescriptorX {
union {
BitField<0, 6, u32> counter_bits_0_5;
BitField<6, 3, u32> address_bits_36_38;
BitField<9, 3, u32> counter_bits_9_11;
BitField<12, 4, u32> address_bits_32_35;
BitField<16, 16, u32> size;
};
u32_le address_bits_0_31;
u32_le Counter() const {
u32_le counter{counter_bits_0_5};
counter |= counter_bits_9_11 << 9;
return counter;
}
VAddr Address() const {
VAddr address{address_bits_0_31};
address |= static_cast<VAddr>(address_bits_32_35) << 32;
address |= static_cast<VAddr>(address_bits_36_38) << 36;
return address;
}
u64 Size() const {
return static_cast<u64>(size);
}
};
static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect");
struct BufferDescriptorABW {
u32_le size_bits_0_31;
u32_le address_bits_0_31;
union {
BitField<0, 2, u32> flags;
BitField<2, 3, u32> address_bits_36_38;
BitField<24, 4, u32> size_bits_32_35;
BitField<28, 4, u32> address_bits_32_35;
};
VAddr Address() const {
VAddr address{address_bits_0_31};
address |= static_cast<VAddr>(address_bits_32_35) << 32;
address |= static_cast<VAddr>(address_bits_36_38) << 36;
return address;
}
u64 Size() const {
u64 size{size_bits_0_31};
size |= static_cast<u64>(size_bits_32_35) << 32;
return size;
}
};
static_assert(sizeof(BufferDescriptorABW) == 12, "BufferDescriptorABW size is incorrect");
struct BufferDescriptorC {
u32_le address_bits_0_31;
union {
BitField<0, 16, u32> address_bits_32_47;
BitField<16, 16, u32> size;
};
VAddr Address() const {
VAddr address{address_bits_0_31};
address |= static_cast<VAddr>(address_bits_32_47) << 32;
return address;
}
u64 Size() const {
return static_cast<u64>(size);
}
};
static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorrect");
struct DataPayloadHeader {
u32_le magic;
INSERT_PADDING_WORDS_NOINIT(1);
};
static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect");
struct DomainMessageHeader {
enum class CommandType : u32_le {
SendMessage = 1,
CloseVirtualHandle = 2,
};
union {
// Used when responding to an IPC request, Server -> Client.
struct {
u32_le num_objects;
INSERT_PADDING_WORDS_NOINIT(3);
};
// Used when performing an IPC request, Client -> Server.
struct {
union {
BitField<0, 8, CommandType> command;
BitField<8, 8, u32> input_object_count;
BitField<16, 16, u32> size;
};
u32_le object_id;
INSERT_PADDING_WORDS_NOINIT(2);
};
std::array<u32, 4> raw;
};
};
static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect");
} // namespace IPC

View File

@@ -1,499 +1,499 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstring>
#include <memory>
#include <type_traits>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/result.h"
namespace IPC {
constexpr Result ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
class RequestHelperBase {
protected:
Kernel::HLERequestContext* context = nullptr;
u32* cmdbuf;
u32 index = 0;
public:
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
explicit RequestHelperBase(Kernel::HLERequestContext& ctx)
: context(&ctx), cmdbuf(ctx.CommandBuffer()) {}
void Skip(u32 size_in_words, bool set_to_null) {
if (set_to_null) {
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
}
index += size_in_words;
}
/**
* Aligns the current position forward to a 16-byte boundary, padding with zeros.
*/
void AlignWithPadding() {
if (index & 3) {
Skip(static_cast<u32>(4 - (index & 3)), true);
}
}
u32 GetCurrentOffset() const {
return index;
}
void SetCurrentOffset(u32 offset) {
index = offset;
}
};
class ResponseBuilder : public RequestHelperBase {
public:
/// Flags used for customizing the behavior of ResponseBuilder
enum class Flags : u32 {
None = 0,
/// Uses move handles to move objects in the response, even when in a domain. This is
/// required when PushMoveObjects is used.
AlwaysMoveHandles = 1,
};
explicit ResponseBuilder(Kernel::HLERequestContext& ctx, u32 normal_params_size_,
u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0,
Flags flags = Flags::None)
: RequestHelperBase(ctx), normal_params_size(normal_params_size_),
num_handles_to_copy(num_handles_to_copy_),
num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} {
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
IPC::CommandHeader header{};
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
// padding.
u32 raw_data_size = ctx.write_size =
ctx.IsTipc() ? normal_params_size - 1 : normal_params_size;
u32 num_handles_to_move{};
u32 num_domain_objects{};
const bool always_move_handles{
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
if (!ctx.GetManager()->IsDomain() || always_move_handles) {
num_handles_to_move = num_objects_to_move;
} else {
num_domain_objects = num_objects_to_move;
}
if (ctx.GetManager()->IsDomain()) {
raw_data_size +=
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
ctx.write_size += num_domain_objects;
}
if (ctx.IsTipc()) {
header.type.Assign(ctx.GetCommandType());
} else {
raw_data_size += static_cast<u32>(sizeof(IPC::DataPayloadHeader) / sizeof(u32) + 4 +
normal_params_size);
}
header.data_size.Assign(raw_data_size);
if (num_handles_to_copy || num_handles_to_move) {
header.enable_handle_descriptor.Assign(1);
}
PushRaw(header);
if (header.enable_handle_descriptor) {
IPC::HandleDescriptorHeader handle_descriptor_header{};
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_);
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
PushRaw(handle_descriptor_header);
ctx.handles_offset = index;
Skip(num_handles_to_copy + num_handles_to_move, true);
}
if (!ctx.IsTipc()) {
AlignWithPadding();
if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
}
IPC::DataPayloadHeader data_payload_header{};
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
PushRaw(data_payload_header);
}
data_payload_index = index;
ctx.data_payload_offset = index;
ctx.write_size += index;
ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
}
template <class T>
void PushIpcInterface(std::shared_ptr<T> iface) {
if (context->GetManager()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
Kernel::LimitableResource::Sessions, 1);
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, iface->GetServiceName());
iface->RegisterSession(&session->GetServerSession(),
std::make_shared<Kernel::SessionRequestManager>(kernel));
context->AddMoveObject(&session->GetClientSession());
}
}
template <class T, class... Args>
void PushIpcInterface(Args&&... args) {
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
}
void PushImpl(s8 value);
void PushImpl(s16 value);
void PushImpl(s32 value);
void PushImpl(s64 value);
void PushImpl(u8 value);
void PushImpl(u16 value);
void PushImpl(u32 value);
void PushImpl(u64 value);
void PushImpl(float value);
void PushImpl(double value);
void PushImpl(bool value);
void PushImpl(Result value);
template <typename T>
void Push(T value) {
return PushImpl(value);
}
template <typename First, typename... Other>
void Push(const First& first_value, const Other&... other_values);
/**
* Helper function for pushing strongly-typed enumeration values.
*
* @tparam Enum The enumeration type to be pushed
*
* @param value The value to push.
*
* @note The underlying size of the enumeration type is the size of the
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
* push a u16-sized amount of data.
*/
template <typename Enum>
void PushEnum(Enum value) {
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
static_assert(!std::is_convertible_v<Enum, int>,
"enum type in PushEnum must be a strongly typed enum.");
Push(static_cast<std::underlying_type_t<Enum>>(value));
}
/**
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
* param
* @note: The input class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
void PushRaw(const T& value);
template <typename... O>
void PushMoveObjects(O*... pointers);
template <typename... O>
void PushMoveObjects(O&... pointers);
template <typename... O>
void PushCopyObjects(O*... pointers);
template <typename... O>
void PushCopyObjects(O&... pointers);
private:
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
u32 data_payload_index{};
Kernel::KernelCore& kernel;
};
/// Push ///
inline void ResponseBuilder::PushImpl(s32 value) {
cmdbuf[index++] = value;
}
inline void ResponseBuilder::PushImpl(u32 value) {
cmdbuf[index++] = value;
}
template <typename T>
void ResponseBuilder::PushRaw(const T& value) {
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
inline void ResponseBuilder::PushImpl(Result value) {
// Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded.
Push(value.raw);
Push<u32>(0);
}
inline void ResponseBuilder::PushImpl(s8 value) {
PushRaw(value);
}
inline void ResponseBuilder::PushImpl(s16 value) {
PushRaw(value);
}
inline void ResponseBuilder::PushImpl(s64 value) {
PushImpl(static_cast<u32>(value));
PushImpl(static_cast<u32>(value >> 32));
}
inline void ResponseBuilder::PushImpl(u8 value) {
PushRaw(value);
}
inline void ResponseBuilder::PushImpl(u16 value) {
PushRaw(value);
}
inline void ResponseBuilder::PushImpl(u64 value) {
PushImpl(static_cast<u32>(value));
PushImpl(static_cast<u32>(value >> 32));
}
inline void ResponseBuilder::PushImpl(float value) {
u32 integral;
std::memcpy(&integral, &value, sizeof(u32));
PushImpl(integral);
}
inline void ResponseBuilder::PushImpl(double value) {
u64 integral;
std::memcpy(&integral, &value, sizeof(u64));
PushImpl(integral);
}
inline void ResponseBuilder::PushImpl(bool value) {
PushImpl(static_cast<u8>(value));
}
template <typename First, typename... Other>
void ResponseBuilder::Push(const First& first_value, const Other&... other_values) {
Push(first_value);
Push(other_values...);
}
template <typename... O>
inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
context->AddCopyObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
auto objects = {&pointers...};
for (auto& object : objects) {
context->AddCopyObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
context->AddMoveObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
auto objects = {&pointers...};
for (auto& object : objects) {
context->AddMoveObject(object);
}
}
class RequestParser : public RequestHelperBase {
public:
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
explicit RequestParser(Kernel::HLERequestContext& ctx) : RequestHelperBase(ctx) {
// TIPC does not have data payload offset
if (!ctx.IsTipc()) {
ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
Skip(ctx.GetDataPayloadOffset(), false);
}
// Skip the u64 command id, it's already stored in the context
static constexpr u32 CommandIdSize = 2;
Skip(CommandIdSize, false);
}
template <typename T>
T Pop();
template <typename T>
void Pop(T& value);
template <typename First, typename... Other>
void Pop(First& first_value, Other&... other_values);
template <typename T>
T PopEnum() {
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
static_assert(!std::is_convertible_v<T, int>,
"enum type in PopEnum must be a strongly typed enum.");
return static_cast<T>(Pop<std::underlying_type_t<T>>());
}
/**
* @brief Reads the next normal parameters as a struct, by copying it
* @note: The output class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
void PopRaw(T& value);
/**
* @brief Reads the next normal parameters as a struct, by copying it into a new value
* @note: The output class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
T PopRaw();
template <class T>
std::weak_ptr<T> PopIpcInterface() {
ASSERT(context->GetManager()->IsDomain());
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainHandler<T>(Pop<u32>() - 1);
}
};
/// Pop ///
template <>
inline u32 RequestParser::Pop() {
return cmdbuf[index++];
}
template <>
inline s32 RequestParser::Pop() {
return static_cast<s32>(Pop<u32>());
}
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
template <typename T>
void RequestParser::PopRaw(T& value) {
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic pop
#endif
template <typename T>
T RequestParser::PopRaw() {
T value;
PopRaw(value);
return value;
}
template <>
inline u8 RequestParser::Pop() {
return PopRaw<u8>();
}
template <>
inline u16 RequestParser::Pop() {
return PopRaw<u16>();
}
template <>
inline u64 RequestParser::Pop() {
const u64 lsw = Pop<u32>();
const u64 msw = Pop<u32>();
return msw << 32 | lsw;
}
template <>
inline s8 RequestParser::Pop() {
return static_cast<s8>(Pop<u8>());
}
template <>
inline s16 RequestParser::Pop() {
return static_cast<s16>(Pop<u16>());
}
template <>
inline s64 RequestParser::Pop() {
return static_cast<s64>(Pop<u64>());
}
template <>
inline float RequestParser::Pop() {
const u32 value = Pop<u32>();
float real;
std::memcpy(&real, &value, sizeof(real));
return real;
}
template <>
inline double RequestParser::Pop() {
const u64 value = Pop<u64>();
double real;
std::memcpy(&real, &value, sizeof(real));
return real;
}
template <>
inline bool RequestParser::Pop() {
return Pop<u8>() != 0;
}
template <>
inline Result RequestParser::Pop() {
return Result{Pop<u32>()};
}
template <typename T>
void RequestParser::Pop(T& value) {
value = Pop<T>();
}
template <typename First, typename... Other>
void RequestParser::Pop(First& first_value, Other&... other_values) {
first_value = Pop<First>();
Pop(other_values...);
}
} // namespace IPC
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstring>
#include <memory>
#include <type_traits>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/result.h"
namespace IPC {
constexpr Result ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
class RequestHelperBase {
protected:
Kernel::HLERequestContext* context = nullptr;
u32* cmdbuf;
u32 index = 0;
public:
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
explicit RequestHelperBase(Kernel::HLERequestContext& ctx)
: context(&ctx), cmdbuf(ctx.CommandBuffer()) {}
void Skip(u32 size_in_words, bool set_to_null) {
if (set_to_null) {
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
}
index += size_in_words;
}
/**
* Aligns the current position forward to a 16-byte boundary, padding with zeros.
*/
void AlignWithPadding() {
if (index & 3) {
Skip(static_cast<u32>(4 - (index & 3)), true);
}
}
u32 GetCurrentOffset() const {
return index;
}
void SetCurrentOffset(u32 offset) {
index = offset;
}
};
class ResponseBuilder : public RequestHelperBase {
public:
/// Flags used for customizing the behavior of ResponseBuilder
enum class Flags : u32 {
None = 0,
/// Uses move handles to move objects in the response, even when in a domain. This is
/// required when PushMoveObjects is used.
AlwaysMoveHandles = 1,
};
explicit ResponseBuilder(Kernel::HLERequestContext& ctx, u32 normal_params_size_,
u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0,
Flags flags = Flags::None)
: RequestHelperBase(ctx), normal_params_size(normal_params_size_),
num_handles_to_copy(num_handles_to_copy_),
num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} {
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
IPC::CommandHeader header{};
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
// padding.
u32 raw_data_size = ctx.write_size =
ctx.IsTipc() ? normal_params_size - 1 : normal_params_size;
u32 num_handles_to_move{};
u32 num_domain_objects{};
const bool always_move_handles{
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
if (!ctx.GetManager()->IsDomain() || always_move_handles) {
num_handles_to_move = num_objects_to_move;
} else {
num_domain_objects = num_objects_to_move;
}
if (ctx.GetManager()->IsDomain()) {
raw_data_size +=
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
ctx.write_size += num_domain_objects;
}
if (ctx.IsTipc()) {
header.type.Assign(ctx.GetCommandType());
} else {
raw_data_size += static_cast<u32>(sizeof(IPC::DataPayloadHeader) / sizeof(u32) + 4 +
normal_params_size);
}
header.data_size.Assign(raw_data_size);
if (num_handles_to_copy || num_handles_to_move) {
header.enable_handle_descriptor.Assign(1);
}
PushRaw(header);
if (header.enable_handle_descriptor) {
IPC::HandleDescriptorHeader handle_descriptor_header{};
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_);
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
PushRaw(handle_descriptor_header);
ctx.handles_offset = index;
Skip(num_handles_to_copy + num_handles_to_move, true);
}
if (!ctx.IsTipc()) {
AlignWithPadding();
if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
}
IPC::DataPayloadHeader data_payload_header{};
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
PushRaw(data_payload_header);
}
data_payload_index = index;
ctx.data_payload_offset = index;
ctx.write_size += index;
ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
}
template <class T>
void PushIpcInterface(std::shared_ptr<T> iface) {
if (context->GetManager()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
Kernel::LimitableResource::Sessions, 1);
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, iface->GetServiceName());
iface->RegisterSession(&session->GetServerSession(),
std::make_shared<Kernel::SessionRequestManager>(kernel));
context->AddMoveObject(&session->GetClientSession());
}
}
template <class T, class... Args>
void PushIpcInterface(Args&&... args) {
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
}
void PushImpl(s8 value);
void PushImpl(s16 value);
void PushImpl(s32 value);
void PushImpl(s64 value);
void PushImpl(u8 value);
void PushImpl(u16 value);
void PushImpl(u32 value);
void PushImpl(u64 value);
void PushImpl(float value);
void PushImpl(double value);
void PushImpl(bool value);
void PushImpl(Result value);
template <typename T>
void Push(T value) {
return PushImpl(value);
}
template <typename First, typename... Other>
void Push(const First& first_value, const Other&... other_values);
/**
* Helper function for pushing strongly-typed enumeration values.
*
* @tparam Enum The enumeration type to be pushed
*
* @param value The value to push.
*
* @note The underlying size of the enumeration type is the size of the
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
* push a u16-sized amount of data.
*/
template <typename Enum>
void PushEnum(Enum value) {
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
static_assert(!std::is_convertible_v<Enum, int>,
"enum type in PushEnum must be a strongly typed enum.");
Push(static_cast<std::underlying_type_t<Enum>>(value));
}
/**
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
* param
* @note: The input class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
void PushRaw(const T& value);
template <typename... O>
void PushMoveObjects(O*... pointers);
template <typename... O>
void PushMoveObjects(O&... pointers);
template <typename... O>
void PushCopyObjects(O*... pointers);
template <typename... O>
void PushCopyObjects(O&... pointers);
private:
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
u32 data_payload_index{};
Kernel::KernelCore& kernel;
};
/// Push ///
inline void ResponseBuilder::PushImpl(s32 value) {
cmdbuf[index++] = value;
}
inline void ResponseBuilder::PushImpl(u32 value) {
cmdbuf[index++] = value;
}
template <typename T>
void ResponseBuilder::PushRaw(const T& value) {
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
inline void ResponseBuilder::PushImpl(Result value) {
// Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded.
Push(value.raw);
Push<u32>(0);
}
inline void ResponseBuilder::PushImpl(s8 value) {
PushRaw(value);
}
inline void ResponseBuilder::PushImpl(s16 value) {
PushRaw(value);
}
inline void ResponseBuilder::PushImpl(s64 value) {
PushImpl(static_cast<u32>(value));
PushImpl(static_cast<u32>(value >> 32));
}
inline void ResponseBuilder::PushImpl(u8 value) {
PushRaw(value);
}
inline void ResponseBuilder::PushImpl(u16 value) {
PushRaw(value);
}
inline void ResponseBuilder::PushImpl(u64 value) {
PushImpl(static_cast<u32>(value));
PushImpl(static_cast<u32>(value >> 32));
}
inline void ResponseBuilder::PushImpl(float value) {
u32 integral;
std::memcpy(&integral, &value, sizeof(u32));
PushImpl(integral);
}
inline void ResponseBuilder::PushImpl(double value) {
u64 integral;
std::memcpy(&integral, &value, sizeof(u64));
PushImpl(integral);
}
inline void ResponseBuilder::PushImpl(bool value) {
PushImpl(static_cast<u8>(value));
}
template <typename First, typename... Other>
void ResponseBuilder::Push(const First& first_value, const Other&... other_values) {
Push(first_value);
Push(other_values...);
}
template <typename... O>
inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
context->AddCopyObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
auto objects = {&pointers...};
for (auto& object : objects) {
context->AddCopyObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
context->AddMoveObject(object);
}
}
template <typename... O>
inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
auto objects = {&pointers...};
for (auto& object : objects) {
context->AddMoveObject(object);
}
}
class RequestParser : public RequestHelperBase {
public:
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
explicit RequestParser(Kernel::HLERequestContext& ctx) : RequestHelperBase(ctx) {
// TIPC does not have data payload offset
if (!ctx.IsTipc()) {
ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
Skip(ctx.GetDataPayloadOffset(), false);
}
// Skip the u64 command id, it's already stored in the context
static constexpr u32 CommandIdSize = 2;
Skip(CommandIdSize, false);
}
template <typename T>
T Pop();
template <typename T>
void Pop(T& value);
template <typename First, typename... Other>
void Pop(First& first_value, Other&... other_values);
template <typename T>
T PopEnum() {
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
static_assert(!std::is_convertible_v<T, int>,
"enum type in PopEnum must be a strongly typed enum.");
return static_cast<T>(Pop<std::underlying_type_t<T>>());
}
/**
* @brief Reads the next normal parameters as a struct, by copying it
* @note: The output class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
void PopRaw(T& value);
/**
* @brief Reads the next normal parameters as a struct, by copying it into a new value
* @note: The output class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
T PopRaw();
template <class T>
std::weak_ptr<T> PopIpcInterface() {
ASSERT(context->GetManager()->IsDomain());
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainHandler<T>(Pop<u32>() - 1);
}
};
/// Pop ///
template <>
inline u32 RequestParser::Pop() {
return cmdbuf[index++];
}
template <>
inline s32 RequestParser::Pop() {
return static_cast<s32>(Pop<u32>());
}
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
template <typename T>
void RequestParser::PopRaw(T& value) {
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
#pragma GCC diagnostic pop
#endif
template <typename T>
T RequestParser::PopRaw() {
T value;
PopRaw(value);
return value;
}
template <>
inline u8 RequestParser::Pop() {
return PopRaw<u8>();
}
template <>
inline u16 RequestParser::Pop() {
return PopRaw<u16>();
}
template <>
inline u64 RequestParser::Pop() {
const u64 lsw = Pop<u32>();
const u64 msw = Pop<u32>();
return msw << 32 | lsw;
}
template <>
inline s8 RequestParser::Pop() {
return static_cast<s8>(Pop<u8>());
}
template <>
inline s16 RequestParser::Pop() {
return static_cast<s16>(Pop<u16>());
}
template <>
inline s64 RequestParser::Pop() {
return static_cast<s64>(Pop<u64>());
}
template <>
inline float RequestParser::Pop() {
const u32 value = Pop<u32>();
float real;
std::memcpy(&real, &value, sizeof(real));
return real;
}
template <>
inline double RequestParser::Pop() {
const u64 value = Pop<u64>();
double real;
std::memcpy(&real, &value, sizeof(real));
return real;
}
template <>
inline bool RequestParser::Pop() {
return Pop<u8>() != 0;
}
template <>
inline Result RequestParser::Pop() {
return Result{Pop<u32>()};
}
template <typename T>
void RequestParser::Pop(T& value) {
value = Pop<T>();
}
template <typename First, typename... Other>
void RequestParser::Pop(First& first_value, Other&... other_values) {
first_value = Pop<First>();
Pop(other_values...);
}
} // namespace IPC

View File

@@ -1,19 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// All architectures must define NumArchitectureDeviceRegions.
constexpr inline const auto NumArchitectureDeviceRegions = 3;
constexpr inline const auto KMemoryRegionType_Uart =
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 0);
constexpr inline const auto KMemoryRegionType_InterruptCpuInterface =
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 1)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
constexpr inline const auto KMemoryRegionType_InterruptDistributor =
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 2)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
static_assert(KMemoryRegionType_Uart.GetValue() == (0x1D));
static_assert(KMemoryRegionType_InterruptCpuInterface.GetValue() ==
(0x2D | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_InterruptDistributor.GetValue() ==
(0x4D | KMemoryRegionAttr_NoUserMap));
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// All architectures must define NumArchitectureDeviceRegions.
constexpr inline const auto NumArchitectureDeviceRegions = 3;
constexpr inline const auto KMemoryRegionType_Uart =
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 0);
constexpr inline const auto KMemoryRegionType_InterruptCpuInterface =
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 1)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
constexpr inline const auto KMemoryRegionType_InterruptDistributor =
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 2)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
static_assert(KMemoryRegionType_Uart.GetValue() == (0x1D));
static_assert(KMemoryRegionType_InterruptCpuInterface.GetValue() ==
(0x2D | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_InterruptDistributor.GetValue() ==
(0x4D | KMemoryRegionAttr_NoUserMap));

View File

@@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel {
constexpr inline PAddr MainMemoryAddress = 0x80000000;
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel {
constexpr inline PAddr MainMemoryAddress = 0x80000000;
} // namespace Kernel

View File

@@ -1,51 +1,51 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// All architectures must define NumBoardDeviceRegions.
constexpr inline const auto NumBoardDeviceRegions = 6;
// UNUSED: .Derive(NumBoardDeviceRegions, 0);
constexpr inline const auto KMemoryRegionType_MemoryController =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 1)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
constexpr inline const auto KMemoryRegionType_MemoryController1 =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 2)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
constexpr inline const auto KMemoryRegionType_MemoryController0 =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 3)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
constexpr inline const auto KMemoryRegionType_PowerManagementController =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 4).DeriveTransition();
constexpr inline const auto KMemoryRegionType_LegacyLpsDevices =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 5);
static_assert(KMemoryRegionType_MemoryController.GetValue() ==
(0x55 | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_MemoryController1.GetValue() ==
(0x65 | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_MemoryController0.GetValue() ==
(0x95 | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_PowerManagementController.GetValue() == (0x1A5));
static_assert(KMemoryRegionType_LegacyLpsDevices.GetValue() == 0xC5);
constexpr inline const auto NumLegacyLpsDevices = 7;
constexpr inline const auto KMemoryRegionType_LegacyLpsExceptionVectors =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 0);
constexpr inline const auto KMemoryRegionType_LegacyLpsIram =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 1);
constexpr inline const auto KMemoryRegionType_LegacyLpsFlowController =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 2);
constexpr inline const auto KMemoryRegionType_LegacyLpsPrimaryICtlr =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 3);
constexpr inline const auto KMemoryRegionType_LegacyLpsSemaphore =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 4);
constexpr inline const auto KMemoryRegionType_LegacyLpsAtomics =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 5);
constexpr inline const auto KMemoryRegionType_LegacyLpsClkRst =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 6);
static_assert(KMemoryRegionType_LegacyLpsExceptionVectors.GetValue() == 0x3C5);
static_assert(KMemoryRegionType_LegacyLpsIram.GetValue() == 0x5C5);
static_assert(KMemoryRegionType_LegacyLpsFlowController.GetValue() == 0x6C5);
static_assert(KMemoryRegionType_LegacyLpsPrimaryICtlr.GetValue() == 0x9C5);
static_assert(KMemoryRegionType_LegacyLpsSemaphore.GetValue() == 0xAC5);
static_assert(KMemoryRegionType_LegacyLpsAtomics.GetValue() == 0xCC5);
static_assert(KMemoryRegionType_LegacyLpsClkRst.GetValue() == 0x11C5);
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// All architectures must define NumBoardDeviceRegions.
constexpr inline const auto NumBoardDeviceRegions = 6;
// UNUSED: .Derive(NumBoardDeviceRegions, 0);
constexpr inline const auto KMemoryRegionType_MemoryController =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 1)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
constexpr inline const auto KMemoryRegionType_MemoryController1 =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 2)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
constexpr inline const auto KMemoryRegionType_MemoryController0 =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 3)
.SetAttribute(KMemoryRegionAttr_NoUserMap);
constexpr inline const auto KMemoryRegionType_PowerManagementController =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 4).DeriveTransition();
constexpr inline const auto KMemoryRegionType_LegacyLpsDevices =
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 5);
static_assert(KMemoryRegionType_MemoryController.GetValue() ==
(0x55 | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_MemoryController1.GetValue() ==
(0x65 | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_MemoryController0.GetValue() ==
(0x95 | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_PowerManagementController.GetValue() == (0x1A5));
static_assert(KMemoryRegionType_LegacyLpsDevices.GetValue() == 0xC5);
constexpr inline const auto NumLegacyLpsDevices = 7;
constexpr inline const auto KMemoryRegionType_LegacyLpsExceptionVectors =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 0);
constexpr inline const auto KMemoryRegionType_LegacyLpsIram =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 1);
constexpr inline const auto KMemoryRegionType_LegacyLpsFlowController =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 2);
constexpr inline const auto KMemoryRegionType_LegacyLpsPrimaryICtlr =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 3);
constexpr inline const auto KMemoryRegionType_LegacyLpsSemaphore =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 4);
constexpr inline const auto KMemoryRegionType_LegacyLpsAtomics =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 5);
constexpr inline const auto KMemoryRegionType_LegacyLpsClkRst =
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 6);
static_assert(KMemoryRegionType_LegacyLpsExceptionVectors.GetValue() == 0x3C5);
static_assert(KMemoryRegionType_LegacyLpsIram.GetValue() == 0x5C5);
static_assert(KMemoryRegionType_LegacyLpsFlowController.GetValue() == 0x6C5);
static_assert(KMemoryRegionType_LegacyLpsPrimaryICtlr.GetValue() == 0x9C5);
static_assert(KMemoryRegionType_LegacyLpsSemaphore.GetValue() == 0xAC5);
static_assert(KMemoryRegionType_LegacyLpsAtomics.GetValue() == 0xCC5);
static_assert(KMemoryRegionType_LegacyLpsClkRst.GetValue() == 0x11C5);

View File

@@ -1,160 +1,160 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <random>
#include "common/literals.h"
#include "common/settings.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
#include "core/hle/kernel/k_trace.h"
namespace Kernel::Board::Nintendo::Nx {
namespace impl {
constexpr const std::size_t RequiredNonSecureSystemMemorySizeVi = 0x2238 * 4 * 1024;
constexpr const std::size_t RequiredNonSecureSystemMemorySizeNvservices = 0x710 * 4 * 1024;
constexpr const std::size_t RequiredNonSecureSystemMemorySizeMisc = 0x80 * 4 * 1024;
} // namespace impl
constexpr const std::size_t RequiredNonSecureSystemMemorySize =
impl::RequiredNonSecureSystemMemorySizeVi + impl::RequiredNonSecureSystemMemorySizeNvservices +
impl::RequiredNonSecureSystemMemorySizeMisc;
namespace {
using namespace Common::Literals;
u32 GetMemorySizeForInit() {
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB;
}
Smc::MemoryArrangement GetMemoryArrangeForInit() {
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB
: Smc::MemoryArrangement_4GB;
}
} // namespace
size_t KSystemControl::Init::GetRealMemorySize() {
return GetIntendedMemorySize();
}
// Initialization.
size_t KSystemControl::Init::GetIntendedMemorySize() {
switch (GetMemorySizeForInit()) {
case Smc::MemorySize_4GB:
default: // All invalid modes should go to 4GB.
return 4_GiB;
case Smc::MemorySize_6GB:
return 6_GiB;
case Smc::MemorySize_8GB:
return 8_GiB;
}
}
PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) {
const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize();
const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
if (intended_dram_size * 2 < real_dram_size) {
return base_address;
} else {
return base_address + ((real_dram_size - intended_dram_size) / 2);
}
}
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
return true;
}
std::size_t KSystemControl::Init::GetApplicationPoolSize() {
// Get the base pool size.
const size_t base_pool_size = []() -> size_t {
switch (GetMemoryArrangeForInit()) {
case Smc::MemoryArrangement_4GB:
default:
return 3285_MiB;
case Smc::MemoryArrangement_4GBForAppletDev:
return 2048_MiB;
case Smc::MemoryArrangement_4GBForSystemDev:
return 3285_MiB;
case Smc::MemoryArrangement_6GB:
return 4916_MiB;
case Smc::MemoryArrangement_6GBForAppletDev:
return 3285_MiB;
case Smc::MemoryArrangement_8GB:
return 4916_MiB;
}
}();
// Return (possibly) adjusted size.
return base_pool_size;
}
size_t KSystemControl::Init::GetAppletPoolSize() {
// Get the base pool size.
const size_t base_pool_size = []() -> size_t {
switch (GetMemoryArrangeForInit()) {
case Smc::MemoryArrangement_4GB:
default:
return 507_MiB;
case Smc::MemoryArrangement_4GBForAppletDev:
return 1554_MiB;
case Smc::MemoryArrangement_4GBForSystemDev:
return 448_MiB;
case Smc::MemoryArrangement_6GB:
return 562_MiB;
case Smc::MemoryArrangement_6GBForAppletDev:
return 2193_MiB;
case Smc::MemoryArrangement_8GB:
return 2193_MiB;
}
}();
// Return (possibly) adjusted size.
constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MiB;
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
}
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
// Verify that our minimum is at least as large as Nintendo's.
constexpr size_t MinimumSize = RequiredNonSecureSystemMemorySize;
static_assert(MinimumSize >= 0x29C8000);
return MinimumSize;
}
namespace {
template <typename F>
u64 GenerateUniformRange(u64 min, u64 max, F f) {
// Handle the case where the difference is too large to represent.
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
return f();
}
// Iterate until we get a value in range.
const u64 range_size = ((max + 1) - min);
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
while (true) {
if (const u64 rnd = f(); rnd < effective_max) {
return min + (rnd % range_size);
}
}
}
} // Anonymous namespace
u64 KSystemControl::GenerateRandomU64() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return distribution(gen);
}
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64);
}
} // namespace Kernel::Board::Nintendo::Nx
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <random>
#include "common/literals.h"
#include "common/settings.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
#include "core/hle/kernel/k_trace.h"
namespace Kernel::Board::Nintendo::Nx {
namespace impl {
constexpr const std::size_t RequiredNonSecureSystemMemorySizeVi = 0x2238 * 4 * 1024;
constexpr const std::size_t RequiredNonSecureSystemMemorySizeNvservices = 0x710 * 4 * 1024;
constexpr const std::size_t RequiredNonSecureSystemMemorySizeMisc = 0x80 * 4 * 1024;
} // namespace impl
constexpr const std::size_t RequiredNonSecureSystemMemorySize =
impl::RequiredNonSecureSystemMemorySizeVi + impl::RequiredNonSecureSystemMemorySizeNvservices +
impl::RequiredNonSecureSystemMemorySizeMisc;
namespace {
using namespace Common::Literals;
u32 GetMemorySizeForInit() {
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB;
}
Smc::MemoryArrangement GetMemoryArrangeForInit() {
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB
: Smc::MemoryArrangement_4GB;
}
} // namespace
size_t KSystemControl::Init::GetRealMemorySize() {
return GetIntendedMemorySize();
}
// Initialization.
size_t KSystemControl::Init::GetIntendedMemorySize() {
switch (GetMemorySizeForInit()) {
case Smc::MemorySize_4GB:
default: // All invalid modes should go to 4GB.
return 4_GiB;
case Smc::MemorySize_6GB:
return 6_GiB;
case Smc::MemorySize_8GB:
return 8_GiB;
}
}
PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) {
const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize();
const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
if (intended_dram_size * 2 < real_dram_size) {
return base_address;
} else {
return base_address + ((real_dram_size - intended_dram_size) / 2);
}
}
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
return true;
}
std::size_t KSystemControl::Init::GetApplicationPoolSize() {
// Get the base pool size.
const size_t base_pool_size = []() -> size_t {
switch (GetMemoryArrangeForInit()) {
case Smc::MemoryArrangement_4GB:
default:
return 3285_MiB;
case Smc::MemoryArrangement_4GBForAppletDev:
return 2048_MiB;
case Smc::MemoryArrangement_4GBForSystemDev:
return 3285_MiB;
case Smc::MemoryArrangement_6GB:
return 4916_MiB;
case Smc::MemoryArrangement_6GBForAppletDev:
return 3285_MiB;
case Smc::MemoryArrangement_8GB:
return 4916_MiB;
}
}();
// Return (possibly) adjusted size.
return base_pool_size;
}
size_t KSystemControl::Init::GetAppletPoolSize() {
// Get the base pool size.
const size_t base_pool_size = []() -> size_t {
switch (GetMemoryArrangeForInit()) {
case Smc::MemoryArrangement_4GB:
default:
return 507_MiB;
case Smc::MemoryArrangement_4GBForAppletDev:
return 1554_MiB;
case Smc::MemoryArrangement_4GBForSystemDev:
return 448_MiB;
case Smc::MemoryArrangement_6GB:
return 562_MiB;
case Smc::MemoryArrangement_6GBForAppletDev:
return 2193_MiB;
case Smc::MemoryArrangement_8GB:
return 2193_MiB;
}
}();
// Return (possibly) adjusted size.
constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MiB;
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
}
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
// Verify that our minimum is at least as large as Nintendo's.
constexpr size_t MinimumSize = RequiredNonSecureSystemMemorySize;
static_assert(MinimumSize >= 0x29C8000);
return MinimumSize;
}
namespace {
template <typename F>
u64 GenerateUniformRange(u64 min, u64 max, F f) {
// Handle the case where the difference is too large to represent.
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
return f();
}
// Iterate until we get a value in range.
const u64 range_size = ((max + 1) - min);
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
while (true) {
if (const u64 rnd = f(); rnd < effective_max) {
return min + (rnd % range_size);
}
}
}
} // Anonymous namespace
u64 KSystemControl::GenerateRandomU64() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return distribution(gen);
}
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64);
}
} // namespace Kernel::Board::Nintendo::Nx

View File

@@ -1,28 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel::Board::Nintendo::Nx {
class KSystemControl {
public:
class Init {
public:
// Initialization.
static std::size_t GetRealMemorySize();
static std::size_t GetIntendedMemorySize();
static PAddr GetKernelPhysicalBaseAddress(u64 base_address);
static bool ShouldIncreaseThreadResourceLimit();
static std::size_t GetApplicationPoolSize();
static std::size_t GetAppletPoolSize();
static std::size_t GetMinimumNonSecureSystemPoolSize();
};
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
};
} // namespace Kernel::Board::Nintendo::Nx
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel::Board::Nintendo::Nx {
class KSystemControl {
public:
class Init {
public:
// Initialization.
static std::size_t GetRealMemorySize();
static std::size_t GetIntendedMemorySize();
static PAddr GetKernelPhysicalBaseAddress(u64 base_address);
static bool ShouldIncreaseThreadResourceLimit();
static std::size_t GetApplicationPoolSize();
static std::size_t GetAppletPoolSize();
static std::size_t GetMinimumNonSecureSystemPoolSize();
};
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
};
} // namespace Kernel::Board::Nintendo::Nx

View File

@@ -1,23 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Kernel::Board::Nintendo::Nx::Smc {
enum MemorySize {
MemorySize_4GB = 0,
MemorySize_6GB = 1,
MemorySize_8GB = 2,
};
enum MemoryArrangement {
MemoryArrangement_4GB = 0,
MemoryArrangement_4GBForAppletDev = 1,
MemoryArrangement_4GBForSystemDev = 2,
MemoryArrangement_6GB = 3,
MemoryArrangement_6GBForAppletDev = 4,
MemoryArrangement_8GB = 5,
};
} // namespace Kernel::Board::Nintendo::Nx::Smc
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Kernel::Board::Nintendo::Nx::Smc {
enum MemorySize {
MemorySize_4GB = 0,
MemorySize_6GB = 1,
MemorySize_8GB = 2,
};
enum MemoryArrangement {
MemoryArrangement_4GB = 0,
MemoryArrangement_4GBForAppletDev = 1,
MemoryArrangement_4GBForSystemDev = 2,
MemoryArrangement_6GB = 3,
MemoryArrangement_6GBForAppletDev = 4,
MemoryArrangement_8GB = 5,
};
} // namespace Kernel::Board::Nintendo::Nx::Smc

View File

@@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/code_set.h"
namespace Kernel {
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/code_set.h"
namespace Kernel {
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
} // namespace Kernel

View File

@@ -1,88 +1,88 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include "common/common_types.h"
#include "core/hle/kernel/physical_memory.h"
namespace Kernel {
/**
* Represents executable data that may be loaded into a kernel process.
*
* A code set consists of three basic segments:
* - A code (AKA text) segment,
* - A read-only data segment (rodata)
* - A data segment
*
* The code segment is the portion of the object file that contains
* executable instructions.
*
* The read-only data segment in the portion of the object file that
* contains (as one would expect) read-only data, such as fixed constant
* values and data structures.
*
* The data segment is similar to the read-only data segment -- it contains
* variables and data structures that have predefined values, however,
* entities within this segment can be modified.
*/
struct CodeSet final {
/// A single segment within a code set.
struct Segment final {
/// The byte offset that this segment is located at.
std::size_t offset = 0;
/// The address to map this segment to.
VAddr addr = 0;
/// The size of this segment in bytes.
u32 size = 0;
};
explicit CodeSet();
~CodeSet();
CodeSet(const CodeSet&) = delete;
CodeSet& operator=(const CodeSet&) = delete;
CodeSet(CodeSet&&) = default;
CodeSet& operator=(CodeSet&&) = default;
Segment& CodeSegment() {
return segments[0];
}
const Segment& CodeSegment() const {
return segments[0];
}
Segment& RODataSegment() {
return segments[1];
}
const Segment& RODataSegment() const {
return segments[1];
}
Segment& DataSegment() {
return segments[2];
}
const Segment& DataSegment() const {
return segments[2];
}
/// The overall data that backs this code set.
Kernel::PhysicalMemory memory;
/// The segments that comprise this code set.
std::array<Segment, 3> segments;
/// The entry point address for this code set.
VAddr entrypoint = 0;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include "common/common_types.h"
#include "core/hle/kernel/physical_memory.h"
namespace Kernel {
/**
* Represents executable data that may be loaded into a kernel process.
*
* A code set consists of three basic segments:
* - A code (AKA text) segment,
* - A read-only data segment (rodata)
* - A data segment
*
* The code segment is the portion of the object file that contains
* executable instructions.
*
* The read-only data segment in the portion of the object file that
* contains (as one would expect) read-only data, such as fixed constant
* values and data structures.
*
* The data segment is similar to the read-only data segment -- it contains
* variables and data structures that have predefined values, however,
* entities within this segment can be modified.
*/
struct CodeSet final {
/// A single segment within a code set.
struct Segment final {
/// The byte offset that this segment is located at.
std::size_t offset = 0;
/// The address to map this segment to.
VAddr addr = 0;
/// The size of this segment in bytes.
u32 size = 0;
};
explicit CodeSet();
~CodeSet();
CodeSet(const CodeSet&) = delete;
CodeSet& operator=(const CodeSet&) = delete;
CodeSet(CodeSet&&) = default;
CodeSet& operator=(CodeSet&&) = default;
Segment& CodeSegment() {
return segments[0];
}
const Segment& CodeSegment() const {
return segments[0];
}
Segment& RODataSegment() {
return segments[1];
}
const Segment& RODataSegment() const {
return segments[1];
}
Segment& DataSegment() {
return segments[2];
}
const Segment& DataSegment() const {
return segments[2];
}
/// The overall data that backs this code set.
Kernel::PhysicalMemory memory;
/// The segments that comprise this code set.
std::array<Segment, 3> segments;
/// The entry point address for this code set.
VAddr entrypoint = 0;
};
} // namespace Kernel

View File

@@ -1,74 +1,74 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel {
GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel_)
: kernel{kernel_}, scheduler_lock{kernel_} {}
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
void GlobalSchedulerContext::AddThread(KThread* thread) {
std::scoped_lock lock{global_list_guard};
thread_list.push_back(thread);
}
void GlobalSchedulerContext::RemoveThread(KThread* thread) {
std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void GlobalSchedulerContext::PreemptThreads() {
// The priority levels at which the global scheduler preempts threads every 10 ms. They are
// ordered from Core 0 to Core 3.
static constexpr std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities{
59,
59,
59,
63,
};
ASSERT(IsLocked());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
KScheduler::RotateScheduledQueue(kernel, core_id, priority);
}
}
bool GlobalSchedulerContext::IsLocked() const {
return scheduler_lock.IsLockedByCurrentThread();
}
void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) {
ASSERT(IsLocked());
woken_dummy_threads.insert(thread);
}
void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) {
ASSERT(IsLocked());
woken_dummy_threads.erase(thread);
}
void GlobalSchedulerContext::WakeupWaitingDummyThreads() {
ASSERT(IsLocked());
for (auto* thread : woken_dummy_threads) {
thread->DummyThreadEndWait();
}
woken_dummy_threads.clear();
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel {
GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel_)
: kernel{kernel_}, scheduler_lock{kernel_} {}
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
void GlobalSchedulerContext::AddThread(KThread* thread) {
std::scoped_lock lock{global_list_guard};
thread_list.push_back(thread);
}
void GlobalSchedulerContext::RemoveThread(KThread* thread) {
std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void GlobalSchedulerContext::PreemptThreads() {
// The priority levels at which the global scheduler preempts threads every 10 ms. They are
// ordered from Core 0 to Core 3.
static constexpr std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities{
59,
59,
59,
63,
};
ASSERT(IsLocked());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
KScheduler::RotateScheduledQueue(kernel, core_id, priority);
}
}
bool GlobalSchedulerContext::IsLocked() const {
return scheduler_lock.IsLockedByCurrentThread();
}
void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) {
ASSERT(IsLocked());
woken_dummy_threads.insert(thread);
}
void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) {
ASSERT(IsLocked());
woken_dummy_threads.erase(thread);
}
void GlobalSchedulerContext::WakeupWaitingDummyThreads() {
ASSERT(IsLocked());
for (auto* thread : woken_dummy_threads) {
thread->DummyThreadEndWait();
}
woken_dummy_threads.clear();
}
} // namespace Kernel

View File

@@ -1,92 +1,92 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <set>
#include <vector>
#include "common/common_types.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc_types.h"
namespace Kernel {
class KernelCore;
class SchedulerLock;
using KSchedulerPriorityQueue =
KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
Svc::HighestThreadPriority>;
static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);
class GlobalSchedulerContext final {
friend class KScheduler;
public:
using LockType = KAbstractSchedulerLock<KScheduler>;
explicit GlobalSchedulerContext(KernelCore& kernel_);
~GlobalSchedulerContext();
/// Adds a new thread to the scheduler
void AddThread(KThread* thread);
/// Removes a thread from the scheduler
void RemoveThread(KThread* thread);
/// Returns a list of all threads managed by the scheduler
[[nodiscard]] const std::vector<KThread*>& GetThreadList() const {
return thread_list;
}
/**
* Rotates the scheduling queues of threads at a preemption priority and then does
* some core rebalancing. Preemption priorities can be found in the array
* 'preemption_priorities'.
*
* @note This operation happens every 10ms.
*/
void PreemptThreads();
/// Returns true if the global scheduler lock is acquired
bool IsLocked() const;
void UnregisterDummyThreadForWakeup(KThread* thread);
void RegisterDummyThreadForWakeup(KThread* thread);
void WakeupWaitingDummyThreads();
[[nodiscard]] LockType& SchedulerLock() {
return scheduler_lock;
}
[[nodiscard]] const LockType& SchedulerLock() const {
return scheduler_lock;
}
private:
friend class KScopedSchedulerLock;
friend class KScopedSchedulerLockAndSleep;
KernelCore& kernel;
std::atomic_bool scheduler_update_needed{};
KSchedulerPriorityQueue priority_queue;
LockType scheduler_lock;
/// Lists dummy threads pending wakeup on lock release
std::set<KThread*> woken_dummy_threads;
/// Lists all thread ids that aren't deleted/etc.
std::vector<KThread*> thread_list;
std::mutex global_list_guard;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <set>
#include <vector>
#include "common/common_types.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc_types.h"
namespace Kernel {
class KernelCore;
class SchedulerLock;
using KSchedulerPriorityQueue =
KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
Svc::HighestThreadPriority>;
static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);
class GlobalSchedulerContext final {
friend class KScheduler;
public:
using LockType = KAbstractSchedulerLock<KScheduler>;
explicit GlobalSchedulerContext(KernelCore& kernel_);
~GlobalSchedulerContext();
/// Adds a new thread to the scheduler
void AddThread(KThread* thread);
/// Removes a thread from the scheduler
void RemoveThread(KThread* thread);
/// Returns a list of all threads managed by the scheduler
[[nodiscard]] const std::vector<KThread*>& GetThreadList() const {
return thread_list;
}
/**
* Rotates the scheduling queues of threads at a preemption priority and then does
* some core rebalancing. Preemption priorities can be found in the array
* 'preemption_priorities'.
*
* @note This operation happens every 10ms.
*/
void PreemptThreads();
/// Returns true if the global scheduler lock is acquired
bool IsLocked() const;
void UnregisterDummyThreadForWakeup(KThread* thread);
void RegisterDummyThreadForWakeup(KThread* thread);
void WakeupWaitingDummyThreads();
[[nodiscard]] LockType& SchedulerLock() {
return scheduler_lock;
}
[[nodiscard]] const LockType& SchedulerLock() const {
return scheduler_lock;
}
private:
friend class KScopedSchedulerLock;
friend class KScopedSchedulerLockAndSleep;
KernelCore& kernel;
std::atomic_bool scheduler_update_needed{};
KSchedulerPriorityQueue priority_queue;
LockType scheduler_lock;
/// Lists dummy threads pending wakeup on lock release
std::set<KThread*> woken_dummy_threads;
/// Lists all thread ids that aren't deleted/etc.
std::vector<KThread*> thread_list;
std::mutex global_list_guard;
};
} // namespace Kernel

File diff suppressed because it is too large Load Diff

View File

@@ -1,400 +1,400 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/concepts.h"
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/svc_common.h"
union Result;
namespace Core::Memory {
class Memory;
}
namespace IPC {
class ResponseBuilder;
}
namespace Service {
class ServiceFrameworkBase;
}
enum class ServiceThreadType {
Default,
CreateNew,
};
namespace Kernel {
class Domain;
class HLERequestContext;
class KAutoObject;
class KernelCore;
class KEvent;
class KHandleTable;
class KServerPort;
class KProcess;
class KServerSession;
class KThread;
class KReadableEvent;
class KSession;
class SessionRequestManager;
class ServiceThread;
enum class ThreadWakeupReason;
/**
* Interface implemented by HLE Session handlers.
* This can be provided to a ServerSession in order to hook into several relevant events
* (such as a new connection or a SyncRequest) so they can be implemented in the emulator.
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
ServiceThreadType thread_type);
virtual ~SessionRequestHandler();
/**
* Handles a sync request from the emulated application.
* @param server_session The ServerSession that was triggered for this sync request,
* it should be used to differentiate which client (As in ClientSession) we're answering to.
* TODO(Subv): Use a wrapper structure to hold all the information relevant to
* this request (ServerSession, Originator thread, Translated command buffer, etc).
* @returns Result the result code of the translate operation.
*/
virtual Result HandleSyncRequest(Kernel::KServerSession& session,
Kernel::HLERequestContext& context) = 0;
void AcceptSession(KServerPort* server_port);
void RegisterSession(KServerSession* server_session,
std::shared_ptr<SessionRequestManager> manager);
std::weak_ptr<ServiceThread> GetServiceThread() const {
return service_thread;
}
protected:
KernelCore& kernel;
std::weak_ptr<ServiceThread> service_thread;
};
using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
/**
* Manages the underlying HLE requests for a session, and whether (or not) the session should be
* treated as a domain. This is managed separately from server sessions, as this state is shared
* when objects are cloned.
*/
class SessionRequestManager final {
public:
explicit SessionRequestManager(KernelCore& kernel);
~SessionRequestManager();
bool IsDomain() const {
return is_domain;
}
void ConvertToDomain() {
domain_handlers = {session_handler};
is_domain = true;
}
void ConvertToDomainOnRequestEnd() {
convert_to_domain = true;
}
std::size_t DomainHandlerCount() const {
return domain_handlers.size();
}
bool HasSessionHandler() const {
return session_handler != nullptr;
}
SessionRequestHandler& SessionHandler() {
return *session_handler;
}
const SessionRequestHandler& SessionHandler() const {
return *session_handler;
}
void CloseDomainHandler(std::size_t index) {
if (index < DomainHandlerCount()) {
domain_handlers[index] = nullptr;
} else {
ASSERT_MSG(false, "Unexpected handler index {}", index);
}
}
SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const {
ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index);
return domain_handlers.at(index);
}
void AppendDomainHandler(SessionRequestHandlerPtr&& handler) {
domain_handlers.emplace_back(std::move(handler));
}
void SetSessionHandler(SessionRequestHandlerPtr&& handler) {
session_handler = std::move(handler);
}
std::weak_ptr<ServiceThread> GetServiceThread() const {
return session_handler->GetServiceThread();
}
bool HasSessionRequestHandler(const HLERequestContext& context) const;
Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
private:
bool convert_to_domain{};
bool is_domain{};
SessionRequestHandlerPtr session_handler;
std::vector<SessionRequestHandlerPtr> domain_handlers;
private:
KernelCore& kernel;
};
/**
* Class containing information about an in-flight IPC request being handled by an HLE service
* implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and
* when possible use the APIs in this class to service the request.
*
* HLE handle protocol
* ===================
*
* To avoid needing HLE services to keep a separate handle table, or having to directly modify the
* requester's table, a tweaked protocol is used to receive and send handles in requests. The kernel
* will decode the incoming handles into object pointers and insert a id in the buffer where the
* handle would normally be. The service then calls GetIncomingHandle() with that id to get the
* pointer to the object. Similarly, instead of inserting a handle into the command buffer, the
* service calls AddOutgoingHandle() and stores the returned id where the handle would normally go.
*
* The end result is similar to just giving services their own real handle tables, but since these
* ids are local to a specific context, it avoids requiring services to manage handles for objects
* across multiple calls and ensuring that unneeded handles are cleaned up.
*/
class HLERequestContext {
public:
explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
KServerSession* session, KThread* thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
u32* CommandBuffer() {
return cmd_buf.data();
}
/**
* Returns the session through which this request was made. This can be used as a map key to
* access per-client data on services.
*/
Kernel::KServerSession* Session() {
return server_session;
}
/// Populates this context with data from the requesting process/thread.
Result PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
u32_le GetHipcCommand() const {
return command;
}
u32_le GetTipcCommand() const {
return static_cast<u32_le>(command_header->type.Value()) -
static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion);
}
u32_le GetCommand() const {
return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand();
}
bool IsTipc() const {
return command_header->IsTipc();
}
IPC::CommandType GetCommandType() const {
return command_header->type;
}
u64 GetPID() const {
return pid;
}
u32 GetDataPayloadOffset() const {
return data_payload_offset;
}
const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
return buffer_x_desciptors;
}
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
return buffer_a_desciptors;
}
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
return buffer_b_desciptors;
}
const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
return buffer_c_desciptors;
}
const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
return domain_message_header.value();
}
bool HasDomainMessageHeader() const {
return domain_message_header.has_value();
}
/// Helper function to read a buffer using the appropriate buffer descriptor
std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to write a buffer using the appropriate buffer descriptor
std::size_t WriteBuffer(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
/// Helper function to write buffer B
std::size_t WriteBufferB(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
/// Helper function to write buffer C
std::size_t WriteBufferC(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
/* Helper function to write a buffer using the appropriate buffer descriptor
*
* @tparam T an arbitrary container that satisfies the
* ContiguousContainer concept in the C++ standard library or a trivially copyable type.
*
* @param data The container/data to write into a buffer.
* @param buffer_index The buffer in particular to write to.
*/
template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
if constexpr (Common::IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Container to WriteBuffer must contain trivially copyable objects");
return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType),
buffer_index);
} else {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return WriteBuffer(&data, sizeof(T), buffer_index);
}
}
/// Helper function to get the size of the input buffer
std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to get the size of the output buffer
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to test whether the input buffer at buffer_index can be read
bool CanReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to test whether the output buffer at buffer_index can be written
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
Handle GetCopyHandle(std::size_t index) const {
return incoming_copy_handles.at(index);
}
Handle GetMoveHandle(std::size_t index) const {
return incoming_move_handles.at(index);
}
void AddMoveObject(KAutoObject* object) {
outgoing_move_objects.emplace_back(object);
}
void AddCopyObject(KAutoObject* object) {
outgoing_copy_objects.emplace_back(object);
}
void AddDomainObject(SessionRequestHandlerPtr object) {
outgoing_domain_objects.emplace_back(std::move(object));
}
template <typename T>
std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock());
}
void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
manager = manager_;
}
std::string Description() const;
KThread& GetThread() {
return *thread;
}
std::shared_ptr<SessionRequestManager> GetManager() const {
return manager.lock();
}
private:
friend class IPC::ResponseBuilder;
void ParseCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
Kernel::KServerSession* server_session{};
KThread* thread;
std::vector<Handle> incoming_move_handles;
std::vector<Handle> incoming_copy_handles;
std::vector<KAutoObject*> outgoing_move_objects;
std::vector<KAutoObject*> outgoing_copy_objects;
std::vector<SessionRequestHandlerPtr> outgoing_domain_objects;
std::optional<IPC::CommandHeader> command_header;
std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::optional<IPC::DataPayloadHeader> data_payload_header;
std::optional<IPC::DomainMessageHeader> domain_message_header;
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors;
std::vector<IPC::BufferDescriptorC> buffer_c_desciptors;
u32_le command{};
u64 pid{};
u32 write_size{};
u32 data_payload_offset{};
u32 handles_offset{};
u32 domain_offset{};
std::weak_ptr<SessionRequestManager> manager{};
KernelCore& kernel;
Core::Memory::Memory& memory;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/concepts.h"
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/svc_common.h"
union Result;
namespace Core::Memory {
class Memory;
}
namespace IPC {
class ResponseBuilder;
}
namespace Service {
class ServiceFrameworkBase;
}
enum class ServiceThreadType {
Default,
CreateNew,
};
namespace Kernel {
class Domain;
class HLERequestContext;
class KAutoObject;
class KernelCore;
class KEvent;
class KHandleTable;
class KServerPort;
class KProcess;
class KServerSession;
class KThread;
class KReadableEvent;
class KSession;
class SessionRequestManager;
class ServiceThread;
enum class ThreadWakeupReason;
/**
* Interface implemented by HLE Session handlers.
* This can be provided to a ServerSession in order to hook into several relevant events
* (such as a new connection or a SyncRequest) so they can be implemented in the emulator.
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
ServiceThreadType thread_type);
virtual ~SessionRequestHandler();
/**
* Handles a sync request from the emulated application.
* @param server_session The ServerSession that was triggered for this sync request,
* it should be used to differentiate which client (As in ClientSession) we're answering to.
* TODO(Subv): Use a wrapper structure to hold all the information relevant to
* this request (ServerSession, Originator thread, Translated command buffer, etc).
* @returns Result the result code of the translate operation.
*/
virtual Result HandleSyncRequest(Kernel::KServerSession& session,
Kernel::HLERequestContext& context) = 0;
void AcceptSession(KServerPort* server_port);
void RegisterSession(KServerSession* server_session,
std::shared_ptr<SessionRequestManager> manager);
std::weak_ptr<ServiceThread> GetServiceThread() const {
return service_thread;
}
protected:
KernelCore& kernel;
std::weak_ptr<ServiceThread> service_thread;
};
using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
/**
* Manages the underlying HLE requests for a session, and whether (or not) the session should be
* treated as a domain. This is managed separately from server sessions, as this state is shared
* when objects are cloned.
*/
class SessionRequestManager final {
public:
explicit SessionRequestManager(KernelCore& kernel);
~SessionRequestManager();
bool IsDomain() const {
return is_domain;
}
void ConvertToDomain() {
domain_handlers = {session_handler};
is_domain = true;
}
void ConvertToDomainOnRequestEnd() {
convert_to_domain = true;
}
std::size_t DomainHandlerCount() const {
return domain_handlers.size();
}
bool HasSessionHandler() const {
return session_handler != nullptr;
}
SessionRequestHandler& SessionHandler() {
return *session_handler;
}
const SessionRequestHandler& SessionHandler() const {
return *session_handler;
}
void CloseDomainHandler(std::size_t index) {
if (index < DomainHandlerCount()) {
domain_handlers[index] = nullptr;
} else {
ASSERT_MSG(false, "Unexpected handler index {}", index);
}
}
SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const {
ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index);
return domain_handlers.at(index);
}
void AppendDomainHandler(SessionRequestHandlerPtr&& handler) {
domain_handlers.emplace_back(std::move(handler));
}
void SetSessionHandler(SessionRequestHandlerPtr&& handler) {
session_handler = std::move(handler);
}
std::weak_ptr<ServiceThread> GetServiceThread() const {
return session_handler->GetServiceThread();
}
bool HasSessionRequestHandler(const HLERequestContext& context) const;
Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
private:
bool convert_to_domain{};
bool is_domain{};
SessionRequestHandlerPtr session_handler;
std::vector<SessionRequestHandlerPtr> domain_handlers;
private:
KernelCore& kernel;
};
/**
* Class containing information about an in-flight IPC request being handled by an HLE service
* implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and
* when possible use the APIs in this class to service the request.
*
* HLE handle protocol
* ===================
*
* To avoid needing HLE services to keep a separate handle table, or having to directly modify the
* requester's table, a tweaked protocol is used to receive and send handles in requests. The kernel
* will decode the incoming handles into object pointers and insert a id in the buffer where the
* handle would normally be. The service then calls GetIncomingHandle() with that id to get the
* pointer to the object. Similarly, instead of inserting a handle into the command buffer, the
* service calls AddOutgoingHandle() and stores the returned id where the handle would normally go.
*
* The end result is similar to just giving services their own real handle tables, but since these
* ids are local to a specific context, it avoids requiring services to manage handles for objects
* across multiple calls and ensuring that unneeded handles are cleaned up.
*/
class HLERequestContext {
public:
explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
KServerSession* session, KThread* thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
u32* CommandBuffer() {
return cmd_buf.data();
}
/**
* Returns the session through which this request was made. This can be used as a map key to
* access per-client data on services.
*/
Kernel::KServerSession* Session() {
return server_session;
}
/// Populates this context with data from the requesting process/thread.
Result PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
u32_le GetHipcCommand() const {
return command;
}
u32_le GetTipcCommand() const {
return static_cast<u32_le>(command_header->type.Value()) -
static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion);
}
u32_le GetCommand() const {
return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand();
}
bool IsTipc() const {
return command_header->IsTipc();
}
IPC::CommandType GetCommandType() const {
return command_header->type;
}
u64 GetPID() const {
return pid;
}
u32 GetDataPayloadOffset() const {
return data_payload_offset;
}
const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
return buffer_x_desciptors;
}
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
return buffer_a_desciptors;
}
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
return buffer_b_desciptors;
}
const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
return buffer_c_desciptors;
}
const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
return domain_message_header.value();
}
bool HasDomainMessageHeader() const {
return domain_message_header.has_value();
}
/// Helper function to read a buffer using the appropriate buffer descriptor
std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to write a buffer using the appropriate buffer descriptor
std::size_t WriteBuffer(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
/// Helper function to write buffer B
std::size_t WriteBufferB(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
/// Helper function to write buffer C
std::size_t WriteBufferC(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
/* Helper function to write a buffer using the appropriate buffer descriptor
*
* @tparam T an arbitrary container that satisfies the
* ContiguousContainer concept in the C++ standard library or a trivially copyable type.
*
* @param data The container/data to write into a buffer.
* @param buffer_index The buffer in particular to write to.
*/
template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
if constexpr (Common::IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Container to WriteBuffer must contain trivially copyable objects");
return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType),
buffer_index);
} else {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return WriteBuffer(&data, sizeof(T), buffer_index);
}
}
/// Helper function to get the size of the input buffer
std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to get the size of the output buffer
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to test whether the input buffer at buffer_index can be read
bool CanReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to test whether the output buffer at buffer_index can be written
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
Handle GetCopyHandle(std::size_t index) const {
return incoming_copy_handles.at(index);
}
Handle GetMoveHandle(std::size_t index) const {
return incoming_move_handles.at(index);
}
void AddMoveObject(KAutoObject* object) {
outgoing_move_objects.emplace_back(object);
}
void AddCopyObject(KAutoObject* object) {
outgoing_copy_objects.emplace_back(object);
}
void AddDomainObject(SessionRequestHandlerPtr object) {
outgoing_domain_objects.emplace_back(std::move(object));
}
template <typename T>
std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock());
}
void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
manager = manager_;
}
std::string Description() const;
KThread& GetThread() {
return *thread;
}
std::shared_ptr<SessionRequestManager> GetManager() const {
return manager.lock();
}
private:
friend class IPC::ResponseBuilder;
void ParseCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
Kernel::KServerSession* server_session{};
KThread* thread;
std::vector<Handle> incoming_move_handles;
std::vector<Handle> incoming_copy_handles;
std::vector<KAutoObject*> outgoing_move_objects;
std::vector<KAutoObject*> outgoing_copy_objects;
std::vector<SessionRequestHandlerPtr> outgoing_domain_objects;
std::optional<IPC::CommandHeader> command_header;
std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::optional<IPC::DataPayloadHeader> data_payload_header;
std::optional<IPC::DomainMessageHeader> domain_message_header;
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors;
std::vector<IPC::BufferDescriptorC> buffer_c_desciptors;
u32_le command{};
u64 pid{};
u32 write_size{};
u32 data_payload_offset{};
u32 handles_offset{};
u32 domain_offset{};
std::weak_ptr<SessionRequestManager> manager{};
KernelCore& kernel;
Core::Memory::Memory& memory;
};
} // namespace Kernel

View File

@@ -1,260 +1,260 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_session_request.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_transfer_memory.h"
namespace Kernel::Init {
#define SLAB_COUNT(CLASS) kernel.SlabResourceCounts().num_##CLASS
#define FOREACH_SLAB_TYPE(HANDLER, ...) \
HANDLER(KProcess, (SLAB_COUNT(KProcess)), ##__VA_ARGS__) \
HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KThreadLocalPage, \
(SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
namespace {
#define DEFINE_SLAB_TYPE_ENUM_MEMBER(NAME, COUNT, ...) KSlabType_##NAME,
enum KSlabType : u32 {
FOREACH_SLAB_TYPE(DEFINE_SLAB_TYPE_ENUM_MEMBER) KSlabType_Count,
};
#undef DEFINE_SLAB_TYPE_ENUM_MEMBER
// Constexpr counts.
constexpr size_t SlabCountKProcess = 80;
constexpr size_t SlabCountKThread = 800;
constexpr size_t SlabCountKEvent = 900;
constexpr size_t SlabCountKInterruptEvent = 100;
constexpr size_t SlabCountKPort = 384;
constexpr size_t SlabCountKSharedMemory = 80;
constexpr size_t SlabCountKTransferMemory = 200;
constexpr size_t SlabCountKCodeMemory = 10;
constexpr size_t SlabCountKDeviceAddressSpace = 300;
constexpr size_t SlabCountKSession = 1133;
constexpr size_t SlabCountKLightSession = 100;
constexpr size_t SlabCountKObjectName = 7;
constexpr size_t SlabCountKResourceLimit = 5;
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
constexpr size_t SlabCountKIoPool = 1;
constexpr size_t SlabCountKIoRegion = 6;
constexpr size_t SlabCountExtraKThread = 160;
/// Helper function to translate from the slab virtual address to the reserved location in physical
/// memory.
static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) {
slab_addr -= memory_layout.GetSlabRegionAddress();
return slab_addr + Core::DramMemoryMap::SlabHeapBase;
}
template <typename T>
VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address,
size_t num_objects) {
const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
VAddr start = Common::AlignUp(address, alignof(T));
// This should use the virtual memory address passed in, but currently, we do not setup the
// kernel virtual memory layout. Instead, we simply map these at a region of physical memory
// that we reserve for the slab heaps.
// TODO(bunnei): Fix this once we support the kernel virtual memory layout.
if (size > 0) {
void* backing_kernel_memory{system.DeviceMemory().GetPointer<void>(
TranslateSlabAddrToPhysical(memory_layout, start))};
const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
ASSERT(region != nullptr);
ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
T::InitializeSlabHeap(system.Kernel(), backing_kernel_memory, size);
}
return start + size;
}
size_t CalculateSlabHeapGapSize() {
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
return KernelSlabHeapGapSize;
}
} // namespace
KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
return {
.num_KProcess = SlabCountKProcess,
.num_KThread = SlabCountKThread,
.num_KEvent = SlabCountKEvent,
.num_KInterruptEvent = SlabCountKInterruptEvent,
.num_KPort = SlabCountKPort,
.num_KSharedMemory = SlabCountKSharedMemory,
.num_KTransferMemory = SlabCountKTransferMemory,
.num_KCodeMemory = SlabCountKCodeMemory,
.num_KDeviceAddressSpace = SlabCountKDeviceAddressSpace,
.num_KSession = SlabCountKSession,
.num_KLightSession = SlabCountKLightSession,
.num_KObjectName = SlabCountKObjectName,
.num_KResourceLimit = SlabCountKResourceLimit,
.num_KDebug = SlabCountKDebug,
.num_KIoPool = SlabCountKIoPool,
.num_KIoRegion = SlabCountKIoRegion,
};
}
void InitializeSlabResourceCounts(KernelCore& kernel) {
kernel.SlabResourceCounts() = KSlabResourceCounts::CreateDefault();
if (KSystemControl::Init::ShouldIncreaseThreadResourceLimit()) {
kernel.SlabResourceCounts().num_KThread += SlabCountExtraKThread;
}
}
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
size_t size = 0;
#define ADD_SLAB_SIZE(NAME, COUNT, ...) \
{ \
size += alignof(NAME); \
size += Common::AlignUp(sizeof(NAME) * (COUNT), alignof(void*)); \
};
// Add the size required for each slab.
FOREACH_SLAB_TYPE(ADD_SLAB_SIZE)
#undef ADD_SLAB_SIZE
// Add the reserved size.
size += CalculateSlabHeapGapSize();
return size;
}
void InitializeKPageBufferSlabHeap(Core::System& system) {
auto& kernel = system.Kernel();
const auto& counts = kernel.SlabResourceCounts();
const size_t num_pages =
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
const size_t slab_size = num_pages * PageSize;
// Reserve memory from the system resource limit.
ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
// Allocate memory for the slab.
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
const PAddr slab_address =
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
ASSERT(slab_address != 0);
// Initialize the slabheap.
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
slab_size);
}
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
auto& kernel = system.Kernel();
// Get the start of the slab region, since that's where we'll be working.
VAddr address = memory_layout.GetSlabRegionAddress();
// Initialize slab type array to be in sorted order.
std::array<KSlabType, KSlabType_Count> slab_types;
for (size_t i = 0; i < slab_types.size(); i++) {
slab_types[i] = static_cast<KSlabType>(i);
}
// N shuffles the slab type array with the following simple algorithm.
for (size_t i = 0; i < slab_types.size(); i++) {
const size_t rnd = KSystemControl::GenerateRandomRange(0, slab_types.size() - 1);
std::swap(slab_types[i], slab_types[rnd]);
}
// Create an array to represent the gaps between the slabs.
const size_t total_gap_size = CalculateSlabHeapGapSize();
std::array<size_t, slab_types.size()> slab_gaps;
for (auto& slab_gap : slab_gaps) {
// Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
// is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
// will include it ourselves.
slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size);
}
// Sort the array, so that we can treat differences between values as offsets to the starts of
// slabs.
for (size_t i = 1; i < slab_gaps.size(); i++) {
for (size_t j = i; j > 0 && slab_gaps[j - 1] > slab_gaps[j]; j--) {
std::swap(slab_gaps[j], slab_gaps[j - 1]);
}
}
// Track the gaps, so that we can free them to the unused slab tree.
VAddr gap_start = address;
size_t gap_size = 0;
for (size_t i = 0; i < slab_gaps.size(); i++) {
// Add the random gap to the address.
const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
address += cur_gap;
gap_size += cur_gap;
#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \
case KSlabType_##NAME: \
if (COUNT > 0) { \
address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
} \
break;
// Initialize the slabheap.
switch (slab_types[i]) {
// For each of the slab types, we want to initialize that heap.
FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
// If we somehow get an invalid type, abort.
default:
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
}
// If we've hit the end of a gap, free it.
if (gap_start + gap_size != address) {
gap_start = address;
gap_size = 0;
}
}
}
} // namespace Kernel::Init
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_session_request.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_transfer_memory.h"
namespace Kernel::Init {
#define SLAB_COUNT(CLASS) kernel.SlabResourceCounts().num_##CLASS
#define FOREACH_SLAB_TYPE(HANDLER, ...) \
HANDLER(KProcess, (SLAB_COUNT(KProcess)), ##__VA_ARGS__) \
HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KThreadLocalPage, \
(SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
namespace {
#define DEFINE_SLAB_TYPE_ENUM_MEMBER(NAME, COUNT, ...) KSlabType_##NAME,
enum KSlabType : u32 {
FOREACH_SLAB_TYPE(DEFINE_SLAB_TYPE_ENUM_MEMBER) KSlabType_Count,
};
#undef DEFINE_SLAB_TYPE_ENUM_MEMBER
// Constexpr counts.
constexpr size_t SlabCountKProcess = 80;
constexpr size_t SlabCountKThread = 800;
constexpr size_t SlabCountKEvent = 900;
constexpr size_t SlabCountKInterruptEvent = 100;
constexpr size_t SlabCountKPort = 384;
constexpr size_t SlabCountKSharedMemory = 80;
constexpr size_t SlabCountKTransferMemory = 200;
constexpr size_t SlabCountKCodeMemory = 10;
constexpr size_t SlabCountKDeviceAddressSpace = 300;
constexpr size_t SlabCountKSession = 1133;
constexpr size_t SlabCountKLightSession = 100;
constexpr size_t SlabCountKObjectName = 7;
constexpr size_t SlabCountKResourceLimit = 5;
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
constexpr size_t SlabCountKIoPool = 1;
constexpr size_t SlabCountKIoRegion = 6;
constexpr size_t SlabCountExtraKThread = 160;
/// Helper function to translate from the slab virtual address to the reserved location in physical
/// memory.
static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) {
slab_addr -= memory_layout.GetSlabRegionAddress();
return slab_addr + Core::DramMemoryMap::SlabHeapBase;
}
template <typename T>
VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address,
size_t num_objects) {
const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
VAddr start = Common::AlignUp(address, alignof(T));
// This should use the virtual memory address passed in, but currently, we do not setup the
// kernel virtual memory layout. Instead, we simply map these at a region of physical memory
// that we reserve for the slab heaps.
// TODO(bunnei): Fix this once we support the kernel virtual memory layout.
if (size > 0) {
void* backing_kernel_memory{system.DeviceMemory().GetPointer<void>(
TranslateSlabAddrToPhysical(memory_layout, start))};
const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
ASSERT(region != nullptr);
ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
T::InitializeSlabHeap(system.Kernel(), backing_kernel_memory, size);
}
return start + size;
}
size_t CalculateSlabHeapGapSize() {
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
return KernelSlabHeapGapSize;
}
} // namespace
KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
return {
.num_KProcess = SlabCountKProcess,
.num_KThread = SlabCountKThread,
.num_KEvent = SlabCountKEvent,
.num_KInterruptEvent = SlabCountKInterruptEvent,
.num_KPort = SlabCountKPort,
.num_KSharedMemory = SlabCountKSharedMemory,
.num_KTransferMemory = SlabCountKTransferMemory,
.num_KCodeMemory = SlabCountKCodeMemory,
.num_KDeviceAddressSpace = SlabCountKDeviceAddressSpace,
.num_KSession = SlabCountKSession,
.num_KLightSession = SlabCountKLightSession,
.num_KObjectName = SlabCountKObjectName,
.num_KResourceLimit = SlabCountKResourceLimit,
.num_KDebug = SlabCountKDebug,
.num_KIoPool = SlabCountKIoPool,
.num_KIoRegion = SlabCountKIoRegion,
};
}
void InitializeSlabResourceCounts(KernelCore& kernel) {
kernel.SlabResourceCounts() = KSlabResourceCounts::CreateDefault();
if (KSystemControl::Init::ShouldIncreaseThreadResourceLimit()) {
kernel.SlabResourceCounts().num_KThread += SlabCountExtraKThread;
}
}
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
size_t size = 0;
#define ADD_SLAB_SIZE(NAME, COUNT, ...) \
{ \
size += alignof(NAME); \
size += Common::AlignUp(sizeof(NAME) * (COUNT), alignof(void*)); \
};
// Add the size required for each slab.
FOREACH_SLAB_TYPE(ADD_SLAB_SIZE)
#undef ADD_SLAB_SIZE
// Add the reserved size.
size += CalculateSlabHeapGapSize();
return size;
}
void InitializeKPageBufferSlabHeap(Core::System& system) {
auto& kernel = system.Kernel();
const auto& counts = kernel.SlabResourceCounts();
const size_t num_pages =
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
const size_t slab_size = num_pages * PageSize;
// Reserve memory from the system resource limit.
ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
// Allocate memory for the slab.
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
const PAddr slab_address =
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
ASSERT(slab_address != 0);
// Initialize the slabheap.
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
slab_size);
}
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
auto& kernel = system.Kernel();
// Get the start of the slab region, since that's where we'll be working.
VAddr address = memory_layout.GetSlabRegionAddress();
// Initialize slab type array to be in sorted order.
std::array<KSlabType, KSlabType_Count> slab_types;
for (size_t i = 0; i < slab_types.size(); i++) {
slab_types[i] = static_cast<KSlabType>(i);
}
// N shuffles the slab type array with the following simple algorithm.
for (size_t i = 0; i < slab_types.size(); i++) {
const size_t rnd = KSystemControl::GenerateRandomRange(0, slab_types.size() - 1);
std::swap(slab_types[i], slab_types[rnd]);
}
// Create an array to represent the gaps between the slabs.
const size_t total_gap_size = CalculateSlabHeapGapSize();
std::array<size_t, slab_types.size()> slab_gaps;
for (auto& slab_gap : slab_gaps) {
// Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
// is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
// will include it ourselves.
slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size);
}
// Sort the array, so that we can treat differences between values as offsets to the starts of
// slabs.
for (size_t i = 1; i < slab_gaps.size(); i++) {
for (size_t j = i; j > 0 && slab_gaps[j - 1] > slab_gaps[j]; j--) {
std::swap(slab_gaps[j], slab_gaps[j - 1]);
}
}
// Track the gaps, so that we can free them to the unused slab tree.
VAddr gap_start = address;
size_t gap_size = 0;
for (size_t i = 0; i < slab_gaps.size(); i++) {
// Add the random gap to the address.
const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
address += cur_gap;
gap_size += cur_gap;
#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \
case KSlabType_##NAME: \
if (COUNT > 0) { \
address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
} \
break;
// Initialize the slabheap.
switch (slab_types[i]) {
// For each of the slab types, we want to initialize that heap.
FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
// If we somehow get an invalid type, abort.
default:
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
}
// If we've hit the end of a gap, free it.
if (gap_start + gap_size != address) {
gap_start = address;
gap_size = 0;
}
}
}
} // namespace Kernel::Init

View File

@@ -1,43 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Core {
class System;
} // namespace Core
namespace Kernel {
class KernelCore;
class KMemoryLayout;
} // namespace Kernel
namespace Kernel::Init {
struct KSlabResourceCounts {
static KSlabResourceCounts CreateDefault();
size_t num_KProcess;
size_t num_KThread;
size_t num_KEvent;
size_t num_KInterruptEvent;
size_t num_KPort;
size_t num_KSharedMemory;
size_t num_KTransferMemory;
size_t num_KCodeMemory;
size_t num_KDeviceAddressSpace;
size_t num_KSession;
size_t num_KLightSession;
size_t num_KObjectName;
size_t num_KResourceLimit;
size_t num_KDebug;
size_t num_KIoPool;
size_t num_KIoRegion;
};
void InitializeSlabResourceCounts(KernelCore& kernel);
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
void InitializeKPageBufferSlabHeap(Core::System& system);
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
} // namespace Kernel::Init
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Core {
class System;
} // namespace Core
namespace Kernel {
class KernelCore;
class KMemoryLayout;
} // namespace Kernel
namespace Kernel::Init {
struct KSlabResourceCounts {
static KSlabResourceCounts CreateDefault();
size_t num_KProcess;
size_t num_KThread;
size_t num_KEvent;
size_t num_KInterruptEvent;
size_t num_KPort;
size_t num_KSharedMemory;
size_t num_KTransferMemory;
size_t num_KCodeMemory;
size_t num_KDeviceAddressSpace;
size_t num_KSession;
size_t num_KLightSession;
size_t num_KObjectName;
size_t num_KResourceLimit;
size_t num_KDebug;
size_t num_KIoPool;
size_t num_KIoRegion;
};
void InitializeSlabResourceCounts(KernelCore& kernel);
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
void InitializeKPageBufferSlabHeap(Core::System& system);
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
} // namespace Kernel::Init

View File

@@ -1,22 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "common/literals.h"
#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
namespace Kernel {
using namespace Common::Literals;
constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB;
static inline PAddr GetInitialProcessBinaryPhysicalAddress() {
return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress(
MainMemoryAddress);
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "common/literals.h"
#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
namespace Kernel {
using namespace Common::Literals;
constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB;
static inline PAddr GetInitialProcessBinaryPhysicalAddress() {
return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress(
MainMemoryAddress);
}
} // namespace Kernel

View File

@@ -1,333 +1,333 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_address_arbiter.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/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/memory.h"
namespace Kernel {
KAddressArbiter::KAddressArbiter(Core::System& system_)
: system{system_}, kernel{system.Kernel()} {}
KAddressArbiter::~KAddressArbiter() = default;
namespace {
bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
*out = system.Memory().Read32(address);
return true;
}
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
// Load the value from the address.
const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
// Compare it to the desired one.
if (current_value < value) {
// If less than, we want to try to decrement.
const s32 decrement_value = current_value - 1;
// Decrement and try to store.
if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) {
// If we failed to store, try again.
DecrementIfLessThan(system, out, address, value);
}
} else {
// Otherwise, clear our exclusive hold and finish
monitor.ClearExclusive(current_core);
}
// We're done.
*out = current_value;
return true;
}
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
// Load the value from the address.
const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
// Compare it to the desired one.
if (current_value == value) {
// If equal, we want to try to write the new value.
// Try to store.
if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) {
// If we failed to store, try again.
UpdateIfEqual(system, out, address, value, new_value);
}
} else {
// Otherwise, clear our exclusive hold and finish.
monitor.ClearExclusive(current_core);
}
// We're done.
*out = current_value;
return true;
}
class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
public:
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// If the thread is waiting on an address arbiter, remove it from the tree.
if (waiting_thread->IsWaitingForAddressArbiter()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearAddressArbiter();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KAddressArbiter::ThreadTree* m_tree;
};
} // namespace
Result KAddressArbiter::Signal(VAddr addr, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
++num_waiters;
}
}
return ResultSuccess;
}
Result KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
// Check the userspace value.
s32 user_value{};
if (!UpdateIfEqual(system, &user_value, addr, value, value + 1)) {
LOG_ERROR(Kernel, "Invalid current memory!");
return ResultInvalidCurrentMemory;
}
if (user_value != value) {
return ResultInvalidState;
}
auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
++num_waiters;
}
}
return ResultSuccess;
}
Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
[[maybe_unused]] const KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_key({addr, -1});
// Determine the updated value.
s32 new_value{};
if (count <= 0) {
if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
new_value = value - 2;
} else {
new_value = value + 1;
}
} else {
if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
auto tmp_it = it;
s32 tmp_num_waiters{};
while (++tmp_it != thread_tree.end() && tmp_it->GetAddressArbiterKey() == addr) {
if (tmp_num_waiters++ >= count) {
break;
}
}
if (tmp_num_waiters < count) {
new_value = value - 1;
} else {
new_value = value;
}
} else {
new_value = value + 1;
}
}
// Check the userspace value.
s32 user_value{};
bool succeeded{};
if (value != new_value) {
succeeded = UpdateIfEqual(system, &user_value, addr, value, new_value);
} else {
succeeded = ReadFromUser(system, &user_value, addr);
}
if (!succeeded) {
LOG_ERROR(Kernel, "Invalid current memory!");
return ResultInvalidCurrentMemory;
}
if (user_value != value) {
return ResultInvalidState;
}
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
++num_waiters;
}
}
return ResultSuccess;
}
Result KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Read the value from userspace.
s32 user_value{};
bool succeeded{};
if (decrement) {
succeeded = DecrementIfLessThan(system, &user_value, addr, value);
} else {
succeeded = ReadFromUser(system, &user_value, addr);
}
if (!succeeded) {
slp.CancelSleep();
return ResultInvalidCurrentMemory;
}
// Check that the value is less than the specified one.
if (user_value >= value) {
slp.CancelSleep();
return ResultInvalidState;
}
// Check that the timeout is non-zero.
if (timeout == 0) {
slp.CancelSleep();
return ResultTimedOut;
}
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Get the result.
return cur_thread->GetWaitResult();
}
Result KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, &user_value, addr)) {
slp.CancelSleep();
return ResultInvalidCurrentMemory;
}
// Check that the value is equal.
if (value != user_value) {
slp.CancelSleep();
return ResultInvalidState;
}
// Check that the timeout is non-zero.
if (timeout == 0) {
slp.CancelSleep();
return ResultTimedOut;
}
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Get the result.
return cur_thread->GetWaitResult();
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_address_arbiter.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/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/memory.h"
namespace Kernel {
KAddressArbiter::KAddressArbiter(Core::System& system_)
: system{system_}, kernel{system.Kernel()} {}
KAddressArbiter::~KAddressArbiter() = default;
namespace {
bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
*out = system.Memory().Read32(address);
return true;
}
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
// Load the value from the address.
const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
// Compare it to the desired one.
if (current_value < value) {
// If less than, we want to try to decrement.
const s32 decrement_value = current_value - 1;
// Decrement and try to store.
if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) {
// If we failed to store, try again.
DecrementIfLessThan(system, out, address, value);
}
} else {
// Otherwise, clear our exclusive hold and finish
monitor.ClearExclusive(current_core);
}
// We're done.
*out = current_value;
return true;
}
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
// Load the value from the address.
const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
// Compare it to the desired one.
if (current_value == value) {
// If equal, we want to try to write the new value.
// Try to store.
if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) {
// If we failed to store, try again.
UpdateIfEqual(system, out, address, value, new_value);
}
} else {
// Otherwise, clear our exclusive hold and finish.
monitor.ClearExclusive(current_core);
}
// We're done.
*out = current_value;
return true;
}
class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
public:
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// If the thread is waiting on an address arbiter, remove it from the tree.
if (waiting_thread->IsWaitingForAddressArbiter()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearAddressArbiter();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KAddressArbiter::ThreadTree* m_tree;
};
} // namespace
Result KAddressArbiter::Signal(VAddr addr, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
++num_waiters;
}
}
return ResultSuccess;
}
Result KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
// Check the userspace value.
s32 user_value{};
if (!UpdateIfEqual(system, &user_value, addr, value, value + 1)) {
LOG_ERROR(Kernel, "Invalid current memory!");
return ResultInvalidCurrentMemory;
}
if (user_value != value) {
return ResultInvalidState;
}
auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
++num_waiters;
}
}
return ResultSuccess;
}
Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
[[maybe_unused]] const KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_key({addr, -1});
// Determine the updated value.
s32 new_value{};
if (count <= 0) {
if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
new_value = value - 2;
} else {
new_value = value + 1;
}
} else {
if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
auto tmp_it = it;
s32 tmp_num_waiters{};
while (++tmp_it != thread_tree.end() && tmp_it->GetAddressArbiterKey() == addr) {
if (tmp_num_waiters++ >= count) {
break;
}
}
if (tmp_num_waiters < count) {
new_value = value - 1;
} else {
new_value = value;
}
} else {
new_value = value + 1;
}
}
// Check the userspace value.
s32 user_value{};
bool succeeded{};
if (value != new_value) {
succeeded = UpdateIfEqual(system, &user_value, addr, value, new_value);
} else {
succeeded = ReadFromUser(system, &user_value, addr);
}
if (!succeeded) {
LOG_ERROR(Kernel, "Invalid current memory!");
return ResultInvalidCurrentMemory;
}
if (user_value != value) {
return ResultInvalidState;
}
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
++num_waiters;
}
}
return ResultSuccess;
}
Result KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Read the value from userspace.
s32 user_value{};
bool succeeded{};
if (decrement) {
succeeded = DecrementIfLessThan(system, &user_value, addr, value);
} else {
succeeded = ReadFromUser(system, &user_value, addr);
}
if (!succeeded) {
slp.CancelSleep();
return ResultInvalidCurrentMemory;
}
// Check that the value is less than the specified one.
if (user_value >= value) {
slp.CancelSleep();
return ResultInvalidState;
}
// Check that the timeout is non-zero.
if (timeout == 0) {
slp.CancelSleep();
return ResultTimedOut;
}
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Get the result.
return cur_thread->GetWaitResult();
}
Result KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, &user_value, addr)) {
slp.CancelSleep();
return ResultInvalidCurrentMemory;
}
// Check that the value is equal.
if (value != user_value) {
slp.CancelSleep();
return ResultInvalidState;
}
// Check that the timeout is non-zero.
if (timeout == 0) {
slp.CancelSleep();
return ResultTimedOut;
}
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Get the result.
return cur_thread->GetWaitResult();
}
} // namespace Kernel

View File

@@ -1,68 +1,68 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/svc_types.h"
union Result;
namespace Core {
class System;
}
namespace Kernel {
class KernelCore;
class KAddressArbiter {
public:
using ThreadTree = KConditionVariable::ThreadTree;
explicit KAddressArbiter(Core::System& system_);
~KAddressArbiter();
[[nodiscard]] Result SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, s32 count) {
switch (type) {
case Svc::SignalType::Signal:
return Signal(addr, count);
case Svc::SignalType::SignalAndIncrementIfEqual:
return SignalAndIncrementIfEqual(addr, value, count);
case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
}
ASSERT(false);
return ResultUnknown;
}
[[nodiscard]] Result WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
s64 timeout) {
switch (type) {
case Svc::ArbitrationType::WaitIfLessThan:
return WaitIfLessThan(addr, value, false, timeout);
case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
return WaitIfLessThan(addr, value, true, timeout);
case Svc::ArbitrationType::WaitIfEqual:
return WaitIfEqual(addr, value, timeout);
}
ASSERT(false);
return ResultUnknown;
}
private:
[[nodiscard]] Result Signal(VAddr addr, s32 count);
[[nodiscard]] Result SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
[[nodiscard]] Result SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
[[nodiscard]] Result WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
[[nodiscard]] Result WaitIfEqual(VAddr addr, s32 value, s64 timeout);
ThreadTree thread_tree;
Core::System& system;
KernelCore& kernel;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/svc_types.h"
union Result;
namespace Core {
class System;
}
namespace Kernel {
class KernelCore;
class KAddressArbiter {
public:
using ThreadTree = KConditionVariable::ThreadTree;
explicit KAddressArbiter(Core::System& system_);
~KAddressArbiter();
[[nodiscard]] Result SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, s32 count) {
switch (type) {
case Svc::SignalType::Signal:
return Signal(addr, count);
case Svc::SignalType::SignalAndIncrementIfEqual:
return SignalAndIncrementIfEqual(addr, value, count);
case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
}
ASSERT(false);
return ResultUnknown;
}
[[nodiscard]] Result WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
s64 timeout) {
switch (type) {
case Svc::ArbitrationType::WaitIfLessThan:
return WaitIfLessThan(addr, value, false, timeout);
case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
return WaitIfLessThan(addr, value, true, timeout);
case Svc::ArbitrationType::WaitIfEqual:
return WaitIfEqual(addr, value, timeout);
}
ASSERT(false);
return ResultUnknown;
}
private:
[[nodiscard]] Result Signal(VAddr addr, s32 count);
[[nodiscard]] Result SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
[[nodiscard]] Result SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
[[nodiscard]] Result WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
[[nodiscard]] Result WaitIfEqual(VAddr addr, s32 value, s64 timeout);
ThreadTree thread_tree;
Core::System& system;
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -1,108 +1,108 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include "common/assert.h"
#include "common/literals.h"
#include "core/hle/kernel/k_address_space_info.h"
namespace Kernel {
namespace {
using namespace Common::Literals;
constexpr u64 Size_Invalid = UINT64_MAX;
// clang-format off
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 32, .address = 2_MiB , .size = 1_GiB - 2_MiB , .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 32, .address = 1_GiB , .size = 4_GiB - 1_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 32, .address = Size_Invalid, .size = 1_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 32, .address = Size_Invalid, .size = 1_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = 128_MiB , .size = 2_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
{ .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::MapSmall },
{ .bit_width = 39, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Size_Invalid, .size = 2_GiB , .type = KAddressSpaceInfo::Type::Stack, },
}};
// clang-format on
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Size_Invalid;
}
using IndexArray =
std::array<std::size_t, static_cast<std::size_t>(KAddressSpaceInfo::Type::Count)>;
constexpr IndexArray AddressSpaceIndices32Bit{
0, 1, 0, 2, 0, 3,
};
constexpr IndexArray AddressSpaceIndices36Bit{
4, 5, 4, 6, 4, 7,
};
constexpr IndexArray AddressSpaceIndices39Bit{
9, 8, 8, 10, 12, 11,
};
constexpr bool IsAllowed32BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
type != KAddressSpaceInfo::Type::Stack;
}
constexpr bool IsAllowed36BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
type != KAddressSpaceInfo::Type::Stack;
}
constexpr bool IsAllowed39BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::MapLarge;
}
} // namespace
u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
const std::size_t index{static_cast<std::size_t>(type)};
switch (width) {
case 32:
ASSERT(IsAllowed32BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].address;
case 36:
ASSERT(IsAllowed36BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].address;
case 39:
ASSERT(IsAllowed39BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
}
ASSERT(false);
return 0;
}
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:
ASSERT(IsAllowed32BitType(type));
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].size;
case 36:
ASSERT(IsAllowed36BitType(type));
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].size;
case 39:
ASSERT(IsAllowed39BitType(type));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
}
ASSERT(false);
return 0;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include "common/assert.h"
#include "common/literals.h"
#include "core/hle/kernel/k_address_space_info.h"
namespace Kernel {
namespace {
using namespace Common::Literals;
constexpr u64 Size_Invalid = UINT64_MAX;
// clang-format off
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
{ .bit_width = 32, .address = 2_MiB , .size = 1_GiB - 2_MiB , .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 32, .address = 1_GiB , .size = 4_GiB - 1_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 32, .address = Size_Invalid, .size = 1_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 32, .address = Size_Invalid, .size = 1_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = 128_MiB , .size = 2_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::MapSmall, },
{ .bit_width = 36, .address = 2_GiB , .size = 64_GiB - 2_GiB , .type = KAddressSpaceInfo::Type::MapLarge, },
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = 128_MiB , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
{ .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::MapSmall },
{ .bit_width = 39, .address = Size_Invalid, .size = 6_GiB , .type = KAddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Size_Invalid, .size = 64_GiB , .type = KAddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Size_Invalid, .size = 2_GiB , .type = KAddressSpaceInfo::Type::Stack, },
}};
// clang-format on
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Size_Invalid;
}
using IndexArray =
std::array<std::size_t, static_cast<std::size_t>(KAddressSpaceInfo::Type::Count)>;
constexpr IndexArray AddressSpaceIndices32Bit{
0, 1, 0, 2, 0, 3,
};
constexpr IndexArray AddressSpaceIndices36Bit{
4, 5, 4, 6, 4, 7,
};
constexpr IndexArray AddressSpaceIndices39Bit{
9, 8, 8, 10, 12, 11,
};
constexpr bool IsAllowed32BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
type != KAddressSpaceInfo::Type::Stack;
}
constexpr bool IsAllowed36BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
type != KAddressSpaceInfo::Type::Stack;
}
constexpr bool IsAllowed39BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::MapLarge;
}
} // namespace
u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
const std::size_t index{static_cast<std::size_t>(type)};
switch (width) {
case 32:
ASSERT(IsAllowed32BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].address;
case 36:
ASSERT(IsAllowed36BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].address;
case 39:
ASSERT(IsAllowed39BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
}
ASSERT(false);
return 0;
}
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:
ASSERT(IsAllowed32BitType(type));
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].size;
case 36:
ASSERT(IsAllowed36BitType(type));
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].size;
case 39:
ASSERT(IsAllowed39BitType(type));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
}
ASSERT(false);
return 0;
}
} // namespace Kernel

View File

@@ -1,30 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel {
struct KAddressSpaceInfo final {
enum class Type : u32 {
MapSmall = 0,
MapLarge = 1,
Map39Bit = 2,
Heap = 3,
Stack = 4,
Alias = 5,
Count,
};
static u64 GetAddressSpaceStart(std::size_t width, Type type);
static std::size_t GetAddressSpaceSize(std::size_t width, Type type);
const std::size_t bit_width{};
const std::size_t address{};
const std::size_t size{};
const Type type{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel {
struct KAddressSpaceInfo final {
enum class Type : u32 {
MapSmall = 0,
MapLarge = 1,
Map39Bit = 2,
Heap = 3,
Stack = 4,
Alias = 5,
Count,
};
static u64 GetAddressSpaceStart(std::size_t width, Type type);
static std::size_t GetAddressSpaceSize(std::size_t width, Type type);
const std::size_t bit_width{};
const std::size_t address{};
const std::size_t size{};
const Type type{};
};
} // namespace Kernel

View File

@@ -1,52 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
namespace Kernel {
class KAffinityMask {
public:
constexpr KAffinityMask() = default;
[[nodiscard]] constexpr u64 GetAffinityMask() const {
return this->mask;
}
constexpr void SetAffinityMask(u64 new_mask) {
ASSERT((new_mask & ~AllowedAffinityMask) == 0);
this->mask = new_mask;
}
[[nodiscard]] constexpr bool GetAffinity(s32 core) const {
return (this->mask & GetCoreBit(core)) != 0;
}
constexpr void SetAffinity(s32 core, bool set) {
if (set) {
this->mask |= GetCoreBit(core);
} else {
this->mask &= ~GetCoreBit(core);
}
}
constexpr void SetAll() {
this->mask = AllowedAffinityMask;
}
private:
[[nodiscard]] static constexpr u64 GetCoreBit(s32 core) {
ASSERT(0 <= core && core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
return (1ULL << core);
}
static constexpr u64 AllowedAffinityMask = (1ULL << Core::Hardware::NUM_CPU_CORES) - 1;
u64 mask{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
namespace Kernel {
class KAffinityMask {
public:
constexpr KAffinityMask() = default;
[[nodiscard]] constexpr u64 GetAffinityMask() const {
return this->mask;
}
constexpr void SetAffinityMask(u64 new_mask) {
ASSERT((new_mask & ~AllowedAffinityMask) == 0);
this->mask = new_mask;
}
[[nodiscard]] constexpr bool GetAffinity(s32 core) const {
return (this->mask & GetCoreBit(core)) != 0;
}
constexpr void SetAffinity(s32 core, bool set) {
if (set) {
this->mask |= GetCoreBit(core);
} else {
this->mask &= ~GetCoreBit(core);
}
}
constexpr void SetAll() {
this->mask = AllowedAffinityMask;
}
private:
[[nodiscard]] static constexpr u64 GetCoreBit(s32 core) {
ASSERT(0 <= core && core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
return (1ULL << core);
}
static constexpr u64 AllowedAffinityMask = (1ULL << Core::Hardware::NUM_CPU_CORES) - 1;
u64 mask{};
};
} // namespace Kernel

View File

@@ -1,22 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
KAutoObject* KAutoObject::Create(KAutoObject* obj) {
obj->m_ref_count = 1;
return obj;
}
void KAutoObject::RegisterWithKernel() {
kernel.RegisterKernelObject(this);
}
void KAutoObject::UnregisterWithKernel() {
kernel.UnregisterKernelObject(this);
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
KAutoObject* KAutoObject::Create(KAutoObject* obj) {
obj->m_ref_count = 1;
return obj;
}
void KAutoObject::RegisterWithKernel() {
kernel.RegisterKernelObject(this);
}
void KAutoObject::UnregisterWithKernel() {
kernel.UnregisterKernelObject(this);
}
} // namespace Kernel

View File

@@ -1,319 +1,319 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <string>
#include <boost/intrusive/rbtree.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_class_token.h"
namespace Kernel {
class KernelCore;
class KProcess;
#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \
\
private: \
friend class ::Kernel::KClassTokenGenerator; \
static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \
static constexpr inline const char* const TypeName = #CLASS; \
static constexpr inline ClassTokenType ClassToken() { \
return ::Kernel::ClassToken<CLASS>; \
} \
\
public: \
YUZU_NON_COPYABLE(CLASS); \
YUZU_NON_MOVEABLE(CLASS); \
\
using BaseClass = BASE_CLASS; \
static constexpr TypeObj GetStaticTypeObj() { \
constexpr ClassTokenType Token = ClassToken(); \
return TypeObj(TypeName, Token); \
} \
static constexpr const char* GetStaticTypeName() { \
return TypeName; \
} \
virtual TypeObj GetTypeObj() ATTRIBUTE { \
return GetStaticTypeObj(); \
} \
virtual const char* GetTypeName() ATTRIBUTE { \
return GetStaticTypeName(); \
} \
\
private: \
constexpr bool operator!=(const TypeObj& rhs)
#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override)
class KAutoObject {
protected:
class TypeObj {
public:
constexpr explicit TypeObj(const char* n, ClassTokenType tok)
: m_name(n), m_class_token(tok) {}
constexpr const char* GetName() const {
return m_name;
}
constexpr ClassTokenType GetClassToken() const {
return m_class_token;
}
constexpr bool operator==(const TypeObj& rhs) const {
return this->GetClassToken() == rhs.GetClassToken();
}
constexpr bool operator!=(const TypeObj& rhs) const {
return this->GetClassToken() != rhs.GetClassToken();
}
constexpr bool IsDerivedFrom(const TypeObj& rhs) const {
return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken();
}
private:
const char* m_name;
ClassTokenType m_class_token;
};
private:
KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);
public:
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
RegisterWithKernel();
}
virtual ~KAutoObject() = default;
static KAutoObject* Create(KAutoObject* ptr);
// Destroy is responsible for destroying the auto object's resources when ref_count hits zero.
virtual void Destroy() {
UNIMPLEMENTED();
}
// Finalize is responsible for cleaning up resource, but does not destroy the object.
virtual void Finalize() {}
virtual KProcess* GetOwner() const {
return nullptr;
}
u32 GetReferenceCount() const {
return m_ref_count.load();
}
bool IsDerivedFrom(const TypeObj& rhs) const {
return this->GetTypeObj().IsDerivedFrom(rhs);
}
bool IsDerivedFrom(const KAutoObject& rhs) const {
return this->IsDerivedFrom(rhs.GetTypeObj());
}
template <typename Derived>
Derived DynamicCast() {
static_assert(std::is_pointer_v<Derived>);
using DerivedType = std::remove_pointer_t<Derived>;
if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
return static_cast<Derived>(this);
} else {
return nullptr;
}
}
template <typename Derived>
const Derived DynamicCast() const {
static_assert(std::is_pointer_v<Derived>);
using DerivedType = std::remove_pointer_t<Derived>;
if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
return static_cast<Derived>(this);
} else {
return nullptr;
}
}
bool Open() {
// Atomically increment the reference count, only if it's positive.
u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
do {
if (cur_ref_count == 0) {
return false;
}
ASSERT(cur_ref_count < cur_ref_count + 1);
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1,
std::memory_order_relaxed));
return true;
}
void Close() {
// Atomically decrement the reference count, not allowing it to become negative.
u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
do {
ASSERT(cur_ref_count > 0);
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
std::memory_order_acq_rel));
// If ref count hits zero, destroy the object.
if (cur_ref_count - 1 == 0) {
this->Destroy();
this->UnregisterWithKernel();
}
}
const std::string& GetName() const {
return name;
}
private:
void RegisterWithKernel();
void UnregisterWithKernel();
protected:
KernelCore& kernel;
std::string name;
private:
std::atomic<u32> m_ref_count{};
};
class KAutoObjectWithListContainer;
class KAutoObjectWithList : public KAutoObject, public boost::intrusive::set_base_hook<> {
public:
explicit KAutoObjectWithList(KernelCore& kernel_) : KAutoObject(kernel_) {}
static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) {
const u64 lid = lhs.GetId();
const u64 rid = rhs.GetId();
if (lid < rid) {
return -1;
} else if (lid > rid) {
return 1;
} else {
return 0;
}
}
friend bool operator<(const KAutoObjectWithList& left, const KAutoObjectWithList& right) {
return &left < &right;
}
public:
virtual u64 GetId() const {
return reinterpret_cast<u64>(this);
}
virtual const std::string& GetName() const {
return name;
}
private:
friend class KAutoObjectWithListContainer;
};
template <typename T>
class KScopedAutoObject {
public:
YUZU_NON_COPYABLE(KScopedAutoObject);
constexpr KScopedAutoObject() = default;
constexpr KScopedAutoObject(T* o) : m_obj(o) {
if (m_obj != nullptr) {
m_obj->Open();
}
}
~KScopedAutoObject() {
if (m_obj != nullptr) {
m_obj->Close();
}
m_obj = nullptr;
}
template <typename U>
requires(std::derived_from<T, U> ||
std::derived_from<U, T>) constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
if constexpr (std::derived_from<U, T>) {
// Upcast.
m_obj = rhs.m_obj;
rhs.m_obj = nullptr;
} else {
// Downcast.
T* derived = nullptr;
if (rhs.m_obj != nullptr) {
derived = rhs.m_obj->template DynamicCast<T*>();
if (derived == nullptr) {
rhs.m_obj->Close();
}
}
m_obj = derived;
rhs.m_obj = nullptr;
}
}
constexpr KScopedAutoObject<T>& operator=(KScopedAutoObject<T>&& rhs) {
rhs.Swap(*this);
return *this;
}
constexpr T* operator->() {
return m_obj;
}
constexpr T& operator*() {
return *m_obj;
}
constexpr void Reset(T* o) {
KScopedAutoObject(o).Swap(*this);
}
constexpr T* GetPointerUnsafe() {
return m_obj;
}
constexpr T* GetPointerUnsafe() const {
return m_obj;
}
constexpr T* ReleasePointerUnsafe() {
T* ret = m_obj;
m_obj = nullptr;
return ret;
}
constexpr bool IsNull() const {
return m_obj == nullptr;
}
constexpr bool IsNotNull() const {
return m_obj != nullptr;
}
private:
template <typename U>
friend class KScopedAutoObject;
private:
T* m_obj{};
private:
constexpr void Swap(KScopedAutoObject& rhs) noexcept {
std::swap(m_obj, rhs.m_obj);
}
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <string>
#include <boost/intrusive/rbtree.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_class_token.h"
namespace Kernel {
class KernelCore;
class KProcess;
#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \
\
private: \
friend class ::Kernel::KClassTokenGenerator; \
static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \
static constexpr inline const char* const TypeName = #CLASS; \
static constexpr inline ClassTokenType ClassToken() { \
return ::Kernel::ClassToken<CLASS>; \
} \
\
public: \
YUZU_NON_COPYABLE(CLASS); \
YUZU_NON_MOVEABLE(CLASS); \
\
using BaseClass = BASE_CLASS; \
static constexpr TypeObj GetStaticTypeObj() { \
constexpr ClassTokenType Token = ClassToken(); \
return TypeObj(TypeName, Token); \
} \
static constexpr const char* GetStaticTypeName() { \
return TypeName; \
} \
virtual TypeObj GetTypeObj() ATTRIBUTE { \
return GetStaticTypeObj(); \
} \
virtual const char* GetTypeName() ATTRIBUTE { \
return GetStaticTypeName(); \
} \
\
private: \
constexpr bool operator!=(const TypeObj& rhs)
#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override)
class KAutoObject {
protected:
class TypeObj {
public:
constexpr explicit TypeObj(const char* n, ClassTokenType tok)
: m_name(n), m_class_token(tok) {}
constexpr const char* GetName() const {
return m_name;
}
constexpr ClassTokenType GetClassToken() const {
return m_class_token;
}
constexpr bool operator==(const TypeObj& rhs) const {
return this->GetClassToken() == rhs.GetClassToken();
}
constexpr bool operator!=(const TypeObj& rhs) const {
return this->GetClassToken() != rhs.GetClassToken();
}
constexpr bool IsDerivedFrom(const TypeObj& rhs) const {
return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken();
}
private:
const char* m_name;
ClassTokenType m_class_token;
};
private:
KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);
public:
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
RegisterWithKernel();
}
virtual ~KAutoObject() = default;
static KAutoObject* Create(KAutoObject* ptr);
// Destroy is responsible for destroying the auto object's resources when ref_count hits zero.
virtual void Destroy() {
UNIMPLEMENTED();
}
// Finalize is responsible for cleaning up resource, but does not destroy the object.
virtual void Finalize() {}
virtual KProcess* GetOwner() const {
return nullptr;
}
u32 GetReferenceCount() const {
return m_ref_count.load();
}
bool IsDerivedFrom(const TypeObj& rhs) const {
return this->GetTypeObj().IsDerivedFrom(rhs);
}
bool IsDerivedFrom(const KAutoObject& rhs) const {
return this->IsDerivedFrom(rhs.GetTypeObj());
}
template <typename Derived>
Derived DynamicCast() {
static_assert(std::is_pointer_v<Derived>);
using DerivedType = std::remove_pointer_t<Derived>;
if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
return static_cast<Derived>(this);
} else {
return nullptr;
}
}
template <typename Derived>
const Derived DynamicCast() const {
static_assert(std::is_pointer_v<Derived>);
using DerivedType = std::remove_pointer_t<Derived>;
if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
return static_cast<Derived>(this);
} else {
return nullptr;
}
}
bool Open() {
// Atomically increment the reference count, only if it's positive.
u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
do {
if (cur_ref_count == 0) {
return false;
}
ASSERT(cur_ref_count < cur_ref_count + 1);
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1,
std::memory_order_relaxed));
return true;
}
void Close() {
// Atomically decrement the reference count, not allowing it to become negative.
u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
do {
ASSERT(cur_ref_count > 0);
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
std::memory_order_acq_rel));
// If ref count hits zero, destroy the object.
if (cur_ref_count - 1 == 0) {
this->Destroy();
this->UnregisterWithKernel();
}
}
const std::string& GetName() const {
return name;
}
private:
void RegisterWithKernel();
void UnregisterWithKernel();
protected:
KernelCore& kernel;
std::string name;
private:
std::atomic<u32> m_ref_count{};
};
class KAutoObjectWithListContainer;
class KAutoObjectWithList : public KAutoObject, public boost::intrusive::set_base_hook<> {
public:
explicit KAutoObjectWithList(KernelCore& kernel_) : KAutoObject(kernel_) {}
static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) {
const u64 lid = lhs.GetId();
const u64 rid = rhs.GetId();
if (lid < rid) {
return -1;
} else if (lid > rid) {
return 1;
} else {
return 0;
}
}
friend bool operator<(const KAutoObjectWithList& left, const KAutoObjectWithList& right) {
return &left < &right;
}
public:
virtual u64 GetId() const {
return reinterpret_cast<u64>(this);
}
virtual const std::string& GetName() const {
return name;
}
private:
friend class KAutoObjectWithListContainer;
};
template <typename T>
class KScopedAutoObject {
public:
YUZU_NON_COPYABLE(KScopedAutoObject);
constexpr KScopedAutoObject() = default;
constexpr KScopedAutoObject(T* o) : m_obj(o) {
if (m_obj != nullptr) {
m_obj->Open();
}
}
~KScopedAutoObject() {
if (m_obj != nullptr) {
m_obj->Close();
}
m_obj = nullptr;
}
template <typename U>
requires(std::derived_from<T, U> ||
std::derived_from<U, T>) constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
if constexpr (std::derived_from<U, T>) {
// Upcast.
m_obj = rhs.m_obj;
rhs.m_obj = nullptr;
} else {
// Downcast.
T* derived = nullptr;
if (rhs.m_obj != nullptr) {
derived = rhs.m_obj->template DynamicCast<T*>();
if (derived == nullptr) {
rhs.m_obj->Close();
}
}
m_obj = derived;
rhs.m_obj = nullptr;
}
}
constexpr KScopedAutoObject<T>& operator=(KScopedAutoObject<T>&& rhs) {
rhs.Swap(*this);
return *this;
}
constexpr T* operator->() {
return m_obj;
}
constexpr T& operator*() {
return *m_obj;
}
constexpr void Reset(T* o) {
KScopedAutoObject(o).Swap(*this);
}
constexpr T* GetPointerUnsafe() {
return m_obj;
}
constexpr T* GetPointerUnsafe() const {
return m_obj;
}
constexpr T* ReleasePointerUnsafe() {
T* ret = m_obj;
m_obj = nullptr;
return ret;
}
constexpr bool IsNull() const {
return m_obj == nullptr;
}
constexpr bool IsNotNull() const {
return m_obj != nullptr;
}
private:
template <typename U>
friend class KScopedAutoObject;
private:
T* m_obj{};
private:
constexpr void Swap(KScopedAutoObject& rhs) noexcept {
std::swap(m_obj, rhs.m_obj);
}
};
} // namespace Kernel

View File

@@ -1,29 +1,29 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include "core/hle/kernel/k_auto_object_container.h"
namespace Kernel {
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
KScopedLightLock lk(m_lock);
m_object_list.insert_unique(*obj);
}
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
KScopedLightLock lk(m_lock);
m_object_list.erase(*obj);
}
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
KScopedLightLock lk(m_lock);
return std::count_if(m_object_list.begin(), m_object_list.end(),
[&](const auto& obj) { return obj.GetOwner() == owner; });
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include "core/hle/kernel/k_auto_object_container.h"
namespace Kernel {
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
KScopedLightLock lk(m_lock);
m_object_list.insert_unique(*obj);
}
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
KScopedLightLock lk(m_lock);
m_object_list.erase(*obj);
}
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
KScopedLightLock lk(m_lock);
return std::count_if(m_object_list.begin(), m_object_list.end(),
[&](const auto& obj) { return obj.GetOwner() == owner; });
}
} // namespace Kernel

View File

@@ -1,63 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <boost/intrusive/rbtree.hpp>
#include "common/common_funcs.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h"
namespace Kernel {
class KernelCore;
class KProcess;
class KAutoObjectWithListContainer {
public:
YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
class ListAccessor : public KScopedLightLock {
public:
explicit ListAccessor(KAutoObjectWithListContainer* container)
: KScopedLightLock(container->m_lock), m_list(container->m_object_list) {}
explicit ListAccessor(KAutoObjectWithListContainer& container)
: KScopedLightLock(container.m_lock), m_list(container.m_object_list) {}
typename ListType::iterator begin() const {
return m_list.begin();
}
typename ListType::iterator end() const {
return m_list.end();
}
typename ListType::iterator find(typename ListType::const_reference ref) const {
return m_list.find(ref);
}
private:
ListType& m_list;
};
friend class ListAccessor;
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
void Initialize() {}
void Finalize() {}
void Register(KAutoObjectWithList* obj);
void Unregister(KAutoObjectWithList* obj);
size_t GetOwnedCount(KProcess* owner);
private:
KLightLock m_lock;
ListType m_object_list;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <boost/intrusive/rbtree.hpp>
#include "common/common_funcs.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h"
namespace Kernel {
class KernelCore;
class KProcess;
class KAutoObjectWithListContainer {
public:
YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
class ListAccessor : public KScopedLightLock {
public:
explicit ListAccessor(KAutoObjectWithListContainer* container)
: KScopedLightLock(container->m_lock), m_list(container->m_object_list) {}
explicit ListAccessor(KAutoObjectWithListContainer& container)
: KScopedLightLock(container.m_lock), m_list(container.m_object_list) {}
typename ListType::iterator begin() const {
return m_list.begin();
}
typename ListType::iterator end() const {
return m_list.end();
}
typename ListType::iterator find(typename ListType::const_reference ref) const {
return m_list.find(ref);
}
private:
ListType& m_list;
};
friend class ListAccessor;
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
void Initialize() {}
void Finalize() {}
void Register(KAutoObjectWithList* obj);
void Unregister(KAutoObjectWithList* obj);
size_t GetOwnedCount(KProcess* owner);
private:
KLightLock m_lock;
ListType m_object_list;
};
} // namespace Kernel

View File

@@ -1,122 +1,122 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_class_token.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.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_transfer_memory.h"
namespace Kernel {
// Ensure that we generate correct class tokens for all types.
// Ensure that the absolute token values are correct.
static_assert(ClassToken<KAutoObject> == 0b00000000'00000000);
static_assert(ClassToken<KSynchronizationObject> == 0b00000000'00000001);
static_assert(ClassToken<KReadableEvent> == 0b00000000'00000011);
// static_assert(ClassToken<KInterruptEvent> == 0b00000111'00000011);
// static_assert(ClassToken<KDebug> == 0b00001011'00000001);
static_assert(ClassToken<KThread> == 0b00010011'00000001);
static_assert(ClassToken<KServerPort> == 0b00100011'00000001);
static_assert(ClassToken<KServerSession> == 0b01000011'00000001);
static_assert(ClassToken<KClientPort> == 0b10000011'00000001);
static_assert(ClassToken<KClientSession> == 0b00001101'00000000);
static_assert(ClassToken<KProcess> == 0b00010101'00000001);
static_assert(ClassToken<KResourceLimit> == 0b00100101'00000000);
// static_assert(ClassToken<KLightSession> == 0b01000101'00000000);
static_assert(ClassToken<KPort> == 0b10000101'00000000);
static_assert(ClassToken<KSession> == 0b00011001'00000000);
static_assert(ClassToken<KSharedMemory> == 0b00101001'00000000);
static_assert(ClassToken<KEvent> == 0b01001001'00000000);
// static_assert(ClassToken<KLightClientSession> == 0b00110001'00000000);
// static_assert(ClassToken<KLightServerSession> == 0b01010001'00000000);
static_assert(ClassToken<KTransferMemory> == 0b01010001'00000000);
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
static_assert(ClassToken<KCodeMemory> == 0b10100001'00000000);
// Ensure that the token hierarchy is correct.
// Base classes
static_assert(ClassToken<KAutoObject> == (0b00000000));
static_assert(ClassToken<KSynchronizationObject> == (0b00000001 | ClassToken<KAutoObject>));
static_assert(ClassToken<KReadableEvent> == (0b00000010 | ClassToken<KSynchronizationObject>));
// Final classes
// static_assert(ClassToken<KInterruptEvent> == ((0b00000111 << 8) | ClassToken<KReadableEvent>));
// static_assert(ClassToken<KDebug> == ((0b00001011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KThread> == ((0b00010011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KServerPort> == ((0b00100011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KServerSession> ==
((0b01000011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KClientPort> == ((0b10000011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KClientSession> == ((0b00001101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KProcess> == ((0b00010101 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KResourceLimit> == ((0b00100101 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightSession> == ((0b01000101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KPort> == ((0b10000101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KSession> == ((0b00011001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KSharedMemory> == ((0b00101001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KEvent> == ((0b01001001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightClientSession> == ((0b00110001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightServerSession> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KTransferMemory> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KCodeMemory> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
// Ensure that the token hierarchy reflects the class hierarchy.
// Base classes.
static_assert(!std::is_final_v<KSynchronizationObject> &&
std::is_base_of_v<KAutoObject, KSynchronizationObject>);
static_assert(!std::is_final_v<KReadableEvent> &&
std::is_base_of_v<KSynchronizationObject, KReadableEvent>);
// Final classes
// static_assert(std::is_final_v<KInterruptEvent> &&
// std::is_base_of_v<KReadableEvent, KInterruptEvent>);
// static_assert(std::is_final_v<KDebug> &&
// std::is_base_of_v<KSynchronizationObject, KDebug>);
static_assert(std::is_final_v<KThread> && std::is_base_of_v<KSynchronizationObject, KThread>);
static_assert(std::is_final_v<KServerPort> &&
std::is_base_of_v<KSynchronizationObject, KServerPort>);
static_assert(std::is_final_v<KServerSession> &&
std::is_base_of_v<KSynchronizationObject, KServerSession>);
static_assert(std::is_final_v<KClientPort> &&
std::is_base_of_v<KSynchronizationObject, KClientPort>);
static_assert(std::is_final_v<KClientSession> && std::is_base_of_v<KAutoObject, KClientSession>);
static_assert(std::is_final_v<KProcess> && std::is_base_of_v<KSynchronizationObject, KProcess>);
static_assert(std::is_final_v<KResourceLimit> && std::is_base_of_v<KAutoObject, KResourceLimit>);
// static_assert(std::is_final_v<KLightSession> &&
// std::is_base_of_v<KAutoObject, KLightSession>);
static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>);
static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>);
static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>);
static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>);
// static_assert(std::is_final_v<KLightClientSession> &&
// std::is_base_of_v<KAutoObject, KLightClientSession>);
// static_assert(std::is_final_v<KLightServerSession> &&
// std::is_base_of_v<KAutoObject, KLightServerSession>);
static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject, KTransferMemory>);
// static_assert(std::is_final_v<KDeviceAddressSpace> &&
// std::is_base_of_v<KAutoObject, KDeviceAddressSpace>);
// static_assert(std::is_final_v<KSessionRequest> &&
// std::is_base_of_v<KAutoObject, KSessionRequest>);
// static_assert(std::is_final_v<KCodeMemory> &&
// std::is_base_of_v<KAutoObject, KCodeMemory>);
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_class_token.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.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_transfer_memory.h"
namespace Kernel {
// Ensure that we generate correct class tokens for all types.
// Ensure that the absolute token values are correct.
static_assert(ClassToken<KAutoObject> == 0b00000000'00000000);
static_assert(ClassToken<KSynchronizationObject> == 0b00000000'00000001);
static_assert(ClassToken<KReadableEvent> == 0b00000000'00000011);
// static_assert(ClassToken<KInterruptEvent> == 0b00000111'00000011);
// static_assert(ClassToken<KDebug> == 0b00001011'00000001);
static_assert(ClassToken<KThread> == 0b00010011'00000001);
static_assert(ClassToken<KServerPort> == 0b00100011'00000001);
static_assert(ClassToken<KServerSession> == 0b01000011'00000001);
static_assert(ClassToken<KClientPort> == 0b10000011'00000001);
static_assert(ClassToken<KClientSession> == 0b00001101'00000000);
static_assert(ClassToken<KProcess> == 0b00010101'00000001);
static_assert(ClassToken<KResourceLimit> == 0b00100101'00000000);
// static_assert(ClassToken<KLightSession> == 0b01000101'00000000);
static_assert(ClassToken<KPort> == 0b10000101'00000000);
static_assert(ClassToken<KSession> == 0b00011001'00000000);
static_assert(ClassToken<KSharedMemory> == 0b00101001'00000000);
static_assert(ClassToken<KEvent> == 0b01001001'00000000);
// static_assert(ClassToken<KLightClientSession> == 0b00110001'00000000);
// static_assert(ClassToken<KLightServerSession> == 0b01010001'00000000);
static_assert(ClassToken<KTransferMemory> == 0b01010001'00000000);
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
static_assert(ClassToken<KCodeMemory> == 0b10100001'00000000);
// Ensure that the token hierarchy is correct.
// Base classes
static_assert(ClassToken<KAutoObject> == (0b00000000));
static_assert(ClassToken<KSynchronizationObject> == (0b00000001 | ClassToken<KAutoObject>));
static_assert(ClassToken<KReadableEvent> == (0b00000010 | ClassToken<KSynchronizationObject>));
// Final classes
// static_assert(ClassToken<KInterruptEvent> == ((0b00000111 << 8) | ClassToken<KReadableEvent>));
// static_assert(ClassToken<KDebug> == ((0b00001011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KThread> == ((0b00010011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KServerPort> == ((0b00100011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KServerSession> ==
((0b01000011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KClientPort> == ((0b10000011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KClientSession> == ((0b00001101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KProcess> == ((0b00010101 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KResourceLimit> == ((0b00100101 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightSession> == ((0b01000101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KPort> == ((0b10000101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KSession> == ((0b00011001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KSharedMemory> == ((0b00101001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KEvent> == ((0b01001001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightClientSession> == ((0b00110001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightServerSession> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KTransferMemory> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KCodeMemory> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
// Ensure that the token hierarchy reflects the class hierarchy.
// Base classes.
static_assert(!std::is_final_v<KSynchronizationObject> &&
std::is_base_of_v<KAutoObject, KSynchronizationObject>);
static_assert(!std::is_final_v<KReadableEvent> &&
std::is_base_of_v<KSynchronizationObject, KReadableEvent>);
// Final classes
// static_assert(std::is_final_v<KInterruptEvent> &&
// std::is_base_of_v<KReadableEvent, KInterruptEvent>);
// static_assert(std::is_final_v<KDebug> &&
// std::is_base_of_v<KSynchronizationObject, KDebug>);
static_assert(std::is_final_v<KThread> && std::is_base_of_v<KSynchronizationObject, KThread>);
static_assert(std::is_final_v<KServerPort> &&
std::is_base_of_v<KSynchronizationObject, KServerPort>);
static_assert(std::is_final_v<KServerSession> &&
std::is_base_of_v<KSynchronizationObject, KServerSession>);
static_assert(std::is_final_v<KClientPort> &&
std::is_base_of_v<KSynchronizationObject, KClientPort>);
static_assert(std::is_final_v<KClientSession> && std::is_base_of_v<KAutoObject, KClientSession>);
static_assert(std::is_final_v<KProcess> && std::is_base_of_v<KSynchronizationObject, KProcess>);
static_assert(std::is_final_v<KResourceLimit> && std::is_base_of_v<KAutoObject, KResourceLimit>);
// static_assert(std::is_final_v<KLightSession> &&
// std::is_base_of_v<KAutoObject, KLightSession>);
static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>);
static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>);
static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>);
static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>);
// static_assert(std::is_final_v<KLightClientSession> &&
// std::is_base_of_v<KAutoObject, KLightClientSession>);
// static_assert(std::is_final_v<KLightServerSession> &&
// std::is_base_of_v<KAutoObject, KLightServerSession>);
static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject, KTransferMemory>);
// static_assert(std::is_final_v<KDeviceAddressSpace> &&
// std::is_base_of_v<KAutoObject, KDeviceAddressSpace>);
// static_assert(std::is_final_v<KSessionRequest> &&
// std::is_base_of_v<KAutoObject, KSessionRequest>);
// static_assert(std::is_final_v<KCodeMemory> &&
// std::is_base_of_v<KAutoObject, KCodeMemory>);
} // namespace Kernel

View File

@@ -1,127 +1,127 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Kernel {
class KAutoObject;
class KClassTokenGenerator {
public:
using TokenBaseType = u16;
public:
static constexpr size_t BaseClassBits = 8;
static constexpr size_t FinalClassBits = (sizeof(TokenBaseType) * CHAR_BIT) - BaseClassBits;
// One bit per base class.
static constexpr size_t NumBaseClasses = BaseClassBits;
// Final classes are permutations of three bits.
static constexpr size_t NumFinalClasses = [] {
TokenBaseType index = 0;
for (size_t i = 0; i < FinalClassBits; i++) {
for (size_t j = i + 1; j < FinalClassBits; j++) {
for (size_t k = j + 1; k < FinalClassBits; k++) {
index++;
}
}
}
return index;
}();
private:
template <TokenBaseType Index>
static constexpr inline TokenBaseType BaseClassToken = 1U << Index;
template <TokenBaseType Index>
static constexpr inline TokenBaseType FinalClassToken = [] {
TokenBaseType index = 0;
for (size_t i = 0; i < FinalClassBits; i++) {
for (size_t j = i + 1; j < FinalClassBits; j++) {
for (size_t k = j + 1; k < FinalClassBits; k++) {
if ((index++) == Index) {
return static_cast<TokenBaseType>(((1ULL << i) | (1ULL << j) | (1ULL << k))
<< BaseClassBits);
}
}
}
}
UNREACHABLE();
}();
template <typename T>
static constexpr inline TokenBaseType GetClassToken() {
static_assert(std::is_base_of<KAutoObject, T>::value);
if constexpr (std::is_same<T, KAutoObject>::value) {
static_assert(T::ObjectType == ObjectType::KAutoObject);
return 0;
} else if constexpr (!std::is_final<T>::value) {
static_assert(ObjectType::BaseClassesStart <= T::ObjectType &&
T::ObjectType < ObjectType::BaseClassesEnd);
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
static_cast<TokenBaseType>(ObjectType::BaseClassesStart);
return BaseClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
} else if constexpr (ObjectType::FinalClassesStart <= T::ObjectType &&
T::ObjectType < ObjectType::FinalClassesEnd) {
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
static_cast<TokenBaseType>(ObjectType::FinalClassesStart);
return FinalClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
} else {
static_assert(!std::is_same<T, T>::value, "GetClassToken: Invalid Type");
}
};
public:
enum class ObjectType {
KAutoObject,
BaseClassesStart,
KSynchronizationObject = BaseClassesStart,
KReadableEvent,
BaseClassesEnd,
FinalClassesStart = BaseClassesEnd,
KInterruptEvent = FinalClassesStart,
KDebug,
KThread,
KServerPort,
KServerSession,
KClientPort,
KClientSession,
KProcess,
KResourceLimit,
KLightSession,
KPort,
KSession,
KSharedMemory,
KEvent,
KLightClientSession,
KLightServerSession,
KTransferMemory,
KDeviceAddressSpace,
KSessionRequest,
KCodeMemory,
// NOTE: True order for these has not been determined yet.
KAlpha,
KBeta,
FinalClassesEnd = FinalClassesStart + NumFinalClasses,
};
template <typename T>
static constexpr inline TokenBaseType ClassToken = GetClassToken<T>();
};
using ClassTokenType = KClassTokenGenerator::TokenBaseType;
template <typename T>
static constexpr inline ClassTokenType ClassToken = KClassTokenGenerator::ClassToken<T>;
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Kernel {
class KAutoObject;
class KClassTokenGenerator {
public:
using TokenBaseType = u16;
public:
static constexpr size_t BaseClassBits = 8;
static constexpr size_t FinalClassBits = (sizeof(TokenBaseType) * CHAR_BIT) - BaseClassBits;
// One bit per base class.
static constexpr size_t NumBaseClasses = BaseClassBits;
// Final classes are permutations of three bits.
static constexpr size_t NumFinalClasses = [] {
TokenBaseType index = 0;
for (size_t i = 0; i < FinalClassBits; i++) {
for (size_t j = i + 1; j < FinalClassBits; j++) {
for (size_t k = j + 1; k < FinalClassBits; k++) {
index++;
}
}
}
return index;
}();
private:
template <TokenBaseType Index>
static constexpr inline TokenBaseType BaseClassToken = 1U << Index;
template <TokenBaseType Index>
static constexpr inline TokenBaseType FinalClassToken = [] {
TokenBaseType index = 0;
for (size_t i = 0; i < FinalClassBits; i++) {
for (size_t j = i + 1; j < FinalClassBits; j++) {
for (size_t k = j + 1; k < FinalClassBits; k++) {
if ((index++) == Index) {
return static_cast<TokenBaseType>(((1ULL << i) | (1ULL << j) | (1ULL << k))
<< BaseClassBits);
}
}
}
}
UNREACHABLE();
}();
template <typename T>
static constexpr inline TokenBaseType GetClassToken() {
static_assert(std::is_base_of<KAutoObject, T>::value);
if constexpr (std::is_same<T, KAutoObject>::value) {
static_assert(T::ObjectType == ObjectType::KAutoObject);
return 0;
} else if constexpr (!std::is_final<T>::value) {
static_assert(ObjectType::BaseClassesStart <= T::ObjectType &&
T::ObjectType < ObjectType::BaseClassesEnd);
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
static_cast<TokenBaseType>(ObjectType::BaseClassesStart);
return BaseClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
} else if constexpr (ObjectType::FinalClassesStart <= T::ObjectType &&
T::ObjectType < ObjectType::FinalClassesEnd) {
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
static_cast<TokenBaseType>(ObjectType::FinalClassesStart);
return FinalClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
} else {
static_assert(!std::is_same<T, T>::value, "GetClassToken: Invalid Type");
}
};
public:
enum class ObjectType {
KAutoObject,
BaseClassesStart,
KSynchronizationObject = BaseClassesStart,
KReadableEvent,
BaseClassesEnd,
FinalClassesStart = BaseClassesEnd,
KInterruptEvent = FinalClassesStart,
KDebug,
KThread,
KServerPort,
KServerSession,
KClientPort,
KClientSession,
KProcess,
KResourceLimit,
KLightSession,
KPort,
KSession,
KSharedMemory,
KEvent,
KLightClientSession,
KLightServerSession,
KTransferMemory,
KDeviceAddressSpace,
KSessionRequest,
KCodeMemory,
// NOTE: True order for these has not been determined yet.
KAlpha,
KBeta,
FinalClassesEnd = FinalClassesStart + NumFinalClasses,
};
template <typename T>
static constexpr inline TokenBaseType ClassToken = GetClassToken<T>();
};
using ClassTokenType = KClassTokenGenerator::TokenBaseType;
template <typename T>
static constexpr inline ClassTokenType ClassToken = KClassTokenGenerator::ClassToken<T>;
} // namespace Kernel

View File

@@ -1,127 +1,127 @@
// SPDX-FileCopyrightText: 2021 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
KClientPort::KClientPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KClientPort::~KClientPort() = default;
void KClientPort::Initialize(KPort* parent_port_, s32 max_sessions_, std::string&& name_) {
// Set member variables.
num_sessions = 0;
peak_sessions = 0;
parent = parent_port_;
max_sessions = max_sessions_;
name = std::move(name_);
}
void KClientPort::OnSessionFinalized() {
KScopedSchedulerLock sl{kernel};
// This might happen if a session was improperly used with this port.
ASSERT_MSG(num_sessions > 0, "num_sessions is invalid");
const auto prev = num_sessions--;
if (prev == max_sessions) {
this->NotifyAvailable();
}
}
void KClientPort::OnServerClosed() {}
bool KClientPort::IsLight() const {
return this->GetParent()->IsLight();
}
bool KClientPort::IsServerClosed() const {
return this->GetParent()->IsServerClosed();
}
void KClientPort::Destroy() {
// Note with our parent that we're closed.
parent->OnClientClosed();
// Close our reference to our parent.
parent->Close();
}
bool KClientPort::IsSignaled() const {
return num_sessions < max_sessions;
}
Result KClientPort::CreateSession(KClientSession** out) {
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
LimitableResource::Sessions);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Update the session counts.
{
// Atomically increment the number of sessions.
s32 new_sessions{};
{
const auto max = max_sessions;
auto cur_sessions = num_sessions.load(std::memory_order_acquire);
do {
R_UNLESS(cur_sessions < max, ResultOutOfSessions);
new_sessions = cur_sessions + 1;
} while (!num_sessions.compare_exchange_weak(cur_sessions, new_sessions,
std::memory_order_relaxed));
}
// Atomically update the peak session tracking.
{
auto peak = peak_sessions.load(std::memory_order_acquire);
do {
if (peak >= new_sessions) {
break;
}
} while (!peak_sessions.compare_exchange_weak(peak, new_sessions,
std::memory_order_relaxed));
}
}
// Create a new session.
KSession* session = KSession::Create(kernel);
if (session == nullptr) {
// Decrement the session count.
const auto prev = num_sessions--;
if (prev == max_sessions) {
this->NotifyAvailable();
}
return ResultOutOfResource;
}
// Initialize the session.
session->Initialize(this, parent->GetName());
// Commit the session reservation.
session_reservation.Commit();
// Register the session.
KSession::Register(kernel, session);
auto session_guard = SCOPE_GUARD({
session->GetClientSession().Close();
session->GetServerSession().Close();
});
// Enqueue the session with our parent.
R_TRY(parent->EnqueueSession(std::addressof(session->GetServerSession())));
// We succeeded, so set the output.
session_guard.Cancel();
*out = std::addressof(session->GetClientSession());
return ResultSuccess;
}
} // namespace Kernel
// SPDX-FileCopyrightText: 2021 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
KClientPort::KClientPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KClientPort::~KClientPort() = default;
void KClientPort::Initialize(KPort* parent_port_, s32 max_sessions_, std::string&& name_) {
// Set member variables.
num_sessions = 0;
peak_sessions = 0;
parent = parent_port_;
max_sessions = max_sessions_;
name = std::move(name_);
}
void KClientPort::OnSessionFinalized() {
KScopedSchedulerLock sl{kernel};
// This might happen if a session was improperly used with this port.
ASSERT_MSG(num_sessions > 0, "num_sessions is invalid");
const auto prev = num_sessions--;
if (prev == max_sessions) {
this->NotifyAvailable();
}
}
void KClientPort::OnServerClosed() {}
bool KClientPort::IsLight() const {
return this->GetParent()->IsLight();
}
bool KClientPort::IsServerClosed() const {
return this->GetParent()->IsServerClosed();
}
void KClientPort::Destroy() {
// Note with our parent that we're closed.
parent->OnClientClosed();
// Close our reference to our parent.
parent->Close();
}
bool KClientPort::IsSignaled() const {
return num_sessions < max_sessions;
}
Result KClientPort::CreateSession(KClientSession** out) {
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
LimitableResource::Sessions);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Update the session counts.
{
// Atomically increment the number of sessions.
s32 new_sessions{};
{
const auto max = max_sessions;
auto cur_sessions = num_sessions.load(std::memory_order_acquire);
do {
R_UNLESS(cur_sessions < max, ResultOutOfSessions);
new_sessions = cur_sessions + 1;
} while (!num_sessions.compare_exchange_weak(cur_sessions, new_sessions,
std::memory_order_relaxed));
}
// Atomically update the peak session tracking.
{
auto peak = peak_sessions.load(std::memory_order_acquire);
do {
if (peak >= new_sessions) {
break;
}
} while (!peak_sessions.compare_exchange_weak(peak, new_sessions,
std::memory_order_relaxed));
}
}
// Create a new session.
KSession* session = KSession::Create(kernel);
if (session == nullptr) {
// Decrement the session count.
const auto prev = num_sessions--;
if (prev == max_sessions) {
this->NotifyAvailable();
}
return ResultOutOfResource;
}
// Initialize the session.
session->Initialize(this, parent->GetName());
// Commit the session reservation.
session_reservation.Commit();
// Register the session.
KSession::Register(kernel, session);
auto session_guard = SCOPE_GUARD({
session->GetClientSession().Close();
session->GetServerSession().Close();
});
// Enqueue the session with our parent.
R_TRY(parent->EnqueueSession(std::addressof(session->GetServerSession())));
// We succeeded, so set the output.
session_guard.Cancel();
*out = std::addressof(session->GetClientSession());
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -1,64 +1,64 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/result.h"
namespace Kernel {
class KClientSession;
class KernelCore;
class KPort;
class SessionRequestManager;
class KClientPort final : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
public:
explicit KClientPort(KernelCore& kernel_);
~KClientPort() override;
void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_);
void OnSessionFinalized();
void OnServerClosed();
const KPort* GetParent() const {
return parent;
}
KPort* GetParent() {
return parent;
}
s32 GetNumSessions() const {
return num_sessions;
}
s32 GetPeakSessions() const {
return peak_sessions;
}
s32 GetMaxSessions() const {
return max_sessions;
}
bool IsLight() const;
bool IsServerClosed() const;
// Overridden virtual functions.
void Destroy() override;
bool IsSignaled() const override;
Result CreateSession(KClientSession** out);
private:
std::atomic<s32> num_sessions{};
std::atomic<s32> peak_sessions{};
s32 max_sessions{};
KPort* parent{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/result.h"
namespace Kernel {
class KClientSession;
class KernelCore;
class KPort;
class SessionRequestManager;
class KClientPort final : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
public:
explicit KClientPort(KernelCore& kernel_);
~KClientPort() override;
void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_);
void OnSessionFinalized();
void OnServerClosed();
const KPort* GetParent() const {
return parent;
}
KPort* GetParent() {
return parent;
}
s32 GetNumSessions() const {
return num_sessions;
}
s32 GetPeakSessions() const {
return peak_sessions;
}
s32 GetMaxSessions() const {
return max_sessions;
}
bool IsLight() const;
bool IsServerClosed() const;
// Overridden virtual functions.
void Destroy() override;
bool IsSignaled() const override;
Result CreateSession(KClientSession** out);
private:
std::atomic<s32> num_sessions{};
std::atomic<s32> peak_sessions{};
s32 max_sessions{};
KPort* parent{};
};
} // namespace Kernel

View File

@@ -1,40 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/result.h"
namespace Kernel {
static constexpr u32 MessageBufferSize = 0x100;
KClientSession::KClientSession(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_} {}
KClientSession::~KClientSession() = default;
void KClientSession::Destroy() {
parent->OnClientClosed();
parent->Close();
}
void KClientSession::OnServerClosed() {}
Result KClientSession::SendSyncRequest() {
// Create a session request.
KSessionRequest* request = KSessionRequest::Create(kernel);
R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); });
// Initialize the request.
request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize);
// Send the request.
return parent->GetServerSession().OnRequest(request);
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/result.h"
namespace Kernel {
static constexpr u32 MessageBufferSize = 0x100;
KClientSession::KClientSession(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_} {}
KClientSession::~KClientSession() = default;
void KClientSession::Destroy() {
parent->OnClientClosed();
parent->Close();
}
void KClientSession::OnServerClosed() {}
Result KClientSession::SendSyncRequest() {
// Create a session request.
KSessionRequest* request = KSessionRequest::Create(kernel);
R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); });
// Initialize the request.
request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize);
// Send the request.
return parent->GetServerSession().OnRequest(request);
}
} // namespace Kernel

View File

@@ -1,57 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
union Result;
namespace Core::Memory {
class Memory;
}
namespace Core::Timing {
class CoreTiming;
}
namespace Kernel {
class KernelCore;
class KSession;
class KThread;
class KClientSession final
: public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
public:
explicit KClientSession(KernelCore& kernel_);
~KClientSession() override;
void Initialize(KSession* parent_session_, std::string&& name_) {
// Set member variables.
parent = parent_session_;
name = std::move(name_);
}
void Destroy() override;
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
KSession* GetParent() const {
return parent;
}
Result SendSyncRequest();
void OnServerClosed();
private:
KSession* parent{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
union Result;
namespace Core::Memory {
class Memory;
}
namespace Core::Timing {
class CoreTiming;
}
namespace Kernel {
class KernelCore;
class KSession;
class KThread;
class KClientSession final
: public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
public:
explicit KClientSession(KernelCore& kernel_);
~KClientSession() override;
void Initialize(KSession* parent_session_, std::string&& name_) {
// Set member variables.
parent = parent_session_;
name = std::move(name_);
}
void Destroy() override;
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
KSession* GetParent() const {
return parent;
}
Result SendSyncRequest();
void OnServerClosed();
private:
KSession* parent{};
};
} // namespace Kernel

View File

@@ -1,152 +1,152 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
KCodeMemory::KCodeMemory(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
// Set members.
m_owner = kernel.CurrentProcess();
// Get the owner page table.
auto& page_table = m_owner->PageTable();
// Construct the page group.
m_page_group = {};
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
// Clear the memory.
for (const auto& block : m_page_group.Nodes()) {
std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize());
}
// Set remaining tracking members.
m_owner->Open();
m_address = addr;
m_is_initialized = true;
m_is_owner_mapped = false;
m_is_mapped = false;
// We succeeded.
return ResultSuccess;
}
void KCodeMemory::Finalize() {
// Unlock.
if (!m_is_mapped && !m_is_owner_mapped) {
const size_t size = m_page_group.GetNumPages() * PageSize;
m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
}
// Close the page group.
m_page_group = {};
// Close our reference to our owner.
m_owner->Close();
}
Result KCodeMemory::Map(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Ensure we're not already mapped.
R_UNLESS(!m_is_mapped, ResultInvalidState);
// Map the memory.
R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
// Mark ourselves as mapped.
m_is_mapped = true;
return ResultSuccess;
}
Result KCodeMemory::Unmap(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
KMemoryState::CodeOut));
// Mark ourselves as unmapped.
m_is_mapped = false;
return ResultSuccess;
}
Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Ensure we're not already mapped.
R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
// Convert the memory permission.
KMemoryPermission k_perm{};
switch (perm) {
case Svc::MemoryPermission::Read:
k_perm = KMemoryPermission::UserRead;
break;
case Svc::MemoryPermission::ReadExecute:
k_perm = KMemoryPermission::UserReadExecute;
break;
default:
// Already validated by ControlCodeMemory svc
UNREACHABLE();
}
// Map the memory.
R_TRY(
m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
// Mark ourselves as mapped.
m_is_owner_mapped = true;
return ResultSuccess;
}
Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
// Mark ourselves as unmapped.
m_is_owner_mapped = false;
return ResultSuccess;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
KCodeMemory::KCodeMemory(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
// Set members.
m_owner = kernel.CurrentProcess();
// Get the owner page table.
auto& page_table = m_owner->PageTable();
// Construct the page group.
m_page_group = {};
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
// Clear the memory.
for (const auto& block : m_page_group.Nodes()) {
std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize());
}
// Set remaining tracking members.
m_owner->Open();
m_address = addr;
m_is_initialized = true;
m_is_owner_mapped = false;
m_is_mapped = false;
// We succeeded.
return ResultSuccess;
}
void KCodeMemory::Finalize() {
// Unlock.
if (!m_is_mapped && !m_is_owner_mapped) {
const size_t size = m_page_group.GetNumPages() * PageSize;
m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
}
// Close the page group.
m_page_group = {};
// Close our reference to our owner.
m_owner->Close();
}
Result KCodeMemory::Map(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Ensure we're not already mapped.
R_UNLESS(!m_is_mapped, ResultInvalidState);
// Map the memory.
R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
// Mark ourselves as mapped.
m_is_mapped = true;
return ResultSuccess;
}
Result KCodeMemory::Unmap(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
KMemoryState::CodeOut));
// Mark ourselves as unmapped.
m_is_mapped = false;
return ResultSuccess;
}
Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Ensure we're not already mapped.
R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
// Convert the memory permission.
KMemoryPermission k_perm{};
switch (perm) {
case Svc::MemoryPermission::Read:
k_perm = KMemoryPermission::UserRead;
break;
case Svc::MemoryPermission::ReadExecute:
k_perm = KMemoryPermission::UserReadExecute;
break;
default:
// Already validated by ControlCodeMemory svc
UNREACHABLE();
}
// Map the memory.
R_TRY(
m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
// Mark ourselves as mapped.
m_is_owner_mapped = true;
return ResultSuccess;
}
Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
// Mark ourselves as unmapped.
m_is_owner_mapped = false;
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -1,65 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
enum class CodeMemoryOperation : u32 {
Map = 0,
MapToOwner = 1,
Unmap = 2,
UnmapFromOwner = 3,
};
class KCodeMemory final
: public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
public:
explicit KCodeMemory(KernelCore& kernel_);
Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
void Finalize() override;
Result Map(VAddr address, size_t size);
Result Unmap(VAddr address, size_t size);
Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
Result UnmapFromOwner(VAddr address, size_t size);
bool IsInitialized() const override {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
KProcess* GetOwner() const override {
return m_owner;
}
VAddr GetSourceAddress() const {
return m_address;
}
size_t GetSize() const {
return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
}
private:
KPageGroup m_page_group{};
KProcess* m_owner{};
VAddr m_address{};
KLightLock m_lock;
bool m_is_initialized{};
bool m_is_owner_mapped{};
bool m_is_mapped{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
enum class CodeMemoryOperation : u32 {
Map = 0,
MapToOwner = 1,
Unmap = 2,
UnmapFromOwner = 3,
};
class KCodeMemory final
: public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
public:
explicit KCodeMemory(KernelCore& kernel_);
Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
void Finalize() override;
Result Map(VAddr address, size_t size);
Result Unmap(VAddr address, size_t size);
Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
Result UnmapFromOwner(VAddr address, size_t size);
bool IsInitialized() const override {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
KProcess* GetOwner() const override {
return m_owner;
}
VAddr GetSourceAddress() const {
return m_address;
}
size_t GetSize() const {
return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
}
private:
KPageGroup m_page_group{};
KProcess* m_owner{};
VAddr m_address{};
KLightLock m_lock;
bool m_is_initialized{};
bool m_is_owner_mapped{};
bool m_is_mapped{};
};
} // namespace Kernel

View File

@@ -1,328 +1,328 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_linked_list.h"
#include "core/hle/kernel/k_process.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/svc_common.h"
#include "core/hle/kernel/svc_results.h"
#include "core/memory.h"
namespace Kernel {
namespace {
bool ReadFromUser(Core::System& system, u32* out, VAddr address) {
*out = system.Memory().Read32(address);
return true;
}
bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
system.Memory().Write32(address, *p);
return true;
}
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
u32 new_orr_mask) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// Load the value from the address.
const auto expected = monitor.ExclusiveRead32(current_core, address);
// Orr in the new mask.
u32 value = expected | new_orr_mask;
// If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
if (!expected) {
value = if_zero;
}
// Try to store.
if (!monitor.ExclusiveWrite32(current_core, address, value)) {
// If we failed to store, try again.
return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask);
}
// We're done.
*out = expected;
return true;
}
class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
public:
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
: KThreadQueue(kernel_) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
private:
KConditionVariable::ThreadTree* m_tree;
public:
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
KernelCore& kernel_, KConditionVariable::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// If the thread is waiting on a condvar, remove it from the tree.
if (waiting_thread->IsWaitingForConditionVariable()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearConditionVariable();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
KConditionVariable::KConditionVariable(Core::System& system_)
: system{system_}, kernel{system.Kernel()} {}
KConditionVariable::~KConditionVariable() = default;
Result KConditionVariable::SignalToAddress(VAddr addr) {
KThread* owner_thread = GetCurrentThreadPointer(kernel);
// Signal the address.
{
KScopedSchedulerLock sl(kernel);
// Remove waiter thread.
s32 num_waiters{};
KThread* next_owner_thread =
owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Determine the next tag.
u32 next_value{};
if (next_owner_thread != nullptr) {
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
// Write the value to userspace.
Result result{ResultSuccess};
if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
result = ResultInvalidCurrentMemory;
}
// Signal the next owner thread.
next_owner_thread->EndWait(result);
return result;
} else {
// Just write the value to userspace.
R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
ResultInvalidCurrentMemory);
return ResultSuccess;
}
}
}
Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
KThread* owner_thread{};
{
KScopedSchedulerLock sl(kernel);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()
->GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Close our reference to the owner thread, now that the wait is over.
owner_thread->Close();
// Get the wait result.
return cur_thread->GetWaitResult();
}
void KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Update the tag.
VAddr address = thread->GetAddressKey();
u32 own_tag = thread->GetAddressKeyValue();
u32 prev_tag{};
bool can_access{};
{
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) [[likely]] {
UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
if (can_access) [[likely]] {
if (prev_tag == Svc::InvalidHandle) {
// If nobody held the lock previously, we're all good.
thread->EndWait(ResultSuccess);
} else {
// Get the previous owner.
KThread* owner_thread = kernel.CurrentProcess()
->GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
if (owner_thread) [[likely]] {
// Add the thread as a waiter on the owner.
owner_thread->AddWaiter(thread);
owner_thread->Close();
} else {
// The lock was tagged with a thread that doesn't exist.
thread->EndWait(ResultInvalidState);
}
}
} else {
// If the address wasn't accessible, note so.
thread->EndWait(ResultInvalidCurrentMemory);
}
}
void KConditionVariable::Signal(u64 cv_key, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_key({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
this->SignalImpl(target_thread);
it = thread_tree.erase(it);
target_thread->ClearConditionVariable();
++num_waiters;
}
// If we have no waiters, clear the has waiter flag.
if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) {
const u32 has_waiter_flag{};
WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
}
}
}
Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Update the value and process for the next owner.
{
// Remove waiter thread.
s32 num_waiters{};
KThread* next_owner_thread =
cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Update for the next owner thread.
u32 next_value{};
if (next_owner_thread != nullptr) {
// Get the next tag value.
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
// Wake up the next owner.
next_owner_thread->EndWait(ResultSuccess);
}
// Write to the cv key.
{
const u32 has_waiter_flag = 1;
WriteToUser(system, key, std::addressof(has_waiter_flag));
// TODO(bunnei): We should call DataMemoryBarrier(..) here.
}
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
slp.CancelSleep();
return ResultInvalidCurrentMemory;
}
}
// If timeout is zero, time out.
R_UNLESS(timeout != 0, ResultTimedOut);
// Update condition variable tracking.
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Get the wait result.
return cur_thread->GetWaitResult();
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_linked_list.h"
#include "core/hle/kernel/k_process.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/svc_common.h"
#include "core/hle/kernel/svc_results.h"
#include "core/memory.h"
namespace Kernel {
namespace {
bool ReadFromUser(Core::System& system, u32* out, VAddr address) {
*out = system.Memory().Read32(address);
return true;
}
bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
system.Memory().Write32(address, *p);
return true;
}
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
u32 new_orr_mask) {
auto& monitor = system.Monitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// Load the value from the address.
const auto expected = monitor.ExclusiveRead32(current_core, address);
// Orr in the new mask.
u32 value = expected | new_orr_mask;
// If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
if (!expected) {
value = if_zero;
}
// Try to store.
if (!monitor.ExclusiveWrite32(current_core, address, value)) {
// If we failed to store, try again.
return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask);
}
// We're done.
*out = expected;
return true;
}
class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
public:
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
: KThreadQueue(kernel_) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
private:
KConditionVariable::ThreadTree* m_tree;
public:
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
KernelCore& kernel_, KConditionVariable::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// If the thread is waiting on a condvar, remove it from the tree.
if (waiting_thread->IsWaitingForConditionVariable()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearConditionVariable();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
KConditionVariable::KConditionVariable(Core::System& system_)
: system{system_}, kernel{system.Kernel()} {}
KConditionVariable::~KConditionVariable() = default;
Result KConditionVariable::SignalToAddress(VAddr addr) {
KThread* owner_thread = GetCurrentThreadPointer(kernel);
// Signal the address.
{
KScopedSchedulerLock sl(kernel);
// Remove waiter thread.
s32 num_waiters{};
KThread* next_owner_thread =
owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Determine the next tag.
u32 next_value{};
if (next_owner_thread != nullptr) {
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
// Write the value to userspace.
Result result{ResultSuccess};
if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
result = ResultInvalidCurrentMemory;
}
// Signal the next owner thread.
next_owner_thread->EndWait(result);
return result;
} else {
// Just write the value to userspace.
R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
ResultInvalidCurrentMemory);
return ResultSuccess;
}
}
}
Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
KThread* owner_thread{};
{
KScopedSchedulerLock sl(kernel);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()
->GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Close our reference to the owner thread, now that the wait is over.
owner_thread->Close();
// Get the wait result.
return cur_thread->GetWaitResult();
}
void KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Update the tag.
VAddr address = thread->GetAddressKey();
u32 own_tag = thread->GetAddressKeyValue();
u32 prev_tag{};
bool can_access{};
{
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) [[likely]] {
UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
if (can_access) [[likely]] {
if (prev_tag == Svc::InvalidHandle) {
// If nobody held the lock previously, we're all good.
thread->EndWait(ResultSuccess);
} else {
// Get the previous owner.
KThread* owner_thread = kernel.CurrentProcess()
->GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
if (owner_thread) [[likely]] {
// Add the thread as a waiter on the owner.
owner_thread->AddWaiter(thread);
owner_thread->Close();
} else {
// The lock was tagged with a thread that doesn't exist.
thread->EndWait(ResultInvalidState);
}
}
} else {
// If the address wasn't accessible, note so.
thread->EndWait(ResultInvalidCurrentMemory);
}
}
void KConditionVariable::Signal(u64 cv_key, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_key({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
this->SignalImpl(target_thread);
it = thread_tree.erase(it);
target_thread->ClearConditionVariable();
++num_waiters;
}
// If we have no waiters, clear the has waiter flag.
if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) {
const u32 has_waiter_flag{};
WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
}
}
}
Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Update the value and process for the next owner.
{
// Remove waiter thread.
s32 num_waiters{};
KThread* next_owner_thread =
cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Update for the next owner thread.
u32 next_value{};
if (next_owner_thread != nullptr) {
// Get the next tag value.
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
// Wake up the next owner.
next_owner_thread->EndWait(ResultSuccess);
}
// Write to the cv key.
{
const u32 has_waiter_flag = 1;
WriteToUser(system, key, std::addressof(has_waiter_flag));
// TODO(bunnei): We should call DataMemoryBarrier(..) here.
}
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
slp.CancelSleep();
return ResultInvalidCurrentMemory;
}
}
// If timeout is zero, time out.
R_UNLESS(timeout != 0, ResultTimedOut);
// Update condition variable tracking.
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Get the wait result.
return cur_thread->GetWaitResult();
}
} // namespace Kernel

View File

@@ -1,58 +1,58 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KConditionVariable {
public:
using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
explicit KConditionVariable(Core::System& system_);
~KConditionVariable();
// Arbitration
[[nodiscard]] Result SignalToAddress(VAddr addr);
[[nodiscard]] Result WaitForAddress(Handle handle, VAddr addr, u32 value);
// Condition variable
void Signal(u64 cv_key, s32 count);
[[nodiscard]] Result Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
void SignalImpl(KThread* thread);
ThreadTree thread_tree;
Core::System& system;
KernelCore& kernel;
};
inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->erase(tree->iterator_to(*thread));
}
inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->insert(*thread);
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KConditionVariable {
public:
using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
explicit KConditionVariable(Core::System& system_);
~KConditionVariable();
// Arbitration
[[nodiscard]] Result SignalToAddress(VAddr addr);
[[nodiscard]] Result WaitForAddress(Handle handle, VAddr addr, u32 value);
// Condition variable
void Signal(u64 cv_key, s32 count);
[[nodiscard]] Result Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
void SignalImpl(KThread* thread);
ThreadTree thread_tree;
Core::System& system;
KernelCore& kernel;
};
inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->erase(tree->iterator_to(*thread));
}
inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->insert(*thread);
}
} // namespace Kernel

View File

@@ -1,136 +1,136 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_page_bitmap.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
class KDynamicPageManager {
public:
class PageBuffer {
private:
u8 m_buffer[PageSize];
};
static_assert(sizeof(PageBuffer) == PageSize);
public:
KDynamicPageManager() = default;
template <typename T>
T* GetPointer(VAddr addr) {
return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
}
template <typename T>
const T* GetPointer(VAddr addr) const {
return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
}
Result Initialize(VAddr addr, size_t sz) {
// We need to have positive size.
R_UNLESS(sz > 0, ResultOutOfMemory);
m_backing_memory.resize(sz);
// Calculate management overhead.
const size_t management_size =
KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer));
const size_t allocatable_size = sz - management_size;
// Set tracking fields.
m_address = addr;
m_size = Common::AlignDown(allocatable_size, sizeof(PageBuffer));
m_count = allocatable_size / sizeof(PageBuffer);
R_UNLESS(m_count > 0, ResultOutOfMemory);
// Clear the management region.
u64* management_ptr = GetPointer<u64>(m_address + allocatable_size);
std::memset(management_ptr, 0, management_size);
// Initialize the bitmap.
m_page_bitmap.Initialize(management_ptr, m_count);
// Free the pages to the bitmap.
for (size_t i = 0; i < m_count; i++) {
// Ensure the freed page is all-zero.
std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize);
// Set the bit for the free page.
m_page_bitmap.SetBit(i);
}
R_SUCCEED();
}
VAddr GetAddress() const {
return m_address;
}
size_t GetSize() const {
return m_size;
}
size_t GetUsed() const {
return m_used;
}
size_t GetPeak() const {
return m_peak;
}
size_t GetCount() const {
return m_count;
}
PageBuffer* Allocate() {
// Take the lock.
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
KScopedSpinLock lk(m_lock);
// Find a random free block.
s64 soffset = m_page_bitmap.FindFreeBlock(true);
if (soffset < 0) [[unlikely]] {
return nullptr;
}
const size_t offset = static_cast<size_t>(soffset);
// Update our tracking.
m_page_bitmap.ClearBit(offset);
m_peak = std::max(m_peak, (++m_used));
return GetPointer<PageBuffer>(m_address) + offset;
}
void Free(PageBuffer* pb) {
// Ensure all pages in the heap are zero.
std::memset(pb, 0, PageSize);
// Take the lock.
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
KScopedSpinLock lk(m_lock);
// Set the bit for the free page.
size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_address) / sizeof(PageBuffer);
m_page_bitmap.SetBit(offset);
// Decrement our used count.
--m_used;
}
private:
KSpinLock m_lock;
KPageBitmap m_page_bitmap;
size_t m_used{};
size_t m_peak{};
size_t m_count{};
VAddr m_address{};
size_t m_size{};
// TODO(bunnei): Back by host memory until we emulate kernel virtual address space.
std::vector<u8> m_backing_memory;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_page_bitmap.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
class KDynamicPageManager {
public:
class PageBuffer {
private:
u8 m_buffer[PageSize];
};
static_assert(sizeof(PageBuffer) == PageSize);
public:
KDynamicPageManager() = default;
template <typename T>
T* GetPointer(VAddr addr) {
return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
}
template <typename T>
const T* GetPointer(VAddr addr) const {
return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
}
Result Initialize(VAddr addr, size_t sz) {
// We need to have positive size.
R_UNLESS(sz > 0, ResultOutOfMemory);
m_backing_memory.resize(sz);
// Calculate management overhead.
const size_t management_size =
KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer));
const size_t allocatable_size = sz - management_size;
// Set tracking fields.
m_address = addr;
m_size = Common::AlignDown(allocatable_size, sizeof(PageBuffer));
m_count = allocatable_size / sizeof(PageBuffer);
R_UNLESS(m_count > 0, ResultOutOfMemory);
// Clear the management region.
u64* management_ptr = GetPointer<u64>(m_address + allocatable_size);
std::memset(management_ptr, 0, management_size);
// Initialize the bitmap.
m_page_bitmap.Initialize(management_ptr, m_count);
// Free the pages to the bitmap.
for (size_t i = 0; i < m_count; i++) {
// Ensure the freed page is all-zero.
std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize);
// Set the bit for the free page.
m_page_bitmap.SetBit(i);
}
R_SUCCEED();
}
VAddr GetAddress() const {
return m_address;
}
size_t GetSize() const {
return m_size;
}
size_t GetUsed() const {
return m_used;
}
size_t GetPeak() const {
return m_peak;
}
size_t GetCount() const {
return m_count;
}
PageBuffer* Allocate() {
// Take the lock.
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
KScopedSpinLock lk(m_lock);
// Find a random free block.
s64 soffset = m_page_bitmap.FindFreeBlock(true);
if (soffset < 0) [[unlikely]] {
return nullptr;
}
const size_t offset = static_cast<size_t>(soffset);
// Update our tracking.
m_page_bitmap.ClearBit(offset);
m_peak = std::max(m_peak, (++m_used));
return GetPointer<PageBuffer>(m_address) + offset;
}
void Free(PageBuffer* pb) {
// Ensure all pages in the heap are zero.
std::memset(pb, 0, PageSize);
// Take the lock.
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
KScopedSpinLock lk(m_lock);
// Set the bit for the free page.
size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_address) / sizeof(PageBuffer);
m_page_bitmap.SetBit(offset);
// Decrement our used count.
--m_used;
}
private:
KSpinLock m_lock;
KPageBitmap m_page_bitmap;
size_t m_used{};
size_t m_peak{};
size_t m_count{};
VAddr m_address{};
size_t m_size{};
// TODO(bunnei): Back by host memory until we emulate kernel virtual address space.
std::vector<u8> m_backing_memory;
};
} // namespace Kernel

View File

@@ -1,58 +1,58 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "core/hle/kernel/k_dynamic_slab_heap.h"
#include "core/hle/kernel/k_memory_block.h"
namespace Kernel {
template <typename T, bool ClearNode = false>
class KDynamicResourceManager {
YUZU_NON_COPYABLE(KDynamicResourceManager);
YUZU_NON_MOVEABLE(KDynamicResourceManager);
public:
using DynamicSlabType = KDynamicSlabHeap<T, ClearNode>;
public:
constexpr KDynamicResourceManager() = default;
constexpr size_t GetSize() const {
return m_slab_heap->GetSize();
}
constexpr size_t GetUsed() const {
return m_slab_heap->GetUsed();
}
constexpr size_t GetPeak() const {
return m_slab_heap->GetPeak();
}
constexpr size_t GetCount() const {
return m_slab_heap->GetCount();
}
void Initialize(KDynamicPageManager* page_allocator, DynamicSlabType* slab_heap) {
m_page_allocator = page_allocator;
m_slab_heap = slab_heap;
}
T* Allocate() const {
return m_slab_heap->Allocate(m_page_allocator);
}
void Free(T* t) const {
m_slab_heap->Free(t);
}
private:
KDynamicPageManager* m_page_allocator{};
DynamicSlabType* m_slab_heap{};
};
class KMemoryBlockSlabManager : public KDynamicResourceManager<KMemoryBlock> {};
using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType;
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "core/hle/kernel/k_dynamic_slab_heap.h"
#include "core/hle/kernel/k_memory_block.h"
namespace Kernel {
template <typename T, bool ClearNode = false>
class KDynamicResourceManager {
YUZU_NON_COPYABLE(KDynamicResourceManager);
YUZU_NON_MOVEABLE(KDynamicResourceManager);
public:
using DynamicSlabType = KDynamicSlabHeap<T, ClearNode>;
public:
constexpr KDynamicResourceManager() = default;
constexpr size_t GetSize() const {
return m_slab_heap->GetSize();
}
constexpr size_t GetUsed() const {
return m_slab_heap->GetUsed();
}
constexpr size_t GetPeak() const {
return m_slab_heap->GetPeak();
}
constexpr size_t GetCount() const {
return m_slab_heap->GetCount();
}
void Initialize(KDynamicPageManager* page_allocator, DynamicSlabType* slab_heap) {
m_page_allocator = page_allocator;
m_slab_heap = slab_heap;
}
T* Allocate() const {
return m_slab_heap->Allocate(m_page_allocator);
}
void Free(T* t) const {
m_slab_heap->Free(t);
}
private:
KDynamicPageManager* m_page_allocator{};
DynamicSlabType* m_slab_heap{};
};
class KMemoryBlockSlabManager : public KDynamicResourceManager<KMemoryBlock> {};
using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType;
} // namespace Kernel

View File

@@ -1,122 +1,122 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/common_funcs.h"
#include "core/hle/kernel/k_dynamic_page_manager.h"
#include "core/hle/kernel/k_slab_heap.h"
namespace Kernel {
template <typename T, bool ClearNode = false>
class KDynamicSlabHeap : protected impl::KSlabHeapImpl {
YUZU_NON_COPYABLE(KDynamicSlabHeap);
YUZU_NON_MOVEABLE(KDynamicSlabHeap);
public:
constexpr KDynamicSlabHeap() = default;
constexpr VAddr GetAddress() const {
return m_address;
}
constexpr size_t GetSize() const {
return m_size;
}
constexpr size_t GetUsed() const {
return m_used.load();
}
constexpr size_t GetPeak() const {
return m_peak.load();
}
constexpr size_t GetCount() const {
return m_count.load();
}
constexpr bool IsInRange(VAddr addr) const {
return this->GetAddress() <= addr && addr <= this->GetAddress() + this->GetSize() - 1;
}
void Initialize(KDynamicPageManager* page_allocator, size_t num_objects) {
ASSERT(page_allocator != nullptr);
// Initialize members.
m_address = page_allocator->GetAddress();
m_size = page_allocator->GetSize();
// Initialize the base allocator.
KSlabHeapImpl::Initialize();
// Allocate until we have the correct number of objects.
while (m_count.load() < num_objects) {
auto* allocated = reinterpret_cast<T*>(page_allocator->Allocate());
ASSERT(allocated != nullptr);
for (size_t i = 0; i < sizeof(PageBuffer) / sizeof(T); i++) {
KSlabHeapImpl::Free(allocated + i);
}
m_count += sizeof(PageBuffer) / sizeof(T);
}
}
T* Allocate(KDynamicPageManager* page_allocator) {
T* allocated = static_cast<T*>(KSlabHeapImpl::Allocate());
// If we successfully allocated and we should clear the node, do so.
if constexpr (ClearNode) {
if (allocated != nullptr) [[likely]] {
reinterpret_cast<KSlabHeapImpl::Node*>(allocated)->next = nullptr;
}
}
// If we fail to allocate, try to get a new page from our next allocator.
if (allocated == nullptr) [[unlikely]] {
if (page_allocator != nullptr) {
allocated = reinterpret_cast<T*>(page_allocator->Allocate());
if (allocated != nullptr) {
// If we succeeded in getting a page, free the rest to our slab.
for (size_t i = 1; i < sizeof(PageBuffer) / sizeof(T); i++) {
KSlabHeapImpl::Free(allocated + i);
}
m_count += sizeof(PageBuffer) / sizeof(T);
}
}
}
if (allocated != nullptr) [[likely]] {
// Construct the object.
std::construct_at(allocated);
// Update our tracking.
const size_t used = ++m_used;
size_t peak = m_peak.load();
while (peak < used) {
if (m_peak.compare_exchange_weak(peak, used, std::memory_order_relaxed)) {
break;
}
}
}
return allocated;
}
void Free(T* t) {
KSlabHeapImpl::Free(t);
--m_used;
}
private:
using PageBuffer = KDynamicPageManager::PageBuffer;
private:
std::atomic<size_t> m_used{};
std::atomic<size_t> m_peak{};
std::atomic<size_t> m_count{};
VAddr m_address{};
size_t m_size{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/common_funcs.h"
#include "core/hle/kernel/k_dynamic_page_manager.h"
#include "core/hle/kernel/k_slab_heap.h"
namespace Kernel {
template <typename T, bool ClearNode = false>
class KDynamicSlabHeap : protected impl::KSlabHeapImpl {
YUZU_NON_COPYABLE(KDynamicSlabHeap);
YUZU_NON_MOVEABLE(KDynamicSlabHeap);
public:
constexpr KDynamicSlabHeap() = default;
constexpr VAddr GetAddress() const {
return m_address;
}
constexpr size_t GetSize() const {
return m_size;
}
constexpr size_t GetUsed() const {
return m_used.load();
}
constexpr size_t GetPeak() const {
return m_peak.load();
}
constexpr size_t GetCount() const {
return m_count.load();
}
constexpr bool IsInRange(VAddr addr) const {
return this->GetAddress() <= addr && addr <= this->GetAddress() + this->GetSize() - 1;
}
void Initialize(KDynamicPageManager* page_allocator, size_t num_objects) {
ASSERT(page_allocator != nullptr);
// Initialize members.
m_address = page_allocator->GetAddress();
m_size = page_allocator->GetSize();
// Initialize the base allocator.
KSlabHeapImpl::Initialize();
// Allocate until we have the correct number of objects.
while (m_count.load() < num_objects) {
auto* allocated = reinterpret_cast<T*>(page_allocator->Allocate());
ASSERT(allocated != nullptr);
for (size_t i = 0; i < sizeof(PageBuffer) / sizeof(T); i++) {
KSlabHeapImpl::Free(allocated + i);
}
m_count += sizeof(PageBuffer) / sizeof(T);
}
}
T* Allocate(KDynamicPageManager* page_allocator) {
T* allocated = static_cast<T*>(KSlabHeapImpl::Allocate());
// If we successfully allocated and we should clear the node, do so.
if constexpr (ClearNode) {
if (allocated != nullptr) [[likely]] {
reinterpret_cast<KSlabHeapImpl::Node*>(allocated)->next = nullptr;
}
}
// If we fail to allocate, try to get a new page from our next allocator.
if (allocated == nullptr) [[unlikely]] {
if (page_allocator != nullptr) {
allocated = reinterpret_cast<T*>(page_allocator->Allocate());
if (allocated != nullptr) {
// If we succeeded in getting a page, free the rest to our slab.
for (size_t i = 1; i < sizeof(PageBuffer) / sizeof(T); i++) {
KSlabHeapImpl::Free(allocated + i);
}
m_count += sizeof(PageBuffer) / sizeof(T);
}
}
}
if (allocated != nullptr) [[likely]] {
// Construct the object.
std::construct_at(allocated);
// Update our tracking.
const size_t used = ++m_used;
size_t peak = m_peak.load();
while (peak < used) {
if (m_peak.compare_exchange_weak(peak, used, std::memory_order_relaxed)) {
break;
}
}
}
return allocated;
}
void Free(T* t) {
KSlabHeapImpl::Free(t);
--m_used;
}
private:
using PageBuffer = KDynamicPageManager::PageBuffer;
private:
std::atomic<size_t> m_used{};
std::atomic<size_t> m_peak{};
std::atomic<size_t> m_count{};
VAddr m_address{};
size_t m_size{};
};
} // namespace Kernel

View File

@@ -1,57 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
namespace Kernel {
KEvent::KEvent(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_readable_event{kernel_} {}
KEvent::~KEvent() = default;
void KEvent::Initialize(KProcess* owner) {
// Create our readable event.
KAutoObject::Create(std::addressof(m_readable_event));
// Initialize our readable event.
m_readable_event.Initialize(this);
// Set our owner process.
m_owner = owner;
m_owner->Open();
// Mark initialized.
m_initialized = true;
}
void KEvent::Finalize() {
KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList>::Finalize();
}
Result KEvent::Signal() {
KScopedSchedulerLock sl{kernel};
R_SUCCEED_IF(m_readable_event_destroyed);
return m_readable_event.Signal();
}
Result KEvent::Clear() {
KScopedSchedulerLock sl{kernel};
R_SUCCEED_IF(m_readable_event_destroyed);
return m_readable_event.Clear();
}
void KEvent::PostDestroy(uintptr_t arg) {
// Release the event count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
owner->Close();
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
namespace Kernel {
KEvent::KEvent(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_readable_event{kernel_} {}
KEvent::~KEvent() = default;
void KEvent::Initialize(KProcess* owner) {
// Create our readable event.
KAutoObject::Create(std::addressof(m_readable_event));
// Initialize our readable event.
m_readable_event.Initialize(this);
// Set our owner process.
m_owner = owner;
m_owner->Open();
// Mark initialized.
m_initialized = true;
}
void KEvent::Finalize() {
KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList>::Finalize();
}
Result KEvent::Signal() {
KScopedSchedulerLock sl{kernel};
R_SUCCEED_IF(m_readable_event_destroyed);
return m_readable_event.Signal();
}
Result KEvent::Clear() {
KScopedSchedulerLock sl{kernel};
R_SUCCEED_IF(m_readable_event_destroyed);
return m_readable_event.Clear();
}
void KEvent::PostDestroy(uintptr_t arg) {
// Release the event count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
owner->Close();
}
} // namespace Kernel

View File

@@ -1,58 +1,58 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KernelCore;
class KReadableEvent;
class KProcess;
class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KEvent, KAutoObject);
public:
explicit KEvent(KernelCore& kernel_);
~KEvent() override;
void Initialize(KProcess* owner);
void Finalize() override;
bool IsInitialized() const override {
return m_initialized;
}
uintptr_t GetPostDestroyArgument() const override {
return reinterpret_cast<uintptr_t>(m_owner);
}
KProcess* GetOwner() const override {
return m_owner;
}
KReadableEvent& GetReadableEvent() {
return m_readable_event;
}
static void PostDestroy(uintptr_t arg);
Result Signal();
Result Clear();
void OnReadableEventDestroyed() {
m_readable_event_destroyed = true;
}
private:
KReadableEvent m_readable_event;
KProcess* m_owner{};
bool m_initialized{};
bool m_readable_event_destroyed{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KernelCore;
class KReadableEvent;
class KProcess;
class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KEvent, KAutoObject);
public:
explicit KEvent(KernelCore& kernel_);
~KEvent() override;
void Initialize(KProcess* owner);
void Finalize() override;
bool IsInitialized() const override {
return m_initialized;
}
uintptr_t GetPostDestroyArgument() const override {
return reinterpret_cast<uintptr_t>(m_owner);
}
KProcess* GetOwner() const override {
return m_owner;
}
KReadableEvent& GetReadableEvent() {
return m_readable_event;
}
static void PostDestroy(uintptr_t arg);
Result Signal();
Result Clear();
void OnReadableEventDestroyed() {
m_readable_event_destroyed = true;
}
private:
KReadableEvent m_readable_event;
KProcess* m_owner{};
bool m_initialized{};
bool m_readable_event_destroyed{};
};
} // namespace Kernel

View File

@@ -1,141 +1,141 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_handle_table.h"
namespace Kernel {
KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
KHandleTable::~KHandleTable() = default;
Result KHandleTable::Finalize() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
std::swap(m_table_size, saved_table_size);
}
// Close and free all entries.
for (size_t i = 0; i < saved_table_size; i++) {
if (KAutoObject* obj = m_objects[i]; obj != nullptr) {
obj->Close();
}
}
return ResultSuccess;
}
bool KHandleTable::Remove(Handle handle) {
// Don't allow removal of a pseudo-handle.
if (Svc::IsPseudoHandle(handle)) {
return false;
}
// Handles must not have reserved bits set.
const auto handle_pack = HandlePack(handle);
if (handle_pack.reserved != 0) {
return false;
}
// Find the object and free the entry.
KAutoObject* obj = nullptr;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if (this->IsValidHandle(handle)) {
const auto index = handle_pack.index;
obj = m_objects[index];
this->FreeEntry(index);
} else {
return false;
}
}
// Close the object.
kernel.UnregisterInUseObject(obj);
obj->Close();
return true;
}
Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
// Allocate entry, set output handle.
{
const auto linear_id = this->AllocateLinearId();
const auto index = this->AllocateEntry();
m_entry_infos[index].linear_id = linear_id;
m_objects[index] = obj;
obj->Open();
*out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
}
return ResultSuccess;
}
Result KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
*out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
return ResultSuccess;
}
void KHandleTable::Unreserve(Handle handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
const auto handle_pack = HandlePack(handle);
const auto index = handle_pack.index;
const auto linear_id = handle_pack.linear_id;
const auto reserved = handle_pack.reserved;
ASSERT(reserved == 0);
ASSERT(linear_id != 0);
if (index < m_table_size) {
// NOTE: This code does not check the linear id.
ASSERT(m_objects[index] == nullptr);
this->FreeEntry(index);
}
}
void KHandleTable::Register(Handle handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
const auto handle_pack = HandlePack(handle);
const auto index = handle_pack.index;
const auto linear_id = handle_pack.linear_id;
const auto reserved = handle_pack.reserved;
ASSERT(reserved == 0);
ASSERT(linear_id != 0);
if (index < m_table_size) {
// Set the entry.
ASSERT(m_objects[index] == nullptr);
m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
m_objects[index] = obj;
obj->Open();
}
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_handle_table.h"
namespace Kernel {
KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
KHandleTable::~KHandleTable() = default;
Result KHandleTable::Finalize() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
std::swap(m_table_size, saved_table_size);
}
// Close and free all entries.
for (size_t i = 0; i < saved_table_size; i++) {
if (KAutoObject* obj = m_objects[i]; obj != nullptr) {
obj->Close();
}
}
return ResultSuccess;
}
bool KHandleTable::Remove(Handle handle) {
// Don't allow removal of a pseudo-handle.
if (Svc::IsPseudoHandle(handle)) {
return false;
}
// Handles must not have reserved bits set.
const auto handle_pack = HandlePack(handle);
if (handle_pack.reserved != 0) {
return false;
}
// Find the object and free the entry.
KAutoObject* obj = nullptr;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if (this->IsValidHandle(handle)) {
const auto index = handle_pack.index;
obj = m_objects[index];
this->FreeEntry(index);
} else {
return false;
}
}
// Close the object.
kernel.UnregisterInUseObject(obj);
obj->Close();
return true;
}
Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
// Allocate entry, set output handle.
{
const auto linear_id = this->AllocateLinearId();
const auto index = this->AllocateEntry();
m_entry_infos[index].linear_id = linear_id;
m_objects[index] = obj;
obj->Open();
*out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
}
return ResultSuccess;
}
Result KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
*out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
return ResultSuccess;
}
void KHandleTable::Unreserve(Handle handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
const auto handle_pack = HandlePack(handle);
const auto index = handle_pack.index;
const auto linear_id = handle_pack.linear_id;
const auto reserved = handle_pack.reserved;
ASSERT(reserved == 0);
ASSERT(linear_id != 0);
if (index < m_table_size) {
// NOTE: This code does not check the linear id.
ASSERT(m_objects[index] == nullptr);
this->FreeEntry(index);
}
}
void KHandleTable::Register(Handle handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
const auto handle_pack = HandlePack(handle);
const auto index = handle_pack.index;
const auto linear_id = handle_pack.linear_id;
const auto reserved = handle_pack.reserved;
ASSERT(reserved == 0);
ASSERT(linear_id != 0);
if (index < m_table_size) {
// Set the entry.
ASSERT(m_objects[index] == nullptr);
m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
m_objects[index] = obj;
obj->Open();
}
}
} // namespace Kernel

View File

@@ -1,291 +1,291 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KHandleTable {
public:
YUZU_NON_COPYABLE(KHandleTable);
YUZU_NON_MOVEABLE(KHandleTable);
static constexpr size_t MaxTableSize = 1024;
explicit KHandleTable(KernelCore& kernel_);
~KHandleTable();
Result Initialize(s32 size) {
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
// Initialize all fields.
m_max_count = 0;
m_table_size = static_cast<u16>((size <= 0) ? MaxTableSize : size);
m_next_linear_id = MinLinearId;
m_count = 0;
m_free_head_index = -1;
// Free all entries.
for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
m_objects[i] = nullptr;
m_entry_infos[i].next_free_index = i - 1;
m_free_head_index = i;
}
return ResultSuccess;
}
size_t GetTableSize() const {
return m_table_size;
}
size_t GetCount() const {
return m_count;
}
size_t GetMaxCount() const {
return m_max_count;
}
Result Finalize();
bool Remove(Handle handle);
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if constexpr (std::is_same_v<T, KAutoObject>) {
return this->GetObjectImpl(handle);
} else {
if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) {
return obj->DynamicCast<T*>();
} else {
return nullptr;
}
}
}
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObject(Handle handle) const {
// Handle pseudo-handles.
if constexpr (std::derived_from<KProcess, T>) {
if (handle == Svc::PseudoHandle::CurrentProcess) {
auto* const cur_process = kernel.CurrentProcess();
ASSERT(cur_process != nullptr);
return cur_process;
}
} else if constexpr (std::derived_from<KThread, T>) {
if (handle == Svc::PseudoHandle::CurrentThread) {
auto* const cur_thread = GetCurrentThreadPointer(kernel);
ASSERT(cur_thread != nullptr);
return cur_thread;
}
}
return this->template GetObjectWithoutPseudoHandle<T>(handle);
}
Result Reserve(Handle* out_handle);
void Unreserve(Handle handle);
Result Add(Handle* out_handle, KAutoObject* obj);
void Register(Handle handle, KAutoObject* obj);
template <typename T>
bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
// Try to convert and open all the handles.
size_t num_opened;
{
// Lock the table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
// Get the current handle.
const auto cur_handle = handles[num_opened];
// Get the object for the current handle.
KAutoObject* cur_object = this->GetObjectImpl(cur_handle);
if (cur_object == nullptr) {
break;
}
// Cast the current object to the desired type.
T* cur_t = cur_object->DynamicCast<T*>();
if (cur_t == nullptr) {
break;
}
// Open a reference to the current object.
cur_t->Open();
out[num_opened] = cur_t;
}
}
// If we converted every object, succeed.
if (num_opened == num_handles) {
return true;
}
// If we didn't convert entry object, close the ones we opened.
for (size_t i = 0; i < num_opened; i++) {
out[i]->Close();
}
return false;
}
private:
s32 AllocateEntry() {
ASSERT(m_count < m_table_size);
const auto index = m_free_head_index;
m_free_head_index = m_entry_infos[index].GetNextFreeIndex();
m_max_count = std::max(m_max_count, ++m_count);
return index;
}
void FreeEntry(s32 index) {
ASSERT(m_count > 0);
m_objects[index] = nullptr;
m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
m_free_head_index = index;
--m_count;
}
u16 AllocateLinearId() {
const u16 id = m_next_linear_id++;
if (m_next_linear_id > MaxLinearId) {
m_next_linear_id = MinLinearId;
}
return id;
}
bool IsValidHandle(Handle handle) const {
// Unpack the handle.
const auto handle_pack = HandlePack(handle);
const auto raw_value = handle_pack.raw;
const auto index = handle_pack.index;
const auto linear_id = handle_pack.linear_id;
const auto reserved = handle_pack.reserved;
ASSERT(reserved == 0);
// Validate our indexing information.
if (raw_value == 0) {
return false;
}
if (linear_id == 0) {
return false;
}
if (index >= m_table_size) {
return false;
}
// Check that there's an object, and our serial id is correct.
if (m_objects[index] == nullptr) {
return false;
}
if (m_entry_infos[index].GetLinearId() != linear_id) {
return false;
}
return true;
}
KAutoObject* GetObjectImpl(Handle handle) const {
// Handles must not have reserved bits set.
const auto handle_pack = HandlePack(handle);
if (handle_pack.reserved != 0) {
return nullptr;
}
if (this->IsValidHandle(handle)) {
return m_objects[handle_pack.index];
} else {
return nullptr;
}
}
KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const {
// Index must be in bounds.
if (index >= m_table_size) {
return nullptr;
}
// Ensure entry has an object.
if (KAutoObject* obj = m_objects[index]; obj != nullptr) {
*out_handle = EncodeHandle(static_cast<u16>(index), m_entry_infos[index].GetLinearId());
return obj;
} else {
return nullptr;
}
}
private:
union HandlePack {
HandlePack() = default;
HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
u32 raw;
BitField<0, 15, u32> index;
BitField<15, 15, u32> linear_id;
BitField<30, 2, u32> reserved;
};
static constexpr u16 MinLinearId = 1;
static constexpr u16 MaxLinearId = 0x7FFF;
static constexpr Handle EncodeHandle(u16 index, u16 linear_id) {
HandlePack handle{};
handle.index.Assign(index);
handle.linear_id.Assign(linear_id);
handle.reserved.Assign(0);
return handle.raw;
}
union EntryInfo {
u16 linear_id;
s16 next_free_index;
constexpr u16 GetLinearId() const {
return linear_id;
}
constexpr s16 GetNextFreeIndex() const {
return next_free_index;
}
};
private:
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
std::array<KAutoObject*, MaxTableSize> m_objects{};
s32 m_free_head_index{-1};
u16 m_table_size{};
u16 m_max_count{};
u16 m_next_linear_id{MinLinearId};
u16 m_count{};
mutable KSpinLock m_lock;
KernelCore& kernel;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KHandleTable {
public:
YUZU_NON_COPYABLE(KHandleTable);
YUZU_NON_MOVEABLE(KHandleTable);
static constexpr size_t MaxTableSize = 1024;
explicit KHandleTable(KernelCore& kernel_);
~KHandleTable();
Result Initialize(s32 size) {
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
// Initialize all fields.
m_max_count = 0;
m_table_size = static_cast<u16>((size <= 0) ? MaxTableSize : size);
m_next_linear_id = MinLinearId;
m_count = 0;
m_free_head_index = -1;
// Free all entries.
for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
m_objects[i] = nullptr;
m_entry_infos[i].next_free_index = i - 1;
m_free_head_index = i;
}
return ResultSuccess;
}
size_t GetTableSize() const {
return m_table_size;
}
size_t GetCount() const {
return m_count;
}
size_t GetMaxCount() const {
return m_max_count;
}
Result Finalize();
bool Remove(Handle handle);
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if constexpr (std::is_same_v<T, KAutoObject>) {
return this->GetObjectImpl(handle);
} else {
if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) {
return obj->DynamicCast<T*>();
} else {
return nullptr;
}
}
}
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObject(Handle handle) const {
// Handle pseudo-handles.
if constexpr (std::derived_from<KProcess, T>) {
if (handle == Svc::PseudoHandle::CurrentProcess) {
auto* const cur_process = kernel.CurrentProcess();
ASSERT(cur_process != nullptr);
return cur_process;
}
} else if constexpr (std::derived_from<KThread, T>) {
if (handle == Svc::PseudoHandle::CurrentThread) {
auto* const cur_thread = GetCurrentThreadPointer(kernel);
ASSERT(cur_thread != nullptr);
return cur_thread;
}
}
return this->template GetObjectWithoutPseudoHandle<T>(handle);
}
Result Reserve(Handle* out_handle);
void Unreserve(Handle handle);
Result Add(Handle* out_handle, KAutoObject* obj);
void Register(Handle handle, KAutoObject* obj);
template <typename T>
bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
// Try to convert and open all the handles.
size_t num_opened;
{
// Lock the table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
// Get the current handle.
const auto cur_handle = handles[num_opened];
// Get the object for the current handle.
KAutoObject* cur_object = this->GetObjectImpl(cur_handle);
if (cur_object == nullptr) {
break;
}
// Cast the current object to the desired type.
T* cur_t = cur_object->DynamicCast<T*>();
if (cur_t == nullptr) {
break;
}
// Open a reference to the current object.
cur_t->Open();
out[num_opened] = cur_t;
}
}
// If we converted every object, succeed.
if (num_opened == num_handles) {
return true;
}
// If we didn't convert entry object, close the ones we opened.
for (size_t i = 0; i < num_opened; i++) {
out[i]->Close();
}
return false;
}
private:
s32 AllocateEntry() {
ASSERT(m_count < m_table_size);
const auto index = m_free_head_index;
m_free_head_index = m_entry_infos[index].GetNextFreeIndex();
m_max_count = std::max(m_max_count, ++m_count);
return index;
}
void FreeEntry(s32 index) {
ASSERT(m_count > 0);
m_objects[index] = nullptr;
m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
m_free_head_index = index;
--m_count;
}
u16 AllocateLinearId() {
const u16 id = m_next_linear_id++;
if (m_next_linear_id > MaxLinearId) {
m_next_linear_id = MinLinearId;
}
return id;
}
bool IsValidHandle(Handle handle) const {
// Unpack the handle.
const auto handle_pack = HandlePack(handle);
const auto raw_value = handle_pack.raw;
const auto index = handle_pack.index;
const auto linear_id = handle_pack.linear_id;
const auto reserved = handle_pack.reserved;
ASSERT(reserved == 0);
// Validate our indexing information.
if (raw_value == 0) {
return false;
}
if (linear_id == 0) {
return false;
}
if (index >= m_table_size) {
return false;
}
// Check that there's an object, and our serial id is correct.
if (m_objects[index] == nullptr) {
return false;
}
if (m_entry_infos[index].GetLinearId() != linear_id) {
return false;
}
return true;
}
KAutoObject* GetObjectImpl(Handle handle) const {
// Handles must not have reserved bits set.
const auto handle_pack = HandlePack(handle);
if (handle_pack.reserved != 0) {
return nullptr;
}
if (this->IsValidHandle(handle)) {
return m_objects[handle_pack.index];
} else {
return nullptr;
}
}
KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const {
// Index must be in bounds.
if (index >= m_table_size) {
return nullptr;
}
// Ensure entry has an object.
if (KAutoObject* obj = m_objects[index]; obj != nullptr) {
*out_handle = EncodeHandle(static_cast<u16>(index), m_entry_infos[index].GetLinearId());
return obj;
} else {
return nullptr;
}
}
private:
union HandlePack {
HandlePack() = default;
HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
u32 raw;
BitField<0, 15, u32> index;
BitField<15, 15, u32> linear_id;
BitField<30, 2, u32> reserved;
};
static constexpr u16 MinLinearId = 1;
static constexpr u16 MaxLinearId = 0x7FFF;
static constexpr Handle EncodeHandle(u16 index, u16 linear_id) {
HandlePack handle{};
handle.index.Assign(index);
handle.linear_id.Assign(linear_id);
handle.reserved.Assign(0);
return handle.raw;
}
union EntryInfo {
u16 linear_id;
s16 next_free_index;
constexpr u16 GetLinearId() const {
return linear_id;
}
constexpr s16 GetNextFreeIndex() const {
return next_free_index;
}
};
private:
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
std::array<KAutoObject*, MaxTableSize> m_objects{};
s32 m_free_head_index{-1};
u16 m_table_size{};
u16 m_max_count{};
u16 m_next_linear_id{MinLinearId};
u16 m_count{};
mutable KSpinLock m_lock;
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -1,44 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel::KInterruptManager {
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
// Acknowledge the interrupt.
kernel.PhysicalCore(core_id).ClearInterrupt();
auto& current_thread = GetCurrentThread(kernel);
if (auto* process = kernel.CurrentProcess(); process) {
// If the user disable count is set, we may need to pin the current thread.
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
KScopedSchedulerLock sl{kernel};
// Pin the current thread.
process->PinCurrentThread(core_id);
// Set the interrupt flag for the thread.
GetCurrentThread(kernel).SetInterruptFlag();
}
}
// Request interrupt scheduling.
kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
}
void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask) {
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
if (core_mask & (1ULL << core_id)) {
kernel.PhysicalCore(core_id).Interrupt();
}
}
}
} // namespace Kernel::KInterruptManager
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel::KInterruptManager {
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
// Acknowledge the interrupt.
kernel.PhysicalCore(core_id).ClearInterrupt();
auto& current_thread = GetCurrentThread(kernel);
if (auto* process = kernel.CurrentProcess(); process) {
// If the user disable count is set, we may need to pin the current thread.
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
KScopedSchedulerLock sl{kernel};
// Pin the current thread.
process->PinCurrentThread(core_id);
// Set the interrupt flag for the thread.
GetCurrentThread(kernel).SetInterruptFlag();
}
}
// Request interrupt scheduling.
kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
}
void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask) {
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
if (core_mask & (1ULL << core_id)) {
kernel.PhysicalCore(core_id).Interrupt();
}
}
}
} // namespace Kernel::KInterruptManager

View File

@@ -1,18 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel {
class KernelCore;
namespace KInterruptManager {
void HandleInterrupt(KernelCore& kernel, s32 core_id);
void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask);
} // namespace KInterruptManager
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel {
class KernelCore;
namespace KInterruptManager {
void HandleInterrupt(KernelCore& kernel, s32 core_id);
void SendInterProcessorInterrupt(KernelCore& kernel, u64 core_mask);
} // namespace KInterruptManager
} // namespace Kernel

View File

@@ -1,78 +1,78 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_light_condition_variable.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_queue.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
public:
ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl,
bool term)
: KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Only process waits if we're allowed to.
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
return;
}
// Remove the thread from the waiting thread from the light condition variable.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KThread::WaiterList* m_wait_list;
bool m_allow_terminating_thread;
};
} // namespace
void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
// Create thread queue.
KThread* owner = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
allow_terminating_thread);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Add the thread to the queue.
wait_list.push_back(*owner);
// Begin waiting.
owner->BeginWait(std::addressof(wait_queue));
}
// Re-acquire the lock.
lock->Lock();
}
void KLightConditionVariable::Broadcast() {
KScopedSchedulerLock lk(kernel);
// Signal all threads.
for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
it->EndWait(ResultSuccess);
}
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_light_condition_variable.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_queue.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
public:
ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl,
bool term)
: KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Only process waits if we're allowed to.
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
return;
}
// Remove the thread from the waiting thread from the light condition variable.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KThread::WaiterList* m_wait_list;
bool m_allow_terminating_thread;
};
} // namespace
void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
// Create thread queue.
KThread* owner = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
allow_terminating_thread);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Add the thread to the queue.
wait_list.push_back(*owner);
// Begin waiting.
owner->BeginWait(std::addressof(wait_queue));
}
// Re-acquire the lock.
lock->Lock();
}
void KLightConditionVariable::Broadcast() {
KScopedSchedulerLock lk(kernel);
// Signal all threads.
for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
it->EndWait(ResultSuccess);
}
}
} // namespace Kernel

View File

@@ -1,25 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
class KernelCore;
class KLightLock;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
void Broadcast();
private:
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
class KernelCore;
class KLightLock;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
void Broadcast();
private:
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
} // namespace Kernel

View File

@@ -1,125 +1,125 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightLock final : public KThreadQueue {
public:
explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
void KLightLock::Lock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
while (true) {
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
std::memory_order_acquire)) {
}
if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
break;
}
}
}
void KLightLock::Unlock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
uintptr_t expected = cur_thread;
if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
this->UnlockSlowPath(cur_thread);
}
}
bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
ThreadQueueImplForKLightLock wait_queue(kernel);
// Pend the current thread waiting on the owner thread.
{
KScopedSchedulerLock sl{kernel};
// Ensure we actually have locking to do.
if (tag.load(std::memory_order_relaxed) != _owner) {
return false;
}
// Add the current thread as a waiter on the owner.
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
// Begin waiting to hold the lock.
cur_thread->BeginWait(std::addressof(wait_queue));
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
}
}
return true;
}
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
// Unlock.
{
KScopedSchedulerLock sl(kernel);
// Get the next owner.
s32 num_waiters;
KThread* next_owner = owner_thread->RemoveWaiterByKey(
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner != nullptr) {
next_tag =
reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
next_owner->EndWait(ResultSuccess);
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
}
}
// We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
// so.
if (owner_thread->IsSuspended()) {
owner_thread->TrySuspend();
}
// Write the new tag value.
tag.store(next_tag, std::memory_order_release);
}
}
bool KLightLock::IsLockedByCurrentThread() const {
return (tag | 1ULL) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 1ULL);
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightLock final : public KThreadQueue {
public:
explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
void KLightLock::Lock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
while (true) {
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
std::memory_order_acquire)) {
}
if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
break;
}
}
}
void KLightLock::Unlock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
uintptr_t expected = cur_thread;
if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
this->UnlockSlowPath(cur_thread);
}
}
bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
ThreadQueueImplForKLightLock wait_queue(kernel);
// Pend the current thread waiting on the owner thread.
{
KScopedSchedulerLock sl{kernel};
// Ensure we actually have locking to do.
if (tag.load(std::memory_order_relaxed) != _owner) {
return false;
}
// Add the current thread as a waiter on the owner.
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
// Begin waiting to hold the lock.
cur_thread->BeginWait(std::addressof(wait_queue));
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
}
}
return true;
}
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
// Unlock.
{
KScopedSchedulerLock sl(kernel);
// Get the next owner.
s32 num_waiters;
KThread* next_owner = owner_thread->RemoveWaiterByKey(
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner != nullptr) {
next_tag =
reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
next_owner->EndWait(ResultSuccess);
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
}
}
// We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
// so.
if (owner_thread->IsSuspended()) {
owner_thread->TrySuspend();
}
// Write the new tag value.
tag.store(next_tag, std::memory_order_release);
}
}
bool KLightLock::IsLockedByCurrentThread() const {
return (tag | 1ULL) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 1ULL);
}
} // namespace Kernel

View File

@@ -1,39 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "core/hle/kernel/k_scoped_lock.h"
namespace Kernel {
class KernelCore;
class KLightLock {
public:
explicit KLightLock(KernelCore& kernel_) : kernel{kernel_} {}
void Lock();
void Unlock();
bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
void UnlockSlowPath(uintptr_t cur_thread);
bool IsLocked() const {
return tag != 0;
}
bool IsLockedByCurrentThread() const;
private:
std::atomic<uintptr_t> tag{};
KernelCore& kernel;
};
using KScopedLightLock = KScopedLock<KLightLock>;
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "core/hle/kernel/k_scoped_lock.h"
namespace Kernel {
class KernelCore;
class KLightLock {
public:
explicit KLightLock(KernelCore& kernel_) : kernel{kernel_} {}
void Lock();
void Unlock();
bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
void UnlockSlowPath(uintptr_t cur_thread);
bool IsLocked() const {
return tag != 0;
}
bool IsLockedByCurrentThread() const;
private:
std::atomic<uintptr_t> tag{};
KernelCore& kernel;
};
using KScopedLightLock = KScopedLock<KLightLock>;
} // namespace Kernel

View File

@@ -1,238 +1,238 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <boost/intrusive/list.hpp>
#include "common/assert.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KernelCore;
class KLinkedListNode : public boost::intrusive::list_base_hook<>,
public KSlabAllocated<KLinkedListNode> {
public:
explicit KLinkedListNode(KernelCore&) {}
KLinkedListNode() = default;
void Initialize(void* it) {
m_item = it;
}
void* GetItem() const {
return m_item;
}
private:
void* m_item = nullptr;
};
template <typename T>
class KLinkedList : private boost::intrusive::list<KLinkedListNode> {
private:
using BaseList = boost::intrusive::list<KLinkedListNode>;
public:
template <bool Const>
class Iterator;
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
template <bool Const>
class Iterator {
private:
using BaseIterator = BaseList::iterator;
friend class KLinkedList;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename KLinkedList::value_type;
using difference_type = typename KLinkedList::difference_type;
using pointer = std::conditional_t<Const, KLinkedList::const_pointer, KLinkedList::pointer>;
using reference =
std::conditional_t<Const, KLinkedList::const_reference, KLinkedList::reference>;
public:
explicit Iterator(BaseIterator it) : m_base_it(it) {}
pointer GetItem() const {
return static_cast<pointer>(m_base_it->GetItem());
}
bool operator==(const Iterator& rhs) const {
return m_base_it == rhs.m_base_it;
}
bool operator!=(const Iterator& rhs) const {
return !(*this == rhs);
}
pointer operator->() const {
return this->GetItem();
}
reference operator*() const {
return *this->GetItem();
}
Iterator& operator++() {
++m_base_it;
return *this;
}
Iterator& operator--() {
--m_base_it;
return *this;
}
Iterator operator++(int) {
const Iterator it{*this};
++(*this);
return it;
}
Iterator operator--(int) {
const Iterator it{*this};
--(*this);
return it;
}
operator Iterator<true>() const {
return Iterator<true>(m_base_it);
}
private:
BaseIterator m_base_it;
};
public:
constexpr KLinkedList(KernelCore& kernel_) : BaseList(), kernel{kernel_} {}
~KLinkedList() {
// Erase all elements.
for (auto it = begin(); it != end(); it = erase(it)) {
}
// Ensure we succeeded.
ASSERT(this->empty());
}
// Iterator accessors.
iterator begin() {
return iterator(BaseList::begin());
}
const_iterator begin() const {
return const_iterator(BaseList::begin());
}
iterator end() {
return iterator(BaseList::end());
}
const_iterator end() const {
return const_iterator(BaseList::end());
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
reverse_iterator rbegin() {
return reverse_iterator(this->end());
}
const_reverse_iterator rbegin() const {
return const_reverse_iterator(this->end());
}
reverse_iterator rend() {
return reverse_iterator(this->begin());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(this->begin());
}
const_reverse_iterator crbegin() const {
return this->rbegin();
}
const_reverse_iterator crend() const {
return this->rend();
}
// Content management.
using BaseList::empty;
using BaseList::size;
reference back() {
return *(--this->end());
}
const_reference back() const {
return *(--this->end());
}
reference front() {
return *this->begin();
}
const_reference front() const {
return *this->begin();
}
iterator insert(const_iterator pos, reference ref) {
KLinkedListNode* new_node = KLinkedListNode::Allocate(kernel);
ASSERT(new_node != nullptr);
new_node->Initialize(std::addressof(ref));
return iterator(BaseList::insert(pos.m_base_it, *new_node));
}
void push_back(reference ref) {
this->insert(this->end(), ref);
}
void push_front(reference ref) {
this->insert(this->begin(), ref);
}
void pop_back() {
this->erase(--this->end());
}
void pop_front() {
this->erase(this->begin());
}
iterator erase(const iterator pos) {
KLinkedListNode* freed_node = std::addressof(*pos.m_base_it);
iterator ret = iterator(BaseList::erase(pos.m_base_it));
KLinkedListNode::Free(kernel, freed_node);
return ret;
}
private:
KernelCore& kernel;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <boost/intrusive/list.hpp>
#include "common/assert.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KernelCore;
class KLinkedListNode : public boost::intrusive::list_base_hook<>,
public KSlabAllocated<KLinkedListNode> {
public:
explicit KLinkedListNode(KernelCore&) {}
KLinkedListNode() = default;
void Initialize(void* it) {
m_item = it;
}
void* GetItem() const {
return m_item;
}
private:
void* m_item = nullptr;
};
template <typename T>
class KLinkedList : private boost::intrusive::list<KLinkedListNode> {
private:
using BaseList = boost::intrusive::list<KLinkedListNode>;
public:
template <bool Const>
class Iterator;
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
template <bool Const>
class Iterator {
private:
using BaseIterator = BaseList::iterator;
friend class KLinkedList;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename KLinkedList::value_type;
using difference_type = typename KLinkedList::difference_type;
using pointer = std::conditional_t<Const, KLinkedList::const_pointer, KLinkedList::pointer>;
using reference =
std::conditional_t<Const, KLinkedList::const_reference, KLinkedList::reference>;
public:
explicit Iterator(BaseIterator it) : m_base_it(it) {}
pointer GetItem() const {
return static_cast<pointer>(m_base_it->GetItem());
}
bool operator==(const Iterator& rhs) const {
return m_base_it == rhs.m_base_it;
}
bool operator!=(const Iterator& rhs) const {
return !(*this == rhs);
}
pointer operator->() const {
return this->GetItem();
}
reference operator*() const {
return *this->GetItem();
}
Iterator& operator++() {
++m_base_it;
return *this;
}
Iterator& operator--() {
--m_base_it;
return *this;
}
Iterator operator++(int) {
const Iterator it{*this};
++(*this);
return it;
}
Iterator operator--(int) {
const Iterator it{*this};
--(*this);
return it;
}
operator Iterator<true>() const {
return Iterator<true>(m_base_it);
}
private:
BaseIterator m_base_it;
};
public:
constexpr KLinkedList(KernelCore& kernel_) : BaseList(), kernel{kernel_} {}
~KLinkedList() {
// Erase all elements.
for (auto it = begin(); it != end(); it = erase(it)) {
}
// Ensure we succeeded.
ASSERT(this->empty());
}
// Iterator accessors.
iterator begin() {
return iterator(BaseList::begin());
}
const_iterator begin() const {
return const_iterator(BaseList::begin());
}
iterator end() {
return iterator(BaseList::end());
}
const_iterator end() const {
return const_iterator(BaseList::end());
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
reverse_iterator rbegin() {
return reverse_iterator(this->end());
}
const_reverse_iterator rbegin() const {
return const_reverse_iterator(this->end());
}
reverse_iterator rend() {
return reverse_iterator(this->begin());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(this->begin());
}
const_reverse_iterator crbegin() const {
return this->rbegin();
}
const_reverse_iterator crend() const {
return this->rend();
}
// Content management.
using BaseList::empty;
using BaseList::size;
reference back() {
return *(--this->end());
}
const_reference back() const {
return *(--this->end());
}
reference front() {
return *this->begin();
}
const_reference front() const {
return *this->begin();
}
iterator insert(const_iterator pos, reference ref) {
KLinkedListNode* new_node = KLinkedListNode::Allocate(kernel);
ASSERT(new_node != nullptr);
new_node->Initialize(std::addressof(ref));
return iterator(BaseList::insert(pos.m_base_it, *new_node));
}
void push_back(reference ref) {
this->insert(this->end(), ref);
}
void push_front(reference ref) {
this->insert(this->begin(), ref);
}
void pop_back() {
this->erase(--this->end());
}
void pop_front() {
this->erase(this->begin());
}
iterator erase(const iterator pos) {
KLinkedListNode* freed_node = std::addressof(*pos.m_base_it);
iterator ret = iterator(BaseList::erase(pos.m_base_it));
KLinkedListNode::Free(kernel, freed_node);
return ret;
}
private:
KernelCore& kernel;
};
} // namespace Kernel

File diff suppressed because it is too large Load Diff

View File

@@ -1,337 +1,337 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_memory_block_manager.h"
namespace Kernel {
KMemoryBlockManager::KMemoryBlockManager() = default;
Result KMemoryBlockManager::Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager) {
// Allocate a block to encapsulate the address space, insert it into the tree.
KMemoryBlock* start_block = slab_manager->Allocate();
R_UNLESS(start_block != nullptr, ResultOutOfResource);
// Set our start and end.
m_start_address = st;
m_end_address = nd;
ASSERT(Common::IsAligned(m_start_address, PageSize));
ASSERT(Common::IsAligned(m_end_address, PageSize));
// Initialize and insert the block.
start_block->Initialize(m_start_address, (m_end_address - m_start_address) / PageSize,
KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None);
m_memory_block_tree.insert(*start_block);
R_SUCCEED();
}
void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
HostUnmapCallback&& host_unmap_callback) {
// Erase every block until we have none left.
auto it = m_memory_block_tree.begin();
while (it != m_memory_block_tree.end()) {
KMemoryBlock* block = std::addressof(*it);
it = m_memory_block_tree.erase(it);
slab_manager->Free(block);
host_unmap_callback(block->GetAddress(), block->GetSize());
}
ASSERT(m_memory_block_tree.empty());
}
VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, size_t region_num_pages,
size_t num_pages, size_t alignment, size_t offset,
size_t guard_pages) const {
if (num_pages > 0) {
const VAddr region_end = region_start + region_num_pages * PageSize;
const VAddr region_last = region_end - 1;
for (const_iterator it = this->FindIterator(region_start); it != m_memory_block_tree.cend();
it++) {
const KMemoryInfo info = it->GetMemoryInfo();
if (region_last < info.GetAddress()) {
break;
}
if (info.m_state != KMemoryState::Free) {
continue;
}
VAddr area = (info.GetAddress() <= region_start) ? region_start : info.GetAddress();
area += guard_pages * PageSize;
const VAddr offset_area = Common::AlignDown(area, alignment) + offset;
area = (area <= offset_area) ? offset_area : offset_area + alignment;
const VAddr area_end = area + num_pages * PageSize + guard_pages * PageSize;
const VAddr area_last = area_end - 1;
if (info.GetAddress() <= area && area < area_last && area_last <= region_last &&
area_last <= info.GetLastAddress()) {
return area;
}
}
}
return {};
}
void KMemoryBlockManager::CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator,
VAddr address, size_t num_pages) {
// Find the iterator now that we've updated.
iterator it = this->FindIterator(address);
if (address != m_start_address) {
it--;
}
// Coalesce blocks that we can.
while (true) {
iterator prev = it++;
if (it == m_memory_block_tree.end()) {
break;
}
if (prev->CanMergeWith(*it)) {
KMemoryBlock* block = std::addressof(*it);
m_memory_block_tree.erase(it);
prev->Add(*block);
allocator->Free(block);
it = prev;
}
if (address + num_pages * PageSize < it->GetMemoryInfo().GetEndAddress()) {
break;
}
}
}
void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
size_t num_pages, KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attr,
KMemoryBlockDisableMergeAttribute set_disable_attr,
KMemoryBlockDisableMergeAttribute clear_disable_attr) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(address, PageSize));
ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
KMemoryAttribute::None);
VAddr cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if (it->HasProperties(state, perm, attr)) {
// If we already have the right properties, just advance.
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
remaining_pages = 0;
cur_address += remaining_size;
} else {
remaining_pages =
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress();
}
} else {
// If we need to, create a new block before and insert it.
if (cur_info.GetAddress() != cur_address) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
}
// If we need to, create a new block after and insert it.
if (cur_info.GetSize() > remaining_size) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
}
// Update block state.
it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
static_cast<u8>(clear_disable_attr));
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
}
it++;
}
this->CoalesceForUpdate(allocator, address, num_pages);
}
void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator,
VAddr address, size_t num_pages, KMemoryState test_state,
KMemoryPermission test_perm, KMemoryAttribute test_attr,
KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attr) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(address, PageSize));
ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
KMemoryAttribute::None);
VAddr cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if (it->HasProperties(test_state, test_perm, test_attr) &&
!it->HasProperties(state, perm, attr)) {
// If we need to, create a new block before and insert it.
if (cur_info.GetAddress() != cur_address) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
}
// If we need to, create a new block after and insert it.
if (cur_info.GetSize() > remaining_size) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
}
// Update block state.
it->Update(state, perm, attr, false, 0, 0);
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
} else {
// If we already have the right properties, just advance.
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
remaining_pages = 0;
cur_address += remaining_size;
} else {
remaining_pages =
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress();
}
}
it++;
}
this->CoalesceForUpdate(allocator, address, num_pages);
}
void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
size_t num_pages, MemoryBlockLockFunction lock_func,
KMemoryPermission perm) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(address, PageSize));
VAddr cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
const VAddr end_address = address + (num_pages * PageSize);
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
// If we need to, create a new block before and insert it.
if (cur_info.m_address != cur_address) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
}
if (cur_info.GetSize() > remaining_size) {
// If we need to, create a new block after and insert it.
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
}
// Call the locked update function.
(std::addressof(*it)->*lock_func)(perm, cur_info.GetAddress() == address,
cur_info.GetEndAddress() == end_address);
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
it++;
}
this->CoalesceForUpdate(allocator, address, num_pages);
}
// Debug.
bool KMemoryBlockManager::CheckState() const {
// Loop over every block, ensuring that we are sorted and coalesced.
auto it = m_memory_block_tree.cbegin();
auto prev = it++;
while (it != m_memory_block_tree.cend()) {
const KMemoryInfo prev_info = prev->GetMemoryInfo();
const KMemoryInfo cur_info = it->GetMemoryInfo();
// Sequential blocks which can be merged should be merged.
if (prev->CanMergeWith(*it)) {
return false;
}
// Sequential blocks should be sequential.
if (prev_info.GetEndAddress() != cur_info.GetAddress()) {
return false;
}
// If the block is ipc locked, it must have a count.
if ((cur_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None &&
cur_info.m_ipc_lock_count == 0) {
return false;
}
// If the block is device shared, it must have a count.
if ((cur_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None &&
cur_info.m_device_use_count == 0) {
return false;
}
// Advance the iterator.
prev = it++;
}
// Our loop will miss checking the last block, potentially, so check it.
if (prev != m_memory_block_tree.cend()) {
const KMemoryInfo prev_info = prev->GetMemoryInfo();
// If the block is ipc locked, it must have a count.
if ((prev_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None &&
prev_info.m_ipc_lock_count == 0) {
return false;
}
// If the block is device shared, it must have a count.
if ((prev_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None &&
prev_info.m_device_use_count == 0) {
return false;
}
}
return true;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_memory_block_manager.h"
namespace Kernel {
KMemoryBlockManager::KMemoryBlockManager() = default;
Result KMemoryBlockManager::Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager) {
// Allocate a block to encapsulate the address space, insert it into the tree.
KMemoryBlock* start_block = slab_manager->Allocate();
R_UNLESS(start_block != nullptr, ResultOutOfResource);
// Set our start and end.
m_start_address = st;
m_end_address = nd;
ASSERT(Common::IsAligned(m_start_address, PageSize));
ASSERT(Common::IsAligned(m_end_address, PageSize));
// Initialize and insert the block.
start_block->Initialize(m_start_address, (m_end_address - m_start_address) / PageSize,
KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None);
m_memory_block_tree.insert(*start_block);
R_SUCCEED();
}
void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
HostUnmapCallback&& host_unmap_callback) {
// Erase every block until we have none left.
auto it = m_memory_block_tree.begin();
while (it != m_memory_block_tree.end()) {
KMemoryBlock* block = std::addressof(*it);
it = m_memory_block_tree.erase(it);
slab_manager->Free(block);
host_unmap_callback(block->GetAddress(), block->GetSize());
}
ASSERT(m_memory_block_tree.empty());
}
VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, size_t region_num_pages,
size_t num_pages, size_t alignment, size_t offset,
size_t guard_pages) const {
if (num_pages > 0) {
const VAddr region_end = region_start + region_num_pages * PageSize;
const VAddr region_last = region_end - 1;
for (const_iterator it = this->FindIterator(region_start); it != m_memory_block_tree.cend();
it++) {
const KMemoryInfo info = it->GetMemoryInfo();
if (region_last < info.GetAddress()) {
break;
}
if (info.m_state != KMemoryState::Free) {
continue;
}
VAddr area = (info.GetAddress() <= region_start) ? region_start : info.GetAddress();
area += guard_pages * PageSize;
const VAddr offset_area = Common::AlignDown(area, alignment) + offset;
area = (area <= offset_area) ? offset_area : offset_area + alignment;
const VAddr area_end = area + num_pages * PageSize + guard_pages * PageSize;
const VAddr area_last = area_end - 1;
if (info.GetAddress() <= area && area < area_last && area_last <= region_last &&
area_last <= info.GetLastAddress()) {
return area;
}
}
}
return {};
}
void KMemoryBlockManager::CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator,
VAddr address, size_t num_pages) {
// Find the iterator now that we've updated.
iterator it = this->FindIterator(address);
if (address != m_start_address) {
it--;
}
// Coalesce blocks that we can.
while (true) {
iterator prev = it++;
if (it == m_memory_block_tree.end()) {
break;
}
if (prev->CanMergeWith(*it)) {
KMemoryBlock* block = std::addressof(*it);
m_memory_block_tree.erase(it);
prev->Add(*block);
allocator->Free(block);
it = prev;
}
if (address + num_pages * PageSize < it->GetMemoryInfo().GetEndAddress()) {
break;
}
}
}
void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
size_t num_pages, KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attr,
KMemoryBlockDisableMergeAttribute set_disable_attr,
KMemoryBlockDisableMergeAttribute clear_disable_attr) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(address, PageSize));
ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
KMemoryAttribute::None);
VAddr cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if (it->HasProperties(state, perm, attr)) {
// If we already have the right properties, just advance.
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
remaining_pages = 0;
cur_address += remaining_size;
} else {
remaining_pages =
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress();
}
} else {
// If we need to, create a new block before and insert it.
if (cur_info.GetAddress() != cur_address) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
}
// If we need to, create a new block after and insert it.
if (cur_info.GetSize() > remaining_size) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
}
// Update block state.
it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
static_cast<u8>(clear_disable_attr));
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
}
it++;
}
this->CoalesceForUpdate(allocator, address, num_pages);
}
void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator,
VAddr address, size_t num_pages, KMemoryState test_state,
KMemoryPermission test_perm, KMemoryAttribute test_attr,
KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attr) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(address, PageSize));
ASSERT((attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
KMemoryAttribute::None);
VAddr cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if (it->HasProperties(test_state, test_perm, test_attr) &&
!it->HasProperties(state, perm, attr)) {
// If we need to, create a new block before and insert it.
if (cur_info.GetAddress() != cur_address) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
}
// If we need to, create a new block after and insert it.
if (cur_info.GetSize() > remaining_size) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
}
// Update block state.
it->Update(state, perm, attr, false, 0, 0);
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
} else {
// If we already have the right properties, just advance.
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
remaining_pages = 0;
cur_address += remaining_size;
} else {
remaining_pages =
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress();
}
}
it++;
}
this->CoalesceForUpdate(allocator, address, num_pages);
}
void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
size_t num_pages, MemoryBlockLockFunction lock_func,
KMemoryPermission perm) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(address, PageSize));
VAddr cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
const VAddr end_address = address + (num_pages * PageSize);
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
// If we need to, create a new block before and insert it.
if (cur_info.m_address != cur_address) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
}
if (cur_info.GetSize() > remaining_size) {
// If we need to, create a new block after and insert it.
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
}
// Call the locked update function.
(std::addressof(*it)->*lock_func)(perm, cur_info.GetAddress() == address,
cur_info.GetEndAddress() == end_address);
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
it++;
}
this->CoalesceForUpdate(allocator, address, num_pages);
}
// Debug.
bool KMemoryBlockManager::CheckState() const {
// Loop over every block, ensuring that we are sorted and coalesced.
auto it = m_memory_block_tree.cbegin();
auto prev = it++;
while (it != m_memory_block_tree.cend()) {
const KMemoryInfo prev_info = prev->GetMemoryInfo();
const KMemoryInfo cur_info = it->GetMemoryInfo();
// Sequential blocks which can be merged should be merged.
if (prev->CanMergeWith(*it)) {
return false;
}
// Sequential blocks should be sequential.
if (prev_info.GetEndAddress() != cur_info.GetAddress()) {
return false;
}
// If the block is ipc locked, it must have a count.
if ((cur_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None &&
cur_info.m_ipc_lock_count == 0) {
return false;
}
// If the block is device shared, it must have a count.
if ((cur_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None &&
cur_info.m_device_use_count == 0) {
return false;
}
// Advance the iterator.
prev = it++;
}
// Our loop will miss checking the last block, potentially, so check it.
if (prev != m_memory_block_tree.cend()) {
const KMemoryInfo prev_info = prev->GetMemoryInfo();
// If the block is ipc locked, it must have a count.
if ((prev_info.m_attribute & KMemoryAttribute::IpcLocked) != KMemoryAttribute::None &&
prev_info.m_ipc_lock_count == 0) {
return false;
}
// If the block is device shared, it must have a count.
if ((prev_info.m_attribute & KMemoryAttribute::DeviceShared) != KMemoryAttribute::None &&
prev_info.m_device_use_count == 0) {
return false;
}
}
return true;
}
} // namespace Kernel

View File

@@ -1,157 +1,157 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_memory_block.h"
namespace Kernel {
class KMemoryBlockManagerUpdateAllocator {
public:
static constexpr size_t MaxBlocks = 2;
private:
KMemoryBlock* m_blocks[MaxBlocks];
size_t m_index;
KMemoryBlockSlabManager* m_slab_manager;
private:
Result Initialize(size_t num_blocks) {
// Check num blocks.
ASSERT(num_blocks <= MaxBlocks);
// Set index.
m_index = MaxBlocks - num_blocks;
// Allocate the blocks.
for (size_t i = 0; i < num_blocks && i < MaxBlocks; ++i) {
m_blocks[m_index + i] = m_slab_manager->Allocate();
R_UNLESS(m_blocks[m_index + i] != nullptr, ResultOutOfResource);
}
R_SUCCEED();
}
public:
KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm,
size_t num_blocks = MaxBlocks)
: m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) {
*out_result = this->Initialize(num_blocks);
}
~KMemoryBlockManagerUpdateAllocator() {
for (const auto& block : m_blocks) {
if (block != nullptr) {
m_slab_manager->Free(block);
}
}
}
KMemoryBlock* Allocate() {
ASSERT(m_index < MaxBlocks);
ASSERT(m_blocks[m_index] != nullptr);
KMemoryBlock* block = nullptr;
std::swap(block, m_blocks[m_index++]);
return block;
}
void Free(KMemoryBlock* block) {
ASSERT(m_index <= MaxBlocks);
ASSERT(block != nullptr);
if (m_index == 0) {
m_slab_manager->Free(block);
} else {
m_blocks[--m_index] = block;
}
}
};
class KMemoryBlockManager final {
public:
using MemoryBlockTree =
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryBlock>::TreeType<KMemoryBlock>;
using MemoryBlockLockFunction = void (KMemoryBlock::*)(KMemoryPermission new_perm, bool left,
bool right);
using iterator = MemoryBlockTree::iterator;
using const_iterator = MemoryBlockTree::const_iterator;
public:
KMemoryBlockManager();
using HostUnmapCallback = std::function<void(VAddr, u64)>;
Result Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager);
void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback);
iterator end() {
return m_memory_block_tree.end();
}
const_iterator end() const {
return m_memory_block_tree.end();
}
const_iterator cend() const {
return m_memory_block_tree.cend();
}
VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages,
size_t alignment, size_t offset, size_t guard_pages) const;
void Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages,
KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr,
KMemoryBlockDisableMergeAttribute set_disable_attr,
KMemoryBlockDisableMergeAttribute clear_disable_attr);
void UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages,
MemoryBlockLockFunction lock_func, KMemoryPermission perm);
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attr);
iterator FindIterator(VAddr address) const {
return m_memory_block_tree.find(KMemoryBlock(
address, 1, KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None));
}
const KMemoryBlock* FindBlock(VAddr address) const {
if (const_iterator it = this->FindIterator(address); it != m_memory_block_tree.end()) {
return std::addressof(*it);
}
return nullptr;
}
// Debug.
bool CheckState() const;
private:
void CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
size_t num_pages);
MemoryBlockTree m_memory_block_tree;
VAddr m_start_address{};
VAddr m_end_address{};
};
class KScopedMemoryBlockManagerAuditor {
public:
explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager* m) : m_manager(m) {
ASSERT(m_manager->CheckState());
}
explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager& m)
: KScopedMemoryBlockManagerAuditor(std::addressof(m)) {}
~KScopedMemoryBlockManagerAuditor() {
ASSERT(m_manager->CheckState());
}
private:
KMemoryBlockManager* m_manager;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_memory_block.h"
namespace Kernel {
class KMemoryBlockManagerUpdateAllocator {
public:
static constexpr size_t MaxBlocks = 2;
private:
KMemoryBlock* m_blocks[MaxBlocks];
size_t m_index;
KMemoryBlockSlabManager* m_slab_manager;
private:
Result Initialize(size_t num_blocks) {
// Check num blocks.
ASSERT(num_blocks <= MaxBlocks);
// Set index.
m_index = MaxBlocks - num_blocks;
// Allocate the blocks.
for (size_t i = 0; i < num_blocks && i < MaxBlocks; ++i) {
m_blocks[m_index + i] = m_slab_manager->Allocate();
R_UNLESS(m_blocks[m_index + i] != nullptr, ResultOutOfResource);
}
R_SUCCEED();
}
public:
KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm,
size_t num_blocks = MaxBlocks)
: m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) {
*out_result = this->Initialize(num_blocks);
}
~KMemoryBlockManagerUpdateAllocator() {
for (const auto& block : m_blocks) {
if (block != nullptr) {
m_slab_manager->Free(block);
}
}
}
KMemoryBlock* Allocate() {
ASSERT(m_index < MaxBlocks);
ASSERT(m_blocks[m_index] != nullptr);
KMemoryBlock* block = nullptr;
std::swap(block, m_blocks[m_index++]);
return block;
}
void Free(KMemoryBlock* block) {
ASSERT(m_index <= MaxBlocks);
ASSERT(block != nullptr);
if (m_index == 0) {
m_slab_manager->Free(block);
} else {
m_blocks[--m_index] = block;
}
}
};
class KMemoryBlockManager final {
public:
using MemoryBlockTree =
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryBlock>::TreeType<KMemoryBlock>;
using MemoryBlockLockFunction = void (KMemoryBlock::*)(KMemoryPermission new_perm, bool left,
bool right);
using iterator = MemoryBlockTree::iterator;
using const_iterator = MemoryBlockTree::const_iterator;
public:
KMemoryBlockManager();
using HostUnmapCallback = std::function<void(VAddr, u64)>;
Result Initialize(VAddr st, VAddr nd, KMemoryBlockSlabManager* slab_manager);
void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback);
iterator end() {
return m_memory_block_tree.end();
}
const_iterator end() const {
return m_memory_block_tree.end();
}
const_iterator cend() const {
return m_memory_block_tree.cend();
}
VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages,
size_t alignment, size_t offset, size_t guard_pages) const;
void Update(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages,
KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr,
KMemoryBlockDisableMergeAttribute set_disable_attr,
KMemoryBlockDisableMergeAttribute clear_disable_attr);
void UpdateLock(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address, size_t num_pages,
MemoryBlockLockFunction lock_func, KMemoryPermission perm);
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
KMemoryAttribute attr);
iterator FindIterator(VAddr address) const {
return m_memory_block_tree.find(KMemoryBlock(
address, 1, KMemoryState::Free, KMemoryPermission::None, KMemoryAttribute::None));
}
const KMemoryBlock* FindBlock(VAddr address) const {
if (const_iterator it = this->FindIterator(address); it != m_memory_block_tree.end()) {
return std::addressof(*it);
}
return nullptr;
}
// Debug.
bool CheckState() const;
private:
void CoalesceForUpdate(KMemoryBlockManagerUpdateAllocator* allocator, VAddr address,
size_t num_pages);
MemoryBlockTree m_memory_block_tree;
VAddr m_start_address{};
VAddr m_end_address{};
};
class KScopedMemoryBlockManagerAuditor {
public:
explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager* m) : m_manager(m) {
ASSERT(m_manager->CheckState());
}
explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager& m)
: KScopedMemoryBlockManagerAuditor(std::addressof(m)) {}
~KScopedMemoryBlockManagerAuditor() {
ASSERT(m_manager->CheckState());
}
private:
KMemoryBlockManager* m_manager;
};
} // namespace Kernel

View File

@@ -1,201 +1,201 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/literals.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_trace.h"
namespace Kernel {
namespace {
using namespace Common::Literals;
constexpr size_t CarveoutAlignment = 0x20000;
constexpr size_t CarveoutSizeMax = (512_MiB) - CarveoutAlignment;
bool SetupPowerManagementControllerMemoryRegion(KMemoryLayout& memory_layout) {
// Above firmware 2.0.0, the PMC is not mappable.
return memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x7000E000, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap) &&
memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x7000E400, 0xC00,
KMemoryRegionType_PowerManagementController | KMemoryRegionAttr_NoUserMap);
}
void InsertPoolPartitionRegionIntoBothTrees(KMemoryLayout& memory_layout, size_t start, size_t size,
KMemoryRegionType phys_type,
KMemoryRegionType virt_type, u32& cur_attr) {
const u32 attr = cur_attr++;
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(start, size,
static_cast<u32>(phys_type), attr));
const KMemoryRegion* phys = memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(
static_cast<u32>(phys_type), attr);
ASSERT(phys != nullptr);
ASSERT(phys->GetEndAddress() != 0);
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size,
static_cast<u32>(virt_type), attr));
}
} // namespace
namespace Init {
void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout) {
ASSERT(SetupPowerManagementControllerMemoryRegion(memory_layout));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x50041000, 0x1000,
KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x50042000, 0x1000,
KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
// Map IRAM unconditionally, to support debug-logging-to-iram build config.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x40000000, 0x40000, KMemoryRegionType_LegacyLpsIram | KMemoryRegionAttr_ShouldKernelMap));
// Above firmware 2.0.0, prevent mapping the bpmp exception vectors or the ipatch region.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
}
void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout) {
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
const PAddr physical_memory_base_address =
KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
// Insert blocks into the tree.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
physical_memory_base_address, intended_memory_size, KMemoryRegionType_Dram));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
physical_memory_base_address, ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
// Insert the KTrace block at the end of Dram, if KTrace is enabled.
static_assert(!IsKTraceEnabled || KTraceBufferSize > 0);
if constexpr (IsKTraceEnabled) {
const PAddr ktrace_buffer_phys_addr =
physical_memory_base_address + intended_memory_size - KTraceBufferSize;
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
ktrace_buffer_phys_addr, KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer));
}
}
void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout) {
// Start by identifying the extents of the DRAM memory region.
const auto dram_extents = memory_layout.GetMainMemoryPhysicalExtents();
ASSERT(dram_extents.GetEndAddress() != 0);
// Determine the end of the pool region.
const u64 pool_end = dram_extents.GetEndAddress() - KTraceBufferSize;
// Find the start of the kernel DRAM region.
const KMemoryRegion* kernel_dram_region =
memory_layout.GetPhysicalMemoryRegionTree().FindFirstDerived(
KMemoryRegionType_DramKernelBase);
ASSERT(kernel_dram_region != nullptr);
const u64 kernel_dram_start = kernel_dram_region->GetAddress();
ASSERT(Common::IsAligned(kernel_dram_start, CarveoutAlignment));
// Find the start of the pool partitions region.
const KMemoryRegion* pool_partitions_region =
memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(
KMemoryRegionType_DramPoolPartition, 0);
ASSERT(pool_partitions_region != nullptr);
const u64 pool_partitions_start = pool_partitions_region->GetAddress();
// Setup the pool partition layouts.
// On 5.0.0+, setup modern 4-pool-partition layout.
// Get Application and Applet pool sizes.
const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
const size_t unsafe_system_pool_min_size =
KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
// Decide on starting addresses for our pools.
const u64 application_pool_start = pool_end - application_pool_size;
const u64 applet_pool_start = application_pool_start - applet_pool_size;
const u64 unsafe_system_pool_start = std::min(
kernel_dram_start + CarveoutSizeMax,
Common::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment));
const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start;
// We want to arrange application pool depending on where the middle of dram is.
const u64 dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2;
u32 cur_pool_attr = 0;
size_t total_overhead_size = 0;
if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) {
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, application_pool_start, application_pool_size,
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
cur_pool_attr);
total_overhead_size +=
KMemoryManager::CalculateManagementOverheadSize(application_pool_size);
} else {
const size_t first_application_pool_size = dram_midpoint - application_pool_start;
const size_t second_application_pool_size =
application_pool_start + application_pool_size - dram_midpoint;
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, application_pool_start, first_application_pool_size,
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
cur_pool_attr);
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, dram_midpoint, second_application_pool_size,
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
cur_pool_attr);
total_overhead_size +=
KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size);
total_overhead_size +=
KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
}
// Insert the applet pool.
InsertPoolPartitionRegionIntoBothTrees(memory_layout, applet_pool_start, applet_pool_size,
KMemoryRegionType_DramAppletPool,
KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size);
// Insert the nonsecure system pool.
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, unsafe_system_pool_start, unsafe_system_pool_size,
KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool,
cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
// Insert the pool management region.
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(
(unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
const u64 pool_management_start = unsafe_system_pool_start - total_overhead_size;
const size_t pool_management_size = total_overhead_size;
u32 pool_management_attr = 0;
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, pool_management_start, pool_management_size,
KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement,
pool_management_attr);
// Insert the system pool.
const u64 system_pool_size = pool_management_start - pool_partitions_start;
InsertPoolPartitionRegionIntoBothTrees(memory_layout, pool_partitions_start, system_pool_size,
KMemoryRegionType_DramSystemPool,
KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
}
} // namespace Init
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/literals.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_trace.h"
namespace Kernel {
namespace {
using namespace Common::Literals;
constexpr size_t CarveoutAlignment = 0x20000;
constexpr size_t CarveoutSizeMax = (512_MiB) - CarveoutAlignment;
bool SetupPowerManagementControllerMemoryRegion(KMemoryLayout& memory_layout) {
// Above firmware 2.0.0, the PMC is not mappable.
return memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x7000E000, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap) &&
memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x7000E400, 0xC00,
KMemoryRegionType_PowerManagementController | KMemoryRegionAttr_NoUserMap);
}
void InsertPoolPartitionRegionIntoBothTrees(KMemoryLayout& memory_layout, size_t start, size_t size,
KMemoryRegionType phys_type,
KMemoryRegionType virt_type, u32& cur_attr) {
const u32 attr = cur_attr++;
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(start, size,
static_cast<u32>(phys_type), attr));
const KMemoryRegion* phys = memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(
static_cast<u32>(phys_type), attr);
ASSERT(phys != nullptr);
ASSERT(phys->GetEndAddress() != 0);
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size,
static_cast<u32>(virt_type), attr));
}
} // namespace
namespace Init {
void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout) {
ASSERT(SetupPowerManagementControllerMemoryRegion(memory_layout));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x50041000, 0x1000,
KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x50042000, 0x1000,
KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
// Map IRAM unconditionally, to support debug-logging-to-iram build config.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x40000000, 0x40000, KMemoryRegionType_LegacyLpsIram | KMemoryRegionAttr_ShouldKernelMap));
// Above firmware 2.0.0, prevent mapping the bpmp exception vectors or the ipatch region.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
}
void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout) {
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
const PAddr physical_memory_base_address =
KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
// Insert blocks into the tree.
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
physical_memory_base_address, intended_memory_size, KMemoryRegionType_Dram));
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
physical_memory_base_address, ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
// Insert the KTrace block at the end of Dram, if KTrace is enabled.
static_assert(!IsKTraceEnabled || KTraceBufferSize > 0);
if constexpr (IsKTraceEnabled) {
const PAddr ktrace_buffer_phys_addr =
physical_memory_base_address + intended_memory_size - KTraceBufferSize;
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
ktrace_buffer_phys_addr, KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer));
}
}
void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout) {
// Start by identifying the extents of the DRAM memory region.
const auto dram_extents = memory_layout.GetMainMemoryPhysicalExtents();
ASSERT(dram_extents.GetEndAddress() != 0);
// Determine the end of the pool region.
const u64 pool_end = dram_extents.GetEndAddress() - KTraceBufferSize;
// Find the start of the kernel DRAM region.
const KMemoryRegion* kernel_dram_region =
memory_layout.GetPhysicalMemoryRegionTree().FindFirstDerived(
KMemoryRegionType_DramKernelBase);
ASSERT(kernel_dram_region != nullptr);
const u64 kernel_dram_start = kernel_dram_region->GetAddress();
ASSERT(Common::IsAligned(kernel_dram_start, CarveoutAlignment));
// Find the start of the pool partitions region.
const KMemoryRegion* pool_partitions_region =
memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(
KMemoryRegionType_DramPoolPartition, 0);
ASSERT(pool_partitions_region != nullptr);
const u64 pool_partitions_start = pool_partitions_region->GetAddress();
// Setup the pool partition layouts.
// On 5.0.0+, setup modern 4-pool-partition layout.
// Get Application and Applet pool sizes.
const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
const size_t unsafe_system_pool_min_size =
KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
// Decide on starting addresses for our pools.
const u64 application_pool_start = pool_end - application_pool_size;
const u64 applet_pool_start = application_pool_start - applet_pool_size;
const u64 unsafe_system_pool_start = std::min(
kernel_dram_start + CarveoutSizeMax,
Common::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment));
const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start;
// We want to arrange application pool depending on where the middle of dram is.
const u64 dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2;
u32 cur_pool_attr = 0;
size_t total_overhead_size = 0;
if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) {
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, application_pool_start, application_pool_size,
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
cur_pool_attr);
total_overhead_size +=
KMemoryManager::CalculateManagementOverheadSize(application_pool_size);
} else {
const size_t first_application_pool_size = dram_midpoint - application_pool_start;
const size_t second_application_pool_size =
application_pool_start + application_pool_size - dram_midpoint;
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, application_pool_start, first_application_pool_size,
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
cur_pool_attr);
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, dram_midpoint, second_application_pool_size,
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
cur_pool_attr);
total_overhead_size +=
KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size);
total_overhead_size +=
KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
}
// Insert the applet pool.
InsertPoolPartitionRegionIntoBothTrees(memory_layout, applet_pool_start, applet_pool_size,
KMemoryRegionType_DramAppletPool,
KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size);
// Insert the nonsecure system pool.
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, unsafe_system_pool_start, unsafe_system_pool_size,
KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool,
cur_pool_attr);
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
// Insert the pool management region.
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(
(unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
const u64 pool_management_start = unsafe_system_pool_start - total_overhead_size;
const size_t pool_management_size = total_overhead_size;
u32 pool_management_attr = 0;
InsertPoolPartitionRegionIntoBothTrees(
memory_layout, pool_management_start, pool_management_size,
KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement,
pool_management_attr);
// Insert the system pool.
const u64 system_pool_size = pool_management_start - pool_partitions_start;
InsertPoolPartitionRegionIntoBothTrees(memory_layout, pool_partitions_start, system_pool_size,
KMemoryRegionType_DramSystemPool,
KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
}
} // namespace Init
} // namespace Kernel

View File

@@ -1,165 +1,165 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include "common/alignment.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_system_control.h"
namespace Kernel {
namespace {
template <typename... Args>
KMemoryRegion* AllocateRegion(KMemoryRegionAllocator& memory_region_allocator, Args&&... args) {
return memory_region_allocator.Allocate(std::forward<Args>(args)...);
}
} // namespace
KMemoryRegionTree::KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_)
: memory_region_allocator{memory_region_allocator_} {}
void KMemoryRegionTree::InsertDirectly(u64 address, u64 last_address, u32 attr, u32 type_id) {
this->insert(*AllocateRegion(memory_region_allocator, address, last_address, attr, type_id));
}
bool KMemoryRegionTree::Insert(u64 address, size_t size, u32 type_id, u32 new_attr, u32 old_attr) {
// Locate the memory region that contains the address.
KMemoryRegion* found = this->FindModifiable(address);
// We require that the old attr is correct.
if (found->GetAttributes() != old_attr) {
return false;
}
// We further require that the region can be split from the old region.
const u64 inserted_region_end = address + size;
const u64 inserted_region_last = inserted_region_end - 1;
if (found->GetLastAddress() < inserted_region_last) {
return false;
}
// Further, we require that the type id is a valid transformation.
if (!found->CanDerive(type_id)) {
return false;
}
// Cache information from the region before we remove it.
const u64 old_address = found->GetAddress();
const u64 old_last = found->GetLastAddress();
const u64 old_pair = found->GetPairAddress();
const u32 old_type = found->GetType();
// Erase the existing region from the tree.
this->erase(this->iterator_to(*found));
// Insert the new region into the tree.
if (old_address == address) {
// Reuse the old object for the new region, if we can.
found->Reset(address, inserted_region_last, old_pair, new_attr, type_id);
this->insert(*found);
} else {
// If we can't re-use, adjust the old region.
found->Reset(old_address, address - 1, old_pair, old_attr, old_type);
this->insert(*found);
// Insert a new region for the split.
const u64 new_pair = (old_pair != std::numeric_limits<u64>::max())
? old_pair + (address - old_address)
: old_pair;
this->insert(*AllocateRegion(memory_region_allocator, address, inserted_region_last,
new_pair, new_attr, type_id));
}
// If we need to insert a region after the region, do so.
if (old_last != inserted_region_last) {
const u64 after_pair = (old_pair != std::numeric_limits<u64>::max())
? old_pair + (inserted_region_end - old_address)
: old_pair;
this->insert(*AllocateRegion(memory_region_allocator, inserted_region_end, old_last,
after_pair, old_attr, old_type));
}
return true;
}
VAddr KMemoryRegionTree::GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id) {
// We want to find the total extents of the type id.
const auto extents = this->GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
// Ensure that our alignment is correct.
ASSERT(Common::IsAligned(extents.GetAddress(), alignment));
const u64 first_address = extents.GetAddress();
const u64 last_address = extents.GetLastAddress();
const u64 first_index = first_address / alignment;
const u64 last_index = last_address / alignment;
while (true) {
const u64 candidate =
KSystemControl::GenerateRandomRange(first_index, last_index) * alignment;
// Ensure that the candidate doesn't overflow with the size.
if (!(candidate < candidate + size)) {
continue;
}
const u64 candidate_last = candidate + size - 1;
// Ensure that the candidate fits within the region.
if (candidate_last > last_address) {
continue;
}
// Locate the candidate region, and ensure it fits and has the correct type id.
if (const auto& candidate_region = *this->Find(candidate);
!(candidate_last <= candidate_region.GetLastAddress() &&
candidate_region.GetType() == type_id)) {
continue;
}
return candidate;
}
}
KMemoryLayout::KMemoryLayout()
: virtual_tree{memory_region_allocator}, physical_tree{memory_region_allocator},
virtual_linear_tree{memory_region_allocator}, physical_linear_tree{memory_region_allocator} {}
void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
VAddr linear_virtual_start) {
// Set static differences.
linear_phys_to_virt_diff = linear_virtual_start - aligned_linear_phys_start;
linear_virt_to_phys_diff = aligned_linear_phys_start - linear_virtual_start;
// Initialize linear trees.
for (auto& region : GetPhysicalMemoryRegionTree()) {
if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
GetPhysicalLinearMemoryRegionTree().InsertDirectly(
region.GetAddress(), region.GetLastAddress(), region.GetAttributes(),
region.GetType());
}
}
for (auto& region : GetVirtualMemoryRegionTree()) {
if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
GetVirtualLinearMemoryRegionTree().InsertDirectly(
region.GetAddress(), region.GetLastAddress(), region.GetAttributes(),
region.GetType());
}
}
}
size_t KMemoryLayout::GetResourceRegionSizeForInit() {
// Calculate resource region size based on whether we allow extra threads.
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
size_t resource_region_size =
KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
return resource_region_size;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include "common/alignment.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_system_control.h"
namespace Kernel {
namespace {
template <typename... Args>
KMemoryRegion* AllocateRegion(KMemoryRegionAllocator& memory_region_allocator, Args&&... args) {
return memory_region_allocator.Allocate(std::forward<Args>(args)...);
}
} // namespace
KMemoryRegionTree::KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_)
: memory_region_allocator{memory_region_allocator_} {}
void KMemoryRegionTree::InsertDirectly(u64 address, u64 last_address, u32 attr, u32 type_id) {
this->insert(*AllocateRegion(memory_region_allocator, address, last_address, attr, type_id));
}
bool KMemoryRegionTree::Insert(u64 address, size_t size, u32 type_id, u32 new_attr, u32 old_attr) {
// Locate the memory region that contains the address.
KMemoryRegion* found = this->FindModifiable(address);
// We require that the old attr is correct.
if (found->GetAttributes() != old_attr) {
return false;
}
// We further require that the region can be split from the old region.
const u64 inserted_region_end = address + size;
const u64 inserted_region_last = inserted_region_end - 1;
if (found->GetLastAddress() < inserted_region_last) {
return false;
}
// Further, we require that the type id is a valid transformation.
if (!found->CanDerive(type_id)) {
return false;
}
// Cache information from the region before we remove it.
const u64 old_address = found->GetAddress();
const u64 old_last = found->GetLastAddress();
const u64 old_pair = found->GetPairAddress();
const u32 old_type = found->GetType();
// Erase the existing region from the tree.
this->erase(this->iterator_to(*found));
// Insert the new region into the tree.
if (old_address == address) {
// Reuse the old object for the new region, if we can.
found->Reset(address, inserted_region_last, old_pair, new_attr, type_id);
this->insert(*found);
} else {
// If we can't re-use, adjust the old region.
found->Reset(old_address, address - 1, old_pair, old_attr, old_type);
this->insert(*found);
// Insert a new region for the split.
const u64 new_pair = (old_pair != std::numeric_limits<u64>::max())
? old_pair + (address - old_address)
: old_pair;
this->insert(*AllocateRegion(memory_region_allocator, address, inserted_region_last,
new_pair, new_attr, type_id));
}
// If we need to insert a region after the region, do so.
if (old_last != inserted_region_last) {
const u64 after_pair = (old_pair != std::numeric_limits<u64>::max())
? old_pair + (inserted_region_end - old_address)
: old_pair;
this->insert(*AllocateRegion(memory_region_allocator, inserted_region_end, old_last,
after_pair, old_attr, old_type));
}
return true;
}
VAddr KMemoryRegionTree::GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id) {
// We want to find the total extents of the type id.
const auto extents = this->GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
// Ensure that our alignment is correct.
ASSERT(Common::IsAligned(extents.GetAddress(), alignment));
const u64 first_address = extents.GetAddress();
const u64 last_address = extents.GetLastAddress();
const u64 first_index = first_address / alignment;
const u64 last_index = last_address / alignment;
while (true) {
const u64 candidate =
KSystemControl::GenerateRandomRange(first_index, last_index) * alignment;
// Ensure that the candidate doesn't overflow with the size.
if (!(candidate < candidate + size)) {
continue;
}
const u64 candidate_last = candidate + size - 1;
// Ensure that the candidate fits within the region.
if (candidate_last > last_address) {
continue;
}
// Locate the candidate region, and ensure it fits and has the correct type id.
if (const auto& candidate_region = *this->Find(candidate);
!(candidate_last <= candidate_region.GetLastAddress() &&
candidate_region.GetType() == type_id)) {
continue;
}
return candidate;
}
}
KMemoryLayout::KMemoryLayout()
: virtual_tree{memory_region_allocator}, physical_tree{memory_region_allocator},
virtual_linear_tree{memory_region_allocator}, physical_linear_tree{memory_region_allocator} {}
void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
VAddr linear_virtual_start) {
// Set static differences.
linear_phys_to_virt_diff = linear_virtual_start - aligned_linear_phys_start;
linear_virt_to_phys_diff = aligned_linear_phys_start - linear_virtual_start;
// Initialize linear trees.
for (auto& region : GetPhysicalMemoryRegionTree()) {
if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
GetPhysicalLinearMemoryRegionTree().InsertDirectly(
region.GetAddress(), region.GetLastAddress(), region.GetAttributes(),
region.GetType());
}
}
for (auto& region : GetVirtualMemoryRegionTree()) {
if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
GetVirtualLinearMemoryRegionTree().InsertDirectly(
region.GetAddress(), region.GetLastAddress(), region.GetAttributes(),
region.GetType());
}
}
}
size_t KMemoryLayout::GetResourceRegionSizeForInit() {
// Calculate resource region size based on whether we allow extra threads.
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
size_t resource_region_size =
KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
return resource_region_size;
}
} // namespace Kernel

View File

@@ -1,403 +1,403 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <utility>
#include "common/alignment.h"
#include "common/literals.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_memory_region.h"
#include "core/hle/kernel/k_memory_region_type.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
using namespace Common::Literals;
constexpr std::size_t L1BlockSize = 1_GiB;
constexpr std::size_t L2BlockSize = 2_MiB;
constexpr std::size_t GetMaximumOverheadSize(std::size_t size) {
return (Common::DivideUp(size, L1BlockSize) + Common::DivideUp(size, L2BlockSize)) * PageSize;
}
constexpr std::size_t MainMemorySize = 4_GiB;
constexpr std::size_t MainMemorySizeMax = 8_GiB;
constexpr std::size_t ReservedEarlyDramSize = 384_KiB;
constexpr std::size_t DramPhysicalAddress = 0x80000000;
constexpr std::size_t KernelAslrAlignment = 2_MiB;
constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth;
constexpr std::size_t KernelVirtualAddressSpaceEnd =
KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment);
constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1ULL;
constexpr std::size_t KernelVirtualAddressSpaceSize =
KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
constexpr std::size_t KernelVirtualAddressCodeBase = KernelVirtualAddressSpaceBase;
constexpr std::size_t KernelVirtualAddressCodeSize = 392_KiB;
constexpr std::size_t KernelVirtualAddressCodeEnd =
KernelVirtualAddressCodeBase + KernelVirtualAddressCodeSize;
constexpr std::size_t KernelPhysicalAddressSpaceBase = 0ULL;
constexpr std::size_t KernelPhysicalAddressSpaceEnd =
KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceWidth;
constexpr std::size_t KernelPhysicalAddressSpaceLast = KernelPhysicalAddressSpaceEnd - 1ULL;
constexpr std::size_t KernelPhysicalAddressSpaceSize =
KernelPhysicalAddressSpaceEnd - KernelPhysicalAddressSpaceBase;
constexpr std::size_t KernelPhysicalAddressCodeBase = DramPhysicalAddress + ReservedEarlyDramSize;
constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemorySizeMax);
constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
constexpr std::size_t KernelResourceSize =
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
constexpr bool IsKernelAddressKey(VAddr key) {
return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
}
constexpr bool IsKernelAddress(VAddr address) {
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
}
class KMemoryLayout final {
public:
KMemoryLayout();
KMemoryRegionTree& GetVirtualMemoryRegionTree() {
return virtual_tree;
}
const KMemoryRegionTree& GetVirtualMemoryRegionTree() const {
return virtual_tree;
}
KMemoryRegionTree& GetPhysicalMemoryRegionTree() {
return physical_tree;
}
const KMemoryRegionTree& GetPhysicalMemoryRegionTree() const {
return physical_tree;
}
KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() {
return virtual_linear_tree;
}
const KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() const {
return virtual_linear_tree;
}
KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() {
return physical_linear_tree;
}
const KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() const {
return physical_linear_tree;
}
VAddr GetLinearVirtualAddress(PAddr address) const {
return address + linear_phys_to_virt_diff;
}
PAddr GetLinearPhysicalAddress(VAddr address) const {
return address + linear_virt_to_phys_diff;
}
const KMemoryRegion* FindVirtual(VAddr address) const {
return Find(address, GetVirtualMemoryRegionTree());
}
const KMemoryRegion* FindPhysical(PAddr address) const {
return Find(address, GetPhysicalMemoryRegionTree());
}
const KMemoryRegion* FindVirtualLinear(VAddr address) const {
return Find(address, GetVirtualLinearMemoryRegionTree());
}
const KMemoryRegion* FindPhysicalLinear(PAddr address) const {
return Find(address, GetPhysicalLinearMemoryRegionTree());
}
VAddr GetMainStackTopAddress(s32 core_id) const {
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscMainStack);
}
VAddr GetIdleStackTopAddress(s32 core_id) const {
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscIdleStack);
}
VAddr GetExceptionStackTopAddress(s32 core_id) const {
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
}
VAddr GetSlabRegionAddress() const {
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
.GetAddress();
}
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
}
PAddr GetDevicePhysicalAddress(KMemoryRegionType type) const {
return GetDeviceRegion(type).GetAddress();
}
VAddr GetDeviceVirtualAddress(KMemoryRegionType type) const {
return GetDeviceRegion(type).GetPairAddress();
}
const KMemoryRegion& GetPoolManagementRegion() const {
return Dereference(
GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramPoolManagement));
}
const KMemoryRegion& GetPageTableHeapRegion() const {
return Dereference(
GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelPtHeap));
}
const KMemoryRegion& GetKernelStackRegion() const {
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelStack));
}
const KMemoryRegion& GetTempRegion() const {
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelTemp));
}
const KMemoryRegion& GetKernelTraceBufferRegion() const {
return Dereference(GetVirtualLinearMemoryRegionTree().FindByType(
KMemoryRegionType_VirtualDramKernelTraceBuffer));
}
const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const {
return Dereference(FindVirtualLinear(address));
}
const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const {
return Dereference(FindPhysicalLinear(address));
}
const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const {
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer);
}
const KMemoryRegion* GetPhysicalOnMemoryBootImageRegion() const {
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_OnMemoryBootImage);
}
const KMemoryRegion* GetPhysicalDTBRegion() const {
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DTB);
}
bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address) const {
return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(),
KMemoryRegionType_DramUserPool);
}
bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address) const {
return IsTypedAddress(region, address, GetVirtualLinearMemoryRegionTree(),
KMemoryRegionType_VirtualDramUserPool);
}
bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address, size_t size) const {
return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(),
KMemoryRegionType_DramUserPool);
}
bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address, size_t size) const {
return IsTypedAddress(region, address, size, GetVirtualLinearMemoryRegionTree(),
KMemoryRegionType_VirtualDramUserPool);
}
bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address) const {
return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(),
static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped));
}
bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address,
size_t size) const {
return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(),
static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped));
}
std::pair<size_t, size_t> GetTotalAndKernelMemorySizes() const {
size_t total_size = 0, kernel_size = 0;
for (const auto& region : GetPhysicalMemoryRegionTree()) {
if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
total_size += region.GetSize();
if (!region.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
kernel_size += region.GetSize();
}
}
}
return std::make_pair(total_size, kernel_size);
}
void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
VAddr linear_virtual_start);
static size_t GetResourceRegionSizeForInit();
auto GetKernelRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel);
}
auto GetKernelCodeRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode);
}
auto GetKernelStackRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelStack);
}
auto GetKernelMiscRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelMisc);
}
auto GetKernelSlabRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelSlab);
}
auto GetLinearRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionAttr_LinearMapped);
}
auto GetLinearRegionVirtualExtents() const {
const auto physical = GetLinearRegionPhysicalExtents();
return KMemoryRegion(GetLinearVirtualAddress(physical.GetAddress()),
GetLinearVirtualAddress(physical.GetLastAddress()), 0,
KMemoryRegionType_None);
}
auto GetMainMemoryPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Dram);
}
auto GetCarveoutRegionExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionAttr_CarveoutProtected);
}
auto GetKernelRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelBase);
}
auto GetKernelCodeRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelCode);
}
auto GetKernelSlabRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelSlab);
}
auto GetKernelPageTableHeapRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelPtHeap);
}
auto GetKernelInitPageTableRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelInitPt);
}
auto GetKernelPoolManagementRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramPoolManagement);
}
auto GetKernelPoolPartitionRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramPoolPartition);
}
auto GetKernelSystemPoolRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramSystemPool);
}
auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramSystemNonSecurePool);
}
auto GetKernelAppletPoolRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramAppletPool);
}
auto GetKernelApplicationPoolRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramApplicationPool);
}
auto GetKernelTraceBufferRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_KernelTraceBuffer);
}
private:
template <typename AddressType>
static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address,
const KMemoryRegionTree& tree, KMemoryRegionType type) {
// Check if the cached region already contains the address.
if (region != nullptr && region->Contains(address)) {
return true;
}
// Find the containing region, and update the cache.
if (const KMemoryRegion* found = tree.Find(address);
found != nullptr && found->IsDerivedFrom(type)) {
region = found;
return true;
} else {
return false;
}
}
template <typename AddressType>
static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address, size_t size,
const KMemoryRegionTree& tree, KMemoryRegionType type) {
// Get the end of the checked region.
const u64 last_address = address + size - 1;
// Walk the tree to verify the region is correct.
const KMemoryRegion* cur =
(region != nullptr && region->Contains(address)) ? region : tree.Find(address);
while (cur != nullptr && cur->IsDerivedFrom(type)) {
if (last_address <= cur->GetLastAddress()) {
region = cur;
return true;
}
cur = cur->GetNext();
}
return false;
}
template <typename AddressType>
static const KMemoryRegion* Find(AddressType address, const KMemoryRegionTree& tree) {
return tree.Find(address);
}
static KMemoryRegion& Dereference(KMemoryRegion* region) {
ASSERT(region != nullptr);
return *region;
}
static const KMemoryRegion& Dereference(const KMemoryRegion* region) {
ASSERT(region != nullptr);
return *region;
}
VAddr GetStackTopAddress(s32 core_id, KMemoryRegionType type) const {
const auto& region = Dereference(
GetVirtualMemoryRegionTree().FindByTypeAndAttribute(type, static_cast<u32>(core_id)));
ASSERT(region.GetEndAddress() != 0);
return region.GetEndAddress();
}
private:
u64 linear_phys_to_virt_diff{};
u64 linear_virt_to_phys_diff{};
KMemoryRegionAllocator memory_region_allocator;
KMemoryRegionTree virtual_tree;
KMemoryRegionTree physical_tree;
KMemoryRegionTree virtual_linear_tree;
KMemoryRegionTree physical_linear_tree;
};
namespace Init {
// These should be generic, regardless of board.
void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout);
// These may be implemented in a board-specific manner.
void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout);
void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout);
} // namespace Init
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <utility>
#include "common/alignment.h"
#include "common/literals.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_memory_region.h"
#include "core/hle/kernel/k_memory_region_type.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
using namespace Common::Literals;
constexpr std::size_t L1BlockSize = 1_GiB;
constexpr std::size_t L2BlockSize = 2_MiB;
constexpr std::size_t GetMaximumOverheadSize(std::size_t size) {
return (Common::DivideUp(size, L1BlockSize) + Common::DivideUp(size, L2BlockSize)) * PageSize;
}
constexpr std::size_t MainMemorySize = 4_GiB;
constexpr std::size_t MainMemorySizeMax = 8_GiB;
constexpr std::size_t ReservedEarlyDramSize = 384_KiB;
constexpr std::size_t DramPhysicalAddress = 0x80000000;
constexpr std::size_t KernelAslrAlignment = 2_MiB;
constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth;
constexpr std::size_t KernelVirtualAddressSpaceEnd =
KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment);
constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1ULL;
constexpr std::size_t KernelVirtualAddressSpaceSize =
KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
constexpr std::size_t KernelVirtualAddressCodeBase = KernelVirtualAddressSpaceBase;
constexpr std::size_t KernelVirtualAddressCodeSize = 392_KiB;
constexpr std::size_t KernelVirtualAddressCodeEnd =
KernelVirtualAddressCodeBase + KernelVirtualAddressCodeSize;
constexpr std::size_t KernelPhysicalAddressSpaceBase = 0ULL;
constexpr std::size_t KernelPhysicalAddressSpaceEnd =
KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceWidth;
constexpr std::size_t KernelPhysicalAddressSpaceLast = KernelPhysicalAddressSpaceEnd - 1ULL;
constexpr std::size_t KernelPhysicalAddressSpaceSize =
KernelPhysicalAddressSpaceEnd - KernelPhysicalAddressSpaceBase;
constexpr std::size_t KernelPhysicalAddressCodeBase = DramPhysicalAddress + ReservedEarlyDramSize;
constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemorySizeMax);
constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
constexpr std::size_t KernelResourceSize =
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
constexpr bool IsKernelAddressKey(VAddr key) {
return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
}
constexpr bool IsKernelAddress(VAddr address) {
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
}
class KMemoryLayout final {
public:
KMemoryLayout();
KMemoryRegionTree& GetVirtualMemoryRegionTree() {
return virtual_tree;
}
const KMemoryRegionTree& GetVirtualMemoryRegionTree() const {
return virtual_tree;
}
KMemoryRegionTree& GetPhysicalMemoryRegionTree() {
return physical_tree;
}
const KMemoryRegionTree& GetPhysicalMemoryRegionTree() const {
return physical_tree;
}
KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() {
return virtual_linear_tree;
}
const KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() const {
return virtual_linear_tree;
}
KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() {
return physical_linear_tree;
}
const KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() const {
return physical_linear_tree;
}
VAddr GetLinearVirtualAddress(PAddr address) const {
return address + linear_phys_to_virt_diff;
}
PAddr GetLinearPhysicalAddress(VAddr address) const {
return address + linear_virt_to_phys_diff;
}
const KMemoryRegion* FindVirtual(VAddr address) const {
return Find(address, GetVirtualMemoryRegionTree());
}
const KMemoryRegion* FindPhysical(PAddr address) const {
return Find(address, GetPhysicalMemoryRegionTree());
}
const KMemoryRegion* FindVirtualLinear(VAddr address) const {
return Find(address, GetVirtualLinearMemoryRegionTree());
}
const KMemoryRegion* FindPhysicalLinear(PAddr address) const {
return Find(address, GetPhysicalLinearMemoryRegionTree());
}
VAddr GetMainStackTopAddress(s32 core_id) const {
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscMainStack);
}
VAddr GetIdleStackTopAddress(s32 core_id) const {
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscIdleStack);
}
VAddr GetExceptionStackTopAddress(s32 core_id) const {
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
}
VAddr GetSlabRegionAddress() const {
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
.GetAddress();
}
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
}
PAddr GetDevicePhysicalAddress(KMemoryRegionType type) const {
return GetDeviceRegion(type).GetAddress();
}
VAddr GetDeviceVirtualAddress(KMemoryRegionType type) const {
return GetDeviceRegion(type).GetPairAddress();
}
const KMemoryRegion& GetPoolManagementRegion() const {
return Dereference(
GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramPoolManagement));
}
const KMemoryRegion& GetPageTableHeapRegion() const {
return Dereference(
GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelPtHeap));
}
const KMemoryRegion& GetKernelStackRegion() const {
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelStack));
}
const KMemoryRegion& GetTempRegion() const {
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelTemp));
}
const KMemoryRegion& GetKernelTraceBufferRegion() const {
return Dereference(GetVirtualLinearMemoryRegionTree().FindByType(
KMemoryRegionType_VirtualDramKernelTraceBuffer));
}
const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const {
return Dereference(FindVirtualLinear(address));
}
const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const {
return Dereference(FindPhysicalLinear(address));
}
const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const {
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer);
}
const KMemoryRegion* GetPhysicalOnMemoryBootImageRegion() const {
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_OnMemoryBootImage);
}
const KMemoryRegion* GetPhysicalDTBRegion() const {
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DTB);
}
bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address) const {
return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(),
KMemoryRegionType_DramUserPool);
}
bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address) const {
return IsTypedAddress(region, address, GetVirtualLinearMemoryRegionTree(),
KMemoryRegionType_VirtualDramUserPool);
}
bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address, size_t size) const {
return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(),
KMemoryRegionType_DramUserPool);
}
bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address, size_t size) const {
return IsTypedAddress(region, address, size, GetVirtualLinearMemoryRegionTree(),
KMemoryRegionType_VirtualDramUserPool);
}
bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address) const {
return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(),
static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped));
}
bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address,
size_t size) const {
return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(),
static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped));
}
std::pair<size_t, size_t> GetTotalAndKernelMemorySizes() const {
size_t total_size = 0, kernel_size = 0;
for (const auto& region : GetPhysicalMemoryRegionTree()) {
if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
total_size += region.GetSize();
if (!region.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
kernel_size += region.GetSize();
}
}
}
return std::make_pair(total_size, kernel_size);
}
void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
VAddr linear_virtual_start);
static size_t GetResourceRegionSizeForInit();
auto GetKernelRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel);
}
auto GetKernelCodeRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode);
}
auto GetKernelStackRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelStack);
}
auto GetKernelMiscRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelMisc);
}
auto GetKernelSlabRegionExtents() const {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelSlab);
}
auto GetLinearRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionAttr_LinearMapped);
}
auto GetLinearRegionVirtualExtents() const {
const auto physical = GetLinearRegionPhysicalExtents();
return KMemoryRegion(GetLinearVirtualAddress(physical.GetAddress()),
GetLinearVirtualAddress(physical.GetLastAddress()), 0,
KMemoryRegionType_None);
}
auto GetMainMemoryPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Dram);
}
auto GetCarveoutRegionExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionAttr_CarveoutProtected);
}
auto GetKernelRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelBase);
}
auto GetKernelCodeRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelCode);
}
auto GetKernelSlabRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelSlab);
}
auto GetKernelPageTableHeapRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelPtHeap);
}
auto GetKernelInitPageTableRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramKernelInitPt);
}
auto GetKernelPoolManagementRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramPoolManagement);
}
auto GetKernelPoolPartitionRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramPoolPartition);
}
auto GetKernelSystemPoolRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramSystemPool);
}
auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramSystemNonSecurePool);
}
auto GetKernelAppletPoolRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramAppletPool);
}
auto GetKernelApplicationPoolRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_DramApplicationPool);
}
auto GetKernelTraceBufferRegionPhysicalExtents() const {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionType_KernelTraceBuffer);
}
private:
template <typename AddressType>
static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address,
const KMemoryRegionTree& tree, KMemoryRegionType type) {
// Check if the cached region already contains the address.
if (region != nullptr && region->Contains(address)) {
return true;
}
// Find the containing region, and update the cache.
if (const KMemoryRegion* found = tree.Find(address);
found != nullptr && found->IsDerivedFrom(type)) {
region = found;
return true;
} else {
return false;
}
}
template <typename AddressType>
static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address, size_t size,
const KMemoryRegionTree& tree, KMemoryRegionType type) {
// Get the end of the checked region.
const u64 last_address = address + size - 1;
// Walk the tree to verify the region is correct.
const KMemoryRegion* cur =
(region != nullptr && region->Contains(address)) ? region : tree.Find(address);
while (cur != nullptr && cur->IsDerivedFrom(type)) {
if (last_address <= cur->GetLastAddress()) {
region = cur;
return true;
}
cur = cur->GetNext();
}
return false;
}
template <typename AddressType>
static const KMemoryRegion* Find(AddressType address, const KMemoryRegionTree& tree) {
return tree.Find(address);
}
static KMemoryRegion& Dereference(KMemoryRegion* region) {
ASSERT(region != nullptr);
return *region;
}
static const KMemoryRegion& Dereference(const KMemoryRegion* region) {
ASSERT(region != nullptr);
return *region;
}
VAddr GetStackTopAddress(s32 core_id, KMemoryRegionType type) const {
const auto& region = Dereference(
GetVirtualMemoryRegionTree().FindByTypeAndAttribute(type, static_cast<u32>(core_id)));
ASSERT(region.GetEndAddress() != 0);
return region.GetEndAddress();
}
private:
u64 linear_phys_to_virt_diff{};
u64 linear_virt_to_phys_diff{};
KMemoryRegionAllocator memory_region_allocator;
KMemoryRegionTree virtual_tree;
KMemoryRegionTree physical_tree;
KMemoryRegionTree virtual_linear_tree;
KMemoryRegionTree physical_linear_tree;
};
namespace Init {
// These should be generic, regardless of board.
void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout);
// These may be implemented in a board-specific manner.
void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout);
void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout);
} // namespace Init
} // namespace Kernel

View File

@@ -1,420 +1,420 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/initial_process.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
if ((type | KMemoryRegionType_DramApplicationPool) == type) {
return KMemoryManager::Pool::Application;
} else if ((type | KMemoryRegionType_DramAppletPool) == type) {
return KMemoryManager::Pool::Applet;
} else if ((type | KMemoryRegionType_DramSystemPool) == type) {
return KMemoryManager::Pool::System;
} else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
return KMemoryManager::Pool::SystemNonSecure;
} else {
ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
return {};
}
}
} // namespace
KMemoryManager::KMemoryManager(Core::System& system_)
: system{system_}, pool_locks{
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
} {}
void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
// Clear the management region to zero.
const VAddr management_region_end = management_region + management_region_size;
// Reset our manager count.
num_managers = 0;
// Traverse the virtual memory layout tree, initializing each manager as appropriate.
while (num_managers != MaxManagerCount) {
// Locate the region that should initialize the current manager.
PAddr region_address = 0;
size_t region_size = 0;
Pool region_pool = Pool::Count;
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
// We only care about regions that we need to create managers for.
if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
continue;
}
// We want to initialize the managers in order.
if (it.GetAttributes() != num_managers) {
continue;
}
const PAddr cur_start = it.GetAddress();
const PAddr cur_end = it.GetEndAddress();
// Validate the region.
ASSERT(cur_end != 0);
ASSERT(cur_start != 0);
ASSERT(it.GetSize() > 0);
// Update the region's extents.
if (region_address == 0) {
region_address = cur_start;
region_size = it.GetSize();
region_pool = GetPoolFromMemoryRegionType(it.GetType());
} else {
ASSERT(cur_start == region_address + region_size);
// Update the size.
region_size = cur_end - region_address;
ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool);
}
}
// If we didn't find a region, we're done.
if (region_size == 0) {
break;
}
// Initialize a new manager for the region.
Impl* manager = std::addressof(managers[num_managers++]);
ASSERT(num_managers <= managers.size());
const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
management_region_end, region_pool);
management_region += cur_size;
ASSERT(management_region <= management_region_end);
// Insert the manager into the pool list.
const auto region_pool_index = static_cast<u32>(region_pool);
if (pool_managers_tail[region_pool_index] == nullptr) {
pool_managers_head[region_pool_index] = manager;
} else {
pool_managers_tail[region_pool_index]->SetNext(manager);
manager->SetPrev(pool_managers_tail[region_pool_index]);
}
pool_managers_tail[region_pool_index] = manager;
}
// Free each region to its corresponding heap.
size_t reserved_sizes[MaxManagerCount] = {};
const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
const PAddr ini_last = ini_end - 1;
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
// Get the manager for the region.
auto index = it.GetAttributes();
auto& manager = managers[index];
const PAddr cur_start = it.GetAddress();
const PAddr cur_last = it.GetLastAddress();
const PAddr cur_end = it.GetEndAddress();
if (cur_start <= ini_start && ini_last <= cur_last) {
// Free memory before the ini to the heap.
if (cur_start != ini_start) {
manager.Free(cur_start, (ini_start - cur_start) / PageSize);
}
// Open/reserve the ini memory.
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
// Free memory after the ini to the heap.
if (ini_last != cur_last) {
ASSERT(cur_end != 0);
manager.Free(ini_end, cur_end - ini_end);
}
} else {
// Ensure there's no partial overlap with the ini image.
if (cur_start <= ini_last) {
ASSERT(cur_last < ini_start);
} else {
// Otherwise, check the region for general validity.
ASSERT(cur_end != 0);
}
// Free the memory to the heap.
manager.Free(cur_start, it.GetSize() / PageSize);
}
}
}
// Update the used size for all managers.
for (size_t i = 0; i < num_managers; ++i) {
managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
}
}
PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
// Early return if we're allocating no pages.
if (num_pages == 0) {
return 0;
}
// Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]);
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
// Loop, trying to iterate from each block.
Impl* chosen_manager = nullptr;
PAddr allocated_block = 0;
for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
chosen_manager = this->GetNextManager(chosen_manager, dir)) {
allocated_block = chosen_manager->AllocateBlock(heap_index, true);
if (allocated_block != 0) {
break;
}
}
// If we failed to allocate, quit now.
if (allocated_block == 0) {
return 0;
}
// If we allocated more than we need, free some.
const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index);
if (allocated_pages > num_pages) {
chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
}
// Open the first reference to the pages.
chosen_manager->OpenFirst(allocated_block, num_pages);
return allocated_block;
}
Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool,
Direction dir, bool random) {
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
R_UNLESS(0 <= heap_index, ResultOutOfMemory);
// Ensure that we don't leave anything un-freed.
auto group_guard = SCOPE_GUARD({
for (const auto& it : out->Nodes()) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress());
const size_t num_pages_to_free =
std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
manager.Free(it.GetAddress(), num_pages_to_free);
}
});
// Keep allocating until we've allocated all our pages.
for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index);
for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr;
cur_manager = this->GetNextManager(cur_manager, dir)) {
while (num_pages >= pages_per_alloc) {
// Allocate a block.
PAddr allocated_block = cur_manager->AllocateBlock(index, random);
if (allocated_block == 0) {
break;
}
// Safely add it to our group.
{
auto block_guard =
SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); });
R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
block_guard.Cancel();
}
num_pages -= pages_per_alloc;
}
}
}
// Only succeed if we allocated as many pages as we wanted.
R_UNLESS(num_pages == 0, ResultOutOfMemory);
// We succeeded!
group_guard.Cancel();
return ResultSuccess;
}
Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
// Early return if we're allocating no pages.
R_SUCCEED_IF(num_pages == 0);
// Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
// Allocate the page group.
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
// Open the first reference to the pages.
for (const auto& block : out->Nodes()) {
PAddr cur_address = block.GetAddress();
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.OpenFirst(cur_address, cur_pages);
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
}
}
return ResultSuccess;
}
Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option,
u64 process_id, u8 fill_pattern) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
// Decode the option.
const auto [pool, dir] = DecodeOption(option);
// Allocate the memory.
{
// Lock the pool that we're allocating from.
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
// Allocate the page group.
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
// Open the first reference to the pages.
for (const auto& block : out->Nodes()) {
PAddr cur_address = block.GetAddress();
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.OpenFirst(cur_address, cur_pages);
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
}
}
}
// Set all the allocated memory.
for (const auto& block : out->Nodes()) {
std::memset(system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
block.GetSize());
}
return ResultSuccess;
}
void KMemoryManager::Open(PAddr address, size_t num_pages) {
// Repeatedly open references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Open(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void KMemoryManager::Close(PAddr address, size_t num_pages) {
// Repeatedly close references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Close(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void KMemoryManager::Close(const KPageGroup& pg) {
for (const auto& node : pg.Nodes()) {
Close(node.GetAddress(), node.GetNumPages());
}
}
void KMemoryManager::Open(const KPageGroup& pg) {
for (const auto& node : pg.Nodes()) {
Open(node.GetAddress(), node.GetNumPages());
}
}
size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
VAddr management_end, Pool p) {
// Calculate management sizes.
const size_t ref_count_size = (size / PageSize) * sizeof(u16);
const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size);
const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size);
const size_t total_management_size = manager_size + page_heap_size;
ASSERT(manager_size <= total_management_size);
ASSERT(management + total_management_size <= management_end);
ASSERT(Common::IsAligned(total_management_size, PageSize));
// Setup region.
pool = p;
management_region = management;
page_reference_counts.resize(
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
ASSERT(Common::IsAligned(management_region, PageSize));
// Initialize the manager's KPageHeap.
heap.Initialize(address, size, management + manager_size, page_heap_size);
return total_management_size;
}
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
const size_t optimize_map_size =
(Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
return manager_meta_size + page_heap_size;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/initial_process.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
if ((type | KMemoryRegionType_DramApplicationPool) == type) {
return KMemoryManager::Pool::Application;
} else if ((type | KMemoryRegionType_DramAppletPool) == type) {
return KMemoryManager::Pool::Applet;
} else if ((type | KMemoryRegionType_DramSystemPool) == type) {
return KMemoryManager::Pool::System;
} else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
return KMemoryManager::Pool::SystemNonSecure;
} else {
ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
return {};
}
}
} // namespace
KMemoryManager::KMemoryManager(Core::System& system_)
: system{system_}, pool_locks{
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
KLightLock{system_.Kernel()},
} {}
void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
// Clear the management region to zero.
const VAddr management_region_end = management_region + management_region_size;
// Reset our manager count.
num_managers = 0;
// Traverse the virtual memory layout tree, initializing each manager as appropriate.
while (num_managers != MaxManagerCount) {
// Locate the region that should initialize the current manager.
PAddr region_address = 0;
size_t region_size = 0;
Pool region_pool = Pool::Count;
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
// We only care about regions that we need to create managers for.
if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
continue;
}
// We want to initialize the managers in order.
if (it.GetAttributes() != num_managers) {
continue;
}
const PAddr cur_start = it.GetAddress();
const PAddr cur_end = it.GetEndAddress();
// Validate the region.
ASSERT(cur_end != 0);
ASSERT(cur_start != 0);
ASSERT(it.GetSize() > 0);
// Update the region's extents.
if (region_address == 0) {
region_address = cur_start;
region_size = it.GetSize();
region_pool = GetPoolFromMemoryRegionType(it.GetType());
} else {
ASSERT(cur_start == region_address + region_size);
// Update the size.
region_size = cur_end - region_address;
ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool);
}
}
// If we didn't find a region, we're done.
if (region_size == 0) {
break;
}
// Initialize a new manager for the region.
Impl* manager = std::addressof(managers[num_managers++]);
ASSERT(num_managers <= managers.size());
const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
management_region_end, region_pool);
management_region += cur_size;
ASSERT(management_region <= management_region_end);
// Insert the manager into the pool list.
const auto region_pool_index = static_cast<u32>(region_pool);
if (pool_managers_tail[region_pool_index] == nullptr) {
pool_managers_head[region_pool_index] = manager;
} else {
pool_managers_tail[region_pool_index]->SetNext(manager);
manager->SetPrev(pool_managers_tail[region_pool_index]);
}
pool_managers_tail[region_pool_index] = manager;
}
// Free each region to its corresponding heap.
size_t reserved_sizes[MaxManagerCount] = {};
const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
const PAddr ini_last = ini_end - 1;
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
// Get the manager for the region.
auto index = it.GetAttributes();
auto& manager = managers[index];
const PAddr cur_start = it.GetAddress();
const PAddr cur_last = it.GetLastAddress();
const PAddr cur_end = it.GetEndAddress();
if (cur_start <= ini_start && ini_last <= cur_last) {
// Free memory before the ini to the heap.
if (cur_start != ini_start) {
manager.Free(cur_start, (ini_start - cur_start) / PageSize);
}
// Open/reserve the ini memory.
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
// Free memory after the ini to the heap.
if (ini_last != cur_last) {
ASSERT(cur_end != 0);
manager.Free(ini_end, cur_end - ini_end);
}
} else {
// Ensure there's no partial overlap with the ini image.
if (cur_start <= ini_last) {
ASSERT(cur_last < ini_start);
} else {
// Otherwise, check the region for general validity.
ASSERT(cur_end != 0);
}
// Free the memory to the heap.
manager.Free(cur_start, it.GetSize() / PageSize);
}
}
}
// Update the used size for all managers.
for (size_t i = 0; i < num_managers; ++i) {
managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
}
}
PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
// Early return if we're allocating no pages.
if (num_pages == 0) {
return 0;
}
// Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]);
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
// Loop, trying to iterate from each block.
Impl* chosen_manager = nullptr;
PAddr allocated_block = 0;
for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
chosen_manager = this->GetNextManager(chosen_manager, dir)) {
allocated_block = chosen_manager->AllocateBlock(heap_index, true);
if (allocated_block != 0) {
break;
}
}
// If we failed to allocate, quit now.
if (allocated_block == 0) {
return 0;
}
// If we allocated more than we need, free some.
const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index);
if (allocated_pages > num_pages) {
chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
}
// Open the first reference to the pages.
chosen_manager->OpenFirst(allocated_block, num_pages);
return allocated_block;
}
Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool,
Direction dir, bool random) {
// Choose a heap based on our page size request.
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
R_UNLESS(0 <= heap_index, ResultOutOfMemory);
// Ensure that we don't leave anything un-freed.
auto group_guard = SCOPE_GUARD({
for (const auto& it : out->Nodes()) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress());
const size_t num_pages_to_free =
std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
manager.Free(it.GetAddress(), num_pages_to_free);
}
});
// Keep allocating until we've allocated all our pages.
for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index);
for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr;
cur_manager = this->GetNextManager(cur_manager, dir)) {
while (num_pages >= pages_per_alloc) {
// Allocate a block.
PAddr allocated_block = cur_manager->AllocateBlock(index, random);
if (allocated_block == 0) {
break;
}
// Safely add it to our group.
{
auto block_guard =
SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); });
R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
block_guard.Cancel();
}
num_pages -= pages_per_alloc;
}
}
}
// Only succeed if we allocated as many pages as we wanted.
R_UNLESS(num_pages == 0, ResultOutOfMemory);
// We succeeded!
group_guard.Cancel();
return ResultSuccess;
}
Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
// Early return if we're allocating no pages.
R_SUCCEED_IF(num_pages == 0);
// Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
// Allocate the page group.
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
// Open the first reference to the pages.
for (const auto& block : out->Nodes()) {
PAddr cur_address = block.GetAddress();
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.OpenFirst(cur_address, cur_pages);
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
}
}
return ResultSuccess;
}
Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option,
u64 process_id, u8 fill_pattern) {
ASSERT(out != nullptr);
ASSERT(out->GetNumPages() == 0);
// Decode the option.
const auto [pool, dir] = DecodeOption(option);
// Allocate the memory.
{
// Lock the pool that we're allocating from.
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
// Allocate the page group.
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
// Open the first reference to the pages.
for (const auto& block : out->Nodes()) {
PAddr cur_address = block.GetAddress();
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
// Get the manager for the current address.
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.OpenFirst(cur_address, cur_pages);
// Advance.
cur_address += cur_pages * PageSize;
remaining_pages -= cur_pages;
}
}
}
// Set all the allocated memory.
for (const auto& block : out->Nodes()) {
std::memset(system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
block.GetSize());
}
return ResultSuccess;
}
void KMemoryManager::Open(PAddr address, size_t num_pages) {
// Repeatedly open references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Open(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void KMemoryManager::Close(PAddr address, size_t num_pages) {
// Repeatedly close references until we've done so for all pages.
while (num_pages) {
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
{
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
manager.Close(address, cur_pages);
}
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void KMemoryManager::Close(const KPageGroup& pg) {
for (const auto& node : pg.Nodes()) {
Close(node.GetAddress(), node.GetNumPages());
}
}
void KMemoryManager::Open(const KPageGroup& pg) {
for (const auto& node : pg.Nodes()) {
Open(node.GetAddress(), node.GetNumPages());
}
}
size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
VAddr management_end, Pool p) {
// Calculate management sizes.
const size_t ref_count_size = (size / PageSize) * sizeof(u16);
const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size);
const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size);
const size_t total_management_size = manager_size + page_heap_size;
ASSERT(manager_size <= total_management_size);
ASSERT(management + total_management_size <= management_end);
ASSERT(Common::IsAligned(total_management_size, PageSize));
// Setup region.
pool = p;
management_region = management;
page_reference_counts.resize(
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
ASSERT(Common::IsAligned(management_region, PageSize));
// Initialize the manager's KPageHeap.
heap.Initialize(address, size, management + manager_size, page_heap_size);
return total_management_size;
}
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
const size_t optimize_map_size =
(Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
return manager_meta_size + page_heap_size;
}
} // namespace Kernel

View File

@@ -1,277 +1,277 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <tuple>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KPageGroup;
class KMemoryManager final {
public:
YUZU_NON_COPYABLE(KMemoryManager);
YUZU_NON_MOVEABLE(KMemoryManager);
enum class Pool : u32 {
Application = 0,
Applet = 1,
System = 2,
SystemNonSecure = 3,
Count,
Shift = 4,
Mask = (0xF << Shift),
// Aliases.
Unsafe = Application,
Secure = System,
};
enum class Direction : u32 {
FromFront = 0,
FromBack = 1,
Shift = 0,
Mask = (0xF << Shift),
};
explicit KMemoryManager(Core::System& system_);
void Initialize(VAddr management_region, size_t management_region_size);
constexpr size_t GetSize(Pool pool) const {
constexpr Direction GetSizeDirection = Direction::FromFront;
size_t total = 0;
for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
manager = this->GetNextManager(manager, GetSizeDirection)) {
total += manager->GetSize();
}
return total;
}
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
u8 fill_pattern);
static constexpr size_t MaxManagerCount = 10;
void Close(PAddr address, size_t num_pages);
void Close(const KPageGroup& pg);
void Open(PAddr address, size_t num_pages);
void Open(const KPageGroup& pg);
public:
static size_t CalculateManagementOverheadSize(size_t region_size) {
return Impl::CalculateManagementOverheadSize(region_size);
}
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 {
public:
YUZU_NON_COPYABLE(Impl);
YUZU_NON_MOVEABLE(Impl);
Impl() = default;
~Impl() = default;
size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
Pool p);
VAddr AllocateBlock(s32 index, bool random) {
return heap.AllocateBlock(index, random);
}
void Free(VAddr addr, size_t num_pages) {
heap.Free(addr, num_pages);
}
void SetInitialUsedHeapSize(size_t reserved_size) {
heap.SetInitialUsedSize(reserved_size);
}
constexpr Pool GetPool() const {
return pool;
}
constexpr size_t GetSize() const {
return heap.GetSize();
}
constexpr VAddr GetAddress() const {
return heap.GetAddress();
}
constexpr VAddr GetEndAddress() const {
return heap.GetEndAddress();
}
constexpr size_t GetPageOffset(PAddr address) const {
return heap.GetPageOffset(address);
}
constexpr size_t GetPageOffsetToEnd(PAddr address) const {
return heap.GetPageOffsetToEnd(address);
}
constexpr void SetNext(Impl* n) {
next = n;
}
constexpr void SetPrev(Impl* n) {
prev = n;
}
constexpr Impl* GetNext() const {
return next;
}
constexpr Impl* GetPrev() const {
return prev;
}
void OpenFirst(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++page_reference_counts[index]);
ASSERT(ref_count == 1);
index++;
}
}
void Open(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++page_reference_counts[index]);
ASSERT(ref_count > 1);
index++;
}
}
void Close(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
size_t free_start = 0;
size_t free_count = 0;
while (index < end) {
ASSERT(page_reference_counts[index] > 0);
const RefCount ref_count = (--page_reference_counts[index]);
// Keep track of how many zero refcounts we see in a row, to minimize calls to free.
if (ref_count == 0) {
if (free_count > 0) {
free_count++;
} else {
free_start = index;
free_count = 1;
}
} else {
if (free_count > 0) {
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
free_count = 0;
}
}
index++;
}
if (free_count > 0) {
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
}
}
static size_t CalculateManagementOverheadSize(size_t region_size);
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
}
private:
using RefCount = u16;
KPageHeap heap;
std::vector<RefCount> page_reference_counts;
VAddr management_region{};
Pool pool{};
Impl* next{};
Impl* prev{};
};
private:
Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) {
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
}
const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const {
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
}
constexpr Impl* GetFirstManager(Pool pool, Direction dir) const {
return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)]
: pool_managers_head[static_cast<size_t>(pool)];
}
constexpr Impl* GetNextManager(Impl* cur, Direction dir) const {
if (dir == Direction::FromBack) {
return cur->GetPrev();
} else {
return cur->GetNext();
}
}
Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir,
bool random);
private:
Core::System& system;
std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks;
std::array<Impl*, MaxManagerCount> pool_managers_head{};
std::array<Impl*, MaxManagerCount> pool_managers_tail{};
std::array<Impl, MaxManagerCount> managers;
size_t num_managers{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <tuple>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KPageGroup;
class KMemoryManager final {
public:
YUZU_NON_COPYABLE(KMemoryManager);
YUZU_NON_MOVEABLE(KMemoryManager);
enum class Pool : u32 {
Application = 0,
Applet = 1,
System = 2,
SystemNonSecure = 3,
Count,
Shift = 4,
Mask = (0xF << Shift),
// Aliases.
Unsafe = Application,
Secure = System,
};
enum class Direction : u32 {
FromFront = 0,
FromBack = 1,
Shift = 0,
Mask = (0xF << Shift),
};
explicit KMemoryManager(Core::System& system_);
void Initialize(VAddr management_region, size_t management_region_size);
constexpr size_t GetSize(Pool pool) const {
constexpr Direction GetSizeDirection = Direction::FromFront;
size_t total = 0;
for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
manager = this->GetNextManager(manager, GetSizeDirection)) {
total += manager->GetSize();
}
return total;
}
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
u8 fill_pattern);
static constexpr size_t MaxManagerCount = 10;
void Close(PAddr address, size_t num_pages);
void Close(const KPageGroup& pg);
void Open(PAddr address, size_t num_pages);
void Open(const KPageGroup& pg);
public:
static size_t CalculateManagementOverheadSize(size_t region_size) {
return Impl::CalculateManagementOverheadSize(region_size);
}
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 {
public:
YUZU_NON_COPYABLE(Impl);
YUZU_NON_MOVEABLE(Impl);
Impl() = default;
~Impl() = default;
size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
Pool p);
VAddr AllocateBlock(s32 index, bool random) {
return heap.AllocateBlock(index, random);
}
void Free(VAddr addr, size_t num_pages) {
heap.Free(addr, num_pages);
}
void SetInitialUsedHeapSize(size_t reserved_size) {
heap.SetInitialUsedSize(reserved_size);
}
constexpr Pool GetPool() const {
return pool;
}
constexpr size_t GetSize() const {
return heap.GetSize();
}
constexpr VAddr GetAddress() const {
return heap.GetAddress();
}
constexpr VAddr GetEndAddress() const {
return heap.GetEndAddress();
}
constexpr size_t GetPageOffset(PAddr address) const {
return heap.GetPageOffset(address);
}
constexpr size_t GetPageOffsetToEnd(PAddr address) const {
return heap.GetPageOffsetToEnd(address);
}
constexpr void SetNext(Impl* n) {
next = n;
}
constexpr void SetPrev(Impl* n) {
prev = n;
}
constexpr Impl* GetNext() const {
return next;
}
constexpr Impl* GetPrev() const {
return prev;
}
void OpenFirst(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++page_reference_counts[index]);
ASSERT(ref_count == 1);
index++;
}
}
void Open(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++page_reference_counts[index]);
ASSERT(ref_count > 1);
index++;
}
}
void Close(PAddr address, size_t num_pages) {
size_t index = this->GetPageOffset(address);
const size_t end = index + num_pages;
size_t free_start = 0;
size_t free_count = 0;
while (index < end) {
ASSERT(page_reference_counts[index] > 0);
const RefCount ref_count = (--page_reference_counts[index]);
// Keep track of how many zero refcounts we see in a row, to minimize calls to free.
if (ref_count == 0) {
if (free_count > 0) {
free_count++;
} else {
free_start = index;
free_count = 1;
}
} else {
if (free_count > 0) {
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
free_count = 0;
}
}
index++;
}
if (free_count > 0) {
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
}
}
static size_t CalculateManagementOverheadSize(size_t region_size);
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
}
private:
using RefCount = u16;
KPageHeap heap;
std::vector<RefCount> page_reference_counts;
VAddr management_region{};
Pool pool{};
Impl* next{};
Impl* prev{};
};
private:
Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) {
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
}
const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const {
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
}
constexpr Impl* GetFirstManager(Pool pool, Direction dir) const {
return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)]
: pool_managers_head[static_cast<size_t>(pool)];
}
constexpr Impl* GetNextManager(Impl* cur, Direction dir) const {
if (dir == Direction::FromBack) {
return cur->GetPrev();
} else {
return cur->GetNext();
}
}
Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir,
bool random);
private:
Core::System& system;
std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks;
std::array<Impl*, MaxManagerCount> pool_managers_head{};
std::array<Impl*, MaxManagerCount> pool_managers_tail{};
std::array<Impl, MaxManagerCount> managers;
size_t num_managers{};
};
} // namespace Kernel

View File

@@ -1,355 +1,355 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "core/hle/kernel/k_memory_region_type.h"
namespace Kernel {
class KMemoryRegionAllocator;
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> {
friend class KMemoryRegionTree;
public:
YUZU_NON_COPYABLE(KMemoryRegion);
YUZU_NON_MOVEABLE(KMemoryRegion);
constexpr KMemoryRegion() = default;
constexpr KMemoryRegion(u64 address_, u64 last_address_)
: address{address_}, last_address{last_address_} {}
constexpr KMemoryRegion(u64 address_, u64 last_address_, u64 pair_address_, u32 attributes_,
u32 type_id_)
: address(address_), last_address(last_address_), pair_address(pair_address_),
attributes(attributes_), type_id(type_id_) {}
constexpr KMemoryRegion(u64 address_, u64 last_address_, u32 attributes_, u32 type_id_)
: KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
type_id_) {}
~KMemoryRegion() = default;
static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
return 0;
} else {
return 1;
}
}
constexpr u64 GetAddress() const {
return address;
}
constexpr u64 GetPairAddress() const {
return pair_address;
}
constexpr u64 GetLastAddress() const {
return last_address;
}
constexpr u64 GetEndAddress() const {
return this->GetLastAddress() + 1;
}
constexpr size_t GetSize() const {
return this->GetEndAddress() - this->GetAddress();
}
constexpr u32 GetAttributes() const {
return attributes;
}
constexpr u32 GetType() const {
return type_id;
}
constexpr void SetType(u32 type) {
ASSERT(this->CanDerive(type));
type_id = type;
}
constexpr bool Contains(u64 addr) const {
ASSERT(this->GetEndAddress() != 0);
return this->GetAddress() <= addr && addr <= this->GetLastAddress();
}
constexpr bool IsDerivedFrom(u32 type) const {
return (this->GetType() | type) == this->GetType();
}
constexpr bool HasTypeAttribute(u32 attr) const {
return (this->GetType() | attr) == this->GetType();
}
constexpr bool CanDerive(u32 type) const {
return (this->GetType() | type) == type;
}
constexpr void SetPairAddress(u64 a) {
pair_address = a;
}
constexpr void SetTypeAttribute(u32 attr) {
type_id |= attr;
}
private:
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
address = a;
pair_address = p;
last_address = la;
attributes = r;
type_id = t;
}
u64 address{};
u64 last_address{};
u64 pair_address{};
u32 attributes{};
u32 type_id{};
};
class KMemoryRegionTree final {
private:
using TreeType =
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
public:
YUZU_NON_COPYABLE(KMemoryRegionTree);
YUZU_NON_MOVEABLE(KMemoryRegionTree);
using value_type = TreeType::value_type;
using size_type = TreeType::size_type;
using difference_type = TreeType::difference_type;
using pointer = TreeType::pointer;
using const_pointer = TreeType::const_pointer;
using reference = TreeType::reference;
using const_reference = TreeType::const_reference;
using iterator = TreeType::iterator;
using const_iterator = TreeType::const_iterator;
struct DerivedRegionExtents {
const KMemoryRegion* first_region{};
const KMemoryRegion* last_region{};
constexpr DerivedRegionExtents() = default;
constexpr u64 GetAddress() const {
return this->first_region->GetAddress();
}
constexpr u64 GetLastAddress() const {
return this->last_region->GetLastAddress();
}
constexpr u64 GetEndAddress() const {
return this->GetLastAddress() + 1;
}
constexpr size_t GetSize() const {
return this->GetEndAddress() - this->GetAddress();
}
};
explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
~KMemoryRegionTree() = default;
KMemoryRegion* FindModifiable(u64 address) {
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
return std::addressof(*it);
} else {
return nullptr;
}
}
const KMemoryRegion* Find(u64 address) const {
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->cend()) {
return std::addressof(*it);
} else {
return nullptr;
}
}
const KMemoryRegion* FindByType(KMemoryRegionType type_id) const {
for (auto it = this->cbegin(); it != this->cend(); ++it) {
if (it->GetType() == static_cast<u32>(type_id)) {
return std::addressof(*it);
}
}
return nullptr;
}
const KMemoryRegion* FindByTypeAndAttribute(u32 type_id, u32 attr) const {
for (auto it = this->cbegin(); it != this->cend(); ++it) {
if (it->GetType() == type_id && it->GetAttributes() == attr) {
return std::addressof(*it);
}
}
return nullptr;
}
const KMemoryRegion* FindFirstDerived(KMemoryRegionType type_id) const {
for (auto it = this->cbegin(); it != this->cend(); it++) {
if (it->IsDerivedFrom(type_id)) {
return std::addressof(*it);
}
}
return nullptr;
}
const KMemoryRegion* FindLastDerived(KMemoryRegionType type_id) const {
const KMemoryRegion* region = nullptr;
for (auto it = this->begin(); it != this->end(); it++) {
if (it->IsDerivedFrom(type_id)) {
region = std::addressof(*it);
}
}
return region;
}
DerivedRegionExtents GetDerivedRegionExtents(KMemoryRegionType type_id) const {
DerivedRegionExtents extents;
ASSERT(extents.first_region == nullptr);
ASSERT(extents.last_region == nullptr);
for (auto it = this->cbegin(); it != this->cend(); it++) {
if (it->IsDerivedFrom(type_id)) {
if (extents.first_region == nullptr) {
extents.first_region = std::addressof(*it);
}
extents.last_region = std::addressof(*it);
}
}
ASSERT(extents.first_region != nullptr);
ASSERT(extents.last_region != nullptr);
return extents;
}
DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const {
return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
}
void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
VAddr GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id);
VAddr GetRandomAlignedRegionWithGuard(size_t size, size_t alignment, u32 type_id,
size_t guard_size) {
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
}
// Iterator accessors.
iterator begin() {
return m_tree.begin();
}
const_iterator begin() const {
return m_tree.begin();
}
iterator end() {
return m_tree.end();
}
const_iterator end() const {
return m_tree.end();
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
iterator iterator_to(reference ref) {
return m_tree.iterator_to(ref);
}
const_iterator iterator_to(const_reference ref) const {
return m_tree.iterator_to(ref);
}
// Content management.
bool empty() const {
return m_tree.empty();
}
reference back() {
return m_tree.back();
}
const_reference back() const {
return m_tree.back();
}
reference front() {
return m_tree.front();
}
const_reference front() const {
return m_tree.front();
}
iterator insert(reference ref) {
return m_tree.insert(ref);
}
iterator erase(iterator it) {
return m_tree.erase(it);
}
iterator find(const_reference ref) const {
return m_tree.find(ref);
}
iterator nfind(const_reference ref) const {
return m_tree.nfind(ref);
}
private:
TreeType m_tree{};
KMemoryRegionAllocator& memory_region_allocator;
};
class KMemoryRegionAllocator final {
public:
YUZU_NON_COPYABLE(KMemoryRegionAllocator);
YUZU_NON_MOVEABLE(KMemoryRegionAllocator);
static constexpr size_t MaxMemoryRegions = 200;
constexpr KMemoryRegionAllocator() = default;
constexpr ~KMemoryRegionAllocator() = default;
template <typename... Args>
KMemoryRegion* Allocate(Args&&... args) {
// Ensure we stay within the bounds of our heap.
ASSERT(this->num_regions < MaxMemoryRegions);
// Create the new region.
KMemoryRegion* region = std::addressof(this->region_heap[this->num_regions++]);
new (region) KMemoryRegion(std::forward<Args>(args)...);
return region;
}
private:
std::array<KMemoryRegion, MaxMemoryRegions> region_heap{};
size_t num_regions{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "core/hle/kernel/k_memory_region_type.h"
namespace Kernel {
class KMemoryRegionAllocator;
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> {
friend class KMemoryRegionTree;
public:
YUZU_NON_COPYABLE(KMemoryRegion);
YUZU_NON_MOVEABLE(KMemoryRegion);
constexpr KMemoryRegion() = default;
constexpr KMemoryRegion(u64 address_, u64 last_address_)
: address{address_}, last_address{last_address_} {}
constexpr KMemoryRegion(u64 address_, u64 last_address_, u64 pair_address_, u32 attributes_,
u32 type_id_)
: address(address_), last_address(last_address_), pair_address(pair_address_),
attributes(attributes_), type_id(type_id_) {}
constexpr KMemoryRegion(u64 address_, u64 last_address_, u32 attributes_, u32 type_id_)
: KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
type_id_) {}
~KMemoryRegion() = default;
static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
return 0;
} else {
return 1;
}
}
constexpr u64 GetAddress() const {
return address;
}
constexpr u64 GetPairAddress() const {
return pair_address;
}
constexpr u64 GetLastAddress() const {
return last_address;
}
constexpr u64 GetEndAddress() const {
return this->GetLastAddress() + 1;
}
constexpr size_t GetSize() const {
return this->GetEndAddress() - this->GetAddress();
}
constexpr u32 GetAttributes() const {
return attributes;
}
constexpr u32 GetType() const {
return type_id;
}
constexpr void SetType(u32 type) {
ASSERT(this->CanDerive(type));
type_id = type;
}
constexpr bool Contains(u64 addr) const {
ASSERT(this->GetEndAddress() != 0);
return this->GetAddress() <= addr && addr <= this->GetLastAddress();
}
constexpr bool IsDerivedFrom(u32 type) const {
return (this->GetType() | type) == this->GetType();
}
constexpr bool HasTypeAttribute(u32 attr) const {
return (this->GetType() | attr) == this->GetType();
}
constexpr bool CanDerive(u32 type) const {
return (this->GetType() | type) == type;
}
constexpr void SetPairAddress(u64 a) {
pair_address = a;
}
constexpr void SetTypeAttribute(u32 attr) {
type_id |= attr;
}
private:
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
address = a;
pair_address = p;
last_address = la;
attributes = r;
type_id = t;
}
u64 address{};
u64 last_address{};
u64 pair_address{};
u32 attributes{};
u32 type_id{};
};
class KMemoryRegionTree final {
private:
using TreeType =
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
public:
YUZU_NON_COPYABLE(KMemoryRegionTree);
YUZU_NON_MOVEABLE(KMemoryRegionTree);
using value_type = TreeType::value_type;
using size_type = TreeType::size_type;
using difference_type = TreeType::difference_type;
using pointer = TreeType::pointer;
using const_pointer = TreeType::const_pointer;
using reference = TreeType::reference;
using const_reference = TreeType::const_reference;
using iterator = TreeType::iterator;
using const_iterator = TreeType::const_iterator;
struct DerivedRegionExtents {
const KMemoryRegion* first_region{};
const KMemoryRegion* last_region{};
constexpr DerivedRegionExtents() = default;
constexpr u64 GetAddress() const {
return this->first_region->GetAddress();
}
constexpr u64 GetLastAddress() const {
return this->last_region->GetLastAddress();
}
constexpr u64 GetEndAddress() const {
return this->GetLastAddress() + 1;
}
constexpr size_t GetSize() const {
return this->GetEndAddress() - this->GetAddress();
}
};
explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
~KMemoryRegionTree() = default;
KMemoryRegion* FindModifiable(u64 address) {
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
return std::addressof(*it);
} else {
return nullptr;
}
}
const KMemoryRegion* Find(u64 address) const {
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->cend()) {
return std::addressof(*it);
} else {
return nullptr;
}
}
const KMemoryRegion* FindByType(KMemoryRegionType type_id) const {
for (auto it = this->cbegin(); it != this->cend(); ++it) {
if (it->GetType() == static_cast<u32>(type_id)) {
return std::addressof(*it);
}
}
return nullptr;
}
const KMemoryRegion* FindByTypeAndAttribute(u32 type_id, u32 attr) const {
for (auto it = this->cbegin(); it != this->cend(); ++it) {
if (it->GetType() == type_id && it->GetAttributes() == attr) {
return std::addressof(*it);
}
}
return nullptr;
}
const KMemoryRegion* FindFirstDerived(KMemoryRegionType type_id) const {
for (auto it = this->cbegin(); it != this->cend(); it++) {
if (it->IsDerivedFrom(type_id)) {
return std::addressof(*it);
}
}
return nullptr;
}
const KMemoryRegion* FindLastDerived(KMemoryRegionType type_id) const {
const KMemoryRegion* region = nullptr;
for (auto it = this->begin(); it != this->end(); it++) {
if (it->IsDerivedFrom(type_id)) {
region = std::addressof(*it);
}
}
return region;
}
DerivedRegionExtents GetDerivedRegionExtents(KMemoryRegionType type_id) const {
DerivedRegionExtents extents;
ASSERT(extents.first_region == nullptr);
ASSERT(extents.last_region == nullptr);
for (auto it = this->cbegin(); it != this->cend(); it++) {
if (it->IsDerivedFrom(type_id)) {
if (extents.first_region == nullptr) {
extents.first_region = std::addressof(*it);
}
extents.last_region = std::addressof(*it);
}
}
ASSERT(extents.first_region != nullptr);
ASSERT(extents.last_region != nullptr);
return extents;
}
DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const {
return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
}
void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
VAddr GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id);
VAddr GetRandomAlignedRegionWithGuard(size_t size, size_t alignment, u32 type_id,
size_t guard_size) {
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
}
// Iterator accessors.
iterator begin() {
return m_tree.begin();
}
const_iterator begin() const {
return m_tree.begin();
}
iterator end() {
return m_tree.end();
}
const_iterator end() const {
return m_tree.end();
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
iterator iterator_to(reference ref) {
return m_tree.iterator_to(ref);
}
const_iterator iterator_to(const_reference ref) const {
return m_tree.iterator_to(ref);
}
// Content management.
bool empty() const {
return m_tree.empty();
}
reference back() {
return m_tree.back();
}
const_reference back() const {
return m_tree.back();
}
reference front() {
return m_tree.front();
}
const_reference front() const {
return m_tree.front();
}
iterator insert(reference ref) {
return m_tree.insert(ref);
}
iterator erase(iterator it) {
return m_tree.erase(it);
}
iterator find(const_reference ref) const {
return m_tree.find(ref);
}
iterator nfind(const_reference ref) const {
return m_tree.nfind(ref);
}
private:
TreeType m_tree{};
KMemoryRegionAllocator& memory_region_allocator;
};
class KMemoryRegionAllocator final {
public:
YUZU_NON_COPYABLE(KMemoryRegionAllocator);
YUZU_NON_MOVEABLE(KMemoryRegionAllocator);
static constexpr size_t MaxMemoryRegions = 200;
constexpr KMemoryRegionAllocator() = default;
constexpr ~KMemoryRegionAllocator() = default;
template <typename... Args>
KMemoryRegion* Allocate(Args&&... args) {
// Ensure we stay within the bounds of our heap.
ASSERT(this->num_regions < MaxMemoryRegions);
// Create the new region.
KMemoryRegion* region = std::addressof(this->region_heap[this->num_regions++]);
new (region) KMemoryRegion(std::forward<Args>(args)...);
return region;
}
private:
std::array<KMemoryRegion, MaxMemoryRegions> region_heap{};
size_t num_regions{};
};
} // namespace Kernel

View File

@@ -1,345 +1,345 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_util.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#define ARCH_ARM64
#define BOARD_NINTENDO_NX
namespace Kernel {
enum KMemoryRegionType : u32 {
KMemoryRegionAttr_CarveoutProtected = 0x02000000,
KMemoryRegionAttr_Uncached = 0x04000000,
KMemoryRegionAttr_DidKernelMap = 0x08000000,
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
KMemoryRegionAttr_UserReadOnly = 0x20000000,
KMemoryRegionAttr_NoUserMap = 0x40000000,
KMemoryRegionAttr_LinearMapped = 0x80000000,
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryRegionType);
namespace impl {
constexpr size_t BitsForDeriveSparse(size_t n) {
return n + 1;
}
constexpr size_t BitsForDeriveDense(size_t n) {
size_t low = 0, high = 1;
for (size_t i = 0; i < n - 1; ++i) {
if ((++low) == high) {
++high;
low = 0;
}
}
return high + 1;
}
class KMemoryRegionTypeValue {
public:
using ValueType = std::underlying_type_t<KMemoryRegionType>;
constexpr KMemoryRegionTypeValue() = default;
constexpr operator KMemoryRegionType() const {
return static_cast<KMemoryRegionType>(m_value);
}
constexpr ValueType GetValue() const {
return m_value;
}
constexpr const KMemoryRegionTypeValue& Finalize() {
m_finalized = true;
return *this;
}
constexpr const KMemoryRegionTypeValue& SetSparseOnly() {
m_sparse_only = true;
return *this;
}
constexpr const KMemoryRegionTypeValue& SetDenseOnly() {
m_dense_only = true;
return *this;
}
constexpr KMemoryRegionTypeValue& SetAttribute(u32 attr) {
m_value |= attr;
return *this;
}
constexpr KMemoryRegionTypeValue DeriveInitial(
size_t i, size_t next = Common::BitSize<ValueType>()) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_value = (ValueType{1} << i);
new_type.m_next_bit = next;
return new_type;
}
constexpr KMemoryRegionTypeValue DeriveAttribute(u32 attr) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_value |= attr;
return new_type;
}
constexpr KMemoryRegionTypeValue DeriveTransition(size_t ofs = 0, size_t adv = 1) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs));
new_type.m_next_bit += adv;
return new_type;
}
constexpr KMemoryRegionTypeValue DeriveSparse(size_t ofs, size_t n, size_t i) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs));
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs + 1 + i));
new_type.m_next_bit += ofs + n + 1;
return new_type;
}
constexpr KMemoryRegionTypeValue Derive(size_t n, size_t i) const {
size_t low = 0, high = 1;
for (size_t j = 0; j < i; ++j) {
if ((++low) == high) {
++high;
low = 0;
}
}
KMemoryRegionTypeValue new_type = *this;
new_type.m_value |= (ValueType{1} << (m_next_bit + low));
new_type.m_value |= (ValueType{1} << (m_next_bit + high));
new_type.m_next_bit += BitsForDeriveDense(n);
return new_type;
}
constexpr KMemoryRegionTypeValue Advance(size_t n) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_next_bit += n;
return new_type;
}
constexpr bool IsAncestorOf(ValueType v) const {
return (m_value | v) == v;
}
private:
constexpr KMemoryRegionTypeValue(ValueType v) : m_value(v) {}
private:
ValueType m_value{};
size_t m_next_bit{};
bool m_finalized{};
bool m_sparse_only{};
bool m_dense_only{};
};
} // namespace impl
constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1);
static_assert(KMemoryRegionType_Dram.GetValue() == 0x2);
constexpr auto KMemoryRegionType_DramKernelBase =
KMemoryRegionType_Dram.DeriveSparse(0, 3, 0)
.SetAttribute(KMemoryRegionAttr_NoUserMap)
.SetAttribute(KMemoryRegionAttr_CarveoutProtected);
constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
constexpr auto KMemoryRegionType_DramHeapBase =
KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped);
static_assert(KMemoryRegionType_DramKernelBase.GetValue() ==
(0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16));
static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped));
constexpr auto KMemoryRegionType_DramKernelCode =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0);
constexpr auto KMemoryRegionType_DramKernelSlab =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1);
constexpr auto KMemoryRegionType_DramKernelPtHeap =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute(
KMemoryRegionAttr_LinearMapped);
constexpr auto KMemoryRegionType_DramKernelInitPt =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute(
KMemoryRegionAttr_LinearMapped);
static_assert(KMemoryRegionType_DramKernelCode.GetValue() ==
(0xCE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramKernelSlab.GetValue() ==
(0x14E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramKernelPtHeap.GetValue() ==
(0x24E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_LinearMapped));
static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
(0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_LinearMapped));
constexpr auto KMemoryRegionType_DramReservedEarly =
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
static_assert(KMemoryRegionType_DramReservedEarly.GetValue() ==
(0x16 | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_KernelTraceBuffer =
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0)
.SetAttribute(KMemoryRegionAttr_LinearMapped)
.SetAttribute(KMemoryRegionAttr_UserReadOnly);
constexpr auto KMemoryRegionType_OnMemoryBootImage =
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1);
constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() ==
(0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly));
static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156);
static_assert(KMemoryRegionType_DTB.GetValue() == 0x256);
constexpr auto KMemoryRegionType_DramPoolPartition =
KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_DramPoolManagement =
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
KMemoryRegionAttr_CarveoutProtected);
constexpr auto KMemoryRegionType_DramUserPool =
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_CarveoutProtected));
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0);
constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1);
constexpr auto KMemoryRegionType_DramSystemNonSecurePool =
KMemoryRegionType_DramUserPool.Derive(4, 2);
constexpr auto KMemoryRegionType_DramSystemPool =
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
(0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
(0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_CarveoutProtected));
constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
// UNUSED: .DeriveSparse(2, 2, 0);
constexpr auto KMemoryRegionType_VirtualDramUnknownDebug =
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
constexpr auto KMemoryRegionType_VirtualDramUserPool =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
// NOTE: For unknown reason, the pools are derived out-of-order here. It's worth eventually trying
// to understand why Nintendo made this choice.
// UNUSED: .Derive(6, 0);
// UNUSED: .Derive(6, 1);
constexpr auto KMemoryRegionType_VirtualDramAppletPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
constexpr auto KMemoryRegionType_VirtualDramApplicationPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
constexpr auto KMemoryRegionType_VirtualDramSystemPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
constexpr auto KMemoryRegionType_ArchDeviceBase =
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
constexpr auto KMemoryRegionType_BoardDeviceBase =
KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly();
static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5);
static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
#if defined(ARCH_ARM64)
#include "core/hle/kernel/arch/arm64/k_memory_region_device_types.inc"
#elif defined(ARCH_ARM)
#error "Unimplemented"
#else
// Default to no architecture devices.
constexpr auto NumArchitectureDeviceRegions = 0;
#endif
static_assert(NumArchitectureDeviceRegions >= 0);
#if defined(BOARD_NINTENDO_NX)
#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc"
#else
// Default to no board devices.
constexpr auto NumBoardDeviceRegions = 0;
#endif
static_assert(NumBoardDeviceRegions >= 0);
constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19);
static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29);
static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49);
static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89);
constexpr auto KMemoryRegionType_KernelMiscDerivedBase =
KMemoryRegionType_KernelMisc.DeriveTransition();
static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149);
// UNUSED: .Derive(7, 0);
constexpr auto KMemoryRegionType_KernelMiscMainStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1);
constexpr auto KMemoryRegionType_KernelMiscMappedDevice =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2);
constexpr auto KMemoryRegionType_KernelMiscExceptionStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3);
constexpr auto KMemoryRegionType_KernelMiscUnknownDebug =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4);
// UNUSED: .Derive(7, 5);
constexpr auto KMemoryRegionType_KernelMiscIdleStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6);
static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49);
static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49);
static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349);
static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549);
static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349);
constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelPtHeap;
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
return KMemoryRegionType_VirtualDramUnknownDebug;
} else {
return KMemoryRegionType_Dram;
}
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_util.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#define ARCH_ARM64
#define BOARD_NINTENDO_NX
namespace Kernel {
enum KMemoryRegionType : u32 {
KMemoryRegionAttr_CarveoutProtected = 0x02000000,
KMemoryRegionAttr_Uncached = 0x04000000,
KMemoryRegionAttr_DidKernelMap = 0x08000000,
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
KMemoryRegionAttr_UserReadOnly = 0x20000000,
KMemoryRegionAttr_NoUserMap = 0x40000000,
KMemoryRegionAttr_LinearMapped = 0x80000000,
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryRegionType);
namespace impl {
constexpr size_t BitsForDeriveSparse(size_t n) {
return n + 1;
}
constexpr size_t BitsForDeriveDense(size_t n) {
size_t low = 0, high = 1;
for (size_t i = 0; i < n - 1; ++i) {
if ((++low) == high) {
++high;
low = 0;
}
}
return high + 1;
}
class KMemoryRegionTypeValue {
public:
using ValueType = std::underlying_type_t<KMemoryRegionType>;
constexpr KMemoryRegionTypeValue() = default;
constexpr operator KMemoryRegionType() const {
return static_cast<KMemoryRegionType>(m_value);
}
constexpr ValueType GetValue() const {
return m_value;
}
constexpr const KMemoryRegionTypeValue& Finalize() {
m_finalized = true;
return *this;
}
constexpr const KMemoryRegionTypeValue& SetSparseOnly() {
m_sparse_only = true;
return *this;
}
constexpr const KMemoryRegionTypeValue& SetDenseOnly() {
m_dense_only = true;
return *this;
}
constexpr KMemoryRegionTypeValue& SetAttribute(u32 attr) {
m_value |= attr;
return *this;
}
constexpr KMemoryRegionTypeValue DeriveInitial(
size_t i, size_t next = Common::BitSize<ValueType>()) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_value = (ValueType{1} << i);
new_type.m_next_bit = next;
return new_type;
}
constexpr KMemoryRegionTypeValue DeriveAttribute(u32 attr) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_value |= attr;
return new_type;
}
constexpr KMemoryRegionTypeValue DeriveTransition(size_t ofs = 0, size_t adv = 1) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs));
new_type.m_next_bit += adv;
return new_type;
}
constexpr KMemoryRegionTypeValue DeriveSparse(size_t ofs, size_t n, size_t i) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs));
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs + 1 + i));
new_type.m_next_bit += ofs + n + 1;
return new_type;
}
constexpr KMemoryRegionTypeValue Derive(size_t n, size_t i) const {
size_t low = 0, high = 1;
for (size_t j = 0; j < i; ++j) {
if ((++low) == high) {
++high;
low = 0;
}
}
KMemoryRegionTypeValue new_type = *this;
new_type.m_value |= (ValueType{1} << (m_next_bit + low));
new_type.m_value |= (ValueType{1} << (m_next_bit + high));
new_type.m_next_bit += BitsForDeriveDense(n);
return new_type;
}
constexpr KMemoryRegionTypeValue Advance(size_t n) const {
KMemoryRegionTypeValue new_type = *this;
new_type.m_next_bit += n;
return new_type;
}
constexpr bool IsAncestorOf(ValueType v) const {
return (m_value | v) == v;
}
private:
constexpr KMemoryRegionTypeValue(ValueType v) : m_value(v) {}
private:
ValueType m_value{};
size_t m_next_bit{};
bool m_finalized{};
bool m_sparse_only{};
bool m_dense_only{};
};
} // namespace impl
constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1);
static_assert(KMemoryRegionType_Dram.GetValue() == 0x2);
constexpr auto KMemoryRegionType_DramKernelBase =
KMemoryRegionType_Dram.DeriveSparse(0, 3, 0)
.SetAttribute(KMemoryRegionAttr_NoUserMap)
.SetAttribute(KMemoryRegionAttr_CarveoutProtected);
constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
constexpr auto KMemoryRegionType_DramHeapBase =
KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped);
static_assert(KMemoryRegionType_DramKernelBase.GetValue() ==
(0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16));
static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped));
constexpr auto KMemoryRegionType_DramKernelCode =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0);
constexpr auto KMemoryRegionType_DramKernelSlab =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1);
constexpr auto KMemoryRegionType_DramKernelPtHeap =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute(
KMemoryRegionAttr_LinearMapped);
constexpr auto KMemoryRegionType_DramKernelInitPt =
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute(
KMemoryRegionAttr_LinearMapped);
static_assert(KMemoryRegionType_DramKernelCode.GetValue() ==
(0xCE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramKernelSlab.GetValue() ==
(0x14E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramKernelPtHeap.GetValue() ==
(0x24E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_LinearMapped));
static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
(0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_LinearMapped));
constexpr auto KMemoryRegionType_DramReservedEarly =
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
static_assert(KMemoryRegionType_DramReservedEarly.GetValue() ==
(0x16 | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_KernelTraceBuffer =
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0)
.SetAttribute(KMemoryRegionAttr_LinearMapped)
.SetAttribute(KMemoryRegionAttr_UserReadOnly);
constexpr auto KMemoryRegionType_OnMemoryBootImage =
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1);
constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() ==
(0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly));
static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156);
static_assert(KMemoryRegionType_DTB.GetValue() == 0x256);
constexpr auto KMemoryRegionType_DramPoolPartition =
KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_DramPoolManagement =
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
KMemoryRegionAttr_CarveoutProtected);
constexpr auto KMemoryRegionType_DramUserPool =
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_CarveoutProtected));
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0);
constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1);
constexpr auto KMemoryRegionType_DramSystemNonSecurePool =
KMemoryRegionType_DramUserPool.Derive(4, 2);
constexpr auto KMemoryRegionType_DramSystemPool =
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
(0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
(0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
KMemoryRegionAttr_CarveoutProtected));
constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
// UNUSED: .DeriveSparse(2, 2, 0);
constexpr auto KMemoryRegionType_VirtualDramUnknownDebug =
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
constexpr auto KMemoryRegionType_VirtualDramUserPool =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
// NOTE: For unknown reason, the pools are derived out-of-order here. It's worth eventually trying
// to understand why Nintendo made this choice.
// UNUSED: .Derive(6, 0);
// UNUSED: .Derive(6, 1);
constexpr auto KMemoryRegionType_VirtualDramAppletPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
constexpr auto KMemoryRegionType_VirtualDramApplicationPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
constexpr auto KMemoryRegionType_VirtualDramSystemPool =
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
constexpr auto KMemoryRegionType_ArchDeviceBase =
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
constexpr auto KMemoryRegionType_BoardDeviceBase =
KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly();
static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5);
static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
#if defined(ARCH_ARM64)
#include "core/hle/kernel/arch/arm64/k_memory_region_device_types.inc"
#elif defined(ARCH_ARM)
#error "Unimplemented"
#else
// Default to no architecture devices.
constexpr auto NumArchitectureDeviceRegions = 0;
#endif
static_assert(NumArchitectureDeviceRegions >= 0);
#if defined(BOARD_NINTENDO_NX)
#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc"
#else
// Default to no board devices.
constexpr auto NumBoardDeviceRegions = 0;
#endif
static_assert(NumBoardDeviceRegions >= 0);
constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19);
static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29);
static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49);
static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89);
constexpr auto KMemoryRegionType_KernelMiscDerivedBase =
KMemoryRegionType_KernelMisc.DeriveTransition();
static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149);
// UNUSED: .Derive(7, 0);
constexpr auto KMemoryRegionType_KernelMiscMainStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1);
constexpr auto KMemoryRegionType_KernelMiscMappedDevice =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2);
constexpr auto KMemoryRegionType_KernelMiscExceptionStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3);
constexpr auto KMemoryRegionType_KernelMiscUnknownDebug =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4);
// UNUSED: .Derive(7, 5);
constexpr auto KMemoryRegionType_KernelMiscIdleStack =
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6);
static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49);
static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49);
static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349);
static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549);
static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349);
constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelPtHeap;
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
return KMemoryRegionType_VirtualDramUnknownDebug;
} else {
return KMemoryRegionType_Dram;
}
}
} // namespace Kernel

View File

@@ -1,278 +1,278 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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

@@ -1,18 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/assert.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) {
ASSERT(Common::IsAligned(phys_addr, PageSize));
return system.DeviceMemory().GetPointer<KPageBuffer>(phys_addr);
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/assert.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/memory_types.h"
namespace Kernel {
KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) {
ASSERT(Common::IsAligned(phys_addr, PageSize));
return system.DeviceMemory().GetPointer<KPageBuffer>(phys_addr);
}
} // namespace Kernel

View File

@@ -1,28 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
public:
explicit KPageBuffer(KernelCore&) {}
KPageBuffer() = default;
static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
private:
[[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
};
static_assert(sizeof(KPageBuffer) == PageSize);
static_assert(alignof(KPageBuffer) == PageSize);
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
public:
explicit KPageBuffer(KernelCore&) {}
KPageBuffer() = default;
static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
private:
[[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
};
static_assert(sizeof(KPageBuffer) == PageSize);
static_assert(alignof(KPageBuffer) == PageSize);
} // namespace Kernel

View File

@@ -1,99 +1,99 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/result.h"
namespace Kernel {
class KPageGroup final {
public:
class Node final {
public:
constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {}
constexpr u64 GetAddress() const {
return addr;
}
constexpr std::size_t GetNumPages() const {
return num_pages;
}
constexpr std::size_t GetSize() const {
return GetNumPages() * PageSize;
}
private:
u64 addr{};
std::size_t num_pages{};
};
public:
KPageGroup() = default;
KPageGroup(u64 address, u64 num_pages) {
ASSERT(AddBlock(address, num_pages).IsSuccess());
}
constexpr std::list<Node>& Nodes() {
return nodes;
}
constexpr const std::list<Node>& Nodes() const {
return nodes;
}
std::size_t GetNumPages() const {
std::size_t num_pages = 0;
for (const Node& node : nodes) {
num_pages += node.GetNumPages();
}
return num_pages;
}
bool IsEqual(KPageGroup& other) const {
auto this_node = nodes.begin();
auto other_node = other.nodes.begin();
while (this_node != nodes.end() && other_node != other.nodes.end()) {
if (this_node->GetAddress() != other_node->GetAddress() ||
this_node->GetNumPages() != other_node->GetNumPages()) {
return false;
}
this_node = std::next(this_node);
other_node = std::next(other_node);
}
return this_node == nodes.end() && other_node == other.nodes.end();
}
Result AddBlock(u64 address, u64 num_pages) {
if (!num_pages) {
return ResultSuccess;
}
if (!nodes.empty()) {
const auto node = nodes.back();
if (node.GetAddress() + node.GetNumPages() * PageSize == address) {
address = node.GetAddress();
num_pages += node.GetNumPages();
nodes.pop_back();
}
}
nodes.push_back({address, num_pages});
return ResultSuccess;
}
bool Empty() const {
return nodes.empty();
}
private:
std::list<Node> nodes;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/result.h"
namespace Kernel {
class KPageGroup final {
public:
class Node final {
public:
constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {}
constexpr u64 GetAddress() const {
return addr;
}
constexpr std::size_t GetNumPages() const {
return num_pages;
}
constexpr std::size_t GetSize() const {
return GetNumPages() * PageSize;
}
private:
u64 addr{};
std::size_t num_pages{};
};
public:
KPageGroup() = default;
KPageGroup(u64 address, u64 num_pages) {
ASSERT(AddBlock(address, num_pages).IsSuccess());
}
constexpr std::list<Node>& Nodes() {
return nodes;
}
constexpr const std::list<Node>& Nodes() const {
return nodes;
}
std::size_t GetNumPages() const {
std::size_t num_pages = 0;
for (const Node& node : nodes) {
num_pages += node.GetNumPages();
}
return num_pages;
}
bool IsEqual(KPageGroup& other) const {
auto this_node = nodes.begin();
auto other_node = other.nodes.begin();
while (this_node != nodes.end() && other_node != other.nodes.end()) {
if (this_node->GetAddress() != other_node->GetAddress() ||
this_node->GetNumPages() != other_node->GetNumPages()) {
return false;
}
this_node = std::next(this_node);
other_node = std::next(other_node);
}
return this_node == nodes.end() && other_node == other.nodes.end();
}
Result AddBlock(u64 address, u64 num_pages) {
if (!num_pages) {
return ResultSuccess;
}
if (!nodes.empty()) {
const auto node = nodes.back();
if (node.GetAddress() + node.GetNumPages() * PageSize == address) {
address = node.GetAddress();
num_pages += node.GetNumPages();
nodes.pop_back();
}
}
nodes.push_back({address, num_pages});
return ResultSuccess;
}
bool Empty() const {
return nodes.empty();
}
private:
std::list<Node> nodes;
};
} // namespace Kernel

View File

@@ -1,130 +1,130 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/kernel/k_page_heap.h"
namespace Kernel {
void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address,
size_t management_size, const size_t* block_shifts,
size_t num_block_shifts) {
// Check our assumptions.
ASSERT(Common::IsAligned(address, PageSize));
ASSERT(Common::IsAligned(size, PageSize));
ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts);
const VAddr management_end = management_address + management_size;
// Set our members.
m_heap_address = address;
m_heap_size = size;
m_num_blocks = num_block_shifts;
// Setup bitmaps.
m_management_data.resize(management_size / sizeof(u64));
u64* cur_bitmap_storage{m_management_data.data()};
for (size_t i = 0; i < num_block_shifts; i++) {
const size_t cur_block_shift = block_shifts[i];
const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift,
next_block_shift, cur_bitmap_storage);
}
// Ensure we didn't overextend our bounds.
ASSERT(VAddr(cur_bitmap_storage) <= management_end);
}
size_t KPageHeap::GetNumFreePages() const {
size_t num_free = 0;
for (size_t i = 0; i < m_num_blocks; i++) {
num_free += m_blocks[i].GetNumFreePages();
}
return num_free;
}
PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
const size_t needed_size = m_blocks[index].GetSize();
for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) {
if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
}
return addr;
}
}
return 0;
}
void KPageHeap::FreeBlock(PAddr block, s32 index) {
do {
block = m_blocks[index++].PushBlock(block);
} while (block != 0);
}
void KPageHeap::Free(PAddr addr, size_t num_pages) {
// Freeing no pages is a no-op.
if (num_pages == 0) {
return;
}
// Find the largest block size that we can free, and free as many as possible.
s32 big_index = static_cast<s32>(m_num_blocks) - 1;
const PAddr start = addr;
const PAddr end = addr + num_pages * PageSize;
PAddr before_start = start;
PAddr before_end = start;
PAddr after_start = end;
PAddr after_end = end;
while (big_index >= 0) {
const size_t block_size = m_blocks[big_index].GetSize();
const PAddr big_start = Common::AlignUp(start, block_size);
const PAddr big_end = Common::AlignDown(end, block_size);
if (big_start < big_end) {
// Free as many big blocks as we can.
for (auto block = big_start; block < big_end; block += block_size) {
this->FreeBlock(block, big_index);
}
before_end = big_start;
after_start = big_end;
break;
}
big_index--;
}
ASSERT(big_index >= 0);
// Free space before the big blocks.
for (s32 i = big_index - 1; i >= 0; i--) {
const size_t block_size = m_blocks[i].GetSize();
while (before_start + block_size <= before_end) {
before_end -= block_size;
this->FreeBlock(before_end, i);
}
}
// Free space after the big blocks.
for (s32 i = big_index - 1; i >= 0; i--) {
const size_t block_size = m_blocks[i].GetSize();
while (after_start + block_size <= after_end) {
this->FreeBlock(after_start, i);
after_start += block_size;
}
}
}
size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
size_t num_block_shifts) {
size_t overhead_size = 0;
for (size_t i = 0; i < num_block_shifts; i++) {
const size_t cur_block_shift = block_shifts[i];
const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
region_size, cur_block_shift, next_block_shift);
}
return Common::AlignUp(overhead_size, PageSize);
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/kernel/k_page_heap.h"
namespace Kernel {
void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address,
size_t management_size, const size_t* block_shifts,
size_t num_block_shifts) {
// Check our assumptions.
ASSERT(Common::IsAligned(address, PageSize));
ASSERT(Common::IsAligned(size, PageSize));
ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts);
const VAddr management_end = management_address + management_size;
// Set our members.
m_heap_address = address;
m_heap_size = size;
m_num_blocks = num_block_shifts;
// Setup bitmaps.
m_management_data.resize(management_size / sizeof(u64));
u64* cur_bitmap_storage{m_management_data.data()};
for (size_t i = 0; i < num_block_shifts; i++) {
const size_t cur_block_shift = block_shifts[i];
const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift,
next_block_shift, cur_bitmap_storage);
}
// Ensure we didn't overextend our bounds.
ASSERT(VAddr(cur_bitmap_storage) <= management_end);
}
size_t KPageHeap::GetNumFreePages() const {
size_t num_free = 0;
for (size_t i = 0; i < m_num_blocks; i++) {
num_free += m_blocks[i].GetNumFreePages();
}
return num_free;
}
PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
const size_t needed_size = m_blocks[index].GetSize();
for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) {
if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
}
return addr;
}
}
return 0;
}
void KPageHeap::FreeBlock(PAddr block, s32 index) {
do {
block = m_blocks[index++].PushBlock(block);
} while (block != 0);
}
void KPageHeap::Free(PAddr addr, size_t num_pages) {
// Freeing no pages is a no-op.
if (num_pages == 0) {
return;
}
// Find the largest block size that we can free, and free as many as possible.
s32 big_index = static_cast<s32>(m_num_blocks) - 1;
const PAddr start = addr;
const PAddr end = addr + num_pages * PageSize;
PAddr before_start = start;
PAddr before_end = start;
PAddr after_start = end;
PAddr after_end = end;
while (big_index >= 0) {
const size_t block_size = m_blocks[big_index].GetSize();
const PAddr big_start = Common::AlignUp(start, block_size);
const PAddr big_end = Common::AlignDown(end, block_size);
if (big_start < big_end) {
// Free as many big blocks as we can.
for (auto block = big_start; block < big_end; block += block_size) {
this->FreeBlock(block, big_index);
}
before_end = big_start;
after_start = big_end;
break;
}
big_index--;
}
ASSERT(big_index >= 0);
// Free space before the big blocks.
for (s32 i = big_index - 1; i >= 0; i--) {
const size_t block_size = m_blocks[i].GetSize();
while (before_start + block_size <= before_end) {
before_end -= block_size;
this->FreeBlock(before_end, i);
}
}
// Free space after the big blocks.
for (s32 i = big_index - 1; i >= 0; i--) {
const size_t block_size = m_blocks[i].GetSize();
while (after_start + block_size <= after_end) {
this->FreeBlock(after_start, i);
after_start += block_size;
}
}
}
size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
size_t num_block_shifts) {
size_t overhead_size = 0;
for (size_t i = 0; i < num_block_shifts; i++) {
const size_t cur_block_shift = block_shifts[i];
const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
region_size, cur_block_shift, next_block_shift);
}
return Common::AlignUp(overhead_size, PageSize);
}
} // namespace Kernel

View File

@@ -1,216 +1,216 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "common/alignment.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 {
public:
YUZU_NON_COPYABLE(KPageHeap);
YUZU_NON_MOVEABLE(KPageHeap);
KPageHeap() = default;
~KPageHeap() = default;
constexpr PAddr GetAddress() const {
return m_heap_address;
}
constexpr size_t GetSize() const {
return m_heap_size;
}
constexpr PAddr GetEndAddress() const {
return this->GetAddress() + this->GetSize();
}
constexpr size_t GetPageOffset(PAddr block) const {
return (block - this->GetAddress()) / PageSize;
}
constexpr size_t GetPageOffsetToEnd(PAddr block) const {
return (this->GetEndAddress() - block) / PageSize;
}
void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
size_t management_size) {
return this->Initialize(heap_address, heap_size, management_address, management_size,
MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts);
}
size_t GetFreeSize() const {
return this->GetNumFreePages() * PageSize;
}
void SetInitialUsedSize(size_t reserved_size) {
// Check that the reserved size is valid.
const size_t free_size = this->GetNumFreePages() * PageSize;
ASSERT(m_heap_size >= free_size + reserved_size);
// Set the initial used size.
m_initial_used_size = m_heap_size - free_size - reserved_size;
}
PAddr AllocateBlock(s32 index, bool random);
void Free(PAddr addr, size_t num_pages);
static size_t CalculateManagementOverheadSize(size_t region_size) {
return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(),
NumMemoryBlockPageShifts);
}
static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
const size_t target_pages = std::max(num_pages, align_pages);
for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
return -1;
}
static constexpr s32 GetBlockIndex(size_t num_pages) {
for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
return -1;
}
static constexpr size_t GetBlockSize(size_t index) {
return size_t(1) << MemoryBlockPageShifts[index];
}
static constexpr size_t GetBlockNumPages(size_t index) {
return GetBlockSize(index) / PageSize;
}
private:
class Block final {
public:
YUZU_NON_COPYABLE(Block);
YUZU_NON_MOVEABLE(Block);
Block() = default;
~Block() = default;
constexpr size_t GetShift() const {
return m_block_shift;
}
constexpr size_t GetNextShift() const {
return m_next_block_shift;
}
constexpr size_t GetSize() const {
return u64(1) << this->GetShift();
}
constexpr size_t GetNumPages() const {
return this->GetSize() / PageSize;
}
constexpr size_t GetNumFreeBlocks() const {
return m_bitmap.GetNumBits();
}
constexpr size_t GetNumFreePages() const {
return this->GetNumFreeBlocks() * this->GetNumPages();
}
u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) {
// Set shifts.
m_block_shift = bs;
m_next_block_shift = nbs;
// Align up the address.
PAddr end = addr + size;
const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift)
: (u64(1) << m_block_shift);
addr = Common::AlignDown(addr, align);
end = Common::AlignUp(end, align);
m_heap_address = addr;
m_end_offset = (end - addr) / (u64(1) << m_block_shift);
return m_bitmap.Initialize(bit_storage, m_end_offset);
}
PAddr PushBlock(PAddr address) {
// Set the bit for the free block.
size_t offset = (address - m_heap_address) >> this->GetShift();
m_bitmap.SetBit(offset);
// If we have a next shift, try to clear the blocks below this one and return the new
// address.
if (this->GetNextShift()) {
const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift());
offset = Common::AlignDown(offset, diff);
if (m_bitmap.ClearRange(offset, diff)) {
return m_heap_address + (offset << this->GetShift());
}
}
// We couldn't coalesce, or we're already as big as possible.
return {};
}
PAddr PopBlock(bool random) {
// Find a free block.
s64 soffset = m_bitmap.FindFreeBlock(random);
if (soffset < 0) {
return {};
}
const size_t offset = static_cast<size_t>(soffset);
// Update our tracking and return it.
m_bitmap.ClearBit(offset);
return m_heap_address + (offset << this->GetShift());
}
public:
static constexpr size_t CalculateManagementOverheadSize(size_t region_size,
size_t cur_block_shift,
size_t next_block_shift) {
const size_t cur_block_size = (u64(1) << cur_block_shift);
const size_t next_block_size = (u64(1) << next_block_shift);
const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size;
return KPageBitmap::CalculateManagementOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
private:
KPageBitmap m_bitmap;
PAddr m_heap_address{};
uintptr_t m_end_offset{};
size_t m_block_shift{};
size_t m_next_block_shift{};
};
private:
void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
size_t management_size, const size_t* block_shifts, size_t num_block_shifts);
size_t GetNumFreePages() const;
void FreeBlock(PAddr block, s32 index);
static constexpr size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
private:
static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
size_t num_block_shifts);
private:
PAddr m_heap_address{};
size_t m_heap_size{};
size_t m_initial_used_size{};
size_t m_num_blocks{};
std::array<Block, NumMemoryBlockPageShifts> m_blocks{};
std::vector<u64> m_management_data;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "common/alignment.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 {
public:
YUZU_NON_COPYABLE(KPageHeap);
YUZU_NON_MOVEABLE(KPageHeap);
KPageHeap() = default;
~KPageHeap() = default;
constexpr PAddr GetAddress() const {
return m_heap_address;
}
constexpr size_t GetSize() const {
return m_heap_size;
}
constexpr PAddr GetEndAddress() const {
return this->GetAddress() + this->GetSize();
}
constexpr size_t GetPageOffset(PAddr block) const {
return (block - this->GetAddress()) / PageSize;
}
constexpr size_t GetPageOffsetToEnd(PAddr block) const {
return (this->GetEndAddress() - block) / PageSize;
}
void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
size_t management_size) {
return this->Initialize(heap_address, heap_size, management_address, management_size,
MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts);
}
size_t GetFreeSize() const {
return this->GetNumFreePages() * PageSize;
}
void SetInitialUsedSize(size_t reserved_size) {
// Check that the reserved size is valid.
const size_t free_size = this->GetNumFreePages() * PageSize;
ASSERT(m_heap_size >= free_size + reserved_size);
// Set the initial used size.
m_initial_used_size = m_heap_size - free_size - reserved_size;
}
PAddr AllocateBlock(s32 index, bool random);
void Free(PAddr addr, size_t num_pages);
static size_t CalculateManagementOverheadSize(size_t region_size) {
return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(),
NumMemoryBlockPageShifts);
}
static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
const size_t target_pages = std::max(num_pages, align_pages);
for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
return -1;
}
static constexpr s32 GetBlockIndex(size_t num_pages) {
for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
return -1;
}
static constexpr size_t GetBlockSize(size_t index) {
return size_t(1) << MemoryBlockPageShifts[index];
}
static constexpr size_t GetBlockNumPages(size_t index) {
return GetBlockSize(index) / PageSize;
}
private:
class Block final {
public:
YUZU_NON_COPYABLE(Block);
YUZU_NON_MOVEABLE(Block);
Block() = default;
~Block() = default;
constexpr size_t GetShift() const {
return m_block_shift;
}
constexpr size_t GetNextShift() const {
return m_next_block_shift;
}
constexpr size_t GetSize() const {
return u64(1) << this->GetShift();
}
constexpr size_t GetNumPages() const {
return this->GetSize() / PageSize;
}
constexpr size_t GetNumFreeBlocks() const {
return m_bitmap.GetNumBits();
}
constexpr size_t GetNumFreePages() const {
return this->GetNumFreeBlocks() * this->GetNumPages();
}
u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) {
// Set shifts.
m_block_shift = bs;
m_next_block_shift = nbs;
// Align up the address.
PAddr end = addr + size;
const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift)
: (u64(1) << m_block_shift);
addr = Common::AlignDown(addr, align);
end = Common::AlignUp(end, align);
m_heap_address = addr;
m_end_offset = (end - addr) / (u64(1) << m_block_shift);
return m_bitmap.Initialize(bit_storage, m_end_offset);
}
PAddr PushBlock(PAddr address) {
// Set the bit for the free block.
size_t offset = (address - m_heap_address) >> this->GetShift();
m_bitmap.SetBit(offset);
// If we have a next shift, try to clear the blocks below this one and return the new
// address.
if (this->GetNextShift()) {
const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift());
offset = Common::AlignDown(offset, diff);
if (m_bitmap.ClearRange(offset, diff)) {
return m_heap_address + (offset << this->GetShift());
}
}
// We couldn't coalesce, or we're already as big as possible.
return {};
}
PAddr PopBlock(bool random) {
// Find a free block.
s64 soffset = m_bitmap.FindFreeBlock(random);
if (soffset < 0) {
return {};
}
const size_t offset = static_cast<size_t>(soffset);
// Update our tracking and return it.
m_bitmap.ClearBit(offset);
return m_heap_address + (offset << this->GetShift());
}
public:
static constexpr size_t CalculateManagementOverheadSize(size_t region_size,
size_t cur_block_shift,
size_t next_block_shift) {
const size_t cur_block_size = (u64(1) << cur_block_shift);
const size_t next_block_size = (u64(1) << next_block_shift);
const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size;
return KPageBitmap::CalculateManagementOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
private:
KPageBitmap m_bitmap;
PAddr m_heap_address{};
uintptr_t m_end_offset{};
size_t m_block_shift{};
size_t m_next_block_shift{};
};
private:
void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
size_t management_size, const size_t* block_shifts, size_t num_block_shifts);
size_t GetNumFreePages() const;
void FreeBlock(PAddr block, s32 index);
static constexpr size_t NumMemoryBlockPageShifts{7};
static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
private:
static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
size_t num_block_shifts);
private:
PAddr m_heap_address{};
size_t m_heap_size{};
size_t m_initial_used_size{};
size_t m_num_blocks{};
std::array<Block, NumMemoryBlockPageShifts> m_blocks{};
std::vector<u64> m_management_data;
};
} // namespace Kernel

File diff suppressed because it is too large Load Diff

View File

@@ -1,374 +1,374 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/page_table.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KMemoryBlockManager;
class KPageTable final {
public:
enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
explicit KPageTable(Core::System& system_);
~KPageTable();
Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
VAddr code_addr, size_t code_size,
KMemoryBlockSlabManager* mem_block_slab_manager,
KMemoryManager::Pool pool);
void Finalize();
Result MapProcessCode(VAddr addr, size_t pages_count, KMemoryState state,
KMemoryPermission perm);
Result MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size);
Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size,
ICacheInvalidationStrategy icache_invalidation_strategy);
Result UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table,
VAddr src_addr);
Result MapPhysicalMemory(VAddr addr, size_t size);
Result UnmapPhysicalMemory(VAddr addr, size_t size);
Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
KMemoryPermission perm);
Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);
Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(VAddr addr);
Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);
Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);
Result SetMaxHeapSize(size_t size);
Result SetHeapSize(VAddr* out, size_t size);
ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only,
VAddr region_start, size_t region_num_pages,
KMemoryState state, KMemoryPermission perm,
PAddr map_addr = 0);
Result LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm,
bool is_aligned);
Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size);
Result UnlockForDeviceAddressSpace(VAddr addr, size_t size);
Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size);
Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg);
Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Common::PageTable& PageTableImpl() {
return *m_page_table_impl;
}
const Common::PageTable& PageTableImpl() const {
return *m_page_table_impl;
}
bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
private:
enum class OperationType : u32 {
Map,
MapGroup,
Unmap,
ChangePermissions,
ChangePermissionsAndRefresh,
};
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm);
Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
bool is_pa_valid, VAddr region_start, size_t region_num_pages,
KMemoryState state, KMemoryPermission perm);
Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
bool IsRegionContiguous(VAddr addr, u64 size) const;
void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);
KMemoryInfo QueryInfoImpl(VAddr addr);
VAddr AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages,
size_t align);
Result Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group,
OperationType operation);
Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation,
PAddr map_addr = 0);
VAddr GetRegionAddress(KMemoryState state) const;
size_t GetRegionSize(KMemoryState state) const;
VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages,
size_t alignment, size_t offset, size_t guard_pages);
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryStateContiguous(VAddr addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr));
}
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed, VAddr addr,
size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr,
ignore_attr));
}
Result CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr));
}
Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr);
Result UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr,
const KPageGroup* pg);
Result MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages);
bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages);
bool IsLockedByCurrentThread() const {
return m_general_lock.IsLockedByCurrentThread();
}
bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr);
}
bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
ASSERT(this->IsLockedByCurrentThread());
*out = GetPhysicalAddr(virt_addr);
return *out != 0;
}
mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock;
public:
constexpr VAddr GetAddressSpaceStart() const {
return m_address_space_start;
}
constexpr VAddr GetAddressSpaceEnd() const {
return m_address_space_end;
}
constexpr size_t GetAddressSpaceSize() const {
return m_address_space_end - m_address_space_start;
}
constexpr VAddr GetHeapRegionStart() const {
return m_heap_region_start;
}
constexpr VAddr GetHeapRegionEnd() const {
return m_heap_region_end;
}
constexpr size_t GetHeapRegionSize() const {
return m_heap_region_end - m_heap_region_start;
}
constexpr VAddr GetAliasRegionStart() const {
return m_alias_region_start;
}
constexpr VAddr GetAliasRegionEnd() const {
return m_alias_region_end;
}
constexpr size_t GetAliasRegionSize() const {
return m_alias_region_end - m_alias_region_start;
}
constexpr VAddr GetStackRegionStart() const {
return m_stack_region_start;
}
constexpr VAddr GetStackRegionEnd() const {
return m_stack_region_end;
}
constexpr size_t GetStackRegionSize() const {
return m_stack_region_end - m_stack_region_start;
}
constexpr VAddr GetKernelMapRegionStart() const {
return m_kernel_map_region_start;
}
constexpr VAddr GetKernelMapRegionEnd() const {
return m_kernel_map_region_end;
}
constexpr VAddr GetCodeRegionStart() const {
return m_code_region_start;
}
constexpr VAddr GetCodeRegionEnd() const {
return m_code_region_end;
}
constexpr VAddr GetAliasCodeRegionStart() const {
return m_alias_code_region_start;
}
constexpr VAddr GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
size_t GetNormalMemorySize() {
KScopedLightLock lk(m_general_lock);
return GetHeapSize() + m_mapped_physical_memory_size;
}
constexpr size_t GetAddressSpaceWidth() const {
return m_address_space_width;
}
constexpr size_t GetHeapSize() const {
return m_current_heap_end - m_heap_region_start;
}
constexpr bool IsInsideAddressSpace(VAddr address, size_t size) const {
return m_address_space_start <= address && address + size - 1 <= m_address_space_end - 1;
}
constexpr bool IsOutsideAliasRegion(VAddr address, size_t size) const {
return m_alias_region_start > address || address + size - 1 > m_alias_region_end - 1;
}
constexpr bool IsOutsideStackRegion(VAddr address, size_t size) const {
return m_stack_region_start > address || address + size - 1 > m_stack_region_end - 1;
}
constexpr bool IsInvalidRegion(VAddr address, size_t size) const {
return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1;
}
constexpr bool IsInsideHeapRegion(VAddr address, size_t size) const {
return address + size > m_heap_region_start && m_heap_region_end > address;
}
constexpr bool IsInsideAliasRegion(VAddr address, size_t size) const {
return address + size > m_alias_region_start && m_alias_region_end > address;
}
constexpr bool IsOutsideASLRRegion(VAddr address, size_t size) const {
if (IsInvalidRegion(address, size)) {
return true;
}
if (IsInsideHeapRegion(address, size)) {
return true;
}
if (IsInsideAliasRegion(address, size)) {
return true;
}
return {};
}
constexpr bool IsInsideASLRRegion(VAddr address, size_t size) const {
return !IsOutsideASLRRegion(address, size);
}
constexpr size_t GetNumGuardPages() const {
return IsKernel() ? 1 : 4;
}
PAddr GetPhysicalAddr(VAddr addr) const {
const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits];
ASSERT(backing_addr);
return backing_addr + addr;
}
constexpr bool Contains(VAddr addr) const {
return m_address_space_start <= addr && addr <= m_address_space_end - 1;
}
constexpr bool Contains(VAddr addr, size_t size) const {
return m_address_space_start <= addr && addr < addr + size &&
addr + size - 1 <= m_address_space_end - 1;
}
private:
constexpr bool IsKernel() const {
return m_is_kernel;
}
constexpr bool IsAslrEnabled() const {
return m_enable_aslr;
}
constexpr bool ContainsPages(VAddr addr, size_t num_pages) const {
return (m_address_space_start <= addr) &&
(num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
}
private:
VAddr m_address_space_start{};
VAddr m_address_space_end{};
VAddr m_heap_region_start{};
VAddr m_heap_region_end{};
VAddr m_current_heap_end{};
VAddr m_alias_region_start{};
VAddr m_alias_region_end{};
VAddr m_stack_region_start{};
VAddr m_stack_region_end{};
VAddr m_kernel_map_region_start{};
VAddr m_kernel_map_region_end{};
VAddr m_code_region_start{};
VAddr m_code_region_end{};
VAddr m_alias_code_region_start{};
VAddr m_alias_code_region_end{};
size_t m_mapped_physical_memory_size{};
size_t m_max_heap_size{};
size_t m_max_physical_memory_size{};
size_t m_address_space_width{};
KMemoryBlockManager m_memory_block_manager;
bool m_is_kernel{};
bool m_enable_aslr{};
bool m_enable_device_address_space_merge{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{};
u32 m_heap_fill_value{};
const KMemoryRegion* m_cached_physical_heap_region{};
KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront};
std::unique_ptr<Common::PageTable> m_page_table_impl;
Core::System& m_system;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/page_table.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_dynamic_resource_manager.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KMemoryBlockManager;
class KPageTable final {
public:
enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
explicit KPageTable(Core::System& system_);
~KPageTable();
Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
VAddr code_addr, size_t code_size,
KMemoryBlockSlabManager* mem_block_slab_manager,
KMemoryManager::Pool pool);
void Finalize();
Result MapProcessCode(VAddr addr, size_t pages_count, KMemoryState state,
KMemoryPermission perm);
Result MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size);
Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size,
ICacheInvalidationStrategy icache_invalidation_strategy);
Result UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table,
VAddr src_addr);
Result MapPhysicalMemory(VAddr addr, size_t size);
Result UnmapPhysicalMemory(VAddr addr, size_t size);
Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
KMemoryPermission perm);
Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
KMemoryState state, KMemoryPermission perm) {
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
this->GetRegionAddress(state),
this->GetRegionSize(state) / PageSize, state, perm));
}
Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);
Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(VAddr addr);
Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);
Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);
Result SetMaxHeapSize(size_t size);
Result SetHeapSize(VAddr* out, size_t size);
ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only,
VAddr region_start, size_t region_num_pages,
KMemoryState state, KMemoryPermission perm,
PAddr map_addr = 0);
Result LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm,
bool is_aligned);
Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size);
Result UnlockForDeviceAddressSpace(VAddr addr, size_t size);
Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size);
Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg);
Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr);
Common::PageTable& PageTableImpl() {
return *m_page_table_impl;
}
const Common::PageTable& PageTableImpl() const {
return *m_page_table_impl;
}
bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
private:
enum class OperationType : u32 {
Map,
MapGroup,
Unmap,
ChangePermissions,
ChangePermissionsAndRefresh,
};
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm);
Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
bool is_pa_valid, VAddr region_start, size_t region_num_pages,
KMemoryState state, KMemoryPermission perm);
Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
bool IsRegionContiguous(VAddr addr, u64 size) const;
void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);
KMemoryInfo QueryInfoImpl(VAddr addr);
VAddr AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages,
size_t align);
Result Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group,
OperationType operation);
Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation,
PAddr map_addr = 0);
VAddr GetRegionAddress(KMemoryState state) const;
size_t GetRegionSize(KMemoryState state) const;
VAddr FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages,
size_t alignment, size_t offset, size_t guard_pages);
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryStateContiguous(VAddr addr, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask,
KMemoryPermission perm, KMemoryAttribute attr_mask,
KMemoryAttribute attr) const {
R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr));
}
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
KMemoryAttribute* out_attr, size_t* out_blocks_needed, VAddr addr,
size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
Result CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr,
ignore_attr));
}
Result CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr));
}
Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr);
Result UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr,
KMemoryPermission new_perm, KMemoryAttribute lock_attr,
const KPageGroup* pg);
Result MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages);
bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages);
bool IsLockedByCurrentThread() const {
return m_general_lock.IsLockedByCurrentThread();
}
bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return layout.IsHeapPhysicalAddress(m_cached_physical_heap_region, phys_addr);
}
bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
ASSERT(this->IsLockedByCurrentThread());
*out = GetPhysicalAddr(virt_addr);
return *out != 0;
}
mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock;
public:
constexpr VAddr GetAddressSpaceStart() const {
return m_address_space_start;
}
constexpr VAddr GetAddressSpaceEnd() const {
return m_address_space_end;
}
constexpr size_t GetAddressSpaceSize() const {
return m_address_space_end - m_address_space_start;
}
constexpr VAddr GetHeapRegionStart() const {
return m_heap_region_start;
}
constexpr VAddr GetHeapRegionEnd() const {
return m_heap_region_end;
}
constexpr size_t GetHeapRegionSize() const {
return m_heap_region_end - m_heap_region_start;
}
constexpr VAddr GetAliasRegionStart() const {
return m_alias_region_start;
}
constexpr VAddr GetAliasRegionEnd() const {
return m_alias_region_end;
}
constexpr size_t GetAliasRegionSize() const {
return m_alias_region_end - m_alias_region_start;
}
constexpr VAddr GetStackRegionStart() const {
return m_stack_region_start;
}
constexpr VAddr GetStackRegionEnd() const {
return m_stack_region_end;
}
constexpr size_t GetStackRegionSize() const {
return m_stack_region_end - m_stack_region_start;
}
constexpr VAddr GetKernelMapRegionStart() const {
return m_kernel_map_region_start;
}
constexpr VAddr GetKernelMapRegionEnd() const {
return m_kernel_map_region_end;
}
constexpr VAddr GetCodeRegionStart() const {
return m_code_region_start;
}
constexpr VAddr GetCodeRegionEnd() const {
return m_code_region_end;
}
constexpr VAddr GetAliasCodeRegionStart() const {
return m_alias_code_region_start;
}
constexpr VAddr GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
size_t GetNormalMemorySize() {
KScopedLightLock lk(m_general_lock);
return GetHeapSize() + m_mapped_physical_memory_size;
}
constexpr size_t GetAddressSpaceWidth() const {
return m_address_space_width;
}
constexpr size_t GetHeapSize() const {
return m_current_heap_end - m_heap_region_start;
}
constexpr bool IsInsideAddressSpace(VAddr address, size_t size) const {
return m_address_space_start <= address && address + size - 1 <= m_address_space_end - 1;
}
constexpr bool IsOutsideAliasRegion(VAddr address, size_t size) const {
return m_alias_region_start > address || address + size - 1 > m_alias_region_end - 1;
}
constexpr bool IsOutsideStackRegion(VAddr address, size_t size) const {
return m_stack_region_start > address || address + size - 1 > m_stack_region_end - 1;
}
constexpr bool IsInvalidRegion(VAddr address, size_t size) const {
return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1;
}
constexpr bool IsInsideHeapRegion(VAddr address, size_t size) const {
return address + size > m_heap_region_start && m_heap_region_end > address;
}
constexpr bool IsInsideAliasRegion(VAddr address, size_t size) const {
return address + size > m_alias_region_start && m_alias_region_end > address;
}
constexpr bool IsOutsideASLRRegion(VAddr address, size_t size) const {
if (IsInvalidRegion(address, size)) {
return true;
}
if (IsInsideHeapRegion(address, size)) {
return true;
}
if (IsInsideAliasRegion(address, size)) {
return true;
}
return {};
}
constexpr bool IsInsideASLRRegion(VAddr address, size_t size) const {
return !IsOutsideASLRRegion(address, size);
}
constexpr size_t GetNumGuardPages() const {
return IsKernel() ? 1 : 4;
}
PAddr GetPhysicalAddr(VAddr addr) const {
const auto backing_addr = m_page_table_impl->backing_addr[addr >> PageBits];
ASSERT(backing_addr);
return backing_addr + addr;
}
constexpr bool Contains(VAddr addr) const {
return m_address_space_start <= addr && addr <= m_address_space_end - 1;
}
constexpr bool Contains(VAddr addr, size_t size) const {
return m_address_space_start <= addr && addr < addr + size &&
addr + size - 1 <= m_address_space_end - 1;
}
private:
constexpr bool IsKernel() const {
return m_is_kernel;
}
constexpr bool IsAslrEnabled() const {
return m_enable_aslr;
}
constexpr bool ContainsPages(VAddr addr, size_t num_pages) const {
return (m_address_space_start <= addr) &&
(num_pages <= (m_address_space_end - m_address_space_start) / PageSize) &&
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
}
private:
VAddr m_address_space_start{};
VAddr m_address_space_end{};
VAddr m_heap_region_start{};
VAddr m_heap_region_end{};
VAddr m_current_heap_end{};
VAddr m_alias_region_start{};
VAddr m_alias_region_end{};
VAddr m_stack_region_start{};
VAddr m_stack_region_end{};
VAddr m_kernel_map_region_start{};
VAddr m_kernel_map_region_end{};
VAddr m_code_region_start{};
VAddr m_code_region_end{};
VAddr m_alias_code_region_start{};
VAddr m_alias_code_region_end{};
size_t m_mapped_physical_memory_size{};
size_t m_max_heap_size{};
size_t m_max_physical_memory_size{};
size_t m_address_space_width{};
KMemoryBlockManager m_memory_block_manager;
bool m_is_kernel{};
bool m_enable_aslr{};
bool m_enable_device_address_space_merge{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{};
u32 m_heap_fill_value{};
const KMemoryRegion* m_cached_physical_heap_region{};
KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
KMemoryManager::Direction m_allocation_option{KMemoryManager::Direction::FromFront};
std::unique_ptr<Common::PageTable> m_page_table_impl;
Core::System& m_system;
};
} // namespace Kernel

View File

@@ -1,63 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
KPort::KPort(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
KPort::~KPort() = default;
void KPort::Initialize(s32 max_sessions_, bool is_light_, const std::string& name_) {
// Open a new reference count to the initialized port.
Open();
// Create and initialize our server/client pair.
KAutoObject::Create(std::addressof(server));
KAutoObject::Create(std::addressof(client));
server.Initialize(this, name_ + ":Server");
client.Initialize(this, max_sessions_, name_ + ":Client");
// Set our member variables.
is_light = is_light_;
name = name_;
state = State::Normal;
}
void KPort::OnClientClosed() {
KScopedSchedulerLock sl{kernel};
if (state == State::Normal) {
state = State::ClientClosed;
}
}
void KPort::OnServerClosed() {
KScopedSchedulerLock sl{kernel};
if (state == State::Normal) {
state = State::ServerClosed;
}
}
bool KPort::IsServerClosed() const {
KScopedSchedulerLock sl{kernel};
return state == State::ServerClosed;
}
Result KPort::EnqueueSession(KServerSession* session) {
KScopedSchedulerLock sl{kernel};
R_UNLESS(state == State::Normal, ResultPortClosed);
server.EnqueueSession(session);
return ResultSuccess;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
KPort::KPort(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
KPort::~KPort() = default;
void KPort::Initialize(s32 max_sessions_, bool is_light_, const std::string& name_) {
// Open a new reference count to the initialized port.
Open();
// Create and initialize our server/client pair.
KAutoObject::Create(std::addressof(server));
KAutoObject::Create(std::addressof(client));
server.Initialize(this, name_ + ":Server");
client.Initialize(this, max_sessions_, name_ + ":Client");
// Set our member variables.
is_light = is_light_;
name = name_;
state = State::Normal;
}
void KPort::OnClientClosed() {
KScopedSchedulerLock sl{kernel};
if (state == State::Normal) {
state = State::ClientClosed;
}
}
void KPort::OnServerClosed() {
KScopedSchedulerLock sl{kernel};
if (state == State::Normal) {
state = State::ServerClosed;
}
}
bool KPort::IsServerClosed() const {
KScopedSchedulerLock sl{kernel};
return state == State::ServerClosed;
}
Result KPort::EnqueueSession(KServerSession* session) {
KScopedSchedulerLock sl{kernel};
R_UNLESS(state == State::Normal, ResultPortClosed);
server.EnqueueSession(session);
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -1,66 +1,66 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Kernel {
class KServerSession;
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KPort, KAutoObject);
public:
explicit KPort(KernelCore& kernel_);
~KPort() override;
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
void Initialize(s32 max_sessions_, bool is_light_, const std::string& name_);
void OnClientClosed();
void OnServerClosed();
bool IsLight() const {
return is_light;
}
bool IsServerClosed() const;
Result EnqueueSession(KServerSession* session);
KClientPort& GetClientPort() {
return client;
}
KServerPort& GetServerPort() {
return server;
}
const KClientPort& GetClientPort() const {
return client;
}
const KServerPort& GetServerPort() const {
return server;
}
private:
enum class State : u8 {
Invalid = 0,
Normal = 1,
ClientClosed = 2,
ServerClosed = 3,
};
KServerPort server;
KClientPort client;
State state{State::Invalid};
bool is_light{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Kernel {
class KServerSession;
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KPort, KAutoObject);
public:
explicit KPort(KernelCore& kernel_);
~KPort() override;
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
void Initialize(s32 max_sessions_, bool is_light_, const std::string& name_);
void OnClientClosed();
void OnServerClosed();
bool IsLight() const {
return is_light;
}
bool IsServerClosed() const;
Result EnqueueSession(KServerSession* session);
KClientPort& GetClientPort() {
return client;
}
KServerPort& GetServerPort() {
return server;
}
const KClientPort& GetClientPort() const {
return client;
}
const KServerPort& GetServerPort() const {
return server;
}
private:
enum class State : u8 {
Invalid = 0,
Normal = 1,
ClientClosed = 2,
ServerClosed = 3,
};
KServerPort server;
KClientPort client;
State state{State::Invalid};
bool is_light{};
};
} // namespace Kernel

View File

@@ -1,476 +1,476 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <bit>
#include <concepts>
#include "common/assert.h"
#include "common/bit_set.h"
#include "common/common_types.h"
#include "common/concepts.h"
namespace Kernel {
class KThread;
template <typename T>
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
{ t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
{t.SetAffinityMask(0)};
{ t.GetAffinity(0) } -> std::same_as<bool>;
{t.SetAffinity(0, false)};
{t.SetAll()};
};
template <typename T>
concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
{typename T::QueueEntry()};
{(typename T::QueueEntry()).Initialize()};
{(typename T::QueueEntry()).SetPrev(std::addressof(t))};
{(typename T::QueueEntry()).SetNext(std::addressof(t))};
{ (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
{ (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
{ t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>;
{t.GetAffinityMask()};
{ std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask;
{ t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
{ t.GetPriority() } -> Common::ConvertibleTo<s32>;
{ t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
};
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
requires KPriorityQueueMember<Member>
class KPriorityQueue {
public:
using AffinityMaskType = std::remove_cv_t<
std::remove_reference_t<decltype(std::declval<Member>().GetAffinityMask())>>;
static_assert(LowestPriority >= 0);
static_assert(HighestPriority >= 0);
static_assert(LowestPriority >= HighestPriority);
static constexpr size_t NumPriority = LowestPriority - HighestPriority + 1;
static constexpr size_t NumCores = NumCores_;
static constexpr bool IsValidCore(s32 core) {
return 0 <= core && core < static_cast<s32>(NumCores);
}
static constexpr bool IsValidPriority(s32 priority) {
return HighestPriority <= priority && priority <= LowestPriority + 1;
}
private:
using Entry = typename Member::QueueEntry;
public:
class KPerCoreQueue {
private:
std::array<Entry, NumCores> root{};
public:
constexpr KPerCoreQueue() {
for (auto& per_core_root : root) {
per_core_root.Initialize();
}
}
constexpr bool PushBack(s32 core, Member* member) {
// Get the entry associated with the member.
Entry& member_entry = member->GetPriorityQueueEntry(core);
// Get the entry associated with the end of the queue.
Member* tail = this->root[core].GetPrev();
Entry& tail_entry =
(tail != nullptr) ? tail->GetPriorityQueueEntry(core) : this->root[core];
// Link the entries.
member_entry.SetPrev(tail);
member_entry.SetNext(nullptr);
tail_entry.SetNext(member);
this->root[core].SetPrev(member);
return tail == nullptr;
}
constexpr bool PushFront(s32 core, Member* member) {
// Get the entry associated with the member.
Entry& member_entry = member->GetPriorityQueueEntry(core);
// Get the entry associated with the front of the queue.
Member* head = this->root[core].GetNext();
Entry& head_entry =
(head != nullptr) ? head->GetPriorityQueueEntry(core) : this->root[core];
// Link the entries.
member_entry.SetPrev(nullptr);
member_entry.SetNext(head);
head_entry.SetPrev(member);
this->root[core].SetNext(member);
return (head == nullptr);
}
constexpr bool Remove(s32 core, Member* member) {
// Get the entry associated with the member.
Entry& member_entry = member->GetPriorityQueueEntry(core);
// Get the entries associated with next and prev.
Member* prev = member_entry.GetPrev();
Member* next = member_entry.GetNext();
Entry& prev_entry =
(prev != nullptr) ? prev->GetPriorityQueueEntry(core) : this->root[core];
Entry& next_entry =
(next != nullptr) ? next->GetPriorityQueueEntry(core) : this->root[core];
// Unlink.
prev_entry.SetNext(next);
next_entry.SetPrev(prev);
return (this->GetFront(core) == nullptr);
}
constexpr Member* GetFront(s32 core) const {
return this->root[core].GetNext();
}
};
class KPriorityQueueImpl {
public:
constexpr KPriorityQueueImpl() = default;
constexpr void PushBack(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority > LowestPriority) {
return;
}
if (this->queues[priority].PushBack(core, member)) {
this->available_priorities[core].SetBit(priority);
}
}
constexpr void PushFront(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority > LowestPriority) {
return;
}
if (this->queues[priority].PushFront(core, member)) {
this->available_priorities[core].SetBit(priority);
}
}
constexpr void Remove(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority > LowestPriority) {
return;
}
if (this->queues[priority].Remove(core, member)) {
this->available_priorities[core].ClearBit(priority);
}
}
constexpr Member* GetFront(s32 core) const {
ASSERT(IsValidCore(core));
const s32 priority =
static_cast<s32>(this->available_priorities[core].CountLeadingZero());
if (priority <= LowestPriority) {
return this->queues[priority].GetFront(core);
} else {
return nullptr;
}
}
constexpr Member* GetFront(s32 priority, s32 core) const {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority <= LowestPriority) {
return this->queues[priority].GetFront(core);
} else {
return nullptr;
}
}
constexpr Member* GetNext(s32 core, const Member* member) const {
ASSERT(IsValidCore(core));
Member* next = member->GetPriorityQueueEntry(core).GetNext();
if (next == nullptr) {
const s32 priority = static_cast<s32>(
this->available_priorities[core].GetNextSet(member->GetPriority()));
if (priority <= LowestPriority) {
next = this->queues[priority].GetFront(core);
}
}
return next;
}
constexpr void MoveToFront(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority <= LowestPriority) {
this->queues[priority].Remove(core, member);
this->queues[priority].PushFront(core, member);
}
}
constexpr Member* MoveToBack(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority <= LowestPriority) {
this->queues[priority].Remove(core, member);
this->queues[priority].PushBack(core, member);
return this->queues[priority].GetFront(core);
} else {
return nullptr;
}
}
private:
std::array<KPerCoreQueue, NumPriority> queues{};
std::array<Common::BitSet64<NumPriority>, NumCores> available_priorities{};
};
private:
KPriorityQueueImpl scheduled_queue;
KPriorityQueueImpl suggested_queue;
private:
constexpr void ClearAffinityBit(u64& affinity, s32 core) {
affinity &= ~(UINT64_C(1) << core);
}
constexpr s32 GetNextCore(u64& affinity) {
const s32 core = std::countr_zero(affinity);
ClearAffinityBit(affinity, core);
return core;
}
constexpr void PushBack(s32 priority, Member* member) {
ASSERT(IsValidPriority(priority));
// Push onto the scheduled queue for its core, if we can.
u64 affinity = member->GetAffinityMask().GetAffinityMask();
if (const s32 core = member->GetActiveCore(); core >= 0) {
this->scheduled_queue.PushBack(priority, core, member);
ClearAffinityBit(affinity, core);
}
// And suggest the thread for all other cores.
while (affinity) {
this->suggested_queue.PushBack(priority, GetNextCore(affinity), member);
}
}
constexpr void PushFront(s32 priority, Member* member) {
ASSERT(IsValidPriority(priority));
// Push onto the scheduled queue for its core, if we can.
u64 affinity = member->GetAffinityMask().GetAffinityMask();
if (const s32 core = member->GetActiveCore(); core >= 0) {
this->scheduled_queue.PushFront(priority, core, member);
ClearAffinityBit(affinity, core);
}
// And suggest the thread for all other cores.
// Note: Nintendo pushes onto the back of the suggested queue, not the front.
while (affinity) {
this->suggested_queue.PushBack(priority, GetNextCore(affinity), member);
}
}
constexpr void Remove(s32 priority, Member* member) {
ASSERT(IsValidPriority(priority));
// Remove from the scheduled queue for its core.
u64 affinity = member->GetAffinityMask().GetAffinityMask();
if (const s32 core = member->GetActiveCore(); core >= 0) {
this->scheduled_queue.Remove(priority, core, member);
ClearAffinityBit(affinity, core);
}
// Remove from the suggested queue for all other cores.
while (affinity) {
this->suggested_queue.Remove(priority, GetNextCore(affinity), member);
}
}
public:
constexpr KPriorityQueue() = default;
// Getters.
constexpr Member* GetScheduledFront(s32 core) const {
return this->scheduled_queue.GetFront(core);
}
constexpr Member* GetScheduledFront(s32 core, s32 priority) const {
return this->scheduled_queue.GetFront(priority, core);
}
constexpr Member* GetSuggestedFront(s32 core) const {
return this->suggested_queue.GetFront(core);
}
constexpr Member* GetSuggestedFront(s32 core, s32 priority) const {
return this->suggested_queue.GetFront(priority, core);
}
constexpr Member* GetScheduledNext(s32 core, const Member* member) const {
return this->scheduled_queue.GetNext(core, member);
}
constexpr Member* GetSuggestedNext(s32 core, const Member* member) const {
return this->suggested_queue.GetNext(core, member);
}
constexpr Member* GetSamePriorityNext(s32 core, const Member* member) const {
return member->GetPriorityQueueEntry(core).GetNext();
}
// Mutators.
constexpr void PushBack(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->PushBack(member->GetPriority(), member);
}
constexpr void Remove(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->Remove(member->GetPriority(), member);
}
constexpr void MoveToScheduledFront(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
}
constexpr KThread* MoveToScheduledBack(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return {};
}
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
member);
}
// First class fancy operations.
constexpr void ChangePriority(s32 prev_priority, bool is_running, Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
ASSERT(IsValidPriority(prev_priority));
// Remove the member from the queues.
const s32 new_priority = member->GetPriority();
this->Remove(prev_priority, member);
// And enqueue. If the member is running, we want to keep it running.
if (is_running) {
this->PushFront(new_priority, member);
} else {
this->PushBack(new_priority, member);
}
}
constexpr void ChangeAffinityMask(s32 prev_core, const AffinityMaskType& prev_affinity,
Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
// Get the new information.
const s32 priority = member->GetPriority();
const AffinityMaskType& new_affinity = member->GetAffinityMask();
const s32 new_core = member->GetActiveCore();
// Remove the member from all queues it was in before.
for (s32 core = 0; core < static_cast<s32>(NumCores); core++) {
if (prev_affinity.GetAffinity(core)) {
if (core == prev_core) {
this->scheduled_queue.Remove(priority, core, member);
} else {
this->suggested_queue.Remove(priority, core, member);
}
}
}
// And add the member to all queues it should be in now.
for (s32 core = 0; core < static_cast<s32>(NumCores); core++) {
if (new_affinity.GetAffinity(core)) {
if (core == new_core) {
this->scheduled_queue.PushBack(priority, core, member);
} else {
this->suggested_queue.PushBack(priority, core, member);
}
}
}
}
constexpr void ChangeCore(s32 prev_core, Member* member, bool to_front = false) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
// Get the new information.
const s32 new_core = member->GetActiveCore();
const s32 priority = member->GetPriority();
// We don't need to do anything if the core is the same.
if (prev_core != new_core) {
// Remove from the scheduled queue for the previous core.
if (prev_core >= 0) {
this->scheduled_queue.Remove(priority, prev_core, member);
}
// Remove from the suggested queue and add to the scheduled queue for the new core.
if (new_core >= 0) {
this->suggested_queue.Remove(priority, new_core, member);
if (to_front) {
this->scheduled_queue.PushFront(priority, new_core, member);
} else {
this->scheduled_queue.PushBack(priority, new_core, member);
}
}
// Add to the suggested queue for the previous core.
if (prev_core >= 0) {
this->suggested_queue.PushBack(priority, prev_core, member);
}
}
}
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <bit>
#include <concepts>
#include "common/assert.h"
#include "common/bit_set.h"
#include "common/common_types.h"
#include "common/concepts.h"
namespace Kernel {
class KThread;
template <typename T>
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
{ t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
{t.SetAffinityMask(0)};
{ t.GetAffinity(0) } -> std::same_as<bool>;
{t.SetAffinity(0, false)};
{t.SetAll()};
};
template <typename T>
concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
{typename T::QueueEntry()};
{(typename T::QueueEntry()).Initialize()};
{(typename T::QueueEntry()).SetPrev(std::addressof(t))};
{(typename T::QueueEntry()).SetNext(std::addressof(t))};
{ (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
{ (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
{ t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>;
{t.GetAffinityMask()};
{ std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask;
{ t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
{ t.GetPriority() } -> Common::ConvertibleTo<s32>;
{ t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
};
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
requires KPriorityQueueMember<Member>
class KPriorityQueue {
public:
using AffinityMaskType = std::remove_cv_t<
std::remove_reference_t<decltype(std::declval<Member>().GetAffinityMask())>>;
static_assert(LowestPriority >= 0);
static_assert(HighestPriority >= 0);
static_assert(LowestPriority >= HighestPriority);
static constexpr size_t NumPriority = LowestPriority - HighestPriority + 1;
static constexpr size_t NumCores = NumCores_;
static constexpr bool IsValidCore(s32 core) {
return 0 <= core && core < static_cast<s32>(NumCores);
}
static constexpr bool IsValidPriority(s32 priority) {
return HighestPriority <= priority && priority <= LowestPriority + 1;
}
private:
using Entry = typename Member::QueueEntry;
public:
class KPerCoreQueue {
private:
std::array<Entry, NumCores> root{};
public:
constexpr KPerCoreQueue() {
for (auto& per_core_root : root) {
per_core_root.Initialize();
}
}
constexpr bool PushBack(s32 core, Member* member) {
// Get the entry associated with the member.
Entry& member_entry = member->GetPriorityQueueEntry(core);
// Get the entry associated with the end of the queue.
Member* tail = this->root[core].GetPrev();
Entry& tail_entry =
(tail != nullptr) ? tail->GetPriorityQueueEntry(core) : this->root[core];
// Link the entries.
member_entry.SetPrev(tail);
member_entry.SetNext(nullptr);
tail_entry.SetNext(member);
this->root[core].SetPrev(member);
return tail == nullptr;
}
constexpr bool PushFront(s32 core, Member* member) {
// Get the entry associated with the member.
Entry& member_entry = member->GetPriorityQueueEntry(core);
// Get the entry associated with the front of the queue.
Member* head = this->root[core].GetNext();
Entry& head_entry =
(head != nullptr) ? head->GetPriorityQueueEntry(core) : this->root[core];
// Link the entries.
member_entry.SetPrev(nullptr);
member_entry.SetNext(head);
head_entry.SetPrev(member);
this->root[core].SetNext(member);
return (head == nullptr);
}
constexpr bool Remove(s32 core, Member* member) {
// Get the entry associated with the member.
Entry& member_entry = member->GetPriorityQueueEntry(core);
// Get the entries associated with next and prev.
Member* prev = member_entry.GetPrev();
Member* next = member_entry.GetNext();
Entry& prev_entry =
(prev != nullptr) ? prev->GetPriorityQueueEntry(core) : this->root[core];
Entry& next_entry =
(next != nullptr) ? next->GetPriorityQueueEntry(core) : this->root[core];
// Unlink.
prev_entry.SetNext(next);
next_entry.SetPrev(prev);
return (this->GetFront(core) == nullptr);
}
constexpr Member* GetFront(s32 core) const {
return this->root[core].GetNext();
}
};
class KPriorityQueueImpl {
public:
constexpr KPriorityQueueImpl() = default;
constexpr void PushBack(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority > LowestPriority) {
return;
}
if (this->queues[priority].PushBack(core, member)) {
this->available_priorities[core].SetBit(priority);
}
}
constexpr void PushFront(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority > LowestPriority) {
return;
}
if (this->queues[priority].PushFront(core, member)) {
this->available_priorities[core].SetBit(priority);
}
}
constexpr void Remove(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority > LowestPriority) {
return;
}
if (this->queues[priority].Remove(core, member)) {
this->available_priorities[core].ClearBit(priority);
}
}
constexpr Member* GetFront(s32 core) const {
ASSERT(IsValidCore(core));
const s32 priority =
static_cast<s32>(this->available_priorities[core].CountLeadingZero());
if (priority <= LowestPriority) {
return this->queues[priority].GetFront(core);
} else {
return nullptr;
}
}
constexpr Member* GetFront(s32 priority, s32 core) const {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority <= LowestPriority) {
return this->queues[priority].GetFront(core);
} else {
return nullptr;
}
}
constexpr Member* GetNext(s32 core, const Member* member) const {
ASSERT(IsValidCore(core));
Member* next = member->GetPriorityQueueEntry(core).GetNext();
if (next == nullptr) {
const s32 priority = static_cast<s32>(
this->available_priorities[core].GetNextSet(member->GetPriority()));
if (priority <= LowestPriority) {
next = this->queues[priority].GetFront(core);
}
}
return next;
}
constexpr void MoveToFront(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority <= LowestPriority) {
this->queues[priority].Remove(core, member);
this->queues[priority].PushFront(core, member);
}
}
constexpr Member* MoveToBack(s32 priority, s32 core, Member* member) {
ASSERT(IsValidCore(core));
ASSERT(IsValidPriority(priority));
if (priority <= LowestPriority) {
this->queues[priority].Remove(core, member);
this->queues[priority].PushBack(core, member);
return this->queues[priority].GetFront(core);
} else {
return nullptr;
}
}
private:
std::array<KPerCoreQueue, NumPriority> queues{};
std::array<Common::BitSet64<NumPriority>, NumCores> available_priorities{};
};
private:
KPriorityQueueImpl scheduled_queue;
KPriorityQueueImpl suggested_queue;
private:
constexpr void ClearAffinityBit(u64& affinity, s32 core) {
affinity &= ~(UINT64_C(1) << core);
}
constexpr s32 GetNextCore(u64& affinity) {
const s32 core = std::countr_zero(affinity);
ClearAffinityBit(affinity, core);
return core;
}
constexpr void PushBack(s32 priority, Member* member) {
ASSERT(IsValidPriority(priority));
// Push onto the scheduled queue for its core, if we can.
u64 affinity = member->GetAffinityMask().GetAffinityMask();
if (const s32 core = member->GetActiveCore(); core >= 0) {
this->scheduled_queue.PushBack(priority, core, member);
ClearAffinityBit(affinity, core);
}
// And suggest the thread for all other cores.
while (affinity) {
this->suggested_queue.PushBack(priority, GetNextCore(affinity), member);
}
}
constexpr void PushFront(s32 priority, Member* member) {
ASSERT(IsValidPriority(priority));
// Push onto the scheduled queue for its core, if we can.
u64 affinity = member->GetAffinityMask().GetAffinityMask();
if (const s32 core = member->GetActiveCore(); core >= 0) {
this->scheduled_queue.PushFront(priority, core, member);
ClearAffinityBit(affinity, core);
}
// And suggest the thread for all other cores.
// Note: Nintendo pushes onto the back of the suggested queue, not the front.
while (affinity) {
this->suggested_queue.PushBack(priority, GetNextCore(affinity), member);
}
}
constexpr void Remove(s32 priority, Member* member) {
ASSERT(IsValidPriority(priority));
// Remove from the scheduled queue for its core.
u64 affinity = member->GetAffinityMask().GetAffinityMask();
if (const s32 core = member->GetActiveCore(); core >= 0) {
this->scheduled_queue.Remove(priority, core, member);
ClearAffinityBit(affinity, core);
}
// Remove from the suggested queue for all other cores.
while (affinity) {
this->suggested_queue.Remove(priority, GetNextCore(affinity), member);
}
}
public:
constexpr KPriorityQueue() = default;
// Getters.
constexpr Member* GetScheduledFront(s32 core) const {
return this->scheduled_queue.GetFront(core);
}
constexpr Member* GetScheduledFront(s32 core, s32 priority) const {
return this->scheduled_queue.GetFront(priority, core);
}
constexpr Member* GetSuggestedFront(s32 core) const {
return this->suggested_queue.GetFront(core);
}
constexpr Member* GetSuggestedFront(s32 core, s32 priority) const {
return this->suggested_queue.GetFront(priority, core);
}
constexpr Member* GetScheduledNext(s32 core, const Member* member) const {
return this->scheduled_queue.GetNext(core, member);
}
constexpr Member* GetSuggestedNext(s32 core, const Member* member) const {
return this->suggested_queue.GetNext(core, member);
}
constexpr Member* GetSamePriorityNext(s32 core, const Member* member) const {
return member->GetPriorityQueueEntry(core).GetNext();
}
// Mutators.
constexpr void PushBack(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->PushBack(member->GetPriority(), member);
}
constexpr void Remove(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->Remove(member->GetPriority(), member);
}
constexpr void MoveToScheduledFront(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
}
constexpr KThread* MoveToScheduledBack(Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return {};
}
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
member);
}
// First class fancy operations.
constexpr void ChangePriority(s32 prev_priority, bool is_running, Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
ASSERT(IsValidPriority(prev_priority));
// Remove the member from the queues.
const s32 new_priority = member->GetPriority();
this->Remove(prev_priority, member);
// And enqueue. If the member is running, we want to keep it running.
if (is_running) {
this->PushFront(new_priority, member);
} else {
this->PushBack(new_priority, member);
}
}
constexpr void ChangeAffinityMask(s32 prev_core, const AffinityMaskType& prev_affinity,
Member* member) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
// Get the new information.
const s32 priority = member->GetPriority();
const AffinityMaskType& new_affinity = member->GetAffinityMask();
const s32 new_core = member->GetActiveCore();
// Remove the member from all queues it was in before.
for (s32 core = 0; core < static_cast<s32>(NumCores); core++) {
if (prev_affinity.GetAffinity(core)) {
if (core == prev_core) {
this->scheduled_queue.Remove(priority, core, member);
} else {
this->suggested_queue.Remove(priority, core, member);
}
}
}
// And add the member to all queues it should be in now.
for (s32 core = 0; core < static_cast<s32>(NumCores); core++) {
if (new_affinity.GetAffinity(core)) {
if (core == new_core) {
this->scheduled_queue.PushBack(priority, core, member);
} else {
this->suggested_queue.PushBack(priority, core, member);
}
}
}
}
constexpr void ChangeCore(s32 prev_core, Member* member, bool to_front = false) {
// This is for host (dummy) threads that we do not want to enter the priority queue.
if (member->IsDummyThread()) {
return;
}
// Get the new information.
const s32 new_core = member->GetActiveCore();
const s32 priority = member->GetPriority();
// We don't need to do anything if the core is the same.
if (prev_core != new_core) {
// Remove from the scheduled queue for the previous core.
if (prev_core >= 0) {
this->scheduled_queue.Remove(priority, prev_core, member);
}
// Remove from the suggested queue and add to the scheduled queue for the new core.
if (new_core >= 0) {
this->suggested_queue.Remove(priority, new_core, member);
if (to_front) {
this->scheduled_queue.PushFront(priority, new_core, member);
} else {
this->scheduled_queue.PushBack(priority, new_core, member);
}
}
// Add to the suggested queue for the previous core.
if (prev_core >= 0) {
this->suggested_queue.PushBack(priority, prev_core, member);
}
}
}
};
} // namespace Kernel

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,71 +1,71 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
KReadableEvent::KReadableEvent(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KReadableEvent::~KReadableEvent() = default;
void KReadableEvent::Initialize(KEvent* parent) {
m_is_signaled = false;
m_parent = parent;
if (m_parent != nullptr) {
m_parent->Open();
}
}
bool KReadableEvent::IsSignaled() const {
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
return m_is_signaled;
}
void KReadableEvent::Destroy() {
if (m_parent) {
{
KScopedSchedulerLock sl{kernel};
m_parent->OnReadableEventDestroyed();
}
m_parent->Close();
}
}
Result KReadableEvent::Signal() {
KScopedSchedulerLock lk{kernel};
if (!m_is_signaled) {
m_is_signaled = true;
this->NotifyAvailable();
}
return ResultSuccess;
}
Result KReadableEvent::Clear() {
this->Reset();
return ResultSuccess;
}
Result KReadableEvent::Reset() {
KScopedSchedulerLock lk{kernel};
if (!m_is_signaled) {
return ResultInvalidState;
}
m_is_signaled = false;
return ResultSuccess;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
KReadableEvent::KReadableEvent(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KReadableEvent::~KReadableEvent() = default;
void KReadableEvent::Initialize(KEvent* parent) {
m_is_signaled = false;
m_parent = parent;
if (m_parent != nullptr) {
m_parent->Open();
}
}
bool KReadableEvent::IsSignaled() const {
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
return m_is_signaled;
}
void KReadableEvent::Destroy() {
if (m_parent) {
{
KScopedSchedulerLock sl{kernel};
m_parent->OnReadableEventDestroyed();
}
m_parent->Close();
}
}
Result KReadableEvent::Signal() {
KScopedSchedulerLock lk{kernel};
if (!m_is_signaled) {
m_is_signaled = true;
this->NotifyAvailable();
}
return ResultSuccess;
}
Result KReadableEvent::Clear() {
this->Reset();
return ResultSuccess;
}
Result KReadableEvent::Reset() {
KScopedSchedulerLock lk{kernel};
if (!m_is_signaled) {
return ResultInvalidState;
}
m_is_signaled = false;
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -1,42 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KEvent;
class KReadableEvent : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KReadableEvent, KSynchronizationObject);
public:
explicit KReadableEvent(KernelCore& kernel_);
~KReadableEvent() override;
void Initialize(KEvent* parent);
KEvent* GetParent() const {
return m_parent;
}
Result Signal();
Result Clear();
bool IsSignaled() const override;
void Destroy() override;
Result Reset();
private:
bool m_is_signaled{};
KEvent* m_parent{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KEvent;
class KReadableEvent : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KReadableEvent, KSynchronizationObject);
public:
explicit KReadableEvent(KernelCore& kernel_);
~KReadableEvent() override;
void Initialize(KEvent* parent);
KEvent* GetParent() const {
return m_parent;
}
Result Signal();
Result Clear();
bool IsSignaled() const override;
void Destroy() override;
Result Reset();
private:
bool m_is_signaled{};
KEvent* m_parent{};
};
} // namespace Kernel

View File

@@ -1,172 +1,172 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
constexpr s64 DefaultTimeout = 10000000000; // 10 seconds
KResourceLimit::KResourceLimit(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, lock{kernel_}, cond_var{kernel_} {}
KResourceLimit::~KResourceLimit() = default;
void KResourceLimit::Initialize(const Core::Timing::CoreTiming* core_timing_) {
core_timing = core_timing_;
}
void KResourceLimit::Finalize() {}
s64 KResourceLimit::GetLimitValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = limit_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetCurrentValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = current_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetPeakValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = peak_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetFreeValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk(lock);
ASSERT(current_values[index] >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
value = limit_values[index] - current_values[index];
}
return value;
}
Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
R_UNLESS(current_values[index] <= value, ResultInvalidState);
limit_values[index] = value;
peak_values[index] = current_values[index];
return ResultSuccess;
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
return Reserve(which, value, core_timing->GetGlobalTimeNs().count() + DefaultTimeout);
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
ASSERT(value >= 0);
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
ASSERT(current_hints[index] <= current_values[index]);
if (current_hints[index] >= limit_values[index]) {
return false;
}
// Loop until we reserve or run out of time.
while (true) {
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
// If we would overflow, don't allow to succeed.
if (current_values[index] + value <= current_values[index]) {
break;
}
if (current_values[index] + value <= limit_values[index]) {
current_values[index] += value;
current_hints[index] += value;
peak_values[index] = std::max(peak_values[index], current_values[index]);
return true;
}
if (current_hints[index] + value <= limit_values[index] &&
(timeout < 0 || core_timing->GetGlobalTimeNs().count() < timeout)) {
waiter_count++;
cond_var.Wait(&lock, timeout, false);
waiter_count--;
} else {
break;
}
}
return false;
}
void KResourceLimit::Release(LimitableResource which, s64 value) {
Release(which, value, value);
}
void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
ASSERT(value >= 0);
ASSERT(hint >= 0);
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
ASSERT(value <= current_values[index]);
ASSERT(hint <= current_hints[index]);
current_values[index] -= value;
current_hints[index] -= hint;
if (waiter_count != 0) {
cond_var.Broadcast();
}
}
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
auto* resource_limit = KResourceLimit::Create(system.Kernel());
resource_limit->Initialize(&system.CoreTiming());
// Initialize default resource limit values.
// TODO(bunnei): These values are the system defaults, the limits for service processes are
// lower. These should use the correct limit values.
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
.IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
return resource_limit;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
constexpr s64 DefaultTimeout = 10000000000; // 10 seconds
KResourceLimit::KResourceLimit(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, lock{kernel_}, cond_var{kernel_} {}
KResourceLimit::~KResourceLimit() = default;
void KResourceLimit::Initialize(const Core::Timing::CoreTiming* core_timing_) {
core_timing = core_timing_;
}
void KResourceLimit::Finalize() {}
s64 KResourceLimit::GetLimitValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = limit_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetCurrentValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = current_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetPeakValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = peak_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetFreeValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk(lock);
ASSERT(current_values[index] >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
value = limit_values[index] - current_values[index];
}
return value;
}
Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
R_UNLESS(current_values[index] <= value, ResultInvalidState);
limit_values[index] = value;
peak_values[index] = current_values[index];
return ResultSuccess;
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
return Reserve(which, value, core_timing->GetGlobalTimeNs().count() + DefaultTimeout);
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
ASSERT(value >= 0);
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
ASSERT(current_hints[index] <= current_values[index]);
if (current_hints[index] >= limit_values[index]) {
return false;
}
// Loop until we reserve or run out of time.
while (true) {
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
// If we would overflow, don't allow to succeed.
if (current_values[index] + value <= current_values[index]) {
break;
}
if (current_values[index] + value <= limit_values[index]) {
current_values[index] += value;
current_hints[index] += value;
peak_values[index] = std::max(peak_values[index], current_values[index]);
return true;
}
if (current_hints[index] + value <= limit_values[index] &&
(timeout < 0 || core_timing->GetGlobalTimeNs().count() < timeout)) {
waiter_count++;
cond_var.Wait(&lock, timeout, false);
waiter_count--;
} else {
break;
}
}
return false;
}
void KResourceLimit::Release(LimitableResource which, s64 value) {
Release(which, value, value);
}
void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
ASSERT(value >= 0);
ASSERT(hint >= 0);
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
ASSERT(value <= current_values[index]);
ASSERT(hint <= current_hints[index]);
current_values[index] -= value;
current_hints[index] -= hint;
if (waiter_count != 0) {
cond_var.Broadcast();
}
}
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
auto* resource_limit = KResourceLimit::Create(system.Kernel());
resource_limit->Initialize(&system.CoreTiming());
// Initialize default resource limit values.
// TODO(bunnei): These values are the system defaults, the limits for service processes are
// lower. These should use the correct limit values.
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
.IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
return resource_limit;
}
} // namespace Kernel

View File

@@ -1,72 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_light_lock.h"
union Result;
namespace Core::Timing {
class CoreTiming;
}
namespace Kernel {
class KernelCore;
enum class LimitableResource : u32 {
PhysicalMemory = 0,
Threads = 1,
Events = 2,
TransferMemory = 3,
Sessions = 4,
Count,
};
constexpr bool IsValidResourceType(LimitableResource type) {
return type < LimitableResource::Count;
}
class KResourceLimit final
: public KAutoObjectWithSlabHeapAndContainer<KResourceLimit, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KResourceLimit, KAutoObject);
public:
explicit KResourceLimit(KernelCore& kernel_);
~KResourceLimit() override;
void Initialize(const Core::Timing::CoreTiming* core_timing_);
void Finalize() override;
s64 GetLimitValue(LimitableResource which) const;
s64 GetCurrentValue(LimitableResource which) const;
s64 GetPeakValue(LimitableResource which) const;
s64 GetFreeValue(LimitableResource which) const;
Result SetLimitValue(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value, s64 timeout);
void Release(LimitableResource which, s64 value);
void Release(LimitableResource which, s64 value, s64 hint);
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
private:
using ResourceArray = std::array<s64, static_cast<std::size_t>(LimitableResource::Count)>;
ResourceArray limit_values{};
ResourceArray current_values{};
ResourceArray current_hints{};
ResourceArray peak_values{};
mutable KLightLock lock;
s32 waiter_count{};
KLightConditionVariable cond_var;
const Core::Timing::CoreTiming* core_timing{};
};
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_light_lock.h"
union Result;
namespace Core::Timing {
class CoreTiming;
}
namespace Kernel {
class KernelCore;
enum class LimitableResource : u32 {
PhysicalMemory = 0,
Threads = 1,
Events = 2,
TransferMemory = 3,
Sessions = 4,
Count,
};
constexpr bool IsValidResourceType(LimitableResource type) {
return type < LimitableResource::Count;
}
class KResourceLimit final
: public KAutoObjectWithSlabHeapAndContainer<KResourceLimit, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KResourceLimit, KAutoObject);
public:
explicit KResourceLimit(KernelCore& kernel_);
~KResourceLimit() override;
void Initialize(const Core::Timing::CoreTiming* core_timing_);
void Finalize() override;
s64 GetLimitValue(LimitableResource which) const;
s64 GetCurrentValue(LimitableResource which) const;
s64 GetPeakValue(LimitableResource which) const;
s64 GetFreeValue(LimitableResource which) const;
Result SetLimitValue(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value, s64 timeout);
void Release(LimitableResource which, s64 value);
void Release(LimitableResource which, s64 value, s64 hint);
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
private:
using ResourceArray = std::array<s64, static_cast<std::size_t>(LimitableResource::Count)>;
ResourceArray limit_values{};
ResourceArray current_values{};
ResourceArray current_hints{};
ResourceArray peak_values{};
mutable KLightLock lock;
s32 waiter_count{};
KLightConditionVariable cond_var;
const Core::Timing::CoreTiming* core_timing{};
};
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);
} // namespace Kernel

File diff suppressed because it is too large Load Diff

View File

@@ -1,173 +1,173 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/common_types.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
namespace Common {
class Fiber;
}
namespace Core {
class System;
}
namespace Kernel {
class KernelCore;
class KInterruptTaskManager;
class KProcess;
class KThread;
class KScopedDisableDispatch;
class KScopedSchedulerLock;
class KScopedSchedulerLockAndSleep;
class KScheduler final {
public:
YUZU_NON_COPYABLE(KScheduler);
YUZU_NON_MOVEABLE(KScheduler);
using LockType = KAbstractSchedulerLock<KScheduler>;
explicit KScheduler(KernelCore& kernel);
~KScheduler();
void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id);
void Activate();
void OnThreadStart();
void Unload(KThread* thread);
void Reload(KThread* thread);
void SetInterruptTaskRunnable();
void RequestScheduleOnInterrupt();
void PreemptSingleCore();
u64 GetIdleCount() {
return m_state.idle_count;
}
KThread* GetIdleThread() const {
return m_idle_thread;
}
bool IsIdle() const {
return m_current_thread.load() == m_idle_thread;
}
KThread* GetPreviousThread() const {
return m_state.prev_thread;
}
KThread* GetSchedulerCurrentThread() const {
return m_current_thread.load();
}
s64 GetLastContextSwitchTime() const {
return m_last_context_switch_time;
}
// Static public API.
static bool CanSchedule(KernelCore& kernel) {
return GetCurrentThread(kernel).GetDisableDispatchCount() == 0;
}
static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread();
}
static bool IsSchedulerUpdateNeeded(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().scheduler_update_needed;
}
static void SetSchedulerUpdateNeeded(KernelCore& kernel) {
kernel.GlobalSchedulerContext().scheduler_update_needed = true;
}
static void ClearSchedulerUpdateNeeded(KernelCore& kernel) {
kernel.GlobalSchedulerContext().scheduler_update_needed = false;
}
static void DisableScheduling(KernelCore& kernel);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority);
static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling);
static void YieldWithoutCoreMigration(KernelCore& kernel);
static void YieldWithCoreMigration(KernelCore& kernel);
static void YieldToAnyThread(KernelCore& kernel);
private:
// Static private API.
static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().priority_queue;
}
static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
static void RescheduleCurrentHLEThread(KernelCore& kernel);
// Instanced private API.
void ScheduleImpl();
void ScheduleImplFiber();
void SwitchThread(KThread* next_thread);
void Schedule();
void ScheduleOnInterrupt();
void RescheduleOtherCores(u64 cores_needing_scheduling);
void RescheduleCurrentCore();
void RescheduleCurrentCoreImpl();
u64 UpdateHighestPriorityThread(KThread* thread);
private:
friend class KScopedDisableDispatch;
struct SchedulingState {
std::atomic<bool> needs_scheduling{false};
bool interrupt_task_runnable{false};
bool should_count_idle{false};
u64 idle_count{0};
KThread* highest_priority_thread{nullptr};
void* idle_thread_stack{nullptr};
std::atomic<KThread*> prev_thread{nullptr};
KInterruptTaskManager* interrupt_task_manager{nullptr};
};
KernelCore& kernel;
SchedulingState m_state;
bool m_is_active{false};
s32 m_core_id{0};
s64 m_last_context_switch_time{0};
KThread* m_idle_thread{nullptr};
std::atomic<KThread*> m_current_thread{nullptr};
std::shared_ptr<Common::Fiber> m_switch_fiber{};
KThread* m_switch_cur_thread{};
KThread* m_switch_highest_priority_thread{};
bool m_switch_from_schedule{};
};
class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> {
public:
explicit KScopedSchedulerLock(KernelCore& kernel)
: KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {}
~KScopedSchedulerLock() = default;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/common_types.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
namespace Common {
class Fiber;
}
namespace Core {
class System;
}
namespace Kernel {
class KernelCore;
class KInterruptTaskManager;
class KProcess;
class KThread;
class KScopedDisableDispatch;
class KScopedSchedulerLock;
class KScopedSchedulerLockAndSleep;
class KScheduler final {
public:
YUZU_NON_COPYABLE(KScheduler);
YUZU_NON_MOVEABLE(KScheduler);
using LockType = KAbstractSchedulerLock<KScheduler>;
explicit KScheduler(KernelCore& kernel);
~KScheduler();
void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id);
void Activate();
void OnThreadStart();
void Unload(KThread* thread);
void Reload(KThread* thread);
void SetInterruptTaskRunnable();
void RequestScheduleOnInterrupt();
void PreemptSingleCore();
u64 GetIdleCount() {
return m_state.idle_count;
}
KThread* GetIdleThread() const {
return m_idle_thread;
}
bool IsIdle() const {
return m_current_thread.load() == m_idle_thread;
}
KThread* GetPreviousThread() const {
return m_state.prev_thread;
}
KThread* GetSchedulerCurrentThread() const {
return m_current_thread.load();
}
s64 GetLastContextSwitchTime() const {
return m_last_context_switch_time;
}
// Static public API.
static bool CanSchedule(KernelCore& kernel) {
return GetCurrentThread(kernel).GetDisableDispatchCount() == 0;
}
static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread();
}
static bool IsSchedulerUpdateNeeded(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().scheduler_update_needed;
}
static void SetSchedulerUpdateNeeded(KernelCore& kernel) {
kernel.GlobalSchedulerContext().scheduler_update_needed = true;
}
static void ClearSchedulerUpdateNeeded(KernelCore& kernel) {
kernel.GlobalSchedulerContext().scheduler_update_needed = false;
}
static void DisableScheduling(KernelCore& kernel);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority);
static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling);
static void YieldWithoutCoreMigration(KernelCore& kernel);
static void YieldWithCoreMigration(KernelCore& kernel);
static void YieldToAnyThread(KernelCore& kernel);
private:
// Static private API.
static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().priority_queue;
}
static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
static void RescheduleCurrentHLEThread(KernelCore& kernel);
// Instanced private API.
void ScheduleImpl();
void ScheduleImplFiber();
void SwitchThread(KThread* next_thread);
void Schedule();
void ScheduleOnInterrupt();
void RescheduleOtherCores(u64 cores_needing_scheduling);
void RescheduleCurrentCore();
void RescheduleCurrentCoreImpl();
u64 UpdateHighestPriorityThread(KThread* thread);
private:
friend class KScopedDisableDispatch;
struct SchedulingState {
std::atomic<bool> needs_scheduling{false};
bool interrupt_task_runnable{false};
bool should_count_idle{false};
u64 idle_count{0};
KThread* highest_priority_thread{nullptr};
void* idle_thread_stack{nullptr};
std::atomic<KThread*> prev_thread{nullptr};
KInterruptTaskManager* interrupt_task_manager{nullptr};
};
KernelCore& kernel;
SchedulingState m_state;
bool m_is_active{false};
s32 m_core_id{0};
s64 m_last_context_switch_time{0};
KThread* m_idle_thread{nullptr};
std::atomic<KThread*> m_current_thread{nullptr};
std::shared_ptr<Common::Fiber> m_switch_fiber{};
KThread* m_switch_cur_thread{};
KThread* m_switch_highest_priority_thread{};
bool m_switch_from_schedule{};
};
class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> {
public:
explicit KScopedSchedulerLock(KernelCore& kernel)
: KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {}
~KScopedSchedulerLock() = default;
};
} // namespace Kernel

View File

@@ -1,83 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/assert.h"
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel {
class KernelCore;
template <typename SchedulerType>
class KAbstractSchedulerLock {
public:
explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
bool IsLockedByCurrentThread() const {
return owner_thread == GetCurrentThreadPointer(kernel);
}
void Lock() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
if (IsLockedByCurrentThread()) {
// If we already own the lock, we can just increment the count.
ASSERT(lock_count > 0);
lock_count++;
} else {
// Otherwise, we want to disable scheduling and acquire the spinlock.
SchedulerType::DisableScheduling(kernel);
spin_lock.Lock();
// For debug, ensure that our state is valid.
ASSERT(lock_count == 0);
ASSERT(owner_thread == nullptr);
// Increment count, take ownership.
lock_count = 1;
owner_thread = GetCurrentThreadPointer(kernel);
}
}
void Unlock() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
ASSERT(IsLockedByCurrentThread());
ASSERT(lock_count > 0);
// Release an instance of the lock.
if ((--lock_count) == 0) {
// We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling =
SchedulerType::UpdateHighestPriorityThreads(kernel);
// Note that we no longer hold the lock, and unlock the spinlock.
owner_thread = nullptr;
spin_lock.Unlock();
// Enable scheduling, and perform a rescheduling operation.
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
}
}
private:
KernelCore& kernel;
KAlignedSpinLock spin_lock{};
s32 lock_count{};
std::atomic<KThread*> owner_thread{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/assert.h"
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel {
class KernelCore;
template <typename SchedulerType>
class KAbstractSchedulerLock {
public:
explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
bool IsLockedByCurrentThread() const {
return owner_thread == GetCurrentThreadPointer(kernel);
}
void Lock() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
if (IsLockedByCurrentThread()) {
// If we already own the lock, we can just increment the count.
ASSERT(lock_count > 0);
lock_count++;
} else {
// Otherwise, we want to disable scheduling and acquire the spinlock.
SchedulerType::DisableScheduling(kernel);
spin_lock.Lock();
// For debug, ensure that our state is valid.
ASSERT(lock_count == 0);
ASSERT(owner_thread == nullptr);
// Increment count, take ownership.
lock_count = 1;
owner_thread = GetCurrentThreadPointer(kernel);
}
}
void Unlock() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
ASSERT(IsLockedByCurrentThread());
ASSERT(lock_count > 0);
// Release an instance of the lock.
if ((--lock_count) == 0) {
// We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling =
SchedulerType::UpdateHighestPriorityThreads(kernel);
// Note that we no longer hold the lock, and unlock the spinlock.
owner_thread = nullptr;
spin_lock.Unlock();
// Enable scheduling, and perform a rescheduling operation.
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
}
}
private:
KernelCore& kernel;
KAlignedSpinLock spin_lock{};
s32 lock_count{};
std::atomic<KThread*> owner_thread{};
};
} // namespace Kernel

View File

@@ -1,40 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <concepts>
#include <type_traits>
namespace Kernel {
template <typename T>
concept KLockable = !std::is_reference_v<T> && requires(T & t) {
{ t.Lock() } -> std::same_as<void>;
{ t.Unlock() } -> std::same_as<void>;
};
template <typename T>
requires KLockable<T>
class [[nodiscard]] KScopedLock {
public:
explicit KScopedLock(T* l) : lock_ptr(l) {
this->lock_ptr->Lock();
}
explicit KScopedLock(T& l) : KScopedLock(std::addressof(l)) {}
~KScopedLock() {
this->lock_ptr->Unlock();
}
KScopedLock(const KScopedLock&) = delete;
KScopedLock& operator=(const KScopedLock&) = delete;
KScopedLock(KScopedLock&&) = delete;
KScopedLock& operator=(KScopedLock&&) = delete;
private:
T* lock_ptr;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <concepts>
#include <type_traits>
namespace Kernel {
template <typename T>
concept KLockable = !std::is_reference_v<T> && requires(T & t) {
{ t.Lock() } -> std::same_as<void>;
{ t.Unlock() } -> std::same_as<void>;
};
template <typename T>
requires KLockable<T>
class [[nodiscard]] KScopedLock {
public:
explicit KScopedLock(T* l) : lock_ptr(l) {
this->lock_ptr->Lock();
}
explicit KScopedLock(T& l) : KScopedLock(std::addressof(l)) {}
~KScopedLock() {
this->lock_ptr->Unlock();
}
KScopedLock(const KScopedLock&) = delete;
KScopedLock& operator=(const KScopedLock&) = delete;
KScopedLock(KScopedLock&&) = delete;
KScopedLock& operator=(KScopedLock&&) = delete;
private:
T* lock_ptr;
};
} // namespace Kernel

View File

@@ -1,61 +1,61 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
namespace Kernel {
class KScopedResourceReservation {
public:
explicit KScopedResourceReservation(KResourceLimit* l, LimitableResource r, s64 v, s64 timeout)
: resource_limit(std::move(l)), value(v), resource(r) {
if (resource_limit && value) {
success = resource_limit->Reserve(resource, value, timeout);
} else {
success = true;
}
}
explicit KScopedResourceReservation(KResourceLimit* l, LimitableResource r, s64 v = 1)
: resource_limit(std::move(l)), value(v), resource(r) {
if (resource_limit && value) {
success = resource_limit->Reserve(resource, value);
} else {
success = true;
}
}
explicit KScopedResourceReservation(const KProcess* p, LimitableResource r, s64 v, s64 t)
: KScopedResourceReservation(p->GetResourceLimit(), r, v, t) {}
explicit KScopedResourceReservation(const KProcess* p, LimitableResource r, s64 v = 1)
: KScopedResourceReservation(p->GetResourceLimit(), r, v) {}
~KScopedResourceReservation() noexcept {
if (resource_limit && value && success) {
// resource was not committed, release the reservation.
resource_limit->Release(resource, value);
}
}
/// Commit the resource reservation, destruction of this object does not release the resource
void Commit() {
resource_limit = nullptr;
}
[[nodiscard]] bool Succeeded() const {
return success;
}
private:
KResourceLimit* resource_limit{};
s64 value;
LimitableResource resource;
bool success;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
namespace Kernel {
class KScopedResourceReservation {
public:
explicit KScopedResourceReservation(KResourceLimit* l, LimitableResource r, s64 v, s64 timeout)
: resource_limit(std::move(l)), value(v), resource(r) {
if (resource_limit && value) {
success = resource_limit->Reserve(resource, value, timeout);
} else {
success = true;
}
}
explicit KScopedResourceReservation(KResourceLimit* l, LimitableResource r, s64 v = 1)
: resource_limit(std::move(l)), value(v), resource(r) {
if (resource_limit && value) {
success = resource_limit->Reserve(resource, value);
} else {
success = true;
}
}
explicit KScopedResourceReservation(const KProcess* p, LimitableResource r, s64 v, s64 t)
: KScopedResourceReservation(p->GetResourceLimit(), r, v, t) {}
explicit KScopedResourceReservation(const KProcess* p, LimitableResource r, s64 v = 1)
: KScopedResourceReservation(p->GetResourceLimit(), r, v) {}
~KScopedResourceReservation() noexcept {
if (resource_limit && value && success) {
// resource was not committed, release the reservation.
resource_limit->Release(resource, value);
}
}
/// Commit the resource reservation, destruction of this object does not release the resource
void Commit() {
resource_limit = nullptr;
}
[[nodiscard]] bool Succeeded() const {
return success;
}
private:
KResourceLimit* resource_limit{};
s64 value;
LimitableResource resource;
bool success;
};
} // namespace Kernel

View File

@@ -1,42 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
class [[nodiscard]] KScopedSchedulerLockAndSleep {
public:
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel_, KThread* t, s64 timeout)
: kernel(kernel_), thread(t), timeout_tick(timeout) {
// Lock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Lock();
}
~KScopedSchedulerLockAndSleep() {
// Register the sleep.
if (timeout_tick > 0) {
kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
}
// Unlock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Unlock();
}
void CancelSleep() {
timeout_tick = 0;
}
private:
KernelCore& kernel;
KThread* thread{};
s64 timeout_tick{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
class [[nodiscard]] KScopedSchedulerLockAndSleep {
public:
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel_, KThread* t, s64 timeout)
: kernel(kernel_), thread(t), timeout_tick(timeout) {
// Lock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Lock();
}
~KScopedSchedulerLockAndSleep() {
// Register the sleep.
if (timeout_tick > 0) {
kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
}
// Unlock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Unlock();
}
void CancelSleep() {
timeout_tick = 0;
}
private:
KernelCore& kernel;
KThread* thread{};
s64 timeout_tick{};
};
} // namespace Kernel

View File

@@ -1,102 +1,102 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <tuple>
#include "common/assert.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
KServerPort::KServerPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KServerPort::~KServerPort() = default;
void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) {
// Set member variables.
parent = parent_port_;
name = std::move(name_);
}
bool KServerPort::IsLight() const {
return this->GetParent()->IsLight();
}
void KServerPort::CleanupSessions() {
// Ensure our preconditions are met.
if (this->IsLight()) {
UNIMPLEMENTED();
}
// Cleanup the session list.
while (true) {
// Get the last session in the list
KServerSession* session = nullptr;
{
KScopedSchedulerLock sl{kernel};
if (!session_list.empty()) {
session = std::addressof(session_list.front());
session_list.pop_front();
}
}
// Close the session.
if (session != nullptr) {
session->Close();
} else {
break;
}
}
}
void KServerPort::Destroy() {
// Note with our parent that we're closed.
parent->OnServerClosed();
// Perform necessary cleanup of our session lists.
this->CleanupSessions();
// Close our reference to our parent.
parent->Close();
}
bool KServerPort::IsSignaled() const {
if (this->IsLight()) {
UNIMPLEMENTED();
return false;
} else {
return !session_list.empty();
}
}
void KServerPort::EnqueueSession(KServerSession* session) {
ASSERT(!this->IsLight());
KScopedSchedulerLock sl{kernel};
// Add the session to our queue.
session_list.push_back(*session);
if (session_list.size() == 1) {
this->NotifyAvailable();
}
}
KServerSession* KServerPort::AcceptSession() {
ASSERT(!this->IsLight());
KScopedSchedulerLock sl{kernel};
// Return the first session in the list.
if (session_list.empty()) {
return nullptr;
}
KServerSession* session = std::addressof(session_list.front());
session_list.pop_front();
return session;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <tuple>
#include "common/assert.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
KServerPort::KServerPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
KServerPort::~KServerPort() = default;
void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) {
// Set member variables.
parent = parent_port_;
name = std::move(name_);
}
bool KServerPort::IsLight() const {
return this->GetParent()->IsLight();
}
void KServerPort::CleanupSessions() {
// Ensure our preconditions are met.
if (this->IsLight()) {
UNIMPLEMENTED();
}
// Cleanup the session list.
while (true) {
// Get the last session in the list
KServerSession* session = nullptr;
{
KScopedSchedulerLock sl{kernel};
if (!session_list.empty()) {
session = std::addressof(session_list.front());
session_list.pop_front();
}
}
// Close the session.
if (session != nullptr) {
session->Close();
} else {
break;
}
}
}
void KServerPort::Destroy() {
// Note with our parent that we're closed.
parent->OnServerClosed();
// Perform necessary cleanup of our session lists.
this->CleanupSessions();
// Close our reference to our parent.
parent->Close();
}
bool KServerPort::IsSignaled() const {
if (this->IsLight()) {
UNIMPLEMENTED();
return false;
} else {
return !session_list.empty();
}
}
void KServerPort::EnqueueSession(KServerSession* session) {
ASSERT(!this->IsLight());
KScopedSchedulerLock sl{kernel};
// Add the session to our queue.
session_list.push_back(*session);
if (session_list.size() == 1) {
this->NotifyAvailable();
}
}
KServerSession* KServerPort::AcceptSession() {
ASSERT(!this->IsLight());
KScopedSchedulerLock sl{kernel};
// Return the first session in the list.
if (session_list.empty()) {
return nullptr;
}
KServerSession* session = std::addressof(session_list.front());
session_list.pop_front();
return session;
}
} // namespace Kernel

View File

@@ -1,53 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <string>
#include <utility>
#include <boost/intrusive/list.hpp>
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_synchronization_object.h"
namespace Kernel {
class KernelCore;
class KPort;
class SessionRequestHandler;
class KServerPort final : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject);
public:
explicit KServerPort(KernelCore& kernel_);
~KServerPort() override;
void Initialize(KPort* parent_port_, std::string&& name_);
void EnqueueSession(KServerSession* pending_session);
KServerSession* AcceptSession();
const KPort* GetParent() const {
return parent;
}
bool IsLight() const;
// Overridden virtual functions.
void Destroy() override;
bool IsSignaled() const override;
private:
using SessionList = boost::intrusive::list<KServerSession>;
void CleanupSessions();
SessionList session_list;
KPort* parent{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <string>
#include <utility>
#include <boost/intrusive/list.hpp>
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_synchronization_object.h"
namespace Kernel {
class KernelCore;
class KPort;
class SessionRequestHandler;
class KServerPort final : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject);
public:
explicit KServerPort(KernelCore& kernel_);
~KServerPort() override;
void Initialize(KPort* parent_port_, std::string&& name_);
void EnqueueSession(KServerSession* pending_session);
KServerSession* AcceptSession();
const KPort* GetParent() const {
return parent;
}
bool IsLight() const;
// Overridden virtual functions.
void Destroy() override;
bool IsSignaled() const override;
private:
using SessionList = boost::intrusive::list<KServerSession>;
void CleanupSessions();
SessionList session_list;
KPort* parent{};
};
} // namespace Kernel

View File

@@ -1,419 +1,419 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <tuple>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/memory.h"
namespace Kernel {
using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
KServerSession::KServerSession(KernelCore& kernel_)
: KSynchronizationObject{kernel_}, m_lock{kernel_} {}
KServerSession::~KServerSession() = default;
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) {
// Set member variables.
parent = parent_session_;
name = std::move(name_);
}
void KServerSession::Destroy() {
parent->OnServerClosed();
this->CleanupRequests();
parent->Close();
}
void KServerSession::OnClientClosed() {
KScopedLightLock lk{m_lock};
// Handle any pending requests.
KSessionRequest* prev_request = nullptr;
while (true) {
// Declare variables for processing the request.
KSessionRequest* request = nullptr;
KEvent* event = nullptr;
KThread* thread = nullptr;
bool cur_request = false;
bool terminate = false;
// Get the next request.
{
KScopedSchedulerLock sl{kernel};
if (m_current_request != nullptr && m_current_request != prev_request) {
// Set the request, open a reference as we process it.
request = m_current_request;
request->Open();
cur_request = true;
// Get thread and event for the request.
thread = request->GetThread();
event = request->GetEvent();
// If the thread is terminating, handle that.
if (thread->IsTerminationRequested()) {
request->ClearThread();
request->ClearEvent();
terminate = true;
}
prev_request = request;
} else if (!m_request_list.empty()) {
// Pop the request from the front of the list.
request = std::addressof(m_request_list.front());
m_request_list.pop_front();
// Get thread and event for the request.
thread = request->GetThread();
event = request->GetEvent();
}
}
// If there are no requests, we're done.
if (request == nullptr) {
break;
}
// All requests must have threads.
ASSERT(thread != nullptr);
// Ensure that we close the request when done.
SCOPE_EXIT({ request->Close(); });
// If we're terminating, close a reference to the thread and event.
if (terminate) {
thread->Close();
if (event != nullptr) {
event->Close();
}
}
// If we need to, reply.
if (event != nullptr && !cur_request) {
// There must be no mappings.
ASSERT(request->GetSendCount() == 0);
ASSERT(request->GetReceiveCount() == 0);
ASSERT(request->GetExchangeCount() == 0);
// // Get the process and page table.
// KProcess *client_process = thread->GetOwnerProcess();
// auto &client_pt = client_process->GetPageTable();
// // Reply to the request.
// ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
// ResultSessionClosed);
// // Unlock the buffer.
// // NOTE: Nintendo does not check the result of this.
// client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
// Signal the event.
event->Signal();
}
}
// Notify.
this->NotifyAvailable(ResultSessionClosed);
}
bool KServerSession::IsSignaled() const {
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
// If the client is closed, we're always signaled.
if (parent->IsClientClosed()) {
return true;
}
// Otherwise, we're signaled if we have a request and aren't handling one.
return !m_request_list.empty() && m_current_request == nullptr;
}
Result KServerSession::OnRequest(KSessionRequest* request) {
// Create the wait queue.
ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
{
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
// Ensure that we can handle new requests.
R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed);
// Check that we're not terminating.
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
// Get whether we're empty.
const bool was_empty = m_request_list.empty();
// Add the request to the list.
request->Open();
m_request_list.push_back(*request);
// If we were empty, signal.
if (was_empty) {
this->NotifyAvailable();
}
// If we have a request event, this is asynchronous, and we don't need to wait.
R_SUCCEED_IF(request->GetEvent() != nullptr);
// This is a synchronous request, so we should wait for our request to complete.
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
GetCurrentThread(kernel).BeginWait(&wait_queue);
}
return GetCurrentThread(kernel).GetWaitResult();
}
Result KServerSession::SendReply(bool is_hle) {
// Lock the session.
KScopedLightLock lk{m_lock};
// Get the request.
KSessionRequest* request;
{
KScopedSchedulerLock sl{kernel};
// Get the current request.
request = m_current_request;
R_UNLESS(request != nullptr, ResultInvalidState);
// Clear the current request, since we're processing it.
m_current_request = nullptr;
if (!m_request_list.empty()) {
this->NotifyAvailable();
}
}
// Close reference to the request once we're done processing it.
SCOPE_EXIT({ request->Close(); });
// Extract relevant information from the request.
const uintptr_t client_message = request->GetAddress();
const size_t client_buffer_size = request->GetSize();
KThread* client_thread = request->GetThread();
KEvent* event = request->GetEvent();
// Check whether we're closed.
const bool closed = (client_thread == nullptr || parent->IsClientClosed());
Result result = ResultSuccess;
if (!closed) {
// If we're not closed, send the reply.
if (is_hle) {
// HLE servers write directly to a pointer to the thread command buffer. Therefore
// the reply has already been written in this case.
} else {
Core::Memory::Memory& memory{kernel.System().Memory()};
KThread* server_thread{GetCurrentThreadPointer(kernel)};
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
auto* dst_msg_buffer = memory.GetPointer(client_message);
std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
}
} else {
result = ResultSessionClosed;
}
// Select a result for the client.
Result client_result = result;
if (closed && R_SUCCEEDED(result)) {
result = ResultSessionClosed;
client_result = ResultSessionClosed;
} else {
result = ResultSuccess;
}
// If there's a client thread, update it.
if (client_thread != nullptr) {
if (event != nullptr) {
// // Get the client process/page table.
// KProcess *client_process = client_thread->GetOwnerProcess();
// KPageTable *client_page_table = &client_process->PageTable();
// // If we need to, reply with an async error.
// if (R_FAILED(client_result)) {
// ReplyAsyncError(client_process, client_message, client_buffer_size,
// client_result);
// }
// // Unlock the client buffer.
// // NOTE: Nintendo does not check the result of this.
// client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
// Signal the event.
event->Signal();
} else {
// End the client thread's wait.
KScopedSchedulerLock sl{kernel};
if (!client_thread->IsTerminationRequested()) {
client_thread->EndWait(client_result);
}
}
}
return result;
}
Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context,
std::weak_ptr<SessionRequestManager> manager) {
// Lock the session.
KScopedLightLock lk{m_lock};
// Get the request and client thread.
KSessionRequest* request;
KThread* client_thread;
{
KScopedSchedulerLock sl{kernel};
// Ensure that we can service the request.
R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
// Ensure we aren't already servicing a request.
R_UNLESS(m_current_request == nullptr, ResultNotFound);
// Ensure we have a request to service.
R_UNLESS(!m_request_list.empty(), ResultNotFound);
// Pop the first request from the list.
request = &m_request_list.front();
m_request_list.pop_front();
// Get the thread for the request.
client_thread = request->GetThread();
R_UNLESS(client_thread != nullptr, ResultSessionClosed);
// Open the client thread.
client_thread->Open();
}
SCOPE_EXIT({ client_thread->Close(); });
// Set the request as our current.
m_current_request = request;
// Get the client address.
uintptr_t client_message = request->GetAddress();
size_t client_buffer_size = request->GetSize();
// bool recv_list_broken = false;
// Receive the message.
Core::Memory::Memory& memory{kernel.System().Memory()};
if (out_context != nullptr) {
// HLE request.
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
*out_context = std::make_shared<HLERequestContext>(kernel, memory, this, client_thread);
(*out_context)->SetSessionRequestManager(manager);
(*out_context)
->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
cmd_buf);
} else {
KThread* server_thread{GetCurrentThreadPointer(kernel)};
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
auto* src_msg_buffer = memory.GetPointer(client_message);
auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
}
// We succeeded.
return ResultSuccess;
}
void KServerSession::CleanupRequests() {
KScopedLightLock lk(m_lock);
// Clean up any pending requests.
while (true) {
// Get the next request.
KSessionRequest* request = nullptr;
{
KScopedSchedulerLock sl{kernel};
if (m_current_request) {
// Choose the current request if we have one.
request = m_current_request;
m_current_request = nullptr;
} else if (!m_request_list.empty()) {
// Pop the request from the front of the list.
request = &m_request_list.front();
m_request_list.pop_front();
}
}
// If there's no request, we're done.
if (request == nullptr) {
break;
}
// Close a reference to the request once it's cleaned up.
SCOPE_EXIT({ request->Close(); });
// Extract relevant information from the request.
// const uintptr_t client_message = request->GetAddress();
// const size_t client_buffer_size = request->GetSize();
KThread* client_thread = request->GetThread();
KEvent* event = request->GetEvent();
// KProcess *server_process = request->GetServerProcess();
// KProcess *client_process = (client_thread != nullptr) ?
// client_thread->GetOwnerProcess() : nullptr;
// KProcessPageTable *client_page_table = (client_process != nullptr) ?
// &client_process->GetPageTable() : nullptr;
// Cleanup the mappings.
// Result result = CleanupMap(request, server_process, client_page_table);
// If there's a client thread, update it.
if (client_thread != nullptr) {
if (event != nullptr) {
// // We need to reply async.
// ReplyAsyncError(client_process, client_message, client_buffer_size,
// (R_SUCCEEDED(result) ? ResultSessionClosed : result));
// // Unlock the client buffer.
// NOTE: Nintendo does not check the result of this.
// client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
// Signal the event.
event->Signal();
} else {
// End the client thread's wait.
KScopedSchedulerLock sl{kernel};
if (!client_thread->IsTerminationRequested()) {
client_thread->EndWait(ResultSessionClosed);
}
}
}
}
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <tuple>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/memory.h"
namespace Kernel {
using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
KServerSession::KServerSession(KernelCore& kernel_)
: KSynchronizationObject{kernel_}, m_lock{kernel_} {}
KServerSession::~KServerSession() = default;
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) {
// Set member variables.
parent = parent_session_;
name = std::move(name_);
}
void KServerSession::Destroy() {
parent->OnServerClosed();
this->CleanupRequests();
parent->Close();
}
void KServerSession::OnClientClosed() {
KScopedLightLock lk{m_lock};
// Handle any pending requests.
KSessionRequest* prev_request = nullptr;
while (true) {
// Declare variables for processing the request.
KSessionRequest* request = nullptr;
KEvent* event = nullptr;
KThread* thread = nullptr;
bool cur_request = false;
bool terminate = false;
// Get the next request.
{
KScopedSchedulerLock sl{kernel};
if (m_current_request != nullptr && m_current_request != prev_request) {
// Set the request, open a reference as we process it.
request = m_current_request;
request->Open();
cur_request = true;
// Get thread and event for the request.
thread = request->GetThread();
event = request->GetEvent();
// If the thread is terminating, handle that.
if (thread->IsTerminationRequested()) {
request->ClearThread();
request->ClearEvent();
terminate = true;
}
prev_request = request;
} else if (!m_request_list.empty()) {
// Pop the request from the front of the list.
request = std::addressof(m_request_list.front());
m_request_list.pop_front();
// Get thread and event for the request.
thread = request->GetThread();
event = request->GetEvent();
}
}
// If there are no requests, we're done.
if (request == nullptr) {
break;
}
// All requests must have threads.
ASSERT(thread != nullptr);
// Ensure that we close the request when done.
SCOPE_EXIT({ request->Close(); });
// If we're terminating, close a reference to the thread and event.
if (terminate) {
thread->Close();
if (event != nullptr) {
event->Close();
}
}
// If we need to, reply.
if (event != nullptr && !cur_request) {
// There must be no mappings.
ASSERT(request->GetSendCount() == 0);
ASSERT(request->GetReceiveCount() == 0);
ASSERT(request->GetExchangeCount() == 0);
// // Get the process and page table.
// KProcess *client_process = thread->GetOwnerProcess();
// auto &client_pt = client_process->GetPageTable();
// // Reply to the request.
// ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
// ResultSessionClosed);
// // Unlock the buffer.
// // NOTE: Nintendo does not check the result of this.
// client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
// Signal the event.
event->Signal();
}
}
// Notify.
this->NotifyAvailable(ResultSessionClosed);
}
bool KServerSession::IsSignaled() const {
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
// If the client is closed, we're always signaled.
if (parent->IsClientClosed()) {
return true;
}
// Otherwise, we're signaled if we have a request and aren't handling one.
return !m_request_list.empty() && m_current_request == nullptr;
}
Result KServerSession::OnRequest(KSessionRequest* request) {
// Create the wait queue.
ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
{
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
// Ensure that we can handle new requests.
R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed);
// Check that we're not terminating.
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
// Get whether we're empty.
const bool was_empty = m_request_list.empty();
// Add the request to the list.
request->Open();
m_request_list.push_back(*request);
// If we were empty, signal.
if (was_empty) {
this->NotifyAvailable();
}
// If we have a request event, this is asynchronous, and we don't need to wait.
R_SUCCEED_IF(request->GetEvent() != nullptr);
// This is a synchronous request, so we should wait for our request to complete.
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
GetCurrentThread(kernel).BeginWait(&wait_queue);
}
return GetCurrentThread(kernel).GetWaitResult();
}
Result KServerSession::SendReply(bool is_hle) {
// Lock the session.
KScopedLightLock lk{m_lock};
// Get the request.
KSessionRequest* request;
{
KScopedSchedulerLock sl{kernel};
// Get the current request.
request = m_current_request;
R_UNLESS(request != nullptr, ResultInvalidState);
// Clear the current request, since we're processing it.
m_current_request = nullptr;
if (!m_request_list.empty()) {
this->NotifyAvailable();
}
}
// Close reference to the request once we're done processing it.
SCOPE_EXIT({ request->Close(); });
// Extract relevant information from the request.
const uintptr_t client_message = request->GetAddress();
const size_t client_buffer_size = request->GetSize();
KThread* client_thread = request->GetThread();
KEvent* event = request->GetEvent();
// Check whether we're closed.
const bool closed = (client_thread == nullptr || parent->IsClientClosed());
Result result = ResultSuccess;
if (!closed) {
// If we're not closed, send the reply.
if (is_hle) {
// HLE servers write directly to a pointer to the thread command buffer. Therefore
// the reply has already been written in this case.
} else {
Core::Memory::Memory& memory{kernel.System().Memory()};
KThread* server_thread{GetCurrentThreadPointer(kernel)};
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
auto* dst_msg_buffer = memory.GetPointer(client_message);
std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
}
} else {
result = ResultSessionClosed;
}
// Select a result for the client.
Result client_result = result;
if (closed && R_SUCCEEDED(result)) {
result = ResultSessionClosed;
client_result = ResultSessionClosed;
} else {
result = ResultSuccess;
}
// If there's a client thread, update it.
if (client_thread != nullptr) {
if (event != nullptr) {
// // Get the client process/page table.
// KProcess *client_process = client_thread->GetOwnerProcess();
// KPageTable *client_page_table = &client_process->PageTable();
// // If we need to, reply with an async error.
// if (R_FAILED(client_result)) {
// ReplyAsyncError(client_process, client_message, client_buffer_size,
// client_result);
// }
// // Unlock the client buffer.
// // NOTE: Nintendo does not check the result of this.
// client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
// Signal the event.
event->Signal();
} else {
// End the client thread's wait.
KScopedSchedulerLock sl{kernel};
if (!client_thread->IsTerminationRequested()) {
client_thread->EndWait(client_result);
}
}
}
return result;
}
Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context,
std::weak_ptr<SessionRequestManager> manager) {
// Lock the session.
KScopedLightLock lk{m_lock};
// Get the request and client thread.
KSessionRequest* request;
KThread* client_thread;
{
KScopedSchedulerLock sl{kernel};
// Ensure that we can service the request.
R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
// Ensure we aren't already servicing a request.
R_UNLESS(m_current_request == nullptr, ResultNotFound);
// Ensure we have a request to service.
R_UNLESS(!m_request_list.empty(), ResultNotFound);
// Pop the first request from the list.
request = &m_request_list.front();
m_request_list.pop_front();
// Get the thread for the request.
client_thread = request->GetThread();
R_UNLESS(client_thread != nullptr, ResultSessionClosed);
// Open the client thread.
client_thread->Open();
}
SCOPE_EXIT({ client_thread->Close(); });
// Set the request as our current.
m_current_request = request;
// Get the client address.
uintptr_t client_message = request->GetAddress();
size_t client_buffer_size = request->GetSize();
// bool recv_list_broken = false;
// Receive the message.
Core::Memory::Memory& memory{kernel.System().Memory()};
if (out_context != nullptr) {
// HLE request.
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
*out_context = std::make_shared<HLERequestContext>(kernel, memory, this, client_thread);
(*out_context)->SetSessionRequestManager(manager);
(*out_context)
->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
cmd_buf);
} else {
KThread* server_thread{GetCurrentThreadPointer(kernel)};
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
auto* src_msg_buffer = memory.GetPointer(client_message);
auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
}
// We succeeded.
return ResultSuccess;
}
void KServerSession::CleanupRequests() {
KScopedLightLock lk(m_lock);
// Clean up any pending requests.
while (true) {
// Get the next request.
KSessionRequest* request = nullptr;
{
KScopedSchedulerLock sl{kernel};
if (m_current_request) {
// Choose the current request if we have one.
request = m_current_request;
m_current_request = nullptr;
} else if (!m_request_list.empty()) {
// Pop the request from the front of the list.
request = &m_request_list.front();
m_request_list.pop_front();
}
}
// If there's no request, we're done.
if (request == nullptr) {
break;
}
// Close a reference to the request once it's cleaned up.
SCOPE_EXIT({ request->Close(); });
// Extract relevant information from the request.
// const uintptr_t client_message = request->GetAddress();
// const size_t client_buffer_size = request->GetSize();
KThread* client_thread = request->GetThread();
KEvent* event = request->GetEvent();
// KProcess *server_process = request->GetServerProcess();
// KProcess *client_process = (client_thread != nullptr) ?
// client_thread->GetOwnerProcess() : nullptr;
// KProcessPageTable *client_page_table = (client_process != nullptr) ?
// &client_process->GetPageTable() : nullptr;
// Cleanup the mappings.
// Result result = CleanupMap(request, server_process, client_page_table);
// If there's a client thread, update it.
if (client_thread != nullptr) {
if (event != nullptr) {
// // We need to reply async.
// ReplyAsyncError(client_process, client_message, client_buffer_size,
// (R_SUCCEEDED(result) ? ResultSessionClosed : result));
// // Unlock the client buffer.
// NOTE: Nintendo does not check the result of this.
// client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
// Signal the event.
event->Signal();
} else {
// End the client thread's wait.
KScopedSchedulerLock sl{kernel};
if (!client_thread->IsTerminationRequested()) {
client_thread->EndWait(ResultSessionClosed);
}
}
}
}
}
} // namespace Kernel

View File

@@ -1,76 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include <memory>
#include <string>
#include <utility>
#include <boost/intrusive/list.hpp>
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_session_request.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/result.h"
namespace Kernel {
class HLERequestContext;
class KernelCore;
class KSession;
class SessionRequestManager;
class KThread;
class KServerSession final : public KSynchronizationObject,
public boost::intrusive::list_base_hook<> {
KERNEL_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject);
friend class ServiceThread;
public:
explicit KServerSession(KernelCore& kernel_);
~KServerSession() override;
void Destroy() override;
void Initialize(KSession* parent_session_, std::string&& name_);
KSession* GetParent() {
return parent;
}
const KSession* GetParent() const {
return parent;
}
bool IsSignaled() const override;
void OnClientClosed();
/// TODO: flesh these out to match the real kernel
Result OnRequest(KSessionRequest* request);
Result SendReply(bool is_hle = false);
Result ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context = nullptr,
std::weak_ptr<SessionRequestManager> manager = {});
Result SendReplyHLE() {
return SendReply(true);
}
private:
/// Frees up waiting client sessions when this server session is about to die
void CleanupRequests();
/// KSession that owns this KServerSession
KSession* parent{};
/// List of threads which are pending a reply.
boost::intrusive::list<KSessionRequest> m_request_list;
KSessionRequest* m_current_request{};
KLightLock m_lock;
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include <memory>
#include <string>
#include <utility>
#include <boost/intrusive/list.hpp>
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_session_request.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/result.h"
namespace Kernel {
class HLERequestContext;
class KernelCore;
class KSession;
class SessionRequestManager;
class KThread;
class KServerSession final : public KSynchronizationObject,
public boost::intrusive::list_base_hook<> {
KERNEL_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject);
friend class ServiceThread;
public:
explicit KServerSession(KernelCore& kernel_);
~KServerSession() override;
void Destroy() override;
void Initialize(KSession* parent_session_, std::string&& name_);
KSession* GetParent() {
return parent;
}
const KSession* GetParent() const {
return parent;
}
bool IsSignaled() const override;
void OnClientClosed();
/// TODO: flesh these out to match the real kernel
Result OnRequest(KSessionRequest* request);
Result SendReply(bool is_hle = false);
Result ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context = nullptr,
std::weak_ptr<SessionRequestManager> manager = {});
Result SendReplyHLE() {
return SendReply(true);
}
private:
/// Frees up waiting client sessions when this server session is about to die
void CleanupRequests();
/// KSession that owns this KServerSession
KSession* parent{};
/// List of threads which are pending a reply.
boost::intrusive::list<KSessionRequest> m_request_list;
KSessionRequest* m_current_request{};
KLightLock m_lock;
};
} // namespace Kernel

View File

@@ -1,83 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h"
namespace Kernel {
KSession::KSession(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
KSession::~KSession() = default;
void KSession::Initialize(KClientPort* port_, const std::string& name_) {
// Increment reference count.
// Because reference count is one on creation, this will result
// in a reference count of two. Thus, when both server and client are closed
// this object will be destroyed.
Open();
// Create our sub sessions.
KAutoObject::Create(std::addressof(server));
KAutoObject::Create(std::addressof(client));
// Initialize our sub sessions.
server.Initialize(this, name_ + ":Server");
client.Initialize(this, name_ + ":Client");
// Set state and name.
SetState(State::Normal);
name = name_;
// Set our owner process.
process = kernel.CurrentProcess();
process->Open();
// Set our port.
port = port_;
if (port != nullptr) {
port->Open();
}
// Mark initialized.
initialized = true;
}
void KSession::Finalize() {
if (port == nullptr) {
return;
}
port->OnSessionFinalized();
port->Close();
}
void KSession::OnServerClosed() {
if (GetState() != State::Normal) {
return;
}
SetState(State::ServerClosed);
client.OnServerClosed();
}
void KSession::OnClientClosed() {
if (GetState() != State::Normal) {
return;
}
SetState(State::ClientClosed);
server.OnClientClosed();
}
void KSession::PostDestroy(uintptr_t arg) {
// Release the session count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1);
owner->Close();
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h"
namespace Kernel {
KSession::KSession(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
KSession::~KSession() = default;
void KSession::Initialize(KClientPort* port_, const std::string& name_) {
// Increment reference count.
// Because reference count is one on creation, this will result
// in a reference count of two. Thus, when both server and client are closed
// this object will be destroyed.
Open();
// Create our sub sessions.
KAutoObject::Create(std::addressof(server));
KAutoObject::Create(std::addressof(client));
// Initialize our sub sessions.
server.Initialize(this, name_ + ":Server");
client.Initialize(this, name_ + ":Client");
// Set state and name.
SetState(State::Normal);
name = name_;
// Set our owner process.
process = kernel.CurrentProcess();
process->Open();
// Set our port.
port = port_;
if (port != nullptr) {
port->Open();
}
// Mark initialized.
initialized = true;
}
void KSession::Finalize() {
if (port == nullptr) {
return;
}
port->OnSessionFinalized();
port->Close();
}
void KSession::OnServerClosed() {
if (GetState() != State::Normal) {
return;
}
SetState(State::ServerClosed);
client.OnServerClosed();
}
void KSession::OnClientClosed() {
if (GetState() != State::Normal) {
return;
}
SetState(State::ClientClosed);
server.OnClientClosed();
}
void KSession::PostDestroy(uintptr_t arg) {
// Release the session count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1);
owner->Close();
}
} // namespace Kernel

View File

@@ -1,99 +1,99 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <string>
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class SessionRequestManager;
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject);
public:
explicit KSession(KernelCore& kernel_);
~KSession() override;
void Initialize(KClientPort* port_, const std::string& name_);
void Finalize() override;
bool IsInitialized() const override {
return initialized;
}
uintptr_t GetPostDestroyArgument() const override {
return reinterpret_cast<uintptr_t>(process);
}
static void PostDestroy(uintptr_t arg);
void OnServerClosed();
void OnClientClosed();
bool IsServerClosed() const {
return this->GetState() != State::Normal;
}
bool IsClientClosed() const {
return this->GetState() != State::Normal;
}
KClientSession& GetClientSession() {
return client;
}
KServerSession& GetServerSession() {
return server;
}
const KClientSession& GetClientSession() const {
return client;
}
const KServerSession& GetServerSession() const {
return server;
}
const KClientPort* GetParent() const {
return port;
}
KClientPort* GetParent() {
return port;
}
private:
enum class State : u8 {
Invalid = 0,
Normal = 1,
ClientClosed = 2,
ServerClosed = 3,
};
void SetState(State state) {
atomic_state = static_cast<u8>(state);
}
State GetState() const {
return static_cast<State>(atomic_state.load(std::memory_order_relaxed));
}
KServerSession server;
KClientSession client;
std::atomic<std::underlying_type_t<State>> atomic_state{
static_cast<std::underlying_type_t<State>>(State::Invalid)};
KClientPort* port{};
KProcess* process{};
bool initialized{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <string>
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class SessionRequestManager;
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject);
public:
explicit KSession(KernelCore& kernel_);
~KSession() override;
void Initialize(KClientPort* port_, const std::string& name_);
void Finalize() override;
bool IsInitialized() const override {
return initialized;
}
uintptr_t GetPostDestroyArgument() const override {
return reinterpret_cast<uintptr_t>(process);
}
static void PostDestroy(uintptr_t arg);
void OnServerClosed();
void OnClientClosed();
bool IsServerClosed() const {
return this->GetState() != State::Normal;
}
bool IsClientClosed() const {
return this->GetState() != State::Normal;
}
KClientSession& GetClientSession() {
return client;
}
KServerSession& GetServerSession() {
return server;
}
const KClientSession& GetClientSession() const {
return client;
}
const KServerSession& GetServerSession() const {
return server;
}
const KClientPort* GetParent() const {
return port;
}
KClientPort* GetParent() {
return port;
}
private:
enum class State : u8 {
Invalid = 0,
Normal = 1,
ClientClosed = 2,
ServerClosed = 3,
};
void SetState(State state) {
atomic_state = static_cast<u8>(state);
}
State GetState() const {
return static_cast<State>(atomic_state.load(std::memory_order_relaxed));
}
KServerSession server;
KClientSession client;
std::atomic<std::underlying_type_t<State>> atomic_state{
static_cast<std::underlying_type_t<State>>(State::Invalid)};
KClientPort* port{};
KProcess* process{};
bool initialized{};
};
} // namespace Kernel

View File

@@ -1,61 +1,61 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_session_request.h"
namespace Kernel {
Result KSessionRequest::SessionMappings::PushMap(VAddr client, VAddr server, size_t size,
KMemoryState state, size_t index) {
// At most 15 buffers of each type (4-bit descriptor counts).
ASSERT(index < ((1ul << 4) - 1) * 3);
// Get the mapping.
Mapping* mapping;
if (index < NumStaticMappings) {
mapping = &m_static_mappings[index];
} else {
// Allocate a page for the extra mappings.
if (m_mappings == nullptr) {
KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel);
R_UNLESS(page_buffer != nullptr, ResultOutOfMemory);
m_mappings = reinterpret_cast<Mapping*>(page_buffer);
}
mapping = &m_mappings[index - NumStaticMappings];
}
// Set the mapping.
mapping->Set(client, server, size, state);
return ResultSuccess;
}
Result KSessionRequest::SessionMappings::PushSend(VAddr client, VAddr server, size_t size,
KMemoryState state) {
ASSERT(m_num_recv == 0);
ASSERT(m_num_exch == 0);
return this->PushMap(client, server, size, state, m_num_send++);
}
Result KSessionRequest::SessionMappings::PushReceive(VAddr client, VAddr server, size_t size,
KMemoryState state) {
ASSERT(m_num_exch == 0);
return this->PushMap(client, server, size, state, m_num_send + m_num_recv++);
}
Result KSessionRequest::SessionMappings::PushExchange(VAddr client, VAddr server, size_t size,
KMemoryState state) {
return this->PushMap(client, server, size, state, m_num_send + m_num_recv + m_num_exch++);
}
void KSessionRequest::SessionMappings::Finalize() {
if (m_mappings) {
KPageBuffer::Free(kernel, reinterpret_cast<KPageBuffer*>(m_mappings));
m_mappings = nullptr;
}
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_session_request.h"
namespace Kernel {
Result KSessionRequest::SessionMappings::PushMap(VAddr client, VAddr server, size_t size,
KMemoryState state, size_t index) {
// At most 15 buffers of each type (4-bit descriptor counts).
ASSERT(index < ((1ul << 4) - 1) * 3);
// Get the mapping.
Mapping* mapping;
if (index < NumStaticMappings) {
mapping = &m_static_mappings[index];
} else {
// Allocate a page for the extra mappings.
if (m_mappings == nullptr) {
KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel);
R_UNLESS(page_buffer != nullptr, ResultOutOfMemory);
m_mappings = reinterpret_cast<Mapping*>(page_buffer);
}
mapping = &m_mappings[index - NumStaticMappings];
}
// Set the mapping.
mapping->Set(client, server, size, state);
return ResultSuccess;
}
Result KSessionRequest::SessionMappings::PushSend(VAddr client, VAddr server, size_t size,
KMemoryState state) {
ASSERT(m_num_recv == 0);
ASSERT(m_num_exch == 0);
return this->PushMap(client, server, size, state, m_num_send++);
}
Result KSessionRequest::SessionMappings::PushReceive(VAddr client, VAddr server, size_t size,
KMemoryState state) {
ASSERT(m_num_exch == 0);
return this->PushMap(client, server, size, state, m_num_send + m_num_recv++);
}
Result KSessionRequest::SessionMappings::PushExchange(VAddr client, VAddr server, size_t size,
KMemoryState state) {
return this->PushMap(client, server, size, state, m_num_send + m_num_recv + m_num_exch++);
}
void KSessionRequest::SessionMappings::Finalize() {
if (m_mappings) {
KPageBuffer::Free(kernel, reinterpret_cast<KPageBuffer*>(m_mappings));
m_mappings = nullptr;
}
}
} // namespace Kernel

View File

@@ -1,306 +1,306 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KSessionRequest final : public KSlabAllocated<KSessionRequest>,
public KAutoObject,
public boost::intrusive::list_base_hook<> {
KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject);
public:
class SessionMappings {
private:
static constexpr size_t NumStaticMappings = 8;
class Mapping {
public:
constexpr void Set(VAddr c, VAddr s, size_t sz, KMemoryState st) {
m_client_address = c;
m_server_address = s;
m_size = sz;
m_state = st;
}
constexpr VAddr GetClientAddress() const {
return m_client_address;
}
constexpr VAddr GetServerAddress() const {
return m_server_address;
}
constexpr size_t GetSize() const {
return m_size;
}
constexpr KMemoryState GetMemoryState() const {
return m_state;
}
private:
VAddr m_client_address;
VAddr m_server_address;
size_t m_size;
KMemoryState m_state;
};
public:
explicit SessionMappings(KernelCore& kernel_) : kernel(kernel_) {}
void Initialize() {}
void Finalize();
size_t GetSendCount() const {
return m_num_send;
}
size_t GetReceiveCount() const {
return m_num_recv;
}
size_t GetExchangeCount() const {
return m_num_exch;
}
Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state);
Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state);
Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state);
VAddr GetSendClientAddress(size_t i) const {
return GetSendMapping(i).GetClientAddress();
}
VAddr GetSendServerAddress(size_t i) const {
return GetSendMapping(i).GetServerAddress();
}
size_t GetSendSize(size_t i) const {
return GetSendMapping(i).GetSize();
}
KMemoryState GetSendMemoryState(size_t i) const {
return GetSendMapping(i).GetMemoryState();
}
VAddr GetReceiveClientAddress(size_t i) const {
return GetReceiveMapping(i).GetClientAddress();
}
VAddr GetReceiveServerAddress(size_t i) const {
return GetReceiveMapping(i).GetServerAddress();
}
size_t GetReceiveSize(size_t i) const {
return GetReceiveMapping(i).GetSize();
}
KMemoryState GetReceiveMemoryState(size_t i) const {
return GetReceiveMapping(i).GetMemoryState();
}
VAddr GetExchangeClientAddress(size_t i) const {
return GetExchangeMapping(i).GetClientAddress();
}
VAddr GetExchangeServerAddress(size_t i) const {
return GetExchangeMapping(i).GetServerAddress();
}
size_t GetExchangeSize(size_t i) const {
return GetExchangeMapping(i).GetSize();
}
KMemoryState GetExchangeMemoryState(size_t i) const {
return GetExchangeMapping(i).GetMemoryState();
}
private:
Result PushMap(VAddr client, VAddr server, size_t size, KMemoryState state, size_t index);
const Mapping& GetSendMapping(size_t i) const {
ASSERT(i < m_num_send);
const size_t index = i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
const Mapping& GetReceiveMapping(size_t i) const {
ASSERT(i < m_num_recv);
const size_t index = m_num_send + i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
const Mapping& GetExchangeMapping(size_t i) const {
ASSERT(i < m_num_exch);
const size_t index = m_num_send + m_num_recv + i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
private:
KernelCore& kernel;
std::array<Mapping, NumStaticMappings> m_static_mappings;
Mapping* m_mappings{};
u8 m_num_send{};
u8 m_num_recv{};
u8 m_num_exch{};
};
public:
explicit KSessionRequest(KernelCore& kernel_) : KAutoObject(kernel_), m_mappings(kernel_) {}
static KSessionRequest* Create(KernelCore& kernel) {
KSessionRequest* req = KSessionRequest::Allocate(kernel);
if (req != nullptr) [[likely]] {
KAutoObject::Create(req);
}
return req;
}
void Destroy() override {
this->Finalize();
KSessionRequest::Free(kernel, this);
}
void Initialize(KEvent* event, uintptr_t address, size_t size) {
m_mappings.Initialize();
m_thread = GetCurrentThreadPointer(kernel);
m_event = event;
m_address = address;
m_size = size;
m_thread->Open();
if (m_event != nullptr) {
m_event->Open();
}
}
static void PostDestroy(uintptr_t arg) {}
KThread* GetThread() const {
return m_thread;
}
KEvent* GetEvent() const {
return m_event;
}
uintptr_t GetAddress() const {
return m_address;
}
size_t GetSize() const {
return m_size;
}
KProcess* GetServerProcess() const {
return m_server;
}
void SetServerProcess(KProcess* process) {
m_server = process;
m_server->Open();
}
void ClearThread() {
m_thread = nullptr;
}
void ClearEvent() {
m_event = nullptr;
}
size_t GetSendCount() const {
return m_mappings.GetSendCount();
}
size_t GetReceiveCount() const {
return m_mappings.GetReceiveCount();
}
size_t GetExchangeCount() const {
return m_mappings.GetExchangeCount();
}
Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushSend(client, server, size, state);
}
Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushReceive(client, server, size, state);
}
Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushExchange(client, server, size, state);
}
VAddr GetSendClientAddress(size_t i) const {
return m_mappings.GetSendClientAddress(i);
}
VAddr GetSendServerAddress(size_t i) const {
return m_mappings.GetSendServerAddress(i);
}
size_t GetSendSize(size_t i) const {
return m_mappings.GetSendSize(i);
}
KMemoryState GetSendMemoryState(size_t i) const {
return m_mappings.GetSendMemoryState(i);
}
VAddr GetReceiveClientAddress(size_t i) const {
return m_mappings.GetReceiveClientAddress(i);
}
VAddr GetReceiveServerAddress(size_t i) const {
return m_mappings.GetReceiveServerAddress(i);
}
size_t GetReceiveSize(size_t i) const {
return m_mappings.GetReceiveSize(i);
}
KMemoryState GetReceiveMemoryState(size_t i) const {
return m_mappings.GetReceiveMemoryState(i);
}
VAddr GetExchangeClientAddress(size_t i) const {
return m_mappings.GetExchangeClientAddress(i);
}
VAddr GetExchangeServerAddress(size_t i) const {
return m_mappings.GetExchangeServerAddress(i);
}
size_t GetExchangeSize(size_t i) const {
return m_mappings.GetExchangeSize(i);
}
KMemoryState GetExchangeMemoryState(size_t i) const {
return m_mappings.GetExchangeMemoryState(i);
}
private:
// NOTE: This is public and virtual in Nintendo's kernel.
void Finalize() override {
m_mappings.Finalize();
if (m_thread) {
m_thread->Close();
}
if (m_event) {
m_event->Close();
}
if (m_server) {
m_server->Close();
}
}
private:
SessionMappings m_mappings;
KThread* m_thread{};
KProcess* m_server{};
KEvent* m_event{};
uintptr_t m_address{};
size_t m_size{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KSessionRequest final : public KSlabAllocated<KSessionRequest>,
public KAutoObject,
public boost::intrusive::list_base_hook<> {
KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject);
public:
class SessionMappings {
private:
static constexpr size_t NumStaticMappings = 8;
class Mapping {
public:
constexpr void Set(VAddr c, VAddr s, size_t sz, KMemoryState st) {
m_client_address = c;
m_server_address = s;
m_size = sz;
m_state = st;
}
constexpr VAddr GetClientAddress() const {
return m_client_address;
}
constexpr VAddr GetServerAddress() const {
return m_server_address;
}
constexpr size_t GetSize() const {
return m_size;
}
constexpr KMemoryState GetMemoryState() const {
return m_state;
}
private:
VAddr m_client_address;
VAddr m_server_address;
size_t m_size;
KMemoryState m_state;
};
public:
explicit SessionMappings(KernelCore& kernel_) : kernel(kernel_) {}
void Initialize() {}
void Finalize();
size_t GetSendCount() const {
return m_num_send;
}
size_t GetReceiveCount() const {
return m_num_recv;
}
size_t GetExchangeCount() const {
return m_num_exch;
}
Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state);
Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state);
Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state);
VAddr GetSendClientAddress(size_t i) const {
return GetSendMapping(i).GetClientAddress();
}
VAddr GetSendServerAddress(size_t i) const {
return GetSendMapping(i).GetServerAddress();
}
size_t GetSendSize(size_t i) const {
return GetSendMapping(i).GetSize();
}
KMemoryState GetSendMemoryState(size_t i) const {
return GetSendMapping(i).GetMemoryState();
}
VAddr GetReceiveClientAddress(size_t i) const {
return GetReceiveMapping(i).GetClientAddress();
}
VAddr GetReceiveServerAddress(size_t i) const {
return GetReceiveMapping(i).GetServerAddress();
}
size_t GetReceiveSize(size_t i) const {
return GetReceiveMapping(i).GetSize();
}
KMemoryState GetReceiveMemoryState(size_t i) const {
return GetReceiveMapping(i).GetMemoryState();
}
VAddr GetExchangeClientAddress(size_t i) const {
return GetExchangeMapping(i).GetClientAddress();
}
VAddr GetExchangeServerAddress(size_t i) const {
return GetExchangeMapping(i).GetServerAddress();
}
size_t GetExchangeSize(size_t i) const {
return GetExchangeMapping(i).GetSize();
}
KMemoryState GetExchangeMemoryState(size_t i) const {
return GetExchangeMapping(i).GetMemoryState();
}
private:
Result PushMap(VAddr client, VAddr server, size_t size, KMemoryState state, size_t index);
const Mapping& GetSendMapping(size_t i) const {
ASSERT(i < m_num_send);
const size_t index = i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
const Mapping& GetReceiveMapping(size_t i) const {
ASSERT(i < m_num_recv);
const size_t index = m_num_send + i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
const Mapping& GetExchangeMapping(size_t i) const {
ASSERT(i < m_num_exch);
const size_t index = m_num_send + m_num_recv + i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
private:
KernelCore& kernel;
std::array<Mapping, NumStaticMappings> m_static_mappings;
Mapping* m_mappings{};
u8 m_num_send{};
u8 m_num_recv{};
u8 m_num_exch{};
};
public:
explicit KSessionRequest(KernelCore& kernel_) : KAutoObject(kernel_), m_mappings(kernel_) {}
static KSessionRequest* Create(KernelCore& kernel) {
KSessionRequest* req = KSessionRequest::Allocate(kernel);
if (req != nullptr) [[likely]] {
KAutoObject::Create(req);
}
return req;
}
void Destroy() override {
this->Finalize();
KSessionRequest::Free(kernel, this);
}
void Initialize(KEvent* event, uintptr_t address, size_t size) {
m_mappings.Initialize();
m_thread = GetCurrentThreadPointer(kernel);
m_event = event;
m_address = address;
m_size = size;
m_thread->Open();
if (m_event != nullptr) {
m_event->Open();
}
}
static void PostDestroy(uintptr_t arg) {}
KThread* GetThread() const {
return m_thread;
}
KEvent* GetEvent() const {
return m_event;
}
uintptr_t GetAddress() const {
return m_address;
}
size_t GetSize() const {
return m_size;
}
KProcess* GetServerProcess() const {
return m_server;
}
void SetServerProcess(KProcess* process) {
m_server = process;
m_server->Open();
}
void ClearThread() {
m_thread = nullptr;
}
void ClearEvent() {
m_event = nullptr;
}
size_t GetSendCount() const {
return m_mappings.GetSendCount();
}
size_t GetReceiveCount() const {
return m_mappings.GetReceiveCount();
}
size_t GetExchangeCount() const {
return m_mappings.GetExchangeCount();
}
Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushSend(client, server, size, state);
}
Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushReceive(client, server, size, state);
}
Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushExchange(client, server, size, state);
}
VAddr GetSendClientAddress(size_t i) const {
return m_mappings.GetSendClientAddress(i);
}
VAddr GetSendServerAddress(size_t i) const {
return m_mappings.GetSendServerAddress(i);
}
size_t GetSendSize(size_t i) const {
return m_mappings.GetSendSize(i);
}
KMemoryState GetSendMemoryState(size_t i) const {
return m_mappings.GetSendMemoryState(i);
}
VAddr GetReceiveClientAddress(size_t i) const {
return m_mappings.GetReceiveClientAddress(i);
}
VAddr GetReceiveServerAddress(size_t i) const {
return m_mappings.GetReceiveServerAddress(i);
}
size_t GetReceiveSize(size_t i) const {
return m_mappings.GetReceiveSize(i);
}
KMemoryState GetReceiveMemoryState(size_t i) const {
return m_mappings.GetReceiveMemoryState(i);
}
VAddr GetExchangeClientAddress(size_t i) const {
return m_mappings.GetExchangeClientAddress(i);
}
VAddr GetExchangeServerAddress(size_t i) const {
return m_mappings.GetExchangeServerAddress(i);
}
size_t GetExchangeSize(size_t i) const {
return m_mappings.GetExchangeSize(i);
}
KMemoryState GetExchangeMemoryState(size_t i) const {
return m_mappings.GetExchangeMemoryState(i);
}
private:
// NOTE: This is public and virtual in Nintendo's kernel.
void Finalize() override {
m_mappings.Finalize();
if (m_thread) {
m_thread->Close();
}
if (m_event) {
m_event->Close();
}
if (m_server) {
m_server->Close();
}
}
private:
SessionMappings m_mappings;
KThread* m_thread{};
KProcess* m_server{};
KEvent* m_event{};
uintptr_t m_address{};
size_t m_size{};
};
} // namespace Kernel

View File

@@ -1,96 +1,96 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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/svc_results.h"
namespace Kernel {
KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
KSharedMemory::~KSharedMemory() {
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
}
Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_,
Svc::MemoryPermission user_permission_, PAddr physical_address_,
std::size_t size_, std::string name_) {
// Set members.
owner_process = owner_process_;
device_memory = &device_memory_;
page_list = std::move(page_list_);
owner_permission = owner_permission_;
user_permission = user_permission_;
physical_address = physical_address_;
size = size_;
name = std::move(name_);
// Get the resource limit.
KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
// Reserve memory for ourselves.
KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory,
size_);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Commit our reservation.
memory_reservation.Commit();
// Set our resource limit.
resource_limit = reslimit;
resource_limit->Open();
// Mark initialized.
is_initialized = true;
// Clear all pages in the memory.
std::memset(device_memory_.GetPointer<void>(physical_address_), 0, size_);
return ResultSuccess;
}
void KSharedMemory::Finalize() {
// Release the memory reservation.
resource_limit->Release(LimitableResource::PhysicalMemory, size);
resource_limit->Close();
// Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList>::Finalize();
}
Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size,
Svc::MemoryPermission permissions) {
const u64 page_count{(map_size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
UNIMPLEMENTED_MSG("Page count does not match");
}
const Svc::MemoryPermission 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, KMemoryState::Shared,
ConvertToKMemoryPermission(permissions));
}
Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
const u64 page_count{(unmap_size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
UNIMPLEMENTED_MSG("Page count does not match");
}
return target_process.PageTable().UnmapPages(address, page_list, KMemoryState::Shared);
}
} // namespace Kernel
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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/svc_results.h"
namespace Kernel {
KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
KSharedMemory::~KSharedMemory() {
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
}
Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_,
Svc::MemoryPermission user_permission_, PAddr physical_address_,
std::size_t size_, std::string name_) {
// Set members.
owner_process = owner_process_;
device_memory = &device_memory_;
page_list = std::move(page_list_);
owner_permission = owner_permission_;
user_permission = user_permission_;
physical_address = physical_address_;
size = size_;
name = std::move(name_);
// Get the resource limit.
KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
// Reserve memory for ourselves.
KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory,
size_);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Commit our reservation.
memory_reservation.Commit();
// Set our resource limit.
resource_limit = reslimit;
resource_limit->Open();
// Mark initialized.
is_initialized = true;
// Clear all pages in the memory.
std::memset(device_memory_.GetPointer<void>(physical_address_), 0, size_);
return ResultSuccess;
}
void KSharedMemory::Finalize() {
// Release the memory reservation.
resource_limit->Release(LimitableResource::PhysicalMemory, size);
resource_limit->Close();
// Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList>::Finalize();
}
Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size,
Svc::MemoryPermission permissions) {
const u64 page_count{(map_size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
UNIMPLEMENTED_MSG("Page count does not match");
}
const Svc::MemoryPermission 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, KMemoryState::Shared,
ConvertToKMemoryPermission(permissions));
}
Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
const u64 page_count{(unmap_size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
UNIMPLEMENTED_MSG("Page count does not match");
}
return target_process.PageTable().UnmapPages(address, page_list, KMemoryState::Shared);
}
} // namespace Kernel

View File

@@ -1,88 +1,88 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KSharedMemory final
: public KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KSharedMemory, KAutoObject);
public:
explicit KSharedMemory(KernelCore& kernel_);
~KSharedMemory() override;
Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_,
Svc::MemoryPermission user_permission_, PAddr physical_address_,
std::size_t size_, std::string name_);
/**
* Maps a shared memory block to an address in the target process' address space
* @param target_process Process on which to map the memory block
* @param address Address in system memory to map shared memory block to
* @param map_size Size of the shared memory block to map
* @param permissions Memory block map permissions (specified by SVC field)
*/
Result Map(KProcess& target_process, VAddr address, std::size_t map_size,
Svc::MemoryPermission permissions);
/**
* Unmaps a shared memory block from an address in the target process' address space
* @param target_process Process on which to unmap the memory block
* @param address Address in system memory to unmap shared memory block
* @param unmap_size Size of the shared memory block to unmap
*/
Result Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size);
/**
* Gets a pointer to the shared memory block
* @param offset Offset from the start of the shared memory block to get pointer
* @return A pointer to the shared memory block from the specified offset
*/
u8* GetPointer(std::size_t offset = 0) {
return device_memory->GetPointer<u8>(physical_address + offset);
}
/**
* Gets a pointer to the shared memory block
* @param offset Offset from the start of the shared memory block to get pointer
* @return A pointer to the shared memory block from the specified offset
*/
const u8* GetPointer(std::size_t offset = 0) const {
return device_memory->GetPointer<u8>(physical_address + offset);
}
void Finalize() override;
bool IsInitialized() const override {
return is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
private:
Core::DeviceMemory* device_memory;
KProcess* owner_process{};
KPageGroup page_list;
Svc::MemoryPermission owner_permission{};
Svc::MemoryPermission user_permission{};
PAddr physical_address{};
std::size_t size{};
KResourceLimit* resource_limit{};
bool is_initialized{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KSharedMemory final
: public KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KSharedMemory, KAutoObject);
public:
explicit KSharedMemory(KernelCore& kernel_);
~KSharedMemory() override;
Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_,
Svc::MemoryPermission user_permission_, PAddr physical_address_,
std::size_t size_, std::string name_);
/**
* Maps a shared memory block to an address in the target process' address space
* @param target_process Process on which to map the memory block
* @param address Address in system memory to map shared memory block to
* @param map_size Size of the shared memory block to map
* @param permissions Memory block map permissions (specified by SVC field)
*/
Result Map(KProcess& target_process, VAddr address, std::size_t map_size,
Svc::MemoryPermission permissions);
/**
* Unmaps a shared memory block from an address in the target process' address space
* @param target_process Process on which to unmap the memory block
* @param address Address in system memory to unmap shared memory block
* @param unmap_size Size of the shared memory block to unmap
*/
Result Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size);
/**
* Gets a pointer to the shared memory block
* @param offset Offset from the start of the shared memory block to get pointer
* @return A pointer to the shared memory block from the specified offset
*/
u8* GetPointer(std::size_t offset = 0) {
return device_memory->GetPointer<u8>(physical_address + offset);
}
/**
* Gets a pointer to the shared memory block
* @param offset Offset from the start of the shared memory block to get pointer
* @return A pointer to the shared memory block from the specified offset
*/
const u8* GetPointer(std::size_t offset = 0) const {
return device_memory->GetPointer<u8>(physical_address + offset);
}
void Finalize() override;
bool IsInitialized() const override {
return is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
private:
Core::DeviceMemory* device_memory;
KProcess* owner_process{};
KPageGroup page_list;
Svc::MemoryPermission owner_permission{};
Svc::MemoryPermission user_permission{};
PAddr physical_address{};
std::size_t size{};
KResourceLimit* resource_limit{};
bool is_initialized{};
};
} // namespace Kernel

View File

@@ -1,42 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <boost/intrusive/list.hpp>
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KSharedMemory;
class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>,
public boost::intrusive::list_base_hook<> {
public:
explicit KSharedMemoryInfo(KernelCore&) {}
KSharedMemoryInfo() = default;
constexpr void Initialize(KSharedMemory* shmem) {
shared_memory = shmem;
}
constexpr KSharedMemory* GetSharedMemory() const {
return shared_memory;
}
constexpr void Open() {
++reference_count;
}
constexpr bool Close() {
return (--reference_count) == 0;
}
private:
KSharedMemory* shared_memory{};
size_t reference_count{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <boost/intrusive/list.hpp>
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KSharedMemory;
class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>,
public boost::intrusive::list_base_hook<> {
public:
explicit KSharedMemoryInfo(KernelCore&) {}
KSharedMemoryInfo() = default;
constexpr void Initialize(KSharedMemory* shmem) {
shared_memory = shmem;
}
constexpr KSharedMemory* GetSharedMemory() const {
return shared_memory;
}
constexpr void Open() {
++reference_count;
}
constexpr bool Close() {
return (--reference_count) == 0;
}
private:
KSharedMemory* shared_memory{};
size_t reference_count{};
};
} // namespace Kernel

View File

@@ -1,213 +1,213 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/spin_lock.h"
namespace Kernel {
class KernelCore;
namespace impl {
class KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapImpl);
YUZU_NON_MOVEABLE(KSlabHeapImpl);
public:
struct Node {
Node* next{};
};
public:
constexpr KSlabHeapImpl() = default;
void Initialize() {
ASSERT(m_head == nullptr);
}
Node* GetHead() const {
return m_head;
}
void* Allocate() {
// KScopedInterruptDisable di;
m_lock.lock();
Node* ret = m_head;
if (ret != nullptr) [[likely]] {
m_head = ret->next;
}
m_lock.unlock();
return ret;
}
void Free(void* obj) {
// KScopedInterruptDisable di;
m_lock.lock();
Node* node = static_cast<Node*>(obj);
node->next = m_head;
m_head = node;
m_lock.unlock();
}
private:
std::atomic<Node*> m_head{};
Common::SpinLock m_lock;
};
} // namespace impl
template <bool SupportDynamicExpansion>
class KSlabHeapBase : protected impl::KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapBase);
YUZU_NON_MOVEABLE(KSlabHeapBase);
private:
size_t m_obj_size{};
uintptr_t m_peak{};
uintptr_t m_start{};
uintptr_t m_end{};
private:
void UpdatePeakImpl(uintptr_t obj) {
static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
std::atomic_ref<uintptr_t> peak_ref(m_peak);
const uintptr_t alloc_peak = obj + this->GetObjectSize();
uintptr_t cur_peak = m_peak;
do {
if (alloc_peak <= cur_peak) {
break;
}
} while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
}
public:
constexpr KSlabHeapBase() = default;
bool Contains(uintptr_t address) const {
return m_start <= address && address < m_end;
}
void Initialize(size_t obj_size, void* memory, size_t memory_size) {
// Ensure we don't initialize a slab using null memory.
ASSERT(memory != nullptr);
// Set our object size.
m_obj_size = obj_size;
// Initialize the base allocator.
KSlabHeapImpl::Initialize();
// Set our tracking variables.
const size_t num_obj = (memory_size / obj_size);
m_start = reinterpret_cast<uintptr_t>(memory);
m_end = m_start + num_obj * obj_size;
m_peak = m_start;
// Free the objects.
u8* cur = reinterpret_cast<u8*>(m_end);
for (size_t i = 0; i < num_obj; i++) {
cur -= obj_size;
KSlabHeapImpl::Free(cur);
}
}
size_t GetSlabHeapSize() const {
return (m_end - m_start) / this->GetObjectSize();
}
size_t GetObjectSize() const {
return m_obj_size;
}
void* Allocate() {
void* obj = KSlabHeapImpl::Allocate();
return obj;
}
void Free(void* obj) {
// Don't allow freeing an object that wasn't allocated from this heap.
const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
ASSERT(contained);
KSlabHeapImpl::Free(obj);
}
size_t GetObjectIndex(const void* obj) const {
if constexpr (SupportDynamicExpansion) {
if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) {
return std::numeric_limits<size_t>::max();
}
}
return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
}
size_t GetPeakIndex() const {
return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
}
uintptr_t GetSlabHeapAddress() const {
return m_start;
}
size_t GetNumRemaining() const {
// Only calculate the number of remaining objects under debug configuration.
return 0;
}
};
template <typename T>
class KSlabHeap final : public KSlabHeapBase<false> {
private:
using BaseHeap = KSlabHeapBase<false>;
public:
constexpr KSlabHeap() = default;
void Initialize(void* memory, size_t memory_size) {
BaseHeap::Initialize(sizeof(T), memory, memory_size);
}
T* Allocate() {
T* obj = static_cast<T*>(BaseHeap::Allocate());
if (obj != nullptr) [[likely]] {
std::construct_at(obj);
}
return obj;
}
T* Allocate(KernelCore& kernel) {
T* obj = static_cast<T*>(BaseHeap::Allocate());
if (obj != nullptr) [[likely]] {
std::construct_at(obj, kernel);
}
return obj;
}
void Free(T* obj) {
BaseHeap::Free(obj);
}
size_t GetObjectIndex(const T* obj) const {
return BaseHeap::GetObjectIndex(obj);
}
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/spin_lock.h"
namespace Kernel {
class KernelCore;
namespace impl {
class KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapImpl);
YUZU_NON_MOVEABLE(KSlabHeapImpl);
public:
struct Node {
Node* next{};
};
public:
constexpr KSlabHeapImpl() = default;
void Initialize() {
ASSERT(m_head == nullptr);
}
Node* GetHead() const {
return m_head;
}
void* Allocate() {
// KScopedInterruptDisable di;
m_lock.lock();
Node* ret = m_head;
if (ret != nullptr) [[likely]] {
m_head = ret->next;
}
m_lock.unlock();
return ret;
}
void Free(void* obj) {
// KScopedInterruptDisable di;
m_lock.lock();
Node* node = static_cast<Node*>(obj);
node->next = m_head;
m_head = node;
m_lock.unlock();
}
private:
std::atomic<Node*> m_head{};
Common::SpinLock m_lock;
};
} // namespace impl
template <bool SupportDynamicExpansion>
class KSlabHeapBase : protected impl::KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapBase);
YUZU_NON_MOVEABLE(KSlabHeapBase);
private:
size_t m_obj_size{};
uintptr_t m_peak{};
uintptr_t m_start{};
uintptr_t m_end{};
private:
void UpdatePeakImpl(uintptr_t obj) {
static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
std::atomic_ref<uintptr_t> peak_ref(m_peak);
const uintptr_t alloc_peak = obj + this->GetObjectSize();
uintptr_t cur_peak = m_peak;
do {
if (alloc_peak <= cur_peak) {
break;
}
} while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
}
public:
constexpr KSlabHeapBase() = default;
bool Contains(uintptr_t address) const {
return m_start <= address && address < m_end;
}
void Initialize(size_t obj_size, void* memory, size_t memory_size) {
// Ensure we don't initialize a slab using null memory.
ASSERT(memory != nullptr);
// Set our object size.
m_obj_size = obj_size;
// Initialize the base allocator.
KSlabHeapImpl::Initialize();
// Set our tracking variables.
const size_t num_obj = (memory_size / obj_size);
m_start = reinterpret_cast<uintptr_t>(memory);
m_end = m_start + num_obj * obj_size;
m_peak = m_start;
// Free the objects.
u8* cur = reinterpret_cast<u8*>(m_end);
for (size_t i = 0; i < num_obj; i++) {
cur -= obj_size;
KSlabHeapImpl::Free(cur);
}
}
size_t GetSlabHeapSize() const {
return (m_end - m_start) / this->GetObjectSize();
}
size_t GetObjectSize() const {
return m_obj_size;
}
void* Allocate() {
void* obj = KSlabHeapImpl::Allocate();
return obj;
}
void Free(void* obj) {
// Don't allow freeing an object that wasn't allocated from this heap.
const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
ASSERT(contained);
KSlabHeapImpl::Free(obj);
}
size_t GetObjectIndex(const void* obj) const {
if constexpr (SupportDynamicExpansion) {
if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) {
return std::numeric_limits<size_t>::max();
}
}
return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
}
size_t GetPeakIndex() const {
return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
}
uintptr_t GetSlabHeapAddress() const {
return m_start;
}
size_t GetNumRemaining() const {
// Only calculate the number of remaining objects under debug configuration.
return 0;
}
};
template <typename T>
class KSlabHeap final : public KSlabHeapBase<false> {
private:
using BaseHeap = KSlabHeapBase<false>;
public:
constexpr KSlabHeap() = default;
void Initialize(void* memory, size_t memory_size) {
BaseHeap::Initialize(sizeof(T), memory, memory_size);
}
T* Allocate() {
T* obj = static_cast<T*>(BaseHeap::Allocate());
if (obj != nullptr) [[likely]] {
std::construct_at(obj);
}
return obj;
}
T* Allocate(KernelCore& kernel) {
T* obj = static_cast<T*>(BaseHeap::Allocate());
if (obj != nullptr) [[likely]] {
std::construct_at(obj, kernel);
}
return obj;
}
void Free(T* obj) {
BaseHeap::Free(obj);
}
size_t GetObjectIndex(const T* obj) const {
return BaseHeap::GetObjectIndex(obj);
}
};
} // namespace Kernel

View File

@@ -1,20 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_spin_lock.h"
namespace Kernel {
void KSpinLock::Lock() {
lck.lock();
}
void KSpinLock::Unlock() {
lck.unlock();
}
bool KSpinLock::TryLock() {
return lck.try_lock();
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_spin_lock.h"
namespace Kernel {
void KSpinLock::Lock() {
lck.lock();
}
void KSpinLock::Unlock() {
lck.unlock();
}
bool KSpinLock::TryLock() {
return lck.try_lock();
}
} // namespace Kernel

View File

@@ -1,38 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#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::mutex lck;
};
// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
using KAlignedSpinLock = KSpinLock;
using KNotAlignedSpinLock = KSpinLock;
using KScopedSpinLock = KScopedLock<KSpinLock>;
using KScopedAlignedSpinLock = KScopedLock<KAlignedSpinLock>;
using KScopedNotAlignedSpinLock = KScopedLock<KNotAlignedSpinLock>;
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#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::mutex lck;
};
// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
using KAlignedSpinLock = KSpinLock;
using KNotAlignedSpinLock = KSpinLock;
using KScopedSpinLock = KScopedLock<KSpinLock>;
using KScopedAlignedSpinLock = KScopedLock<KAlignedSpinLock>;
using KScopedNotAlignedSpinLock = KScopedLock<KNotAlignedSpinLock>;
} // namespace Kernel

View File

@@ -1,177 +1,177 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.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/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
public:
ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
KSynchronizationObject::ThreadListNode* n, s32 c)
: KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
Result wait_result) override {
// Determine the sync index, and unlink all nodes.
s32 sync_index = -1;
for (auto i = 0; i < m_count; ++i) {
// Check if this is the signaled object.
if (m_objects[i] == signaled_object && sync_index == -1) {
sync_index = i;
}
// Unlink the current node from the current object.
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
}
// Set the waiting thread's sync index.
waiting_thread->SetSyncedIndex(sync_index);
// Set the waiting thread as not cancellable.
waiting_thread->ClearCancellable();
// Invoke the base end wait handler.
KThreadQueue::EndWait(waiting_thread, wait_result);
}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove all nodes from our list.
for (auto i = 0; i < m_count; ++i) {
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
}
// Set the waiting thread as not cancellable.
waiting_thread->ClearCancellable();
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KSynchronizationObject** m_objects;
KSynchronizationObject::ThreadListNode* m_nodes;
s32 m_count;
};
} // namespace
void KSynchronizationObject::Finalize() {
this->OnFinalizeSynchronizationObject();
KAutoObject::Finalize();
}
Result KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
KSynchronizationObject** objects, const s32 num_objects,
s64 timeout) {
// Allocate space on stack for thread nodes.
std::vector<ThreadListNode> thread_nodes(num_objects);
// Prepare for wait.
KThread* thread = GetCurrentThreadPointer(kernel_ctx);
ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
thread_nodes.data(), num_objects);
{
// Setup the scheduling lock and sleep.
KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
// Check if the thread should terminate.
if (thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Check if any of the objects are already signaled.
for (auto i = 0; i < num_objects; ++i) {
ASSERT(objects[i] != nullptr);
if (objects[i]->IsSignaled()) {
*out_index = i;
slp.CancelSleep();
return ResultSuccess;
}
}
// Check if the timeout is zero.
if (timeout == 0) {
slp.CancelSleep();
return ResultTimedOut;
}
// Check if waiting was canceled.
if (thread->IsWaitCancelled()) {
slp.CancelSleep();
thread->ClearWaitCancelled();
return ResultCancelled;
}
// Add the waiters.
for (auto i = 0; i < num_objects; ++i) {
thread_nodes[i].thread = thread;
thread_nodes[i].next = nullptr;
objects[i]->LinkNode(std::addressof(thread_nodes[i]));
}
// Mark the thread as cancellable.
thread->SetCancellable();
// Clear the thread's synced index.
thread->SetSyncedIndex(-1);
// Wait for an object to be signaled.
thread->BeginWait(std::addressof(wait_queue));
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
}
// Set the output index.
*out_index = thread->GetSyncedIndex();
// Get the wait result.
return thread->GetWaitResult();
}
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
: KAutoObjectWithList{kernel_} {}
KSynchronizationObject::~KSynchronizationObject() = default;
void KSynchronizationObject::NotifyAvailable(Result result) {
KScopedSchedulerLock sl(kernel);
// If we're not signaled, we've nothing to notify.
if (!this->IsSignaled()) {
return;
}
// Iterate over each thread.
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
cur_node->thread->NotifyAvailable(this, result);
}
}
std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
std::vector<KThread*> threads;
// If debugging, dump the list of waiters.
{
KScopedSchedulerLock lock(kernel);
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
threads.emplace_back(cur_node->thread);
}
}
return threads;
}
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.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/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
public:
ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
KSynchronizationObject::ThreadListNode* n, s32 c)
: KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
Result wait_result) override {
// Determine the sync index, and unlink all nodes.
s32 sync_index = -1;
for (auto i = 0; i < m_count; ++i) {
// Check if this is the signaled object.
if (m_objects[i] == signaled_object && sync_index == -1) {
sync_index = i;
}
// Unlink the current node from the current object.
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
}
// Set the waiting thread's sync index.
waiting_thread->SetSyncedIndex(sync_index);
// Set the waiting thread as not cancellable.
waiting_thread->ClearCancellable();
// Invoke the base end wait handler.
KThreadQueue::EndWait(waiting_thread, wait_result);
}
void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove all nodes from our list.
for (auto i = 0; i < m_count; ++i) {
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
}
// Set the waiting thread as not cancellable.
waiting_thread->ClearCancellable();
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KSynchronizationObject** m_objects;
KSynchronizationObject::ThreadListNode* m_nodes;
s32 m_count;
};
} // namespace
void KSynchronizationObject::Finalize() {
this->OnFinalizeSynchronizationObject();
KAutoObject::Finalize();
}
Result KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
KSynchronizationObject** objects, const s32 num_objects,
s64 timeout) {
// Allocate space on stack for thread nodes.
std::vector<ThreadListNode> thread_nodes(num_objects);
// Prepare for wait.
KThread* thread = GetCurrentThreadPointer(kernel_ctx);
ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
thread_nodes.data(), num_objects);
{
// Setup the scheduling lock and sleep.
KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
// Check if the thread should terminate.
if (thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Check if any of the objects are already signaled.
for (auto i = 0; i < num_objects; ++i) {
ASSERT(objects[i] != nullptr);
if (objects[i]->IsSignaled()) {
*out_index = i;
slp.CancelSleep();
return ResultSuccess;
}
}
// Check if the timeout is zero.
if (timeout == 0) {
slp.CancelSleep();
return ResultTimedOut;
}
// Check if waiting was canceled.
if (thread->IsWaitCancelled()) {
slp.CancelSleep();
thread->ClearWaitCancelled();
return ResultCancelled;
}
// Add the waiters.
for (auto i = 0; i < num_objects; ++i) {
thread_nodes[i].thread = thread;
thread_nodes[i].next = nullptr;
objects[i]->LinkNode(std::addressof(thread_nodes[i]));
}
// Mark the thread as cancellable.
thread->SetCancellable();
// Clear the thread's synced index.
thread->SetSyncedIndex(-1);
// Wait for an object to be signaled.
thread->BeginWait(std::addressof(wait_queue));
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
}
// Set the output index.
*out_index = thread->GetSyncedIndex();
// Get the wait result.
return thread->GetWaitResult();
}
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
: KAutoObjectWithList{kernel_} {}
KSynchronizationObject::~KSynchronizationObject() = default;
void KSynchronizationObject::NotifyAvailable(Result result) {
KScopedSchedulerLock sl(kernel);
// If we're not signaled, we've nothing to notify.
if (!this->IsSignaled()) {
return;
}
// Iterate over each thread.
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
cur_node->thread->NotifyAvailable(this, result);
}
}
std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
std::vector<KThread*> threads;
// If debugging, dump the list of waiters.
{
KScopedSchedulerLock lock(kernel);
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
threads.emplace_back(cur_node->thread);
}
}
return threads;
}
} // namespace Kernel

View File

@@ -1,85 +1,85 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class Synchronization;
class KThread;
/// Class that represents a Kernel object that a thread can be waiting on
class KSynchronizationObject : public KAutoObjectWithList {
KERNEL_AUTOOBJECT_TRAITS(KSynchronizationObject, KAutoObject);
public:
struct ThreadListNode {
ThreadListNode* next{};
KThread* thread{};
};
[[nodiscard]] static Result Wait(KernelCore& kernel, s32* out_index,
KSynchronizationObject** objects, const s32 num_objects,
s64 timeout);
void Finalize() override;
[[nodiscard]] virtual bool IsSignaled() const = 0;
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
void LinkNode(ThreadListNode* node_) {
// Link the node to the list.
if (thread_list_tail == nullptr) {
thread_list_head = node_;
} else {
thread_list_tail->next = node_;
}
thread_list_tail = node_;
}
void UnlinkNode(ThreadListNode* node_) {
// Unlink the node from the list.
ThreadListNode* prev_ptr =
reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
ThreadListNode* prev_val = nullptr;
ThreadListNode *prev, *tail_prev;
do {
prev = prev_ptr;
prev_ptr = prev_ptr->next;
tail_prev = prev_val;
prev_val = prev_ptr;
} while (prev_ptr != node_);
if (thread_list_tail == node_) {
thread_list_tail = tail_prev;
}
prev->next = node_->next;
}
protected:
explicit KSynchronizationObject(KernelCore& kernel);
~KSynchronizationObject() override;
virtual void OnFinalizeSynchronizationObject() {}
void NotifyAvailable(Result result);
void NotifyAvailable() {
return this->NotifyAvailable(ResultSuccess);
}
private:
ThreadListNode* thread_list_head{};
ThreadListNode* thread_list_tail{};
};
} // namespace Kernel
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class Synchronization;
class KThread;
/// Class that represents a Kernel object that a thread can be waiting on
class KSynchronizationObject : public KAutoObjectWithList {
KERNEL_AUTOOBJECT_TRAITS(KSynchronizationObject, KAutoObject);
public:
struct ThreadListNode {
ThreadListNode* next{};
KThread* thread{};
};
[[nodiscard]] static Result Wait(KernelCore& kernel, s32* out_index,
KSynchronizationObject** objects, const s32 num_objects,
s64 timeout);
void Finalize() override;
[[nodiscard]] virtual bool IsSignaled() const = 0;
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
void LinkNode(ThreadListNode* node_) {
// Link the node to the list.
if (thread_list_tail == nullptr) {
thread_list_head = node_;
} else {
thread_list_tail->next = node_;
}
thread_list_tail = node_;
}
void UnlinkNode(ThreadListNode* node_) {
// Unlink the node from the list.
ThreadListNode* prev_ptr =
reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
ThreadListNode* prev_val = nullptr;
ThreadListNode *prev, *tail_prev;
do {
prev = prev_ptr;
prev_ptr = prev_ptr->next;
tail_prev = prev_val;
prev_val = prev_ptr;
} while (prev_ptr != node_);
if (thread_list_tail == node_) {
thread_list_tail = tail_prev;
}
prev->next = node_->next;
}
protected:
explicit KSynchronizationObject(KernelCore& kernel);
~KSynchronizationObject() override;
virtual void OnFinalizeSynchronizationObject() {}
void NotifyAvailable(Result result);
void NotifyAvailable() {
return this->NotifyAvailable(ResultSuccess);
}
private:
ThreadListNode* thread_list_head{};
ThreadListNode* thread_list_tail{};
};
} // namespace Kernel

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