Compare commits
7 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a40ed17c28 | ||
|
|
56beb2c85b | ||
|
|
0f82eb83f9 | ||
|
|
41881d8d26 | ||
|
|
c67bab2512 | ||
|
|
9d6059ba9f | ||
|
|
7d9ddb9150 |
@@ -24,7 +24,7 @@ matrix:
|
||||
- os: osx
|
||||
env: NAME="macos build"
|
||||
sudo: false
|
||||
osx_image: xcode10.1
|
||||
osx_image: xcode10
|
||||
install: "./.travis/macos/deps.sh"
|
||||
script: "./.travis/macos/build.sh"
|
||||
after_success: "./.travis/macos/upload.sh"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set -o pipefail
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.14
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
@@ -163,6 +163,12 @@ else()
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
# Fix GCC C++17 and Boost.ICL incompatibility (needed to build dynarmic)
|
||||
# See https://bugzilla.redhat.com/show_bug.cgi?id=1485641#c1
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-new-ttp-matching")
|
||||
endif()
|
||||
|
||||
# Set file offset size to 64 bits.
|
||||
#
|
||||
# On modern Unixes, this is typically already the case. The lone exception is
|
||||
@@ -179,9 +185,9 @@ set_property(DIRECTORY APPEND PROPERTY
|
||||
# System imported libraries
|
||||
# ======================
|
||||
|
||||
find_package(Boost 1.66.0 QUIET)
|
||||
find_package(Boost 1.63.0 QUIET)
|
||||
if (NOT Boost_FOUND)
|
||||
message(STATUS "Boost 1.66.0 or newer not found, falling back to externals")
|
||||
message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
|
||||
|
||||
set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
|
||||
set(Boost_NO_SYSTEM_PATHS OFF)
|
||||
|
||||
@@ -73,7 +73,6 @@ set(HASH_FILES
|
||||
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/memory.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/texture.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/other.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
|
||||
|
||||
2
externals/opus
vendored
2
externals/opus
vendored
Submodule externals/opus updated: 562f8ba555...b2871922a1
@@ -12,7 +12,7 @@
|
||||
#include "common/ring_buffer.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
@@ -113,7 +113,7 @@ private:
|
||||
|
||||
CubebSink::CubebSink(std::string_view target_device_name) {
|
||||
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
#endif
|
||||
|
||||
@@ -152,7 +152,7 @@ CubebSink::~CubebSink() {
|
||||
|
||||
cubeb_destroy(ctx);
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
if (SUCCEEDED(com_init_result)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ private:
|
||||
cubeb_devid output_device{};
|
||||
std::vector<SinkStreamPtr> sink_streams;
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
u32 com_init_result = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -47,7 +47,6 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/memory.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/texture.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/other.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
|
||||
@@ -92,14 +91,10 @@ add_library(common STATIC
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
math_util.h
|
||||
memory_hook.cpp
|
||||
memory_hook.h
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
misc.cpp
|
||||
page_table.cpp
|
||||
page_table.h
|
||||
param_package.cpp
|
||||
param_package.h
|
||||
quaternion.h
|
||||
@@ -118,8 +113,6 @@ add_library(common STATIC
|
||||
threadsafe_queue.h
|
||||
timer.cpp
|
||||
timer.h
|
||||
uint128.cpp
|
||||
uint128.h
|
||||
vector_math.h
|
||||
web_result.h
|
||||
)
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
/*
|
||||
* Abstract bitfield class
|
||||
@@ -109,9 +108,15 @@
|
||||
* symptoms.
|
||||
*/
|
||||
#pragma pack(1)
|
||||
template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>
|
||||
template <std::size_t Position, std::size_t Bits, typename T>
|
||||
struct BitField {
|
||||
private:
|
||||
// We hide the copy assigment operator here, because the default copy
|
||||
// assignment would copy the full storage value, rather than just the bits
|
||||
// relevant to this particular bit field.
|
||||
// We don't delete it because we want BitField to be trivially copyable.
|
||||
constexpr BitField& operator=(const BitField&) = default;
|
||||
|
||||
// UnderlyingType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
@@ -122,8 +127,6 @@ private:
|
||||
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
|
||||
using StorageType = std::make_unsigned_t<UnderlyingType>;
|
||||
|
||||
using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type;
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr std::size_t position = Position;
|
||||
@@ -160,20 +163,16 @@ public:
|
||||
BitField(T val) = delete;
|
||||
BitField& operator=(T val) = delete;
|
||||
|
||||
constexpr BitField() noexcept = default;
|
||||
|
||||
constexpr BitField(const BitField&) noexcept = default;
|
||||
constexpr BitField& operator=(const BitField&) noexcept = default;
|
||||
|
||||
constexpr BitField(BitField&&) noexcept = default;
|
||||
constexpr BitField& operator=(BitField&&) noexcept = default;
|
||||
// Force default constructor to be created
|
||||
// so that we can use this within unions
|
||||
constexpr BitField() = default;
|
||||
|
||||
constexpr FORCE_INLINE operator T() const {
|
||||
return Value();
|
||||
}
|
||||
|
||||
constexpr FORCE_INLINE void Assign(const T& value) {
|
||||
storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
|
||||
storage = (storage & ~mask) | FormatValue(value);
|
||||
}
|
||||
|
||||
constexpr T Value() const {
|
||||
@@ -185,7 +184,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
StorageTypeWithEndian storage;
|
||||
StorageType storage;
|
||||
|
||||
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
|
||||
|
||||
@@ -196,6 +195,3 @@ private:
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField");
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
template <std::size_t Position, std::size_t Bits, typename T>
|
||||
using BitFieldBE = BitField<Position, Bits, T, BETag>;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/page_table.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {}
|
||||
|
||||
PageTable::~PageTable() = default;
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits) {
|
||||
const std::size_t num_page_table_entries = 1ULL
|
||||
<< (address_space_width_in_bits - page_size_in_bits);
|
||||
|
||||
pointers.resize(num_page_table_entries);
|
||||
attributes.resize(num_page_table_entries);
|
||||
|
||||
// The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
|
||||
// vector size is subsequently decreased (via resize), the vector might not automatically
|
||||
// actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
|
||||
// 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
|
||||
|
||||
pointers.shrink_to_fit();
|
||||
attributes.shrink_to_fit();
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_hook.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum class PageType : u8 {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
||||
Special,
|
||||
};
|
||||
|
||||
struct SpecialRegion {
|
||||
enum class Type {
|
||||
DebugHook,
|
||||
IODevice,
|
||||
} type;
|
||||
|
||||
MemoryHookPointer handler;
|
||||
|
||||
bool operator<(const SpecialRegion& other) const {
|
||||
return std::tie(type, handler) < std::tie(other.type, other.handler);
|
||||
}
|
||||
|
||||
bool operator==(const SpecialRegion& other) const {
|
||||
return std::tie(type, handler) == std::tie(other.type, other.handler);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
|
||||
* mimics the way a real CPU page table works.
|
||||
*/
|
||||
struct PageTable {
|
||||
explicit PageTable(std::size_t page_size_in_bits);
|
||||
~PageTable();
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accomodate enough pages within
|
||||
* a given address space.
|
||||
*
|
||||
* @param address_space_width_in_bits The address size width in bits.
|
||||
*/
|
||||
void Resize(std::size_t address_space_width_in_bits);
|
||||
|
||||
/**
|
||||
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||
* corresponding entry in the `attributes` vector is of type `Memory`.
|
||||
*/
|
||||
std::vector<u8*> pointers;
|
||||
|
||||
/**
|
||||
* Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
|
||||
* of type `Special`.
|
||||
*/
|
||||
boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
|
||||
|
||||
/**
|
||||
* Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
|
||||
* the corresponding entry in `pointers` MUST be set to null.
|
||||
*/
|
||||
std::vector<PageType> attributes;
|
||||
|
||||
const std::size_t page_size_in_bits{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <cstdlib>
|
||||
#elif defined(__linux__)
|
||||
@@ -172,7 +170,7 @@ struct swap_struct_t {
|
||||
using swapped_t = swap_struct_t;
|
||||
|
||||
protected:
|
||||
T value;
|
||||
T value = T();
|
||||
|
||||
static T swap(T v) {
|
||||
return F::swap(v);
|
||||
@@ -607,154 +605,52 @@ struct swap_double_t {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct swap_enum_t {
|
||||
static_assert(std::is_enum_v<T>);
|
||||
using base = std::underlying_type_t<T>;
|
||||
|
||||
public:
|
||||
swap_enum_t() = default;
|
||||
swap_enum_t(const T& v) : value(swap(v)) {}
|
||||
|
||||
swap_enum_t& operator=(const T& v) {
|
||||
value = swap(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T() const {
|
||||
return swap(value);
|
||||
}
|
||||
|
||||
explicit operator base() const {
|
||||
return static_cast<base>(swap(value));
|
||||
}
|
||||
|
||||
protected:
|
||||
T value{};
|
||||
// clang-format off
|
||||
using swap_t = std::conditional_t<
|
||||
std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
|
||||
std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
|
||||
std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
|
||||
std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
|
||||
std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
|
||||
std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
|
||||
// clang-format on
|
||||
static T swap(T x) {
|
||||
return static_cast<T>(swap_t::swap(static_cast<base>(x)));
|
||||
}
|
||||
};
|
||||
|
||||
struct SwapTag {}; // Use the different endianness from the system
|
||||
struct KeepTag {}; // Use the same endianness as the system
|
||||
|
||||
template <typename T, typename Tag>
|
||||
struct AddEndian;
|
||||
|
||||
// KeepTag specializations
|
||||
|
||||
template <typename T>
|
||||
struct AddEndian<T, KeepTag> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
// SwapTag specializations
|
||||
|
||||
template <>
|
||||
struct AddEndian<u8, SwapTag> {
|
||||
using type = u8;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<u16, SwapTag> {
|
||||
using type = swap_struct_t<u16, swap_16_t<u16>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<u32, SwapTag> {
|
||||
using type = swap_struct_t<u32, swap_32_t<u32>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<u64, SwapTag> {
|
||||
using type = swap_struct_t<u64, swap_64_t<u64>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<s8, SwapTag> {
|
||||
using type = s8;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<s16, SwapTag> {
|
||||
using type = swap_struct_t<s16, swap_16_t<s16>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<s32, SwapTag> {
|
||||
using type = swap_struct_t<s32, swap_32_t<s32>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<s64, SwapTag> {
|
||||
using type = swap_struct_t<s64, swap_64_t<s64>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<float, SwapTag> {
|
||||
using type = swap_struct_t<float, swap_float_t<float>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<double, SwapTag> {
|
||||
using type = swap_struct_t<double, swap_double_t<double>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct AddEndian<T, SwapTag> {
|
||||
static_assert(std::is_enum_v<T>);
|
||||
using type = swap_enum_t<T>;
|
||||
};
|
||||
|
||||
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
|
||||
#if COMMON_LITTLE_ENDIAN
|
||||
using u16_le = u16;
|
||||
using u32_le = u32;
|
||||
using u64_le = u64;
|
||||
|
||||
using LETag = KeepTag;
|
||||
using BETag = SwapTag;
|
||||
using s16_le = s16;
|
||||
using s32_le = s32;
|
||||
using s64_le = s64;
|
||||
|
||||
using float_le = float;
|
||||
using double_le = double;
|
||||
|
||||
using u64_be = swap_struct_t<u64, swap_64_t<u64>>;
|
||||
using s64_be = swap_struct_t<s64, swap_64_t<s64>>;
|
||||
|
||||
using u32_be = swap_struct_t<u32, swap_32_t<u32>>;
|
||||
using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
|
||||
|
||||
using u16_be = swap_struct_t<u16, swap_16_t<u16>>;
|
||||
using s16_be = swap_struct_t<s16, swap_16_t<s16>>;
|
||||
|
||||
using float_be = swap_struct_t<float, swap_float_t<float>>;
|
||||
using double_be = swap_struct_t<double, swap_double_t<double>>;
|
||||
#else
|
||||
|
||||
using BETag = KeepTag;
|
||||
using LETag = SwapTag;
|
||||
using u64_le = swap_struct_t<u64, swap_64_t<u64>>;
|
||||
using s64_le = swap_struct_t<s64, swap_64_t<s64>>;
|
||||
|
||||
using u32_le = swap_struct_t<u32, swap_32_t<u32>>;
|
||||
using s32_le = swap_struct_t<s32, swap_32_t<s32>>;
|
||||
|
||||
using u16_le = swap_struct_t<u16, swap_16_t<u16>>;
|
||||
using s16_le = swap_struct_t<s16, swap_16_t<s16>>;
|
||||
|
||||
using float_le = swap_struct_t<float, swap_float_t<float>>;
|
||||
using double_le = swap_struct_t<double, swap_double_t<double>>;
|
||||
|
||||
using u16_be = u16;
|
||||
using u32_be = u32;
|
||||
using u64_be = u64;
|
||||
|
||||
using s16_be = s16;
|
||||
using s32_be = s32;
|
||||
using s64_be = s64;
|
||||
|
||||
using float_be = float;
|
||||
using double_be = double;
|
||||
|
||||
#endif
|
||||
|
||||
// Aliases for LE types
|
||||
using u16_le = AddEndian<u16, LETag>::type;
|
||||
using u32_le = AddEndian<u32, LETag>::type;
|
||||
using u64_le = AddEndian<u64, LETag>::type;
|
||||
|
||||
using s16_le = AddEndian<s16, LETag>::type;
|
||||
using s32_le = AddEndian<s32, LETag>::type;
|
||||
using s64_le = AddEndian<s64, LETag>::type;
|
||||
|
||||
template <typename T>
|
||||
using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>;
|
||||
|
||||
using float_le = AddEndian<float, LETag>::type;
|
||||
using double_le = AddEndian<double, LETag>::type;
|
||||
|
||||
// Aliases for BE types
|
||||
using u16_be = AddEndian<u16, BETag>::type;
|
||||
using u32_be = AddEndian<u32, BETag>::type;
|
||||
using u64_be = AddEndian<u64, BETag>::type;
|
||||
|
||||
using s16_be = AddEndian<s16, BETag>::type;
|
||||
using s32_be = AddEndian<s32, BETag>::type;
|
||||
using s64_be = AddEndian<s64, BETag>::type;
|
||||
|
||||
template <typename T>
|
||||
using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>;
|
||||
|
||||
using float_be = AddEndian<float, BETag>::type;
|
||||
using double_be = AddEndian<double, BETag>::type;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -110,9 +111,8 @@ struct ThreadQueueList {
|
||||
}
|
||||
|
||||
void remove(Priority priority, const T& thread_id) {
|
||||
Queue* const cur = &queues[priority];
|
||||
const auto iter = std::remove(cur->data.begin(), cur->data.end(), thread_id);
|
||||
cur->data.erase(iter, cur->data.end());
|
||||
Queue* cur = &queues[priority];
|
||||
boost::remove_erase(cur->data, thread_id);
|
||||
}
|
||||
|
||||
void rotate(Priority priority) {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
||||
#pragma intrinsic(_umul128)
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include "common/uint128.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
u128 Multiply64Into128(u64 a, u64 b) {
|
||||
u128 result;
|
||||
#ifdef _MSC_VER
|
||||
result[0] = _umul128(a, b, &result[1]);
|
||||
#else
|
||||
unsigned __int128 tmp = a;
|
||||
tmp *= b;
|
||||
std::memcpy(&result, &tmp, sizeof(u128));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
|
||||
u64 remainder = dividend[0] % divisor;
|
||||
u64 accum = dividend[0] / divisor;
|
||||
if (dividend[1] == 0)
|
||||
return {accum, remainder};
|
||||
// We ignore dividend[1] / divisor as that overflows
|
||||
const u64 first_segment = (dividend[1] % divisor) << 32;
|
||||
accum += (first_segment / divisor) << 32;
|
||||
const u64 second_segment = (first_segment % divisor) << 32;
|
||||
accum += (second_segment / divisor);
|
||||
remainder += second_segment % divisor;
|
||||
if (remainder >= divisor) {
|
||||
accum++;
|
||||
remainder -= divisor;
|
||||
}
|
||||
return {accum, remainder};
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
// This function multiplies 2 u64 values and produces a u128 value;
|
||||
u128 Multiply64Into128(u64 a, u64 b);
|
||||
|
||||
// This function divides a u128 by a u32 value and produces two u64 values:
|
||||
// the result of division and the remainder
|
||||
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
|
||||
|
||||
} // namespace Common
|
||||
@@ -84,6 +84,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_vector.h
|
||||
file_sys/xts_archive.cpp
|
||||
file_sys/xts_archive.h
|
||||
frontend/applets/controller.cpp
|
||||
frontend/applets/controller.h
|
||||
frontend/applets/profile_select.cpp
|
||||
frontend/applets/profile_select.h
|
||||
frontend/applets/software_keyboard.cpp
|
||||
@@ -107,8 +109,6 @@ add_library(core STATIC
|
||||
hle/kernel/client_port.h
|
||||
hle/kernel/client_session.cpp
|
||||
hle/kernel/client_session.h
|
||||
hle/kernel/code_set.cpp
|
||||
hle/kernel/code_set.h
|
||||
hle/kernel/errors.h
|
||||
hle/kernel/handle_table.cpp
|
||||
hle/kernel/handle_table.h
|
||||
@@ -171,6 +171,8 @@ add_library(core STATIC
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/am/applets/applets.cpp
|
||||
hle/service/am/applets/applets.h
|
||||
hle/service/am/applets/controller.cpp
|
||||
hle/service/am/applets/controller.h
|
||||
hle/service/am/applets/profile_select.cpp
|
||||
hle/service/am/applets/profile_select.h
|
||||
hle/service/am/applets/software_keyboard.cpp
|
||||
@@ -219,7 +221,6 @@ add_library(core STATIC
|
||||
hle/service/audio/audren_u.h
|
||||
hle/service/audio/codecctl.cpp
|
||||
hle/service/audio/codecctl.h
|
||||
hle/service/audio/errors.h
|
||||
hle/service/audio/hwopus.cpp
|
||||
hle/service/audio/hwopus.h
|
||||
hle/service/bcat/bcat.cpp
|
||||
@@ -421,6 +422,8 @@ add_library(core STATIC
|
||||
loader/deconstructed_rom_directory.h
|
||||
loader/elf.cpp
|
||||
loader/elf.h
|
||||
loader/linker.cpp
|
||||
loader/linker.h
|
||||
loader/loader.cpp
|
||||
loader/loader.h
|
||||
loader/nax.cpp
|
||||
@@ -437,6 +440,8 @@ add_library(core STATIC
|
||||
loader/xci.h
|
||||
memory.cpp
|
||||
memory.h
|
||||
memory_hook.cpp
|
||||
memory_hook.h
|
||||
memory_setup.h
|
||||
perf_stats.cpp
|
||||
perf_stats.h
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
@@ -120,7 +119,7 @@ public:
|
||||
return std::max(parent.core_timing.GetDowncount(), 0);
|
||||
}
|
||||
u64 GetCNTPCT() override {
|
||||
return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
|
||||
return parent.core_timing.GetTicks();
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
@@ -152,7 +151,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
|
||||
config.tpidr_el0 = &cb->tpidr_el0;
|
||||
config.dczid_el0 = 4;
|
||||
config.ctr_el0 = 0x8444c004;
|
||||
config.cntfrq_el0 = Timing::CNTFREQ;
|
||||
config.cntfrq_el0 = 19200000; // Value from fusee.
|
||||
|
||||
// Unpredictable instructions
|
||||
config.define_unpredictable_behaviour = true;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
|
||||
namespace Common {
|
||||
namespace Memory {
|
||||
struct PageTable;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ private:
|
||||
Timing::CoreTiming& core_timing;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
|
||||
Common::PageTable* current_page_table = nullptr;
|
||||
Memory::PageTable* current_page_table = nullptr;
|
||||
};
|
||||
|
||||
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
||||
|
||||
@@ -25,19 +25,18 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "frontend/applets/controller.h"
|
||||
#include "frontend/applets/profile_select.h"
|
||||
#include "frontend/applets/software_keyboard.h"
|
||||
#include "frontend/applets/web_browser.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu_asynch.h"
|
||||
#include "video_core/gpu_synch.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
@@ -79,7 +78,6 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
return vfs->OpenFile(path, FileSys::Mode::Read);
|
||||
}
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system) : kernel{system} {}
|
||||
|
||||
Cpu& CurrentCpuCore() {
|
||||
return cpu_core_manager.GetCurrentCore();
|
||||
@@ -97,7 +95,7 @@ struct System::Impl {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
core_timing.Initialize();
|
||||
kernel.Initialize();
|
||||
kernel.Initialize(core_timing);
|
||||
|
||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
@@ -109,6 +107,8 @@ struct System::Impl {
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
|
||||
/// Create default implementations of applets if one is not provided.
|
||||
if (controller_applet == nullptr)
|
||||
controller_applet = std::make_unique<Core::Frontend::DefaultControllerApplet>();
|
||||
if (profile_selector == nullptr)
|
||||
profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
|
||||
if (software_keyboard == nullptr)
|
||||
@@ -116,7 +116,7 @@ struct System::Impl {
|
||||
if (web_browser == nullptr)
|
||||
web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
|
||||
|
||||
auto main_process = Kernel::Process::Create(system, "main");
|
||||
auto main_process = Kernel::Process::Create(kernel, "main");
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
@@ -130,16 +130,10 @@ struct System::Impl {
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
|
||||
is_powered_on = true;
|
||||
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
|
||||
} else {
|
||||
gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
|
||||
}
|
||||
gpu_core = std::make_unique<Tegra::GPU>(system, renderer->Rasterizer());
|
||||
|
||||
cpu_core_manager.Initialize(system);
|
||||
|
||||
is_powered_on = true;
|
||||
LOG_DEBUG(Core, "Initialized OK");
|
||||
|
||||
// Reset counters and set time origin to current frame
|
||||
@@ -190,13 +184,13 @@ struct System::Impl {
|
||||
|
||||
void Shutdown() {
|
||||
// Log last frame performance stats
|
||||
const auto perf_results = GetAndResetPerfStats();
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
|
||||
perf_results.emulation_speed * 100.0);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
|
||||
perf_results.game_fps);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
|
||||
perf_results.frametime * 1000.0);
|
||||
auto perf_results = GetAndResetPerfStats();
|
||||
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
|
||||
perf_results.emulation_speed * 100.0);
|
||||
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
|
||||
perf_results.game_fps);
|
||||
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
|
||||
perf_results.frametime * 1000.0);
|
||||
|
||||
is_powered_on = false;
|
||||
|
||||
@@ -219,6 +213,7 @@ struct System::Impl {
|
||||
app_loader.reset();
|
||||
|
||||
// Clear all applets
|
||||
controller_applet.reset();
|
||||
profile_selector.reset();
|
||||
software_keyboard.reset();
|
||||
web_browser.reset();
|
||||
@@ -256,6 +251,7 @@ struct System::Impl {
|
||||
bool is_powered_on = false;
|
||||
|
||||
/// Frontend applets
|
||||
std::unique_ptr<Core::Frontend::ControllerApplet> controller_applet;
|
||||
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
|
||||
@@ -273,7 +269,7 @@ struct System::Impl {
|
||||
Core::FrameLimiter frame_limiter;
|
||||
};
|
||||
|
||||
System::System() : impl{std::make_unique<Impl>(*this)} {}
|
||||
System::System() : impl{std::make_unique<Impl>()} {}
|
||||
System::~System() = default;
|
||||
|
||||
Cpu& System::CurrentCpuCore() {
|
||||
@@ -461,6 +457,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
|
||||
return impl->virtual_filesystem;
|
||||
}
|
||||
|
||||
void System::SetControllerApplet(std::unique_ptr<Core::Frontend::ControllerApplet> applet) {
|
||||
impl->controller_applet = std::move(applet);
|
||||
}
|
||||
|
||||
const Core::Frontend::ControllerApplet& System::GetControllerApplet() const {
|
||||
return *impl->controller_applet;
|
||||
}
|
||||
|
||||
void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
|
||||
impl->profile_selector = std::move(applet);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
class ControllerApplet;
|
||||
class ProfileSelectApplet;
|
||||
class SoftwareKeyboardApplet;
|
||||
class WebBrowserApplet;
|
||||
@@ -253,6 +254,10 @@ public:
|
||||
|
||||
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
|
||||
|
||||
void SetControllerApplet(std::unique_ptr<Core::Frontend::ControllerApplet> applet);
|
||||
|
||||
const Core::Frontend::ControllerApplet& GetControllerApplet() const;
|
||||
|
||||
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
|
||||
|
||||
const Frontend::ProfileSelectApplet& GetProfileSelector() const;
|
||||
@@ -293,6 +298,10 @@ inline ARM_Interface& CurrentArmInterface() {
|
||||
return System::GetInstance().CurrentArmInterface();
|
||||
}
|
||||
|
||||
inline TelemetrySession& Telemetry() {
|
||||
return System::GetInstance().TelemetrySession();
|
||||
}
|
||||
|
||||
inline Kernel::Process* CurrentProcess() {
|
||||
return System::GetInstance().CurrentProcess();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#endif
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
@@ -50,9 +49,9 @@ bool CpuBarrier::Rendezvous() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
|
||||
std::size_t core_index)
|
||||
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
|
||||
Cpu::Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||
CpuBarrier& cpu_barrier, std::size_t core_index)
|
||||
: cpu_barrier{cpu_barrier}, core_timing{core_timing}, core_index{core_index} {
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
|
||||
@@ -64,7 +63,7 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
|
||||
arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
|
||||
}
|
||||
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
|
||||
}
|
||||
|
||||
Cpu::~Cpu() = default;
|
||||
|
||||
@@ -15,10 +15,6 @@ namespace Kernel {
|
||||
class Scheduler;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
@@ -49,8 +45,8 @@ private:
|
||||
|
||||
class Cpu {
|
||||
public:
|
||||
Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
|
||||
std::size_t core_index);
|
||||
Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||
CpuBarrier& cpu_barrier, std::size_t core_index);
|
||||
~Cpu();
|
||||
|
||||
void RunLoop(bool tight_loop = true);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uint128.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
@@ -61,9 +60,4 @@ s64 nsToCycles(u64 ns) {
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
|
||||
}
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks) {
|
||||
const u128 temporal = Common::Multiply64Into128(ticks, CNTFREQ);
|
||||
return Common::Divide128On32(temporal, static_cast<u32>(BASE_CLOCK_RATE)).first;
|
||||
}
|
||||
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace Core::Timing {
|
||||
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
|
||||
// The exact value used is of course unverified.
|
||||
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
|
||||
constexpr u64 CNTFREQ = 19200000; // Value from fusee.
|
||||
|
||||
inline s64 msToCycles(int ms) {
|
||||
// since ms is int there is no way to overflow
|
||||
@@ -62,6 +61,4 @@ inline u64 cyclesToMs(s64 cycles) {
|
||||
return cycles * 1000 / BASE_CLOCK_RATE;
|
||||
}
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks);
|
||||
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -27,7 +27,8 @@ void CpuCoreManager::Initialize(System& system) {
|
||||
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
|
||||
|
||||
for (std::size_t index = 0; index < cores.size(); ++index) {
|
||||
cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
|
||||
cores[index] =
|
||||
std::make_unique<Cpu>(system.CoreTiming(), *exclusive_monitor, *barrier, index);
|
||||
}
|
||||
|
||||
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
||||
|
||||
@@ -24,26 +24,13 @@ namespace FileSys {
|
||||
|
||||
union NCASectionHeader;
|
||||
|
||||
/// Describes the type of content within an NCA archive.
|
||||
enum class NCAContentType : u8 {
|
||||
/// Executable-related data
|
||||
Program = 0,
|
||||
|
||||
/// Metadata.
|
||||
Meta = 1,
|
||||
|
||||
/// Access control data.
|
||||
Control = 2,
|
||||
|
||||
/// Information related to the game manual
|
||||
/// e.g. Legal information, etc.
|
||||
Manual = 3,
|
||||
|
||||
/// System data.
|
||||
Data = 4,
|
||||
|
||||
/// Data that can be accessed by applications.
|
||||
PublicData = 5,
|
||||
Data_Unknown5 = 5, ///< Seems to be used on some system archives
|
||||
};
|
||||
|
||||
enum class NCASectionCryptoType : u8 {
|
||||
|
||||
@@ -94,7 +94,7 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
case NCAContentType::Control:
|
||||
return ContentRecordType::Control;
|
||||
case NCAContentType::Data:
|
||||
case NCAContentType::PublicData:
|
||||
case NCAContentType::Data_Unknown5:
|
||||
return ContentRecordType::Data;
|
||||
case NCAContentType::Manual:
|
||||
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
|
||||
|
||||
38
src/core/frontend/applets/controller.cpp
Normal file
38
src/core/frontend/applets/controller.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ControllerApplet::~ControllerApplet() = default;
|
||||
|
||||
DefaultControllerApplet::~DefaultControllerApplet() = default;
|
||||
|
||||
void DefaultControllerApplet::ReconfigureControllers(std::function<void(bool)> completed,
|
||||
ControllerParameters parameters) const {
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, automatically setting controller types to best fit "
|
||||
"in configuration, may be incorrect!");
|
||||
|
||||
const auto& npad =
|
||||
Core::System::GetInstance()
|
||||
.ServiceManager()
|
||||
.GetService<Service::HID::Hid>("hid")
|
||||
->GetAppletResource()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
|
||||
|
||||
for (auto& player : Settings::values.players) {
|
||||
const auto prio = Service::HID::Controller_NPad::MapSettingsTypeToNPad(player.type);
|
||||
player.type =
|
||||
Service::HID::Controller_NPad::MapNPadTypeToSettings(npad.DecideBestController(prio));
|
||||
}
|
||||
|
||||
completed(true);
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
47
src/core/frontend/applets/controller.h
Normal file
47
src/core/frontend/applets/controller.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
struct ControllerParameters {
|
||||
u8 min_players;
|
||||
u8 max_players;
|
||||
bool keep_current_connected;
|
||||
bool merge_dual_joycons;
|
||||
bool allowed_single_layout;
|
||||
|
||||
bool allowed_pro_controller;
|
||||
bool allowed_handheld;
|
||||
bool allowed_joycon_dual;
|
||||
bool allowed_joycon_left;
|
||||
bool allowed_joycon_right;
|
||||
|
||||
bool horizontal_single_joycons;
|
||||
};
|
||||
|
||||
class ControllerApplet {
|
||||
public:
|
||||
virtual ~ControllerApplet();
|
||||
|
||||
virtual void ReconfigureControllers(std::function<void(bool)> completed,
|
||||
ControllerParameters parameters) const = 0;
|
||||
};
|
||||
|
||||
class DefaultControllerApplet final : public ControllerApplet {
|
||||
public:
|
||||
~DefaultControllerApplet() override;
|
||||
|
||||
void ReconfigureControllers(std::function<void(bool)> completed,
|
||||
ControllerParameters parameters) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
@@ -39,10 +39,10 @@ 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;
|
||||
BitField<16, 4, u32_le> num_buf_x_descriptors;
|
||||
BitField<20, 4, u32_le> num_buf_a_descriptors;
|
||||
BitField<24, 4, u32_le> num_buf_b_descriptors;
|
||||
BitField<28, 4, u32_le> num_buf_w_descriptors;
|
||||
};
|
||||
|
||||
enum class BufferDescriptorCFlag : u32 {
|
||||
@@ -53,28 +53,28 @@ struct CommandHeader {
|
||||
|
||||
union {
|
||||
u32_le raw_high;
|
||||
BitField<0, 10, u32> data_size;
|
||||
BitField<0, 10, u32_le> data_size;
|
||||
BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags;
|
||||
BitField<31, 1, u32> enable_handle_descriptor;
|
||||
BitField<31, 1, u32_le> enable_handle_descriptor;
|
||||
};
|
||||
};
|
||||
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;
|
||||
BitField<0, 1, u32_le> send_current_pid;
|
||||
BitField<1, 4, u32_le> num_handles_to_copy;
|
||||
BitField<5, 4, u32_le> 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;
|
||||
BitField<0, 6, u32_le> counter_bits_0_5;
|
||||
BitField<6, 3, u32_le> address_bits_36_38;
|
||||
BitField<9, 3, u32_le> counter_bits_9_11;
|
||||
BitField<12, 4, u32_le> address_bits_32_35;
|
||||
BitField<16, 16, u32_le> size;
|
||||
};
|
||||
|
||||
u32_le address_bits_0_31;
|
||||
@@ -103,10 +103,10 @@ struct BufferDescriptorABW {
|
||||
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;
|
||||
BitField<0, 2, u32_le> flags;
|
||||
BitField<2, 3, u32_le> address_bits_36_38;
|
||||
BitField<24, 4, u32_le> size_bits_32_35;
|
||||
BitField<28, 4, u32_le> address_bits_32_35;
|
||||
};
|
||||
|
||||
VAddr Address() const {
|
||||
@@ -128,8 +128,8 @@ struct BufferDescriptorC {
|
||||
u32_le address_bits_0_31;
|
||||
|
||||
union {
|
||||
BitField<0, 16, u32> address_bits_32_47;
|
||||
BitField<16, 16, u32> size;
|
||||
BitField<0, 16, u32_le> address_bits_32_47;
|
||||
BitField<16, 16, u32_le> size;
|
||||
};
|
||||
|
||||
VAddr Address() const {
|
||||
@@ -167,8 +167,8 @@ struct DomainMessageHeader {
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 8, CommandType> command;
|
||||
BitField<8, 8, u32> input_object_count;
|
||||
BitField<16, 16, u32> size;
|
||||
BitField<8, 8, u32_le> input_object_count;
|
||||
BitField<16, 16, u32_le> size;
|
||||
};
|
||||
u32_le object_id;
|
||||
INSERT_PADDING_WORDS(2);
|
||||
|
||||
@@ -19,12 +19,9 @@
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
|
||||
|
||||
class RequestHelperBase {
|
||||
protected:
|
||||
Kernel::HLERequestContext* context = nullptr;
|
||||
@@ -274,20 +271,6 @@ inline void ResponseBuilder::Push(u64 value) {
|
||||
Push(static_cast<u32>(value >> 32));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(float value) {
|
||||
u32 integral;
|
||||
std::memcpy(&integral, &value, sizeof(u32));
|
||||
Push(integral);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(double value) {
|
||||
u64 integral;
|
||||
std::memcpy(&integral, &value, sizeof(u64));
|
||||
Push(integral);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void ResponseBuilder::Push(bool value) {
|
||||
Push(static_cast<u8>(value));
|
||||
@@ -367,7 +350,7 @@ public:
|
||||
template <class T>
|
||||
std::shared_ptr<T> PopIpcInterface() {
|
||||
ASSERT(context->Session()->IsDomain());
|
||||
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
|
||||
ASSERT(context->GetDomainMessageHeader()->input_object_count > 0);
|
||||
return context->GetDomainRequestHandler<T>(Pop<u32>() - 1);
|
||||
}
|
||||
};
|
||||
@@ -379,11 +362,6 @@ inline u32 RequestParser::Pop() {
|
||||
return cmdbuf[index++];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline s32 RequestParser::Pop() {
|
||||
return static_cast<s32>(Pop<u32>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void RequestParser::PopRaw(T& value) {
|
||||
std::memcpy(&value, cmdbuf + index, sizeof(T));
|
||||
@@ -414,37 +392,11 @@ inline u64 RequestParser::Pop() {
|
||||
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>();
|
||||
float real;
|
||||
std::memcpy(&real, &value, sizeof(real));
|
||||
return real;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool RequestParser::Pop() {
|
||||
return Pop<u8>() != 0;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -18,172 +17,32 @@
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
// Wake up num_to_wake (or all) threads in a vector.
|
||||
void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
|
||||
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
|
||||
// them all.
|
||||
std::size_t last = waiting_threads.size();
|
||||
if (num_to_wake > 0) {
|
||||
last = num_to_wake;
|
||||
}
|
||||
namespace Kernel::AddressArbiter {
|
||||
|
||||
// Signal the waiting threads.
|
||||
for (std::size_t i = 0; i < last; i++) {
|
||||
ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
|
||||
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||
waiting_threads[i]->SetArbiterWaitAddress(0);
|
||||
waiting_threads[i]->ResumeFromWait();
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
|
||||
AddressArbiter::~AddressArbiter() = default;
|
||||
|
||||
ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
|
||||
s32 num_to_wake) {
|
||||
switch (type) {
|
||||
case SignalType::Signal:
|
||||
return SignalToAddressOnly(address, num_to_wake);
|
||||
case SignalType::IncrementAndSignalIfEqual:
|
||||
return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
|
||||
case SignalType::ModifyByWaitingCountAndSignalIfEqual:
|
||||
return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
|
||||
default:
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
|
||||
const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
|
||||
WakeThreads(waiting_threads, num_to_wake);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
|
||||
s32 num_to_wake) {
|
||||
// Ensure that we can write to the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
Memory::Write32(address, static_cast<u32>(value + 1));
|
||||
return SignalToAddressOnly(address, num_to_wake);
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
|
||||
s32 num_to_wake) {
|
||||
// Ensure that we can write to the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
// Get threads waiting on the address.
|
||||
const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
|
||||
|
||||
// Determine the modified value depending on the waiting count.
|
||||
s32 updated_value;
|
||||
if (waiting_threads.empty()) {
|
||||
updated_value = value - 1;
|
||||
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
|
||||
updated_value = value + 1;
|
||||
} else {
|
||||
updated_value = value;
|
||||
}
|
||||
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
Memory::Write32(address, static_cast<u32>(updated_value));
|
||||
WakeThreads(waiting_threads, num_to_wake);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
|
||||
s64 timeout_ns) {
|
||||
switch (type) {
|
||||
case ArbitrationType::WaitIfLessThan:
|
||||
return WaitForAddressIfLessThan(address, value, timeout_ns, false);
|
||||
case ArbitrationType::DecrementAndWaitIfLessThan:
|
||||
return WaitForAddressIfLessThan(address, value, timeout_ns, true);
|
||||
case ArbitrationType::WaitIfEqual:
|
||||
return WaitForAddressIfEqual(address, value, timeout_ns);
|
||||
default:
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
|
||||
bool should_decrement) {
|
||||
// Ensure that we can read the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const s32 cur_value = static_cast<s32>(Memory::Read32(address));
|
||||
if (cur_value >= value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (should_decrement) {
|
||||
Memory::Write32(address, static_cast<u32>(cur_value - 1));
|
||||
}
|
||||
|
||||
// Short-circuit without rescheduling, if timeout is zero.
|
||||
if (timeout == 0) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
return WaitForAddressImpl(address, timeout);
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
|
||||
// Ensure that we can read the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
// Only wait for the address if equal.
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
// Short-circuit without rescheduling, if timeout is zero.
|
||||
if (timeout == 0) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
return WaitForAddressImpl(address, timeout);
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
|
||||
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
// Performs actual address waiting logic.
|
||||
static ResultCode WaitForAddress(VAddr address, s64 timeout) {
|
||||
SharedPtr<Thread> current_thread = GetCurrentThread();
|
||||
current_thread->SetArbiterWaitAddress(address);
|
||||
current_thread->SetStatus(ThreadStatus::WaitArb);
|
||||
current_thread->InvalidateWakeupCallback();
|
||||
|
||||
current_thread->WakeAfterDelay(timeout);
|
||||
|
||||
system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) const {
|
||||
const auto RetrieveWaitingThreads = [this](std::size_t core_index,
|
||||
std::vector<SharedPtr<Thread>>& waiting_threads,
|
||||
VAddr arb_addr) {
|
||||
const auto& scheduler = system.Scheduler(core_index);
|
||||
// Gets the threads waiting on an address.
|
||||
static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
|
||||
const auto RetrieveWaitingThreads = [](std::size_t core_index,
|
||||
std::vector<SharedPtr<Thread>>& waiting_threads,
|
||||
VAddr arb_addr) {
|
||||
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
|
||||
const auto& thread_list = scheduler.GetThreadList();
|
||||
|
||||
for (const auto& thread : thread_list) {
|
||||
if (thread->GetArbiterWaitAddress() == arb_addr) {
|
||||
if (thread->GetArbiterWaitAddress() == arb_addr)
|
||||
waiting_threads.push_back(thread);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -202,4 +61,118 @@ std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr
|
||||
|
||||
return threads;
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
// Wake up num_to_wake (or all) threads in a vector.
|
||||
static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
|
||||
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
|
||||
// them all.
|
||||
std::size_t last = waiting_threads.size();
|
||||
if (num_to_wake > 0)
|
||||
last = num_to_wake;
|
||||
|
||||
// Signal the waiting threads.
|
||||
for (std::size_t i = 0; i < last; i++) {
|
||||
ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
|
||||
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||
waiting_threads[i]->SetArbiterWaitAddress(0);
|
||||
waiting_threads[i]->ResumeFromWait();
|
||||
}
|
||||
}
|
||||
|
||||
// Signals an address being waited on.
|
||||
ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
|
||||
std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
|
||||
|
||||
WakeThreads(waiting_threads, num_to_wake);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Signals an address being waited on and increments its value if equal to the value argument.
|
||||
ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
|
||||
// Ensure that we can write to the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (static_cast<s32>(Memory::Read32(address)) == value) {
|
||||
Memory::Write32(address, static_cast<u32>(value + 1));
|
||||
} else {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
return SignalToAddress(address, num_to_wake);
|
||||
}
|
||||
|
||||
// Signals an address being waited on and modifies its value based on waiting thread count if equal
|
||||
// to the value argument.
|
||||
ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
|
||||
s32 num_to_wake) {
|
||||
// Ensure that we can write to the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
// Get threads waiting on the address.
|
||||
std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
|
||||
|
||||
// Determine the modified value depending on the waiting count.
|
||||
s32 updated_value;
|
||||
if (waiting_threads.empty()) {
|
||||
updated_value = value - 1;
|
||||
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
|
||||
updated_value = value + 1;
|
||||
} else {
|
||||
updated_value = value;
|
||||
}
|
||||
|
||||
if (static_cast<s32>(Memory::Read32(address)) == value) {
|
||||
Memory::Write32(address, static_cast<u32>(updated_value));
|
||||
} else {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
WakeThreads(waiting_threads, num_to_wake);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Waits on an address if the value passed is less than the argument value, optionally decrementing.
|
||||
ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) {
|
||||
// Ensure that we can read the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
s32 cur_value = static_cast<s32>(Memory::Read32(address));
|
||||
if (cur_value < value) {
|
||||
if (should_decrement) {
|
||||
Memory::Write32(address, static_cast<u32>(cur_value - 1));
|
||||
}
|
||||
} else {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
// Short-circuit without rescheduling, if timeout is zero.
|
||||
if (timeout == 0) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
return WaitForAddress(address, timeout);
|
||||
}
|
||||
|
||||
// Waits on an address if the value passed is equal to the argument value.
|
||||
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
|
||||
// Ensure that we can read the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
// Only wait for the address if equal.
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
// Short-circuit without rescheduling, if timeout is zero.
|
||||
if (timeout == 0) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
return WaitForAddress(address, timeout);
|
||||
}
|
||||
} // namespace Kernel::AddressArbiter
|
||||
|
||||
@@ -4,77 +4,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
namespace Kernel::AddressArbiter {
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Thread;
|
||||
|
||||
class AddressArbiter {
|
||||
public:
|
||||
enum class ArbitrationType {
|
||||
WaitIfLessThan = 0,
|
||||
DecrementAndWaitIfLessThan = 1,
|
||||
WaitIfEqual = 2,
|
||||
};
|
||||
|
||||
enum class SignalType {
|
||||
Signal = 0,
|
||||
IncrementAndSignalIfEqual = 1,
|
||||
ModifyByWaitingCountAndSignalIfEqual = 2,
|
||||
};
|
||||
|
||||
explicit AddressArbiter(Core::System& system);
|
||||
~AddressArbiter();
|
||||
|
||||
AddressArbiter(const AddressArbiter&) = delete;
|
||||
AddressArbiter& operator=(const AddressArbiter&) = delete;
|
||||
|
||||
AddressArbiter(AddressArbiter&&) = default;
|
||||
AddressArbiter& operator=(AddressArbiter&&) = delete;
|
||||
|
||||
/// Signals an address being waited on with a particular signaling type.
|
||||
ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake);
|
||||
|
||||
/// Waits on an address with a particular arbitration type.
|
||||
ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
|
||||
|
||||
private:
|
||||
/// Signals an address being waited on.
|
||||
ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
|
||||
|
||||
/// Signals an address being waited on and increments its value if equal to the value argument.
|
||||
ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
|
||||
|
||||
/// Signals an address being waited on and modifies its value based on waiting thread count if
|
||||
/// equal to the value argument.
|
||||
ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
|
||||
s32 num_to_wake);
|
||||
|
||||
/// Waits on an address if the value passed is less than the argument value,
|
||||
/// optionally decrementing.
|
||||
ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
|
||||
bool should_decrement);
|
||||
|
||||
/// Waits on an address if the value passed is equal to the argument value.
|
||||
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
|
||||
|
||||
// Waits on the given address with a timeout in nanoseconds
|
||||
ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
|
||||
|
||||
// Gets the threads waiting on an address.
|
||||
std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
|
||||
|
||||
Core::System& system;
|
||||
enum class ArbitrationType {
|
||||
WaitIfLessThan = 0,
|
||||
DecrementAndWaitIfLessThan = 1,
|
||||
WaitIfEqual = 2,
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
enum class SignalType {
|
||||
Signal = 0,
|
||||
IncrementAndSignalIfEqual = 1,
|
||||
ModifyByWaitingCountAndSignalIfEqual = 2,
|
||||
};
|
||||
|
||||
ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
|
||||
ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
|
||||
ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
|
||||
|
||||
ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement);
|
||||
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
|
||||
|
||||
} // namespace Kernel::AddressArbiter
|
||||
|
||||
@@ -33,11 +33,10 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
|
||||
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
|
||||
auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
|
||||
|
||||
if (server_port->HasHLEHandler()) {
|
||||
server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
|
||||
} else {
|
||||
server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions));
|
||||
}
|
||||
if (server_port->hle_handler)
|
||||
server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
|
||||
else
|
||||
server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions));
|
||||
|
||||
// Wake the threads waiting on the ServerPort
|
||||
server_port->WakeupAllWaitingThreads();
|
||||
|
||||
@@ -17,11 +17,21 @@ ClientSession::~ClientSession() {
|
||||
// This destructor will be called automatically when the last ClientSession handle is closed by
|
||||
// the emulated application.
|
||||
|
||||
// A local reference to the ServerSession is necessary to guarantee it
|
||||
// Local references to ServerSession and SessionRequestHandler are necessary to guarantee they
|
||||
// will be kept alive until after ClientDisconnected() returns.
|
||||
SharedPtr<ServerSession> server = parent->server;
|
||||
if (server) {
|
||||
server->ClientDisconnected();
|
||||
std::shared_ptr<SessionRequestHandler> hle_handler = server->hle_handler;
|
||||
if (hle_handler)
|
||||
hle_handler->ClientDisconnected(server);
|
||||
|
||||
// TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
|
||||
// their WaitSynchronization result to 0xC920181A.
|
||||
|
||||
// Clean up the list of client threads with pending requests, they are unneeded now that the
|
||||
// client endpoint is closed.
|
||||
server->pending_requesting_threads.clear();
|
||||
server->currently_handling = nullptr;
|
||||
}
|
||||
|
||||
parent->client = nullptr;
|
||||
|
||||
@@ -36,15 +36,14 @@ public:
|
||||
|
||||
ResultCode SendSyncRequest(SharedPtr<Thread> thread);
|
||||
|
||||
private:
|
||||
explicit ClientSession(KernelCore& kernel);
|
||||
~ClientSession() override;
|
||||
std::string name; ///< Name of client port (optional)
|
||||
|
||||
/// The parent session, which links to the server endpoint.
|
||||
std::shared_ptr<Session> parent;
|
||||
|
||||
/// Name of the client session (optional)
|
||||
std::string name;
|
||||
private:
|
||||
explicit ClientSession(KernelCore& kernel);
|
||||
~ClientSession() override;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
CodeSet::CodeSet() = default;
|
||||
CodeSet::~CodeSet() = default;
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.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.
|
||||
std::shared_ptr<std::vector<u8>> 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
|
||||
@@ -86,7 +86,7 @@ HLERequestContext::~HLERequestContext() = default;
|
||||
void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
|
||||
bool incoming) {
|
||||
IPC::RequestParser rp(src_cmdbuf);
|
||||
command_header = rp.PopRaw<IPC::CommandHeader>();
|
||||
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
|
||||
|
||||
if (command_header->type == IPC::CommandType::Close) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
@@ -95,7 +95,8 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
|
||||
|
||||
// If handle descriptor is present, add size of it
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
|
||||
handle_descriptor_header =
|
||||
std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
|
||||
if (handle_descriptor_header->send_current_pid) {
|
||||
rp.Skip(2, false);
|
||||
}
|
||||
@@ -139,15 +140,16 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
|
||||
// If this is an incoming message, only CommandType "Request" has a domain header
|
||||
// All outgoing domain messages have the domain header, if only incoming has it
|
||||
if (incoming || domain_message_header) {
|
||||
domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
|
||||
domain_message_header =
|
||||
std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
|
||||
} else {
|
||||
if (Session()->IsDomain()) {
|
||||
if (Session()->IsDomain())
|
||||
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_payload_header = rp.PopRaw<IPC::DataPayloadHeader>();
|
||||
data_payload_header =
|
||||
std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
|
||||
|
||||
data_payload_offset = rp.GetCurrentOffset();
|
||||
|
||||
@@ -262,11 +264,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
// Write the domain objects to the command buffer, these go after the raw untranslated data.
|
||||
// TODO(Subv): This completely ignores C buffers.
|
||||
std::size_t domain_offset = size - domain_message_header->num_objects;
|
||||
auto& request_handlers = server_session->domain_request_handlers;
|
||||
|
||||
for (const auto& object : domain_objects) {
|
||||
server_session->AppendDomainRequestHandler(object);
|
||||
dst_cmdbuf[domain_offset++] =
|
||||
static_cast<u32_le>(server_session->NumDomainRequestHandlers());
|
||||
for (auto& object : domain_objects) {
|
||||
request_handlers.emplace_back(object);
|
||||
dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
@@ -16,8 +15,6 @@
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Service {
|
||||
class ServiceFrameworkBase;
|
||||
}
|
||||
@@ -169,12 +166,12 @@ public:
|
||||
return buffer_c_desciptors;
|
||||
}
|
||||
|
||||
const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
|
||||
return domain_message_header.value();
|
||||
const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
|
||||
return domain_message_header.get();
|
||||
}
|
||||
|
||||
bool HasDomainMessageHeader() const {
|
||||
return domain_message_header.has_value();
|
||||
return domain_message_header != nullptr;
|
||||
}
|
||||
|
||||
/// Helper function to read a buffer using the appropriate buffer descriptor
|
||||
@@ -211,12 +208,14 @@ public:
|
||||
|
||||
template <typename T>
|
||||
SharedPtr<T> GetCopyObject(std::size_t index) {
|
||||
return DynamicObjectCast<T>(copy_objects.at(index));
|
||||
ASSERT(index < copy_objects.size());
|
||||
return DynamicObjectCast<T>(copy_objects[index]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SharedPtr<T> GetMoveObject(std::size_t index) {
|
||||
return DynamicObjectCast<T>(move_objects.at(index));
|
||||
ASSERT(index < move_objects.size());
|
||||
return DynamicObjectCast<T>(move_objects[index]);
|
||||
}
|
||||
|
||||
void AddMoveObject(SharedPtr<Object> object) {
|
||||
@@ -233,7 +232,7 @@ public:
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const {
|
||||
return std::static_pointer_cast<T>(domain_request_handlers.at(index));
|
||||
return std::static_pointer_cast<T>(domain_request_handlers[index]);
|
||||
}
|
||||
|
||||
void SetDomainRequestHandlers(
|
||||
@@ -273,10 +272,10 @@ private:
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
|
||||
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> 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::shared_ptr<IPC::CommandHeader> command_header;
|
||||
std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::shared_ptr<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;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -87,13 +86,11 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
|
||||
}
|
||||
|
||||
struct KernelCore::Impl {
|
||||
explicit Impl(Core::System& system) : system{system} {}
|
||||
|
||||
void Initialize(KernelCore& kernel) {
|
||||
void Initialize(KernelCore& kernel, Core::Timing::CoreTiming& core_timing) {
|
||||
Shutdown();
|
||||
|
||||
InitializeSystemResourceLimit(kernel);
|
||||
InitializeThreads();
|
||||
InitializeThreads(core_timing);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
@@ -125,9 +122,9 @@ struct KernelCore::Impl {
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
|
||||
}
|
||||
|
||||
void InitializeThreads() {
|
||||
void InitializeThreads(Core::Timing::CoreTiming& core_timing) {
|
||||
thread_wakeup_event_type =
|
||||
system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||
core_timing.RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
@@ -148,18 +145,15 @@ struct KernelCore::Impl {
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using
|
||||
/// the ConnectToPort SVC.
|
||||
NamedPortTable named_ports;
|
||||
|
||||
// System context
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
|
||||
KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {}
|
||||
KernelCore::~KernelCore() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void KernelCore::Initialize() {
|
||||
impl->Initialize(*this);
|
||||
void KernelCore::Initialize(Core::Timing::CoreTiming& core_timing) {
|
||||
impl->Initialize(*this, core_timing);
|
||||
}
|
||||
|
||||
void KernelCore::Shutdown() {
|
||||
|
||||
@@ -11,10 +11,6 @@
|
||||
template <typename T>
|
||||
class ResultVal;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
struct EventType;
|
||||
@@ -22,7 +18,6 @@ struct EventType;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class AddressArbiter;
|
||||
class ClientPort;
|
||||
class HandleTable;
|
||||
class Process;
|
||||
@@ -35,14 +30,7 @@ private:
|
||||
using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
|
||||
|
||||
public:
|
||||
/// Constructs an instance of the kernel using the given System
|
||||
/// instance as a context for any necessary system-related state,
|
||||
/// such as threads, CPU core state, etc.
|
||||
///
|
||||
/// @post After execution of the constructor, the provided System
|
||||
/// object *must* outlive the kernel instance itself.
|
||||
///
|
||||
explicit KernelCore(Core::System& system);
|
||||
KernelCore();
|
||||
~KernelCore();
|
||||
|
||||
KernelCore(const KernelCore&) = delete;
|
||||
@@ -52,7 +40,11 @@ public:
|
||||
KernelCore& operator=(KernelCore&&) = delete;
|
||||
|
||||
/// Resets the kernel to a clean slate for use.
|
||||
void Initialize();
|
||||
///
|
||||
/// @param core_timing CoreTiming instance used to create any necessary
|
||||
/// kernel-specific callback events.
|
||||
///
|
||||
void Initialize(Core::Timing::CoreTiming& core_timing);
|
||||
|
||||
/// Clears all resources in use by the kernel instance.
|
||||
void Shutdown();
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -32,7 +31,7 @@ namespace {
|
||||
*/
|
||||
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
|
||||
// Setup page table so we can write to memory
|
||||
Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
|
||||
SetCurrentPageTable(&owner_process.VMManager().page_table);
|
||||
|
||||
// Initialize new "main" thread
|
||||
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
|
||||
@@ -51,10 +50,12 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
|
||||
auto& kernel = system.Kernel();
|
||||
CodeSet::CodeSet() = default;
|
||||
CodeSet::~CodeSet() = default;
|
||||
|
||||
SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
SharedPtr<Process> process(new Process(kernel));
|
||||
|
||||
SharedPtr<Process> process(new Process(system));
|
||||
process->name = std::move(name);
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->status = ProcessStatus::Created;
|
||||
@@ -131,7 +132,7 @@ void Process::PrepareForTermination() {
|
||||
if (thread->GetOwnerProcess() != this)
|
||||
continue;
|
||||
|
||||
if (thread == system.CurrentScheduler().GetCurrentThread())
|
||||
if (thread == GetCurrentThread())
|
||||
continue;
|
||||
|
||||
// TODO(Subv): When are the other running/ready threads terminated?
|
||||
@@ -143,6 +144,7 @@ void Process::PrepareForTermination() {
|
||||
}
|
||||
};
|
||||
|
||||
const auto& system = Core::System::GetInstance();
|
||||
stop_threads(system.Scheduler(0).GetThreadList());
|
||||
stop_threads(system.Scheduler(1).GetThreadList());
|
||||
stop_threads(system.Scheduler(2).GetThreadList());
|
||||
@@ -210,7 +212,7 @@ void Process::FreeTLSSlot(VAddr tls_address) {
|
||||
}
|
||||
|
||||
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
|
||||
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
|
||||
MemoryState memory_state) {
|
||||
const auto vma = vm_manager
|
||||
.MapMemoryBlock(segment.addr + base_addr, module_.memory,
|
||||
@@ -225,12 +227,14 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
|
||||
|
||||
// Clear instruction cache in CPU JIT
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
|
||||
}
|
||||
|
||||
Process::Process(Core::System& system)
|
||||
: WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {}
|
||||
Process::~Process() = default;
|
||||
Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
Kernel::Process::~Process() {}
|
||||
|
||||
void Process::Acquire(Thread* thread) {
|
||||
ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
|
||||
|
||||
@@ -7,21 +7,17 @@
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class ProgramMetadata;
|
||||
}
|
||||
@@ -32,8 +28,6 @@ class KernelCore;
|
||||
class ResourceLimit;
|
||||
class Thread;
|
||||
|
||||
struct CodeSet;
|
||||
|
||||
struct AddressMapping {
|
||||
// Address and size must be page-aligned
|
||||
VAddr address;
|
||||
@@ -66,6 +60,46 @@ enum class ProcessStatus {
|
||||
DebugBreak,
|
||||
};
|
||||
|
||||
struct CodeSet final {
|
||||
struct Segment {
|
||||
std::size_t offset = 0;
|
||||
VAddr addr = 0;
|
||||
u32 size = 0;
|
||||
};
|
||||
|
||||
explicit CodeSet();
|
||||
~CodeSet();
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<u8>> memory;
|
||||
|
||||
std::array<Segment, 3> segments;
|
||||
VAddr entrypoint = 0;
|
||||
};
|
||||
|
||||
class Process final : public WaitObject {
|
||||
public:
|
||||
enum : u64 {
|
||||
@@ -82,7 +116,7 @@ public:
|
||||
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static SharedPtr<Process> Create(Core::System& system, std::string&& name);
|
||||
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Process";
|
||||
@@ -116,16 +150,6 @@ public:
|
||||
return handle_table;
|
||||
}
|
||||
|
||||
/// Gets a reference to the process' address arbiter.
|
||||
AddressArbiter& GetAddressArbiter() {
|
||||
return address_arbiter;
|
||||
}
|
||||
|
||||
/// Gets a const reference to the process' address arbiter.
|
||||
const AddressArbiter& GetAddressArbiter() const {
|
||||
return address_arbiter;
|
||||
}
|
||||
|
||||
/// Gets the current status of the process
|
||||
ProcessStatus GetStatus() const {
|
||||
return status;
|
||||
@@ -227,7 +251,7 @@ public:
|
||||
void FreeTLSSlot(VAddr tls_address);
|
||||
|
||||
private:
|
||||
explicit Process(Core::System& system);
|
||||
explicit Process(KernelCore& kernel);
|
||||
~Process() override;
|
||||
|
||||
/// Checks if the specified thread should wait until this process is available.
|
||||
@@ -285,16 +309,9 @@ private:
|
||||
/// Per-process handle table for storing created object handles in.
|
||||
HandleTable handle_table;
|
||||
|
||||
/// Per-process address arbiter.
|
||||
AddressArbiter address_arbiter;
|
||||
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
|
||||
|
||||
/// System context
|
||||
Core::System& system;
|
||||
|
||||
/// Name of this process
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace Kernel {
|
||||
|
||||
std::mutex Scheduler::scheduler_mutex;
|
||||
|
||||
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core)
|
||||
: cpu_core{cpu_core}, system{system} {}
|
||||
Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
|
||||
|
||||
Scheduler::~Scheduler() {
|
||||
for (auto& thread : thread_list) {
|
||||
@@ -62,7 +61,7 @@ Thread* Scheduler::PopNextReadyThread() {
|
||||
|
||||
void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
Thread* const previous_thread = GetCurrentThread();
|
||||
Process* const previous_process = system.Kernel().CurrentProcess();
|
||||
Process* const previous_process = Core::CurrentProcess();
|
||||
|
||||
UpdateLastContextSwitchTime(previous_thread, previous_process);
|
||||
|
||||
@@ -95,8 +94,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
|
||||
auto* const thread_owner_process = current_thread->GetOwnerProcess();
|
||||
if (previous_process != thread_owner_process) {
|
||||
system.Kernel().MakeCurrentProcess(thread_owner_process);
|
||||
Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
|
||||
Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process);
|
||||
SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
|
||||
}
|
||||
|
||||
cpu_core.LoadContext(new_thread->GetContext());
|
||||
@@ -112,7 +111,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
|
||||
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
const u64 prev_switch_ticks = last_context_switch_time;
|
||||
const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks();
|
||||
const u64 most_recent_switch_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
|
||||
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
|
||||
|
||||
if (thread != nullptr) {
|
||||
@@ -199,7 +198,8 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
|
||||
ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
|
||||
|
||||
// Yield this thread -- sleep for zero time and force reschedule to different thread
|
||||
GetCurrentThread()->Sleep(0);
|
||||
WaitCurrentThread_Sleep();
|
||||
GetCurrentThread()->WakeAfterDelay(0);
|
||||
}
|
||||
|
||||
void Scheduler::YieldWithLoadBalancing(Thread* thread) {
|
||||
@@ -214,7 +214,8 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
|
||||
ASSERT(priority < THREADPRIO_COUNT);
|
||||
|
||||
// Sleep for zero time to be able to force reschedule to different thread
|
||||
GetCurrentThread()->Sleep(0);
|
||||
WaitCurrentThread_Sleep();
|
||||
GetCurrentThread()->WakeAfterDelay(0);
|
||||
|
||||
Thread* suggested_thread = nullptr;
|
||||
|
||||
@@ -222,7 +223,8 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
|
||||
// Take the first non-nullptr one
|
||||
for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
|
||||
const auto res =
|
||||
system.CpuCore(cur_core).Scheduler().GetNextSuggestedThread(core, priority);
|
||||
Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread(
|
||||
core, priority);
|
||||
|
||||
// If scheduler provides a suggested thread
|
||||
if (res != nullptr) {
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
|
||||
namespace Core {
|
||||
class ARM_Interface;
|
||||
class System;
|
||||
} // namespace Core
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -22,7 +21,7 @@ class Process;
|
||||
|
||||
class Scheduler final {
|
||||
public:
|
||||
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core);
|
||||
explicit Scheduler(Core::ARM_Interface& cpu_core);
|
||||
~Scheduler();
|
||||
|
||||
/// Returns whether there are any threads that are ready to run.
|
||||
@@ -163,7 +162,6 @@ private:
|
||||
Core::ARM_Interface& cpu_core;
|
||||
u64 last_context_switch_time = 0;
|
||||
|
||||
Core::System& system;
|
||||
static std::mutex scheduler_mutex;
|
||||
};
|
||||
|
||||
|
||||
@@ -26,10 +26,6 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
|
||||
return MakeResult(std::move(session));
|
||||
}
|
||||
|
||||
void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) {
|
||||
pending_sessions.push_back(std::move(pending_session));
|
||||
}
|
||||
|
||||
bool ServerPort::ShouldWait(Thread* thread) const {
|
||||
// If there are no pending sessions, we wait until a new one is added.
|
||||
return pending_sessions.empty();
|
||||
|
||||
@@ -22,8 +22,6 @@ class SessionRequestHandler;
|
||||
|
||||
class ServerPort final : public WaitObject {
|
||||
public:
|
||||
using HLEHandler = std::shared_ptr<SessionRequestHandler>;
|
||||
|
||||
/**
|
||||
* Creates a pair of ServerPort and an associated ClientPort.
|
||||
*
|
||||
@@ -53,27 +51,22 @@ public:
|
||||
*/
|
||||
ResultVal<SharedPtr<ServerSession>> Accept();
|
||||
|
||||
/// Whether or not this server port has an HLE handler available.
|
||||
bool HasHLEHandler() const {
|
||||
return hle_handler != nullptr;
|
||||
}
|
||||
|
||||
/// Gets the HLE handler for this port.
|
||||
HLEHandler GetHLEHandler() const {
|
||||
return hle_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
|
||||
* will inherit a reference to this handler.
|
||||
*/
|
||||
void SetHleHandler(HLEHandler hle_handler_) {
|
||||
void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) {
|
||||
hle_handler = std::move(hle_handler_);
|
||||
}
|
||||
|
||||
/// Appends a ServerSession to the collection of ServerSessions
|
||||
/// waiting to be accepted by this port.
|
||||
void AppendPendingSession(SharedPtr<ServerSession> pending_session);
|
||||
std::string name; ///< Name of port (optional)
|
||||
|
||||
/// ServerSessions waiting to be accepted by the port
|
||||
std::vector<SharedPtr<ServerSession>> pending_sessions;
|
||||
|
||||
/// This session's HLE request handler template (optional)
|
||||
/// ServerSessions created from this port inherit a reference to this handler.
|
||||
std::shared_ptr<SessionRequestHandler> hle_handler;
|
||||
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
@@ -81,16 +74,6 @@ public:
|
||||
private:
|
||||
explicit ServerPort(KernelCore& kernel);
|
||||
~ServerPort() override;
|
||||
|
||||
/// ServerSessions waiting to be accepted by the port
|
||||
std::vector<SharedPtr<ServerSession>> pending_sessions;
|
||||
|
||||
/// This session's HLE request handler template (optional)
|
||||
/// ServerSessions created from this port inherit a reference to this handler.
|
||||
HLEHandler hle_handler;
|
||||
|
||||
/// Name of the port (optional)
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -63,71 +63,42 @@ void ServerSession::Acquire(Thread* thread) {
|
||||
pending_requesting_threads.pop_back();
|
||||
}
|
||||
|
||||
void ServerSession::ClientDisconnected() {
|
||||
// We keep a shared pointer to the hle handler to keep it alive throughout
|
||||
// the call to ClientDisconnected, as ClientDisconnected invalidates the
|
||||
// hle_handler member itself during the course of the function executing.
|
||||
std::shared_ptr<SessionRequestHandler> handler = hle_handler;
|
||||
if (handler) {
|
||||
// Note that after this returns, this server session's hle_handler is
|
||||
// invalidated (set to null).
|
||||
handler->ClientDisconnected(this);
|
||||
}
|
||||
|
||||
// TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
|
||||
// their WaitSynchronization result to 0xC920181A.
|
||||
|
||||
// Clean up the list of client threads with pending requests, they are unneeded now that the
|
||||
// client endpoint is closed.
|
||||
pending_requesting_threads.clear();
|
||||
currently_handling = nullptr;
|
||||
}
|
||||
|
||||
void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
|
||||
domain_request_handlers.push_back(std::move(handler));
|
||||
}
|
||||
|
||||
std::size_t ServerSession::NumDomainRequestHandlers() const {
|
||||
return domain_request_handlers.size();
|
||||
}
|
||||
|
||||
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
|
||||
if (!context.HasDomainMessageHeader()) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
auto* const domain_message_header = context.GetDomainMessageHeader();
|
||||
if (domain_message_header) {
|
||||
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
||||
context.SetDomainRequestHandlers(domain_request_handlers);
|
||||
|
||||
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
||||
context.SetDomainRequestHandlers(domain_request_handlers);
|
||||
// If there is a DomainMessageHeader, then this is CommandType "Request"
|
||||
const u32 object_id{context.GetDomainMessageHeader()->object_id};
|
||||
switch (domain_message_header->command) {
|
||||
case IPC::DomainMessageHeader::CommandType::SendMessage:
|
||||
if (object_id > domain_request_handlers.size()) {
|
||||
LOG_CRITICAL(IPC,
|
||||
"object_id {} is too big! This probably means a recent service call "
|
||||
"to {} needed to return a new interface!",
|
||||
object_id, name);
|
||||
UNREACHABLE();
|
||||
return RESULT_SUCCESS; // Ignore error if asserts are off
|
||||
}
|
||||
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
|
||||
|
||||
// If there is a DomainMessageHeader, then this is CommandType "Request"
|
||||
const auto& domain_message_header = context.GetDomainMessageHeader();
|
||||
const u32 object_id{domain_message_header.object_id};
|
||||
switch (domain_message_header.command) {
|
||||
case IPC::DomainMessageHeader::CommandType::SendMessage:
|
||||
if (object_id > domain_request_handlers.size()) {
|
||||
LOG_CRITICAL(IPC,
|
||||
"object_id {} is too big! This probably means a recent service call "
|
||||
"to {} needed to return a new interface!",
|
||||
object_id, name);
|
||||
UNREACHABLE();
|
||||
return RESULT_SUCCESS; // Ignore error if asserts are off
|
||||
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
|
||||
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
|
||||
|
||||
domain_request_handlers[object_id - 1] = nullptr;
|
||||
|
||||
IPC::ResponseBuilder rb{context, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
|
||||
|
||||
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
|
||||
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
|
||||
|
||||
domain_request_handlers[object_id - 1] = nullptr;
|
||||
|
||||
IPC::ResponseBuilder rb{context, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
LOG_CRITICAL(IPC, "Unknown domain command={}",
|
||||
static_cast<int>(domain_message_header->command.Value()));
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
LOG_CRITICAL(IPC, "Unknown domain command={}",
|
||||
static_cast<int>(domain_message_header.command.Value()));
|
||||
ASSERT(false);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,14 +46,6 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
Session* GetParent() {
|
||||
return parent.get();
|
||||
}
|
||||
|
||||
const Session* GetParent() const {
|
||||
return parent.get();
|
||||
}
|
||||
|
||||
using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
|
||||
|
||||
/**
|
||||
@@ -86,16 +78,23 @@ public:
|
||||
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
/// Called when a client disconnection occurs.
|
||||
void ClientDisconnected();
|
||||
std::string name; ///< The name of this session (optional)
|
||||
std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint.
|
||||
std::shared_ptr<SessionRequestHandler>
|
||||
hle_handler; ///< This session's HLE request handler (applicable when not a domain)
|
||||
|
||||
/// Adds a new domain request handler to the collection of request handlers within
|
||||
/// this ServerSession instance.
|
||||
void AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler);
|
||||
/// This is the list of domain request handlers (after conversion to a domain)
|
||||
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
|
||||
|
||||
/// Retrieves the total number of domain request handlers that have been
|
||||
/// appended to this ServerSession instance.
|
||||
std::size_t NumDomainRequestHandlers() const;
|
||||
/// List of threads that are pending a response after a sync request. This list is processed in
|
||||
/// a LIFO manner, thus, the last request will be dispatched first.
|
||||
/// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
|
||||
std::vector<SharedPtr<Thread>> pending_requesting_threads;
|
||||
|
||||
/// Thread whose request is currently being handled. A request is considered "handled" when a
|
||||
/// response is sent via svcReplyAndReceive.
|
||||
/// TODO(Subv): Find a better name for this.
|
||||
SharedPtr<Thread> currently_handling;
|
||||
|
||||
/// Returns true if the session has been converted to a domain, otherwise False
|
||||
bool IsDomain() const {
|
||||
@@ -130,30 +129,8 @@ private:
|
||||
/// object handle.
|
||||
ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
|
||||
|
||||
/// The parent session, which links to the client endpoint.
|
||||
std::shared_ptr<Session> parent;
|
||||
|
||||
/// This session's HLE request handler (applicable when not a domain)
|
||||
std::shared_ptr<SessionRequestHandler> hle_handler;
|
||||
|
||||
/// This is the list of domain request handlers (after conversion to a domain)
|
||||
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
|
||||
|
||||
/// List of threads that are pending a response after a sync request. This list is processed in
|
||||
/// a LIFO manner, thus, the last request will be dispatched first.
|
||||
/// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
|
||||
std::vector<SharedPtr<Thread>> pending_requesting_threads;
|
||||
|
||||
/// Thread whose request is currently being handled. A request is considered "handled" when a
|
||||
/// response is sent via svcReplyAndReceive.
|
||||
/// TODO(Subv): Find a better name for this.
|
||||
SharedPtr<Thread> currently_handling;
|
||||
|
||||
/// When set to True, converts the session to a domain at the end of the command
|
||||
bool convert_to_domain{};
|
||||
|
||||
/// The name of this session (optional)
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
@@ -33,8 +34,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
|
||||
shared_memory->backing_block_offset = 0;
|
||||
|
||||
// Refresh the address mappings for the current process.
|
||||
if (kernel.CurrentProcess() != nullptr) {
|
||||
kernel.CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
|
||||
if (Core::CurrentProcess() != nullptr) {
|
||||
Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
|
||||
shared_memory->backing_block.get());
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/mutex.h"
|
||||
@@ -48,6 +47,23 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
|
||||
return address + size > address;
|
||||
}
|
||||
|
||||
// Checks if a given address range lies within a larger address range.
|
||||
constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
|
||||
VAddr address_range_end) {
|
||||
const VAddr end_address = address + size - 1;
|
||||
return address_range_begin <= address && end_address <= address_range_end - 1;
|
||||
}
|
||||
|
||||
bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
|
||||
return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
|
||||
vm.GetAddressSpaceEndAddress());
|
||||
}
|
||||
|
||||
bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
|
||||
return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
|
||||
vm.GetNewMapRegionEndAddress());
|
||||
}
|
||||
|
||||
// 8 GiB
|
||||
constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
|
||||
|
||||
@@ -89,14 +105,14 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (!vm_manager.IsWithinAddressSpace(src_addr, size)) {
|
||||
if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
|
||||
src_addr, size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) {
|
||||
if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
|
||||
dst_addr, size);
|
||||
@@ -222,7 +238,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (!vm_manager.IsWithinAddressSpace(addr, size)) {
|
||||
if (!IsInsideAddressSpace(vm_manager, addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
|
||||
size);
|
||||
@@ -283,7 +299,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr
|
||||
}
|
||||
|
||||
auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
if (!vm_manager.IsWithinAddressSpace(address, size)) {
|
||||
if (!IsInsideAddressSpace(vm_manager, address, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Given address (0x{:016X}) is outside the bounds of the address space.", address);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
@@ -1284,14 +1300,10 @@ static ResultCode StartThread(Handle thread_handle) {
|
||||
|
||||
/// Called when a thread exits
|
||||
static void ExitThread() {
|
||||
auto& system = Core::System::GetInstance();
|
||||
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC());
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
|
||||
|
||||
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
current_thread->Stop();
|
||||
system.CurrentScheduler().RemoveThread(current_thread);
|
||||
system.PrepareReschedule();
|
||||
ExitCurrentThread();
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
}
|
||||
|
||||
/// Sleep the current thread
|
||||
@@ -1304,32 +1316,32 @@ static void SleepThread(s64 nanoseconds) {
|
||||
YieldAndWaitForLoadBalancing = -2,
|
||||
};
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& scheduler = system.CurrentScheduler();
|
||||
auto* const current_thread = scheduler.GetCurrentThread();
|
||||
|
||||
if (nanoseconds <= 0) {
|
||||
auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
|
||||
switch (static_cast<SleepType>(nanoseconds)) {
|
||||
case SleepType::YieldWithoutLoadBalancing:
|
||||
scheduler.YieldWithoutLoadBalancing(current_thread);
|
||||
scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
|
||||
break;
|
||||
case SleepType::YieldWithLoadBalancing:
|
||||
scheduler.YieldWithLoadBalancing(current_thread);
|
||||
scheduler.YieldWithLoadBalancing(GetCurrentThread());
|
||||
break;
|
||||
case SleepType::YieldAndWaitForLoadBalancing:
|
||||
scheduler.YieldAndWaitForLoadBalancing(current_thread);
|
||||
scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
|
||||
}
|
||||
} else {
|
||||
current_thread->Sleep(nanoseconds);
|
||||
// Sleep current thread and check for next thread to schedule
|
||||
WaitCurrentThread_Sleep();
|
||||
|
||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
||||
GetCurrentThread()->WakeAfterDelay(nanoseconds);
|
||||
}
|
||||
|
||||
// Reschedule all CPU cores
|
||||
for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
|
||||
system.CpuCore(i).PrepareReschedule();
|
||||
}
|
||||
for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
|
||||
Core::System::GetInstance().CpuCore(i).PrepareReschedule();
|
||||
}
|
||||
|
||||
/// Wait process wide key atomic
|
||||
@@ -1483,10 +1495,20 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
|
||||
auto& address_arbiter =
|
||||
Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter();
|
||||
return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
|
||||
switch (static_cast<AddressArbiter::ArbitrationType>(type)) {
|
||||
case AddressArbiter::ArbitrationType::WaitIfLessThan:
|
||||
return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false);
|
||||
case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan:
|
||||
return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true);
|
||||
case AddressArbiter::ArbitrationType::WaitIfEqual:
|
||||
return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan "
|
||||
"or WaitIfEqual but got {}",
|
||||
type);
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Signals to an address (via Address Arbiter)
|
||||
@@ -1504,10 +1526,21 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
const auto signal_type = static_cast<AddressArbiter::SignalType>(type);
|
||||
auto& address_arbiter =
|
||||
Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter();
|
||||
return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
|
||||
switch (static_cast<AddressArbiter::SignalType>(type)) {
|
||||
case AddressArbiter::SignalType::Signal:
|
||||
return AddressArbiter::SignalToAddress(address, num_to_wake);
|
||||
case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
|
||||
return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
|
||||
case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
|
||||
return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
|
||||
num_to_wake);
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Invalid signal type, expected Signal, IncrementAndSignalIfEqual "
|
||||
"or ModifyByWaitingCountAndSignalIfEqual but got {}",
|
||||
type);
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
/// This returns the total CPU ticks elapsed since the CPU was powered-on
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -66,6 +68,17 @@ void Thread::Stop() {
|
||||
owner_process->FreeTLSSlot(tls_address);
|
||||
}
|
||||
|
||||
void WaitCurrentThread_Sleep() {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->SetStatus(ThreadStatus::WaitSleep);
|
||||
}
|
||||
|
||||
void ExitCurrentThread() {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->Stop();
|
||||
Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
|
||||
}
|
||||
|
||||
void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||
// Don't schedule a wakeup if the thread wants to wait forever
|
||||
if (nanoseconds == -1)
|
||||
@@ -171,6 +184,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
// TODO(yuriks): Other checks, returning 0xD9001BEA
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
|
||||
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
|
||||
// TODO (bunnei): Find the correct error code to use here
|
||||
@@ -256,8 +271,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
|
||||
if (thread->lock_owner == this) {
|
||||
// If the thread is already waiting for this thread to release the mutex, ensure that the
|
||||
// waiters list is consistent and return without doing anything.
|
||||
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||
ASSERT(iter != wait_mutex_threads.end());
|
||||
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||
ASSERT(itr != wait_mutex_threads.end());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,16 +280,11 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
|
||||
ASSERT(thread->lock_owner == nullptr);
|
||||
|
||||
// Ensure that the thread is not already in the list of mutex waiters
|
||||
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||
ASSERT(iter == wait_mutex_threads.end());
|
||||
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||
ASSERT(itr == wait_mutex_threads.end());
|
||||
|
||||
// Keep the list in an ordered fashion
|
||||
const auto insertion_point = std::find_if(
|
||||
wait_mutex_threads.begin(), wait_mutex_threads.end(),
|
||||
[&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
|
||||
wait_mutex_threads.insert(insertion_point, thread);
|
||||
thread->lock_owner = this;
|
||||
|
||||
wait_mutex_threads.emplace_back(std::move(thread));
|
||||
UpdatePriority();
|
||||
}
|
||||
|
||||
@@ -282,44 +292,32 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
|
||||
ASSERT(thread->lock_owner == this);
|
||||
|
||||
// Ensure that the thread is in the list of mutex waiters
|
||||
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||
ASSERT(iter != wait_mutex_threads.end());
|
||||
|
||||
wait_mutex_threads.erase(iter);
|
||||
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
|
||||
ASSERT(itr != wait_mutex_threads.end());
|
||||
|
||||
boost::remove_erase(wait_mutex_threads, thread);
|
||||
thread->lock_owner = nullptr;
|
||||
UpdatePriority();
|
||||
}
|
||||
|
||||
void Thread::UpdatePriority() {
|
||||
// If any of the threads waiting on the mutex have a higher priority
|
||||
// (taking into account priority inheritance), then this thread inherits
|
||||
// that thread's priority.
|
||||
// Find the highest priority among all the threads that are waiting for this thread's lock
|
||||
u32 new_priority = nominal_priority;
|
||||
if (!wait_mutex_threads.empty()) {
|
||||
if (wait_mutex_threads.front()->current_priority < new_priority) {
|
||||
new_priority = wait_mutex_threads.front()->current_priority;
|
||||
}
|
||||
for (const auto& thread : wait_mutex_threads) {
|
||||
if (thread->nominal_priority < new_priority)
|
||||
new_priority = thread->nominal_priority;
|
||||
}
|
||||
|
||||
if (new_priority == current_priority) {
|
||||
if (new_priority == current_priority)
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler->SetThreadPriority(this, new_priority);
|
||||
|
||||
current_priority = new_priority;
|
||||
|
||||
if (!lock_owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that the thread is within the correct location in the waiting list.
|
||||
auto old_owner = lock_owner;
|
||||
lock_owner->RemoveMutexWaiter(this);
|
||||
old_owner->AddMutexWaiter(this);
|
||||
|
||||
// Recursively update the priority of the thread that depends on the priority of this one.
|
||||
lock_owner->UpdatePriority();
|
||||
if (lock_owner)
|
||||
lock_owner->UpdatePriority();
|
||||
}
|
||||
|
||||
void Thread::ChangeCore(u32 core, u64 mask) {
|
||||
@@ -395,14 +393,6 @@ void Thread::SetActivity(ThreadActivity value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::Sleep(s64 nanoseconds) {
|
||||
// Sleep current thread and check for next thread to schedule
|
||||
SetStatus(ThreadStatus::WaitSleep);
|
||||
|
||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
||||
WakeAfterDelay(nanoseconds);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
@@ -383,9 +383,6 @@ public:
|
||||
|
||||
void SetActivity(ThreadActivity value);
|
||||
|
||||
/// Sleeps this thread for the given amount of nanoseconds.
|
||||
void Sleep(s64 nanoseconds);
|
||||
|
||||
private:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
@@ -401,14 +398,8 @@ private:
|
||||
VAddr entry_point = 0;
|
||||
VAddr stack_top = 0;
|
||||
|
||||
/// Nominal thread priority, as set by the emulated application.
|
||||
/// The nominal priority is the thread priority without priority
|
||||
/// inheritance taken into account.
|
||||
u32 nominal_priority = 0;
|
||||
|
||||
/// Current thread priority. This may change over the course of the
|
||||
/// thread's lifetime in order to facilitate priority inheritance.
|
||||
u32 current_priority = 0;
|
||||
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
|
||||
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
|
||||
|
||||
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
|
||||
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
|
||||
@@ -469,4 +460,14 @@ private:
|
||||
*/
|
||||
Thread* GetCurrentThread();
|
||||
|
||||
/**
|
||||
* Waits the current thread on a sleep
|
||||
*/
|
||||
void WaitCurrentThread_Sleep();
|
||||
|
||||
/**
|
||||
* Stops the current thread and removes it from the thread_list
|
||||
*/
|
||||
void ExitCurrentThread();
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -7,18 +7,18 @@
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_hook.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_hook.h"
|
||||
#include "core/memory_setup.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
const char* GetMemoryStateName(MemoryState state) {
|
||||
|
||||
static const char* GetMemoryStateName(MemoryState state) {
|
||||
static constexpr const char* names[] = {
|
||||
"Unmapped", "Io",
|
||||
"Normal", "CodeStatic",
|
||||
@@ -35,14 +35,6 @@ const char* GetMemoryStateName(MemoryState state) {
|
||||
return names[ToSvcMemoryState(state)];
|
||||
}
|
||||
|
||||
// Checks if a given address range lies within a larger address range.
|
||||
constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
|
||||
VAddr address_range_end) {
|
||||
const VAddr end_address = address + size - 1;
|
||||
return address_range_begin <= address && end_address <= address_range_end - 1;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
||||
ASSERT(base + size == next.base);
|
||||
if (permissions != next.permissions || state != next.state || attribute != next.attribute ||
|
||||
@@ -177,7 +169,7 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
|
||||
|
||||
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
|
||||
MemoryState state,
|
||||
Common::MemoryHookPointer mmio_handler) {
|
||||
Memory::MemoryHookPointer mmio_handler) {
|
||||
// This is the appropriately sized VMA that will turn into our allocation.
|
||||
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
|
||||
VirtualMemoryArea& final_vma = vma_handle->second;
|
||||
@@ -257,7 +249,8 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
|
||||
}
|
||||
|
||||
ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
||||
if (!IsWithinHeapRegion(target, size)) {
|
||||
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
|
||||
target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
@@ -292,7 +285,8 @@ ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission p
|
||||
}
|
||||
|
||||
ResultCode VMManager::HeapFree(VAddr target, u64 size) {
|
||||
if (!IsWithinHeapRegion(target, size)) {
|
||||
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
|
||||
target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
@@ -624,7 +618,7 @@ void VMManager::ClearPageTable() {
|
||||
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
|
||||
page_table.special_regions.clear();
|
||||
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
|
||||
Common::PageType::Unmapped);
|
||||
Memory::PageType::Unmapped);
|
||||
}
|
||||
|
||||
VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
|
||||
@@ -712,11 +706,6 @@ u64 VMManager::GetAddressSpaceWidth() const {
|
||||
return address_space_width;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinAddressSpace(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetAddressSpaceBaseAddress(),
|
||||
GetAddressSpaceEndAddress());
|
||||
}
|
||||
|
||||
VAddr VMManager::GetASLRRegionBaseAddress() const {
|
||||
return aslr_region_base;
|
||||
}
|
||||
@@ -761,11 +750,6 @@ u64 VMManager::GetCodeRegionSize() const {
|
||||
return code_region_end - code_region_base;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinCodeRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetCodeRegionBaseAddress(),
|
||||
GetCodeRegionEndAddress());
|
||||
}
|
||||
|
||||
VAddr VMManager::GetHeapRegionBaseAddress() const {
|
||||
return heap_region_base;
|
||||
}
|
||||
@@ -778,11 +762,6 @@ u64 VMManager::GetHeapRegionSize() const {
|
||||
return heap_region_end - heap_region_base;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
|
||||
GetHeapRegionEndAddress());
|
||||
}
|
||||
|
||||
VAddr VMManager::GetMapRegionBaseAddress() const {
|
||||
return map_region_base;
|
||||
}
|
||||
@@ -795,10 +774,6 @@ u64 VMManager::GetMapRegionSize() const {
|
||||
return map_region_end - map_region_base;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress());
|
||||
}
|
||||
|
||||
VAddr VMManager::GetNewMapRegionBaseAddress() const {
|
||||
return new_map_region_base;
|
||||
}
|
||||
@@ -811,11 +786,6 @@ u64 VMManager::GetNewMapRegionSize() const {
|
||||
return new_map_region_end - new_map_region_base;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinNewMapRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(),
|
||||
GetNewMapRegionEndAddress());
|
||||
}
|
||||
|
||||
VAddr VMManager::GetTLSIORegionBaseAddress() const {
|
||||
return tls_io_region_base;
|
||||
}
|
||||
@@ -828,9 +798,4 @@ u64 VMManager::GetTLSIORegionSize() const {
|
||||
return tls_io_region_end - tls_io_region_base;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinTLSIORegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetTLSIORegionBaseAddress(),
|
||||
GetTLSIORegionEndAddress());
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -9,10 +9,9 @@
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_hook.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_hook.h"
|
||||
|
||||
namespace FileSys {
|
||||
enum class ProgramAddressSpaceType : u8;
|
||||
@@ -291,7 +290,7 @@ struct VirtualMemoryArea {
|
||||
// Settings for type = MMIO
|
||||
/// Physical address of the register area this VMA maps to.
|
||||
PAddr paddr = 0;
|
||||
Common::MemoryHookPointer mmio_handler = nullptr;
|
||||
Memory::MemoryHookPointer mmio_handler = nullptr;
|
||||
|
||||
/// Tests if this area can be merged to the right with `next`.
|
||||
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
|
||||
@@ -369,7 +368,7 @@ public:
|
||||
* @param mmio_handler The handler that will implement read and write for this MMIO region.
|
||||
*/
|
||||
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
|
||||
Common::MemoryHookPointer mmio_handler);
|
||||
Memory::MemoryHookPointer mmio_handler);
|
||||
|
||||
/// Unmaps a range of addresses, splitting VMAs as necessary.
|
||||
ResultCode UnmapRange(VAddr target, u64 size);
|
||||
@@ -433,21 +432,18 @@ public:
|
||||
/// Gets the address space width in bits.
|
||||
u64 GetAddressSpaceWidth() const;
|
||||
|
||||
/// Determines whether or not the given address range lies within the address space.
|
||||
bool IsWithinAddressSpace(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the base address of the ASLR region.
|
||||
VAddr GetASLRRegionBaseAddress() const;
|
||||
|
||||
/// Gets the end address of the ASLR region.
|
||||
VAddr GetASLRRegionEndAddress() const;
|
||||
|
||||
/// Gets the size of the ASLR region
|
||||
u64 GetASLRRegionSize() const;
|
||||
|
||||
/// Determines whether or not the specified address range is within the ASLR region.
|
||||
bool IsWithinASLRRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the size of the ASLR region
|
||||
u64 GetASLRRegionSize() const;
|
||||
|
||||
/// Gets the base address of the code region.
|
||||
VAddr GetCodeRegionBaseAddress() const;
|
||||
|
||||
@@ -457,9 +453,6 @@ public:
|
||||
/// Gets the total size of the code region in bytes.
|
||||
u64 GetCodeRegionSize() const;
|
||||
|
||||
/// Determines whether or not the specified range is within the code region.
|
||||
bool IsWithinCodeRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the base address of the heap region.
|
||||
VAddr GetHeapRegionBaseAddress() const;
|
||||
|
||||
@@ -469,9 +462,6 @@ public:
|
||||
/// Gets the total size of the heap region in bytes.
|
||||
u64 GetHeapRegionSize() const;
|
||||
|
||||
/// Determines whether or not the specified range is within the heap region.
|
||||
bool IsWithinHeapRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the base address of the map region.
|
||||
VAddr GetMapRegionBaseAddress() const;
|
||||
|
||||
@@ -481,9 +471,6 @@ public:
|
||||
/// Gets the total size of the map region in bytes.
|
||||
u64 GetMapRegionSize() const;
|
||||
|
||||
/// Determines whether or not the specified range is within the map region.
|
||||
bool IsWithinMapRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the base address of the new map region.
|
||||
VAddr GetNewMapRegionBaseAddress() const;
|
||||
|
||||
@@ -493,9 +480,6 @@ public:
|
||||
/// Gets the total size of the new map region in bytes.
|
||||
u64 GetNewMapRegionSize() const;
|
||||
|
||||
/// Determines whether or not the given address range is within the new map region
|
||||
bool IsWithinNewMapRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the base address of the TLS IO region.
|
||||
VAddr GetTLSIORegionBaseAddress() const;
|
||||
|
||||
@@ -505,12 +489,9 @@ public:
|
||||
/// Gets the total size of the TLS IO region in bytes.
|
||||
u64 GetTLSIORegionSize() const;
|
||||
|
||||
/// Determines if the given address range is within the TLS IO region.
|
||||
bool IsWithinTLSIORegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Each VMManager has its own page table, which is set as the main one when the owning process
|
||||
/// is scheduled.
|
||||
Common::PageTable page_table{Memory::PAGE_BITS};
|
||||
Memory::PageTable page_table;
|
||||
|
||||
private:
|
||||
using VMAIter = VMAMap::iterator;
|
||||
|
||||
@@ -8,10 +8,19 @@
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes
|
||||
|
||||
/**
|
||||
* Detailed description of the error. Code 0 always means success.
|
||||
*/
|
||||
enum class ErrorDescription : u32 {
|
||||
Success = 0,
|
||||
RemoteProcessDead = 301,
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifies the module which caused the error. Error codes can be propagated through a call
|
||||
* chain, meaning that this doesn't always correspond to the module where the API call made is
|
||||
@@ -112,7 +121,7 @@ enum class ErrorModule : u32 {
|
||||
ShopN = 811,
|
||||
};
|
||||
|
||||
/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
|
||||
/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields.
|
||||
union ResultCode {
|
||||
u32 raw;
|
||||
|
||||
@@ -125,9 +134,17 @@ union ResultCode {
|
||||
|
||||
constexpr explicit ResultCode(u32 raw) : raw(raw) {}
|
||||
|
||||
constexpr ResultCode(ErrorModule module, ErrorDescription description)
|
||||
: ResultCode(module, static_cast<u32>(description)) {}
|
||||
|
||||
constexpr ResultCode(ErrorModule module_, u32 description_)
|
||||
: raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
|
||||
|
||||
constexpr ResultCode& operator=(const ResultCode& o) {
|
||||
raw = o.raw;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr bool IsSuccess() const {
|
||||
return raw == 0;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/am/applets/controller.h"
|
||||
#include "core/hle/service/am/applets/profile_select.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
#include "core/hle/service/am/applets/stub_applet.h"
|
||||
@@ -43,6 +44,7 @@ constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
|
||||
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
|
||||
|
||||
enum class AppletId : u32 {
|
||||
Controller = 0x0C,
|
||||
ProfileSelect = 0x10,
|
||||
SoftwareKeyboard = 0x11,
|
||||
LibAppletOff = 0x17,
|
||||
@@ -93,84 +95,38 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx)
|
||||
}
|
||||
|
||||
IAudioController::IAudioController() : ServiceFramework("IAudioController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
|
||||
{1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
|
||||
{2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
|
||||
{3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
|
||||
{4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
|
||||
{1, &IAudioController::GetMainAppletExpectedMasterVolume,
|
||||
"GetMainAppletExpectedMasterVolume"},
|
||||
{2, &IAudioController::GetLibraryAppletExpectedMasterVolume,
|
||||
"GetLibraryAppletExpectedMasterVolume"},
|
||||
{3, nullptr, "ChangeMainAppletMasterVolume"},
|
||||
{4, nullptr, "SetTransparentVolumeRate"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IAudioController::~IAudioController() = default;
|
||||
|
||||
void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const float main_applet_volume_tmp = rp.Pop<float>();
|
||||
const float library_applet_volume_tmp = rp.Pop<float>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
|
||||
main_applet_volume_tmp, library_applet_volume_tmp);
|
||||
|
||||
// Ensure the volume values remain within the 0-100% range
|
||||
main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
|
||||
library_applet_volume =
|
||||
std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(main_applet_volume);
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(library_applet_volume);
|
||||
}
|
||||
|
||||
void IAudioController::ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
float volume;
|
||||
s64 fade_time_ns;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
|
||||
parameters.fade_time_ns);
|
||||
|
||||
main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
|
||||
fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IAudioController::SetTransparentAudioRate(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const float transparent_volume_rate_tmp = rp.Pop<float>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
|
||||
|
||||
// Clamp volume range to 0-100%.
|
||||
transparent_volume_rate =
|
||||
std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
|
||||
@@ -836,6 +792,8 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default;
|
||||
|
||||
static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
||||
switch (id) {
|
||||
case AppletId::Controller:
|
||||
return std::make_shared<Applets::Controller>();
|
||||
case AppletId::ProfileSelect:
|
||||
return std::make_shared<Applets::ProfileSelect>();
|
||||
case AppletId::SoftwareKeyboard:
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
@@ -82,21 +81,8 @@ private:
|
||||
void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
|
||||
void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
|
||||
void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
|
||||
void ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx);
|
||||
void SetTransparentAudioRate(Kernel::HLERequestContext& ctx);
|
||||
|
||||
static constexpr float min_allowed_volume = 0.0f;
|
||||
static constexpr float max_allowed_volume = 1.0f;
|
||||
|
||||
float main_applet_volume{0.25f};
|
||||
float library_applet_volume{max_allowed_volume};
|
||||
float transparent_volume_rate{min_allowed_volume};
|
||||
|
||||
// Volume transition fade time in nanoseconds.
|
||||
// e.g. If the main applet volume was 0% and was changed to 50%
|
||||
// with a fade of 50ns, then over the course of 50ns,
|
||||
// the volume will gradually fade up to 50%
|
||||
std::chrono::nanoseconds fade_time_ns{0};
|
||||
u32 volume{100};
|
||||
};
|
||||
|
||||
class IDisplayController final : public ServiceFramework<IDisplayController> {
|
||||
|
||||
96
src/core/hle/service/am/applets/controller.cpp
Normal file
96
src/core/hle/service/am/applets/controller.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/hle/service/am/applets/controller.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
|
||||
ControllerConfigPrivate config_private, ControllerConfig config) {
|
||||
Core::Frontend::ControllerParameters params{};
|
||||
|
||||
params.min_players = config.minimum_player_count;
|
||||
params.max_players = config.maximum_player_count;
|
||||
params.keep_current_connected = config.keep_current_connections;
|
||||
params.merge_dual_joycons = config.permit_dual_joycons;
|
||||
params.allowed_single_layout = config.enable_single_mode;
|
||||
|
||||
params.horizontal_single_joycons = config_private.npad_hold_type;
|
||||
|
||||
HID::Controller_NPad::NPadType styleset;
|
||||
styleset.raw = config_private.npad_style_set;
|
||||
params.allowed_pro_controller = styleset.pro_controller;
|
||||
params.allowed_handheld = styleset.handheld;
|
||||
params.allowed_joycon_dual = styleset.joycon_dual;
|
||||
params.allowed_joycon_left = styleset.joycon_left;
|
||||
params.allowed_joycon_right = styleset.joycon_right;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
Controller::Controller() = default;
|
||||
|
||||
Controller::~Controller() = default;
|
||||
|
||||
void Controller::Initialize() {
|
||||
Applet::Initialize();
|
||||
|
||||
const auto private_config_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(private_config_storage != nullptr);
|
||||
const auto& private_controller_config = private_config_storage->GetData();
|
||||
|
||||
ASSERT(private_controller_config.size() >= sizeof(ControllerConfigPrivate));
|
||||
std::memcpy(&private_config, private_controller_config.data(), sizeof(ControllerConfigPrivate));
|
||||
|
||||
const auto config_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(config_storage != nullptr);
|
||||
const auto& controller_config = config_storage->GetData();
|
||||
|
||||
ASSERT(controller_config.size() >= sizeof(ControllerConfig));
|
||||
std::memcpy(&config, controller_config.data(), sizeof(ControllerConfig));
|
||||
}
|
||||
|
||||
bool Controller::TransactionComplete() const {
|
||||
return complete;
|
||||
}
|
||||
|
||||
ResultCode Controller::GetStatus() const {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Controller::ExecuteInteractive() {
|
||||
UNREACHABLE_MSG("Tried to interact with non-interactable applet.");
|
||||
}
|
||||
|
||||
void Controller::Execute() {
|
||||
if (complete) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const auto& frontend{Core::System::GetInstance().GetControllerApplet()};
|
||||
|
||||
const auto parameters = ConvertToFrontendParameters(private_config, config);
|
||||
|
||||
frontend.ReconfigureControllers([this](bool ok) { ConfigurationComplete(ok); }, parameters);
|
||||
}
|
||||
|
||||
void Controller::ConfigurationComplete(bool ok) {
|
||||
ControllerConfigOutput out{};
|
||||
|
||||
out.player_code = 0;
|
||||
out.selected_npad_id = static_cast<u32>(
|
||||
HID::Controller_NPad::MapSettingsTypeToNPad(Settings::values.players[0].type));
|
||||
out.result = !ok;
|
||||
|
||||
complete = true;
|
||||
|
||||
std::vector<u8> out_data(sizeof(ControllerConfigOutput));
|
||||
std::memcpy(out_data.data(), &out, sizeof(ControllerConfigOutput));
|
||||
broker.PushNormalDataFromApplet(IStorage{out_data});
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
} // namespace Service::AM::Applets
|
||||
61
src/core/hle/service/am/applets/controller.h
Normal file
61
src/core/hle/service/am/applets/controller.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
struct ControllerConfigPrivate {
|
||||
u32 private_arg_size; // unknown, seems to be an offset of some kind
|
||||
u32 normal_arg_size;
|
||||
u32 flags;
|
||||
u32 npad_style_set;
|
||||
u32 npad_hold_type;
|
||||
};
|
||||
static_assert(sizeof(ControllerConfigPrivate) == 0x14,
|
||||
"ControllerConfigPrivate has incorrect size");
|
||||
|
||||
struct ControllerConfig {
|
||||
u8 minimum_player_count;
|
||||
u8 maximum_player_count;
|
||||
u8 keep_current_connections;
|
||||
u8 left_justify;
|
||||
u8 permit_dual_joycons;
|
||||
u8 enable_single_mode;
|
||||
INSERT_PADDING_BYTES(0x216);
|
||||
};
|
||||
static_assert(sizeof(ControllerConfig) == 0x21C, "ControllerConfig has incorrect size");
|
||||
|
||||
struct ControllerConfigOutput {
|
||||
u8 player_code;
|
||||
INSERT_PADDING_BYTES(0x3);
|
||||
u32 selected_npad_id;
|
||||
u32 result;
|
||||
};
|
||||
static_assert(sizeof(ControllerConfigOutput) == 0xC, "ControllerConfigOutput has incorrect size");
|
||||
|
||||
class Controller final : public Applet {
|
||||
public:
|
||||
Controller();
|
||||
~Controller() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
|
||||
void ConfigurationComplete(bool ok);
|
||||
|
||||
private:
|
||||
ControllerConfigPrivate private_config;
|
||||
ControllerConfig config;
|
||||
bool complete = false;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
|
||||
|
||||
@@ -9,13 +9,10 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class KeysetDisable : u32 {
|
||||
|
||||
@@ -18,11 +18,17 @@
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/audio/audout_u.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
ErrorUnknown = 2,
|
||||
BufferCountExceeded = 8,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
|
||||
constexpr int DefaultSampleRate{48000};
|
||||
|
||||
@@ -94,7 +100,7 @@ private:
|
||||
|
||||
if (stream->IsPlaying()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_OPERATION_FAILED);
|
||||
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,9 +113,7 @@ private:
|
||||
void StopAudioOut(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
if (stream->IsPlaying()) {
|
||||
audio_core.StopStream(stream);
|
||||
}
|
||||
audio_core.StopStream(stream);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -139,8 +143,7 @@ private:
|
||||
|
||||
if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
|
||||
return;
|
||||
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/audio/audren_u.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
@@ -147,7 +146,7 @@ private:
|
||||
// code in this case.
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NOT_SUPPORTED);
|
||||
rb.Push(ResultCode{ErrorModule::Audio, 201});
|
||||
}
|
||||
|
||||
Kernel::EventPair system_event;
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
|
||||
constexpr ResultCode ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
|
||||
constexpr ResultCode ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
|
||||
|
||||
} // namespace Service::Audio
|
||||
@@ -8,34 +8,44 @@
|
||||
#include <vector>
|
||||
|
||||
#include <opus.h>
|
||||
#include <opus_multistream.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/audio/hwopus.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
namespace {
|
||||
|
||||
struct OpusDeleter {
|
||||
void operator()(OpusMSDecoder* ptr) const {
|
||||
opus_multistream_decoder_destroy(ptr);
|
||||
void operator()(void* ptr) const {
|
||||
operator delete(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>;
|
||||
|
||||
struct OpusPacketHeader {
|
||||
// Packet size in bytes.
|
||||
u32_be size;
|
||||
// Indicates the final range of the codec's entropy coder.
|
||||
u32_be final_range;
|
||||
};
|
||||
static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
|
||||
|
||||
class OpusDecoderState {
|
||||
class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
|
||||
public:
|
||||
IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate,
|
||||
u32 channel_count)
|
||||
: ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
|
||||
sample_rate(sample_rate), channel_count(channel_count) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
|
||||
{1, nullptr, "SetContext"},
|
||||
{2, nullptr, "DecodeInterleavedForMultiStreamOld"},
|
||||
{3, nullptr, "SetContextForMultiStream"},
|
||||
{4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
|
||||
{5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
|
||||
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
||||
{7, nullptr, "DecodeInterleavedForMultiStream"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Describes extra behavior that may be asked of the decoding context.
|
||||
enum class ExtraBehavior {
|
||||
/// No extra behavior.
|
||||
@@ -45,27 +55,30 @@ public:
|
||||
ResetContext,
|
||||
};
|
||||
|
||||
enum class PerfTime {
|
||||
Disabled,
|
||||
Enabled,
|
||||
};
|
||||
void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
|
||||
: decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
|
||||
|
||||
// Decodes interleaved Opus packets. Optionally allows reporting time taken to
|
||||
// perform the decoding, as well as any relevant extra behavior.
|
||||
void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
|
||||
ExtraBehavior extra_behavior) {
|
||||
if (perf_time == PerfTime::Disabled) {
|
||||
DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
|
||||
} else {
|
||||
u64 performance = 0;
|
||||
DecodeInterleavedHelper(ctx, &performance, extra_behavior);
|
||||
}
|
||||
DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None);
|
||||
}
|
||||
|
||||
void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
u64 performance = 0;
|
||||
DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None);
|
||||
}
|
||||
|
||||
void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto extra_behavior =
|
||||
rp.Pop<bool>() ? ExtraBehavior::ResetContext : ExtraBehavior::None;
|
||||
|
||||
u64 performance = 0;
|
||||
DecodeInterleavedHelper(ctx, &performance, extra_behavior);
|
||||
}
|
||||
|
||||
private:
|
||||
void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance,
|
||||
ExtraBehavior extra_behavior) {
|
||||
u32 consumed = 0;
|
||||
@@ -76,7 +89,8 @@ private:
|
||||
ResetDecoderContext();
|
||||
}
|
||||
|
||||
if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) {
|
||||
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
|
||||
performance)) {
|
||||
LOG_ERROR(Audio, "Failed to decode opus data");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(ogniK): Use correct error code
|
||||
@@ -95,27 +109,27 @@ private:
|
||||
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
|
||||
}
|
||||
|
||||
bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
|
||||
std::vector<opus_int16>& output, u64* out_performance_time) const {
|
||||
bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
|
||||
std::vector<opus_int16>& output, u64* out_performance_time) {
|
||||
const auto start_time = std::chrono::high_resolution_clock::now();
|
||||
const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
|
||||
if (sizeof(OpusPacketHeader) > input.size()) {
|
||||
if (sizeof(OpusHeader) > input.size()) {
|
||||
LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
|
||||
sizeof(OpusPacketHeader), input.size());
|
||||
sizeof(OpusHeader), input.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
OpusPacketHeader hdr{};
|
||||
std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader));
|
||||
if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) {
|
||||
OpusHeader hdr{};
|
||||
std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
|
||||
if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
|
||||
LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}",
|
||||
sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size());
|
||||
sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto frame = input.data() + sizeof(OpusPacketHeader);
|
||||
const auto frame = input.data() + sizeof(OpusHeader);
|
||||
const auto decoded_sample_count = opus_packet_get_nb_samples(
|
||||
frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)),
|
||||
frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
|
||||
static_cast<opus_int32>(sample_rate));
|
||||
if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
|
||||
LOG_ERROR(
|
||||
@@ -127,18 +141,18 @@ private:
|
||||
|
||||
const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
|
||||
const auto out_sample_count =
|
||||
opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
|
||||
opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0);
|
||||
if (out_sample_count < 0) {
|
||||
LOG_ERROR(Audio,
|
||||
"Incorrect sample count received from opus_decode, "
|
||||
"output_sample_count={}, frame_size={}, data_sz_from_hdr={}",
|
||||
out_sample_count, frame_size, static_cast<u32>(hdr.size));
|
||||
out_sample_count, frame_size, static_cast<u32>(hdr.sz));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
|
||||
sample_count = out_sample_count;
|
||||
consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
|
||||
consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
|
||||
if (out_performance_time != nullptr) {
|
||||
*out_performance_time =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
|
||||
@@ -150,86 +164,25 @@ private:
|
||||
void ResetDecoderContext() {
|
||||
ASSERT(decoder != nullptr);
|
||||
|
||||
opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
|
||||
opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
|
||||
}
|
||||
|
||||
OpusDecoderPtr decoder;
|
||||
struct OpusHeader {
|
||||
u32_be sz; // Needs to be BE for some odd reason
|
||||
INSERT_PADDING_WORDS(1);
|
||||
};
|
||||
static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size");
|
||||
|
||||
std::unique_ptr<OpusDecoder, OpusDeleter> decoder;
|
||||
u32 sample_rate;
|
||||
u32 channel_count;
|
||||
};
|
||||
|
||||
class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
|
||||
public:
|
||||
explicit IHardwareOpusDecoderManager(OpusDecoderState decoder_state)
|
||||
: ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
|
||||
{1, nullptr, "SetContext"},
|
||||
{2, nullptr, "DecodeInterleavedForMultiStreamOld"},
|
||||
{3, nullptr, "SetContextForMultiStream"},
|
||||
{4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
|
||||
{5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
|
||||
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
||||
{7, nullptr, "DecodeInterleavedForMultiStream"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled,
|
||||
OpusDecoderState::ExtraBehavior::None);
|
||||
}
|
||||
|
||||
void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled,
|
||||
OpusDecoderState::ExtraBehavior::None);
|
||||
}
|
||||
|
||||
void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Audio, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
|
||||
: OpusDecoderState::ExtraBehavior::None;
|
||||
|
||||
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
|
||||
}
|
||||
|
||||
OpusDecoderState decoder_state;
|
||||
};
|
||||
|
||||
std::size_t WorkerBufferSize(u32 channel_count) {
|
||||
static std::size_t WorkerBufferSize(u32 channel_count) {
|
||||
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
|
||||
constexpr int num_streams = 1;
|
||||
const int num_stereo_streams = channel_count == 2 ? 1 : 0;
|
||||
return opus_multistream_decoder_get_size(num_streams, num_stereo_streams);
|
||||
return opus_decoder_get_size(static_cast<int>(channel_count));
|
||||
}
|
||||
|
||||
// Creates the mapping table that maps the input channels to the particular
|
||||
// output channels. In the stereo case, we map the left and right input channels
|
||||
// to the left and right output channels respectively.
|
||||
//
|
||||
// However, in the monophonic case, we only map the one available channel
|
||||
// to the sole output channel. We specify 255 for the would-be right channel
|
||||
// as this is a special value defined by Opus to indicate to the decoder to
|
||||
// ignore that channel.
|
||||
std::array<u8, 2> CreateMappingTable(u32 channel_count) {
|
||||
if (channel_count == 2) {
|
||||
return {{0, 1}};
|
||||
}
|
||||
|
||||
return {{0, 255}};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto sample_rate = rp.Pop<u32>();
|
||||
@@ -267,15 +220,10 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
|
||||
const std::size_t worker_sz = WorkerBufferSize(channel_count);
|
||||
ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
|
||||
|
||||
const int num_stereo_streams = channel_count == 2 ? 1 : 0;
|
||||
const auto mapping_table = CreateMappingTable(channel_count);
|
||||
|
||||
int error = 0;
|
||||
OpusDecoderPtr decoder{
|
||||
opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
|
||||
num_stereo_streams, mapping_table.data(), &error)};
|
||||
if (error != OPUS_OK || decoder == nullptr) {
|
||||
LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
|
||||
std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
|
||||
static_cast<OpusDecoder*>(operator new(worker_sz))};
|
||||
if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
|
||||
LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(ogniK): Use correct error code
|
||||
rb.Push(ResultCode(-1));
|
||||
@@ -284,8 +232,8 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHardwareOpusDecoderManager>(
|
||||
OpusDecoderState{std::move(decoder), sample_rate, channel_count});
|
||||
rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate,
|
||||
channel_count);
|
||||
}
|
||||
|
||||
HwOpus::HwOpus() : ServiceFramework("hwopus") {
|
||||
|
||||
@@ -733,10 +733,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
FSP_SRV::~FSP_SRV() = default;
|
||||
|
||||
void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
current_process_id = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -32,7 +32,6 @@ private:
|
||||
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
|
||||
FileSys::VirtualFile romfs;
|
||||
u64 current_process_id = 0;
|
||||
};
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
||||
@@ -41,20 +41,20 @@ private:
|
||||
struct PadState {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32> a;
|
||||
BitField<1, 1, u32> b;
|
||||
BitField<2, 1, u32> x;
|
||||
BitField<3, 1, u32> y;
|
||||
BitField<4, 1, u32> l;
|
||||
BitField<5, 1, u32> r;
|
||||
BitField<6, 1, u32> zl;
|
||||
BitField<7, 1, u32> zr;
|
||||
BitField<8, 1, u32> plus;
|
||||
BitField<9, 1, u32> minus;
|
||||
BitField<10, 1, u32> d_left;
|
||||
BitField<11, 1, u32> d_up;
|
||||
BitField<12, 1, u32> d_right;
|
||||
BitField<13, 1, u32> d_down;
|
||||
BitField<0, 1, u32_le> a;
|
||||
BitField<1, 1, u32_le> b;
|
||||
BitField<2, 1, u32_le> x;
|
||||
BitField<3, 1, u32_le> y;
|
||||
BitField<4, 1, u32_le> l;
|
||||
BitField<5, 1, u32_le> r;
|
||||
BitField<6, 1, u32_le> zl;
|
||||
BitField<7, 1, u32_le> zr;
|
||||
BitField<8, 1, u32_le> plus;
|
||||
BitField<9, 1, u32_le> minus;
|
||||
BitField<10, 1, u32_le> d_left;
|
||||
BitField<11, 1, u32_le> d_up;
|
||||
BitField<12, 1, u32_le> d_right;
|
||||
BitField<13, 1, u32_le> d_down;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
|
||||
@@ -62,7 +62,7 @@ private:
|
||||
struct Attributes {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32> connected;
|
||||
BitField<0, 1, u32_le> connected;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
|
||||
|
||||
@@ -33,19 +33,37 @@ enum class JoystickId : std::size_t {
|
||||
Joystick_Right,
|
||||
};
|
||||
|
||||
static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
|
||||
Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
|
||||
Settings::ControllerType type) {
|
||||
switch (type) {
|
||||
case Settings::ControllerType::ProController:
|
||||
return Controller_NPad::NPadControllerType::ProController;
|
||||
return NPadControllerType::ProController;
|
||||
case Settings::ControllerType::DualJoycon:
|
||||
return Controller_NPad::NPadControllerType::JoyDual;
|
||||
return NPadControllerType::JoyDual;
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return Controller_NPad::NPadControllerType::JoyLeft;
|
||||
return NPadControllerType::JoyLeft;
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return Controller_NPad::NPadControllerType::JoyRight;
|
||||
return NPadControllerType::JoyRight;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Controller_NPad::NPadControllerType::JoyDual;
|
||||
return NPadControllerType::JoyDual;
|
||||
}
|
||||
}
|
||||
|
||||
Settings::ControllerType Controller_NPad::MapNPadTypeToSettings(NPadControllerType type) {
|
||||
switch (type) {
|
||||
case NPadControllerType::ProController:
|
||||
case NPadControllerType::Handheld:
|
||||
return Settings::ControllerType::ProController;
|
||||
case NPadControllerType::JoyDual:
|
||||
return Settings::ControllerType::DualJoycon;
|
||||
case NPadControllerType::JoyLeft:
|
||||
return Settings::ControllerType::LeftJoycon;
|
||||
case NPadControllerType::JoyRight:
|
||||
return Settings::ControllerType::RightJoycon;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Settings::ControllerType::DualJoycon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +114,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
|
||||
Controller_NPad::Controller_NPad() = default;
|
||||
Controller_NPad::~Controller_NPad() = default;
|
||||
|
||||
void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
|
||||
const auto controller_type = connected_controllers[controller_idx].type;
|
||||
auto& controller = shared_memory_entries[controller_idx];
|
||||
if (controller_type == NPadControllerType::None) {
|
||||
@@ -448,7 +466,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
AddNewController(requested_controller);
|
||||
} else {
|
||||
controller.type = requested_controller;
|
||||
InitNewlyAddedControler(i);
|
||||
InitNewlyAddedController(i);
|
||||
}
|
||||
had_controller_update = true;
|
||||
}
|
||||
@@ -513,7 +531,7 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
|
||||
controller = DecideBestController(controller);
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
connected_controllers[8] = {controller, true};
|
||||
InitNewlyAddedControler(8);
|
||||
InitNewlyAddedController(8);
|
||||
return;
|
||||
}
|
||||
const auto pos =
|
||||
@@ -525,19 +543,19 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
|
||||
}
|
||||
const auto controller_id = std::distance(connected_controllers.begin(), pos);
|
||||
connected_controllers[controller_id] = {controller, true};
|
||||
InitNewlyAddedControler(controller_id);
|
||||
InitNewlyAddedController(controller_id);
|
||||
}
|
||||
|
||||
void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
|
||||
controller = DecideBestController(controller);
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
|
||||
InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
|
||||
InitNewlyAddedController(NPadIdToIndex(NPAD_HANDHELD));
|
||||
return;
|
||||
}
|
||||
|
||||
connected_controllers[NPadIdToIndex(npad_id)] = {controller, true};
|
||||
InitNewlyAddedControler(NPadIdToIndex(npad_id));
|
||||
InitNewlyAddedController(NPadIdToIndex(npad_id));
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectNPad(u32 npad_id) {
|
||||
@@ -548,36 +566,6 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) {
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
// Handheld is not even a supported type, lets stop here
|
||||
if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
|
||||
NPAD_HANDHELD) == supported_npad_id_types.end()) {
|
||||
return false;
|
||||
}
|
||||
// Handheld should not be supported in docked mode
|
||||
if (Settings::values.use_docked_mode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
switch (controller) {
|
||||
case NPadControllerType::ProController:
|
||||
return style.pro_controller;
|
||||
case NPadControllerType::Handheld:
|
||||
return style.handheld;
|
||||
case NPadControllerType::JoyDual:
|
||||
return style.joycon_dual;
|
||||
case NPadControllerType::JoyLeft:
|
||||
return style.joycon_left;
|
||||
case NPadControllerType::JoyRight:
|
||||
return style.joycon_right;
|
||||
case NPadControllerType::Pokeball:
|
||||
return style.pokeball;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
|
||||
if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
|
||||
// These are controllers without led patterns
|
||||
|
||||
@@ -39,13 +39,13 @@ public:
|
||||
union {
|
||||
u32_le raw{};
|
||||
|
||||
BitField<0, 1, u32> pro_controller;
|
||||
BitField<1, 1, u32> handheld;
|
||||
BitField<2, 1, u32> joycon_dual;
|
||||
BitField<3, 1, u32> joycon_left;
|
||||
BitField<4, 1, u32> joycon_right;
|
||||
BitField<0, 1, u32_le> pro_controller;
|
||||
BitField<1, 1, u32_le> handheld;
|
||||
BitField<2, 1, u32_le> joycon_dual;
|
||||
BitField<3, 1, u32_le> joycon_left;
|
||||
BitField<4, 1, u32_le> joycon_right;
|
||||
|
||||
BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
|
||||
BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
|
||||
@@ -128,6 +128,10 @@ public:
|
||||
// Specifically for cheat engine and other features.
|
||||
u32 GetAndResetPressState();
|
||||
|
||||
NPadControllerType DecideBestController(NPadControllerType priority) const;
|
||||
|
||||
static NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
|
||||
static Settings::ControllerType MapNPadTypeToSettings(NPadControllerType type);
|
||||
static std::size_t NPadIdToIndex(u32 npad_id);
|
||||
static u32 IndexToNPad(std::size_t index);
|
||||
|
||||
@@ -150,43 +154,43 @@ private:
|
||||
union {
|
||||
u64_le raw{};
|
||||
// Button states
|
||||
BitField<0, 1, u64> a;
|
||||
BitField<1, 1, u64> b;
|
||||
BitField<2, 1, u64> x;
|
||||
BitField<3, 1, u64> y;
|
||||
BitField<4, 1, u64> l_stick;
|
||||
BitField<5, 1, u64> r_stick;
|
||||
BitField<6, 1, u64> l;
|
||||
BitField<7, 1, u64> r;
|
||||
BitField<8, 1, u64> zl;
|
||||
BitField<9, 1, u64> zr;
|
||||
BitField<10, 1, u64> plus;
|
||||
BitField<11, 1, u64> minus;
|
||||
BitField<0, 1, u64_le> a;
|
||||
BitField<1, 1, u64_le> b;
|
||||
BitField<2, 1, u64_le> x;
|
||||
BitField<3, 1, u64_le> y;
|
||||
BitField<4, 1, u64_le> l_stick;
|
||||
BitField<5, 1, u64_le> r_stick;
|
||||
BitField<6, 1, u64_le> l;
|
||||
BitField<7, 1, u64_le> r;
|
||||
BitField<8, 1, u64_le> zl;
|
||||
BitField<9, 1, u64_le> zr;
|
||||
BitField<10, 1, u64_le> plus;
|
||||
BitField<11, 1, u64_le> minus;
|
||||
|
||||
// D-Pad
|
||||
BitField<12, 1, u64> d_left;
|
||||
BitField<13, 1, u64> d_up;
|
||||
BitField<14, 1, u64> d_right;
|
||||
BitField<15, 1, u64> d_down;
|
||||
BitField<12, 1, u64_le> d_left;
|
||||
BitField<13, 1, u64_le> d_up;
|
||||
BitField<14, 1, u64_le> d_right;
|
||||
BitField<15, 1, u64_le> d_down;
|
||||
|
||||
// Left JoyStick
|
||||
BitField<16, 1, u64> l_stick_left;
|
||||
BitField<17, 1, u64> l_stick_up;
|
||||
BitField<18, 1, u64> l_stick_right;
|
||||
BitField<19, 1, u64> l_stick_down;
|
||||
BitField<16, 1, u64_le> l_stick_left;
|
||||
BitField<17, 1, u64_le> l_stick_up;
|
||||
BitField<18, 1, u64_le> l_stick_right;
|
||||
BitField<19, 1, u64_le> l_stick_down;
|
||||
|
||||
// Right JoyStick
|
||||
BitField<20, 1, u64> r_stick_left;
|
||||
BitField<21, 1, u64> r_stick_up;
|
||||
BitField<22, 1, u64> r_stick_right;
|
||||
BitField<23, 1, u64> r_stick_down;
|
||||
BitField<20, 1, u64_le> r_stick_left;
|
||||
BitField<21, 1, u64_le> r_stick_up;
|
||||
BitField<22, 1, u64_le> r_stick_right;
|
||||
BitField<23, 1, u64_le> r_stick_down;
|
||||
|
||||
// Not always active?
|
||||
BitField<24, 1, u64> left_sl;
|
||||
BitField<25, 1, u64> left_sr;
|
||||
BitField<24, 1, u64_le> left_sl;
|
||||
BitField<25, 1, u64_le> left_sr;
|
||||
|
||||
BitField<26, 1, u64> right_sl;
|
||||
BitField<27, 1, u64> right_sr;
|
||||
BitField<26, 1, u64_le> right_sl;
|
||||
BitField<27, 1, u64_le> right_sr;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
|
||||
@@ -200,12 +204,12 @@ private:
|
||||
struct ConnectionState {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32> IsConnected;
|
||||
BitField<1, 1, u32> IsWired;
|
||||
BitField<2, 1, u32> IsLeftJoyConnected;
|
||||
BitField<3, 1, u32> IsLeftJoyWired;
|
||||
BitField<4, 1, u32> IsRightJoyConnected;
|
||||
BitField<5, 1, u32> IsRightJoyWired;
|
||||
BitField<0, 1, u32_le> IsConnected;
|
||||
BitField<1, 1, u32_le> IsWired;
|
||||
BitField<2, 1, u32_le> IsLeftJoyConnected;
|
||||
BitField<3, 1, u32_le> IsLeftJoyWired;
|
||||
BitField<4, 1, u32_le> IsRightJoyConnected;
|
||||
BitField<5, 1, u32_le> IsRightJoyWired;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
|
||||
@@ -240,23 +244,23 @@ private:
|
||||
struct NPadProperties {
|
||||
union {
|
||||
s64_le raw{};
|
||||
BitField<11, 1, s64> is_vertical;
|
||||
BitField<12, 1, s64> is_horizontal;
|
||||
BitField<13, 1, s64> use_plus;
|
||||
BitField<14, 1, s64> use_minus;
|
||||
BitField<11, 1, s64_le> is_vertical;
|
||||
BitField<12, 1, s64_le> is_horizontal;
|
||||
BitField<13, 1, s64_le> use_plus;
|
||||
BitField<14, 1, s64_le> use_minus;
|
||||
};
|
||||
};
|
||||
|
||||
struct NPadDevice {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, s32> pro_controller;
|
||||
BitField<1, 1, s32> handheld;
|
||||
BitField<2, 1, s32> handheld_left;
|
||||
BitField<3, 1, s32> handheld_right;
|
||||
BitField<4, 1, s32> joycon_left;
|
||||
BitField<5, 1, s32> joycon_right;
|
||||
BitField<6, 1, s32> pokeball;
|
||||
BitField<0, 1, s32_le> pro_controller;
|
||||
BitField<1, 1, s32_le> handheld;
|
||||
BitField<2, 1, s32_le> handheld_left;
|
||||
BitField<3, 1, s32_le> handheld_right;
|
||||
BitField<4, 1, s32_le> joycon_left;
|
||||
BitField<5, 1, s32_le> joycon_right;
|
||||
BitField<6, 1, s32_le> pokeball;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -315,11 +319,9 @@ private:
|
||||
std::array<ControllerHolder, 10> connected_controllers{};
|
||||
bool can_controllers_vibrate{true};
|
||||
|
||||
void InitNewlyAddedControler(std::size_t controller_idx);
|
||||
void InitNewlyAddedController(std::size_t controller_idx);
|
||||
bool IsControllerSupported(NPadControllerType controller) const;
|
||||
NPadControllerType DecideBestController(NPadControllerType priority) const;
|
||||
void RequestPadStateUpdate(u32 npad_id);
|
||||
std::array<ControllerPad, 10> npad_pad_states{};
|
||||
bool IsControllerSupported(NPadControllerType controller);
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -33,8 +33,8 @@ private:
|
||||
struct Attributes {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> start_touch;
|
||||
BitField<1, 1, u32> end_touch;
|
||||
BitField<0, 1, u32_le> start_touch;
|
||||
BitField<1, 1, u32_le> end_touch;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include "controllers/controller_base.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
#include "controllers/controller_base.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ private:
|
||||
union {
|
||||
BitField<0, 16, Flags> flags;
|
||||
BitField<16, 8, Severity> severity;
|
||||
BitField<24, 8, u32> verbosity;
|
||||
BitField<24, 8, u32_le> verbosity;
|
||||
};
|
||||
u32_le payload_size;
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ public:
|
||||
virtual ~nvdevice() = default;
|
||||
union Ioctl {
|
||||
u32_le raw;
|
||||
BitField<0, 8, u32> cmd;
|
||||
BitField<8, 8, u32> group;
|
||||
BitField<16, 14, u32> length;
|
||||
BitField<30, 1, u32> is_in;
|
||||
BitField<31, 1, u32> is_out;
|
||||
BitField<0, 8, u32_le> cmd;
|
||||
BitField<8, 8, u32_le> group;
|
||||
BitField<16, 14, u32_le> length;
|
||||
BitField<30, 1, u32_le> is_in;
|
||||
BitField<31, 1, u32_le> is_out;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
|
||||
auto& instance = Core::System::GetInstance();
|
||||
instance.GetPerfStats().EndGameFrame();
|
||||
instance.GPU().SwapBuffers(framebuffer);
|
||||
instance.Renderer().SwapBuffers(framebuffer);
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -179,7 +178,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
auto& gpu = system_instance.GPU();
|
||||
auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
|
||||
ASSERT(cpu_addr);
|
||||
gpu.FlushAndInvalidateRegion(ToCacheAddr(Memory::GetPointer(*cpu_addr)), itr->second.size);
|
||||
system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
|
||||
|
||||
params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
|
||||
|
||||
|
||||
@@ -136,6 +136,16 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PushGPUEntries(Tegra::CommandList&& entries) {
|
||||
if (entries.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()};
|
||||
dma_pusher.Push(std::move(entries));
|
||||
dma_pusher.DispatchCalls();
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
|
||||
UNIMPLEMENTED();
|
||||
@@ -153,7 +163,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
|
||||
Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
|
||||
PushGPUEntries(std::move(entries));
|
||||
|
||||
params.fence_out.id = 0;
|
||||
params.fence_out.value = 0;
|
||||
@@ -174,7 +184,7 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
|
||||
Memory::ReadBlock(params.address, entries.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
|
||||
Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
|
||||
PushGPUEntries(std::move(entries));
|
||||
|
||||
params.fence_out.id = 0;
|
||||
params.fence_out.value = 0;
|
||||
|
||||
@@ -186,7 +186,7 @@ void NVFlinger::Compose() {
|
||||
|
||||
// There was no queued buffer to draw, render previous frame
|
||||
system_instance.GetPerfStats().EndGameFrame();
|
||||
system_instance.GPU().SwapBuffers({});
|
||||
system_instance.Renderer().SwapBuffers({});
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
@@ -75,8 +76,7 @@ namespace Service {
|
||||
* Creates a function string for logging, complete with the name (or header code, depending
|
||||
* on what's passed in) the port name, and all the cmd_buff arguments.
|
||||
*/
|
||||
[[maybe_unused]] static std::string MakeFunctionString(std::string_view name,
|
||||
std::string_view port_name,
|
||||
[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
|
||||
const u32* cmd_buff) {
|
||||
// Number of params == bits 0-5 + bits 6-11
|
||||
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
|
||||
@@ -158,7 +158,9 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
|
||||
return ReportUnimplementedFunction(ctx, info);
|
||||
}
|
||||
|
||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||
LOG_TRACE(
|
||||
Service, "{}",
|
||||
MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str());
|
||||
handler_invoker(this, info->handler_callback, ctx);
|
||||
}
|
||||
|
||||
@@ -167,7 +169,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
case IPC::CommandType::Close: {
|
||||
IPC::ResponseBuilder rb{context, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
return IPC::ERR_REMOTE_PROCESS_DEAD;
|
||||
return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead);
|
||||
}
|
||||
case IPC::CommandType::ControlWithContext:
|
||||
case IPC::CommandType::Control: {
|
||||
|
||||
@@ -30,7 +30,7 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->GetParent()->client};
|
||||
Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client};
|
||||
rb.PushMoveObjects(session);
|
||||
|
||||
LOG_DEBUG(Service, "session={}", session->GetObjectId());
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
if (port == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::static_pointer_cast<T>(port->GetHLEHandler());
|
||||
return std::static_pointer_cast<T>(port->hle_handler);
|
||||
}
|
||||
|
||||
void InvokeControlRequest(Kernel::HLERequestContext& context);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_m.h"
|
||||
#include "core/hle/service/vi/vi_s.h"
|
||||
@@ -34,7 +33,6 @@
|
||||
namespace Service::VI {
|
||||
|
||||
constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
|
||||
constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
|
||||
constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
|
||||
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
|
||||
|
||||
@@ -1205,40 +1203,26 @@ IApplicationDisplayService::IApplicationDisplayService(
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
static bool IsValidServiceAccess(Permission permission, Policy policy) {
|
||||
if (permission == Permission::User) {
|
||||
return policy == Policy::User;
|
||||
}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
|
||||
|
||||
if (permission == Permission::System || permission == Permission::Manager) {
|
||||
return policy == Policy::User || policy == Policy::Compositor;
|
||||
}
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
|
||||
Permission permission) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto policy = rp.PopEnum<Policy>();
|
||||
|
||||
if (!IsValidServiceAccess(permission, policy)) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger));
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
|
||||
std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -4,21 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
}
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
enum class DisplayResolution : u32 {
|
||||
@@ -28,24 +19,21 @@ enum class DisplayResolution : u32 {
|
||||
UndockedHeight = 720,
|
||||
};
|
||||
|
||||
/// Permission level for a particular VI service instance
|
||||
enum class Permission {
|
||||
User,
|
||||
System,
|
||||
Manager,
|
||||
};
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module, const char* name,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~Interface() override;
|
||||
|
||||
/// A policy type that may be requested via GetDisplayService and
|
||||
/// GetDisplayServiceWithProxyNameExchange
|
||||
enum class Policy {
|
||||
User,
|
||||
Compositor,
|
||||
};
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
namespace detail {
|
||||
void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission);
|
||||
} // namespace detail
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
};
|
||||
|
||||
/// Registers all VI services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_m.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
|
||||
VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{2, &VI_M::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
@@ -19,10 +17,4 @@ VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
|
||||
VI_M::~VI_M() = default;
|
||||
|
||||
void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::Manager);
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -4,27 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class VI_M final : public ServiceFramework<VI_M> {
|
||||
class VI_M final : public Module::Interface {
|
||||
public:
|
||||
explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_M() override;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_s.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
|
||||
VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &VI_S::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
@@ -19,10 +17,4 @@ VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
|
||||
VI_S::~VI_S() = default;
|
||||
|
||||
void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::System);
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -4,27 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class VI_S final : public ServiceFramework<VI_S> {
|
||||
class VI_S final : public Module::Interface {
|
||||
public:
|
||||
explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_S() override;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_u.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
|
||||
VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &VI_U::GetDisplayService, "GetDisplayService"},
|
||||
};
|
||||
@@ -18,10 +16,4 @@ VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
|
||||
VI_U::~VI_U() = default;
|
||||
|
||||
void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::User);
|
||||
}
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -4,27 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
class VI_U final : public ServiceFramework<VI_U> {
|
||||
class VI_U final : public Module::Interface {
|
||||
public:
|
||||
explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_U() override;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/loader/elf.h"
|
||||
|
||||
147
src/core/loader/linker.cpp
Normal file
147
src/core/loader/linker.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/loader/linker.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
|
||||
|
||||
enum DynamicType : u32 {
|
||||
DT_NULL = 0,
|
||||
DT_PLTRELSZ = 2,
|
||||
DT_STRTAB = 5,
|
||||
DT_SYMTAB = 6,
|
||||
DT_RELA = 7,
|
||||
DT_RELASZ = 8,
|
||||
DT_STRSZ = 10,
|
||||
DT_JMPREL = 23,
|
||||
};
|
||||
|
||||
struct Elf64_Rela {
|
||||
u64_le offset;
|
||||
RelocationType type;
|
||||
u32_le symbol;
|
||||
s64_le addend;
|
||||
};
|
||||
static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size.");
|
||||
|
||||
struct Elf64_Dyn {
|
||||
u64_le tag;
|
||||
u64_le value;
|
||||
};
|
||||
static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size.");
|
||||
|
||||
struct Elf64_Sym {
|
||||
u32_le name;
|
||||
INSERT_PADDING_BYTES(0x2);
|
||||
u16_le shndx;
|
||||
u64_le value;
|
||||
u64_le size;
|
||||
};
|
||||
static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
|
||||
|
||||
void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
|
||||
u64 relocation_offset, u64 size, VAddr load_base) {
|
||||
for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
|
||||
Elf64_Rela rela;
|
||||
std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
|
||||
|
||||
const Symbol& symbol = symbols[rela.symbol];
|
||||
switch (rela.type) {
|
||||
case RelocationType::RELATIVE: {
|
||||
const u64 value = load_base + rela.addend;
|
||||
if (!symbol.name.empty()) {
|
||||
exports[symbol.name] = value;
|
||||
}
|
||||
std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
|
||||
break;
|
||||
}
|
||||
case RelocationType::JUMP_SLOT:
|
||||
case RelocationType::GLOB_DAT:
|
||||
if (!symbol.value) {
|
||||
imports[symbol.name] = {rela.offset + load_base, 0};
|
||||
} else {
|
||||
exports[symbol.name] = symbol.value;
|
||||
std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64));
|
||||
}
|
||||
break;
|
||||
case RelocationType::ABS64:
|
||||
if (!symbol.value) {
|
||||
imports[symbol.name] = {rela.offset + load_base, rela.addend};
|
||||
} else {
|
||||
const u64 value = symbol.value + rela.addend;
|
||||
exports[symbol.name] = value;
|
||||
std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base) {
|
||||
std::map<u64, u64> dynamic;
|
||||
while (dynamic_section_offset < program_image.size()) {
|
||||
Elf64_Dyn dyn;
|
||||
std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn));
|
||||
dynamic_section_offset += sizeof(Elf64_Dyn);
|
||||
|
||||
if (dyn.tag == DT_NULL) {
|
||||
break;
|
||||
}
|
||||
dynamic[dyn.tag] = dyn.value;
|
||||
}
|
||||
|
||||
u64 offset = dynamic[DT_SYMTAB];
|
||||
std::vector<Symbol> symbols;
|
||||
while (offset < program_image.size()) {
|
||||
Elf64_Sym sym;
|
||||
std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym));
|
||||
offset += sizeof(Elf64_Sym);
|
||||
|
||||
if (sym.name >= dynamic[DT_STRSZ]) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]);
|
||||
if (sym.value) {
|
||||
exports[name] = load_base + sym.value;
|
||||
symbols.emplace_back(std::move(name), load_base + sym.value);
|
||||
} else {
|
||||
symbols.emplace_back(std::move(name), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (dynamic.find(DT_RELA) != dynamic.end()) {
|
||||
WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
|
||||
}
|
||||
|
||||
if (dynamic.find(DT_JMPREL) != dynamic.end()) {
|
||||
WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
|
||||
load_base);
|
||||
}
|
||||
}
|
||||
|
||||
void Linker::ResolveImports() {
|
||||
// Resolve imports
|
||||
for (const auto& import : imports) {
|
||||
const auto& search = exports.find(import.first);
|
||||
if (search != exports.end()) {
|
||||
Memory::Write64(import.second.ea, search->second + import.second.addend);
|
||||
} else {
|
||||
LOG_ERROR(Loader, "Unresolved import: {}", import.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
36
src/core/loader/linker.h
Normal file
36
src/core/loader/linker.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class Linker {
|
||||
protected:
|
||||
struct Symbol {
|
||||
Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
|
||||
std::string name;
|
||||
u64 value;
|
||||
};
|
||||
|
||||
struct Import {
|
||||
VAddr ea;
|
||||
s64 addend;
|
||||
};
|
||||
|
||||
void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
|
||||
u64 relocation_offset, u64 size, VAddr load_base);
|
||||
void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
|
||||
|
||||
void ResolveImports();
|
||||
|
||||
std::map<std::string, Import> imports;
|
||||
std::map<std::string, VAddr> exports;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/linker.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
@@ -21,7 +21,7 @@ class Process;
|
||||
namespace Loader {
|
||||
|
||||
/// Loads an NRO file
|
||||
class AppLoader_NRO final : public AppLoader {
|
||||
class AppLoader_NRO final : public AppLoader, Linker {
|
||||
public:
|
||||
explicit AppLoader_NRO(FileSys::VirtualFile file);
|
||||
~AppLoader_NRO() override;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/loader/nso.h"
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
#include <optional>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/loader/linker.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -26,7 +26,7 @@ struct NSOArgumentHeader {
|
||||
static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
|
||||
|
||||
/// Loads an NSO file
|
||||
class AppLoader_NSO final : public AppLoader {
|
||||
class AppLoader_NSO final : public AppLoader, Linker {
|
||||
public:
|
||||
explicit AppLoader_NSO(FileSys::VirtualFile file);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
@@ -19,14 +18,13 @@
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_setup.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Memory {
|
||||
|
||||
static Common::PageTable* current_page_table = nullptr;
|
||||
static PageTable* current_page_table = nullptr;
|
||||
|
||||
void SetCurrentPageTable(Common::PageTable* page_table) {
|
||||
void SetCurrentPageTable(PageTable* page_table) {
|
||||
current_page_table = page_table;
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
@@ -38,20 +36,39 @@ void SetCurrentPageTable(Common::PageTable* page_table) {
|
||||
}
|
||||
}
|
||||
|
||||
Common::PageTable* GetCurrentPageTable() {
|
||||
PageTable* GetCurrentPageTable() {
|
||||
return current_page_table;
|
||||
}
|
||||
|
||||
static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
|
||||
Common::PageType type) {
|
||||
PageTable::PageTable() = default;
|
||||
|
||||
PageTable::PageTable(std::size_t address_space_width_in_bits) {
|
||||
Resize(address_space_width_in_bits);
|
||||
}
|
||||
|
||||
PageTable::~PageTable() = default;
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits) {
|
||||
const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
|
||||
|
||||
pointers.resize(num_page_table_entries);
|
||||
attributes.resize(num_page_table_entries);
|
||||
|
||||
// The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
|
||||
// vector size is subsequently decreased (via resize), the vector might not automatically
|
||||
// actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
|
||||
// 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
|
||||
|
||||
pointers.shrink_to_fit();
|
||||
attributes.shrink_to_fit();
|
||||
}
|
||||
|
||||
static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
|
||||
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
|
||||
(base + size) * PAGE_SIZE);
|
||||
|
||||
// During boot, current_page_table might not be set yet, in which case we need not flush
|
||||
if (current_page_table) {
|
||||
Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
|
||||
size * PAGE_SIZE);
|
||||
}
|
||||
RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
|
||||
FlushMode::FlushAndInvalidate);
|
||||
|
||||
VAddr end = base + size;
|
||||
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
|
||||
@@ -71,47 +88,41 @@ static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* me
|
||||
}
|
||||
}
|
||||
|
||||
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
|
||||
void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
|
||||
}
|
||||
|
||||
void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer mmio_handler) {
|
||||
void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
|
||||
|
||||
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
|
||||
Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
|
||||
page_table.special_regions.add(
|
||||
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
|
||||
SpecialRegion region{SpecialRegion::Type::IODevice, std::move(mmio_handler)};
|
||||
page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
|
||||
}
|
||||
|
||||
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
|
||||
void UnmapRegion(PageTable& page_table, VAddr base, u64 size) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
|
||||
|
||||
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
|
||||
page_table.special_regions.erase(interval);
|
||||
}
|
||||
|
||||
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer hook) {
|
||||
void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
|
||||
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
|
||||
Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
|
||||
page_table.special_regions.add(
|
||||
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
|
||||
SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
|
||||
page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
|
||||
}
|
||||
|
||||
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer hook) {
|
||||
void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
|
||||
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
|
||||
Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
|
||||
page_table.special_regions.subtract(
|
||||
std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
|
||||
SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
|
||||
page_table.special_regions.subtract(std::make_pair(interval, std::set<SpecialRegion>{region}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,19 +171,19 @@ T Read(const VAddr vaddr) {
|
||||
return value;
|
||||
}
|
||||
|
||||
Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped:
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
|
||||
return 0;
|
||||
case Common::PageType::Memory:
|
||||
case PageType::Memory:
|
||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
auto host_ptr{GetPointerFromVMA(vaddr)};
|
||||
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Flush);
|
||||
|
||||
T value;
|
||||
std::memcpy(&value, host_ptr, sizeof(T));
|
||||
std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T));
|
||||
return value;
|
||||
}
|
||||
default:
|
||||
@@ -190,19 +201,18 @@ void Write(const VAddr vaddr, const T data) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
switch (type) {
|
||||
case Common::PageType::Unmapped:
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
||||
static_cast<u32>(data), vaddr);
|
||||
return;
|
||||
case Common::PageType::Memory:
|
||||
case PageType::Memory:
|
||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
auto host_ptr{GetPointerFromVMA(vaddr)};
|
||||
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
|
||||
std::memcpy(host_ptr, &data, sizeof(T));
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate);
|
||||
std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -217,10 +227,10 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
|
||||
if (page_pointer)
|
||||
return true;
|
||||
|
||||
if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
|
||||
if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
|
||||
return true;
|
||||
|
||||
if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
|
||||
if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
@@ -240,8 +250,7 @@ u8* GetPointer(const VAddr vaddr) {
|
||||
return page_pointer + (vaddr & PAGE_MASK);
|
||||
}
|
||||
|
||||
if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
|
||||
Common::PageType::RasterizerCachedMemory) {
|
||||
if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) {
|
||||
return GetPointerFromVMA(vaddr);
|
||||
}
|
||||
|
||||
@@ -275,20 +284,20 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
|
||||
|
||||
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
|
||||
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
|
||||
Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
|
||||
if (cached) {
|
||||
// Switch page type to cached if now cached
|
||||
switch (page_type) {
|
||||
case Common::PageType::Unmapped:
|
||||
case PageType::Unmapped:
|
||||
// It is not necessary for a process to have this region mapped into its address
|
||||
// space, for example, a system module need not have a VRAM mapping.
|
||||
break;
|
||||
case Common::PageType::Memory:
|
||||
page_type = Common::PageType::RasterizerCachedMemory;
|
||||
case PageType::Memory:
|
||||
page_type = PageType::RasterizerCachedMemory;
|
||||
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory:
|
||||
case PageType::RasterizerCachedMemory:
|
||||
// There can be more than one GPU region mapped per CPU region, so it's common that
|
||||
// this area is already marked as cached.
|
||||
break;
|
||||
@@ -298,23 +307,23 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
|
||||
} else {
|
||||
// Switch page type to uncached if now uncached
|
||||
switch (page_type) {
|
||||
case Common::PageType::Unmapped:
|
||||
case PageType::Unmapped:
|
||||
// It is not necessary for a process to have this region mapped into its address
|
||||
// space, for example, a system module need not have a VRAM mapping.
|
||||
break;
|
||||
case Common::PageType::Memory:
|
||||
case PageType::Memory:
|
||||
// There can be more than one GPU region mapped per CPU region, so it's common that
|
||||
// this area is already unmarked as cached.
|
||||
break;
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
|
||||
if (pointer == nullptr) {
|
||||
// It's possible that this function has been called while updating the pagetable
|
||||
// after unmapping a VMA. In that case the underlying VMA will no longer exist,
|
||||
// and we should just leave the pagetable entry blank.
|
||||
page_type = Common::PageType::Unmapped;
|
||||
page_type = PageType::Unmapped;
|
||||
} else {
|
||||
page_type = Common::PageType::Memory;
|
||||
page_type = PageType::Memory;
|
||||
current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
|
||||
}
|
||||
break;
|
||||
@@ -326,6 +335,47 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
|
||||
auto& system_instance = Core::System::GetInstance();
|
||||
|
||||
// Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
|
||||
// null here
|
||||
if (!system_instance.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const VAddr end = start + size;
|
||||
|
||||
const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
|
||||
if (start >= region_end || end <= region_start) {
|
||||
// No overlap with region
|
||||
return;
|
||||
}
|
||||
|
||||
const VAddr overlap_start = std::max(start, region_start);
|
||||
const VAddr overlap_end = std::min(end, region_end);
|
||||
const VAddr overlap_size = overlap_end - overlap_start;
|
||||
|
||||
auto& rasterizer = system_instance.Renderer().Rasterizer();
|
||||
switch (mode) {
|
||||
case FlushMode::Flush:
|
||||
rasterizer.FlushRegion(overlap_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::Invalidate:
|
||||
rasterizer.InvalidateRegion(overlap_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::FlushAndInvalidate:
|
||||
rasterizer.FlushAndInvalidateRegion(overlap_start, overlap_size);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
|
||||
CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress());
|
||||
CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress());
|
||||
}
|
||||
|
||||
u8 Read8(const VAddr addr) {
|
||||
return Read<u8>(addr);
|
||||
}
|
||||
@@ -356,24 +406,24 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Unmapped: {
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||
current_vaddr, src_addr, size);
|
||||
std::memset(dest_buffer, 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
case Common::PageType::Memory: {
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
|
||||
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
|
||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
|
||||
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
|
||||
std::memcpy(dest_buffer, host_ptr, copy_amount);
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Flush);
|
||||
std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -420,23 +470,23 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Unmapped: {
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||
current_vaddr, dest_addr, size);
|
||||
break;
|
||||
}
|
||||
case Common::PageType::Memory: {
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
|
||||
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
|
||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
|
||||
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
|
||||
std::memcpy(host_ptr, src_buffer, copy_amount);
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Invalidate);
|
||||
std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -466,23 +516,23 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Unmapped: {
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||
current_vaddr, dest_addr, size);
|
||||
break;
|
||||
}
|
||||
case Common::PageType::Memory: {
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
|
||||
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
|
||||
std::memset(dest_ptr, 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
|
||||
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
|
||||
std::memset(host_ptr, 0, copy_amount);
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Invalidate);
|
||||
std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -508,23 +558,23 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Unmapped: {
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
|
||||
current_vaddr, src_addr, size);
|
||||
ZeroBlock(process, dest_addr, copy_amount);
|
||||
break;
|
||||
}
|
||||
case Common::PageType::Memory: {
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
|
||||
WriteBlock(process, dest_addr, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
case Common::PageType::RasterizerCachedMemory: {
|
||||
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
|
||||
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
|
||||
WriteBlock(process, dest_addr, host_ptr, copy_amount);
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Flush);
|
||||
WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -10,10 +10,7 @@
|
||||
#include <vector>
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
struct PageTable;
|
||||
}
|
||||
#include "core/memory_hook.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Process;
|
||||
@@ -29,6 +26,71 @@ constexpr std::size_t PAGE_BITS = 12;
|
||||
constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
|
||||
constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
|
||||
|
||||
enum class PageType : u8 {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
||||
Special,
|
||||
};
|
||||
|
||||
struct SpecialRegion {
|
||||
enum class Type {
|
||||
DebugHook,
|
||||
IODevice,
|
||||
} type;
|
||||
|
||||
MemoryHookPointer handler;
|
||||
|
||||
bool operator<(const SpecialRegion& other) const {
|
||||
return std::tie(type, handler) < std::tie(other.type, other.handler);
|
||||
}
|
||||
|
||||
bool operator==(const SpecialRegion& other) const {
|
||||
return std::tie(type, handler) == std::tie(other.type, other.handler);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
|
||||
* mimics the way a real CPU page table works.
|
||||
*/
|
||||
struct PageTable {
|
||||
explicit PageTable();
|
||||
explicit PageTable(std::size_t address_space_width_in_bits);
|
||||
~PageTable();
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accomodate enough pages within
|
||||
* a given address space.
|
||||
*
|
||||
* @param address_space_width_in_bits The address size width in bits.
|
||||
*/
|
||||
void Resize(std::size_t address_space_width_in_bits);
|
||||
|
||||
/**
|
||||
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||
* corresponding entry in the `attributes` vector is of type `Memory`.
|
||||
*/
|
||||
std::vector<u8*> pointers;
|
||||
|
||||
/**
|
||||
* Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
|
||||
* of type `Special`.
|
||||
*/
|
||||
boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
|
||||
|
||||
/**
|
||||
* Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
|
||||
* the corresponding entry in `pointers` MUST be set to null.
|
||||
*/
|
||||
std::vector<PageType> attributes;
|
||||
};
|
||||
|
||||
/// Virtual user-space memory regions
|
||||
enum : VAddr {
|
||||
/// Read-only page containing kernel and system configuration values.
|
||||
@@ -54,8 +116,8 @@ enum : VAddr {
|
||||
};
|
||||
|
||||
/// Currently active page table
|
||||
void SetCurrentPageTable(Common::PageTable* page_table);
|
||||
Common::PageTable* GetCurrentPageTable();
|
||||
void SetCurrentPageTable(PageTable* page_table);
|
||||
PageTable* GetCurrentPageTable();
|
||||
|
||||
/// Determines if the given VAddr is valid for the specified process.
|
||||
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
|
||||
@@ -99,4 +161,10 @@ enum class FlushMode {
|
||||
*/
|
||||
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
|
||||
|
||||
/**
|
||||
* Flushes and invalidates any externally cached rasterizer resources touching the given virtual
|
||||
* address region.
|
||||
*/
|
||||
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode);
|
||||
|
||||
} // namespace Memory
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user