Compare commits

..

1 Commits

Author SHA1 Message Date
Dimitri A
0e7ad1c367 gdbstub: Fix some bugs in IsMemoryBreak() and ServeBreak. Add workaround to let watchpoints break into GDB. (#4651)
* gdbstub: fix IsMemoryBreak() returning false while connected to client

As a result, the only existing codepath for a memory watchpoint hit to break into GDB (InterpeterMainLoop, GDB_BP_CHECK, ARMul_State::RecordBreak) is finally taken,
which exposes incorrect logic* in both RecordBreak and ServeBreak.

* a blank BreakpointAddress structure is passed, which sets r15 (PC) to NULL

* gdbstub: DynCom: default-initialize two members/vars used in conditionals

* gdbstub: DynCom: don't record memory watchpoint hits via RecordBreak()

For now, instead check for GDBStub::IsMemoryBreak() in InterpreterMainLoop and ServeBreak.

Fixes PC being set to a stale/unhit breakpoint address (often zero) when a memory watchpoint (rwatch, watch, awatch) is handled in ServeBreak() and generates a GDB trap.

Reasons for removing a call to RecordBreak() for memory watchpoints:
* The``breakpoint_data`` we pass is typed Execute or None. It describes the predicted next code breakpoint hit relative to PC;

* GDBStub::IsMemoryBreak() returns true if a recent Read/Write operation hit a watchpoint. It doesn't specify which in return, nor does it trace it anywhere. Thus, the only data we could give RecordBreak() is a placeholder BreakpointAddress at offset NULL and type Access. I found the idea silly, compared to simply relying on GDBStub::IsMemoryBreak().

There is currently no measure in the code that remembers the addresses (and types) of any watchpoints that were hit by an instruction, in order to send them to GDB as "extended stop information."
I'm considering an implementation for this.

* gdbstub: Change an ASSERT to DEBUG_ASSERT

I have never seen the (Reg[15] == last_bkpt.address) assert fail in practice, even after several weeks of (locally) developping various branches around GDB.  Only leave it inside Debug builds.
2019-03-15 16:31:06 +01:00
172 changed files with 2256 additions and 4785 deletions

View File

@@ -1,6 +1,5 @@
#!/bin/sh -ex
brew update
brew install p7zip qt5 sdl2 ccache
brew install dylibbundler p7zip qt5 sdl2 ccache
brew outdated cmake || brew upgrade cmake
pip3 install macpack

View File

@@ -11,19 +11,92 @@ mkdir "$REV_NAME"
cp build/bin/yuzu-cmd "$REV_NAME"
cp -r build/bin/yuzu.app "$REV_NAME"
# move libs into folder for deployment
macpack "${REV_NAME}/yuzu.app/Contents/MacOS/yuzu" -d "../Frameworks"
# move qt frameworks into app bundle for deployment
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app" -executable="${REV_NAME}/yuzu.app/Contents/MacOS/yuzu"
# move qt libs into app bundle for deployment
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app"
# move libs into folder for deployment
macpack "${REV_NAME}/yuzu-cmd" -d "libs"
# move SDL2 libs into folder for deployment
dylibbundler -b -x "${REV_NAME}/yuzu-cmd" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/"
# Make the changes to make the yuzu app standalone (i.e. not dependent on the current brew installation).
# To do this, the absolute references to each and every QT framework must be re-written to point to the local frameworks
# (in the Contents/Frameworks folder).
# The "install_name_tool" is used to do so.
# Coreutils is a hack to coerce Homebrew to point to the absolute Cellar path (symlink dereferenced). i.e:
# ls -l /usr/local/opt/qt5:: /usr/local/opt/qt5 -> ../Cellar/qt5/5.6.1-1
# grealpath ../Cellar/qt5/5.6.1-1:: /usr/local/Cellar/qt5/5.6.1-1
brew install coreutils || brew upgrade coreutils || true
REV_NAME_ALT=$REV_NAME/
# grealpath is located in coreutils, there is no "realpath" for OS X :(
QT_BREWS_PATH=$(grealpath "$(brew --prefix qt5)")
BREW_PATH=$(brew --prefix)
QT_VERSION_NUM=5
$BREW_PATH/opt/qt5/bin/macdeployqt "${REV_NAME_ALT}yuzu.app" \
-executable="${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu"
# These are the files that macdeployqt packed into Contents/Frameworks/ - we don't want those, so we replace them.
declare -a macos_libs=("QtCore" "QtWidgets" "QtGui" "QtOpenGL" "QtPrintSupport")
for macos_lib in "${macos_libs[@]}"
do
SC_FRAMEWORK_PART=$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib
# Replace macdeployqt versions of the Frameworks with our own (from /usr/local/opt/qt5/lib/)
cp "$BREW_PATH/opt/qt5/lib/$SC_FRAMEWORK_PART" "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
# Replace references within the embedded Framework files with "internal" versions.
for macos_lib2 in "${macos_libs[@]}"
do
# Since brew references both the non-symlinked and symlink paths of QT5, it needs to be duplicated.
# /usr/local/Cellar/qt5/5.6.1-1/lib and /usr/local/opt/qt5/lib both resolve to the same files.
# So the two lines below are effectively duplicates when resolved as a path, but as strings, they aren't.
RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
install_name_tool -change \
$QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
install_name_tool -change \
"$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
done
done
# Handles `This application failed to start because it could not find or load the Qt platform plugin "cocoa"`
# Which manifests itself as:
# "Exception Type: EXC_CRASH (SIGABRT) | Exception Codes: 0x0000000000000000, 0x0000000000000000 | Exception Note: EXC_CORPSE_NOTIFY"
# There may be more dylibs needed to be fixed...
declare -a macos_plugins=("Plugins/platforms/libqcocoa.dylib")
for macos_lib in "${macos_plugins[@]}"
do
install_name_tool -id @executable_path/../$macos_lib "${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
for macos_lib2 in "${macos_libs[@]}"
do
RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
install_name_tool -change \
$QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
install_name_tool -change \
"$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
done
done
for macos_lib in "${macos_libs[@]}"
do
# Debugging info for Travis-CI
otool -L "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib"
done
# Make the yuzu.app application launch a debugging terminal.
# Store away the actual binary
mv ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu-bin
mv ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu-bin
cat > ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu <<EOL
cat > ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu <<EOL
#!/usr/bin/env bash
cd "\`dirname "\$0"\`"
chmod +x yuzu-bin
@@ -32,9 +105,6 @@ EOL
# Content that will serve as the launching script for yuzu (within the .app folder)
# Make the launching script executable
chmod +x ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu
# Verify loader instructions
find "$REV_NAME" -exec otool -L {} \;
chmod +x ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu
. .travis/common/post-upload.sh

View File

@@ -104,18 +104,90 @@ endif()
message(STATUS "Target architecture: ${ARCHITECTURE}")
# Configure C++ standard
# Configure compilation flags
# ===========================
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
if (MINGW)
add_definitions(-DMINGW_HAS_SECURE_API)
if (MINGW_STATIC_BUILD)
add_definitions(-DQT_STATICPLUGIN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
endif()
endif()
else()
# Silence "deprecation" warnings
add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS)
# Avoid windows.h junk
add_definitions(/DNOMINMAX)
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(/DWIN32_LEAN_AND_MEAN)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
# Tweak optimization settings
# As far as I can tell, there's no way to override the CMake defaults while leaving user
# changes intact, so we'll just clobber everything and say sorry.
message(STATUS "Cache compiler flags ignored, please edit CMakeLists.txt to change the flags.")
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - enhanced debug info for optimized builds
# /permissive- - enables stricter C++ standards conformance checks
set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo /permissive-" CACHE STRING "" FORCE)
# /EHsc - C++-only exception handling semantics
# /Zc:throwingNew - let codegen assume `operator new` will never return null
# /Zc:inline - let codegen omit inline functions in object files
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc /std:c++latest /Zc:throwingNew,inline" CACHE STRING "" FORCE)
# /MDd - Multi-threaded Debug Runtime DLL
set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}" CACHE STRING "" FORCE)
# /O2 - Optimization level 2
# /GS- - No stack buffer overflow checks
# /MD - Multi-threaded runtime DLL
set(CMAKE_C_FLAGS_RELEASE "/O2 /GS- /MD" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
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
# glibc, which may default to 32 bits. glibc allows this to be configured
# by setting _FILE_OFFSET_BITS.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
add_definitions(-D_FILE_OFFSET_BITS=64)
endif()
# CMake seems to only define _DEBUG on Windows
set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
# 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)
@@ -260,21 +332,25 @@ endif()
# Platform-specific library requirements
# ======================================
if (APPLE)
# Umbrella framework for everything GUI-related
find_library(COCOA_LIBRARY Cocoa)
IF (APPLE)
find_library(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
elseif (WIN32)
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
endif()
ELSEIF (WIN32)
# WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista)
add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600)
set(PLATFORM_LIBRARIES winmm ws2_32)
if (MINGW)
IF (MINGW)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif()
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
ENDIF (MINGW)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
endif()
ENDIF (APPLE)
# Setup a custom clang-format target (if clang-format can be found) that will run
# against all the src files. This should be used before making a pull request.

2
externals/opus vendored

View File

@@ -1,79 +1,18 @@
# Enable modules to include each other's files
include_directories(.)
# CMake seems to only define _DEBUG on Windows
set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
# Set compilation flags
if (MSVC)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
# Silence "deprecation" warnings
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
# Avoid windows.h junk
add_definitions(-DNOMINMAX)
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(-DWIN32_LEAN_AND_MEAN)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - enhanced debug info for optimized builds
# /permissive- - enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /Zc:throwingNew - let codegen assume `operator new` will never return null
# /Zc:inline - let codegen omit inline functions in object files
add_compile_options(/W3 /MP /Zi /Zo /permissive- /EHsc /std:c++latest /Zc:throwingNew,inline)
# /GS- - No stack buffer overflow checks
add_compile_options("$<$<CONFIG:Release>:/GS->")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
else()
add_compile_options("-Wno-attributes")
if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
add_compile_options("-stdlib=libc++")
endif()
# Set file offset size to 64 bits.
#
# On modern Unixes, this is typically already the case. The lone exception is
# glibc, which may default to 32 bits. glibc allows this to be configured
# by setting _FILE_OFFSET_BITS.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
add_definitions(-D_FILE_OFFSET_BITS=64)
endif()
if (MINGW)
add_definitions(-DMINGW_HAS_SECURE_API)
if (MINGW_STATIC_BUILD)
add_definitions(-DQT_STATICPLUGIN)
add_compile_options("-static")
endif()
endif()
endif()
add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(audio_core)
add_subdirectory(video_core)
add_subdirectory(input_common)
add_subdirectory(tests)
if (ENABLE_SDL2)
add_subdirectory(yuzu_cmd)
endif()
if (ENABLE_QT)
add_subdirectory(yuzu)
endif()
if (ENABLE_WEB_SERVICE)
add_subdirectory(web_service)
endif()

View File

@@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
release_event = core_timing.RegisterEvent(
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
}
void Stream::Play() {

View File

@@ -92,15 +92,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
multi_level_queue.h
page_table.cpp
page_table.h
param_package.cpp
param_package.h
quaternion.h
@@ -119,8 +114,6 @@ add_library(common STATIC
threadsafe_queue.h
timer.cpp
timer.h
uint128.cpp
uint128.h
vector_math.h
web_result.h
)

View File

@@ -34,7 +34,6 @@
#include <limits>
#include <type_traits>
#include "common/common_funcs.h"
#include "common/swap.h"
/*
* Abstract bitfield class
@@ -109,7 +108,7 @@
* 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:
// UnderlyingType is T for non-enum types and the underlying type of T if
@@ -122,8 +121,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;
@@ -173,7 +170,7 @@ public:
}
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 +182,7 @@ public:
}
private:
StorageTypeWithEndian storage;
StorageType storage;
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
@@ -196,6 +193,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>;

View File

@@ -58,43 +58,4 @@ inline u64 CountLeadingZeroes64(u64 value) {
return __builtin_clzll(value);
}
#endif
#ifdef _MSC_VER
inline u32 CountTrailingZeroes32(u32 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value) != 0) {
return trailing_zero;
}
return 32;
}
inline u64 CountTrailingZeroes64(u64 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
return trailing_zero;
}
return 64;
}
#else
inline u32 CountTrailingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
return __builtin_ctz(value);
}
inline u64 CountTrailingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return __builtin_ctzll(value);
}
#endif
} // namespace Common

View File

@@ -40,9 +40,10 @@ using s64 = std::int64_t; ///< 64-bit signed int
using f32 = float; ///< 32-bit floating point
using f64 = double; ///< 64-bit floating point
using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space.
// TODO: It would be nice to eventually replace these with strong types that prevent accidental
// conversion between each other.
using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
using u128 = std::array<std::uint64_t, 2>;
static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");

View File

@@ -16,22 +16,22 @@ DetachedTasks::DetachedTasks() {
}
void DetachedTasks::WaitForAllTasks() {
std::unique_lock lock{mutex};
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this]() { return count == 0; });
}
DetachedTasks::~DetachedTasks() {
std::unique_lock lock{mutex};
std::unique_lock<std::mutex> lock(mutex);
ASSERT(count == 0);
instance = nullptr;
}
void DetachedTasks::AddTask(std::function<void()> task) {
std::unique_lock lock{instance->mutex};
std::unique_lock<std::mutex> lock(instance->mutex);
++instance->count;
std::thread([task{std::move(task)}]() {
task();
std::unique_lock lock{instance->mutex};
std::unique_lock<std::mutex> lock(instance->mutex);
--instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
})

View File

@@ -46,12 +46,12 @@ public:
}
void AddBackend(std::unique_ptr<Backend> backend) {
std::lock_guard lock{writing_mutex};
std::lock_guard<std::mutex> lock(writing_mutex);
backends.push_back(std::move(backend));
}
void RemoveBackend(std::string_view backend_name) {
std::lock_guard lock{writing_mutex};
std::lock_guard<std::mutex> lock(writing_mutex);
const auto it =
std::remove_if(backends.begin(), backends.end(),
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
@@ -80,7 +80,7 @@ private:
backend_thread = std::thread([&] {
Entry entry;
auto write_logs = [&](Entry& e) {
std::lock_guard lock{writing_mutex};
std::lock_guard<std::mutex> lock(writing_mutex);
for (const auto& backend : backends) {
backend->Write(e);
}

View File

@@ -1,337 +0,0 @@
// Copyright 2019 TuxSH
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <iterator>
#include <list>
#include <utility>
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Common {
/**
* A MultiLevelQueue is a type of priority queue which has the following characteristics:
* - iteratable through each of its elements.
* - back can be obtained.
* - O(1) add, lookup (both front and back)
* - discrete priorities and a max of 64 priorities (limited domain)
* This type of priority queue is normaly used for managing threads within an scheduler
*/
template <typename T, std::size_t Depth>
class MultiLevelQueue {
public:
using value_type = T;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using difference_type = typename std::pointer_traits<pointer>::difference_type;
using size_type = std::size_t;
template <bool is_constant>
class iterator_impl {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using pointer = std::conditional_t<is_constant, T*, const T*>;
using reference = std::conditional_t<is_constant, const T&, T&>;
using difference_type = typename std::pointer_traits<pointer>::difference_type;
friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
if (lhs.IsEnd() && rhs.IsEnd())
return true;
return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it);
}
friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
return !operator==(lhs, rhs);
}
reference operator*() const {
return *it;
}
pointer operator->() const {
return it.operator->();
}
iterator_impl& operator++() {
if (IsEnd()) {
return *this;
}
++it;
if (it == GetEndItForPrio()) {
u64 prios = mlq.used_priorities;
prios &= ~((1ULL << (current_priority + 1)) - 1);
if (prios == 0) {
current_priority = mlq.depth();
} else {
current_priority = CountTrailingZeroes64(prios);
it = GetBeginItForPrio();
}
}
return *this;
}
iterator_impl& operator--() {
if (IsEnd()) {
if (mlq.used_priorities != 0) {
current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
it = GetEndItForPrio();
--it;
}
} else if (it == GetBeginItForPrio()) {
u64 prios = mlq.used_priorities;
prios &= (1ULL << current_priority) - 1;
if (prios != 0) {
current_priority = CountTrailingZeroes64(prios);
it = GetEndItForPrio();
--it;
}
} else {
--it;
}
return *this;
}
iterator_impl operator++(int) {
const iterator_impl v{*this};
++(*this);
return v;
}
iterator_impl operator--(int) {
const iterator_impl v{*this};
--(*this);
return v;
}
// allow implicit const->non-const
iterator_impl(const iterator_impl<false>& other)
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
iterator_impl(const iterator_impl<true>& other)
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
iterator_impl& operator=(const iterator_impl<false>& other) {
mlq = other.mlq;
it = other.it;
current_priority = other.current_priority;
return *this;
}
friend class iterator_impl<true>;
iterator_impl() = default;
private:
friend class MultiLevelQueue;
using container_ref =
std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
typename std::list<T>::iterator>;
explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
: mlq(mlq), it(it), current_priority(current_priority) {}
explicit iterator_impl(container_ref mlq, u32 current_priority)
: mlq(mlq), it(), current_priority(current_priority) {}
bool IsEnd() const {
return current_priority == mlq.depth();
}
list_iterator GetBeginItForPrio() const {
return mlq.levels[current_priority].begin();
}
list_iterator GetEndItForPrio() const {
return mlq.levels[current_priority].end();
}
container_ref mlq;
list_iterator it;
u32 current_priority;
};
using iterator = iterator_impl<false>;
using const_iterator = iterator_impl<true>;
void add(const T& element, u32 priority, bool send_back = true) {
if (send_back)
levels[priority].push_back(element);
else
levels[priority].push_front(element);
used_priorities |= 1ULL << priority;
}
void remove(const T& element, u32 priority) {
auto it = ListIterateTo(levels[priority], element);
if (it == levels[priority].end())
return;
levels[priority].erase(it);
if (levels[priority].empty()) {
used_priorities &= ~(1ULL << priority);
}
}
void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
remove(element, old_priority);
add(element, new_priority, !adjust_front);
}
void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
adjust(*it, old_priority, new_priority, adjust_front);
}
void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
ListIterateTo(levels[priority], element));
other.used_priorities |= 1ULL << priority;
if (levels[priority].empty()) {
used_priorities &= ~(1ULL << priority);
}
}
void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
transfer_to_front(*it, priority, other);
}
void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
ListIterateTo(levels[priority], element));
other.used_priorities |= 1ULL << priority;
if (levels[priority].empty()) {
used_priorities &= ~(1ULL << priority);
}
}
void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
transfer_to_back(*it, priority, other);
}
void yield(u32 priority, std::size_t n = 1) {
ListShiftForward(levels[priority], n);
}
std::size_t depth() const {
return Depth;
}
std::size_t size(u32 priority) const {
return levels[priority].size();
}
std::size_t size() const {
u64 priorities = used_priorities;
std::size_t size = 0;
while (priorities != 0) {
const u64 current_priority = CountTrailingZeroes64(priorities);
size += levels[current_priority].size();
priorities &= ~(1ULL << current_priority);
}
return size;
}
bool empty() const {
return used_priorities == 0;
}
bool empty(u32 priority) const {
return (used_priorities & (1ULL << priority)) == 0;
}
u32 highest_priority_set(u32 max_priority = 0) const {
const u64 priorities =
max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
}
u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
const u64 priorities = min_priority >= Depth - 1
? used_priorities
: (used_priorities & ((1ULL << (min_priority + 1)) - 1));
return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
}
const_iterator cbegin(u32 max_prio = 0) const {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? cend()
: const_iterator{*this, levels[priority].cbegin(), priority};
}
const_iterator begin(u32 max_prio = 0) const {
return cbegin(max_prio);
}
iterator begin(u32 max_prio = 0) {
const u32 priority = highest_priority_set(max_prio);
return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
}
const_iterator cend(u32 min_prio = Depth - 1) const {
return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
}
const_iterator end(u32 min_prio = Depth - 1) const {
return cend(min_prio);
}
iterator end(u32 min_prio = Depth - 1) {
return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
}
T& front(u32 max_priority = 0) {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
const T& front(u32 max_priority = 0) const {
const u32 priority = highest_priority_set(max_priority);
return levels[priority == Depth ? 0 : priority].front();
}
T back(u32 min_priority = Depth - 1) {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
const T& back(u32 min_priority = Depth - 1) const {
const u32 priority = lowest_priority_set(min_priority); // intended
return levels[priority == Depth ? 63 : priority].back();
}
private:
using const_list_iterator = typename std::list<T>::const_iterator;
static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
if (shift >= list.size()) {
return;
}
const auto begin_range = list.begin();
const auto end_range = std::next(begin_range, shift);
list.splice(list.end(), list, begin_range, end_range);
}
static void ListSplice(std::list<T>& in_list, const_list_iterator position,
std::list<T>& out_list, const_list_iterator element) {
in_list.splice(position, out_list, element);
}
static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
auto it = list.cbegin();
while (it != list.cend() && *it != element) {
++it;
}
return it;
}
std::array<std::list<T>, Depth> levels;
u64 used_priorities = 0;
};
} // namespace Common

View File

@@ -1,31 +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);
backing_addr.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();
backing_addr.shrink_to_fit();
}
} // namespace Common

View File

@@ -1,84 +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,
/// Page is allocated for use.
Allocated,
};
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<u64, 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;
std::vector<u64> backing_addr;
const std::size_t page_size_in_bits{};
};
} // namespace Common

View File

@@ -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;

View File

@@ -27,6 +27,18 @@ namespace Common {
#ifdef _MSC_VER
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
SetThreadAffinityMask(thread, mask);
}
void SetCurrentThreadAffinity(u32 mask) {
SetThreadAffinityMask(GetCurrentThread(), mask);
}
void SwitchCurrentThread() {
SwitchToThread();
}
// Sets the debugger-visible name of the current thread.
// Uses undocumented (actually, it is now documented) trick.
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
@@ -58,6 +70,31 @@ void SetCurrentThreadName(const char* name) {
#else // !MSVC_VER, so must be POSIX threads
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
#ifdef __APPLE__
thread_policy_set(pthread_mach_thread_np(thread), THREAD_AFFINITY_POLICY, (integer_t*)&mask, 1);
#elif (defined __linux__ || defined __FreeBSD__) && !(defined ANDROID)
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
for (int i = 0; i != sizeof(mask) * 8; ++i)
if ((mask >> i) & 1)
CPU_SET(i, &cpu_set);
pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set);
#endif
}
void SetCurrentThreadAffinity(u32 mask) {
SetThreadAffinity(pthread_self(), mask);
}
#ifndef _WIN32
void SwitchCurrentThread() {
usleep(1000 * 1);
}
#endif
// MinGW with the POSIX threading model does not support pthread_setname_np
#if !defined(_WIN32) || defined(_MSC_VER)
void SetCurrentThreadName(const char* name) {

View File

@@ -9,13 +9,14 @@
#include <cstddef>
#include <mutex>
#include <thread>
#include "common/common_types.h"
namespace Common {
class Event {
public:
void Set() {
std::lock_guard lk{mutex};
std::lock_guard<std::mutex> lk(mutex);
if (!is_set) {
is_set = true;
condvar.notify_one();
@@ -23,14 +24,14 @@ public:
}
void Wait() {
std::unique_lock lk{mutex};
std::unique_lock<std::mutex> lk(mutex);
condvar.wait(lk, [&] { return is_set; });
is_set = false;
}
template <class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
std::unique_lock lk{mutex};
std::unique_lock<std::mutex> lk(mutex);
if (!condvar.wait_until(lk, time, [this] { return is_set; }))
return false;
is_set = false;
@@ -38,7 +39,7 @@ public:
}
void Reset() {
std::unique_lock lk{mutex};
std::unique_lock<std::mutex> lk(mutex);
// no other action required, since wait loops on the predicate and any lingering signal will
// get cleared on the first iteration
is_set = false;
@@ -56,7 +57,7 @@ public:
/// Blocks until all "count" threads have called Sync()
void Sync() {
std::unique_lock lk{mutex};
std::unique_lock<std::mutex> lk(mutex);
const std::size_t current_generation = generation;
if (++waiting == count) {
@@ -77,6 +78,9 @@ private:
std::size_t generation = 0; // Incremented once each time the barrier is used
};
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
void SetCurrentThreadAffinity(u32 mask);
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
void SetCurrentThreadName(const char* name);
} // namespace Common

View File

@@ -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) {

View File

@@ -78,7 +78,7 @@ public:
T PopWait() {
if (Empty()) {
std::unique_lock lock{cv_mutex};
std::unique_lock<std::mutex> lock(cv_mutex);
cv.wait(lock, [this]() { return !Empty(); });
}
T t;
@@ -137,7 +137,7 @@ public:
template <typename Arg>
void Push(Arg&& t) {
std::lock_guard lock{write_lock};
std::lock_guard<std::mutex> lock(write_lock);
spsc_queue.Push(t);
}

View File

@@ -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

View File

@@ -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

View File

@@ -31,8 +31,6 @@ add_library(core STATIC
file_sys/bis_factory.h
file_sys/card_image.cpp
file_sys/card_image.h
file_sys/cheat_engine.cpp
file_sys/cheat_engine.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -70,8 +68,6 @@ add_library(core STATIC
file_sys/system_archive/ng_word.h
file_sys/system_archive/system_archive.cpp
file_sys/system_archive/system_archive.h
file_sys/system_archive/system_version.cpp
file_sys/system_archive/system_version.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_concat.cpp
@@ -111,8 +107,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
@@ -146,8 +140,6 @@ add_library(core STATIC
hle/kernel/svc_wrap.h
hle/kernel/thread.cpp
hle/kernel/thread.h
hle/kernel/transfer_memory.cpp
hle/kernel/transfer_memory.h
hle/kernel/vm_manager.cpp
hle/kernel/vm_manager.h
hle/kernel/wait_object.cpp
@@ -427,6 +419,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
@@ -443,6 +437,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

View File

@@ -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;

View File

@@ -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 {

View File

@@ -192,12 +192,13 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
core_timing.AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
if (last_bkpt_hit) {
if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->GetContext());
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);

View File

@@ -50,7 +50,7 @@ private:
uc_engine* uc{};
Timing::CoreTiming& core_timing;
GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit;
bool last_bkpt_hit = false;
};
} // namespace Core

View File

@@ -32,7 +32,6 @@
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "file_sys/cheat_engine.h"
#include "frontend/applets/profile_select.h"
#include "frontend/applets/software_keyboard.h"
#include "frontend/applets/web_browser.h"
@@ -206,7 +205,6 @@ struct System::Impl {
GDBStub::Shutdown();
Service::Shutdown();
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
gpu_core.reset();
@@ -257,8 +255,6 @@ struct System::Impl {
CpuCoreManager cpu_core_manager;
bool is_powered_on = false;
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
/// Frontend applets
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
@@ -457,13 +453,6 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
const std::string& build_id, VAddr code_region_start,
VAddr code_region_end) {
impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
code_region_start, code_region_end);
}
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}

View File

@@ -20,7 +20,6 @@ class WebBrowserApplet;
} // namespace Core::Frontend
namespace FileSys {
class CheatList;
class VfsFilesystem;
} // namespace FileSys
@@ -254,9 +253,6 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
const Frontend::ProfileSelectApplet& GetProfileSelector() const;

View File

@@ -22,7 +22,7 @@
namespace Core {
void CpuBarrier::NotifyEnd() {
std::unique_lock lock{mutex};
std::unique_lock<std::mutex> lock(mutex);
end = true;
condition.notify_all();
}
@@ -34,7 +34,7 @@ bool CpuBarrier::Rendezvous() {
}
if (!end) {
std::unique_lock lock{mutex};
std::unique_lock<std::mutex> lock(mutex);
--cores_waiting;
if (!cores_waiting) {
@@ -131,7 +131,7 @@ void Cpu::Reschedule() {
reschedule_pending = false;
// Lock the global kernel mutex when we manipulate the HLE state
std::lock_guard lock{HLE::g_hle_lock};
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
scheduler->Reschedule();
}

View File

@@ -186,7 +186,7 @@ void CoreTiming::Advance() {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
evt.type->callback(evt.userdata, global_timer - evt.time);
evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
}
is_global_timer_sane = false;

View File

@@ -15,7 +15,7 @@
namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event.
using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>;
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
/// Contains the characteristics of a particular event.
struct EventType {

View File

@@ -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

View File

@@ -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

View File

@@ -1,492 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <locale>
#include "common/hex_util.h"
#include "common/microprofile.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/file_sys/cheat_engine.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
namespace FileSys {
constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
u64 Cheat::Address() const {
u64 out;
std::memcpy(&out, raw.data(), sizeof(u64));
return Common::swap64(out) & 0xFFFFFFFFFF;
}
u64 Cheat::ValueWidth(u64 offset) const {
return Value(offset, width);
}
u64 Cheat::Value(u64 offset, u64 width) const {
u64 out;
std::memcpy(&out, raw.data() + offset, sizeof(u64));
out = Common::swap64(out);
if (width == 8)
return out;
return out & ((1ull << (width * CHAR_BIT)) - 1);
}
u32 Cheat::KeypadValue() const {
u32 out;
std::memcpy(&out, raw.data(), sizeof(u32));
return Common::swap32(out) & 0x0FFFFFFF;
}
void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
this->main_region_begin = main_begin;
this->main_region_end = main_end;
this->heap_region_begin = heap_begin;
this->heap_region_end = heap_end;
this->writer = writer;
this->reader = reader;
}
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
void CheatList::Execute() {
MICROPROFILE_SCOPE(Cheat_Engine);
std::fill(scratch.begin(), scratch.end(), 0);
in_standard = false;
for (std::size_t i = 0; i < master_list.size(); ++i) {
LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
current_block = i;
ExecuteBlock(master_list[i].second);
}
in_standard = true;
for (std::size_t i = 0; i < standard_list.size(); ++i) {
LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
current_block = i;
ExecuteBlock(standard_list[i].second);
}
}
CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
: master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
bool CheatList::EvaluateConditional(const Cheat& cheat) const {
using ComparisonFunction = bool (*)(u64, u64);
constexpr std::array<ComparisonFunction, 6> comparison_functions{
[](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
[](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
[](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
};
if (cheat.type == CodeType::ConditionalInput) {
const auto applet_resource =
system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
if (applet_resource == nullptr) {
LOG_WARNING(
Common_Filesystem,
"Attempted to evaluate input conditional, but applet resource is not initialized!");
return false;
}
const auto press_state =
applet_resource
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
.GetAndResetPressState();
return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
}
ASSERT(cheat.type == CodeType::Conditional);
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
const auto addr = cheat.Address() + offset;
return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
}
void CheatList::ProcessBlockPairs(const Block& block) {
block_pairs.clear();
u64 scope = 0;
std::map<u64, u64> pairs;
for (std::size_t i = 0; i < block.size(); ++i) {
const auto& cheat = block[i];
switch (cheat.type) {
case CodeType::Conditional:
case CodeType::ConditionalInput:
pairs.insert_or_assign(scope, i);
++scope;
break;
case CodeType::EndConditional: {
--scope;
const auto idx = pairs.at(scope);
block_pairs.insert_or_assign(idx, i);
break;
}
case CodeType::Loop: {
if (cheat.end_of_loop) {
--scope;
const auto idx = pairs.at(scope);
block_pairs.insert_or_assign(idx, i);
} else {
pairs.insert_or_assign(scope, i);
++scope;
}
break;
}
}
}
}
void CheatList::WriteImmediate(const Cheat& cheat) {
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
const auto& register_3 = scratch.at(cheat.register_3);
const auto addr = cheat.Address() + offset + register_3;
LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
cheat.Value(8, cheat.width));
writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
}
void CheatList::BeginConditional(const Cheat& cheat) {
if (EvaluateConditional(cheat)) {
return;
}
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
current_index = iter->second - 1;
}
void CheatList::EndConditional(const Cheat& cheat) {
LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
}
void CheatList::Loop(const Cheat& cheat) {
if (cheat.end_of_loop.Value())
ASSERT(!cheat.end_of_loop.Value());
auto& register_3 = scratch.at(cheat.register_3);
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
ASSERT(iter->first < iter->second);
const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
for (s32 i = initial_value; i >= 0; --i) {
register_3 = static_cast<u64>(i);
for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
current_index = c;
ExecuteSingleCheat(
(in_standard ? standard_list : master_list)[current_block].second[c]);
}
}
current_index = iter->second;
}
void CheatList::LoadImmediate(const Cheat& cheat) {
auto& register_3 = scratch.at(cheat.register_3);
LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
cheat.Value(4, 8));
register_3 = cheat.Value(4, 8);
}
void CheatList::LoadIndexed(const Cheat& cheat) {
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
auto& register_3 = scratch.at(cheat.register_3);
const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
cheat.register_3, addr);
register_3 = reader(cheat.width, SanitizeAddress(addr));
}
void CheatList::StoreIndexed(const Cheat& cheat) {
const auto& register_3 = scratch.at(cheat.register_3);
const auto addr =
register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
cheat.Value(4, cheat.width), addr);
writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
}
void CheatList::RegisterArithmetic(const Cheat& cheat) {
using ArithmeticFunction = u64 (*)(u64, u64);
constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
[](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
[](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
[](u64 a, u64 b) { return a >> b; },
};
using ArithmeticOverflowCheck = bool (*)(u64, u64);
constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
[](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
[](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
};
static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
"Missing or have extra arithmetic overflow checks compared to functions!");
auto& register_3 = scratch.at(cheat.register_3);
ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
auto* overflow_function =
arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
cheat.register_3, cheat.ValueWidth(4));
if (overflow_function(register_3, cheat.ValueWidth(4))) {
LOG_WARNING(Common_Filesystem,
"overflow will occur when performing arithmetic operation={:02X} with operands "
"a={:016X}, b={:016X}!",
static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
}
register_3 = function(register_3, cheat.ValueWidth(4));
}
void CheatList::BeginConditionalInput(const Cheat& cheat) {
if (EvaluateConditional(cheat))
return;
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
current_index = iter->second - 1;
}
VAddr CheatList::SanitizeAddress(VAddr in) const {
if ((in < main_region_begin || in >= main_region_end) &&
(in < heap_region_begin || in >= heap_region_end)) {
LOG_ERROR(Common_Filesystem,
"Cheat attempting to access memory at invalid address={:016X}, if this persists, "
"the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.",
in);
return 0; ///< Invalid addresses will hard crash
}
return in;
}
void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
using CheatOperationFunction = void (CheatList::*)(const Cheat&);
constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
&CheatList::WriteImmediate, &CheatList::BeginConditional,
&CheatList::EndConditional, &CheatList::Loop,
&CheatList::LoadImmediate, &CheatList::LoadIndexed,
&CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
&CheatList::BeginConditionalInput,
};
const auto index = static_cast<u8>(cheat.type.Value());
ASSERT(index < sizeof(cheat_operation_functions));
const auto op = cheat_operation_functions[index];
(this->*op)(cheat);
}
void CheatList::ExecuteBlock(const Block& block) {
encountered_loops.clear();
ProcessBlockPairs(block);
for (std::size_t i = 0; i < block.size(); ++i) {
current_index = i;
ExecuteSingleCheat(block[i]);
i = current_index;
}
}
CheatParser::~CheatParser() = default;
CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
CheatList::ProgramSegment standard) const {
return {system, std::move(master), std::move(standard)};
}
TextCheatParser::~TextCheatParser() = default;
CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
std::stringstream ss;
ss.write(reinterpret_cast<const char*>(data.data()), data.size());
std::vector<std::string> lines;
std::string stream_line;
while (std::getline(ss, stream_line)) {
// Remove a trailing \r
if (!stream_line.empty() && stream_line.back() == '\r')
stream_line.pop_back();
lines.push_back(std::move(stream_line));
}
CheatList::ProgramSegment master_list;
CheatList::ProgramSegment standard_list;
for (std::size_t i = 0; i < lines.size(); ++i) {
auto line = lines[i];
if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
const auto master = line[0] == '{';
const auto begin = master ? line.find('{') : line.find('[');
const auto end = master ? line.rfind('}') : line.rfind(']');
ASSERT(begin != std::string::npos && end != std::string::npos);
const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
CheatList::Block block{};
while (i < lines.size() - 1) {
line = lines[++i];
if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
--i;
break;
}
if (line.size() < 8)
continue;
Cheat out{};
out.raw = ParseSingleLineCheat(line);
block.push_back(out);
}
(master ? master_list : standard_list).emplace_back(patch_name, block);
}
}
return MakeCheatList(system, master_list, standard_list);
}
std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
std::array<u8, 16> out{};
if (line.size() < 8)
return out;
const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
std::memcpy(out.data(), word1.data(), sizeof(u32));
if (line.size() < 17 || line[8] != ' ')
return out;
const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
if (line.size() < 26 || line[17] != ' ') {
// Perform shifting in case value is truncated early.
const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
std::memset(out.data() + 4, 0, sizeof(u32));
}
return out;
}
const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
if (line.size() < 35 || line[26] != ' ') {
// Perform shifting in case value is truncated early.
const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
std::memset(out.data() + 8, 0, sizeof(u32));
}
return out;
}
const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
return out;
}
namespace {
u64 MemoryReadImpl(u32 width, VAddr addr) {
switch (width) {
case 1:
return Memory::Read8(addr);
case 2:
return Memory::Read16(addr);
case 4:
return Memory::Read32(addr);
case 8:
return Memory::Read64(addr);
default:
UNREACHABLE();
return 0;
}
}
void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
switch (width) {
case 1:
Memory::Write8(addr, static_cast<u8>(value));
break;
case 2:
Memory::Write16(addr, static_cast<u16>(value));
break;
case 4:
Memory::Write32(addr, static_cast<u32>(value));
break;
case 8:
Memory::Write64(addr, value);
break;
default:
UNREACHABLE();
}
}
} // Anonymous namespace
CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
const std::string& build_id, VAddr code_region_start,
VAddr code_region_end)
: cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
event = core_timing.RegisterEvent(
"CheatEngine::FrameCallback::" + build_id,
[this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
const auto& vm_manager = system.CurrentProcess()->VMManager();
for (auto& list : this->cheats) {
list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
code_region_end, vm_manager.GetHeapRegionEndAddress(),
&MemoryWriteImpl, &MemoryReadImpl);
}
}
CheatEngine::~CheatEngine() {
core_timing.UnscheduleEvent(event, 0);
}
void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
for (auto& list : cheats) {
list.Execute();
}
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
}
} // namespace FileSys

View File

@@ -1,234 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <set>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Core {
class System;
}
namespace Core::Timing {
class CoreTiming;
struct EventType;
} // namespace Core::Timing
namespace FileSys {
enum class CodeType : u32 {
// 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
// Writes a T sized value Y to the address A added to the value of register R in memory domain M
WriteImmediate = 0,
// 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
// Compares the T sized value Y to the value at address A in memory domain M using the
// conditional function C. If success, continues execution. If failure, jumps to the matching
// EndConditional statement.
Conditional = 1,
// 20000000
// Terminates a Conditional or ConditionalInput block.
EndConditional = 2,
// 300R0000 VVVVVVVV
// Starts looping V times, storing the current count in register R.
// Loop block is terminated with a matching 310R0000.
Loop = 3,
// 400R0000 VVVVVVVV VVVVVVVV
// Sets the value of register R to the value V.
LoadImmediate = 4,
// 5TMRI0AA AAAAAAAA
// Sets the value of register R to the value of width T at address A in memory domain M, with
// the current value of R added to the address if I == 1.
LoadIndexed = 5,
// 6T0RIFG0 VVVVVVVV VVVVVVVV
// Writes the value V of width T to the memory address stored in register R. Adds the value of
// register G to the final calculation if F is nonzero. Increments the value of register R by T
// after operation if I is nonzero.
StoreIndexed = 6,
// 7T0RA000 VVVVVVVV
// Performs the arithmetic operation A on the value in register R and the value V of width T,
// storing the result in register R.
RegisterArithmetic = 7,
// 8KKKKKKK
// Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
// execution continues. If none are, execution skips to the next EndConditional command.
ConditionalInput = 8,
};
enum class MemoryType : u32 {
// Addressed relative to start of main NSO
MainNSO = 0,
// Addressed relative to start of heap
Heap = 1,
};
enum class ArithmeticOp : u32 {
Add = 0,
Sub = 1,
Mult = 2,
LShift = 3,
RShift = 4,
};
enum class ComparisonOp : u32 {
GreaterThan = 1,
GreaterThanEqual = 2,
LessThan = 3,
LessThanEqual = 4,
Equal = 5,
Inequal = 6,
};
union Cheat {
std::array<u8, 16> raw;
BitField<4, 4, CodeType> type;
BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
BitField<0, 4, u32> end_of_loop;
BitField<12, 4, MemoryType> memory_type;
BitField<8, 4, u32> register_3;
BitField<8, 4, ComparisonOp> comparison_op;
BitField<20, 4, u32> load_from_register;
BitField<20, 4, u32> increment_register;
BitField<20, 4, ArithmeticOp> arithmetic_op;
BitField<16, 4, u32> add_additional_register;
BitField<28, 4, u32> register_6;
u64 Address() const;
u64 ValueWidth(u64 offset) const;
u64 Value(u64 offset, u64 width) const;
u32 KeypadValue() const;
};
class CheatParser;
// Represents a full collection of cheats for a game. The Execute function should be called every
// interval that all cheats should be executed. Clients should not directly instantiate this class
// (hence private constructor), they should instead receive an instance from CheatParser, which
// guarantees the list is always in an acceptable state.
class CheatList {
public:
friend class CheatParser;
using Block = std::vector<Cheat>;
using ProgramSegment = std::vector<std::pair<std::string, Block>>;
// (width in bytes, address, value)
using MemoryWriter = void (*)(u32, VAddr, u64);
// (width in bytes, address) -> value
using MemoryReader = u64 (*)(u32, VAddr);
void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
MemoryWriter writer, MemoryReader reader);
void Execute();
private:
CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard);
void ProcessBlockPairs(const Block& block);
void ExecuteSingleCheat(const Cheat& cheat);
void ExecuteBlock(const Block& block);
bool EvaluateConditional(const Cheat& cheat) const;
// Individual cheat operations
void WriteImmediate(const Cheat& cheat);
void BeginConditional(const Cheat& cheat);
void EndConditional(const Cheat& cheat);
void Loop(const Cheat& cheat);
void LoadImmediate(const Cheat& cheat);
void LoadIndexed(const Cheat& cheat);
void StoreIndexed(const Cheat& cheat);
void RegisterArithmetic(const Cheat& cheat);
void BeginConditionalInput(const Cheat& cheat);
VAddr SanitizeAddress(VAddr in) const;
// Master Codes are defined as codes that cannot be disabled and are run prior to all
// others.
ProgramSegment master_list;
// All other codes
ProgramSegment standard_list;
bool in_standard = false;
// 16 (0x0-0xF) scratch registers that can be used by cheats
std::array<u64, 16> scratch{};
MemoryWriter writer = nullptr;
MemoryReader reader = nullptr;
u64 main_region_begin{};
u64 heap_region_begin{};
u64 main_region_end{};
u64 heap_region_end{};
u64 current_block{};
// The current index of the cheat within the current Block
u64 current_index{};
// The 'stack' of the program. When a conditional or loop statement is encountered, its index is
// pushed onto this queue. When a end block is encountered, the condition is checked.
std::map<u64, u64> block_pairs;
std::set<u64> encountered_loops;
const Core::System* system;
};
// Intermediary class that parses a text file or other disk format for storing cheats into a
// CheatList object, that can be used for execution.
class CheatParser {
public:
virtual ~CheatParser();
virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0;
protected:
CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master,
CheatList::ProgramSegment standard) const;
};
// CheatParser implementation that parses text files
class TextCheatParser final : public CheatParser {
public:
~TextCheatParser() override;
CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override;
private:
std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
};
// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
class CheatEngine final {
public:
CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
~CheatEngine();
private:
void FrameCallback(u64 userdata, s64 cycles_late);
std::vector<CheatList> cheats;
Core::Timing::EventType* event;
Core::Timing::CoreTiming& core_timing;
};
} // namespace FileSys

View File

@@ -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 {

View File

@@ -11,9 +11,6 @@ namespace FileSys {
constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
constexpr ResultCode ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
constexpr ResultCode ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};

View File

@@ -7,7 +7,6 @@
#include <cstddef>
#include <cstring>
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/file_sys/content_archive.h"
@@ -20,7 +19,6 @@
#include "core/file_sys/vfs_vector.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/loader/nso.h"
#include "core/settings.h"
namespace FileSys {
@@ -33,6 +31,14 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
};
struct NSOBuildHeader {
u32_le magic;
INSERT_PADDING_BYTES(0x3C);
std::array<u8, 0x20> build_id;
INSERT_PADDING_BYTES(0xA0);
};
static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
std::array<u8, sizeof(u32)> bytes{};
bytes[0] = version % SINGLE_BYTE_MODULUS;
@@ -156,16 +162,14 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
}
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
if (nso.size() < sizeof(Loader::NSOHeader)) {
if (nso.size() < 0x100)
return nso;
}
Loader::NSOHeader header;
std::memcpy(&header, nso.data(), sizeof(header));
NSOBuildHeader header;
std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader));
if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
if (header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return nso;
}
const auto build_id_raw = Common::HexArrayToString(header.build_id);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
@@ -208,11 +212,9 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
}
}
if (out.size() < sizeof(Loader::NSOHeader)) {
if (out.size() < 0x100)
return nso;
}
std::memcpy(out.data(), &header, sizeof(header));
std::memcpy(out.data(), &header, sizeof(NSOBuildHeader));
return out;
}
@@ -230,57 +232,6 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
return !CollectPatches(patch_dirs, build_id).empty();
}
static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
const std::array<u8, 0x20>& build_id_,
const VirtualDir& base_path, bool upper) {
const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
if (file == nullptr) {
LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
title_id, build_id);
return std::nullopt;
}
std::vector<u8> data(file->GetSize());
if (file->Read(data.data(), data.size()) != data.size()) {
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
title_id, build_id);
return std::nullopt;
}
TextCheatParser parser;
return parser.Parse(system, data);
}
std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
const std::array<u8, 32>& build_id_) const {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
std::vector<CheatList> out;
out.reserve(patch_dirs.size());
for (const auto& subdir : patch_dirs) {
auto cheats_dir = subdir->GetSubdirectory("cheats");
if (cheats_dir != nullptr) {
auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
if (res.has_value()) {
out.push_back(std::move(*res));
continue;
}
res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
if (res.has_value())
out.push_back(std::move(*res));
}
}
return out;
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
@@ -452,8 +403,6 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
AppendCommaIfNotEmpty(types, "LayeredFS");
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats")))
AppendCommaIfNotEmpty(types, "Cheats");
if (types.empty())
continue;

View File

@@ -8,14 +8,9 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/cheat_engine.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
namespace Core {
class System;
}
namespace FileSys {
class NCA;
@@ -50,10 +45,6 @@ public:
// Used to prevent expensive copies in NSO loader.
bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
// Creates a CheatList object with all
std::vector<CheatList> CreateCheatList(const Core::System& system,
const std::array<u8, 0x20>& build_id) const;
// Currently tracked RomFS patches:
// - Game Updates
// - LayeredFS

View File

@@ -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.

View File

@@ -6,7 +6,6 @@
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/ng_word.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/system_archive/system_version.h"
namespace FileSys::SystemArchive {
@@ -31,7 +30,7 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
{0x0100000000000806, "NgWord", &NgWord1},
{0x0100000000000807, "SsidList", nullptr},
{0x0100000000000808, "Dictionary", nullptr},
{0x0100000000000809, "SystemVersion", &SystemVersion},
{0x0100000000000809, "SystemVersion", nullptr},
{0x010000000000080A, "AvatarImage", nullptr},
{0x010000000000080B, "LocalNews", nullptr},
{0x010000000000080C, "Eula", nullptr},

View File

@@ -1,52 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/file_sys/system_archive/system_version.h"
#include "core/file_sys/vfs_vector.h"
namespace FileSys::SystemArchive {
namespace SystemVersionData {
// This section should reflect the best system version to describe yuzu's HLE api.
// TODO(DarkLordZach): Update when HLE gets better.
constexpr u8 VERSION_MAJOR = 5;
constexpr u8 VERSION_MINOR = 1;
constexpr u8 VERSION_MICRO = 0;
constexpr u8 REVISION_MAJOR = 3;
constexpr u8 REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
constexpr char VERSION_HASH[] = "23f9df53e25709d756e0c76effcb2473bd3447dd";
constexpr char DISPLAY_VERSION[] = "5.1.0";
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 5.1.0-3.0";
} // namespace SystemVersionData
std::string GetLongDisplayVersion() {
return SystemVersionData::DISPLAY_TITLE;
}
VirtualDir SystemVersion() {
VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
file->WriteObject(SystemVersionData::VERSION_MAJOR, 0);
file->WriteObject(SystemVersionData::VERSION_MINOR, 1);
file->WriteObject(SystemVersionData::VERSION_MICRO, 2);
file->WriteObject(SystemVersionData::REVISION_MAJOR, 4);
file->WriteObject(SystemVersionData::REVISION_MINOR, 5);
file->WriteArray(SystemVersionData::PLATFORM_STRING,
std::min<u64>(sizeof(SystemVersionData::PLATFORM_STRING), 0x20ULL), 0x8);
file->WriteArray(SystemVersionData::VERSION_HASH,
std::min<u64>(sizeof(SystemVersionData::VERSION_HASH), 0x40ULL), 0x28);
file->WriteArray(SystemVersionData::DISPLAY_VERSION,
std::min<u64>(sizeof(SystemVersionData::DISPLAY_VERSION), 0x18ULL), 0x68);
file->WriteArray(SystemVersionData::DISPLAY_TITLE,
std::min<u64>(sizeof(SystemVersionData::DISPLAY_TITLE), 0x80ULL), 0x80);
return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file},
std::vector<VirtualDir>{}, "data");
}
} // namespace FileSys::SystemArchive

View File

@@ -1,16 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "core/file_sys/vfs_types.h"
namespace FileSys::SystemArchive {
std::string GetLongDisplayVersion();
VirtualDir SystemVersion();
} // namespace FileSys::SystemArchive

View File

@@ -30,7 +30,7 @@ private:
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
std::tuple<float, float, bool> GetStatus() const override {
if (auto state = touch_state.lock()) {
std::lock_guard guard{state->mutex};
std::lock_guard<std::mutex> guard(state->mutex);
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
}
return std::make_tuple(0.0f, 0.0f, false);
@@ -81,7 +81,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
std::lock_guard guard{touch_state->mutex};
std::lock_guard<std::mutex> guard(touch_state->mutex);
touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
@@ -91,7 +91,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
}
void EmuWindow::TouchReleased() {
std::lock_guard guard{touch_state->mutex};
std::lock_guard<std::mutex> guard(touch_state->mutex);
touch_state->touch_pressed = false;
touch_state->touch_x = 0;
touch_state->touch_y = 0;

View File

@@ -1030,7 +1030,7 @@ static void Step() {
/// Tell the CPU if we hit a memory breakpoint.
bool IsMemoryBreak() {
if (IsConnected()) {
if (!IsConnected()) {
return false;
}

View File

@@ -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);

View File

@@ -274,20 +274,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));
@@ -429,22 +415,6 @@ 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;

View File

@@ -26,7 +26,7 @@ void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_
// them all.
std::size_t last = waiting_threads.size();
if (num_to_wake > 0) {
last = std::min(last, static_cast<std::size_t>(num_to_wake));
last = num_to_wake;
}
// Signal the waiting threads.
@@ -90,9 +90,9 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
// 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 if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;
} else {
updated_value = value;
}

View File

@@ -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

View File

@@ -1,89 +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 <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::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

View File

@@ -29,12 +29,12 @@ namespace Kernel {
* @param thread_handle The handle of the thread that's been awoken
* @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
*/
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
const auto& system = Core::System::GetInstance();
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard lock{HLE::g_hle_lock};
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
SharedPtr<Thread> thread =
system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
@@ -62,8 +62,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 ||
thread->GetWaitHandle() != 0) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex ||
thread->GetStatus() == ThreadStatus::WaitCondVar);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
@@ -115,7 +114,7 @@ struct KernelCore::Impl {
// Creates the default system resource limit
void InitializeSystemResourceLimit(KernelCore& kernel) {
system_resource_limit = ResourceLimit::Create(kernel);
system_resource_limit = ResourceLimit::Create(kernel, "System");
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
@@ -191,10 +190,6 @@ const Process* KernelCore::CurrentProcess() const {
return impl->current_process;
}
const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const {
return impl->process_list;
}
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
impl->named_ports.emplace(std::move(name), std::move(port));
}

View File

@@ -8,6 +8,9 @@
#include <unordered_map>
#include "core/hle/kernel/object.h"
template <typename T>
class ResultVal;
namespace Core {
class System;
}
@@ -72,9 +75,6 @@ public:
/// Retrieves a const pointer to the current process.
const Process* CurrentProcess() const;
/// Retrieves the list of processes.
const std::vector<SharedPtr<Process>>& GetProcessList() const;
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <map>
#include <utility>
#include <vector>
@@ -9,11 +10,8 @@
#include "core/core.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"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -59,47 +57,41 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t
}
}
Mutex::Mutex(Core::System& system) : system{system} {}
Mutex::~Mutex() = default;
ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ERR_INVALID_ADDRESS;
}
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
Thread* const current_thread = system.CurrentScheduler().GetCurrentThread();
SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
// thread.
ASSERT(requesting_thread == current_thread);
ASSERT(requesting_thread == GetCurrentThread());
const u32 addr_value = Memory::Read32(address);
u32 addr_value = Memory::Read32(address);
// If the mutex isn't being held, just return success.
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
return RESULT_SUCCESS;
}
if (holding_thread == nullptr) {
if (holding_thread == nullptr)
return ERR_INVALID_HANDLE;
}
// Wait until the mutex is released
current_thread->SetMutexWaitAddress(address);
current_thread->SetWaitHandle(requesting_thread_handle);
GetCurrentThread()->SetMutexWaitAddress(address);
GetCurrentThread()->SetWaitHandle(requesting_thread_handle);
current_thread->SetStatus(ThreadStatus::WaitMutex);
current_thread->InvalidateWakeupCallback();
GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex);
GetCurrentThread()->InvalidateWakeupCallback();
// Update the lock holder thread's priority to prevent priority inversion.
holding_thread->AddMutexWaiter(current_thread);
holding_thread->AddMutexWaiter(GetCurrentThread());
system.PrepareReschedule();
Core::System::GetInstance().PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -110,8 +102,7 @@ ResultCode Mutex::Release(VAddr address) {
return ERR_INVALID_ADDRESS;
}
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
// There are no more threads waiting for the mutex, release it completely.
if (thread == nullptr) {
@@ -120,7 +111,7 @@ ResultCode Mutex::Release(VAddr address) {
}
// Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, current_thread, thread);
TransferMutexOwnership(address, GetCurrentThread(), thread);
u32 mutex_value = thread->GetWaitHandle();

View File

@@ -5,34 +5,32 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Core {
class System;
}
namespace Kernel {
class HandleTable;
class Thread;
class Mutex final {
public:
explicit Mutex(Core::System& system);
~Mutex();
/// Flag that indicates that a mutex still has threads waiting for it.
static constexpr u32 MutexHasWaitersFlag = 0x40000000;
/// Mask of the bits in a mutex address value that contain the mutex owner.
static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
/// Attempts to acquire a mutex at the specified address.
ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle);
static ResultCode TryAcquire(HandleTable& handle_table, VAddr address,
Handle holding_thread_handle, Handle requesting_thread_handle);
/// Releases the mutex at the specified address.
ResultCode Release(VAddr address);
static ResultCode Release(VAddr address);
private:
Core::System& system;
Mutex() = default;
~Mutex() = default;
};
} // namespace Kernel

View File

@@ -23,7 +23,7 @@ bool Object::IsWaitable() const {
case HandleType::Unknown:
case HandleType::WritableEvent:
case HandleType::SharedMemory:
case HandleType::TransferMemory:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::ClientPort:
case HandleType::ClientSession:

View File

@@ -22,9 +22,9 @@ enum class HandleType : u32 {
WritableEvent,
ReadableEvent,
SharedMemory,
TransferMemory,
Thread,
Process,
AddressArbiter,
ResourceLimit,
ClientPort,
ServerPort,

View File

@@ -5,12 +5,10 @@
#include <algorithm>
#include <memory>
#include <random>
#include "common/alignment.h"
#include "common/assert.h"
#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"
@@ -33,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();
@@ -52,6 +50,9 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi
}
} // Anonymous namespace
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
auto& kernel = system.Kernel();
@@ -76,18 +77,6 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
return resource_limit;
}
u64 Process::GetTotalPhysicalMemoryUsed() const {
return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
}
void Process::RegisterThread(const Thread* thread) {
thread_list.push_back(thread);
}
void Process::UnregisterThread(const Thread* thread) {
thread_list.remove(thread);
}
ResultCode Process::ClearSignalState() {
if (status == ProcessStatus::Exited) {
LOG_ERROR(Kernel, "called on a terminated process instance.");
@@ -120,17 +109,14 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
return handle_table.SetSize(capabilities.GetHandleTableSize());
}
void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) {
// The kernel always ensures that the given stack size is page aligned.
main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
// Allocate and map the main thread stack
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
vm_manager
.MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
0, main_thread_stack_size, MemoryState::Stack)
.MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Stack)
.Unwrap();
vm_manager.LogLayout();
@@ -226,31 +212,26 @@ void Process::FreeTLSSlot(VAddr tls_address) {
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory));
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, memory, segment.offset,
segment.size, memory_state)
.MapMemoryBlock(segment.addr + base_addr, module_.memory,
segment.offset, segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
};
// Map CodeSet segments
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code);
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
code_memory_size += module_.memory.size();
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
// Clear instruction cache in CPU JIT
system.InvalidateCpuInstructionCaches();
}
Process::Process(Core::System& system)
: WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
: WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {}
Process::~Process() = default;
void Process::Acquire(Thread* thread) {

View File

@@ -7,14 +7,13 @@
#include <array>
#include <bitset>
#include <cstddef>
#include <list>
#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/mutex.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/wait_object.h"
@@ -34,7 +33,13 @@ class KernelCore;
class ResourceLimit;
class Thread;
struct CodeSet;
struct AddressMapping {
// Address and size must be page-aligned
VAddr address;
u64 size;
bool read_only;
bool unk_flag;
};
enum class MemoryRegion : u16 {
APPLICATION = 1,
@@ -60,6 +65,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 {
@@ -120,16 +165,6 @@ public:
return address_arbiter;
}
/// Gets a reference to the process' mutex lock.
Mutex& GetMutex() {
return mutex;
}
/// Gets a const reference to the process' mutex lock
const Mutex& GetMutex() const {
return mutex;
}
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -187,22 +222,6 @@ public:
return random_entropy.at(index);
}
/// Retrieves the total physical memory used by this process in bytes.
u64 GetTotalPhysicalMemoryUsed() const;
/// Gets the list of all threads created with this process as their owner.
const std::list<const Thread*>& GetThreadList() const {
return thread_list;
}
/// Registers a thread as being created under this process,
/// adding it to this process' thread list.
void RegisterThread(const Thread* thread);
/// Unregisters a thread from this process, removing it
/// from this process' thread list.
void UnregisterThread(const Thread* thread);
/// Clears the signaled state of the process if and only if it's signaled.
///
/// @pre The process must not be already terminated. If this is called on a
@@ -227,7 +246,7 @@ public:
/**
* Applies address space changes and launches the process main thread.
*/
void Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size);
void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
/**
* Prepares a process for termination by stopping all of its threads
@@ -264,12 +283,6 @@ private:
/// Memory manager for this process.
Kernel::VMManager vm_manager;
/// Size of the main thread's stack in bytes.
u64 main_thread_stack_size = 0;
/// Size of the loaded code memory in bytes.
u64 code_memory_size = 0;
/// Current status of the process
ProcessStatus status;
@@ -314,17 +327,9 @@ private:
/// Per-process address arbiter.
AddressArbiter address_arbiter;
/// The per-process mutex lock instance used for handling various
/// forms of services, such as lock arbitration, and condition
/// variable related facilities.
Mutex mutex;
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
/// List of threads that are running with this process as their owner.
std::list<const Thread*> thread_list;
/// System context
Core::System& system;

View File

@@ -16,8 +16,11 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
return new ResourceLimit(kernel);
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string name) {
SharedPtr<ResourceLimit> resource_limit(new ResourceLimit(kernel));
resource_limit->name = std::move(name);
return resource_limit;
}
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {

View File

@@ -31,14 +31,16 @@ constexpr bool IsValidResourceType(ResourceType type) {
class ResourceLimit final : public Object {
public:
/// Creates a resource limit object.
static SharedPtr<ResourceLimit> Create(KernelCore& kernel);
/**
* Creates a resource limit object.
*/
static SharedPtr<ResourceLimit> Create(KernelCore& kernel, std::string name = "Unknown");
std::string GetTypeName() const override {
return "ResourceLimit";
}
std::string GetName() const override {
return GetTypeName();
return name;
}
static const HandleType HANDLE_TYPE = HandleType::ResourceLimit;
@@ -93,6 +95,9 @@ private:
ResourceArray limits{};
/// Current resource limit values.
ResourceArray values{};
/// Name of resource limit object.
std::string name;
};
} // namespace Kernel

View File

@@ -29,8 +29,8 @@ Scheduler::~Scheduler() {
}
bool Scheduler::HaveReadyThreads() const {
std::lock_guard lock{scheduler_mutex};
return !ready_queue.empty();
std::lock_guard<std::mutex> lock(scheduler_mutex);
return ready_queue.get_first() != nullptr;
}
Thread* Scheduler::GetCurrentThread() const {
@@ -46,27 +46,22 @@ Thread* Scheduler::PopNextReadyThread() {
Thread* thread = GetCurrentThread();
if (thread && thread->GetStatus() == ThreadStatus::Running) {
if (ready_queue.empty()) {
return thread;
}
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.front();
if (next == nullptr || next->GetPriority() >= thread->GetPriority()) {
next = ready_queue.pop_first_better(thread->GetPriority());
if (!next) {
// Otherwise just keep going with the current thread
next = thread;
}
} else {
if (ready_queue.empty()) {
return nullptr;
}
next = ready_queue.front();
next = ready_queue.pop_first();
}
return next;
}
void Scheduler::SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread();
Thread* const previous_thread = GetCurrentThread();
Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -80,7 +75,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
if (previous_thread->GetStatus() == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.add(previous_thread, previous_thread->GetPriority(), false);
ready_queue.push_front(previous_thread->GetPriority(), previous_thread);
previous_thread->SetStatus(ThreadStatus::Ready);
}
}
@@ -95,13 +90,13 @@ void Scheduler::SwitchContext(Thread* new_thread) {
current_thread = new_thread;
ready_queue.remove(new_thread, new_thread->GetPriority());
ready_queue.remove(new_thread->GetPriority(), new_thread);
new_thread->SetStatus(ThreadStatus::Running);
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);
SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
}
cpu_core.LoadContext(new_thread->GetContext());
@@ -132,7 +127,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
}
void Scheduler::Reschedule() {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
@@ -148,54 +143,51 @@ void Scheduler::Reschedule() {
SwitchContext(next);
}
void Scheduler::AddThread(SharedPtr<Thread> thread) {
std::lock_guard lock{scheduler_mutex};
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
thread_list.push_back(std::move(thread));
ready_queue.prepare(priority);
}
void Scheduler::RemoveThread(Thread* thread) {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.add(thread, priority);
ready_queue.push_back(priority, thread);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.remove(thread, priority);
ready_queue.remove(priority, thread);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
if (thread->GetPriority() == priority) {
return;
}
std::lock_guard<std::mutex> lock(scheduler_mutex);
// If thread was ready, adjust queues
if (thread->GetStatus() == ThreadStatus::Ready)
ready_queue.adjust(thread, thread->GetPriority(), priority);
ready_queue.move(thread, thread->GetPriority(), priority);
else
ready_queue.prepare(priority);
}
Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
const u32 mask = 1U << core;
for (auto* thread : ready_queue) {
if ((thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority) {
return thread;
}
}
return nullptr;
return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) {
return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority;
});
}
void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
@@ -207,7 +199,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) {
@@ -222,7 +215,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;

View File

@@ -7,7 +7,7 @@
#include <mutex>
#include <vector>
#include "common/common_types.h"
#include "common/multi_level_queue.h"
#include "common/thread_queue_list.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
@@ -38,7 +38,7 @@ public:
u64 GetLastContextSwitchTicks() const;
/// Adds a new thread to the scheduler
void AddThread(SharedPtr<Thread> thread);
void AddThread(SharedPtr<Thread> thread, u32 priority);
/// Removes a thread from the scheduler
void RemoveThread(Thread* thread);
@@ -156,7 +156,7 @@ private:
std::vector<SharedPtr<Thread>> thread_list;
/// Lists only ready thread ids.
Common::MultiLevelQueue<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
SharedPtr<Thread> current_thread = nullptr;

View File

@@ -32,7 +32,6 @@
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -175,8 +174,11 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
return ERR_INVALID_SIZE;
}
auto& vm_manager = Core::System::GetInstance().Kernel().CurrentProcess()->VMManager();
const auto alloc_result = vm_manager.SetHeapSize(heap_size);
auto& vm_manager = Core::CurrentProcess()->VMManager();
const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
const auto alloc_result =
vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
if (alloc_result.Failed()) {
return alloc_result.Code();
}
@@ -549,9 +551,9 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
return ERR_INVALID_ADDRESS;
}
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
requesting_thread_handle);
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
requesting_thread_handle);
}
/// Unlock a mutex
@@ -569,8 +571,7 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
return ERR_INVALID_ADDRESS;
}
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
return current_process->GetMutex().Release(mutex_addr);
return Mutex::Release(mutex_addr);
}
enum class BreakType : u32 {
@@ -709,7 +710,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
HeapRegionBaseAddr = 4,
HeapRegionSize = 5,
TotalMemoryUsage = 6,
TotalPhysicalMemoryUsed = 7,
TotalHeapUsage = 7,
IsCurrentProcessBeingDebugged = 8,
RegisterResourceLimit = 9,
IdleTickCount = 10,
@@ -745,7 +746,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
case GetInfoType::NewMapRegionBaseAddr:
case GetInfoType::NewMapRegionSize:
case GetInfoType::TotalMemoryUsage:
case GetInfoType::TotalPhysicalMemoryUsed:
case GetInfoType::TotalHeapUsage:
case GetInfoType::IsVirtualAddressMemoryEnabled:
case GetInfoType::PersonalMmHeapUsage:
case GetInfoType::TitleId:
@@ -805,8 +806,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = process->VMManager().GetTotalMemoryUsage();
return RESULT_SUCCESS;
case GetInfoType::TotalPhysicalMemoryUsed:
*result = process->GetTotalPhysicalMemoryUsed();
case GetInfoType::TotalHeapUsage:
*result = process->VMManager().GetTotalHeapUsage();
return RESULT_SUCCESS;
case GetInfoType::IsVirtualAddressMemoryEnabled:
@@ -1283,14 +1284,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
@@ -1303,32 +1300,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
@@ -1339,21 +1336,17 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
const auto& handle_table = current_process->GetHandleTable();
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
const auto release_result = current_process->GetMutex().Release(mutex_addr);
if (release_result.IsError()) {
return release_result;
}
CASCADE_CODE(Mutex::Release(mutex_addr));
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->SetCondVarWaitAddress(condition_variable_addr);
current_thread->SetMutexWaitAddress(mutex_addr);
current_thread->SetWaitHandle(thread_handle);
current_thread->SetStatus(ThreadStatus::WaitCondVar);
current_thread->SetStatus(ThreadStatus::WaitMutex);
current_thread->InvalidateWakeupCallback();
current_thread->WakeAfterDelay(nano_seconds);
@@ -1397,10 +1390,10 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
// them all.
std::size_t last = waiting_threads.size();
if (target != -1)
last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
last = target;
// If there are no threads waiting on this condition variable, just exit
if (last == 0)
if (last > waiting_threads.size())
return RESULT_SUCCESS;
for (std::size_t index = 0; index < last; ++index) {
@@ -1408,9 +1401,6 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
// liberate Cond Var Thread.
thread->SetCondVarWaitAddress(0);
std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
auto& monitor = Core::System::GetInstance().Monitor();
@@ -1429,9 +1419,10 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
}
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
thread->GetWaitHandle()));
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->ResumeFromWait();
auto* const lock_owner = thread->GetLockOwner();
@@ -1441,8 +1432,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
thread->SetLockOwner(nullptr);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
} else {
// Atomically signal that the mutex now has a waiting thread.
do {
@@ -1461,11 +1452,12 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto owner = handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->InvalidateWakeupCallback();
thread->SetStatus(ThreadStatus::WaitMutex);
owner->AddMutexWaiter(thread);
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
}
}
@@ -1585,121 +1577,14 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
}
auto& kernel = Core::System::GetInstance().Kernel();
auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms);
auto process = kernel.CurrentProcess();
auto& handle_table = process->GetHandleTable();
const auto shared_mem_handle = SharedMemory::Create(kernel, process, size, perms, perms, addr);
auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
const auto result = handle_table.Create(std::move(transfer_mem_handle));
if (result.Failed()) {
return result.Code();
}
*handle = *result;
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
return RESULT_SUCCESS;
}
static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 permission_raw) {
LOG_DEBUG(Kernel_SVC,
"called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}",
handle, address, size, permission_raw);
if (!Common::Is4KBAligned(address)) {
LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).",
address);
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC,
"Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).",
size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(address, size)) {
LOG_ERROR(Kernel_SVC,
"Given address and size overflows the 64-bit range (address=0x{:016X}, "
"size=0x{:016X}).",
address, size);
return ERR_INVALID_ADDRESS_STATE;
}
const auto permissions = static_cast<MemoryPermission>(permission_raw);
if (permissions != MemoryPermission::None && permissions != MemoryPermission::Read &&
permissions != MemoryPermission::ReadWrite) {
LOG_ERROR(Kernel_SVC, "Invalid transfer memory permissions given (permissions=0x{:08X}).",
permission_raw);
return ERR_INVALID_STATE;
}
const auto& kernel = Core::System::GetInstance().Kernel();
const auto* const current_process = kernel.CurrentProcess();
const auto& handle_table = current_process->GetHandleTable();
auto transfer_memory = handle_table.Get<TransferMemory>(handle);
if (!transfer_memory) {
LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).",
handle);
return ERR_INVALID_HANDLE;
}
if (!current_process->VMManager().IsWithinASLRRegion(address, size)) {
LOG_ERROR(Kernel_SVC,
"Given address and size don't fully fit within the ASLR region "
"(address=0x{:016X}, size=0x{:016X}).",
address, size);
return ERR_INVALID_MEMORY_RANGE;
}
return transfer_memory->MapMemory(address, size, permissions);
}
static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) {
LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle,
address, size);
if (!Common::Is4KBAligned(address)) {
LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).",
address);
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC,
"Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).",
size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(address, size)) {
LOG_ERROR(Kernel_SVC,
"Given address and size overflows the 64-bit range (address=0x{:016X}, "
"size=0x{:016X}).",
address, size);
return ERR_INVALID_ADDRESS_STATE;
}
const auto& kernel = Core::System::GetInstance().Kernel();
const auto* const current_process = kernel.CurrentProcess();
const auto& handle_table = current_process->GetHandleTable();
auto transfer_memory = handle_table.Get<TransferMemory>(handle);
if (!transfer_memory) {
LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).",
handle);
return ERR_INVALID_HANDLE;
}
if (!current_process->VMManager().IsWithinASLRRegion(address, size)) {
LOG_ERROR(Kernel_SVC,
"Given address and size don't fully fit within the ASLR region "
"(address=0x{:016X}, size=0x{:016X}).",
address, size);
return ERR_INVALID_MEMORY_RANGE;
}
return transfer_memory->UnmapMemory(address, size);
}
static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
@@ -1983,83 +1868,6 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource
return RESULT_SUCCESS;
}
static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids,
u32 out_process_ids_size) {
LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
out_process_ids, out_process_ids_size);
// If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
if ((out_process_ids_size & 0xF0000000) != 0) {
LOG_ERROR(Kernel_SVC,
"Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
out_process_ids_size);
return ERR_OUT_OF_RANGE;
}
const auto& kernel = Core::System::GetInstance().Kernel();
const auto& vm_manager = kernel.CurrentProcess()->VMManager();
const auto total_copy_size = out_process_ids_size * sizeof(u64);
if (out_process_ids_size > 0 &&
!vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) {
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
out_process_ids, out_process_ids + total_copy_size);
return ERR_INVALID_ADDRESS_STATE;
}
const auto& process_list = kernel.GetProcessList();
const auto num_processes = process_list.size();
const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
for (std::size_t i = 0; i < copy_amount; ++i) {
Memory::Write64(out_process_ids, process_list[i]->GetProcessID());
out_process_ids += sizeof(u64);
}
*out_num_processes = static_cast<u32>(num_processes);
return RESULT_SUCCESS;
}
ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size,
Handle debug_handle) {
// TODO: Handle this case when debug events are supported.
UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
out_thread_ids, out_thread_ids_size);
// If the size is negative or larger than INT32_MAX / sizeof(u64)
if ((out_thread_ids_size & 0xF0000000) != 0) {
LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
out_thread_ids_size);
return ERR_OUT_OF_RANGE;
}
const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
const auto& vm_manager = current_process->VMManager();
const auto total_copy_size = out_thread_ids_size * sizeof(u64);
if (out_thread_ids_size > 0 &&
!vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) {
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
out_thread_ids, out_thread_ids + total_copy_size);
return ERR_INVALID_ADDRESS_STATE;
}
const auto& thread_list = current_process->GetThreadList();
const auto num_threads = thread_list.size();
const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
auto list_iter = thread_list.cbegin();
for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID());
out_thread_ids += sizeof(u64);
}
*out_num_threads = static_cast<u32>(num_threads);
return RESULT_SUCCESS;
}
namespace {
struct FunctionDef {
using Func = void();
@@ -2152,8 +1960,8 @@ static const FunctionDef SVC_Table[] = {
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
{0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"},
{0x51, SvcWrap<MapTransferMemory>, "MapTransferMemory"},
{0x52, SvcWrap<UnmapTransferMemory>, "UnmapTransferMemory"},
{0x51, nullptr, "MapTransferMemory"},
{0x52, nullptr, "UnmapTransferMemory"},
{0x53, nullptr, "CreateInterruptEvent"},
{0x54, nullptr, "QueryPhysicalAddress"},
{0x55, nullptr, "QueryIoMapping"},
@@ -2172,8 +1980,8 @@ static const FunctionDef SVC_Table[] = {
{0x62, nullptr, "TerminateDebugProcess"},
{0x63, nullptr, "GetDebugEvent"},
{0x64, nullptr, "ContinueDebugEvent"},
{0x65, SvcWrap<GetProcessList>, "GetProcessList"},
{0x66, SvcWrap<GetThreadList>, "GetThreadList"},
{0x65, nullptr, "GetProcessList"},
{0x66, nullptr, "GetThreadList"},
{0x67, nullptr, "GetDebugThreadContext"},
{0x68, nullptr, "SetDebugThreadContext"},
{0x69, nullptr, "QueryDebugProcessMemory"},
@@ -2215,7 +2023,7 @@ void CallSVC(u32 immediate) {
MICROPROFILE_SCOPE(Kernel_SVC);
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard lock{HLE::g_hle_lock};
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
const FunctionDef* info = GetSVCInfo(immediate);
if (info) {

View File

@@ -78,14 +78,6 @@ void SvcWrap() {
FuncReturn(retval);
}
template <ResultCode func(u32*, u64, u32)>
void SvcWrap() {
u32 param_1 = 0;
const u32 retval = func(&param_1, Param(1), static_cast<u32>(Param(2))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u64*, u32)>
void SvcWrap() {
u64 param_1 = 0;

View File

@@ -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"
@@ -62,12 +64,21 @@ void Thread::Stop() {
}
wait_objects.clear();
owner_process->UnregisterThread(this);
// Mark the TLS slot in the thread's page as free.
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)
@@ -107,7 +118,6 @@ void Thread::ResumeFromWait() {
case ThreadStatus::WaitSleep:
case ThreadStatus::WaitIPC:
case ThreadStatus::WaitMutex:
case ThreadStatus::WaitCondVar:
case ThreadStatus::WaitArb:
break;
@@ -201,11 +211,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = &owner_process;
thread->scheduler = &system.Scheduler(processor_id);
thread->scheduler->AddThread(thread);
thread->scheduler->AddThread(thread, priority);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
thread->owner_process->RegisterThread(thread.get());
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
ResetThreadContext(thread->context, stack_top, entry_point, arg);
@@ -261,8 +269,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;
}
@@ -270,16 +278,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();
}
@@ -287,44 +290,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) {
@@ -356,7 +347,7 @@ void Thread::ChangeScheduler() {
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
scheduler->RemoveThread(this);
next_scheduler.AddThread(this);
next_scheduler.AddThread(this, current_priority);
}
processor_id = *new_processor_id;
@@ -400,14 +391,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);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**

View File

@@ -51,8 +51,7 @@ enum class ThreadStatus {
WaitIPC, ///< Waiting for the reply from an IPC request
WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
WaitMutex, ///< Waiting due to an ArbitrateLock svc
WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
WaitMutex, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
Dormant, ///< Created but not yet made ready
Dead ///< Run to completion, or forcefully terminated
@@ -384,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;
@@ -402,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
@@ -470,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

View File

@@ -1,73 +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/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/transfer_memory.h"
#include "core/hle/result.h"
namespace Kernel {
TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {}
TransferMemory::~TransferMemory() = default;
SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address,
size_t size, MemoryPermission permissions) {
SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)};
transfer_memory->base_address = base_address;
transfer_memory->memory_size = size;
transfer_memory->owner_permissions = permissions;
transfer_memory->owner_process = kernel.CurrentProcess();
return transfer_memory;
}
ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermission permissions) {
if (memory_size != size) {
return ERR_INVALID_SIZE;
}
if (owner_permissions != permissions) {
return ERR_INVALID_STATE;
}
if (is_mapped) {
return ERR_INVALID_STATE;
}
const auto map_state = owner_permissions == MemoryPermission::None
? MemoryState::TransferMemoryIsolated
: MemoryState::TransferMemory;
auto& vm_manager = owner_process->VMManager();
const auto map_result = vm_manager.MapMemoryBlock(
address, std::make_shared<std::vector<u8>>(size), 0, size, map_state);
if (map_result.Failed()) {
return map_result.Code();
}
is_mapped = true;
return RESULT_SUCCESS;
}
ResultCode TransferMemory::UnmapMemory(VAddr address, size_t size) {
if (memory_size != size) {
return ERR_INVALID_SIZE;
}
auto& vm_manager = owner_process->VMManager();
const auto result = vm_manager.UnmapRange(address, size);
if (result.IsError()) {
return result;
}
is_mapped = false;
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -1,91 +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/kernel/object.h"
union ResultCode;
namespace Kernel {
class KernelCore;
class Process;
enum class MemoryPermission : u32;
/// Defines the interface for transfer memory objects.
///
/// Transfer memory is typically used for the purpose of
/// transferring memory between separate process instances,
/// thus the name.
///
class TransferMemory final : public Object {
public:
static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, size_t size,
MemoryPermission permissions);
TransferMemory(const TransferMemory&) = delete;
TransferMemory& operator=(const TransferMemory&) = delete;
TransferMemory(TransferMemory&&) = delete;
TransferMemory& operator=(TransferMemory&&) = delete;
std::string GetTypeName() const override {
return "TransferMemory";
}
std::string GetName() const override {
return GetTypeName();
}
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
/// Attempts to map transfer memory with the given range and memory permissions.
///
/// @param address The base address to being mapping memory at.
/// @param size The size of the memory to map, in bytes.
/// @param permissions The memory permissions to check against when mapping memory.
///
/// @pre The given address, size, and memory permissions must all match
/// the same values that were given when creating the transfer memory
/// instance.
///
ResultCode MapMemory(VAddr address, size_t size, MemoryPermission permissions);
/// Unmaps the transfer memory with the given range
///
/// @param address The base address to begin unmapping memory at.
/// @param size The size of the memory to unmap, in bytes.
///
/// @pre The given address and size must be the same as the ones used
/// to create the transfer memory instance.
///
ResultCode UnmapMemory(VAddr address, size_t size);
private:
explicit TransferMemory(KernelCore& kernel);
~TransferMemory() override;
/// The base address for the memory managed by this instance.
VAddr base_address = 0;
/// Size of the memory, in bytes, that this instance manages.
size_t memory_size = 0;
/// The memory permissions that are applied to this instance.
MemoryPermission owner_permissions{};
/// The process that this transfer memory instance was created under.
Process* owner_process = nullptr;
/// Whether or not this transfer memory instance has mapped memory.
bool is_mapped = false;
};
} // namespace Kernel

View File

@@ -7,29 +7,29 @@
#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 constexpr const char* names[] = {
"Unmapped", "Io",
"Normal", "Code",
"CodeData", "Heap",
"Shared", "Unknown1",
"ModuleCode", "ModuleCodeData",
"IpcBuffer0", "Stack",
"ThreadLocal", "TransferMemoryIsolated",
"TransferMemory", "ProcessMemory",
"Inaccessible", "IpcBuffer1",
"IpcBuffer3", "KernelStack",
"Unmapped", "Io",
"Normal", "CodeStatic",
"CodeMutable", "Heap",
"Shared", "Unknown1",
"ModuleCodeStatic", "ModuleCodeMutable",
"IpcBuffer0", "Stack",
"ThreadLocal", "TransferMemoryIsolated",
"TransferMemory", "ProcessMemory",
"Inaccessible", "IpcBuffer1",
"IpcBuffer3", "KernelStack",
};
return names[ToSvcMemoryState(state)];
@@ -177,7 +177,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;
@@ -256,50 +256,57 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
return RESULT_SUCCESS;
}
ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
if (size > GetHeapRegionSize()) {
return ERR_OUT_OF_MEMORY;
}
// No need to do any additional work if the heap is already the given size.
if (size == GetCurrentHeapSize()) {
return MakeResult(heap_region_base);
ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
if (!IsWithinHeapRegion(target, size)) {
return ERR_INVALID_ADDRESS;
}
if (heap_memory == nullptr) {
// Initialize heap
heap_memory = std::make_shared<std::vector<u8>>(size);
heap_end = heap_region_base + size;
heap_memory = std::make_shared<std::vector<u8>>();
heap_start = heap_end = target;
} else {
UnmapRange(heap_region_base, GetCurrentHeapSize());
UnmapRange(heap_start, heap_end - heap_start);
}
// If necessary, expand backing vector to cover new heap extents in
// the case of allocating. Otherwise, shrink the backing memory,
// if a smaller heap has been requested.
const u64 old_heap_size = GetCurrentHeapSize();
if (size > old_heap_size) {
const u64 alloc_size = size - old_heap_size;
heap_memory->insert(heap_memory->end(), alloc_size, 0);
RefreshMemoryBlockMappings(heap_memory.get());
} else if (size < old_heap_size) {
heap_memory->resize(size);
heap_memory->shrink_to_fit();
// If necessary, expand backing vector to cover new heap extents.
if (target < heap_start) {
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
heap_start = target;
RefreshMemoryBlockMappings(heap_memory.get());
}
if (target + size > heap_end) {
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
heap_end = target + size;
RefreshMemoryBlockMappings(heap_memory.get());
}
ASSERT(heap_end - heap_start == heap_memory->size());
heap_end = heap_region_base + size;
ASSERT(GetCurrentHeapSize() == heap_memory->size());
CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
MemoryState::Heap));
Reprotect(vma, perms);
const auto mapping_result =
MapMemoryBlock(heap_region_base, heap_memory, 0, size, MemoryState::Heap);
if (mapping_result.Failed()) {
return mapping_result.Code();
heap_used = size;
return MakeResult<VAddr>(heap_end - size);
}
ResultCode VMManager::HeapFree(VAddr target, u64 size) {
if (!IsWithinHeapRegion(target, size)) {
return ERR_INVALID_ADDRESS;
}
return MakeResult<VAddr>(heap_region_base);
if (size == 0) {
return RESULT_SUCCESS;
}
const ResultCode result = UnmapRange(target, size);
if (result.IsError()) {
return result;
}
heap_used -= size;
return RESULT_SUCCESS;
}
MemoryInfo VMManager::QueryMemory(VAddr address) const {
@@ -591,7 +598,6 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
heap_region_base = map_region_end;
heap_region_end = heap_region_base + heap_region_size;
heap_end = heap_region_base;
new_map_region_base = heap_region_end;
new_map_region_end = new_map_region_base + new_map_region_size;
@@ -618,7 +624,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,
@@ -686,6 +692,10 @@ u64 VMManager::GetTotalMemoryUsage() const {
return 0xF8000000;
}
u64 VMManager::GetTotalHeapUsage() const {
return heap_used;
}
VAddr VMManager::GetAddressSpaceBaseAddress() const {
return address_space_base;
}
@@ -768,10 +778,6 @@ u64 VMManager::GetHeapRegionSize() const {
return heap_region_end - heap_region_base;
}
u64 VMManager::GetCurrentHeapSize() const {
return heap_end - heap_region_base;
}
bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
GetHeapRegionEndAddress());

View File

@@ -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;
@@ -165,12 +164,12 @@ enum class MemoryState : u32 {
Unmapped = 0x00,
Io = 0x01 | FlagMapped,
Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed,
Code = 0x03 | CodeFlags | FlagMapProcess,
CodeData = 0x04 | DataFlags | FlagMapProcess | FlagCodeMemory,
CodeStatic = 0x03 | CodeFlags | FlagMapProcess,
CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory,
Heap = 0x05 | DataFlags | FlagCodeMemory,
Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated,
ModuleCode = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
ModuleCodeData = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated |
IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned,
@@ -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);
@@ -380,40 +379,10 @@ public:
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u64 size);
/// Attempts to allocate a heap with the given size.
///
/// @param size The size of the heap to allocate in bytes.
///
/// @note If a heap is currently allocated, and this is called
/// with a size that is equal to the size of the current heap,
/// then this function will do nothing and return the current
/// heap's starting address, as there's no need to perform
/// any additional heap allocation work.
///
/// @note If a heap is currently allocated, and this is called
/// with a size less than the current heap's size, then
/// this function will attempt to shrink the heap.
///
/// @note If a heap is currently allocated, and this is called
/// with a size larger than the current heap's size, then
/// this function will attempt to extend the size of the heap.
///
/// @returns A result indicating either success or failure.
/// <p>
/// If successful, this function will return a result
/// containing the starting address to the allocated heap.
/// <p>
/// If unsuccessful, this function will return a result
/// containing an error code.
///
/// @pre The given size must lie within the allowable heap
/// memory region managed by this VMManager instance.
/// Failure to abide by this will result in ERR_OUT_OF_MEMORY
/// being returned as the result.
///
ResultVal<VAddr> SetHeapSize(u64 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
/// Queries the memory manager for information about the given address.
///
@@ -448,6 +417,9 @@ public:
/// Gets the total memory usage, used by svcGetInfo
u64 GetTotalMemoryUsage() const;
/// Gets the total heap usage, used by svcGetInfo
u64 GetTotalHeapUsage() const;
/// Gets the address space base address
VAddr GetAddressSpaceBaseAddress() const;
@@ -496,13 +468,6 @@ public:
/// Gets the total size of the heap region in bytes.
u64 GetHeapRegionSize() const;
/// Gets the total size of the current heap in bytes.
///
/// @note This is the current allocated heap size, not the size
/// of the region it's allowed to exist within.
///
u64 GetCurrentHeapSize() const;
/// Determines whether or not the specified range is within the heap region.
bool IsWithinHeapRegion(VAddr address, u64 size) const;
@@ -544,7 +509,7 @@ public:
/// 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;
@@ -659,9 +624,9 @@ private:
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
// The end of the currently allocated heap. This is not an inclusive
// end of the range. This is essentially 'base_address + current_size'.
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0;
VAddr heap_end = 0;
u64 heap_used = 0;
};
} // namespace Kernel

View File

@@ -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"
@@ -93,84 +93,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") {
@@ -215,21 +169,7 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
IDisplayController::~IDisplayController() = default;
IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "NotifyMessageToHomeMenuForDebug"},
{1, nullptr, "OpenMainApplication"},
{10, nullptr, "EmulateButtonEvent"},
{20, nullptr, "InvalidateTransitionLayer"},
{30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
{40, nullptr, "GetAppletResourceUsageInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {}
IDebugFunctions::~IDebugFunctions() = default;
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)

View File

@@ -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> {

View File

@@ -8,7 +8,6 @@
#include <vector>
#include <opus.h>
#include <opus_multistream.h>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -19,12 +18,12 @@
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>;
using OpusDecoderPtr = std::unique_ptr<OpusDecoder, OpusDeleter>;
struct OpusPacketHeader {
// Packet size in bytes.
@@ -34,7 +33,7 @@ struct OpusPacketHeader {
};
static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
class OpusDecoderState {
class OpusDecoderStateBase {
public:
/// Describes extra behavior that may be asked of the decoding context.
enum class ExtraBehavior {
@@ -50,13 +49,22 @@ public:
Enabled,
};
explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
: decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
virtual ~OpusDecoderStateBase() = default;
// Decodes interleaved Opus packets. Optionally allows reporting time taken to
// perform the decoding, as well as any relevant extra behavior.
virtual void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
ExtraBehavior extra_behavior) = 0;
};
// Represents the decoder state for a non-multistream decoder.
class OpusDecoderState final : public OpusDecoderStateBase {
public:
explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
: decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
ExtraBehavior extra_behavior) {
ExtraBehavior extra_behavior) override {
if (perf_time == PerfTime::Disabled) {
DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
} else {
@@ -127,7 +135,7 @@ 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.size, output.data(), frame_size, 0);
if (out_sample_count < 0) {
LOG_ERROR(Audio,
"Incorrect sample count received from opus_decode, "
@@ -150,7 +158,7 @@ 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;
@@ -160,7 +168,7 @@ private:
class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
public:
explicit IHardwareOpusDecoderManager(OpusDecoderState decoder_state)
explicit IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoderStateBase> decoder_state)
: ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
// clang-format off
static const FunctionInfo functions[] = {
@@ -182,51 +190,35 @@ private:
void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled,
OpusDecoderState::ExtraBehavior::None);
decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Disabled,
OpusDecoderStateBase::ExtraBehavior::None);
}
void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled,
OpusDecoderState::ExtraBehavior::None);
decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled,
OpusDecoderStateBase::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;
const auto extra_behavior = rp.Pop<bool>()
? OpusDecoderStateBase::ExtraBehavior::ResetContext
: OpusDecoderStateBase::ExtraBehavior::None;
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled,
extra_behavior);
}
OpusDecoderState decoder_state;
std::unique_ptr<OpusDecoderStateBase> decoder_state;
};
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);
}
// 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}};
return opus_decoder_get_size(static_cast<int>(channel_count));
}
} // Anonymous namespace
@@ -267,15 +259,9 @@ 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);
OpusDecoderPtr 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));
@@ -285,7 +271,7 @@ 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});
std::make_unique<OpusDecoderState>(std::move(decoder), sample_rate, channel_count));
}
HwOpus::HwOpus() : ServiceFramework("hwopus") {

View File

@@ -25,34 +25,21 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
Module::Interface::~Interface() = default;
struct FatalInfo {
enum class Architecture : s32 {
AArch64,
AArch32,
};
const char* ArchAsString() const {
return arch == Architecture::AArch64 ? "AArch64" : "AArch32";
}
std::array<u64_le, 31> registers{};
u64_le sp{};
u64_le pc{};
u64_le pstate{};
u64_le afsr0{};
u64_le afsr1{};
u64_le esr{};
u64_le far{};
std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or
// not(find a game which has non zero valeus)
u64_le unk0{};
u64_le unk1{};
u64_le unk2{};
u64_le unk3{};
u64_le unk4{};
u64_le unk5{};
u64_le unk6{};
std::array<u64_le, 32> backtrace{};
u64_le program_entry_point{};
// Bit flags that indicate which registers have been set with values
// for this context. The service itself uses these to determine which
// registers to specifically print out.
u64_le set_flags{};
u64_le unk7{};
u64_le unk8{};
u32_le backtrace_size{};
Architecture arch{};
u32_le unk9{};
u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
};
static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
@@ -65,36 +52,36 @@ enum class FatalType : u32 {
static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
const auto title_id = Core::CurrentProcess()->GetTitleID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
"Result: 0x{:X} ({:04}-{:04d})\n"
"Set flags: 0x{:16X}\n"
"Program entry point: 0x{:16X}\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
2000 + static_cast<u32>(error_code.module.Value()),
static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point);
std::string crash_report =
fmt::format("Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
"Result: 0x{:X} ({:04}-{:04d})\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
2000 + static_cast<u32>(error_code.module.Value()),
static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7);
if (info.backtrace_size != 0x0) {
crash_report += "Registers:\n";
// TODO(ogniK): This is just a guess, find a game which actually has non zero values
for (size_t i = 0; i < info.registers.size(); i++) {
crash_report +=
fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
}
crash_report += fmt::format(" SP: {:016x}\n", info.sp);
crash_report += fmt::format(" PC: {:016x}\n", info.pc);
crash_report += fmt::format(" PSTATE: {:016x}\n", info.pstate);
crash_report += fmt::format(" AFSR0: {:016x}\n", info.afsr0);
crash_report += fmt::format(" AFSR1: {:016x}\n", info.afsr1);
crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
crash_report += fmt::format(" FAR: {:016x}\n", info.far);
crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0);
crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1);
crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2);
crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3);
crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4);
crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5);
crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6);
crash_report += "\nBacktrace:\n";
for (size_t i = 0; i < info.backtrace_size; i++) {
crash_report +=
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
}
crash_report += fmt::format("Architecture: {}\n", info.ArchAsString());
crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7);
crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8);
crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9);
crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
}
@@ -138,13 +125,13 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F
case FatalType::ErrorReport:
GenerateErrorReport(error_code, info);
break;
}
};
}
void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp{ctx};
const auto error_code = rp.Pop<ResultCode>();
auto error_code = rp.Pop<ResultCode>();
ThrowFatalError(error_code, FatalType::ErrorScreen, {});
IPC::ResponseBuilder rb{ctx, 2};
@@ -154,8 +141,8 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
const auto error_code = rp.Pop<ResultCode>();
const auto fatal_type = rp.PopEnum<FatalType>();
auto error_code = rp.Pop<ResultCode>();
auto fatal_type = rp.PopEnum<FatalType>();
ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
IPC::ResponseBuilder rb{ctx, 2};
@@ -165,9 +152,9 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
const auto error_code = rp.Pop<ResultCode>();
const auto fatal_type = rp.PopEnum<FatalType>();
const auto fatal_info = ctx.ReadBuffer();
auto error_code = rp.Pop<ResultCode>();
auto fatal_type = rp.PopEnum<FatalType>();
auto fatal_info = ctx.ReadBuffer();
FatalInfo info{};
ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");

View File

@@ -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);

View File

@@ -32,7 +32,6 @@ private:
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
FileSys::VirtualFile romfs;
u64 current_process_id = 0;
};
} // namespace Service::FileSystem

View File

@@ -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");

View File

@@ -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");
@@ -150,43 +150,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 +200,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 +240,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;
};
};

View File

@@ -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");

View File

@@ -36,9 +36,9 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(ogniK): Find actual polling rate of hid
constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66);
constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr u64 pad_update_ticks = Core::Timing::BASE_CLOCK_RATE / 66;
constexpr u64 accelerometer_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
@@ -75,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
// Register update callbacks
auto& core_timing = Core::System::GetInstance().CoreTiming();
pad_update_event =
core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
UpdateControllers(userdata, cycles_late);
});
@@ -106,7 +106,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
rb.PushCopyObjects(shared_mem);
}
void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
auto& core_timing = Core::System::GetInstance().CoreTiming();
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);

View File

@@ -4,9 +4,6 @@
#pragma once
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/service.h"
#include "controllers/controller_base.h"
#include "core/hle/service/service.h"
@@ -65,7 +62,7 @@ private:
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(u64 userdata, s64 cycles_late);
void UpdateControllers(u64 userdata, int cycles_late);
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;

View File

@@ -319,14 +319,15 @@ public:
}
ASSERT(vm_manager
.MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode)
.MirrorMemory(*map_address, nro_addr, nro_size,
Kernel::MemoryState::ModuleCodeStatic)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
if (bss_size > 0) {
ASSERT(vm_manager
.MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
Kernel::MemoryState::ModuleCode)
Kernel::MemoryState::ModuleCodeStatic)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
}
@@ -387,7 +388,8 @@ public:
const auto& nro_size = iter->second.size;
ASSERT(vm_manager
.MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode)
.MirrorMemory(heap_addr, mapped_addr, nro_size,
Kernel::MemoryState::ModuleCodeStatic)
.IsSuccess());
ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());

View File

@@ -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;

View File

@@ -150,7 +150,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u8>(true);
rb.PushRaw<u8>(Settings::values.enable_nfc);
}
void GetStateOld(Kernel::HLERequestContext& ctx) {

View File

@@ -335,7 +335,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
}
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
std::lock_guard lock{HLE::g_hle_lock};
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
if (buffer.size() < sizeof(AmiiboFile)) {
return false;
}

View File

@@ -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;
};
/**

View File

@@ -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"
@@ -89,7 +88,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
for (const auto& entry : entries) {
LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
entry.offset, entry.nvmap_handle, entry.pages);
GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10;
Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
auto object = nvmap_dev->GetObject(entry.nvmap_handle);
if (!object) {
LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
@@ -102,7 +101,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
u64 size = static_cast<u64>(entry.pages) << 0x10;
ASSERT(size <= object->size);
GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size);
Tegra::GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size);
ASSERT(returned == offset);
}
std::memcpy(output.data(), entries.data(), output.size());
@@ -173,8 +172,16 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
return 0;
}
params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset,
itr->second.size);
auto& system_instance = Core::System::GetInstance();
// Remove this memory region from the rasterizer cache.
auto& gpu = system_instance.GPU();
auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
ASSERT(cpu_addr);
gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);
std::memcpy(output.data(), &params, output.size());

View File

@@ -26,7 +26,7 @@
namespace Service::NVFlinger {
constexpr std::size_t SCREEN_REFRESH_RATE = 60;
constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
displays.emplace_back(0, "Default");
@@ -37,7 +37,7 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
// Schedule the screen composition events
composition_event =
core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) {
core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
Compose();
this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event);
});

View File

@@ -2,88 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/system_archive/system_version.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/set/set_sys.h"
namespace Service::Set {
namespace {
constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05;
enum class GetFirmwareVersionType {
Version1,
Version2,
};
void GetFirmwareVersionImpl(Kernel::HLERequestContext& ctx, GetFirmwareVersionType type) {
LOG_WARNING(Service_SET, "called - Using hardcoded firmware version '{}'",
FileSys::SystemArchive::GetLongDisplayVersion());
ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100,
"FirmwareVersion output buffer must be 0x100 bytes in size!");
// Instead of using the normal procedure of checking for the real system archive and if it
// doesn't exist, synthesizing one, I feel that that would lead to strange bugs because a
// used is using a really old or really new SystemVersion title. The synthesized one ensures
// consistence (currently reports as 5.1.0-0.0)
const auto archive = FileSys::SystemArchive::SystemVersion();
const auto early_exit_failure = [&ctx](const std::string& desc, ResultCode code) {
LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
desc.c_str());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(code);
};
if (archive == nullptr) {
early_exit_failure("The system version archive couldn't be synthesized.",
FileSys::ERROR_FAILED_MOUNT_ARCHIVE);
return;
}
const auto ver_file = archive->GetFile("file");
if (ver_file == nullptr) {
early_exit_failure("The system version archive didn't contain the file 'file'.",
FileSys::ERROR_INVALID_ARGUMENT);
return;
}
auto data = ver_file->ReadAllBytes();
if (data.size() != 0x100) {
early_exit_failure("The system version file 'file' was not the correct size.",
FileSys::ERROR_OUT_OF_BOUNDS);
return;
}
// If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will
// zero out the REVISION_MINOR field.
if (type == GetFirmwareVersionType::Version1) {
data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0;
}
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
} // Anonymous namespace
void SET_SYS::GetFirmwareVersion(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
GetFirmwareVersionImpl(ctx, GetFirmwareVersionType::Version1);
}
void SET_SYS::GetFirmwareVersion2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
GetFirmwareVersionImpl(ctx, GetFirmwareVersionType::Version2);
}
void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
@@ -108,8 +33,8 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
{0, nullptr, "SetLanguageCode"},
{1, nullptr, "SetNetworkSettings"},
{2, nullptr, "GetNetworkSettings"},
{3, &SET_SYS::GetFirmwareVersion, "GetFirmwareVersion"},
{4, &SET_SYS::GetFirmwareVersion2, "GetFirmwareVersion2"},
{3, nullptr, "GetFirmwareVersion"},
{4, nullptr, "GetFirmwareVersion2"},
{5, nullptr, "GetFirmwareVersionDigest"},
{7, nullptr, "GetLockScreenFlag"},
{8, nullptr, "SetLockScreenFlag"},

View File

@@ -20,8 +20,6 @@ private:
BasicBlack = 1,
};
void GetFirmwareVersion(Kernel::HLERequestContext& ctx);
void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
void GetColorSetId(Kernel::HLERequestContext& ctx);
void SetColorSetId(Kernel::HLERequestContext& ctx);

View File

@@ -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"
@@ -341,7 +340,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
}
codeset.entrypoint = base_addr + header->e_entry;
codeset.memory = std::move(program_image);
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
LOG_DEBUG(Loader, "Done loading.");

147
src/core/loader/linker.cpp Normal file
View 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
View 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

View File

@@ -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"
@@ -187,7 +186,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
// Load codeset for current process
codeset.memory = std::move(program_image);
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
process.LoadModule(std::move(codeset), load_base);
// Register module with GDBStub

View File

@@ -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;

View File

@@ -7,13 +7,10 @@
#include <lz4.h>
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.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"
@@ -21,8 +18,36 @@
#include "core/settings.h"
namespace Loader {
namespace {
struct MODHeader {
struct NsoSegmentHeader {
u32_le offset;
u32_le location;
u32_le size;
union {
u32_le alignment;
u32_le bss_size;
};
};
static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
struct NsoHeader {
u32_le magic;
u32_le version;
INSERT_PADDING_WORDS(1);
u8 flags;
std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
std::array<u8, 0x20> build_id;
std::array<u32_le, 3> segments_compressed_size;
bool IsSegmentCompressed(size_t segment_num) const {
ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
return ((flags >> segment_num) & 1);
}
};
static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
struct ModHeader {
u32_le magic;
u32_le dynamic_offset;
u32_le bss_start_offset;
@@ -31,32 +56,7 @@ struct MODHeader {
u32_le eh_frame_hdr_end_offset;
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
};
static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NSOSegmentHeader& header) {
std::vector<u8> uncompressed_data(header.size);
const int bytes_uncompressed =
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()),
static_cast<int>(compressed_data.size()), header.size);
ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
} // Anonymous namespace
bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
return ((flags >> segment_num) & 1) != 0;
}
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
@@ -73,22 +73,38 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::NSO;
}
static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NsoSegmentHeader& header) {
std::vector<u8> uncompressed_data(header.size);
const int bytes_uncompressed =
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()),
static_cast<int>(compressed_data.size()), header.size);
ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments,
std::optional<FileSys::PatchManager> pm) {
if (file.GetSize() < sizeof(NSOHeader)) {
if (file.GetSize() < sizeof(NsoHeader))
return {};
}
NSOHeader nso_header{};
if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
NsoHeader nso_header{};
if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
return {};
}
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return {};
}
// Build program image
Kernel::CodeSet codeset;
@@ -124,10 +140,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
// Read MOD header
MODHeader mod_header{};
ModHeader mod_header{};
// Default .bss to size in segment header if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader));
std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
@@ -139,29 +155,17 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
// Apply patches if necessary
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
program_image.end());
std::vector<u8> pi_header(program_image.size() + 0x100);
std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
pi_header = pm->PatchNSO(pi_header);
std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
}
// Apply cheats if they exist and the program has a valid title ID
if (pm) {
auto& system = Core::System::GetInstance();
const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
if (!cheats.empty()) {
system.RegisterCheatList(cheats, Common::HexArrayToString(nso_header.build_id),
load_base, load_base + program_image.size());
}
std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size());
}
// Load codeset for current process
codeset.memory = std::move(program_image);
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
process.LoadModule(std::move(codeset), load_base);
// Register module with GDBStub

View File

@@ -4,12 +4,10 @@
#pragma once
#include <array>
#include <optional>
#include <type_traits>
#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 {
@@ -18,43 +16,6 @@ class Process;
namespace Loader {
struct NSOSegmentHeader {
u32_le offset;
u32_le location;
u32_le size;
union {
u32_le alignment;
u32_le bss_size;
};
};
static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
struct NSOHeader {
using SHA256Hash = std::array<u8, 0x20>;
struct RODataRelativeExtent {
u32_le data_offset;
u32_le size;
};
u32_le magic;
u32_le version;
u32 reserved;
u32_le flags;
std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
std::array<u8, 0x20> build_id;
std::array<u32_le, 3> segments_compressed_size;
std::array<u8, 0x1C> padding;
RODataRelativeExtent api_info_extent;
RODataRelativeExtent dynstr_extent;
RODataRelativeExtent dynsyn_extent;
std::array<SHA256Hash, 3> segment_hashes;
bool IsSegmentCompressed(size_t segment_num) const;
};
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
struct NSOArgumentHeader {
@@ -65,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);

View 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 (Core::System::GetInstance().IsPoweredOn()) {
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& gpu = system_instance.GPU();
switch (mode) {
case FlushMode::Flush:
gpu.FlushRegion(overlap_start, overlap_size);
break;
case FlushMode::Invalidate:
gpu.InvalidateRegion(overlap_start, overlap_size);
break;
case FlushMode::FlushAndInvalidate:
gpu.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:

View File

@@ -6,11 +6,11 @@
#include <cstddef>
#include <string>
#include <tuple>
#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;
@@ -26,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.
@@ -51,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);
@@ -96,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