Compare commits
209 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adec4d0da7 | ||
|
|
4e89979c87 | ||
|
|
8fd4e44014 | ||
|
|
49219b8a86 | ||
|
|
d6b63239ae | ||
|
|
cafca891ea | ||
|
|
c845d8a9e8 | ||
|
|
1a6785d296 | ||
|
|
b6d93b2c77 | ||
|
|
c4af7b3f5c | ||
|
|
96ffc174aa | ||
|
|
d7ec031419 | ||
|
|
55a3cbfa0d | ||
|
|
55b546a110 | ||
|
|
6b8ab9ed8f | ||
|
|
a7f1fa7bfc | ||
|
|
b6373c5ea5 | ||
|
|
0941ae0b61 | ||
|
|
47b133c0b8 | ||
|
|
a9efea8ae9 | ||
|
|
25dda06f49 | ||
|
|
ae74f46e78 | ||
|
|
3020f72b0c | ||
|
|
c0c4f6dfa6 | ||
|
|
260a495a23 | ||
|
|
7a329ae56c | ||
|
|
d0883027d6 | ||
|
|
bbb963a31a | ||
|
|
51abe35e05 | ||
|
|
64ff79f919 | ||
|
|
e507c01a22 | ||
|
|
ad787b20ca | ||
|
|
c2c9b44749 | ||
|
|
8265c167d3 | ||
|
|
780ae92265 | ||
|
|
2ec7d0b5fd | ||
|
|
7f42432f42 | ||
|
|
e96f55b6e2 | ||
|
|
6291eec700 | ||
|
|
aaada241dc | ||
|
|
6b64557ad6 | ||
|
|
a2fde04da9 | ||
|
|
71f2b4ba8f | ||
|
|
7d8095d944 | ||
|
|
a67e776af9 | ||
|
|
3ab8d9ac7c | ||
|
|
ef6144bf48 | ||
|
|
278c0e6e8d | ||
|
|
4a46da6fb5 | ||
|
|
eabe45346f | ||
|
|
8f6245be9a | ||
|
|
701ca96827 | ||
|
|
26211ac339 | ||
|
|
3e53d8138c | ||
|
|
ddca512f3f | ||
|
|
972bd6cb54 | ||
|
|
c8d84cb6cb | ||
|
|
19a640286c | ||
|
|
b670c350e4 | ||
|
|
e16d1b85f1 | ||
|
|
2572b0a5ea | ||
|
|
e8cbc3b4c5 | ||
|
|
64965cc658 | ||
|
|
20b62dbd30 | ||
|
|
9d081a8729 | ||
|
|
826e0785bf | ||
|
|
3b582d5fb2 | ||
|
|
7356ab1de6 | ||
|
|
daf2ef8f1c | ||
|
|
5fbd6954ef | ||
|
|
957840be91 | ||
|
|
a75542ad2d | ||
|
|
f209e976f4 | ||
|
|
35319ca3a5 | ||
|
|
3e6c835a64 | ||
|
|
852de7a771 | ||
|
|
ca154d466a | ||
|
|
97f273e94e | ||
|
|
59335f6796 | ||
|
|
c31f19b6d1 | ||
|
|
9abceaed61 | ||
|
|
cdb2e4eaff | ||
|
|
168c9ee341 | ||
|
|
8d99aae45b | ||
|
|
f047f376d4 | ||
|
|
84d4da89a5 | ||
|
|
1d57851fc7 | ||
|
|
3027917f39 | ||
|
|
7e0f70e5a1 | ||
|
|
aab68674c0 | ||
|
|
7f1c6def1f | ||
|
|
db7bcd51ae | ||
|
|
eb0713f781 | ||
|
|
57a05b1653 | ||
|
|
69c92b8156 | ||
|
|
4acbf3a193 | ||
|
|
54d6273975 | ||
|
|
98a6cd02c8 | ||
|
|
52acdafa95 | ||
|
|
975e17aa13 | ||
|
|
72118935a1 | ||
|
|
109c31c90f | ||
|
|
344e171cc7 | ||
|
|
bcbc25eeb3 | ||
|
|
b0365a81c2 | ||
|
|
327d225c3e | ||
|
|
aa075a0c08 | ||
|
|
38c48cf8d8 | ||
|
|
4975f60162 | ||
|
|
0d033e6b45 | ||
|
|
9c67334031 | ||
|
|
1fb33bd1e1 | ||
|
|
d23a35dfbd | ||
|
|
ad3ee5c52b | ||
|
|
405d685101 | ||
|
|
e5a446a0df | ||
|
|
0e61d711e2 | ||
|
|
bc95753107 | ||
|
|
60e0d4a177 | ||
|
|
4c42655a2d | ||
|
|
ece0c1095d | ||
|
|
f426fd95fe | ||
|
|
fa660190ff | ||
|
|
7c50a916c7 | ||
|
|
48b4eca28a | ||
|
|
e94bcf03cb | ||
|
|
8ca02794c5 | ||
|
|
ef5184cf1c | ||
|
|
9e27624a19 | ||
|
|
cb971ad654 | ||
|
|
cf202f3718 | ||
|
|
18fcc03b3c | ||
|
|
6fa3faec65 | ||
|
|
75e6ec85e1 | ||
|
|
a253d1557d | ||
|
|
9afadca5dc | ||
|
|
fb57cd26a1 | ||
|
|
b193d40d22 | ||
|
|
6c045c9beb | ||
|
|
040a01a5dd | ||
|
|
8cc5ad8742 | ||
|
|
540c1696d1 | ||
|
|
651f6598ac | ||
|
|
70ea1c2000 | ||
|
|
08091ff3e3 | ||
|
|
b51c1544b9 | ||
|
|
7dfe35eca6 | ||
|
|
69768ec71e | ||
|
|
e4d55e4ee4 | ||
|
|
c4bc7ce7e2 | ||
|
|
211da31b34 | ||
|
|
c973029374 | ||
|
|
b832942b6e | ||
|
|
5eb30c7827 | ||
|
|
33ea0fdfe8 | ||
|
|
c9bb888adf | ||
|
|
d05b183f21 | ||
|
|
83eb9cf7da | ||
|
|
0e84fd95e2 | ||
|
|
18123ff958 | ||
|
|
ceb829cc33 | ||
|
|
bb55d2e701 | ||
|
|
0c176ce828 | ||
|
|
b34d3d5882 | ||
|
|
26a1d4fc37 | ||
|
|
ea41c53ab1 | ||
|
|
766941f1a3 | ||
|
|
d03afd6f4b | ||
|
|
4eece4d35d | ||
|
|
770f23db34 | ||
|
|
cbaf642ffe | ||
|
|
d581a4a367 | ||
|
|
3161b34ff6 | ||
|
|
71c0e20f95 | ||
|
|
ad9e5bc5b7 | ||
|
|
a4472b5526 | ||
|
|
6a0d8b2aa1 | ||
|
|
dc520a487d | ||
|
|
aa97f39ba8 | ||
|
|
df38c03a09 | ||
|
|
4c198bbf06 | ||
|
|
cf0f821565 | ||
|
|
a5d8703235 | ||
|
|
e6fe40428c | ||
|
|
85527cc7c7 | ||
|
|
05ae0cab0e | ||
|
|
119315af08 | ||
|
|
661fe06d9d | ||
|
|
ba21ba0c5c | ||
|
|
32d7faafa8 | ||
|
|
b7b47f3099 | ||
|
|
6f941121e6 | ||
|
|
6636b81573 | ||
|
|
1f21fa866d | ||
|
|
84d130f143 | ||
|
|
d928ba8e40 | ||
|
|
3aab7d4473 | ||
|
|
6b6c02f541 | ||
|
|
50bfacca88 | ||
|
|
0cb9bc12fc | ||
|
|
6257461684 | ||
|
|
d353c45f7d | ||
|
|
f76b4417e6 | ||
|
|
0897f4f96c | ||
|
|
6d4f411c08 | ||
|
|
37b17252d1 | ||
|
|
ddd3f48736 | ||
|
|
46322be735 | ||
|
|
b42b894785 |
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
name: New Issue (Developers Only)
|
||||
description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.**
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Issue"
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
name: Bug Report / Feature Request
|
||||
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
|
||||
|
||||
Please read the FAQ:
|
||||
https://yuzu-emu.org/wiki/faq/
|
||||
|
||||
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
|
||||
https://community.citra-emu.org/
|
||||
|
||||
If the FAQ does not answer your question, please go to:
|
||||
https://community.citra-emu.org/
|
||||
|
||||
When submitting an issue, please check the following:
|
||||
|
||||
- You have read the above.
|
||||
- You have provided the version (commit hash) of yuzu you are using.
|
||||
- You have provided sufficient detail for the issue to be reproduced.
|
||||
- You have provided system specs (if relevant).
|
||||
- Please also provide:
|
||||
- For any issues, a log file
|
||||
- For crashes, a backtrace.
|
||||
- For graphical issues, comparison screenshots with real hardware.
|
||||
- For emulation inaccuracies, a test-case (if able).
|
||||
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Affected Build(s)
|
||||
description: List the affected build(s) that this issue applies to.
|
||||
placeholder: Mainline 1234 / Early Access 1234
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: issue-desc
|
||||
attributes:
|
||||
label: Description of Issue
|
||||
description: A brief description of the issue encountered along with any images and/or videos.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A brief description of how it is expected to work along with any images and/or videos.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction-steps
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Log File
|
||||
description: A log file will help our developers to better diagnose and fix the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: system-config
|
||||
attributes:
|
||||
label: System Configuration
|
||||
placeholder: |
|
||||
CPU: Intel i5-10400 / AMD Ryzen 5 3600
|
||||
GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95)
|
||||
RAM: 16GB DDR4-3200
|
||||
OS: Windows 11 22H2 (Build 22621.819)
|
||||
value: |
|
||||
CPU:
|
||||
GPU/Driver:
|
||||
RAM:
|
||||
OS:
|
||||
validations:
|
||||
required: true
|
||||
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Feature Request
|
||||
description: File a feature request
|
||||
labels: "request"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the feature you are requesting.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-feature
|
||||
attributes:
|
||||
label: What feature are you suggesting?
|
||||
description: A brief description of the requested feature.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: why-feature
|
||||
attributes:
|
||||
label: Why would this feature be useful?
|
||||
description: A brief description of why this feature would make yuzu better.
|
||||
validations:
|
||||
required: true
|
||||
@@ -132,10 +132,6 @@ Files: vcpkg.json
|
||||
Copyright: 2022 yuzu Emulator Project
|
||||
License: GPL-3.0-or-later
|
||||
|
||||
Files: .github/ISSUE_TEMPLATE/config.yml
|
||||
Copyright: 2020 tgsm <doodrabbit@hotmail.com>
|
||||
License: GPL-2.0-or-later
|
||||
|
||||
Files: .github/ISSUE_TEMPLATE/bug-report-feature-request.md
|
||||
Copyright: 2016 MerryMage
|
||||
Files: .github/ISSUE_TEMPLATE/*
|
||||
Copyright: 2022 yuzu Emulator Project
|
||||
License: GPL-2.0-or-later
|
||||
|
||||
281
CMakeLists.txt
281
CMakeLists.txt
@@ -1,7 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||
@@ -19,6 +19,9 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF)
|
||||
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
option(ENABLE_QT6 "Allow usage of Qt6 to be attempted" OFF)
|
||||
set(QT6_LOCATION "" CACHE PATH "Additional Location to search for Qt6 libraries like C:/Qt/6.3.1/msvc2019_64/")
|
||||
|
||||
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
|
||||
|
||||
@@ -28,6 +31,8 @@ option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
|
||||
|
||||
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
|
||||
|
||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
|
||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
@@ -133,13 +138,13 @@ if (NOT ENABLE_GENERIC)
|
||||
if (MSVC)
|
||||
detect_architecture("_M_AMD64" x86_64)
|
||||
detect_architecture("_M_IX86" x86)
|
||||
detect_architecture("_M_ARM" ARM)
|
||||
detect_architecture("_M_ARM64" ARM64)
|
||||
detect_architecture("_M_ARM" arm)
|
||||
detect_architecture("_M_ARM64" arm64)
|
||||
else()
|
||||
detect_architecture("__x86_64__" x86_64)
|
||||
detect_architecture("__i386__" x86)
|
||||
detect_architecture("__arm__" ARM)
|
||||
detect_architecture("__aarch64__" ARM64)
|
||||
detect_architecture("__arm__" arm)
|
||||
detect_architecture("__aarch64__" arm64)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -213,128 +218,166 @@ if (MINGW)
|
||||
find_library(MSWSOCK_LIBRARY mswsock REQUIRED)
|
||||
endif()
|
||||
|
||||
# Please consider this as a stub
|
||||
if(ENABLE_QT6 AND Qt6_LOCATION)
|
||||
list(APPEND CMAKE_PREFIX_PATH "${Qt6_LOCATION}")
|
||||
endif()
|
||||
|
||||
function(set_yuzu_qt_components)
|
||||
# Best practice is to ask for all components at once, so they are from the same version
|
||||
set(YUZU_QT_COMPONENTS2 Core Widgets Concurrent)
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
list(APPEND YUZU_QT_COMPONENTS2 DBus)
|
||||
endif()
|
||||
if (YUZU_USE_QT_MULTIMEDIA)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 Multimedia)
|
||||
endif()
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
if (ENABLE_QT_TRANSLATION)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
||||
endif()
|
||||
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
||||
endfunction(set_yuzu_qt_components)
|
||||
|
||||
# Qt5 requires that we find components, so it doesn't fit our pretty little find package function
|
||||
if(ENABLE_QT)
|
||||
set(QT_VERSION 5.15)
|
||||
# These are used to specify minimum versions
|
||||
set(QT5_VERSION 5.15)
|
||||
set(QT6_VERSION 6.3.1)
|
||||
|
||||
# Check for system Qt on Linux, fallback to bundled Qt
|
||||
if (UNIX AND NOT APPLE)
|
||||
if (NOT YUZU_USE_BUNDLED_QT)
|
||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
|
||||
# Check for dependencies, then enable bundled Qt download
|
||||
|
||||
# Check that the system GLIBCXX version is compatible
|
||||
find_program(OBJDUMP objdump)
|
||||
if ("${OBJDUMP}" STREQUAL "OBJDUMP-NOTFOUND")
|
||||
message(FATAL_ERROR "Required program `objdump` not found.")
|
||||
endif()
|
||||
find_library(LIBSTDCXX libstdc++.so.6)
|
||||
execute_process(
|
||||
COMMAND
|
||||
${OBJDUMP} -T ${LIBSTDCXX}
|
||||
COMMAND
|
||||
grep GLIBCXX_3.4.28
|
||||
COMMAND
|
||||
sed "s/[0-9a-f]*.* //"
|
||||
COMMAND
|
||||
sed "s/ .*//"
|
||||
COMMAND
|
||||
sort -u
|
||||
OUTPUT_VARIABLE
|
||||
GLIBCXX_MET
|
||||
)
|
||||
if (NOT GLIBCXX_MET)
|
||||
message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \
|
||||
compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \
|
||||
to Qt by setting the variable Qt5_ROOT.")
|
||||
endif()
|
||||
|
||||
# Check for headers
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0)
|
||||
if (NOT QT_DEP_GLU_FOUND)
|
||||
message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \
|
||||
Perhaps `libglu1-mesa-dev` needs to be installed?")
|
||||
endif()
|
||||
pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8)
|
||||
if (NOT QT_DEP_MESA_FOUND)
|
||||
message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \
|
||||
Perhaps `mesa-common-dev` needs to be installed?")
|
||||
endif()
|
||||
|
||||
# Check for X libraries
|
||||
set(BUNDLED_QT_REQUIREMENTS
|
||||
libxcb-icccm.so.4
|
||||
libxcb-image.so.0
|
||||
libxcb-keysyms.so.1
|
||||
libxcb-randr.so.0
|
||||
libxcb-render-util.so.0
|
||||
libxcb-render.so.0
|
||||
libxcb-shape.so.0
|
||||
libxcb-shm.so.0
|
||||
libxcb-sync.so.1
|
||||
libxcb-xfixes.so.0
|
||||
libxcb-xinerama.so.0
|
||||
libxcb-xkb.so.1
|
||||
libxcb.so.1
|
||||
libxkbcommon-x11.so.0
|
||||
libxkbcommon.so.0
|
||||
)
|
||||
set(UNRESOLVED_QT_DEPS "")
|
||||
foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS})
|
||||
find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT})
|
||||
if ("${BUNDLED_QT_${REQUIREMENT}}" STREQUAL "BUNDLED_QT_${REQUIREMENT}-NOTFOUND")
|
||||
set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT})
|
||||
endif()
|
||||
unset(BUNDLED_QT_${REQUIREMENT})
|
||||
endforeach()
|
||||
unset(BUNDLED_QT_REQUIREMENTS)
|
||||
|
||||
if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "")
|
||||
message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}")
|
||||
endif()
|
||||
|
||||
set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE)
|
||||
endif()
|
||||
if (YUZU_USE_BUNDLED_QT)
|
||||
# Binary package currently does not support Qt webengine, so make sure it's disabled
|
||||
set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE)
|
||||
endif()
|
||||
set_yuzu_qt_components()
|
||||
if (ENABLE_QT6)
|
||||
find_package(Qt6 ${QT6_VERSION} COMPONENTS ${YUZU_QT_COMPONENTS})
|
||||
endif()
|
||||
|
||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH)
|
||||
|
||||
if(YUZU_USE_BUNDLED_QT)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
||||
set(QT_BUILD qt-5.15.2-msvc2019_64)
|
||||
elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64)
|
||||
set(QT_BUILD qt5_5_15_2)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
|
||||
endif()
|
||||
|
||||
if (DEFINED QT_BUILD)
|
||||
download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX)
|
||||
endif()
|
||||
|
||||
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
|
||||
|
||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
|
||||
endif()
|
||||
if (UNIX AND NOT APPLE AND YUZU_USE_BUNDLED_QT)
|
||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||
if (Qt6_FOUND)
|
||||
message(STATUS "yuzu/CMakeLists.txt: Qt6Widgets_VERSION ${Qt6Widgets_VERSION}, setting QT_VERSION")
|
||||
set(QT_VERSION ${Qt6Widgets_VERSION})
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
# Qt6 sets cxx_std_17 and we need to undo that
|
||||
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
|
||||
else()
|
||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||
endif()
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
message(STATUS "yuzu/CMakeLists.txt: Qt6 not found/not selected, trying for Qt5")
|
||||
# When Qt6 partially found, need this set to use Qt5 when not specifying version
|
||||
set(QT_DEFAULT_MAJOR_VERSION 5)
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
|
||||
set(YUZU_USE_QT_MULTIMEDIA ON)
|
||||
# Check for system Qt on Linux, fallback to bundled Qt
|
||||
if (UNIX AND NOT APPLE)
|
||||
if (NOT YUZU_USE_BUNDLED_QT)
|
||||
find_package(Qt5 ${QT5_VERSION} COMPONENTS Widgets DBus Multimedia)
|
||||
endif()
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
|
||||
# Check for dependencies, then enable bundled Qt download
|
||||
|
||||
# Check that the system GLIBCXX version is compatible
|
||||
find_program(OBJDUMP objdump)
|
||||
if (NOT OBJDUMP)
|
||||
message(FATAL_ERROR "Required program `objdump` not found.")
|
||||
endif()
|
||||
find_library(LIBSTDCXX libstdc++.so.6)
|
||||
execute_process(
|
||||
COMMAND
|
||||
${OBJDUMP} -T ${LIBSTDCXX}
|
||||
COMMAND
|
||||
grep GLIBCXX_3.4.28
|
||||
COMMAND
|
||||
sed "s/[0-9a-f]*.* //"
|
||||
COMMAND
|
||||
sed "s/ .*//"
|
||||
COMMAND
|
||||
sort -u
|
||||
OUTPUT_VARIABLE
|
||||
GLIBCXX_MET
|
||||
)
|
||||
if (NOT GLIBCXX_MET)
|
||||
message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \
|
||||
compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \
|
||||
to Qt by setting the variable Qt5_ROOT.")
|
||||
endif()
|
||||
|
||||
# Check for headers
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0)
|
||||
if (NOT QT_DEP_GLU_FOUND)
|
||||
message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \
|
||||
Perhaps `libglu1-mesa-dev` needs to be installed?")
|
||||
endif()
|
||||
pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8)
|
||||
if (NOT QT_DEP_MESA_FOUND)
|
||||
message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \
|
||||
Perhaps `mesa-common-dev` needs to be installed?")
|
||||
endif()
|
||||
|
||||
# Check for X libraries
|
||||
set(BUNDLED_QT_REQUIREMENTS
|
||||
libxcb-icccm.so.4
|
||||
libxcb-image.so.0
|
||||
libxcb-keysyms.so.1
|
||||
libxcb-randr.so.0
|
||||
libxcb-render-util.so.0
|
||||
libxcb-render.so.0
|
||||
libxcb-shape.so.0
|
||||
libxcb-shm.so.0
|
||||
libxcb-sync.so.1
|
||||
libxcb-xfixes.so.0
|
||||
libxcb-xinerama.so.0
|
||||
libxcb-xkb.so.1
|
||||
libxcb.so.1
|
||||
libxkbcommon-x11.so.0
|
||||
libxkbcommon.so.0
|
||||
)
|
||||
set(UNRESOLVED_QT_DEPS "")
|
||||
foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS})
|
||||
find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT})
|
||||
if (NOT BUNDLED_QT_${REQUIREMENT})
|
||||
set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT})
|
||||
endif()
|
||||
unset(BUNDLED_QT_${REQUIREMENT})
|
||||
endforeach()
|
||||
unset(BUNDLED_QT_REQUIREMENTS)
|
||||
|
||||
if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "")
|
||||
message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}")
|
||||
endif()
|
||||
|
||||
set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE)
|
||||
endif()
|
||||
if (YUZU_USE_BUNDLED_QT)
|
||||
# Binary package currently does not support Qt webengine, so make sure it's disabled
|
||||
set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH)
|
||||
|
||||
if(YUZU_USE_BUNDLED_QT)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
||||
set(QT_BUILD qt-5.15.2-msvc2019_64)
|
||||
elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64)
|
||||
set(QT_BUILD qt5_5_15_2)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
|
||||
endif()
|
||||
|
||||
if (DEFINED QT_BUILD)
|
||||
download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX)
|
||||
endif()
|
||||
|
||||
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
|
||||
|
||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
|
||||
# Binary package for Qt5 has Qt Multimedia
|
||||
set(YUZU_USE_QT_MULTIMEDIA ON CACHE BOOL "Use Qt Multimedia" FORCE)
|
||||
endif()
|
||||
|
||||
set_yuzu_qt_components()
|
||||
find_package(Qt5 ${QT5_VERSION} COMPONENTS ${YUZU_QT_COMPONENTS} ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT_TRANSLATION)
|
||||
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the yuzu_find_package
|
||||
|
||||
@@ -27,10 +27,13 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
Qt5Core$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Network$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
|
||||
if (YUZU_USE_QT_MULTIMEDIA)
|
||||
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
|
||||
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
endif()
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
|
||||
Qt5Network$<$<CONFIG:Debug>:d>.*
|
||||
|
||||
24
externals/CMakeLists.txt
vendored
24
externals/CMakeLists.txt
vendored
@@ -7,15 +7,14 @@ include(DownloadExternals)
|
||||
|
||||
# xbyak
|
||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
add_library(xbyak INTERFACE)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
||||
add_subdirectory(xbyak)
|
||||
endif()
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
if (ARCHITECTURE_arm64)
|
||||
set(DYNARMIC_FRONTENDS "A32")
|
||||
endif()
|
||||
set(DYNARMIC_NO_BUNDLED_FMT ON)
|
||||
set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(dynarmic)
|
||||
@@ -70,7 +69,6 @@ if (YUZU_USE_EXTERNAL_SDL2)
|
||||
set(SDL_SHARED OFF)
|
||||
|
||||
add_subdirectory(SDL EXCLUDE_FROM_ALL)
|
||||
add_library(SDL2 ALIAS SDL2-static)
|
||||
endif()
|
||||
|
||||
# ENet
|
||||
@@ -93,10 +91,14 @@ endif()
|
||||
add_subdirectory(sirit)
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
find_package(OpenSSL 1.1)
|
||||
if (OPENSSL_FOUND)
|
||||
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||
else()
|
||||
if (NOT WIN32)
|
||||
find_package(OpenSSL 1.1)
|
||||
if (OPENSSL_FOUND)
|
||||
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32 OR NOT OPENSSL_FOUND)
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
set(OPENSSLDIR "/etc/ssl/")
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 2d4602a651...07c614f91b
4
externals/microprofile/microprofileui.h
vendored
4
externals/microprofile/microprofileui.h
vendored
@@ -845,8 +845,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
MicroProfile& S = *MicroProfileGet();
|
||||
MP_DEBUG_DUMP_RANGE();
|
||||
int nY = nBaseY - UI.nOffsetY;
|
||||
int64_t nNumBoxes = 0;
|
||||
int64_t nNumLines = 0;
|
||||
|
||||
uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
|
||||
MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
|
||||
@@ -1149,7 +1147,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
}
|
||||
}
|
||||
#endif
|
||||
++nNumBoxes;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1165,7 +1162,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
}
|
||||
nLinesDrawn[nStackPos] = nLineX;
|
||||
MicroProfileDrawLineVertical(nLineX, fYStart + 0.5f, fYEnd + 0.5f, nColor|UI.nOpacityForeground);
|
||||
++nNumLines;
|
||||
}
|
||||
}
|
||||
nStackPos--;
|
||||
|
||||
2
externals/xbyak
vendored
2
externals/xbyak
vendored
Submodule externals/xbyak updated: c306b8e578...348e3e548e
@@ -217,7 +217,7 @@ else()
|
||||
endif()
|
||||
|
||||
target_link_libraries(audio_core PUBLIC common core)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_link_libraries(audio_core PRIVATE dynarmic)
|
||||
endif()
|
||||
|
||||
@@ -226,6 +226,10 @@ if(ENABLE_CUBEB)
|
||||
target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
|
||||
endif()
|
||||
if(ENABLE_SDL2)
|
||||
target_link_libraries(audio_core PRIVATE SDL2)
|
||||
if (YUZU_USE_EXTERNAL_SDL2)
|
||||
target_link_libraries(audio_core PRIVATE SDL2-static)
|
||||
else()
|
||||
target_link_libraries(audio_core PRIVATE SDL2)
|
||||
endif()
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
@@ -460,21 +460,23 @@ void CommandBuffer::GenerateDeviceSinkCommand(const s32 node_id, const s16 buffe
|
||||
|
||||
cmd.session_id = session_id;
|
||||
|
||||
cmd.input_count = parameter.input_count;
|
||||
s16 max_input{0};
|
||||
for (u32 i = 0; i < parameter.input_count; i++) {
|
||||
cmd.inputs[i] = buffer_offset + parameter.inputs[i];
|
||||
max_input = std::max(max_input, cmd.inputs[i]);
|
||||
}
|
||||
|
||||
if (state.upsampler_info != nullptr) {
|
||||
const auto size_{state.upsampler_info->sample_count * parameter.input_count};
|
||||
const auto size_bytes{size_ * sizeof(s32)};
|
||||
const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)};
|
||||
cmd.sample_buffer = {reinterpret_cast<s32*>(addr),
|
||||
parameter.input_count * state.upsampler_info->sample_count};
|
||||
(max_input + 1) * state.upsampler_info->sample_count};
|
||||
} else {
|
||||
cmd.sample_buffer = samples_buffer;
|
||||
}
|
||||
|
||||
cmd.input_count = parameter.input_count;
|
||||
for (u32 i = 0; i < parameter.input_count; i++) {
|
||||
cmd.inputs[i] = buffer_offset + parameter.inputs[i];
|
||||
}
|
||||
|
||||
GenerateEnd<DeviceSinkCommand>(cmd);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) {
|
||||
impl = std::make_unique<
|
||||
PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,
|
||||
PerformanceEntryVersion1, PerformanceDetailVersion1>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -266,19 +266,20 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
}
|
||||
|
||||
void SinkStream::Stall() {
|
||||
if (stalled) {
|
||||
std::scoped_lock lk{stall_guard};
|
||||
if (stalled_lock) {
|
||||
return;
|
||||
}
|
||||
stalled = true;
|
||||
system.StallProcesses();
|
||||
stalled_lock = system.StallProcesses();
|
||||
}
|
||||
|
||||
void SinkStream::Unstall() {
|
||||
if (!stalled) {
|
||||
std::scoped_lock lk{stall_guard};
|
||||
if (!stalled_lock) {
|
||||
return;
|
||||
}
|
||||
system.UnstallProcesses();
|
||||
stalled = false;
|
||||
stalled_lock.unlock();
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
@@ -240,8 +241,8 @@ private:
|
||||
f32 system_volume{1.0f};
|
||||
/// Set via IAudioDevice service calls
|
||||
f32 device_volume{1.0f};
|
||||
/// True if coretiming has been stalled
|
||||
bool stalled{false};
|
||||
std::mutex stall_guard;
|
||||
std::unique_lock<std::mutex> stalled_lock;
|
||||
};
|
||||
|
||||
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
||||
|
||||
@@ -34,6 +34,8 @@ add_library(common STATIC
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
cache_management.cpp
|
||||
cache_management.h
|
||||
common_funcs.h
|
||||
common_types.h
|
||||
concepts.h
|
||||
|
||||
@@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
59
src/common/cache_management.cpp
Normal file
59
src/common/cache_management.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/cache_management.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
|
||||
// Most cache operations are no-ops on x86
|
||||
|
||||
void DataCacheLineCleanByVAToPoU(void* start, size_t size) {}
|
||||
void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {}
|
||||
void DataCacheLineCleanByVAToPoC(void* start, size_t size) {}
|
||||
void DataCacheZeroByVA(void* start, size_t size) {
|
||||
std::memset(start, 0, size);
|
||||
}
|
||||
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
|
||||
// BS/DminLine is log2(cache size in words), we want size in bytes
|
||||
#define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2))
|
||||
#define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2))
|
||||
|
||||
#define DEFINE_DC_OP(op_name, function_name) \
|
||||
void function_name(void* start, size_t size) { \
|
||||
size_t ctr_el0; \
|
||||
asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \
|
||||
size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \
|
||||
uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
|
||||
uintptr_t va_end = va_start + size; \
|
||||
for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
|
||||
asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DEFINE_DC_OP_DCZID(op_name, function_name) \
|
||||
void function_name(void* start, size_t size) { \
|
||||
size_t dczid_el0; \
|
||||
asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \
|
||||
size_t cacheline_size = EXTRACT_BS(dczid_el0); \
|
||||
uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
|
||||
uintptr_t va_end = va_start + size; \
|
||||
for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
|
||||
asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU);
|
||||
DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC);
|
||||
DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC);
|
||||
DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
27
src/common/cache_management.h
Normal file
27
src/common/cache_management.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Data cache instructions enabled at EL0 by SCTLR_EL1.UCI.
|
||||
// VA = virtual address
|
||||
// PoC = point of coherency
|
||||
// PoU = point of unification
|
||||
|
||||
// dc cvau
|
||||
void DataCacheLineCleanByVAToPoU(void* start, size_t size);
|
||||
|
||||
// dc civac
|
||||
void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size);
|
||||
|
||||
// dc cvac
|
||||
void DataCacheLineCleanByVAToPoC(void* start, size_t size);
|
||||
|
||||
// dc zva
|
||||
void DataCacheZeroByVA(void* start, size_t size);
|
||||
|
||||
} // namespace Common
|
||||
@@ -31,8 +31,10 @@
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
#define Crash() __asm__ __volatile__("int $3")
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
#define Crash() __asm__ __volatile__("brk #0")
|
||||
#else
|
||||
#define Crash() exit(1)
|
||||
#endif
|
||||
|
||||
@@ -359,6 +359,12 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
long page_size = sysconf(_SC_PAGESIZE);
|
||||
if (page_size != 0x1000) {
|
||||
LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
// Backing memory initialization
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
|
||||
@@ -383,6 +383,16 @@ void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDevic
|
||||
}
|
||||
}
|
||||
|
||||
inline void RegisterInputFactory(const std::string& name,
|
||||
std::shared_ptr<Factory<InputDevice>> factory) {
|
||||
RegisterFactory<InputDevice>(name, std::move(factory));
|
||||
}
|
||||
|
||||
inline void RegisterOutputFactory(const std::string& name,
|
||||
std::shared_ptr<Factory<OutputDevice>> factory) {
|
||||
RegisterFactory<OutputDevice>(name, std::move(factory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters an input device factory.
|
||||
* @tparam InputDeviceType the type of input devices the factory can create
|
||||
@@ -395,6 +405,14 @@ void UnregisterFactory(const std::string& name) {
|
||||
}
|
||||
}
|
||||
|
||||
inline void UnregisterInputFactory(const std::string& name) {
|
||||
UnregisterFactory<InputDevice>(name);
|
||||
}
|
||||
|
||||
inline void UnregisterOutputFactory(const std::string& name) {
|
||||
UnregisterFactory<OutputDevice>(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an input device from given paramters.
|
||||
* @tparam InputDeviceType the type of input devices to create
|
||||
@@ -416,13 +434,21 @@ std::unique_ptr<InputDeviceType> CreateDeviceFromString(const std::string& param
|
||||
return pair->second->Create(package);
|
||||
}
|
||||
|
||||
inline std::unique_ptr<InputDevice> CreateInputDeviceFromString(const std::string& params) {
|
||||
return CreateDeviceFromString<InputDevice>(params);
|
||||
}
|
||||
|
||||
inline std::unique_ptr<OutputDevice> CreateOutputDeviceFromString(const std::string& params) {
|
||||
return CreateDeviceFromString<OutputDevice>(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an input device from given paramters.
|
||||
* Create an input device from given parameters.
|
||||
* @tparam InputDeviceType the type of input devices to create
|
||||
* @param A ParamPackage that contains all parameters for creating the device
|
||||
* @param package A ParamPackage that contains all parameters for creating the device
|
||||
*/
|
||||
template <typename InputDeviceType>
|
||||
std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package) {
|
||||
std::unique_ptr<InputDeviceType> CreateDevice(const ParamPackage& package) {
|
||||
const std::string engine = package.Get("engine", "null");
|
||||
const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
|
||||
const auto pair = factory_list.find(engine);
|
||||
@@ -435,4 +461,12 @@ std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package
|
||||
return pair->second->Create(package);
|
||||
}
|
||||
|
||||
inline std::unique_ptr<InputDevice> CreateInputDevice(const ParamPackage& package) {
|
||||
return CreateDevice<InputDevice>(package);
|
||||
}
|
||||
|
||||
inline std::unique_ptr<OutputDevice> CreateOutputDevice(const ParamPackage& package) {
|
||||
return CreateDevice<OutputDevice>(package);
|
||||
}
|
||||
|
||||
} // namespace Common::Input
|
||||
|
||||
@@ -48,6 +48,7 @@ void LogSettings() {
|
||||
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
|
||||
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
|
||||
log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
|
||||
log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue());
|
||||
log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue());
|
||||
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
|
||||
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
|
||||
@@ -151,6 +152,7 @@ void UpdateRescalingInfo() {
|
||||
ASSERT(false);
|
||||
info.up_scale = 1;
|
||||
info.down_shift = 0;
|
||||
break;
|
||||
}
|
||||
info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
|
||||
info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale;
|
||||
@@ -180,6 +182,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
|
||||
|
||||
// Renderer
|
||||
values.fsr_sharpening_slider.SetGlobal(true);
|
||||
values.renderer_backend.SetGlobal(true);
|
||||
values.vulkan_device.SetGlobal(true);
|
||||
values.aspect_ratio.SetGlobal(true);
|
||||
|
||||
@@ -421,6 +421,7 @@ struct Values {
|
||||
ResolutionScalingInfo resolution_info{};
|
||||
SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
|
||||
SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
|
||||
SwitchableSetting<int, true> fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"};
|
||||
SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
|
||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||
// Default to exclusive fullscreen on these platforms for now.
|
||||
@@ -442,7 +443,7 @@ struct Values {
|
||||
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
|
||||
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
|
||||
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
|
||||
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
|
||||
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
|
||||
ShaderBackend::SPIRV, "shader_backend"};
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
|
||||
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
|
||||
|
||||
@@ -4,14 +4,27 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/x64/cpu_detect.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
||||
static inline u64 xgetbv(u32 index) {
|
||||
return _xgetbv(index);
|
||||
}
|
||||
#else
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
@@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) {
|
||||
}
|
||||
|
||||
#define _XCR_XFEATURE_ENABLED_MASK 0
|
||||
static inline u64 _xgetbv(u32 index) {
|
||||
static inline u64 xgetbv(u32 index) {
|
||||
u32 eax, edx;
|
||||
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
|
||||
return ((u64)edx << 32) | eax;
|
||||
}
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
namespace Common {
|
||||
@@ -107,7 +119,7 @@ static CPUCaps Detect() {
|
||||
// - Is the XSAVE bit set in CPUID?
|
||||
// - XGETBV result has the XCR bit set.
|
||||
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
|
||||
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
caps.avx = true;
|
||||
if (Common::Bit<12>(cpu_id[2]))
|
||||
caps.fma = true;
|
||||
@@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() {
|
||||
return caps;
|
||||
}
|
||||
|
||||
std::optional<int> GetProcessorCount() {
|
||||
#if defined(_WIN32)
|
||||
// Get the buffer length.
|
||||
DWORD length = 0;
|
||||
GetLogicalProcessorInformation(nullptr, &length);
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
LOG_ERROR(Frontend, "Failed to query core count.");
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(
|
||||
length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
|
||||
// Now query the core count.
|
||||
if (!GetLogicalProcessorInformation(buffer.data(), &length)) {
|
||||
LOG_ERROR(Frontend, "Failed to query core count.");
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<int>(
|
||||
std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) {
|
||||
return proc_info.Relationship == RelationProcessorCore;
|
||||
}));
|
||||
#elif defined(__unix__)
|
||||
const int thread_count = std::thread::hardware_concurrency();
|
||||
std::ifstream smt("/sys/devices/system/cpu/smt/active");
|
||||
char state = '0';
|
||||
if (smt) {
|
||||
smt.read(&state, sizeof(state));
|
||||
}
|
||||
switch (state) {
|
||||
case '0':
|
||||
return thread_count;
|
||||
case '1':
|
||||
return thread_count / 2;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
#else
|
||||
// Shame on you
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -74,4 +75,7 @@ struct CPUCaps {
|
||||
*/
|
||||
const CPUCaps& GetCPUCaps();
|
||||
|
||||
/// Detects CPU core count
|
||||
std::optional<int> GetProcessorCount();
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -120,6 +120,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_vector.h
|
||||
file_sys/xts_archive.cpp
|
||||
file_sys/xts_archive.h
|
||||
frontend/applets/cabinet.cpp
|
||||
frontend/applets/cabinet.h
|
||||
frontend/applets/controller.cpp
|
||||
frontend/applets/controller.h
|
||||
frontend/applets/error.cpp
|
||||
@@ -190,11 +192,13 @@ add_library(core STATIC
|
||||
hle/kernel/k_code_memory.h
|
||||
hle/kernel/k_condition_variable.cpp
|
||||
hle/kernel/k_condition_variable.h
|
||||
hle/kernel/k_debug.h
|
||||
hle/kernel/k_dynamic_page_manager.h
|
||||
hle/kernel/k_dynamic_resource_manager.h
|
||||
hle/kernel/k_dynamic_slab_heap.h
|
||||
hle/kernel/k_event.cpp
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_event_info.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
@@ -222,6 +226,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_page_group.h
|
||||
hle/kernel/k_page_table.cpp
|
||||
hle/kernel/k_page_table.h
|
||||
hle/kernel/k_page_table_manager.h
|
||||
hle/kernel/k_page_table_slab_heap.h
|
||||
hle/kernel/k_port.cpp
|
||||
hle/kernel/k_port.h
|
||||
hle/kernel/k_priority_queue.h
|
||||
@@ -254,6 +260,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_synchronization_object.cpp
|
||||
hle/kernel/k_synchronization_object.h
|
||||
hle/kernel/k_system_control.h
|
||||
hle/kernel/k_system_resource.cpp
|
||||
hle/kernel/k_system_resource.h
|
||||
hle/kernel/k_thread.cpp
|
||||
hle/kernel/k_thread.h
|
||||
hle/kernel/k_thread_local_page.cpp
|
||||
@@ -306,6 +314,8 @@ add_library(core STATIC
|
||||
hle/service/am/applet_ae.h
|
||||
hle/service/am/applet_oe.cpp
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/am/applets/applet_cabinet.cpp
|
||||
hle/service/am/applets/applet_cabinet.h
|
||||
hle/service/am/applets/applet_controller.cpp
|
||||
hle/service/am/applets/applet_controller.h
|
||||
hle/service/am/applets/applet_error.cpp
|
||||
@@ -491,10 +501,6 @@ add_library(core STATIC
|
||||
hle/service/hid/irsensor/processor_base.h
|
||||
hle/service/hid/irsensor/tera_plugin_processor.cpp
|
||||
hle/service/hid/irsensor/tera_plugin_processor.h
|
||||
hle/service/jit/jit_context.cpp
|
||||
hle/service/jit/jit_context.h
|
||||
hle/service/jit/jit.cpp
|
||||
hle/service/jit/jit.h
|
||||
hle/service/lbl/lbl.cpp
|
||||
hle/service/lbl/lbl.h
|
||||
hle/service/ldn/lan_discovery.cpp
|
||||
@@ -524,6 +530,11 @@ add_library(core STATIC
|
||||
hle/service/ncm/ncm.h
|
||||
hle/service/nfc/nfc.cpp
|
||||
hle/service/nfc/nfc.h
|
||||
hle/service/nfc/nfc_device.cpp
|
||||
hle/service/nfc/nfc_device.h
|
||||
hle/service/nfc/nfc_result.h
|
||||
hle/service/nfc/nfc_user.cpp
|
||||
hle/service/nfc/nfc_user.h
|
||||
hle/service/nfp/amiibo_crypto.cpp
|
||||
hle/service/nfp/amiibo_crypto.h
|
||||
hle/service/nfp/nfp.cpp
|
||||
@@ -799,14 +810,18 @@ if (ENABLE_WEB_SERVICE)
|
||||
target_link_libraries(core PRIVATE web_service)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic_32.cpp
|
||||
arm/dynarmic/arm_dynarmic_32.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
arm/dynarmic/arm_dynarmic_64.h
|
||||
arm/dynarmic/arm_dynarmic_32.cpp
|
||||
arm/dynarmic/arm_dynarmic_32.h
|
||||
arm/dynarmic/arm_dynarmic_cp15.cpp
|
||||
arm/dynarmic/arm_dynarmic_cp15.h
|
||||
hle/service/jit/jit_context.cpp
|
||||
hle/service/jit/jit_context.h
|
||||
hle/service/jit/jit.cpp
|
||||
hle/service/jit/jit.h
|
||||
)
|
||||
target_link_libraries(core PRIVATE dynarmic)
|
||||
endif()
|
||||
|
||||
@@ -301,6 +301,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
// TODO: remove when fixed in dynarmic
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
|
||||
#endif
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -348,7 +348,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
|
||||
config.unsafe_optimizations = true;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
|
||||
@@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
|
||||
case 4:
|
||||
// CP15_DATA_SYNC_BARRIER
|
||||
return Callback{
|
||||
[](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#ifdef _MSC_VER
|
||||
[](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
|
||||
_mm_mfence();
|
||||
_mm_lfence();
|
||||
#else
|
||||
#elif defined(ARCHITECTURE_x86_64)
|
||||
asm volatile("mfence\n\tlfence\n\t" : : : "memory");
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
asm volatile("dsb sy\n\t" : : : "memory");
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
return 0;
|
||||
},
|
||||
@@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
|
||||
case 5:
|
||||
// CP15_DATA_MEMORY_BARRIER
|
||||
return Callback{
|
||||
[](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#ifdef _MSC_VER
|
||||
[](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
|
||||
_mm_mfence();
|
||||
#else
|
||||
#elif defined(ARCHITECTURE_x86_64)
|
||||
asm volatile("mfence\n\t" : : : "memory");
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
asm volatile("dmb sy\n\t" : : : "memory");
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
return 0;
|
||||
},
|
||||
@@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1,
|
||||
CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
|
||||
if (!two && opc == 0 && CRm == CoprocReg::C14) {
|
||||
// CNTPCT
|
||||
const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 {
|
||||
const auto callback = [](void* arg, u32, u32) -> u64 {
|
||||
const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg);
|
||||
return parent_arg.system.CoreTiming().GetClockTicks();
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#endif
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
@@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default;
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
|
||||
std::size_t num_cores) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
|
||||
#else
|
||||
// TODO(merry): Passthrough exclusive monitor
|
||||
|
||||
@@ -189,7 +189,7 @@ struct System::Impl {
|
||||
|
||||
kernel.Suspend(false);
|
||||
core_timing.SyncPause(false);
|
||||
is_paused = false;
|
||||
is_paused.store(false, std::memory_order_relaxed);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -200,14 +200,13 @@ struct System::Impl {
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
kernel.Suspend(true);
|
||||
is_paused = true;
|
||||
is_paused.store(true, std::memory_order_relaxed);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool IsPaused() const {
|
||||
std::unique_lock lk(suspend_guard);
|
||||
return is_paused;
|
||||
return is_paused.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> StallProcesses() {
|
||||
@@ -218,7 +217,7 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
void UnstallProcesses() {
|
||||
if (!is_paused) {
|
||||
if (!IsPaused()) {
|
||||
core_timing.SyncPause(false);
|
||||
kernel.Suspend(false);
|
||||
}
|
||||
@@ -465,7 +464,7 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
mutable std::mutex suspend_guard;
|
||||
bool is_paused{};
|
||||
std::atomic_bool is_paused{};
|
||||
std::atomic<bool> is_shutting_down{};
|
||||
|
||||
Timing::CoreTiming core_timing;
|
||||
|
||||
@@ -578,18 +578,18 @@ KeyManager::KeyManager() {
|
||||
|
||||
if (Settings::values.use_dev_keys) {
|
||||
dev_mode = true;
|
||||
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
|
||||
LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
|
||||
} else {
|
||||
dev_mode = false;
|
||||
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
|
||||
LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
|
||||
}
|
||||
|
||||
LoadFromFile(yuzu_keys_dir / "title.keys", true);
|
||||
LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true);
|
||||
LoadFromFile(yuzu_keys_dir / "console.keys", false);
|
||||
LoadFromFile(yuzu_keys_dir / "title.keys", true);
|
||||
LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "console.keys", false);
|
||||
}
|
||||
|
||||
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
|
||||
|
||||
@@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
c(received_data);
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
}
|
||||
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) {
|
||||
acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) {
|
||||
if (!error.failed()) {
|
||||
c(peer_socket);
|
||||
AsyncAccept(acceptor, c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Readable, typename Buffer>
|
||||
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
@@ -59,9 +68,7 @@ namespace Core {
|
||||
|
||||
class DebuggerImpl : public DebuggerBackend {
|
||||
public:
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port)
|
||||
: system{system_}, signal_pipe{io_context}, client_socket{io_context} {
|
||||
frontend = std::make_unique<GDBStub>(*this, system);
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
|
||||
InitializeServer(port);
|
||||
}
|
||||
|
||||
@@ -70,39 +77,42 @@ public:
|
||||
}
|
||||
|
||||
bool SignalDebugger(SignalInfo signal_info) {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
if (stopped) {
|
||||
// Do not notify the debugger about another event.
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up the state.
|
||||
stopped = true;
|
||||
info = signal_info;
|
||||
if (stopped || !state) {
|
||||
// Do not notify the debugger about another event.
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up the state.
|
||||
stopped = true;
|
||||
state->info = signal_info;
|
||||
|
||||
// Write a single byte into the pipe to wake up the debug interface.
|
||||
boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
|
||||
boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// These functions are callbacks from the frontend, and the lock will be held.
|
||||
// There is no need to relock it.
|
||||
|
||||
std::span<const u8> ReadFromClient() override {
|
||||
return ReceiveInto(client_socket, client_data);
|
||||
return ReceiveInto(state->client_socket, state->client_data);
|
||||
}
|
||||
|
||||
void WriteToClient(std::span<const u8> data) override {
|
||||
boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
boost::asio::write(state->client_socket,
|
||||
boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
}
|
||||
|
||||
void SetActiveThread(Kernel::KThread* thread) override {
|
||||
active_thread = thread;
|
||||
state->active_thread = thread;
|
||||
}
|
||||
|
||||
Kernel::KThread* GetActiveThread() override {
|
||||
return active_thread;
|
||||
return state->active_thread;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -113,65 +123,78 @@ private:
|
||||
|
||||
// Run the connection thread.
|
||||
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Debugger");
|
||||
|
||||
try {
|
||||
// Initialize the listening socket and accept a new client.
|
||||
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
|
||||
tcp::acceptor acceptor{io_context, endpoint};
|
||||
|
||||
acceptor.async_accept(client_socket, [](const auto&) {});
|
||||
io_context.run_one();
|
||||
io_context.restart();
|
||||
AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
|
||||
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
while (!stop_token.stop_requested() && io_context.run()) {
|
||||
}
|
||||
|
||||
ThreadLoop(stop_token);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
|
||||
LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
|
||||
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
// Ensure everything is stopped.
|
||||
PauseEmulation();
|
||||
|
||||
// Set up the new frontend.
|
||||
frontend = std::make_unique<GDBStub>(*this, system);
|
||||
|
||||
// Set the new state. This will tear down any existing state.
|
||||
state = ConnectionState{
|
||||
.client_socket{std::move(peer)},
|
||||
.signal_pipe{io_context},
|
||||
.info{},
|
||||
.active_thread{},
|
||||
.client_data{},
|
||||
.pipe_data{},
|
||||
};
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Set the active thread.
|
||||
UpdateActiveThread();
|
||||
|
||||
// Set up the frontend.
|
||||
frontend->Connected();
|
||||
}
|
||||
|
||||
void ShutdownServer() {
|
||||
connection_thread.request_stop();
|
||||
io_context.stop();
|
||||
connection_thread.join();
|
||||
}
|
||||
|
||||
void ThreadLoop(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Debugger");
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Set the active thread.
|
||||
UpdateActiveThread();
|
||||
|
||||
// Set up the frontend.
|
||||
frontend->Connected();
|
||||
|
||||
// Main event loop.
|
||||
while (!stop_token.stop_requested() && io_context.run()) {
|
||||
}
|
||||
}
|
||||
|
||||
void PipeData(std::span<const u8> data) {
|
||||
switch (info.type) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
switch (state->info.type) {
|
||||
case SignalType::Stopped:
|
||||
case SignalType::Watchpoint:
|
||||
// Stop emulation.
|
||||
PauseEmulation();
|
||||
|
||||
// Notify the client.
|
||||
active_thread = info.thread;
|
||||
state->active_thread = state->info.thread;
|
||||
UpdateActiveThread();
|
||||
|
||||
if (info.type == SignalType::Watchpoint) {
|
||||
frontend->Watchpoint(active_thread, *info.watchpoint);
|
||||
if (state->info.type == SignalType::Watchpoint) {
|
||||
frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
|
||||
} else {
|
||||
frontend->Stopped(active_thread);
|
||||
frontend->Stopped(state->active_thread);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -179,8 +202,8 @@ private:
|
||||
frontend->ShuttingDown();
|
||||
|
||||
// Wait for emulation to shut down gracefully now.
|
||||
signal_pipe.close();
|
||||
client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||
state->signal_pipe.close();
|
||||
state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||
LOG_INFO(Debug_GDBStub, "Shut down server");
|
||||
|
||||
break;
|
||||
@@ -188,17 +211,16 @@ private:
|
||||
}
|
||||
|
||||
void ClientData(std::span<const u8> data) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
const auto actions{frontend->ClientData(data)};
|
||||
for (const auto action : actions) {
|
||||
switch (action) {
|
||||
case DebuggerAction::Interrupt: {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
stopped = true;
|
||||
}
|
||||
stopped = true;
|
||||
PauseEmulation();
|
||||
UpdateActiveThread();
|
||||
frontend->Stopped(active_thread);
|
||||
frontend->Stopped(state->active_thread);
|
||||
break;
|
||||
}
|
||||
case DebuggerAction::Continue:
|
||||
@@ -206,15 +228,15 @@ private:
|
||||
break;
|
||||
case DebuggerAction::StepThreadUnlocked:
|
||||
MarkResumed([&] {
|
||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
ResumeEmulation(active_thread);
|
||||
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
ResumeEmulation(state->active_thread);
|
||||
});
|
||||
break;
|
||||
case DebuggerAction::StepThreadLocked: {
|
||||
MarkResumed([&] {
|
||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -254,15 +276,14 @@ private:
|
||||
template <typename Callback>
|
||||
void MarkResumed(Callback&& cb) {
|
||||
Kernel::KScopedSchedulerLock sl{system.Kernel()};
|
||||
std::scoped_lock cl{connection_lock};
|
||||
stopped = false;
|
||||
cb();
|
||||
}
|
||||
|
||||
void UpdateActiveThread() {
|
||||
const auto& threads{ThreadList()};
|
||||
if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
|
||||
active_thread = threads[0];
|
||||
if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
|
||||
state->active_thread = threads[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,18 +295,22 @@ private:
|
||||
System& system;
|
||||
std::unique_ptr<DebuggerFrontend> frontend;
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
std::jthread connection_thread;
|
||||
std::mutex connection_lock;
|
||||
boost::asio::io_context io_context;
|
||||
boost::process::async_pipe signal_pipe;
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
|
||||
SignalInfo info;
|
||||
Kernel::KThread* active_thread;
|
||||
bool pipe_data;
|
||||
bool stopped;
|
||||
struct ConnectionState {
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
boost::process::async_pipe signal_pipe;
|
||||
|
||||
std::array<u8, 4096> client_data;
|
||||
SignalInfo info;
|
||||
Kernel::KThread* active_thread;
|
||||
std::array<u8, 4096> client_data;
|
||||
bool pipe_data;
|
||||
};
|
||||
|
||||
std::optional<ConnectionState> state{};
|
||||
bool stopped{};
|
||||
};
|
||||
|
||||
Debugger::Debugger(Core::System& system, u16 port) {
|
||||
|
||||
@@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
} else if (command.starts_with("StartNoAckMode")) {
|
||||
no_ack = true;
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
} else if (command.starts_with("Rcmd,")) {
|
||||
HandleRcmd(Common::HexStringToVector(command.substr(5), false));
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_EMPTY);
|
||||
}
|
||||
@@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
|
||||
}
|
||||
}
|
||||
|
||||
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
|
||||
{"----- Free -----", Kernel::Svc::MemoryState::Free},
|
||||
{"Io ", Kernel::Svc::MemoryState::Io},
|
||||
{"Static ", Kernel::Svc::MemoryState::Static},
|
||||
{"Code ", Kernel::Svc::MemoryState::Code},
|
||||
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
|
||||
{"Normal ", Kernel::Svc::MemoryState::Normal},
|
||||
{"Shared ", Kernel::Svc::MemoryState::Shared},
|
||||
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
|
||||
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
|
||||
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
|
||||
{"Stack ", Kernel::Svc::MemoryState::Stack},
|
||||
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
|
||||
{"Transfered ", Kernel::Svc::MemoryState::Transfered},
|
||||
{"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
|
||||
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
|
||||
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
|
||||
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
|
||||
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
|
||||
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
|
||||
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
|
||||
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
|
||||
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
|
||||
}};
|
||||
|
||||
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
|
||||
for (size_t i = 0; i < MemoryStateNames.size(); i++) {
|
||||
if (std::get<1>(MemoryStateNames[i]) == state) {
|
||||
return std::get<0>(MemoryStateNames[i]);
|
||||
}
|
||||
}
|
||||
return "Unknown ";
|
||||
}
|
||||
|
||||
static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
|
||||
if (info.state == Kernel::Svc::MemoryState::Free) {
|
||||
return " ";
|
||||
}
|
||||
|
||||
switch (info.permission) {
|
||||
case Kernel::Svc::MemoryPermission::ReadExecute:
|
||||
return "r-x";
|
||||
case Kernel::Svc::MemoryPermission::Read:
|
||||
return "r--";
|
||||
case Kernel::Svc::MemoryPermission::ReadWrite:
|
||||
return "rw-";
|
||||
default:
|
||||
return "---";
|
||||
}
|
||||
}
|
||||
|
||||
static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
|
||||
Kernel::Svc::MemoryInfo mem_info;
|
||||
VAddr cur_addr{base};
|
||||
|
||||
// Expect: r-x Code (.text)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
|
||||
mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
// Expect: r-- Code (.rodata)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
|
||||
mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
// Expect: rw- CodeData (.data)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
|
||||
std::string reply;
|
||||
|
||||
auto* process = system.CurrentProcess();
|
||||
auto& page_table = process->PageTable();
|
||||
|
||||
if (command_str == "get info") {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
|
||||
reply = fmt::format("Process: {:#x} ({})\n"
|
||||
"Program Id: {:#018x}\n",
|
||||
process->GetProcessID(), process->GetName(), process->GetProgramID());
|
||||
reply +=
|
||||
fmt::format("Layout:\n"
|
||||
" Alias: {:#012x} - {:#012x}\n"
|
||||
" Heap: {:#012x} - {:#012x}\n"
|
||||
" Aslr: {:#012x} - {:#012x}\n"
|
||||
" Stack: {:#012x} - {:#012x}\n"
|
||||
"Modules:\n",
|
||||
page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(),
|
||||
page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(),
|
||||
page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(),
|
||||
page_table.GetStackRegionStart(), page_table.GetStackRegionEnd());
|
||||
|
||||
for (const auto& [vaddr, name] : modules) {
|
||||
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
||||
GetModuleEnd(page_table, vaddr), name);
|
||||
}
|
||||
} else if (command_str == "get mappings") {
|
||||
reply = "Mappings:\n";
|
||||
VAddr cur_addr = 0;
|
||||
|
||||
while (true) {
|
||||
using MemoryAttribute = Kernel::Svc::MemoryAttribute;
|
||||
|
||||
auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
|
||||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
|
||||
const char* state = GetMemoryStateName(mem_info.state);
|
||||
const char* perm = GetMemoryPermissionString(mem_info);
|
||||
|
||||
const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
|
||||
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
||||
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
||||
|
||||
reply +=
|
||||
fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
|
||||
mem_info.base_address, mem_info.base_address + mem_info.size - 1,
|
||||
perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
|
||||
}
|
||||
|
||||
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
||||
if (next_address <= cur_addr) {
|
||||
break;
|
||||
}
|
||||
|
||||
cur_addr = next_address;
|
||||
}
|
||||
} else if (command_str == "help") {
|
||||
reply = "Commands:\n get info\n get mappings\n";
|
||||
} else {
|
||||
reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
|
||||
}
|
||||
|
||||
std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
|
||||
SendReply(Common::HexToString(reply_span, false));
|
||||
}
|
||||
|
||||
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
||||
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
|
||||
for (auto* thread : threads) {
|
||||
|
||||
@@ -32,6 +32,7 @@ private:
|
||||
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
|
||||
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
|
||||
void HandleQuery(std::string_view command);
|
||||
void HandleRcmd(const std::vector<u8>& command);
|
||||
void HandleBreakpointInsert(std::string_view command);
|
||||
void HandleBreakpointRemove(std::string_view command);
|
||||
std::vector<char>::const_iterator CommandEnd() const;
|
||||
|
||||
20
src/core/frontend/applets/cabinet.cpp
Normal file
20
src/core/frontend/applets/cabinet.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
CabinetApplet::~CabinetApplet() = default;
|
||||
|
||||
void DefaultCabinetApplet::ShowCabinetApplet(
|
||||
const CabinetCallback& callback, const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
callback(false, {});
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
37
src/core/frontend/applets/cabinet.h
Normal file
37
src/core/frontend/applets/cabinet.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
} // namespace Service::NFP
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
struct CabinetParameters {
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
Service::NFP::CabinetMode mode;
|
||||
};
|
||||
|
||||
using CabinetCallback = std::function<void(bool, const std::string&)>;
|
||||
|
||||
class CabinetApplet {
|
||||
public:
|
||||
virtual ~CabinetApplet();
|
||||
virtual void ShowCabinetApplet(const CabinetCallback& callback,
|
||||
const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
|
||||
};
|
||||
|
||||
class DefaultCabinetApplet final : public CabinetApplet {
|
||||
public:
|
||||
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() {
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetTouchParams() {
|
||||
// TODO(german77): Support any number of fingers
|
||||
std::size_t index = 0;
|
||||
|
||||
// Hardcode mouse, touchscreen and cemuhook parameters
|
||||
// We can't use mouse as touch if native mouse is enabled
|
||||
if (!Settings::values.mouse_enabled) {
|
||||
// We can't use mouse as touch if native mouse is enabled
|
||||
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
|
||||
}
|
||||
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
|
||||
|
||||
for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
|
||||
Common::ParamPackage touchscreen_param{};
|
||||
touchscreen_param.Set("engine", "touch");
|
||||
touchscreen_param.Set("axis_x", i * 2);
|
||||
touchscreen_param.Set("axis_y", (i * 2) + 1);
|
||||
touchscreen_param.Set("button", i);
|
||||
touch_params[index++] = touchscreen_param;
|
||||
}
|
||||
|
||||
const auto button_index =
|
||||
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
|
||||
@@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() {
|
||||
|
||||
// Map the rest of the fingers from touch from button configuration
|
||||
for (const auto& config_entry : touch_buttons) {
|
||||
if (index >= touch_params.size()) {
|
||||
if (index >= MaxTouchDevices) {
|
||||
continue;
|
||||
}
|
||||
Common::ParamPackage params{config_entry};
|
||||
@@ -60,7 +59,6 @@ void EmulatedConsole::SetTouchParams() {
|
||||
touch_button_params.Set("button", params.Serialize());
|
||||
touch_button_params.Set("x", x);
|
||||
touch_button_params.Set("y", y);
|
||||
touch_button_params.Set("touch_id", static_cast<int>(index));
|
||||
touch_params[index] = touch_button_params;
|
||||
index++;
|
||||
}
|
||||
@@ -70,7 +68,7 @@ void EmulatedConsole::ReloadInput() {
|
||||
// If you load any device here add the equivalent to the UnloadInput() function
|
||||
SetTouchParams();
|
||||
|
||||
motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
|
||||
motion_devices = Common::Input::CreateInputDevice(motion_params);
|
||||
if (motion_devices) {
|
||||
motion_devices->SetCallback({
|
||||
.on_change =
|
||||
@@ -81,7 +79,7 @@ void EmulatedConsole::ReloadInput() {
|
||||
// Unique index for identifying touch device source
|
||||
std::size_t index = 0;
|
||||
for (auto& touch_device : touch_devices) {
|
||||
touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]);
|
||||
touch_device = Common::Input::CreateInputDevice(touch_params[index]);
|
||||
if (!touch_device) {
|
||||
continue;
|
||||
}
|
||||
@@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
|
||||
if (index >= console.touch_values.size()) {
|
||||
if (index >= MaxTouchDevices) {
|
||||
return;
|
||||
}
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
console.touch_values[index] = TransformToTouch(callback);
|
||||
const auto touch_input = TransformToTouch(callback);
|
||||
auto touch_index = GetIndexFromFingerId(index);
|
||||
bool is_new_input = false;
|
||||
|
||||
if (!touch_index.has_value() && touch_input.pressed.value) {
|
||||
touch_index = GetNextFreeIndex();
|
||||
is_new_input = true;
|
||||
}
|
||||
|
||||
// No free entries or invalid state. Ignore input
|
||||
if (!touch_index.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& touch_value = console.touch_values[touch_index.value()];
|
||||
|
||||
if (is_new_input) {
|
||||
touch_value.pressed.value = true;
|
||||
touch_value.id = static_cast<u32>(index);
|
||||
}
|
||||
|
||||
touch_value.x = touch_input.x;
|
||||
touch_value.y = touch_input.y;
|
||||
|
||||
if (!touch_input.pressed.value) {
|
||||
touch_value.pressed.value = false;
|
||||
}
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
@@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(german77): Remap touch id in sequential order
|
||||
console.touch_state[index] = {
|
||||
.position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
|
||||
.id = static_cast<u32>(console.touch_values[index].id),
|
||||
.pressed = console.touch_values[index].pressed.value,
|
||||
// Touch outside allowed range. Ignore input
|
||||
if (touch_index.value() >= MaxActiveTouchInputs) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.touch_state[touch_index.value()] = {
|
||||
.position = {touch_value.x.value, touch_value.y.value},
|
||||
.id = static_cast<u32>(touch_index.value()),
|
||||
.pressed = touch_input.pressed.value,
|
||||
};
|
||||
|
||||
lock.unlock();
|
||||
@@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const {
|
||||
return console.touch_state;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
|
||||
const auto& finger = console.touch_values[index];
|
||||
if (!finger.pressed.value) {
|
||||
continue;
|
||||
}
|
||||
if (finger.id == static_cast<int>(finger_id)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
|
||||
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
|
||||
if (!console.touch_values[index].pressed.value) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
@@ -20,6 +21,8 @@
|
||||
#include "core/hid/motion_input.h"
|
||||
|
||||
namespace Core::HID {
|
||||
static constexpr std::size_t MaxTouchDevices = 32;
|
||||
static constexpr std::size_t MaxActiveTouchInputs = 16;
|
||||
|
||||
struct ConsoleMotionInfo {
|
||||
Common::Input::MotionStatus raw_status{};
|
||||
@@ -27,13 +30,13 @@ struct ConsoleMotionInfo {
|
||||
};
|
||||
|
||||
using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
|
||||
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
|
||||
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
|
||||
|
||||
using ConsoleMotionParams = Common::ParamPackage;
|
||||
using TouchParams = std::array<Common::ParamPackage, 16>;
|
||||
using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
|
||||
|
||||
using ConsoleMotionValues = ConsoleMotionInfo;
|
||||
using TouchValues = std::array<Common::Input::TouchStatus, 16>;
|
||||
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
|
||||
|
||||
struct TouchFinger {
|
||||
u64 last_touch{};
|
||||
@@ -55,7 +58,7 @@ struct ConsoleMotion {
|
||||
bool is_at_rest{};
|
||||
};
|
||||
|
||||
using TouchFingerState = std::array<TouchFinger, 16>;
|
||||
using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>;
|
||||
|
||||
struct ConsoleStatus {
|
||||
// Data from input_common
|
||||
@@ -166,6 +169,10 @@ private:
|
||||
*/
|
||||
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
|
||||
|
||||
std::optional<std::size_t> GetNextFreeIndex() const;
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the console status
|
||||
* @param type Input type of the event to trigger
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/input_converter.h"
|
||||
@@ -144,29 +146,23 @@ void EmulatedController::LoadDevices() {
|
||||
|
||||
LoadTASParams();
|
||||
|
||||
std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
|
||||
button_params.begin() + Settings::NativeButton::BUTTON_NS_END,
|
||||
button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
|
||||
stick_params.begin() + Settings::NativeAnalog::STICK_HID_END,
|
||||
stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
|
||||
motion_params.begin() + Settings::NativeMotion::MOTION_HID_END,
|
||||
motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
|
||||
nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params);
|
||||
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::OutputDevice>);
|
||||
std::ranges::transform(button_params, button_devices.begin(), Common::Input::CreateInputDevice);
|
||||
std::ranges::transform(stick_params, stick_devices.begin(), Common::Input::CreateInputDevice);
|
||||
std::ranges::transform(motion_params, motion_devices.begin(), Common::Input::CreateInputDevice);
|
||||
std::ranges::transform(trigger_params, trigger_devices.begin(),
|
||||
Common::Input::CreateInputDevice);
|
||||
std::ranges::transform(battery_params, battery_devices.begin(),
|
||||
Common::Input::CreateInputDevice);
|
||||
camera_devices = Common::Input::CreateInputDevice(camera_params);
|
||||
nfc_devices = Common::Input::CreateInputDevice(nfc_params);
|
||||
std::ranges::transform(output_params, output_devices.begin(),
|
||||
Common::Input::CreateOutputDevice);
|
||||
|
||||
// Initialize TAS devices
|
||||
std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::ranges::transform(tas_button_params, tas_button_devices.begin(),
|
||||
Common::Input::CreateInputDevice);
|
||||
std::ranges::transform(tas_stick_params, tas_stick_devices.begin(),
|
||||
Common::Input::CreateInputDevice);
|
||||
}
|
||||
|
||||
void EmulatedController::LoadTASParams() {
|
||||
|
||||
@@ -25,12 +25,12 @@ void EmulatedDevices::ReloadInput() {
|
||||
Common::ParamPackage mouse_params;
|
||||
mouse_params.Set("engine", "mouse");
|
||||
mouse_params.Set("button", static_cast<int>(key_index));
|
||||
mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
|
||||
mouse_device = Common::Input::CreateInputDevice(mouse_params);
|
||||
key_index++;
|
||||
}
|
||||
|
||||
mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
"engine:mouse,axis_x:0,axis_y:1");
|
||||
mouse_stick_device =
|
||||
Common::Input::CreateInputDeviceFromString("engine:mouse,axis_x:0,axis_y:1");
|
||||
|
||||
// First two axis are reserved for mouse position
|
||||
key_index = 2;
|
||||
@@ -38,7 +38,7 @@ void EmulatedDevices::ReloadInput() {
|
||||
Common::ParamPackage mouse_params;
|
||||
mouse_params.Set("engine", "mouse");
|
||||
mouse_params.Set("axis", static_cast<int>(key_index));
|
||||
mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
|
||||
mouse_device = Common::Input::CreateInputDevice(mouse_params);
|
||||
key_index++;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ void EmulatedDevices::ReloadInput() {
|
||||
keyboard_params.Set("button", static_cast<int>(key_index));
|
||||
keyboard_params.Set("port", 1);
|
||||
keyboard_params.Set("pad", 0);
|
||||
keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
|
||||
keyboard_device = Common::Input::CreateInputDevice(keyboard_params);
|
||||
key_index++;
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@ void EmulatedDevices::ReloadInput() {
|
||||
keyboard_params.Set("button", static_cast<int>(key_index));
|
||||
keyboard_params.Set("port", 1);
|
||||
keyboard_params.Set("pad", 1);
|
||||
keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
|
||||
keyboard_device = Common::Input::CreateInputDevice(keyboard_params);
|
||||
key_index++;
|
||||
}
|
||||
|
||||
ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
|
||||
ring_analog_device = Common::Input::CreateInputDevice(ring_params);
|
||||
|
||||
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
|
||||
if (!mouse_button_devices[index]) {
|
||||
@@ -145,6 +145,7 @@ void EmulatedDevices::UnloadInput() {
|
||||
for (auto& button : keyboard_modifier_devices) {
|
||||
button.reset();
|
||||
}
|
||||
ring_analog_device.reset();
|
||||
}
|
||||
|
||||
void EmulatedDevices::EnableConfiguration() {
|
||||
|
||||
@@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
|
||||
x = std::clamp(x, 0.0f, 1.0f);
|
||||
y = std::clamp(y, 0.0f, 1.0f);
|
||||
|
||||
// Limit id to maximum number of fingers
|
||||
status.id = std::clamp(status.id, 0, 16);
|
||||
|
||||
if (status.pressed.inverted) {
|
||||
status.pressed.value = !status.pressed.value;
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
context->AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
|
||||
Kernel::LimitableResource::Sessions, 1);
|
||||
Kernel::LimitableResource::SessionCountMax, 1);
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(nullptr, iface->GetServiceName());
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
namespace Kernel::Board::Nintendo::Nx {
|
||||
|
||||
class KSystemControl {
|
||||
public:
|
||||
// This can be overridden as needed.
|
||||
static constexpr size_t SecureAppletMemorySize = 4 * 1024 * 1024; // 4_MB
|
||||
|
||||
public:
|
||||
class Init {
|
||||
public:
|
||||
|
||||
@@ -27,16 +27,12 @@ namespace Kernel {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
|
||||
ServiceThreadType thread_type)
|
||||
: kernel{kernel_} {
|
||||
if (thread_type == ServiceThreadType::CreateNew) {
|
||||
service_thread = kernel.CreateServiceThread(service_name_);
|
||||
} else {
|
||||
service_thread = kernel.GetDefaultServiceThread();
|
||||
}
|
||||
}
|
||||
: kernel{kernel_}, service_thread{thread_type == ServiceThreadType::CreateNew
|
||||
? kernel.CreateServiceThread(service_name_)
|
||||
: kernel.GetDefaultServiceThread()} {}
|
||||
|
||||
SessionRequestHandler::~SessionRequestHandler() {
|
||||
kernel.ReleaseServiceThread(service_thread.lock());
|
||||
kernel.ReleaseServiceThread(service_thread);
|
||||
}
|
||||
|
||||
void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
|
||||
@@ -49,7 +45,7 @@ void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
|
||||
void SessionRequestHandler::RegisterSession(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager) {
|
||||
manager->SetSessionHandler(shared_from_this());
|
||||
service_thread.lock()->RegisterServerSession(server_session, manager);
|
||||
service_thread.RegisterServerSession(server_session, manager);
|
||||
server_session->Close();
|
||||
}
|
||||
|
||||
|
||||
@@ -82,13 +82,13 @@ public:
|
||||
void RegisterSession(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager);
|
||||
|
||||
std::weak_ptr<ServiceThread> GetServiceThread() const {
|
||||
ServiceThread& GetServiceThread() const {
|
||||
return service_thread;
|
||||
}
|
||||
|
||||
protected:
|
||||
KernelCore& kernel;
|
||||
std::weak_ptr<ServiceThread> service_thread;
|
||||
ServiceThread& service_thread;
|
||||
};
|
||||
|
||||
using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
|
||||
@@ -154,7 +154,7 @@ public:
|
||||
session_handler = std::move(handler);
|
||||
}
|
||||
|
||||
std::weak_ptr<ServiceThread> GetServiceThread() const {
|
||||
ServiceThread& GetServiceThread() const {
|
||||
return session_handler->GetServiceThread();
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ public:
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
u32* CommandBuffer() {
|
||||
[[nodiscard]] u32* CommandBuffer() {
|
||||
return cmd_buf.data();
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ public:
|
||||
* Returns the session through which this request was made. This can be used as a map key to
|
||||
* access per-client data on services.
|
||||
*/
|
||||
Kernel::KServerSession* Session() {
|
||||
[[nodiscard]] Kernel::KServerSession* Session() {
|
||||
return server_session;
|
||||
}
|
||||
|
||||
@@ -217,61 +217,61 @@ public:
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
|
||||
|
||||
u32_le GetHipcCommand() const {
|
||||
[[nodiscard]] u32_le GetHipcCommand() const {
|
||||
return command;
|
||||
}
|
||||
|
||||
u32_le GetTipcCommand() const {
|
||||
[[nodiscard]] u32_le GetTipcCommand() const {
|
||||
return static_cast<u32_le>(command_header->type.Value()) -
|
||||
static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion);
|
||||
}
|
||||
|
||||
u32_le GetCommand() const {
|
||||
[[nodiscard]] u32_le GetCommand() const {
|
||||
return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand();
|
||||
}
|
||||
|
||||
bool IsTipc() const {
|
||||
[[nodiscard]] bool IsTipc() const {
|
||||
return command_header->IsTipc();
|
||||
}
|
||||
|
||||
IPC::CommandType GetCommandType() const {
|
||||
[[nodiscard]] IPC::CommandType GetCommandType() const {
|
||||
return command_header->type;
|
||||
}
|
||||
|
||||
u64 GetPID() const {
|
||||
[[nodiscard]] u64 GetPID() const {
|
||||
return pid;
|
||||
}
|
||||
|
||||
u32 GetDataPayloadOffset() const {
|
||||
[[nodiscard]] u32 GetDataPayloadOffset() const {
|
||||
return data_payload_offset;
|
||||
}
|
||||
|
||||
const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
|
||||
return buffer_x_desciptors;
|
||||
}
|
||||
|
||||
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
|
||||
return buffer_a_desciptors;
|
||||
}
|
||||
|
||||
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
|
||||
return buffer_b_desciptors;
|
||||
}
|
||||
|
||||
const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
|
||||
return buffer_c_desciptors;
|
||||
}
|
||||
|
||||
const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
|
||||
[[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
|
||||
return domain_message_header.value();
|
||||
}
|
||||
|
||||
bool HasDomainMessageHeader() const {
|
||||
[[nodiscard]] bool HasDomainMessageHeader() const {
|
||||
return domain_message_header.has_value();
|
||||
}
|
||||
|
||||
/// Helper function to read a buffer using the appropriate buffer descriptor
|
||||
std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to write a buffer using the appropriate buffer descriptor
|
||||
std::size_t WriteBuffer(const void* buffer, std::size_t size,
|
||||
@@ -308,22 +308,34 @@ public:
|
||||
}
|
||||
|
||||
/// Helper function to get the size of the input buffer
|
||||
std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to get the size of the output buffer
|
||||
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to derive the number of elements able to be contained in the read buffer
|
||||
template <typename T>
|
||||
[[nodiscard]] std::size_t GetReadBufferNumElements(std::size_t buffer_index = 0) const {
|
||||
return GetReadBufferSize(buffer_index) / sizeof(T);
|
||||
}
|
||||
|
||||
/// Helper function to derive the number of elements able to be contained in the write buffer
|
||||
template <typename T>
|
||||
[[nodiscard]] std::size_t GetWriteBufferNumElements(std::size_t buffer_index = 0) const {
|
||||
return GetWriteBufferSize(buffer_index) / sizeof(T);
|
||||
}
|
||||
|
||||
/// Helper function to test whether the input buffer at buffer_index can be read
|
||||
bool CanReadBuffer(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] bool CanReadBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to test whether the output buffer at buffer_index can be written
|
||||
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
|
||||
[[nodiscard]] bool CanWriteBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
Handle GetCopyHandle(std::size_t index) const {
|
||||
[[nodiscard]] Handle GetCopyHandle(std::size_t index) const {
|
||||
return incoming_copy_handles.at(index);
|
||||
}
|
||||
|
||||
Handle GetMoveHandle(std::size_t index) const {
|
||||
[[nodiscard]] Handle GetMoveHandle(std::size_t index) const {
|
||||
return incoming_move_handles.at(index);
|
||||
}
|
||||
|
||||
@@ -348,13 +360,13 @@ public:
|
||||
manager = manager_;
|
||||
}
|
||||
|
||||
std::string Description() const;
|
||||
[[nodiscard]] std::string Description() const;
|
||||
|
||||
KThread& GetThread() {
|
||||
[[nodiscard]] KThread& GetThread() {
|
||||
return *thread;
|
||||
}
|
||||
|
||||
std::shared_ptr<SessionRequestManager> GetManager() const {
|
||||
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
|
||||
return manager.lock();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/init/init_slab_setup.h"
|
||||
#include "core/hle/kernel/k_code_memory.h"
|
||||
#include "core/hle/kernel/k_debug.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_event_info.h"
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_buffer.h"
|
||||
@@ -22,6 +24,7 @@
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_shared_memory_info.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_local_page.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
@@ -44,7 +47,10 @@ namespace Kernel::Init {
|
||||
HANDLER(KThreadLocalPage, \
|
||||
(SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
|
||||
##__VA_ARGS__) \
|
||||
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
|
||||
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) \
|
||||
HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
|
||||
HANDLER(KDebug, (SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
|
||||
HANDLER(KSecureSystemResource, (SLAB_COUNT(KProcess)), ##__VA_ARGS__)
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -73,8 +79,20 @@ constexpr size_t SlabCountKResourceLimit = 5;
|
||||
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
|
||||
constexpr size_t SlabCountKIoPool = 1;
|
||||
constexpr size_t SlabCountKIoRegion = 6;
|
||||
constexpr size_t SlabcountKSessionRequestMappings = 40;
|
||||
|
||||
constexpr size_t SlabCountExtraKThread = 160;
|
||||
constexpr size_t SlabCountExtraKThread = (1024 + 256 + 256) - SlabCountKThread;
|
||||
|
||||
namespace test {
|
||||
|
||||
static_assert(KernelPageBufferHeapSize ==
|
||||
2 * PageSize + (SlabCountKProcess + SlabCountKThread +
|
||||
(SlabCountKProcess + SlabCountKThread) / 8) *
|
||||
PageSize);
|
||||
static_assert(KernelPageBufferAdditionalSize ==
|
||||
(SlabCountExtraKThread + (SlabCountExtraKThread / 8)) * PageSize);
|
||||
|
||||
} // namespace test
|
||||
|
||||
/// Helper function to translate from the slab virtual address to the reserved location in physical
|
||||
/// memory.
|
||||
@@ -109,7 +127,7 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
|
||||
}
|
||||
|
||||
size_t CalculateSlabHeapGapSize() {
|
||||
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
|
||||
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 320_KiB;
|
||||
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
|
||||
return KernelSlabHeapGapSize;
|
||||
}
|
||||
@@ -134,6 +152,7 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
|
||||
.num_KDebug = SlabCountKDebug,
|
||||
.num_KIoPool = SlabCountKIoPool,
|
||||
.num_KIoRegion = SlabCountKIoRegion,
|
||||
.num_KSessionRequestMappings = SlabcountKSessionRequestMappings,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -164,29 +183,6 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
|
||||
return size;
|
||||
}
|
||||
|
||||
void InitializeKPageBufferSlabHeap(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
const auto& counts = kernel.SlabResourceCounts();
|
||||
const size_t num_pages =
|
||||
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
|
||||
const size_t slab_size = num_pages * PageSize;
|
||||
|
||||
// Reserve memory from the system resource limit.
|
||||
ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
|
||||
|
||||
// Allocate memory for the slab.
|
||||
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
|
||||
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
|
||||
const PAddr slab_address =
|
||||
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
|
||||
ASSERT(slab_address != 0);
|
||||
|
||||
// Initialize the slabheap.
|
||||
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
|
||||
slab_size);
|
||||
}
|
||||
|
||||
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
@@ -247,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
// If we somehow get an invalid type, abort.
|
||||
default:
|
||||
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
// If we've hit the end of a gap, free it.
|
||||
@@ -258,3 +255,30 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
}
|
||||
|
||||
} // namespace Kernel::Init
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KPageBufferSlabHeap::Initialize(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
const auto& counts = kernel.SlabResourceCounts();
|
||||
const size_t num_pages =
|
||||
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
|
||||
const size_t slab_size = num_pages * PageSize;
|
||||
|
||||
// Reserve memory from the system resource limit.
|
||||
ASSERT(
|
||||
kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size));
|
||||
|
||||
// Allocate memory for the slab.
|
||||
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
|
||||
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
|
||||
const PAddr slab_address =
|
||||
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
|
||||
ASSERT(slab_address != 0);
|
||||
|
||||
// Initialize the slabheap.
|
||||
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
|
||||
slab_size);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -33,11 +33,11 @@ struct KSlabResourceCounts {
|
||||
size_t num_KDebug;
|
||||
size_t num_KIoPool;
|
||||
size_t num_KIoRegion;
|
||||
size_t num_KSessionRequestMappings;
|
||||
};
|
||||
|
||||
void InitializeSlabResourceCounts(KernelCore& kernel);
|
||||
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
|
||||
void InitializeKPageBufferSlabHeap(Core::System& system);
|
||||
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
|
||||
|
||||
} // namespace Kernel::Init
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
|
||||
@@ -119,4 +120,6 @@ static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject,
|
||||
// static_assert(std::is_final_v<KCodeMemory> &&
|
||||
// std::is_base_of_v<KAutoObject, KCodeMemory>);
|
||||
|
||||
static_assert(std::is_base_of_v<KAutoObject, KSystemResource>);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Kernel {
|
||||
|
||||
class KAutoObject;
|
||||
|
||||
class KSystemResource;
|
||||
|
||||
class KClassTokenGenerator {
|
||||
public:
|
||||
using TokenBaseType = u16;
|
||||
@@ -58,7 +60,7 @@ private:
|
||||
if constexpr (std::is_same<T, KAutoObject>::value) {
|
||||
static_assert(T::ObjectType == ObjectType::KAutoObject);
|
||||
return 0;
|
||||
} else if constexpr (!std::is_final<T>::value) {
|
||||
} else if constexpr (!std::is_final<T>::value && !std::same_as<T, KSystemResource>) {
|
||||
static_assert(ObjectType::BaseClassesStart <= T::ObjectType &&
|
||||
T::ObjectType < ObjectType::BaseClassesEnd);
|
||||
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
|
||||
@@ -108,6 +110,8 @@ public:
|
||||
KSessionRequest,
|
||||
KCodeMemory,
|
||||
|
||||
KSystemResource,
|
||||
|
||||
// NOTE: True order for these has not been determined yet.
|
||||
KAlpha,
|
||||
KBeta,
|
||||
|
||||
@@ -61,7 +61,7 @@ bool KClientPort::IsSignaled() const {
|
||||
Result KClientPort::CreateSession(KClientSession** out) {
|
||||
// Reserve a new session from the resource limit.
|
||||
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
|
||||
LimitableResource::Sessions);
|
||||
LimitableResource::SessionCountMax);
|
||||
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Update the session counts.
|
||||
|
||||
20
src/core/hle/kernel/k_debug.h
Normal file
20
src/core/hle/kernel/k_debug.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KAutoObjectWithList> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KDebug, KAutoObject);
|
||||
|
||||
public:
|
||||
explicit KDebug(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
|
||||
|
||||
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_page_bitmap.h"
|
||||
@@ -33,28 +35,36 @@ public:
|
||||
return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
|
||||
}
|
||||
|
||||
Result Initialize(VAddr addr, size_t sz) {
|
||||
Result Initialize(VAddr memory, size_t size, size_t align) {
|
||||
// We need to have positive size.
|
||||
R_UNLESS(sz > 0, ResultOutOfMemory);
|
||||
m_backing_memory.resize(sz);
|
||||
R_UNLESS(size > 0, ResultOutOfMemory);
|
||||
m_backing_memory.resize(size);
|
||||
|
||||
// Calculate management overhead.
|
||||
const size_t management_size =
|
||||
KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer));
|
||||
const size_t allocatable_size = sz - management_size;
|
||||
// Set addresses.
|
||||
m_address = memory;
|
||||
m_aligned_address = Common::AlignDown(memory, align);
|
||||
|
||||
// Calculate extents.
|
||||
const size_t managed_size = m_address + size - m_aligned_address;
|
||||
const size_t overhead_size = Common::AlignUp(
|
||||
KPageBitmap::CalculateManagementOverheadSize(managed_size / sizeof(PageBuffer)),
|
||||
sizeof(PageBuffer));
|
||||
R_UNLESS(overhead_size < size, ResultOutOfMemory);
|
||||
|
||||
// Set tracking fields.
|
||||
m_address = addr;
|
||||
m_size = Common::AlignDown(allocatable_size, sizeof(PageBuffer));
|
||||
m_count = allocatable_size / sizeof(PageBuffer);
|
||||
R_UNLESS(m_count > 0, ResultOutOfMemory);
|
||||
m_size = Common::AlignDown(size - overhead_size, sizeof(PageBuffer));
|
||||
m_count = m_size / sizeof(PageBuffer);
|
||||
|
||||
// Clear the management region.
|
||||
u64* management_ptr = GetPointer<u64>(m_address + allocatable_size);
|
||||
std::memset(management_ptr, 0, management_size);
|
||||
u64* management_ptr = GetPointer<u64>(m_address + size - overhead_size);
|
||||
std::memset(management_ptr, 0, overhead_size);
|
||||
|
||||
// Initialize the bitmap.
|
||||
m_page_bitmap.Initialize(management_ptr, m_count);
|
||||
const size_t allocatable_region_size =
|
||||
(m_address + size - overhead_size) - m_aligned_address;
|
||||
ASSERT(allocatable_region_size >= sizeof(PageBuffer));
|
||||
|
||||
m_page_bitmap.Initialize(management_ptr, allocatable_region_size / sizeof(PageBuffer));
|
||||
|
||||
// Free the pages to the bitmap.
|
||||
for (size_t i = 0; i < m_count; i++) {
|
||||
@@ -62,7 +72,8 @@ public:
|
||||
std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize);
|
||||
|
||||
// Set the bit for the free page.
|
||||
m_page_bitmap.SetBit(i);
|
||||
m_page_bitmap.SetBit((m_address + (i * sizeof(PageBuffer)) - m_aligned_address) /
|
||||
sizeof(PageBuffer));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -101,7 +112,28 @@ public:
|
||||
m_page_bitmap.ClearBit(offset);
|
||||
m_peak = std::max(m_peak, (++m_used));
|
||||
|
||||
return GetPointer<PageBuffer>(m_address) + offset;
|
||||
return GetPointer<PageBuffer>(m_aligned_address) + offset;
|
||||
}
|
||||
|
||||
PageBuffer* Allocate(size_t count) {
|
||||
// Take the lock.
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Find a random free block.
|
||||
s64 soffset = m_page_bitmap.FindFreeRange(count);
|
||||
if (soffset < 0) [[likely]] {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t offset = static_cast<size_t>(soffset);
|
||||
|
||||
// Update our tracking.
|
||||
m_page_bitmap.ClearRange(offset, count);
|
||||
m_used += count;
|
||||
m_peak = std::max(m_peak, m_used);
|
||||
|
||||
return GetPointer<PageBuffer>(m_aligned_address) + offset;
|
||||
}
|
||||
|
||||
void Free(PageBuffer* pb) {
|
||||
@@ -113,7 +145,7 @@ public:
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Set the bit for the free page.
|
||||
size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_address) / sizeof(PageBuffer);
|
||||
size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_aligned_address) / sizeof(PageBuffer);
|
||||
m_page_bitmap.SetBit(offset);
|
||||
|
||||
// Decrement our used count.
|
||||
@@ -127,6 +159,7 @@ private:
|
||||
size_t m_peak{};
|
||||
size_t m_count{};
|
||||
VAddr m_address{};
|
||||
VAddr m_aligned_address{};
|
||||
size_t m_size{};
|
||||
|
||||
// TODO(bunnei): Back by host memory until we emulate kernel virtual address space.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/kernel/k_dynamic_slab_heap.h"
|
||||
#include "core/hle/kernel/k_memory_block.h"
|
||||
#include "core/hle/kernel/k_page_group.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -51,8 +52,10 @@ private:
|
||||
DynamicSlabType* m_slab_heap{};
|
||||
};
|
||||
|
||||
class KBlockInfoManager : public KDynamicResourceManager<KBlockInfo> {};
|
||||
class KMemoryBlockSlabManager : public KDynamicResourceManager<KMemoryBlock> {};
|
||||
|
||||
using KBlockInfoSlabHeap = typename KBlockInfoManager::DynamicSlabType;
|
||||
using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType;
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -20,8 +20,12 @@ void KEvent::Initialize(KProcess* owner) {
|
||||
m_readable_event.Initialize(this);
|
||||
|
||||
// Set our owner process.
|
||||
m_owner = owner;
|
||||
m_owner->Open();
|
||||
// HACK: this should never be nullptr, but service threads don't have a
|
||||
// proper parent process yet.
|
||||
if (owner != nullptr) {
|
||||
m_owner = owner;
|
||||
m_owner->Open();
|
||||
}
|
||||
|
||||
// Mark initialized.
|
||||
m_initialized = true;
|
||||
@@ -50,8 +54,11 @@ Result KEvent::Clear() {
|
||||
void KEvent::PostDestroy(uintptr_t arg) {
|
||||
// Release the event count resource the owner process holds.
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
|
||||
owner->Close();
|
||||
|
||||
if (owner != nullptr) {
|
||||
owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1);
|
||||
owner->Close();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
64
src/core/hle/kernel/k_event_info.h
Normal file
64
src/core/hle/kernel/k_event_info.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KEventInfo : public KSlabAllocated<KEventInfo>, public boost::intrusive::list_base_hook<> {
|
||||
public:
|
||||
struct InfoCreateThread {
|
||||
u32 thread_id{};
|
||||
uintptr_t tls_address{};
|
||||
};
|
||||
|
||||
struct InfoExitProcess {
|
||||
Svc::ProcessExitReason reason{};
|
||||
};
|
||||
|
||||
struct InfoExitThread {
|
||||
Svc::ThreadExitReason reason{};
|
||||
};
|
||||
|
||||
struct InfoException {
|
||||
Svc::DebugException exception_type{};
|
||||
s32 exception_data_count{};
|
||||
uintptr_t exception_address{};
|
||||
std::array<uintptr_t, 4> exception_data{};
|
||||
};
|
||||
|
||||
struct InfoSystemCall {
|
||||
s64 tick{};
|
||||
s32 id{};
|
||||
};
|
||||
|
||||
public:
|
||||
KEventInfo() = default;
|
||||
~KEventInfo() = default;
|
||||
|
||||
public:
|
||||
Svc::DebugEvent event{};
|
||||
u32 thread_id{};
|
||||
u32 flags{};
|
||||
bool is_attached{};
|
||||
bool continue_flag{};
|
||||
bool ignore_continue{};
|
||||
bool close_once{};
|
||||
union {
|
||||
InfoCreateThread create_thread;
|
||||
InfoExitProcess exit_process;
|
||||
InfoExitThread exit_thread;
|
||||
InfoException exception;
|
||||
InfoSystemCall system_call;
|
||||
} info{};
|
||||
KThread* debug_thread{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -2,17 +2,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
KHandleTable::~KHandleTable() = default;
|
||||
|
||||
Result KHandleTable::Finalize() {
|
||||
// Get the table and clear our record of it.
|
||||
u16 saved_table_size = 0;
|
||||
{
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
std::swap(m_table_size, saved_table_size);
|
||||
@@ -25,28 +23,28 @@ Result KHandleTable::Finalize() {
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool KHandleTable::Remove(Handle handle) {
|
||||
// Don't allow removal of a pseudo-handle.
|
||||
if (Svc::IsPseudoHandle(handle)) {
|
||||
if (Svc::IsPseudoHandle(handle)) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handles must not have reserved bits set.
|
||||
const auto handle_pack = HandlePack(handle);
|
||||
if (handle_pack.reserved != 0) {
|
||||
if (handle_pack.reserved != 0) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the object and free the entry.
|
||||
KAutoObject* obj = nullptr;
|
||||
{
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
if (this->IsValidHandle(handle)) {
|
||||
if (this->IsValidHandle(handle)) [[likely]] {
|
||||
const auto index = handle_pack.index;
|
||||
|
||||
obj = m_objects[index];
|
||||
@@ -57,13 +55,13 @@ bool KHandleTable::Remove(Handle handle) {
|
||||
}
|
||||
|
||||
// Close the object.
|
||||
kernel.UnregisterInUseObject(obj);
|
||||
m_kernel.UnregisterInUseObject(obj);
|
||||
obj->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Never exceed our capacity.
|
||||
@@ -82,22 +80,38 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
|
||||
*out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(Handle handle,
|
||||
KThread* cur_thread) const {
|
||||
// Handle pseudo-handles.
|
||||
ASSERT(cur_thread != nullptr);
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process = cur_thread->GetOwnerProcess();
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
if (handle == Svc::PseudoHandle::CurrentThread) {
|
||||
return cur_thread;
|
||||
}
|
||||
|
||||
return GetObjectForIpcWithoutPseudoHandle(handle);
|
||||
}
|
||||
|
||||
Result KHandleTable::Reserve(Handle* out_handle) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Never exceed our capacity.
|
||||
R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
|
||||
|
||||
*out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void KHandleTable::Unreserve(Handle handle) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
@@ -108,7 +122,7 @@ void KHandleTable::Unreserve(Handle handle) {
|
||||
ASSERT(reserved == 0);
|
||||
ASSERT(linear_id != 0);
|
||||
|
||||
if (index < m_table_size) {
|
||||
if (index < m_table_size) [[likely]] {
|
||||
// NOTE: This code does not check the linear id.
|
||||
ASSERT(m_objects[index] == nullptr);
|
||||
this->FreeEntry(index);
|
||||
@@ -116,7 +130,7 @@ void KHandleTable::Unreserve(Handle handle) {
|
||||
}
|
||||
|
||||
void KHandleTable::Register(Handle handle, KAutoObject* obj) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
@@ -127,7 +141,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj) {
|
||||
ASSERT(reserved == 0);
|
||||
ASSERT(linear_id != 0);
|
||||
|
||||
if (index < m_table_size) {
|
||||
if (index < m_table_size) [[likely]] {
|
||||
// Set the entry.
|
||||
ASSERT(m_objects[index] == nullptr);
|
||||
|
||||
|
||||
@@ -21,33 +21,38 @@ namespace Kernel {
|
||||
class KernelCore;
|
||||
|
||||
class KHandleTable {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KHandleTable);
|
||||
YUZU_NON_MOVEABLE(KHandleTable);
|
||||
|
||||
public:
|
||||
static constexpr size_t MaxTableSize = 1024;
|
||||
|
||||
explicit KHandleTable(KernelCore& kernel_);
|
||||
~KHandleTable();
|
||||
public:
|
||||
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
||||
|
||||
Result Initialize(s32 size) {
|
||||
// Check that the table size is valid.
|
||||
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
||||
|
||||
// Lock.
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Initialize all fields.
|
||||
m_max_count = 0;
|
||||
m_table_size = static_cast<u16>((size <= 0) ? MaxTableSize : size);
|
||||
m_table_size = static_cast<s16>((size <= 0) ? MaxTableSize : size);
|
||||
m_next_linear_id = MinLinearId;
|
||||
m_count = 0;
|
||||
m_free_head_index = -1;
|
||||
|
||||
// Free all entries.
|
||||
for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
|
||||
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
|
||||
m_objects[i] = nullptr;
|
||||
m_entry_infos[i].next_free_index = i - 1;
|
||||
m_entry_infos[i].next_free_index = static_cast<s16>(i - 1);
|
||||
m_free_head_index = i;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
size_t GetTableSize() const {
|
||||
@@ -66,13 +71,13 @@ public:
|
||||
template <typename T = KAutoObject>
|
||||
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
|
||||
// Lock and look up in table.
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
if constexpr (std::is_same_v<T, KAutoObject>) {
|
||||
return this->GetObjectImpl(handle);
|
||||
} else {
|
||||
if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) {
|
||||
if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) [[likely]] {
|
||||
return obj->DynamicCast<T*>();
|
||||
} else {
|
||||
return nullptr;
|
||||
@@ -85,13 +90,13 @@ public:
|
||||
// Handle pseudo-handles.
|
||||
if constexpr (std::derived_from<KProcess, T>) {
|
||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||
auto* const cur_process = kernel.CurrentProcess();
|
||||
auto* const cur_process = m_kernel.CurrentProcess();
|
||||
ASSERT(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
} else if constexpr (std::derived_from<KThread, T>) {
|
||||
if (handle == Svc::PseudoHandle::CurrentThread) {
|
||||
auto* const cur_thread = GetCurrentThreadPointer(kernel);
|
||||
auto* const cur_thread = GetCurrentThreadPointer(m_kernel);
|
||||
ASSERT(cur_thread != nullptr);
|
||||
return cur_thread;
|
||||
}
|
||||
@@ -100,6 +105,23 @@ public:
|
||||
return this->template GetObjectWithoutPseudoHandle<T>(handle);
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(Handle handle) const {
|
||||
// Lock and look up in table.
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
return this->GetObjectImpl(handle);
|
||||
}
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const;
|
||||
|
||||
KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
return this->GetObjectByIndexImpl(out_handle, index);
|
||||
}
|
||||
|
||||
Result Reserve(Handle* out_handle);
|
||||
void Unreserve(Handle handle);
|
||||
|
||||
@@ -112,7 +134,7 @@ public:
|
||||
size_t num_opened;
|
||||
{
|
||||
// Lock the table.
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk(m_lock);
|
||||
for (num_opened = 0; num_opened < num_handles; num_opened++) {
|
||||
// Get the current handle.
|
||||
@@ -120,13 +142,13 @@ public:
|
||||
|
||||
// Get the object for the current handle.
|
||||
KAutoObject* cur_object = this->GetObjectImpl(cur_handle);
|
||||
if (cur_object == nullptr) {
|
||||
if (cur_object == nullptr) [[unlikely]] {
|
||||
break;
|
||||
}
|
||||
|
||||
// Cast the current object to the desired type.
|
||||
T* cur_t = cur_object->DynamicCast<T*>();
|
||||
if (cur_t == nullptr) {
|
||||
if (cur_t == nullptr) [[unlikely]] {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -137,7 +159,7 @@ public:
|
||||
}
|
||||
|
||||
// If we converted every object, succeed.
|
||||
if (num_opened == num_handles) {
|
||||
if (num_opened == num_handles) [[likely]] {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -191,21 +213,21 @@ private:
|
||||
ASSERT(reserved == 0);
|
||||
|
||||
// Validate our indexing information.
|
||||
if (raw_value == 0) {
|
||||
if (raw_value == 0) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
if (linear_id == 0) {
|
||||
if (linear_id == 0) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
if (index >= m_table_size) {
|
||||
if (index >= m_table_size) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that there's an object, and our serial id is correct.
|
||||
if (m_objects[index] == nullptr) {
|
||||
if (m_objects[index] == nullptr) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
if (m_entry_infos[index].GetLinearId() != linear_id) {
|
||||
if (m_entry_infos[index].GetLinearId() != linear_id) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -215,11 +237,11 @@ private:
|
||||
KAutoObject* GetObjectImpl(Handle handle) const {
|
||||
// Handles must not have reserved bits set.
|
||||
const auto handle_pack = HandlePack(handle);
|
||||
if (handle_pack.reserved != 0) {
|
||||
if (handle_pack.reserved != 0) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (this->IsValidHandle(handle)) {
|
||||
if (this->IsValidHandle(handle)) [[likely]] {
|
||||
return m_objects[handle_pack.index];
|
||||
} else {
|
||||
return nullptr;
|
||||
@@ -227,9 +249,8 @@ private:
|
||||
}
|
||||
|
||||
KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const {
|
||||
|
||||
// Index must be in bounds.
|
||||
if (index >= m_table_size) {
|
||||
if (index >= m_table_size) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -244,18 +265,15 @@ private:
|
||||
|
||||
private:
|
||||
union HandlePack {
|
||||
HandlePack() = default;
|
||||
HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
|
||||
constexpr HandlePack() = default;
|
||||
constexpr HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
|
||||
|
||||
u32 raw;
|
||||
u32 raw{};
|
||||
BitField<0, 15, u32> index;
|
||||
BitField<15, 15, u32> linear_id;
|
||||
BitField<30, 2, u32> reserved;
|
||||
};
|
||||
|
||||
static constexpr u16 MinLinearId = 1;
|
||||
static constexpr u16 MaxLinearId = 0x7FFF;
|
||||
|
||||
static constexpr Handle EncodeHandle(u16 index, u16 linear_id) {
|
||||
HandlePack handle{};
|
||||
handle.index.Assign(index);
|
||||
@@ -264,6 +282,10 @@ private:
|
||||
return handle.raw;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr u16 MinLinearId = 1;
|
||||
static constexpr u16 MaxLinearId = 0x7FFF;
|
||||
|
||||
union EntryInfo {
|
||||
u16 linear_id;
|
||||
s16 next_free_index;
|
||||
@@ -271,21 +293,21 @@ private:
|
||||
constexpr u16 GetLinearId() const {
|
||||
return linear_id;
|
||||
}
|
||||
constexpr s16 GetNextFreeIndex() const {
|
||||
constexpr s32 GetNextFreeIndex() const {
|
||||
return next_free_index;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
KernelCore& m_kernel;
|
||||
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
|
||||
std::array<KAutoObject*, MaxTableSize> m_objects{};
|
||||
s32 m_free_head_index{-1};
|
||||
mutable KSpinLock m_lock;
|
||||
s32 m_free_head_index{};
|
||||
u16 m_table_size{};
|
||||
u16 m_max_count{};
|
||||
u16 m_next_linear_id{MinLinearId};
|
||||
u16 m_next_linear_id{};
|
||||
u16 m_count{};
|
||||
mutable KSpinLock m_lock;
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -35,26 +35,32 @@ enum class KMemoryState : u32 {
|
||||
FlagCanMapProcess = (1 << 23),
|
||||
FlagCanChangeAttribute = (1 << 24),
|
||||
FlagCanCodeMemory = (1 << 25),
|
||||
FlagLinearMapped = (1 << 26),
|
||||
|
||||
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
||||
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
|
||||
FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer |
|
||||
FlagReferenceCounted | FlagCanChangeAttribute,
|
||||
FlagReferenceCounted | FlagCanChangeAttribute | FlagLinearMapped,
|
||||
|
||||
FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
||||
FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap |
|
||||
FlagCanAlignedDeviceMap | FlagReferenceCounted,
|
||||
FlagCanAlignedDeviceMap | FlagReferenceCounted | FlagLinearMapped,
|
||||
|
||||
FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap,
|
||||
FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap |
|
||||
FlagLinearMapped,
|
||||
|
||||
Free = static_cast<u32>(Svc::MemoryState::Free),
|
||||
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped,
|
||||
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
||||
FlagCanAlignedDeviceMap,
|
||||
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
|
||||
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
|
||||
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
|
||||
FlagCanCodeMemory,
|
||||
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted,
|
||||
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
|
||||
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
|
||||
FlagLinearMapped,
|
||||
|
||||
// Alias was removed after 1.0.0.
|
||||
|
||||
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
|
||||
FlagCanCodeAlias,
|
||||
@@ -67,18 +73,18 @@ enum class KMemoryState : u32 {
|
||||
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
ThreadLocal =
|
||||
static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
|
||||
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
|
||||
|
||||
Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
|
||||
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
|
||||
SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
|
||||
FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc |
|
||||
FlagCanUseNonDeviceIpc,
|
||||
|
||||
Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible),
|
||||
|
||||
@@ -91,69 +97,69 @@ enum class KMemoryState : u32 {
|
||||
Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
|
||||
|
||||
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
||||
FlagReferenceCounted | FlagCanDebug,
|
||||
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
|
||||
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
|
||||
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted |
|
||||
FlagLinearMapped,
|
||||
|
||||
Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped,
|
||||
|
||||
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
|
||||
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
|
||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
||||
|
||||
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
||||
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00002001);
|
||||
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
|
||||
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
||||
static_assert(static_cast<u32>(KMemoryState::Code) == 0x00DC7E03);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x03FEBD04);
|
||||
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x037EBD05);
|
||||
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x00402006);
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x00DD7E08);
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
|
||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
|
||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
|
||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
|
||||
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
|
||||
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
|
||||
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
|
||||
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
|
||||
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
|
||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
|
||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
|
||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
|
||||
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
|
||||
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
|
||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
|
||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
|
||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
|
||||
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
||||
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
|
||||
|
||||
enum class KMemoryPermission : u8 {
|
||||
None = 0,
|
||||
All = static_cast<u8>(~None),
|
||||
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
Execute = 1 << 2,
|
||||
|
||||
ReadAndWrite = Read | Write,
|
||||
ReadAndExecute = Read | Execute,
|
||||
|
||||
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
|
||||
Svc::MemoryPermission::Execute),
|
||||
|
||||
KernelShift = 3,
|
||||
|
||||
KernelRead = Read << KernelShift,
|
||||
KernelWrite = Write << KernelShift,
|
||||
KernelExecute = Execute << KernelShift,
|
||||
KernelRead = static_cast<u8>(Svc::MemoryPermission::Read) << KernelShift,
|
||||
KernelWrite = static_cast<u8>(Svc::MemoryPermission::Write) << KernelShift,
|
||||
KernelExecute = static_cast<u8>(Svc::MemoryPermission::Execute) << KernelShift,
|
||||
|
||||
NotMapped = (1 << (2 * KernelShift)),
|
||||
|
||||
KernelReadWrite = KernelRead | KernelWrite,
|
||||
KernelReadExecute = KernelRead | KernelExecute,
|
||||
|
||||
UserRead = Read | KernelRead,
|
||||
UserWrite = Write | KernelWrite,
|
||||
UserExecute = Execute,
|
||||
UserRead = static_cast<u8>(Svc::MemoryPermission::Read) | KernelRead,
|
||||
UserWrite = static_cast<u8>(Svc::MemoryPermission::Write) | KernelWrite,
|
||||
UserExecute = static_cast<u8>(Svc::MemoryPermission::Execute),
|
||||
|
||||
UserReadWrite = UserRead | UserWrite,
|
||||
UserReadExecute = UserRead | UserExecute,
|
||||
|
||||
IpcLockChangeMask = NotMapped | UserReadWrite
|
||||
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
|
||||
Svc::MemoryPermission::Execute),
|
||||
|
||||
IpcLockChangeMask = NotMapped | UserReadWrite,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
|
||||
|
||||
@@ -210,13 +216,15 @@ struct KMemoryInfo {
|
||||
|
||||
constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
|
||||
return {
|
||||
.addr = m_address,
|
||||
.base_address = m_address,
|
||||
.size = m_size,
|
||||
.state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask),
|
||||
.attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
|
||||
.perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
|
||||
.ipc_refcount = m_ipc_lock_count,
|
||||
.device_refcount = m_device_use_count,
|
||||
.attribute =
|
||||
static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
|
||||
.permission =
|
||||
static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
|
||||
.ipc_count = m_ipc_lock_count,
|
||||
.device_count = m_device_use_count,
|
||||
.padding = {},
|
||||
};
|
||||
}
|
||||
@@ -468,6 +476,7 @@ public:
|
||||
|
||||
constexpr void UpdateDeviceDisableMergeStateForShareLeft(
|
||||
[[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
|
||||
// New permission/right aren't used.
|
||||
if (left) {
|
||||
m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
|
||||
m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceLeft);
|
||||
@@ -478,6 +487,7 @@ public:
|
||||
|
||||
constexpr void UpdateDeviceDisableMergeStateForShareRight(
|
||||
[[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) {
|
||||
// New permission/left aren't used.
|
||||
if (right) {
|
||||
m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
|
||||
m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceRight);
|
||||
@@ -494,6 +504,8 @@ public:
|
||||
|
||||
constexpr void ShareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left,
|
||||
bool right) {
|
||||
// New permission isn't used.
|
||||
|
||||
// We must either be shared or have a zero lock count.
|
||||
ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared ||
|
||||
m_device_use_count == 0);
|
||||
@@ -509,6 +521,7 @@ public:
|
||||
|
||||
constexpr void UpdateDeviceDisableMergeStateForUnshareLeft(
|
||||
[[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
|
||||
// New permission/right aren't used.
|
||||
|
||||
if (left) {
|
||||
if (!m_device_disable_merge_left_count) {
|
||||
@@ -528,6 +541,8 @@ public:
|
||||
|
||||
constexpr void UpdateDeviceDisableMergeStateForUnshareRight(
|
||||
[[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) {
|
||||
// New permission/left aren't used.
|
||||
|
||||
if (right) {
|
||||
const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--;
|
||||
ASSERT(old_device_disable_merge_right_count > 0);
|
||||
@@ -546,6 +561,8 @@ public:
|
||||
|
||||
constexpr void UnshareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left,
|
||||
bool right) {
|
||||
// New permission isn't used.
|
||||
|
||||
// We must be shared.
|
||||
ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
|
||||
|
||||
@@ -563,6 +580,7 @@ public:
|
||||
|
||||
constexpr void UnshareToDeviceRight([[maybe_unused]] KMemoryPermission new_perm, bool left,
|
||||
bool right) {
|
||||
// New permission isn't used.
|
||||
|
||||
// We must be shared.
|
||||
ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
|
||||
@@ -613,6 +631,8 @@ public:
|
||||
|
||||
constexpr void UnlockForIpc([[maybe_unused]] KMemoryPermission new_perm, bool left,
|
||||
[[maybe_unused]] bool right) {
|
||||
// New permission isn't used.
|
||||
|
||||
// We must be locked.
|
||||
ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked);
|
||||
|
||||
|
||||
@@ -153,13 +153,9 @@ void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_
|
||||
}
|
||||
}
|
||||
|
||||
size_t KMemoryLayout::GetResourceRegionSizeForInit() {
|
||||
// Calculate resource region size based on whether we allow extra threads.
|
||||
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
|
||||
size_t resource_region_size =
|
||||
KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
|
||||
|
||||
return resource_region_size;
|
||||
size_t KMemoryLayout::GetResourceRegionSizeForInit(bool use_extra_resource) {
|
||||
return KernelResourceSize + KSystemControl::SecureAppletMemorySize +
|
||||
(use_extra_resource ? KernelSlabHeapAdditionalSize + KernelPageBufferAdditionalSize : 0);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -60,10 +60,12 @@ constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
|
||||
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
|
||||
|
||||
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
|
||||
constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
|
||||
constexpr size_t KernelPageBufferHeapSize = 0x3E0000;
|
||||
constexpr size_t KernelSlabHeapAdditionalSize = 0x148000;
|
||||
constexpr size_t KernelPageBufferAdditionalSize = 0x33C000;
|
||||
|
||||
constexpr std::size_t KernelResourceSize =
|
||||
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
|
||||
constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize +
|
||||
KernelSlabHeapSize + KernelPageBufferHeapSize;
|
||||
|
||||
constexpr bool IsKernelAddressKey(VAddr key) {
|
||||
return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
|
||||
@@ -168,6 +170,11 @@ public:
|
||||
KMemoryRegionType_VirtualDramKernelTraceBuffer));
|
||||
}
|
||||
|
||||
const KMemoryRegion& GetSecureAppletMemoryRegion() {
|
||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(
|
||||
KMemoryRegionType_VirtualDramKernelSecureAppletMemory));
|
||||
}
|
||||
|
||||
const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const {
|
||||
return Dereference(FindVirtualLinear(address));
|
||||
}
|
||||
@@ -229,7 +236,7 @@ public:
|
||||
|
||||
void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
|
||||
VAddr linear_virtual_start);
|
||||
static size_t GetResourceRegionSizeForInit();
|
||||
static size_t GetResourceRegionSizeForInit(bool use_extra_resource);
|
||||
|
||||
auto GetKernelRegionExtents() const {
|
||||
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel);
|
||||
@@ -279,6 +286,10 @@ public:
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramKernelSlab);
|
||||
}
|
||||
auto GetKernelSecureAppletMemoryRegionPhysicalExtents() {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramKernelSecureAppletMemory);
|
||||
}
|
||||
auto GetKernelPageTableHeapRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramKernelPtHeap);
|
||||
|
||||
@@ -29,43 +29,44 @@ constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
|
||||
} else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
|
||||
return KMemoryManager::Pool::SystemNonSecure;
|
||||
} else {
|
||||
ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
|
||||
return {};
|
||||
UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
KMemoryManager::KMemoryManager(Core::System& system_)
|
||||
: system{system_}, pool_locks{
|
||||
KLightLock{system_.Kernel()},
|
||||
KLightLock{system_.Kernel()},
|
||||
KLightLock{system_.Kernel()},
|
||||
KLightLock{system_.Kernel()},
|
||||
} {}
|
||||
KMemoryManager::KMemoryManager(Core::System& system)
|
||||
: m_system{system}, m_memory_layout{system.Kernel().MemoryLayout()},
|
||||
m_pool_locks{
|
||||
KLightLock{system.Kernel()},
|
||||
KLightLock{system.Kernel()},
|
||||
KLightLock{system.Kernel()},
|
||||
KLightLock{system.Kernel()},
|
||||
} {}
|
||||
|
||||
void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
|
||||
|
||||
// Clear the management region to zero.
|
||||
const VAddr management_region_end = management_region + management_region_size;
|
||||
// std::memset(GetVoidPointer(management_region), 0, management_region_size);
|
||||
|
||||
// Reset our manager count.
|
||||
num_managers = 0;
|
||||
m_num_managers = 0;
|
||||
|
||||
// Traverse the virtual memory layout tree, initializing each manager as appropriate.
|
||||
while (num_managers != MaxManagerCount) {
|
||||
while (m_num_managers != MaxManagerCount) {
|
||||
// Locate the region that should initialize the current manager.
|
||||
PAddr region_address = 0;
|
||||
size_t region_size = 0;
|
||||
Pool region_pool = Pool::Count;
|
||||
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||
// We only care about regions that we need to create managers for.
|
||||
if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We want to initialize the managers in order.
|
||||
if (it.GetAttributes() != num_managers) {
|
||||
if (it.GetAttributes() != m_num_managers) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -97,8 +98,8 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
|
||||
}
|
||||
|
||||
// Initialize a new manager for the region.
|
||||
Impl* manager = std::addressof(managers[num_managers++]);
|
||||
ASSERT(num_managers <= managers.size());
|
||||
Impl* manager = std::addressof(m_managers[m_num_managers++]);
|
||||
ASSERT(m_num_managers <= m_managers.size());
|
||||
|
||||
const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
|
||||
management_region_end, region_pool);
|
||||
@@ -107,13 +108,13 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
|
||||
|
||||
// Insert the manager into the pool list.
|
||||
const auto region_pool_index = static_cast<u32>(region_pool);
|
||||
if (pool_managers_tail[region_pool_index] == nullptr) {
|
||||
pool_managers_head[region_pool_index] = manager;
|
||||
if (m_pool_managers_tail[region_pool_index] == nullptr) {
|
||||
m_pool_managers_head[region_pool_index] = manager;
|
||||
} else {
|
||||
pool_managers_tail[region_pool_index]->SetNext(manager);
|
||||
manager->SetPrev(pool_managers_tail[region_pool_index]);
|
||||
m_pool_managers_tail[region_pool_index]->SetNext(manager);
|
||||
manager->SetPrev(m_pool_managers_tail[region_pool_index]);
|
||||
}
|
||||
pool_managers_tail[region_pool_index] = manager;
|
||||
m_pool_managers_tail[region_pool_index] = manager;
|
||||
}
|
||||
|
||||
// Free each region to its corresponding heap.
|
||||
@@ -121,11 +122,10 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
|
||||
const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
|
||||
const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
|
||||
const PAddr ini_last = ini_end - 1;
|
||||
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||
// Get the manager for the region.
|
||||
auto index = it.GetAttributes();
|
||||
auto& manager = managers[index];
|
||||
auto& manager = m_managers[it.GetAttributes()];
|
||||
|
||||
const PAddr cur_start = it.GetAddress();
|
||||
const PAddr cur_last = it.GetLastAddress();
|
||||
@@ -162,11 +162,19 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
|
||||
}
|
||||
|
||||
// Update the used size for all managers.
|
||||
for (size_t i = 0; i < num_managers; ++i) {
|
||||
managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
|
||||
for (size_t i = 0; i < m_num_managers; ++i) {
|
||||
m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
|
||||
// Early return if we're allocating no pages.
|
||||
if (num_pages == 0) {
|
||||
@@ -175,7 +183,7 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
|
||||
|
||||
// Lock the pool that we're allocating from.
|
||||
const auto [pool, dir] = DecodeOption(option);
|
||||
KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]);
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<std::size_t>(pool)]);
|
||||
|
||||
// Choose a heap based on our page size request.
|
||||
const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
|
||||
@@ -185,7 +193,7 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
|
||||
PAddr allocated_block = 0;
|
||||
for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
|
||||
chosen_manager = this->GetNextManager(chosen_manager, dir)) {
|
||||
allocated_block = chosen_manager->AllocateBlock(heap_index, true);
|
||||
allocated_block = chosen_manager->AllocateAligned(heap_index, num_pages, align_pages);
|
||||
if (allocated_block != 0) {
|
||||
break;
|
||||
}
|
||||
@@ -196,10 +204,9 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we allocated more than we need, free some.
|
||||
const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index);
|
||||
if (allocated_pages > num_pages) {
|
||||
chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
|
||||
// Maintain the optimized memory bitmap, if we should.
|
||||
if (m_has_optimized_process[static_cast<size_t>(pool)]) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
// Open the first reference to the pages.
|
||||
@@ -209,20 +216,21 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
|
||||
}
|
||||
|
||||
Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool,
|
||||
Direction dir, bool random) {
|
||||
Direction dir, bool unoptimized, bool random) {
|
||||
// Choose a heap based on our page size request.
|
||||
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
|
||||
R_UNLESS(0 <= heap_index, ResultOutOfMemory);
|
||||
|
||||
// Ensure that we don't leave anything un-freed.
|
||||
auto group_guard = SCOPE_GUARD({
|
||||
ON_RESULT_FAILURE {
|
||||
for (const auto& it : out->Nodes()) {
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress());
|
||||
const size_t num_pages_to_free =
|
||||
auto& manager = this->GetManager(it.GetAddress());
|
||||
const size_t node_num_pages =
|
||||
std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
|
||||
manager.Free(it.GetAddress(), num_pages_to_free);
|
||||
manager.Free(it.GetAddress(), node_num_pages);
|
||||
}
|
||||
});
|
||||
out->Finalize();
|
||||
};
|
||||
|
||||
// Keep allocating until we've allocated all our pages.
|
||||
for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
|
||||
@@ -236,12 +244,17 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
|
||||
break;
|
||||
}
|
||||
|
||||
// Safely add it to our group.
|
||||
{
|
||||
auto block_guard =
|
||||
SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); });
|
||||
R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
|
||||
block_guard.Cancel();
|
||||
// Ensure we don't leak the block if we fail.
|
||||
ON_RESULT_FAILURE_2 {
|
||||
cur_manager->Free(allocated_block, pages_per_alloc);
|
||||
};
|
||||
|
||||
// Add the block to our group.
|
||||
R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
|
||||
|
||||
// Maintain the optimized memory bitmap, if we should.
|
||||
if (unoptimized) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
num_pages -= pages_per_alloc;
|
||||
@@ -253,8 +266,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
|
||||
R_UNLESS(num_pages == 0, ResultOutOfMemory);
|
||||
|
||||
// We succeeded!
|
||||
group_guard.Cancel();
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) {
|
||||
@@ -266,10 +278,11 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
|
||||
|
||||
// Lock the pool that we're allocating from.
|
||||
const auto [pool, dir] = DecodeOption(option);
|
||||
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
|
||||
|
||||
// Allocate the page group.
|
||||
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
|
||||
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir,
|
||||
m_has_optimized_process[static_cast<size_t>(pool)], true));
|
||||
|
||||
// Open the first reference to the pages.
|
||||
for (const auto& block : out->Nodes()) {
|
||||
@@ -277,7 +290,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
|
||||
size_t remaining_pages = block.GetNumPages();
|
||||
while (remaining_pages > 0) {
|
||||
// Get the manager for the current address.
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
|
||||
auto& manager = this->GetManager(cur_address);
|
||||
|
||||
// Process part or all of the block.
|
||||
const size_t cur_pages =
|
||||
@@ -290,11 +303,11 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option,
|
||||
u64 process_id, u8 fill_pattern) {
|
||||
Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option,
|
||||
u64 process_id, u8 fill_pattern) {
|
||||
ASSERT(out != nullptr);
|
||||
ASSERT(out->GetNumPages() == 0);
|
||||
|
||||
@@ -302,83 +315,89 @@ Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pag
|
||||
const auto [pool, dir] = DecodeOption(option);
|
||||
|
||||
// Allocate the memory.
|
||||
bool optimized;
|
||||
{
|
||||
// Lock the pool that we're allocating from.
|
||||
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
|
||||
|
||||
// Check if we have an optimized process.
|
||||
const bool has_optimized = m_has_optimized_process[static_cast<size_t>(pool)];
|
||||
const bool is_optimized = m_optimized_process_ids[static_cast<size_t>(pool)] == process_id;
|
||||
|
||||
// Allocate the page group.
|
||||
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
|
||||
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, has_optimized && !is_optimized,
|
||||
false));
|
||||
|
||||
// Open the first reference to the pages.
|
||||
// Set whether we should optimize.
|
||||
optimized = has_optimized && is_optimized;
|
||||
}
|
||||
|
||||
// Perform optimized memory tracking, if we should.
|
||||
if (optimized) {
|
||||
// Iterate over the allocated blocks.
|
||||
for (const auto& block : out->Nodes()) {
|
||||
PAddr cur_address = block.GetAddress();
|
||||
size_t remaining_pages = block.GetNumPages();
|
||||
while (remaining_pages > 0) {
|
||||
// Get the manager for the current address.
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
|
||||
// Get the block extents.
|
||||
const PAddr block_address = block.GetAddress();
|
||||
const size_t block_pages = block.GetNumPages();
|
||||
|
||||
// Process part or all of the block.
|
||||
const size_t cur_pages =
|
||||
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
||||
manager.OpenFirst(cur_address, cur_pages);
|
||||
// If it has no pages, we don't need to do anything.
|
||||
if (block_pages == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
remaining_pages -= cur_pages;
|
||||
// Fill all the pages that we need to fill.
|
||||
bool any_new = false;
|
||||
{
|
||||
PAddr cur_address = block_address;
|
||||
size_t remaining_pages = block_pages;
|
||||
while (remaining_pages > 0) {
|
||||
// Get the manager for the current address.
|
||||
auto& manager = this->GetManager(cur_address);
|
||||
|
||||
// Process part or all of the block.
|
||||
const size_t cur_pages =
|
||||
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
||||
any_new =
|
||||
manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern);
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
remaining_pages -= cur_pages;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are new pages, update tracking for the allocation.
|
||||
if (any_new) {
|
||||
// Update tracking for the allocation.
|
||||
PAddr cur_address = block_address;
|
||||
size_t remaining_pages = block_pages;
|
||||
while (remaining_pages > 0) {
|
||||
// Get the manager for the current address.
|
||||
auto& manager = this->GetManager(cur_address);
|
||||
|
||||
// Lock the pool for the manager.
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
|
||||
|
||||
// Track some or all of the current pages.
|
||||
const size_t cur_pages =
|
||||
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
||||
manager.TrackOptimizedAllocation(cur_address, cur_pages);
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
remaining_pages -= cur_pages;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set all the allocated memory.
|
||||
for (const auto& block : out->Nodes()) {
|
||||
std::memset(system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
|
||||
block.GetSize());
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KMemoryManager::Open(PAddr address, size_t num_pages) {
|
||||
// Repeatedly open references until we've done so for all pages.
|
||||
while (num_pages) {
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
|
||||
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
|
||||
|
||||
{
|
||||
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
|
||||
manager.Open(address, cur_pages);
|
||||
} else {
|
||||
// Set all the allocated memory.
|
||||
for (const auto& block : out->Nodes()) {
|
||||
std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
|
||||
block.GetSize());
|
||||
}
|
||||
|
||||
num_pages -= cur_pages;
|
||||
address += cur_pages * PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
void KMemoryManager::Close(PAddr address, size_t num_pages) {
|
||||
// Repeatedly close references until we've done so for all pages.
|
||||
while (num_pages) {
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
|
||||
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
|
||||
|
||||
{
|
||||
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
|
||||
manager.Close(address, cur_pages);
|
||||
}
|
||||
|
||||
num_pages -= cur_pages;
|
||||
address += cur_pages * PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
void KMemoryManager::Close(const KPageGroup& pg) {
|
||||
for (const auto& node : pg.Nodes()) {
|
||||
Close(node.GetAddress(), node.GetNumPages());
|
||||
}
|
||||
}
|
||||
void KMemoryManager::Open(const KPageGroup& pg) {
|
||||
for (const auto& node : pg.Nodes()) {
|
||||
Open(node.GetAddress(), node.GetNumPages());
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
|
||||
@@ -394,18 +413,31 @@ size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr manage
|
||||
ASSERT(Common::IsAligned(total_management_size, PageSize));
|
||||
|
||||
// Setup region.
|
||||
pool = p;
|
||||
management_region = management;
|
||||
page_reference_counts.resize(
|
||||
m_pool = p;
|
||||
m_management_region = management;
|
||||
m_page_reference_counts.resize(
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
|
||||
ASSERT(Common::IsAligned(management_region, PageSize));
|
||||
ASSERT(Common::IsAligned(m_management_region, PageSize));
|
||||
|
||||
// Initialize the manager's KPageHeap.
|
||||
heap.Initialize(address, size, management + manager_size, page_heap_size);
|
||||
m_heap.Initialize(address, size, management + manager_size, page_heap_size);
|
||||
|
||||
return total_management_size;
|
||||
}
|
||||
|
||||
void KMemoryManager::Impl::TrackUnoptimizedAllocation(PAddr block, size_t num_pages) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void KMemoryManager::Impl::TrackOptimizedAllocation(PAddr block, size_t num_pages) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool KMemoryManager::Impl::ProcessOptimizedAllocation(PAddr block, size_t num_pages,
|
||||
u8 fill_pattern) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
|
||||
const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
|
||||
const size_t optimize_map_size =
|
||||
|
||||
@@ -21,11 +21,8 @@ namespace Kernel {
|
||||
|
||||
class KPageGroup;
|
||||
|
||||
class KMemoryManager final {
|
||||
class KMemoryManager {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryManager);
|
||||
YUZU_NON_MOVEABLE(KMemoryManager);
|
||||
|
||||
enum class Pool : u32 {
|
||||
Application = 0,
|
||||
Applet = 1,
|
||||
@@ -45,16 +42,85 @@ public:
|
||||
enum class Direction : u32 {
|
||||
FromFront = 0,
|
||||
FromBack = 1,
|
||||
|
||||
Shift = 0,
|
||||
Mask = (0xF << Shift),
|
||||
};
|
||||
|
||||
explicit KMemoryManager(Core::System& system_);
|
||||
static constexpr size_t MaxManagerCount = 10;
|
||||
|
||||
explicit KMemoryManager(Core::System& system);
|
||||
|
||||
void Initialize(VAddr management_region, size_t management_region_size);
|
||||
|
||||
constexpr size_t GetSize(Pool pool) const {
|
||||
Result InitializeOptimizedMemory(u64 process_id, Pool pool);
|
||||
void FinalizeOptimizedMemory(u64 process_id, Pool pool);
|
||||
|
||||
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
|
||||
Result AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
|
||||
u8 fill_pattern);
|
||||
|
||||
Pool GetPool(PAddr address) const {
|
||||
return this->GetManager(address).GetPool();
|
||||
}
|
||||
|
||||
void Open(PAddr address, size_t num_pages) {
|
||||
// Repeatedly open references until we've done so for all pages.
|
||||
while (num_pages) {
|
||||
auto& manager = this->GetManager(address);
|
||||
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
|
||||
|
||||
{
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
|
||||
manager.Open(address, cur_pages);
|
||||
}
|
||||
|
||||
num_pages -= cur_pages;
|
||||
address += cur_pages * PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenFirst(PAddr address, size_t num_pages) {
|
||||
// Repeatedly open references until we've done so for all pages.
|
||||
while (num_pages) {
|
||||
auto& manager = this->GetManager(address);
|
||||
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
|
||||
|
||||
{
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
|
||||
manager.OpenFirst(address, cur_pages);
|
||||
}
|
||||
|
||||
num_pages -= cur_pages;
|
||||
address += cur_pages * PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Close(PAddr address, size_t num_pages) {
|
||||
// Repeatedly close references until we've done so for all pages.
|
||||
while (num_pages) {
|
||||
auto& manager = this->GetManager(address);
|
||||
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
|
||||
|
||||
{
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
|
||||
manager.Close(address, cur_pages);
|
||||
}
|
||||
|
||||
num_pages -= cur_pages;
|
||||
address += cur_pages * PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
size_t GetSize() {
|
||||
size_t total = 0;
|
||||
for (size_t i = 0; i < m_num_managers; i++) {
|
||||
total += m_managers[i].GetSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
size_t GetSize(Pool pool) {
|
||||
constexpr Direction GetSizeDirection = Direction::FromFront;
|
||||
size_t total = 0;
|
||||
for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
|
||||
@@ -64,18 +130,36 @@ public:
|
||||
return total;
|
||||
}
|
||||
|
||||
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
|
||||
Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
|
||||
u8 fill_pattern);
|
||||
size_t GetFreeSize() {
|
||||
size_t total = 0;
|
||||
for (size_t i = 0; i < m_num_managers; i++) {
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(m_managers[i].GetPool())]);
|
||||
total += m_managers[i].GetFreeSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static constexpr size_t MaxManagerCount = 10;
|
||||
size_t GetFreeSize(Pool pool) {
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
|
||||
|
||||
void Close(PAddr address, size_t num_pages);
|
||||
void Close(const KPageGroup& pg);
|
||||
constexpr Direction GetSizeDirection = Direction::FromFront;
|
||||
size_t total = 0;
|
||||
for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
|
||||
manager = this->GetNextManager(manager, GetSizeDirection)) {
|
||||
total += manager->GetFreeSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void Open(PAddr address, size_t num_pages);
|
||||
void Open(const KPageGroup& pg);
|
||||
void DumpFreeList(Pool pool) {
|
||||
KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
|
||||
|
||||
constexpr Direction DumpDirection = Direction::FromFront;
|
||||
for (auto* manager = this->GetFirstManager(pool, DumpDirection); manager != nullptr;
|
||||
manager = this->GetNextManager(manager, DumpDirection)) {
|
||||
manager->DumpFreeList();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size) {
|
||||
@@ -88,14 +172,13 @@ public:
|
||||
}
|
||||
|
||||
static constexpr Pool GetPool(u32 option) {
|
||||
return static_cast<Pool>((static_cast<u32>(option) & static_cast<u32>(Pool::Mask)) >>
|
||||
return static_cast<Pool>((option & static_cast<u32>(Pool::Mask)) >>
|
||||
static_cast<u32>(Pool::Shift));
|
||||
}
|
||||
|
||||
static constexpr Direction GetDirection(u32 option) {
|
||||
return static_cast<Direction>(
|
||||
(static_cast<u32>(option) & static_cast<u32>(Direction::Mask)) >>
|
||||
static_cast<u32>(Direction::Shift));
|
||||
return static_cast<Direction>((option & static_cast<u32>(Direction::Mask)) >>
|
||||
static_cast<u32>(Direction::Shift));
|
||||
}
|
||||
|
||||
static constexpr std::tuple<Pool, Direction> DecodeOption(u32 option) {
|
||||
@@ -103,74 +186,88 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
class Impl final {
|
||||
class Impl {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Impl);
|
||||
YUZU_NON_MOVEABLE(Impl);
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size);
|
||||
|
||||
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
|
||||
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
}
|
||||
|
||||
public:
|
||||
Impl() = default;
|
||||
~Impl() = default;
|
||||
|
||||
size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
|
||||
Pool p);
|
||||
|
||||
VAddr AllocateBlock(s32 index, bool random) {
|
||||
return heap.AllocateBlock(index, random);
|
||||
PAddr AllocateBlock(s32 index, bool random) {
|
||||
return m_heap.AllocateBlock(index, random);
|
||||
}
|
||||
|
||||
void Free(VAddr addr, size_t num_pages) {
|
||||
heap.Free(addr, num_pages);
|
||||
PAddr AllocateAligned(s32 index, size_t num_pages, size_t align_pages) {
|
||||
return m_heap.AllocateAligned(index, num_pages, align_pages);
|
||||
}
|
||||
void Free(PAddr addr, size_t num_pages) {
|
||||
m_heap.Free(addr, num_pages);
|
||||
}
|
||||
|
||||
void SetInitialUsedHeapSize(size_t reserved_size) {
|
||||
heap.SetInitialUsedSize(reserved_size);
|
||||
m_heap.SetInitialUsedSize(reserved_size);
|
||||
}
|
||||
|
||||
void InitializeOptimizedMemory() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void TrackUnoptimizedAllocation(PAddr block, size_t num_pages);
|
||||
void TrackOptimizedAllocation(PAddr block, size_t num_pages);
|
||||
|
||||
bool ProcessOptimizedAllocation(PAddr block, size_t num_pages, u8 fill_pattern);
|
||||
|
||||
constexpr Pool GetPool() const {
|
||||
return pool;
|
||||
return m_pool;
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return heap.GetSize();
|
||||
return m_heap.GetSize();
|
||||
}
|
||||
constexpr PAddr GetEndAddress() const {
|
||||
return m_heap.GetEndAddress();
|
||||
}
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
return heap.GetAddress();
|
||||
size_t GetFreeSize() const {
|
||||
return m_heap.GetFreeSize();
|
||||
}
|
||||
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return heap.GetEndAddress();
|
||||
void DumpFreeList() const {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
constexpr size_t GetPageOffset(PAddr address) const {
|
||||
return heap.GetPageOffset(address);
|
||||
return m_heap.GetPageOffset(address);
|
||||
}
|
||||
|
||||
constexpr size_t GetPageOffsetToEnd(PAddr address) const {
|
||||
return heap.GetPageOffsetToEnd(address);
|
||||
return m_heap.GetPageOffsetToEnd(address);
|
||||
}
|
||||
|
||||
constexpr void SetNext(Impl* n) {
|
||||
next = n;
|
||||
m_next = n;
|
||||
}
|
||||
|
||||
constexpr void SetPrev(Impl* n) {
|
||||
prev = n;
|
||||
m_prev = n;
|
||||
}
|
||||
|
||||
constexpr Impl* GetNext() const {
|
||||
return next;
|
||||
return m_next;
|
||||
}
|
||||
|
||||
constexpr Impl* GetPrev() const {
|
||||
return prev;
|
||||
return m_prev;
|
||||
}
|
||||
|
||||
void OpenFirst(PAddr address, size_t num_pages) {
|
||||
size_t index = this->GetPageOffset(address);
|
||||
const size_t end = index + num_pages;
|
||||
while (index < end) {
|
||||
const RefCount ref_count = (++page_reference_counts[index]);
|
||||
const RefCount ref_count = (++m_page_reference_counts[index]);
|
||||
ASSERT(ref_count == 1);
|
||||
|
||||
index++;
|
||||
@@ -181,7 +278,7 @@ private:
|
||||
size_t index = this->GetPageOffset(address);
|
||||
const size_t end = index + num_pages;
|
||||
while (index < end) {
|
||||
const RefCount ref_count = (++page_reference_counts[index]);
|
||||
const RefCount ref_count = (++m_page_reference_counts[index]);
|
||||
ASSERT(ref_count > 1);
|
||||
|
||||
index++;
|
||||
@@ -195,8 +292,8 @@ private:
|
||||
size_t free_start = 0;
|
||||
size_t free_count = 0;
|
||||
while (index < end) {
|
||||
ASSERT(page_reference_counts[index] > 0);
|
||||
const RefCount ref_count = (--page_reference_counts[index]);
|
||||
ASSERT(m_page_reference_counts[index] > 0);
|
||||
const RefCount ref_count = (--m_page_reference_counts[index]);
|
||||
|
||||
// Keep track of how many zero refcounts we see in a row, to minimize calls to free.
|
||||
if (ref_count == 0) {
|
||||
@@ -208,7 +305,7 @@ private:
|
||||
}
|
||||
} else {
|
||||
if (free_count > 0) {
|
||||
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
|
||||
this->Free(m_heap.GetAddress() + free_start * PageSize, free_count);
|
||||
free_count = 0;
|
||||
}
|
||||
}
|
||||
@@ -217,44 +314,36 @@ private:
|
||||
}
|
||||
|
||||
if (free_count > 0) {
|
||||
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
|
||||
this->Free(m_heap.GetAddress() + free_start * PageSize, free_count);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size);
|
||||
|
||||
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
|
||||
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
}
|
||||
|
||||
private:
|
||||
using RefCount = u16;
|
||||
|
||||
KPageHeap heap;
|
||||
std::vector<RefCount> page_reference_counts;
|
||||
VAddr management_region{};
|
||||
Pool pool{};
|
||||
Impl* next{};
|
||||
Impl* prev{};
|
||||
KPageHeap m_heap;
|
||||
std::vector<RefCount> m_page_reference_counts;
|
||||
VAddr m_management_region{};
|
||||
Pool m_pool{};
|
||||
Impl* m_next{};
|
||||
Impl* m_prev{};
|
||||
};
|
||||
|
||||
private:
|
||||
Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) {
|
||||
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
|
||||
Impl& GetManager(PAddr address) {
|
||||
return m_managers[m_memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
|
||||
}
|
||||
|
||||
const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const {
|
||||
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
|
||||
const Impl& GetManager(PAddr address) const {
|
||||
return m_managers[m_memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
|
||||
}
|
||||
|
||||
constexpr Impl* GetFirstManager(Pool pool, Direction dir) const {
|
||||
return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)]
|
||||
: pool_managers_head[static_cast<size_t>(pool)];
|
||||
constexpr Impl* GetFirstManager(Pool pool, Direction dir) {
|
||||
return dir == Direction::FromBack ? m_pool_managers_tail[static_cast<size_t>(pool)]
|
||||
: m_pool_managers_head[static_cast<size_t>(pool)];
|
||||
}
|
||||
|
||||
constexpr Impl* GetNextManager(Impl* cur, Direction dir) const {
|
||||
constexpr Impl* GetNextManager(Impl* cur, Direction dir) {
|
||||
if (dir == Direction::FromBack) {
|
||||
return cur->GetPrev();
|
||||
} else {
|
||||
@@ -263,15 +352,21 @@ private:
|
||||
}
|
||||
|
||||
Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir,
|
||||
bool random);
|
||||
bool unoptimized, bool random);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks;
|
||||
std::array<Impl*, MaxManagerCount> pool_managers_head{};
|
||||
std::array<Impl*, MaxManagerCount> pool_managers_tail{};
|
||||
std::array<Impl, MaxManagerCount> managers;
|
||||
size_t num_managers{};
|
||||
template <typename T>
|
||||
using PoolArray = std::array<T, static_cast<size_t>(Pool::Count)>;
|
||||
|
||||
Core::System& m_system;
|
||||
const KMemoryLayout& m_memory_layout;
|
||||
PoolArray<KLightLock> m_pool_locks;
|
||||
std::array<Impl*, MaxManagerCount> m_pool_managers_head{};
|
||||
std::array<Impl*, MaxManagerCount> m_pool_managers_tail{};
|
||||
std::array<Impl, MaxManagerCount> m_managers;
|
||||
size_t m_num_managers{};
|
||||
PoolArray<u64> m_optimized_process_ids{};
|
||||
PoolArray<bool> m_has_optimized_process{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -142,32 +142,38 @@ private:
|
||||
|
||||
} // namespace impl
|
||||
|
||||
constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
|
||||
constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
|
||||
constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
|
||||
constexpr inline auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
|
||||
|
||||
constexpr inline auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
|
||||
constexpr inline auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
|
||||
static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1);
|
||||
static_assert(KMemoryRegionType_Dram.GetValue() == 0x2);
|
||||
|
||||
constexpr auto KMemoryRegionType_DramKernelBase =
|
||||
// constexpr inline auto KMemoryRegionType_CoreLocalRegion =
|
||||
// KMemoryRegionType_None.DeriveInitial(2).Finalize();
|
||||
// static_assert(KMemoryRegionType_CoreLocalRegion.GetValue() == 0x4);
|
||||
|
||||
constexpr inline auto KMemoryRegionType_DramKernelBase =
|
||||
KMemoryRegionType_Dram.DeriveSparse(0, 3, 0)
|
||||
.SetAttribute(KMemoryRegionAttr_NoUserMap)
|
||||
.SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||
constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
|
||||
constexpr auto KMemoryRegionType_DramHeapBase =
|
||||
constexpr inline auto KMemoryRegionType_DramReservedBase =
|
||||
KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
|
||||
constexpr inline auto KMemoryRegionType_DramHeapBase =
|
||||
KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped);
|
||||
static_assert(KMemoryRegionType_DramKernelBase.GetValue() ==
|
||||
(0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16));
|
||||
static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped));
|
||||
|
||||
constexpr auto KMemoryRegionType_DramKernelCode =
|
||||
constexpr inline auto KMemoryRegionType_DramKernelCode =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0);
|
||||
constexpr auto KMemoryRegionType_DramKernelSlab =
|
||||
constexpr inline auto KMemoryRegionType_DramKernelSlab =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1);
|
||||
constexpr auto KMemoryRegionType_DramKernelPtHeap =
|
||||
constexpr inline auto KMemoryRegionType_DramKernelPtHeap =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute(
|
||||
KMemoryRegionAttr_LinearMapped);
|
||||
constexpr auto KMemoryRegionType_DramKernelInitPt =
|
||||
constexpr inline auto KMemoryRegionType_DramKernelInitPt =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute(
|
||||
KMemoryRegionAttr_LinearMapped);
|
||||
static_assert(KMemoryRegionType_DramKernelCode.GetValue() ==
|
||||
@@ -181,32 +187,40 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
|
||||
(0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||
KMemoryRegionAttr_LinearMapped));
|
||||
|
||||
constexpr auto KMemoryRegionType_DramReservedEarly =
|
||||
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
|
||||
KMemoryRegionAttr_LinearMapped);
|
||||
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
|
||||
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||
KMemoryRegionAttr_LinearMapped));
|
||||
|
||||
constexpr inline auto KMemoryRegionType_DramReservedEarly =
|
||||
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
static_assert(KMemoryRegionType_DramReservedEarly.GetValue() ==
|
||||
(0x16 | KMemoryRegionAttr_NoUserMap));
|
||||
|
||||
constexpr auto KMemoryRegionType_KernelTraceBuffer =
|
||||
constexpr inline auto KMemoryRegionType_KernelTraceBuffer =
|
||||
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0)
|
||||
.SetAttribute(KMemoryRegionAttr_LinearMapped)
|
||||
.SetAttribute(KMemoryRegionAttr_UserReadOnly);
|
||||
constexpr auto KMemoryRegionType_OnMemoryBootImage =
|
||||
constexpr inline auto KMemoryRegionType_OnMemoryBootImage =
|
||||
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1);
|
||||
constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
|
||||
constexpr inline auto KMemoryRegionType_DTB =
|
||||
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
|
||||
static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() ==
|
||||
(0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly));
|
||||
static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156);
|
||||
static_assert(KMemoryRegionType_DTB.GetValue() == 0x256);
|
||||
|
||||
constexpr auto KMemoryRegionType_DramPoolPartition =
|
||||
constexpr inline auto KMemoryRegionType_DramPoolPartition =
|
||||
KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
||||
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||
|
||||
constexpr auto KMemoryRegionType_DramPoolManagement =
|
||||
constexpr inline auto KMemoryRegionType_DramPoolManagement =
|
||||
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
|
||||
KMemoryRegionAttr_CarveoutProtected);
|
||||
constexpr auto KMemoryRegionType_DramUserPool =
|
||||
constexpr inline auto KMemoryRegionType_DramUserPool =
|
||||
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
|
||||
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
||||
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||
@@ -214,11 +228,13 @@ static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
||||
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
||||
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||
|
||||
constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0);
|
||||
constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1);
|
||||
constexpr auto KMemoryRegionType_DramSystemNonSecurePool =
|
||||
constexpr inline auto KMemoryRegionType_DramApplicationPool =
|
||||
KMemoryRegionType_DramUserPool.Derive(4, 0);
|
||||
constexpr inline auto KMemoryRegionType_DramAppletPool =
|
||||
KMemoryRegionType_DramUserPool.Derive(4, 1);
|
||||
constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool =
|
||||
KMemoryRegionType_DramUserPool.Derive(4, 2);
|
||||
constexpr auto KMemoryRegionType_DramSystemPool =
|
||||
constexpr inline auto KMemoryRegionType_DramSystemPool =
|
||||
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
||||
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||
@@ -230,50 +246,55 @@ static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
||||
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||
KMemoryRegionAttr_CarveoutProtected));
|
||||
|
||||
constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
|
||||
constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
|
||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
|
||||
constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
|
||||
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
||||
|
||||
// UNUSED: .DeriveSparse(2, 2, 0);
|
||||
constexpr auto KMemoryRegionType_VirtualDramUnknownDebug =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug =
|
||||
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
|
||||
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
|
||||
|
||||
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
|
||||
KMemoryRegionType_Dram.DeriveSparse(3, 1, 0);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62));
|
||||
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt =
|
||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
|
||||
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement =
|
||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
|
||||
constexpr auto KMemoryRegionType_VirtualDramUserPool =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramUserPool =
|
||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
|
||||
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
|
||||
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
|
||||
|
||||
// NOTE: For unknown reason, the pools are derived out-of-order here. It's worth eventually trying
|
||||
// to understand why Nintendo made this choice.
|
||||
// NOTE: For unknown reason, the pools are derived out-of-order here.
|
||||
// It's worth eventually trying to understand why Nintendo made this choice.
|
||||
// UNUSED: .Derive(6, 0);
|
||||
// UNUSED: .Derive(6, 1);
|
||||
constexpr auto KMemoryRegionType_VirtualDramAppletPool =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramAppletPool =
|
||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
|
||||
constexpr auto KMemoryRegionType_VirtualDramApplicationPool =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool =
|
||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
|
||||
constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
|
||||
constexpr auto KMemoryRegionType_VirtualDramSystemPool =
|
||||
constexpr inline auto KMemoryRegionType_VirtualDramSystemPool =
|
||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
|
||||
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
|
||||
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
|
||||
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
|
||||
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
|
||||
|
||||
constexpr auto KMemoryRegionType_ArchDeviceBase =
|
||||
constexpr inline auto KMemoryRegionType_ArchDeviceBase =
|
||||
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
||||
constexpr auto KMemoryRegionType_BoardDeviceBase =
|
||||
constexpr inline auto KMemoryRegionType_BoardDeviceBase =
|
||||
KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly();
|
||||
static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5);
|
||||
static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
|
||||
@@ -284,7 +305,7 @@ static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
|
||||
#error "Unimplemented"
|
||||
#else
|
||||
// Default to no architecture devices.
|
||||
constexpr auto NumArchitectureDeviceRegions = 0;
|
||||
constexpr inline auto NumArchitectureDeviceRegions = 0;
|
||||
#endif
|
||||
static_assert(NumArchitectureDeviceRegions >= 0);
|
||||
|
||||
@@ -292,34 +313,35 @@ static_assert(NumArchitectureDeviceRegions >= 0);
|
||||
#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc"
|
||||
#else
|
||||
// Default to no board devices.
|
||||
constexpr auto NumBoardDeviceRegions = 0;
|
||||
constexpr inline auto NumBoardDeviceRegions = 0;
|
||||
#endif
|
||||
static_assert(NumBoardDeviceRegions >= 0);
|
||||
|
||||
constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
|
||||
constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
|
||||
constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
|
||||
constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
|
||||
constexpr inline auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
|
||||
constexpr inline auto KMemoryRegionType_KernelStack =
|
||||
KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
|
||||
constexpr inline auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
|
||||
constexpr inline auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
|
||||
static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19);
|
||||
static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29);
|
||||
static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49);
|
||||
static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89);
|
||||
|
||||
constexpr auto KMemoryRegionType_KernelMiscDerivedBase =
|
||||
constexpr inline auto KMemoryRegionType_KernelMiscDerivedBase =
|
||||
KMemoryRegionType_KernelMisc.DeriveTransition();
|
||||
static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149);
|
||||
|
||||
// UNUSED: .Derive(7, 0);
|
||||
constexpr auto KMemoryRegionType_KernelMiscMainStack =
|
||||
constexpr inline auto KMemoryRegionType_KernelMiscMainStack =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1);
|
||||
constexpr auto KMemoryRegionType_KernelMiscMappedDevice =
|
||||
constexpr inline auto KMemoryRegionType_KernelMiscMappedDevice =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2);
|
||||
constexpr auto KMemoryRegionType_KernelMiscExceptionStack =
|
||||
constexpr inline auto KMemoryRegionType_KernelMiscExceptionStack =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3);
|
||||
constexpr auto KMemoryRegionType_KernelMiscUnknownDebug =
|
||||
constexpr inline auto KMemoryRegionType_KernelMiscUnknownDebug =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4);
|
||||
// UNUSED: .Derive(7, 5);
|
||||
constexpr auto KMemoryRegionType_KernelMiscIdleStack =
|
||||
constexpr inline auto KMemoryRegionType_KernelMiscIdleStack =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6);
|
||||
static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49);
|
||||
static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49);
|
||||
@@ -327,7 +349,8 @@ static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349);
|
||||
static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549);
|
||||
static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349);
|
||||
|
||||
constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
|
||||
constexpr inline auto KMemoryRegionType_KernelTemp =
|
||||
KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
|
||||
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
||||
|
||||
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
||||
@@ -335,6 +358,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
||||
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
||||
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
||||
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
||||
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
|
||||
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
|
||||
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
||||
return KMemoryRegionType_VirtualDramUnknownDebug;
|
||||
} else {
|
||||
|
||||
@@ -16,107 +16,126 @@
|
||||
namespace Kernel {
|
||||
|
||||
class KPageBitmap {
|
||||
private:
|
||||
public:
|
||||
class RandomBitGenerator {
|
||||
private:
|
||||
Common::TinyMT rng{};
|
||||
u32 entropy{};
|
||||
u32 bits_available{};
|
||||
|
||||
private:
|
||||
void RefreshEntropy() {
|
||||
entropy = rng.GenerateRandomU32();
|
||||
bits_available = static_cast<u32>(Common::BitSize<decltype(entropy)>());
|
||||
}
|
||||
|
||||
bool GenerateRandomBit() {
|
||||
if (bits_available == 0) {
|
||||
this->RefreshEntropy();
|
||||
}
|
||||
|
||||
const bool rnd_bit = (entropy & 1) != 0;
|
||||
entropy >>= 1;
|
||||
--bits_available;
|
||||
return rnd_bit;
|
||||
}
|
||||
|
||||
public:
|
||||
RandomBitGenerator() {
|
||||
rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
|
||||
m_rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
|
||||
}
|
||||
|
||||
std::size_t SelectRandomBit(u64 bitmap) {
|
||||
u64 SelectRandomBit(u64 bitmap) {
|
||||
u64 selected = 0;
|
||||
|
||||
u64 cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2;
|
||||
u64 cur_mask = (1ULL << cur_num_bits) - 1;
|
||||
for (size_t cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2; cur_num_bits != 0;
|
||||
cur_num_bits /= 2) {
|
||||
const u64 high = (bitmap >> cur_num_bits);
|
||||
const u64 low = (bitmap & (~(UINT64_C(0xFFFFFFFFFFFFFFFF) << cur_num_bits)));
|
||||
|
||||
while (cur_num_bits) {
|
||||
const u64 low = (bitmap >> 0) & cur_mask;
|
||||
const u64 high = (bitmap >> cur_num_bits) & cur_mask;
|
||||
|
||||
bool choose_low;
|
||||
if (high == 0) {
|
||||
// If only low val is set, choose low.
|
||||
choose_low = true;
|
||||
} else if (low == 0) {
|
||||
// If only high val is set, choose high.
|
||||
choose_low = false;
|
||||
} else {
|
||||
// If both are set, choose random.
|
||||
choose_low = this->GenerateRandomBit();
|
||||
}
|
||||
|
||||
// If we chose low, proceed with low.
|
||||
if (choose_low) {
|
||||
bitmap = low;
|
||||
selected += 0;
|
||||
} else {
|
||||
// Choose high if we have high and (don't have low or select high randomly).
|
||||
if (high && (low == 0 || this->GenerateRandomBit())) {
|
||||
bitmap = high;
|
||||
selected += cur_num_bits;
|
||||
} else {
|
||||
bitmap = low;
|
||||
selected += 0;
|
||||
}
|
||||
|
||||
// Proceed.
|
||||
cur_num_bits /= 2;
|
||||
cur_mask >>= cur_num_bits;
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
u64 GenerateRandom(u64 max) {
|
||||
// Determine the number of bits we need.
|
||||
const u64 bits_needed = 1 + (Common::BitSize<decltype(max)>() - std::countl_zero(max));
|
||||
|
||||
// Generate a random value of the desired bitwidth.
|
||||
const u64 rnd = this->GenerateRandomBits(static_cast<u32>(bits_needed));
|
||||
|
||||
// Adjust the value to be in range.
|
||||
return rnd - ((rnd / max) * max);
|
||||
}
|
||||
|
||||
private:
|
||||
void RefreshEntropy() {
|
||||
m_entropy = m_rng.GenerateRandomU32();
|
||||
m_bits_available = static_cast<u32>(Common::BitSize<decltype(m_entropy)>());
|
||||
}
|
||||
|
||||
bool GenerateRandomBit() {
|
||||
if (m_bits_available == 0) {
|
||||
this->RefreshEntropy();
|
||||
}
|
||||
|
||||
const bool rnd_bit = (m_entropy & 1) != 0;
|
||||
m_entropy >>= 1;
|
||||
--m_bits_available;
|
||||
return rnd_bit;
|
||||
}
|
||||
|
||||
u64 GenerateRandomBits(u32 num_bits) {
|
||||
u64 result = 0;
|
||||
|
||||
// Iteratively add random bits to our result.
|
||||
while (num_bits > 0) {
|
||||
// Ensure we have random bits to take from.
|
||||
if (m_bits_available == 0) {
|
||||
this->RefreshEntropy();
|
||||
}
|
||||
|
||||
// Determine how many bits to take this round.
|
||||
const auto cur_bits = std::min(num_bits, m_bits_available);
|
||||
|
||||
// Generate mask for our current bits.
|
||||
const u64 mask = (static_cast<u64>(1) << cur_bits) - 1;
|
||||
|
||||
// Add bits to output from our entropy.
|
||||
result <<= cur_bits;
|
||||
result |= (m_entropy & mask);
|
||||
|
||||
// Remove bits from our entropy.
|
||||
m_entropy >>= cur_bits;
|
||||
m_bits_available -= cur_bits;
|
||||
|
||||
// Advance.
|
||||
num_bits -= cur_bits;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::TinyMT m_rng;
|
||||
u32 m_entropy{};
|
||||
u32 m_bits_available{};
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr std::size_t MaxDepth = 4;
|
||||
|
||||
private:
|
||||
std::array<u64*, MaxDepth> bit_storages{};
|
||||
RandomBitGenerator rng{};
|
||||
std::size_t num_bits{};
|
||||
std::size_t used_depths{};
|
||||
static constexpr size_t MaxDepth = 4;
|
||||
|
||||
public:
|
||||
KPageBitmap() = default;
|
||||
|
||||
constexpr std::size_t GetNumBits() const {
|
||||
return num_bits;
|
||||
constexpr size_t GetNumBits() const {
|
||||
return m_num_bits;
|
||||
}
|
||||
constexpr s32 GetHighestDepthIndex() const {
|
||||
return static_cast<s32>(used_depths) - 1;
|
||||
return static_cast<s32>(m_used_depths) - 1;
|
||||
}
|
||||
|
||||
u64* Initialize(u64* storage, std::size_t size) {
|
||||
u64* Initialize(u64* storage, size_t size) {
|
||||
// Initially, everything is un-set.
|
||||
num_bits = 0;
|
||||
m_num_bits = 0;
|
||||
|
||||
// Calculate the needed bitmap depth.
|
||||
used_depths = static_cast<std::size_t>(GetRequiredDepth(size));
|
||||
ASSERT(used_depths <= MaxDepth);
|
||||
m_used_depths = static_cast<size_t>(GetRequiredDepth(size));
|
||||
ASSERT(m_used_depths <= MaxDepth);
|
||||
|
||||
// Set the bitmap pointers.
|
||||
for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) {
|
||||
bit_storages[depth] = storage;
|
||||
m_bit_storages[depth] = storage;
|
||||
size = Common::AlignUp(size, Common::BitSize<u64>()) / Common::BitSize<u64>();
|
||||
storage += size;
|
||||
m_end_storages[depth] = storage;
|
||||
}
|
||||
|
||||
return storage;
|
||||
@@ -128,19 +147,19 @@ public:
|
||||
|
||||
if (random) {
|
||||
do {
|
||||
const u64 v = bit_storages[depth][offset];
|
||||
const u64 v = m_bit_storages[depth][offset];
|
||||
if (v == 0) {
|
||||
// If depth is bigger than zero, then a previous level indicated a block was
|
||||
// free.
|
||||
ASSERT(depth == 0);
|
||||
return -1;
|
||||
}
|
||||
offset = offset * Common::BitSize<u64>() + rng.SelectRandomBit(v);
|
||||
offset = offset * Common::BitSize<u64>() + m_rng.SelectRandomBit(v);
|
||||
++depth;
|
||||
} while (depth < static_cast<s32>(used_depths));
|
||||
} while (depth < static_cast<s32>(m_used_depths));
|
||||
} else {
|
||||
do {
|
||||
const u64 v = bit_storages[depth][offset];
|
||||
const u64 v = m_bit_storages[depth][offset];
|
||||
if (v == 0) {
|
||||
// If depth is bigger than zero, then a previous level indicated a block was
|
||||
// free.
|
||||
@@ -149,28 +168,69 @@ public:
|
||||
}
|
||||
offset = offset * Common::BitSize<u64>() + std::countr_zero(v);
|
||||
++depth;
|
||||
} while (depth < static_cast<s32>(used_depths));
|
||||
} while (depth < static_cast<s32>(m_used_depths));
|
||||
}
|
||||
|
||||
return static_cast<s64>(offset);
|
||||
}
|
||||
|
||||
void SetBit(std::size_t offset) {
|
||||
s64 FindFreeRange(size_t count) {
|
||||
// Check that it is possible to find a range.
|
||||
const u64* const storage_start = m_bit_storages[m_used_depths - 1];
|
||||
const u64* const storage_end = m_end_storages[m_used_depths - 1];
|
||||
|
||||
// If we don't have a storage to iterate (or want more blocks than fit in a single storage),
|
||||
// we can't find a free range.
|
||||
if (!(storage_start < storage_end && count <= Common::BitSize<u64>())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Walk the storages to select a random free range.
|
||||
const size_t options_per_storage = std::max<size_t>(Common::BitSize<u64>() / count, 1);
|
||||
const size_t num_entries = std::max<size_t>(storage_end - storage_start, 1);
|
||||
|
||||
const u64 free_mask = (static_cast<u64>(1) << count) - 1;
|
||||
|
||||
size_t num_valid_options = 0;
|
||||
s64 chosen_offset = -1;
|
||||
for (size_t storage_index = 0; storage_index < num_entries; ++storage_index) {
|
||||
u64 storage = storage_start[storage_index];
|
||||
for (size_t option = 0; option < options_per_storage; ++option) {
|
||||
if ((storage & free_mask) == free_mask) {
|
||||
// We've found a new valid option.
|
||||
++num_valid_options;
|
||||
|
||||
// Select the Kth valid option with probability 1/K. This leads to an overall
|
||||
// uniform distribution.
|
||||
if (num_valid_options == 1 || m_rng.GenerateRandom(num_valid_options) == 0) {
|
||||
// This is our first option, so select it.
|
||||
chosen_offset = storage_index * Common::BitSize<u64>() + option * count;
|
||||
}
|
||||
}
|
||||
storage >>= count;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the random offset we chose.*/
|
||||
return chosen_offset;
|
||||
}
|
||||
|
||||
void SetBit(size_t offset) {
|
||||
this->SetBit(this->GetHighestDepthIndex(), offset);
|
||||
num_bits++;
|
||||
m_num_bits++;
|
||||
}
|
||||
|
||||
void ClearBit(std::size_t offset) {
|
||||
void ClearBit(size_t offset) {
|
||||
this->ClearBit(this->GetHighestDepthIndex(), offset);
|
||||
num_bits--;
|
||||
m_num_bits--;
|
||||
}
|
||||
|
||||
bool ClearRange(std::size_t offset, std::size_t count) {
|
||||
bool ClearRange(size_t offset, size_t count) {
|
||||
s32 depth = this->GetHighestDepthIndex();
|
||||
u64* bits = bit_storages[depth];
|
||||
std::size_t bit_ind = offset / Common::BitSize<u64>();
|
||||
if (count < Common::BitSize<u64>()) {
|
||||
const std::size_t shift = offset % Common::BitSize<u64>();
|
||||
u64* bits = m_bit_storages[depth];
|
||||
size_t bit_ind = offset / Common::BitSize<u64>();
|
||||
if (count < Common::BitSize<u64>()) [[likely]] {
|
||||
const size_t shift = offset % Common::BitSize<u64>();
|
||||
ASSERT(shift + count <= Common::BitSize<u64>());
|
||||
// Check that all the bits are set.
|
||||
const u64 mask = ((u64(1) << count) - 1) << shift;
|
||||
@@ -189,8 +249,8 @@ public:
|
||||
ASSERT(offset % Common::BitSize<u64>() == 0);
|
||||
ASSERT(count % Common::BitSize<u64>() == 0);
|
||||
// Check that all the bits are set.
|
||||
std::size_t remaining = count;
|
||||
std::size_t i = 0;
|
||||
size_t remaining = count;
|
||||
size_t i = 0;
|
||||
do {
|
||||
if (bits[bit_ind + i++] != ~u64(0)) {
|
||||
return false;
|
||||
@@ -209,18 +269,18 @@ public:
|
||||
} while (remaining > 0);
|
||||
}
|
||||
|
||||
num_bits -= count;
|
||||
m_num_bits -= count;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetBit(s32 depth, std::size_t offset) {
|
||||
void SetBit(s32 depth, size_t offset) {
|
||||
while (depth >= 0) {
|
||||
std::size_t ind = offset / Common::BitSize<u64>();
|
||||
std::size_t which = offset % Common::BitSize<u64>();
|
||||
size_t ind = offset / Common::BitSize<u64>();
|
||||
size_t which = offset % Common::BitSize<u64>();
|
||||
const u64 mask = u64(1) << which;
|
||||
|
||||
u64* bit = std::addressof(bit_storages[depth][ind]);
|
||||
u64* bit = std::addressof(m_bit_storages[depth][ind]);
|
||||
u64 v = *bit;
|
||||
ASSERT((v & mask) == 0);
|
||||
*bit = v | mask;
|
||||
@@ -232,13 +292,13 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void ClearBit(s32 depth, std::size_t offset) {
|
||||
void ClearBit(s32 depth, size_t offset) {
|
||||
while (depth >= 0) {
|
||||
std::size_t ind = offset / Common::BitSize<u64>();
|
||||
std::size_t which = offset % Common::BitSize<u64>();
|
||||
size_t ind = offset / Common::BitSize<u64>();
|
||||
size_t which = offset % Common::BitSize<u64>();
|
||||
const u64 mask = u64(1) << which;
|
||||
|
||||
u64* bit = std::addressof(bit_storages[depth][ind]);
|
||||
u64* bit = std::addressof(m_bit_storages[depth][ind]);
|
||||
u64 v = *bit;
|
||||
ASSERT((v & mask) != 0);
|
||||
v &= ~mask;
|
||||
@@ -252,7 +312,7 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr s32 GetRequiredDepth(std::size_t region_size) {
|
||||
static constexpr s32 GetRequiredDepth(size_t region_size) {
|
||||
s32 depth = 0;
|
||||
while (true) {
|
||||
region_size /= Common::BitSize<u64>();
|
||||
@@ -264,8 +324,8 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
|
||||
std::size_t overhead_bits = 0;
|
||||
static constexpr size_t CalculateManagementOverheadSize(size_t region_size) {
|
||||
size_t overhead_bits = 0;
|
||||
for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) {
|
||||
region_size =
|
||||
Common::AlignUp(region_size, Common::BitSize<u64>()) / Common::BitSize<u64>();
|
||||
@@ -273,6 +333,13 @@ public:
|
||||
}
|
||||
return overhead_bits * sizeof(u64);
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<u64*, MaxDepth> m_bit_storages{};
|
||||
std::array<u64*, MaxDepth> m_end_storages{};
|
||||
RandomBitGenerator m_rng;
|
||||
size_t m_num_bits{};
|
||||
size_t m_used_depths{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -11,6 +11,16 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
class KPageBufferSlabHeap : protected impl::KSlabHeapImpl {
|
||||
public:
|
||||
static constexpr size_t BufferSize = PageSize;
|
||||
|
||||
public:
|
||||
void Initialize(Core::System& system);
|
||||
};
|
||||
|
||||
class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
|
||||
public:
|
||||
explicit KPageBuffer(KernelCore&) {}
|
||||
@@ -21,8 +31,6 @@ public:
|
||||
private:
|
||||
[[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
|
||||
};
|
||||
|
||||
static_assert(sizeof(KPageBuffer) == PageSize);
|
||||
static_assert(alignof(KPageBuffer) == PageSize);
|
||||
static_assert(sizeof(KPageBuffer) == KPageBufferSlabHeap::BufferSize);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
@@ -12,6 +13,89 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageGroup;
|
||||
|
||||
class KBlockInfo {
|
||||
private:
|
||||
friend class KPageGroup;
|
||||
|
||||
public:
|
||||
constexpr KBlockInfo() = default;
|
||||
|
||||
constexpr void Initialize(PAddr addr, size_t np) {
|
||||
ASSERT(Common::IsAligned(addr, PageSize));
|
||||
ASSERT(static_cast<u32>(np) == np);
|
||||
|
||||
m_page_index = static_cast<u32>(addr) / PageSize;
|
||||
m_num_pages = static_cast<u32>(np);
|
||||
}
|
||||
|
||||
constexpr PAddr GetAddress() const {
|
||||
return m_page_index * PageSize;
|
||||
}
|
||||
constexpr size_t GetNumPages() const {
|
||||
return m_num_pages;
|
||||
}
|
||||
constexpr size_t GetSize() const {
|
||||
return this->GetNumPages() * PageSize;
|
||||
}
|
||||
constexpr PAddr GetEndAddress() const {
|
||||
return (m_page_index + m_num_pages) * PageSize;
|
||||
}
|
||||
constexpr PAddr GetLastAddress() const {
|
||||
return this->GetEndAddress() - 1;
|
||||
}
|
||||
|
||||
constexpr KBlockInfo* GetNext() const {
|
||||
return m_next;
|
||||
}
|
||||
|
||||
constexpr bool IsEquivalentTo(const KBlockInfo& rhs) const {
|
||||
return m_page_index == rhs.m_page_index && m_num_pages == rhs.m_num_pages;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const KBlockInfo& rhs) const {
|
||||
return this->IsEquivalentTo(rhs);
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const KBlockInfo& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr bool IsStrictlyBefore(PAddr addr) const {
|
||||
const PAddr end = this->GetEndAddress();
|
||||
|
||||
if (m_page_index != 0 && end == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return end < addr;
|
||||
}
|
||||
|
||||
constexpr bool operator<(PAddr addr) const {
|
||||
return this->IsStrictlyBefore(addr);
|
||||
}
|
||||
|
||||
constexpr bool TryConcatenate(PAddr addr, size_t np) {
|
||||
if (addr != 0 && addr == this->GetEndAddress()) {
|
||||
m_num_pages += static_cast<u32>(np);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void SetNext(KBlockInfo* next) {
|
||||
m_next = next;
|
||||
}
|
||||
|
||||
private:
|
||||
KBlockInfo* m_next{};
|
||||
u32 m_page_index{};
|
||||
u32 m_num_pages{};
|
||||
};
|
||||
static_assert(sizeof(KBlockInfo) <= 0x10);
|
||||
|
||||
class KPageGroup final {
|
||||
public:
|
||||
class Node final {
|
||||
@@ -92,6 +176,8 @@ public:
|
||||
return nodes.empty();
|
||||
}
|
||||
|
||||
void Finalize() {}
|
||||
|
||||
private:
|
||||
std::list<Node> nodes;
|
||||
};
|
||||
|
||||
@@ -44,11 +44,11 @@ size_t KPageHeap::GetNumFreePages() const {
|
||||
return num_free;
|
||||
}
|
||||
|
||||
PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
|
||||
PAddr KPageHeap::AllocateByLinearSearch(s32 index) {
|
||||
const size_t needed_size = m_blocks[index].GetSize();
|
||||
|
||||
for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
|
||||
if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) {
|
||||
if (const PAddr addr = m_blocks[i].PopBlock(false); addr != 0) {
|
||||
if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
|
||||
this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
|
||||
}
|
||||
@@ -59,6 +59,88 @@ PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PAddr KPageHeap::AllocateByRandom(s32 index, size_t num_pages, size_t align_pages) {
|
||||
// Get the size and required alignment.
|
||||
const size_t needed_size = num_pages * PageSize;
|
||||
const size_t align_size = align_pages * PageSize;
|
||||
|
||||
// Determine meta-alignment of our desired alignment size.
|
||||
const size_t align_shift = std::countr_zero(align_size);
|
||||
|
||||
// Decide on a block to allocate from.
|
||||
constexpr size_t MinimumPossibleAlignmentsForRandomAllocation = 4;
|
||||
{
|
||||
// By default, we'll want to look at all blocks larger than our current one.
|
||||
s32 max_blocks = static_cast<s32>(m_num_blocks);
|
||||
|
||||
// Determine the maximum block we should try to allocate from.
|
||||
size_t possible_alignments = 0;
|
||||
for (s32 i = index; i < max_blocks; ++i) {
|
||||
// Add the possible alignments from blocks at the current size.
|
||||
possible_alignments += (1 + ((m_blocks[i].GetSize() - needed_size) >> align_shift)) *
|
||||
m_blocks[i].GetNumFreeBlocks();
|
||||
|
||||
// If there are enough possible alignments, we don't need to look at larger blocks.
|
||||
if (possible_alignments >= MinimumPossibleAlignmentsForRandomAllocation) {
|
||||
max_blocks = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have any possible alignments which require a larger block, we need to pick one.
|
||||
if (possible_alignments > 0 && index + 1 < max_blocks) {
|
||||
// Select a random alignment from the possibilities.
|
||||
const size_t rnd = m_rng.GenerateRandom(possible_alignments);
|
||||
|
||||
// Determine which block corresponds to the random alignment we chose.
|
||||
possible_alignments = 0;
|
||||
for (s32 i = index; i < max_blocks; ++i) {
|
||||
// Add the possible alignments from blocks at the current size.
|
||||
possible_alignments +=
|
||||
(1 + ((m_blocks[i].GetSize() - needed_size) >> align_shift)) *
|
||||
m_blocks[i].GetNumFreeBlocks();
|
||||
|
||||
// If the current block gets us to our random choice, use the current block.
|
||||
if (rnd < possible_alignments) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pop a block from the index we selected.
|
||||
if (PAddr addr = m_blocks[index].PopBlock(true); addr != 0) {
|
||||
// Determine how much size we have left over.
|
||||
if (const size_t leftover_size = m_blocks[index].GetSize() - needed_size;
|
||||
leftover_size > 0) {
|
||||
// Determine how many valid alignments we can have.
|
||||
const size_t possible_alignments = 1 + (leftover_size >> align_shift);
|
||||
|
||||
// Select a random valid alignment.
|
||||
const size_t random_offset = m_rng.GenerateRandom(possible_alignments) << align_shift;
|
||||
|
||||
// Free memory before the random offset.
|
||||
if (random_offset != 0) {
|
||||
this->Free(addr, random_offset / PageSize);
|
||||
}
|
||||
|
||||
// Advance our block by the random offset.
|
||||
addr += random_offset;
|
||||
|
||||
// Free memory after our allocated block.
|
||||
if (random_offset != leftover_size) {
|
||||
this->Free(addr + needed_size, (leftover_size - random_offset) / PageSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the block we allocated.
|
||||
return addr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KPageHeap::FreeBlock(PAddr block, s32 index) {
|
||||
do {
|
||||
block = m_blocks[index++].PushBlock(block);
|
||||
|
||||
@@ -14,13 +14,9 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageHeap final {
|
||||
class KPageHeap {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KPageHeap);
|
||||
YUZU_NON_MOVEABLE(KPageHeap);
|
||||
|
||||
KPageHeap() = default;
|
||||
~KPageHeap() = default;
|
||||
|
||||
constexpr PAddr GetAddress() const {
|
||||
return m_heap_address;
|
||||
@@ -57,7 +53,20 @@ public:
|
||||
m_initial_used_size = m_heap_size - free_size - reserved_size;
|
||||
}
|
||||
|
||||
PAddr AllocateBlock(s32 index, bool random);
|
||||
PAddr AllocateBlock(s32 index, bool random) {
|
||||
if (random) {
|
||||
const size_t block_pages = m_blocks[index].GetNumPages();
|
||||
return this->AllocateByRandom(index, block_pages, block_pages);
|
||||
} else {
|
||||
return this->AllocateByLinearSearch(index);
|
||||
}
|
||||
}
|
||||
|
||||
PAddr AllocateAligned(s32 index, size_t num_pages, size_t align_pages) {
|
||||
// TODO: linear search support?
|
||||
return this->AllocateByRandom(index, num_pages, align_pages);
|
||||
}
|
||||
|
||||
void Free(PAddr addr, size_t num_pages);
|
||||
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size) {
|
||||
@@ -68,7 +77,7 @@ public:
|
||||
static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
|
||||
const size_t target_pages = std::max(num_pages, align_pages);
|
||||
for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
|
||||
if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
if (target_pages <= (static_cast<size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
return static_cast<s32>(i);
|
||||
}
|
||||
}
|
||||
@@ -77,7 +86,7 @@ public:
|
||||
|
||||
static constexpr s32 GetBlockIndex(size_t num_pages) {
|
||||
for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
|
||||
if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
if (num_pages >= (static_cast<size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -85,7 +94,7 @@ public:
|
||||
}
|
||||
|
||||
static constexpr size_t GetBlockSize(size_t index) {
|
||||
return size_t(1) << MemoryBlockPageShifts[index];
|
||||
return static_cast<size_t>(1) << MemoryBlockPageShifts[index];
|
||||
}
|
||||
|
||||
static constexpr size_t GetBlockNumPages(size_t index) {
|
||||
@@ -93,13 +102,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
class Block final {
|
||||
class Block {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Block);
|
||||
YUZU_NON_MOVEABLE(Block);
|
||||
|
||||
Block() = default;
|
||||
~Block() = default;
|
||||
|
||||
constexpr size_t GetShift() const {
|
||||
return m_block_shift;
|
||||
@@ -201,6 +206,9 @@ private:
|
||||
};
|
||||
|
||||
private:
|
||||
PAddr AllocateByLinearSearch(s32 index);
|
||||
PAddr AllocateByRandom(s32 index, size_t num_pages, size_t align_pages);
|
||||
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
|
||||
size_t num_block_shifts);
|
||||
|
||||
@@ -209,7 +217,8 @@ private:
|
||||
size_t m_heap_size{};
|
||||
size_t m_initial_used_size{};
|
||||
size_t m_num_blocks{};
|
||||
std::array<Block, NumMemoryBlockPageShifts> m_blocks{};
|
||||
std::array<Block, NumMemoryBlockPageShifts> m_blocks;
|
||||
KPageBitmap::RandomBitGenerator m_rng;
|
||||
std::vector<u64> m_management_data;
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -23,7 +24,10 @@ class System;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KBlockInfoManager;
|
||||
class KMemoryBlockManager;
|
||||
class KResourceLimit;
|
||||
class KSystemResource;
|
||||
|
||||
class KPageTable final {
|
||||
public:
|
||||
@@ -36,9 +40,9 @@ public:
|
||||
~KPageTable();
|
||||
|
||||
Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
|
||||
VAddr code_addr, size_t code_size,
|
||||
KMemoryBlockSlabManager* mem_block_slab_manager,
|
||||
KMemoryManager::Pool pool);
|
||||
bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
|
||||
VAddr code_addr, size_t code_size, KSystemResource* system_resource,
|
||||
KResourceLimit* resource_limit);
|
||||
|
||||
void Finalize();
|
||||
|
||||
@@ -74,12 +78,20 @@ public:
|
||||
KMemoryState state, KMemoryPermission perm,
|
||||
PAddr map_addr = 0);
|
||||
|
||||
Result LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm,
|
||||
bool is_aligned);
|
||||
Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size);
|
||||
Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
|
||||
KMemoryPermission perm, bool is_aligned, bool check_heap);
|
||||
Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap);
|
||||
|
||||
Result UnlockForDeviceAddressSpace(VAddr addr, size_t size);
|
||||
|
||||
Result LockForIpcUserBuffer(PAddr* out, VAddr address, size_t size);
|
||||
Result UnlockForIpcUserBuffer(VAddr address, size_t size);
|
||||
|
||||
Result SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr, KPageTable& src_page_table,
|
||||
KMemoryPermission test_perm, KMemoryState dst_state, bool send);
|
||||
Result CleanupForIpcServer(VAddr address, size_t size, KMemoryState dst_state);
|
||||
Result CleanupForIpcClient(VAddr address, size_t size, KMemoryState dst_state);
|
||||
|
||||
Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size);
|
||||
Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg);
|
||||
Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
|
||||
@@ -97,13 +109,54 @@ public:
|
||||
|
||||
bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
|
||||
|
||||
protected:
|
||||
struct PageLinkedList {
|
||||
private:
|
||||
struct Node {
|
||||
Node* m_next;
|
||||
std::array<u8, PageSize - sizeof(Node*)> m_buffer;
|
||||
};
|
||||
|
||||
public:
|
||||
constexpr PageLinkedList() = default;
|
||||
|
||||
void Push(Node* n) {
|
||||
ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
|
||||
n->m_next = m_root;
|
||||
m_root = n;
|
||||
}
|
||||
|
||||
void Push(Core::Memory::Memory& memory, VAddr addr) {
|
||||
this->Push(memory.GetPointer<Node>(addr));
|
||||
}
|
||||
|
||||
Node* Peek() const {
|
||||
return m_root;
|
||||
}
|
||||
|
||||
Node* Pop() {
|
||||
Node* const r = m_root;
|
||||
|
||||
m_root = r->m_next;
|
||||
r->m_next = nullptr;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private:
|
||||
Node* m_root{};
|
||||
};
|
||||
static_assert(std::is_trivially_destructible<PageLinkedList>::value);
|
||||
|
||||
private:
|
||||
enum class OperationType : u32 {
|
||||
Map,
|
||||
MapGroup,
|
||||
Unmap,
|
||||
ChangePermissions,
|
||||
ChangePermissionsAndRefresh,
|
||||
Map = 0,
|
||||
MapFirst = 1,
|
||||
MapGroup = 2,
|
||||
Unmap = 3,
|
||||
ChangePermissions = 4,
|
||||
ChangePermissionsAndRefresh = 5,
|
||||
Separate = 6,
|
||||
};
|
||||
|
||||
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
|
||||
@@ -123,6 +176,7 @@ private:
|
||||
OperationType operation);
|
||||
Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation,
|
||||
PAddr map_addr = 0);
|
||||
void FinalizeUpdate(PageLinkedList* page_list);
|
||||
VAddr GetRegionAddress(KMemoryState state) const;
|
||||
size_t GetRegionSize(KMemoryState state) const;
|
||||
|
||||
@@ -199,6 +253,18 @@ private:
|
||||
return *out != 0;
|
||||
}
|
||||
|
||||
Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, VAddr address,
|
||||
size_t size, KMemoryPermission test_perm, KMemoryState dst_state);
|
||||
Result SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_addr,
|
||||
KMemoryPermission test_perm, KMemoryState dst_state,
|
||||
KPageTable& src_page_table, bool send);
|
||||
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
|
||||
size_t size, KMemoryPermission prot_perm);
|
||||
|
||||
// HACK: These will be removed once we automatically manage page reference counts.
|
||||
void HACK_OpenPages(PAddr phys_addr, size_t num_pages);
|
||||
void HACK_ClosePages(VAddr virt_addr, size_t num_pages);
|
||||
|
||||
mutable KLightLock m_general_lock;
|
||||
mutable KLightLock m_map_physical_memory_lock;
|
||||
|
||||
@@ -254,6 +320,9 @@ public:
|
||||
constexpr VAddr GetAliasCodeRegionStart() const {
|
||||
return m_alias_code_region_start;
|
||||
}
|
||||
constexpr VAddr GetAliasCodeRegionEnd() const {
|
||||
return m_alias_code_region_end;
|
||||
}
|
||||
constexpr VAddr GetAliasCodeRegionSize() const {
|
||||
return m_alias_code_region_end - m_alias_code_region_start;
|
||||
}
|
||||
@@ -316,6 +385,31 @@ public:
|
||||
addr + size - 1 <= m_address_space_end - 1;
|
||||
}
|
||||
|
||||
public:
|
||||
static VAddr GetLinearMappedVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
|
||||
return layout.GetLinearVirtualAddress(addr);
|
||||
}
|
||||
|
||||
static PAddr GetLinearMappedPhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
|
||||
return layout.GetLinearPhysicalAddress(addr);
|
||||
}
|
||||
|
||||
static VAddr GetHeapVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
|
||||
return GetLinearMappedVirtualAddress(layout, addr);
|
||||
}
|
||||
|
||||
static PAddr GetHeapPhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
|
||||
return GetLinearMappedPhysicalAddress(layout, addr);
|
||||
}
|
||||
|
||||
static VAddr GetPageTableVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
|
||||
return GetLinearMappedVirtualAddress(layout, addr);
|
||||
}
|
||||
|
||||
static PAddr GetPageTablePhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
|
||||
return GetLinearMappedPhysicalAddress(layout, addr);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr bool IsKernel() const {
|
||||
return m_is_kernel;
|
||||
@@ -330,6 +424,24 @@ private:
|
||||
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1);
|
||||
}
|
||||
|
||||
private:
|
||||
class KScopedPageTableUpdater {
|
||||
private:
|
||||
KPageTable* m_pt{};
|
||||
PageLinkedList m_ll;
|
||||
|
||||
public:
|
||||
explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {}
|
||||
explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {}
|
||||
~KScopedPageTableUpdater() {
|
||||
m_pt->FinalizeUpdate(this->GetPageList());
|
||||
}
|
||||
|
||||
PageLinkedList* GetPageList() {
|
||||
return &m_ll;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
VAddr m_address_space_start{};
|
||||
VAddr m_address_space_end{};
|
||||
@@ -347,20 +459,27 @@ private:
|
||||
VAddr m_alias_code_region_start{};
|
||||
VAddr m_alias_code_region_end{};
|
||||
|
||||
size_t m_mapped_physical_memory_size{};
|
||||
size_t m_max_heap_size{};
|
||||
size_t m_max_physical_memory_size{};
|
||||
size_t m_mapped_physical_memory_size{};
|
||||
size_t m_mapped_unsafe_physical_memory{};
|
||||
size_t m_mapped_insecure_memory{};
|
||||
size_t m_mapped_ipc_server_memory{};
|
||||
size_t m_address_space_width{};
|
||||
|
||||
KMemoryBlockManager m_memory_block_manager;
|
||||
u32 m_allocate_option{};
|
||||
|
||||
bool m_is_kernel{};
|
||||
bool m_enable_aslr{};
|
||||
bool m_enable_device_address_space_merge{};
|
||||
|
||||
KMemoryBlockSlabManager* m_memory_block_slab_manager{};
|
||||
KBlockInfoManager* m_block_info_manager{};
|
||||
KResourceLimit* m_resource_limit{};
|
||||
|
||||
u32 m_heap_fill_value{};
|
||||
u32 m_ipc_fill_value{};
|
||||
u32 m_stack_fill_value{};
|
||||
const KMemoryRegion* m_cached_physical_heap_region{};
|
||||
|
||||
KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
|
||||
|
||||
55
src/core/hle/kernel/k_page_table_manager.h
Normal file
55
src/core/hle/kernel/k_page_table_manager.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_dynamic_resource_manager.h"
|
||||
#include "core/hle/kernel/k_page_table_slab_heap.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageTableManager : public KDynamicResourceManager<impl::PageTablePage, true> {
|
||||
public:
|
||||
using RefCount = KPageTableSlabHeap::RefCount;
|
||||
static constexpr size_t PageTableSize = KPageTableSlabHeap::PageTableSize;
|
||||
|
||||
public:
|
||||
KPageTableManager() = default;
|
||||
|
||||
void Initialize(KDynamicPageManager* page_allocator, KPageTableSlabHeap* pt_heap) {
|
||||
m_pt_heap = pt_heap;
|
||||
|
||||
static_assert(std::derived_from<KPageTableSlabHeap, DynamicSlabType>);
|
||||
BaseHeap::Initialize(page_allocator, pt_heap);
|
||||
}
|
||||
|
||||
VAddr Allocate() {
|
||||
return VAddr(BaseHeap::Allocate());
|
||||
}
|
||||
|
||||
RefCount GetRefCount(VAddr addr) const {
|
||||
return m_pt_heap->GetRefCount(addr);
|
||||
}
|
||||
|
||||
void Open(VAddr addr, int count) {
|
||||
return m_pt_heap->Open(addr, count);
|
||||
}
|
||||
|
||||
bool Close(VAddr addr, int count) {
|
||||
return m_pt_heap->Close(addr, count);
|
||||
}
|
||||
|
||||
bool IsInPageTableHeap(VAddr addr) const {
|
||||
return m_pt_heap->IsInRange(addr);
|
||||
}
|
||||
|
||||
private:
|
||||
using BaseHeap = KDynamicResourceManager<impl::PageTablePage, true>;
|
||||
|
||||
KPageTableSlabHeap* m_pt_heap{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
93
src/core/hle/kernel/k_page_table_slab_heap.h
Normal file
93
src/core/hle/kernel/k_page_table_slab_heap.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_dynamic_slab_heap.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace impl {
|
||||
|
||||
class PageTablePage {
|
||||
public:
|
||||
// Do not initialize anything.
|
||||
PageTablePage() = default;
|
||||
|
||||
private:
|
||||
std::array<u8, PageSize> m_buffer{};
|
||||
};
|
||||
static_assert(sizeof(PageTablePage) == PageSize);
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class KPageTableSlabHeap : public KDynamicSlabHeap<impl::PageTablePage, true> {
|
||||
public:
|
||||
using RefCount = u16;
|
||||
static constexpr size_t PageTableSize = sizeof(impl::PageTablePage);
|
||||
static_assert(PageTableSize == PageSize);
|
||||
|
||||
public:
|
||||
KPageTableSlabHeap() = default;
|
||||
|
||||
static constexpr size_t CalculateReferenceCountSize(size_t size) {
|
||||
return (size / PageSize) * sizeof(RefCount);
|
||||
}
|
||||
|
||||
void Initialize(KDynamicPageManager* page_allocator, size_t object_count, RefCount* rc) {
|
||||
BaseHeap::Initialize(page_allocator, object_count);
|
||||
this->Initialize(rc);
|
||||
}
|
||||
|
||||
RefCount GetRefCount(VAddr addr) {
|
||||
ASSERT(this->IsInRange(addr));
|
||||
return *this->GetRefCountPointer(addr);
|
||||
}
|
||||
|
||||
void Open(VAddr addr, int count) {
|
||||
ASSERT(this->IsInRange(addr));
|
||||
|
||||
*this->GetRefCountPointer(addr) += static_cast<RefCount>(count);
|
||||
|
||||
ASSERT(this->GetRefCount(addr) > 0);
|
||||
}
|
||||
|
||||
bool Close(VAddr addr, int count) {
|
||||
ASSERT(this->IsInRange(addr));
|
||||
ASSERT(this->GetRefCount(addr) >= count);
|
||||
|
||||
*this->GetRefCountPointer(addr) -= static_cast<RefCount>(count);
|
||||
return this->GetRefCount(addr) == 0;
|
||||
}
|
||||
|
||||
bool IsInPageTableHeap(VAddr addr) const {
|
||||
return this->IsInRange(addr);
|
||||
}
|
||||
|
||||
private:
|
||||
void Initialize([[maybe_unused]] RefCount* rc) {
|
||||
// TODO(bunnei): Use rc once we support kernel virtual memory allocations.
|
||||
const auto count = this->GetSize() / PageSize;
|
||||
m_ref_counts.resize(count);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
m_ref_counts[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
RefCount* GetRefCountPointer(VAddr addr) {
|
||||
return m_ref_counts.data() + ((addr - this->GetAddress()) / PageSize);
|
||||
}
|
||||
|
||||
private:
|
||||
using BaseHeap = KDynamicSlabHeap<impl::PageTablePage, true>;
|
||||
|
||||
std::vector<RefCount> m_ref_counts;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -38,7 +38,7 @@ namespace {
|
||||
*/
|
||||
void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) {
|
||||
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
|
||||
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
|
||||
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
|
||||
|
||||
KThread* thread = KThread::Create(system.Kernel());
|
||||
SCOPE_EXIT({ thread->Close(); });
|
||||
@@ -124,7 +124,7 @@ void KProcess::DecrementRunningThreadCount() {
|
||||
}
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryAvailable() {
|
||||
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
|
||||
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) +
|
||||
page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size +
|
||||
main_thread_stack_size};
|
||||
if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
|
||||
@@ -349,8 +349,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
// We currently do not support process-specific system resource
|
||||
UNIMPLEMENTED_IF(system_resource_size != 0);
|
||||
|
||||
KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
|
||||
code_size + system_resource_size);
|
||||
KScopedResourceReservation memory_reservation(
|
||||
resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size);
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
|
||||
code_size + system_resource_size);
|
||||
@@ -358,8 +358,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
}
|
||||
// Initialize proces address space
|
||||
if (const Result result{page_table.InitializeForProcess(
|
||||
metadata.GetAddressSpaceType(), false, 0x8000000, code_size,
|
||||
&kernel.GetApplicationMemoryBlockManager(), KMemoryManager::Pool::Application)};
|
||||
metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application,
|
||||
0x8000000, code_size, &kernel.GetSystemSystemResource(), resource_limit)};
|
||||
result.IsError()) {
|
||||
R_RETURN(result);
|
||||
}
|
||||
@@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
|
||||
default:
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Create TLS region
|
||||
@@ -406,8 +407,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
|
||||
void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
|
||||
AllocateMainThreadStack(stack_size);
|
||||
resource_limit->Reserve(LimitableResource::Threads, 1);
|
||||
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
|
||||
resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
|
||||
resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
|
||||
|
||||
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
|
||||
ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
|
||||
@@ -442,7 +443,7 @@ void KProcess::PrepareForTermination() {
|
||||
plr_address = 0;
|
||||
|
||||
if (resource_limit) {
|
||||
resource_limit->Release(LimitableResource::PhysicalMemory,
|
||||
resource_limit->Release(LimitableResource::PhysicalMemoryMax,
|
||||
main_thread_stack_size + image_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,12 +159,13 @@ KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical
|
||||
// TODO(bunnei): These values are the system defaults, the limits for service processes are
|
||||
// lower. These should use the correct limit values.
|
||||
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size)
|
||||
.IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess());
|
||||
ASSERT(
|
||||
resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess());
|
||||
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
@@ -16,15 +16,8 @@ class CoreTiming;
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
enum class LimitableResource : u32 {
|
||||
PhysicalMemory = 0,
|
||||
Threads = 1,
|
||||
Events = 2,
|
||||
TransferMemory = 3,
|
||||
Sessions = 4,
|
||||
|
||||
Count,
|
||||
};
|
||||
using LimitableResource = Svc::LimitableResource;
|
||||
|
||||
constexpr bool IsValidResourceType(LimitableResource type) {
|
||||
return type < LimitableResource::Count;
|
||||
|
||||
@@ -384,7 +384,8 @@ void KScheduler::SwitchThread(KThread* next_thread) {
|
||||
|
||||
void KScheduler::ScheduleImpl() {
|
||||
// First, clear the needs scheduling bool.
|
||||
m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
|
||||
m_state.needs_scheduling.store(false, std::memory_order_relaxed);
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
// Load the appropriate thread pointers for scheduling.
|
||||
KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
|
||||
@@ -400,7 +401,8 @@ void KScheduler::ScheduleImpl() {
|
||||
// If there aren't, we want to check if the highest priority thread is the same as the current
|
||||
// thread.
|
||||
if (highest_priority_thread == cur_thread) {
|
||||
// If they're the same, then we can just return.
|
||||
// If they're the same, then we can just issue a memory barrier and return.
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -476,7 +478,8 @@ void KScheduler::ScheduleImplFiber() {
|
||||
|
||||
// We failed to successfully do the context switch, and need to retry.
|
||||
// Clear needs_scheduling.
|
||||
m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
|
||||
m_state.needs_scheduling.store(false, std::memory_order_relaxed);
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
// Refresh the highest priority thread.
|
||||
highest_priority_thread = m_state.highest_priority_thread;
|
||||
|
||||
@@ -60,6 +60,9 @@ public:
|
||||
|
||||
// Release an instance of the lock.
|
||||
if ((--lock_count) == 0) {
|
||||
// Perform a memory barrier here.
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
// We're no longer going to hold the lock. Take note of what cores need scheduling.
|
||||
const u64 cores_needing_scheduling =
|
||||
SchedulerType::UpdateHighestPriorityThreads(kernel);
|
||||
|
||||
@@ -76,7 +76,7 @@ void KSession::OnClientClosed() {
|
||||
void KSession::PostDestroy(uintptr_t arg) {
|
||||
// Release the session count resource the owner process holds.
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1);
|
||||
owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1);
|
||||
owner->Close();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Kernel {
|
||||
KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
|
||||
|
||||
KSharedMemory::~KSharedMemory() {
|
||||
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
|
||||
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
|
||||
}
|
||||
|
||||
Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
|
||||
@@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
|
||||
KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
|
||||
|
||||
// Reserve memory for ourselves.
|
||||
KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory,
|
||||
KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax,
|
||||
size_);
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
@@ -57,7 +57,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
|
||||
|
||||
void KSharedMemory::Finalize() {
|
||||
// Release the memory reservation.
|
||||
resource_limit->Release(LimitableResource::PhysicalMemory, size);
|
||||
resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
|
||||
resource_limit->Close();
|
||||
|
||||
// Perform inherited finalization.
|
||||
|
||||
26
src/core/hle/kernel/k_system_resource.cpp
Normal file
26
src/core/hle/kernel/k_system_resource.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
Result KSecureSystemResource::Initialize([[maybe_unused]] size_t size,
|
||||
[[maybe_unused]] KResourceLimit* resource_limit,
|
||||
[[maybe_unused]] KMemoryManager::Pool pool) {
|
||||
// Unimplemented
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void KSecureSystemResource::Finalize() {
|
||||
// Unimplemented
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
size_t KSecureSystemResource::CalculateRequiredSecureMemorySize(
|
||||
[[maybe_unused]] size_t size, [[maybe_unused]] KMemoryManager::Pool pool) {
|
||||
// Unimplemented
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
137
src/core/hle/kernel/k_system_resource.h
Normal file
137
src/core/hle/kernel/k_system_resource.h
Normal file
@@ -0,0 +1,137 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/k_dynamic_resource_manager.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_table_manager.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
// NOTE: Nintendo's implementation does not have the "is_secure_resource" field, and instead uses
|
||||
// virtual IsSecureResource().
|
||||
|
||||
class KSystemResource : public KAutoObject {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KSystemResource, KAutoObject);
|
||||
|
||||
public:
|
||||
explicit KSystemResource(KernelCore& kernel_) : KAutoObject(kernel_) {}
|
||||
|
||||
protected:
|
||||
void SetSecureResource() {
|
||||
m_is_secure_resource = true;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void Destroy() override {
|
||||
UNREACHABLE_MSG("KSystemResource::Destroy() was called");
|
||||
}
|
||||
|
||||
bool IsSecureResource() const {
|
||||
return m_is_secure_resource;
|
||||
}
|
||||
|
||||
void SetManagers(KMemoryBlockSlabManager& mb, KBlockInfoManager& bi, KPageTableManager& pt) {
|
||||
ASSERT(m_p_memory_block_slab_manager == nullptr);
|
||||
ASSERT(m_p_block_info_manager == nullptr);
|
||||
ASSERT(m_p_page_table_manager == nullptr);
|
||||
|
||||
m_p_memory_block_slab_manager = std::addressof(mb);
|
||||
m_p_block_info_manager = std::addressof(bi);
|
||||
m_p_page_table_manager = std::addressof(pt);
|
||||
}
|
||||
|
||||
const KMemoryBlockSlabManager& GetMemoryBlockSlabManager() const {
|
||||
return *m_p_memory_block_slab_manager;
|
||||
}
|
||||
const KBlockInfoManager& GetBlockInfoManager() const {
|
||||
return *m_p_block_info_manager;
|
||||
}
|
||||
const KPageTableManager& GetPageTableManager() const {
|
||||
return *m_p_page_table_manager;
|
||||
}
|
||||
|
||||
KMemoryBlockSlabManager& GetMemoryBlockSlabManager() {
|
||||
return *m_p_memory_block_slab_manager;
|
||||
}
|
||||
KBlockInfoManager& GetBlockInfoManager() {
|
||||
return *m_p_block_info_manager;
|
||||
}
|
||||
KPageTableManager& GetPageTableManager() {
|
||||
return *m_p_page_table_manager;
|
||||
}
|
||||
|
||||
KMemoryBlockSlabManager* GetMemoryBlockSlabManagerPointer() {
|
||||
return m_p_memory_block_slab_manager;
|
||||
}
|
||||
KBlockInfoManager* GetBlockInfoManagerPointer() {
|
||||
return m_p_block_info_manager;
|
||||
}
|
||||
KPageTableManager* GetPageTableManagerPointer() {
|
||||
return m_p_page_table_manager;
|
||||
}
|
||||
|
||||
private:
|
||||
KMemoryBlockSlabManager* m_p_memory_block_slab_manager{};
|
||||
KBlockInfoManager* m_p_block_info_manager{};
|
||||
KPageTableManager* m_p_page_table_manager{};
|
||||
bool m_is_secure_resource{false};
|
||||
};
|
||||
|
||||
class KSecureSystemResource final
|
||||
: public KAutoObjectWithSlabHeap<KSecureSystemResource, KSystemResource> {
|
||||
public:
|
||||
explicit KSecureSystemResource(KernelCore& kernel_)
|
||||
: KAutoObjectWithSlabHeap<KSecureSystemResource, KSystemResource>(kernel_) {
|
||||
// Mark ourselves as being a secure resource.
|
||||
this->SetSecureResource();
|
||||
}
|
||||
|
||||
Result Initialize(size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool);
|
||||
void Finalize();
|
||||
|
||||
bool IsInitialized() const {
|
||||
return m_is_initialized;
|
||||
}
|
||||
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
|
||||
|
||||
size_t CalculateRequiredSecureMemorySize() const {
|
||||
return CalculateRequiredSecureMemorySize(m_resource_size, m_resource_pool);
|
||||
}
|
||||
|
||||
size_t GetSize() const {
|
||||
return m_resource_size;
|
||||
}
|
||||
size_t GetUsedSize() const {
|
||||
return m_dynamic_page_manager.GetUsed() * PageSize;
|
||||
}
|
||||
|
||||
const KDynamicPageManager& GetDynamicPageManager() const {
|
||||
return m_dynamic_page_manager;
|
||||
}
|
||||
|
||||
public:
|
||||
static size_t CalculateRequiredSecureMemorySize(size_t size, KMemoryManager::Pool pool);
|
||||
|
||||
private:
|
||||
bool m_is_initialized{};
|
||||
KMemoryManager::Pool m_resource_pool{};
|
||||
KDynamicPageManager m_dynamic_page_manager;
|
||||
KMemoryBlockSlabManager m_memory_block_slab_manager;
|
||||
KBlockInfoManager m_block_info_manager;
|
||||
KPageTableManager m_page_table_manager;
|
||||
KMemoryBlockSlabHeap m_memory_block_heap;
|
||||
KBlockInfoSlabHeap m_block_info_heap;
|
||||
KPageTableSlabHeap m_page_table_heap;
|
||||
KResourceLimit* m_resource_limit{};
|
||||
VAddr m_resource_address{};
|
||||
size_t m_resource_size{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -263,9 +263,9 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KThread::InitializeDummyThread(KThread* thread) {
|
||||
Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) {
|
||||
// Initialize the thread.
|
||||
R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy));
|
||||
R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy));
|
||||
|
||||
// Initialize emulation parameters.
|
||||
thread->stack_parameters.disable_count = 0;
|
||||
@@ -303,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) {
|
||||
const bool resource_limit_release_hint = (arg & 1);
|
||||
const s64 hint_value = (resource_limit_release_hint ? 0 : 1);
|
||||
if (owner != nullptr) {
|
||||
owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value);
|
||||
owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value);
|
||||
owner->Close();
|
||||
}
|
||||
}
|
||||
@@ -1054,7 +1054,7 @@ void KThread::Exit() {
|
||||
|
||||
// Release the thread resource hint, running thread count from parent.
|
||||
if (parent != nullptr) {
|
||||
parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1);
|
||||
parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1);
|
||||
resource_limit_release_hint = true;
|
||||
parent->DecrementRunningThreadCount();
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ public:
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
[[nodiscard]] static Result InitializeDummyThread(KThread* thread);
|
||||
[[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner);
|
||||
|
||||
[[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
|
||||
s32 virt_core);
|
||||
|
||||
@@ -37,7 +37,7 @@ void KTransferMemory::Finalize() {
|
||||
|
||||
void KTransferMemory::PostDestroy(uintptr_t arg) {
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1);
|
||||
owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1);
|
||||
owner->Close();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,12 @@
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_buffer.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_worker_task_manager.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -47,6 +49,11 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
|
||||
namespace Kernel {
|
||||
|
||||
struct KernelCore::Impl {
|
||||
static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000;
|
||||
static constexpr size_t SystemMemoryBlockSlabHeapSize = 10000;
|
||||
static constexpr size_t BlockInfoSlabHeapSize = 4000;
|
||||
static constexpr size_t ReservedDynamicPageCount = 64;
|
||||
|
||||
explicit Impl(Core::System& system_, KernelCore& kernel_)
|
||||
: time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"},
|
||||
service_thread_barrier{2}, system{system_} {}
|
||||
@@ -71,7 +78,6 @@ struct KernelCore::Impl {
|
||||
// Initialize kernel memory and resources.
|
||||
InitializeSystemResourceLimit(kernel, system.CoreTiming());
|
||||
InitializeMemoryLayout();
|
||||
Init::InitializeKPageBufferSlabHeap(system);
|
||||
InitializeShutdownThreads();
|
||||
InitializePhysicalCores();
|
||||
InitializePreemption(kernel);
|
||||
@@ -81,12 +87,13 @@ struct KernelCore::Impl {
|
||||
const auto& pt_heap_region = memory_layout->GetPageTableHeapRegion();
|
||||
ASSERT(pt_heap_region.GetEndAddress() != 0);
|
||||
|
||||
InitializeResourceManagers(pt_heap_region.GetAddress(), pt_heap_region.GetSize());
|
||||
InitializeResourceManagers(kernel, pt_heap_region.GetAddress(),
|
||||
pt_heap_region.GetSize());
|
||||
}
|
||||
|
||||
RegisterHostThread();
|
||||
RegisterHostThread(nullptr);
|
||||
|
||||
default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
|
||||
default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread");
|
||||
}
|
||||
|
||||
void InitializeCores() {
|
||||
@@ -222,18 +229,22 @@ struct KernelCore::Impl {
|
||||
const auto kernel_size{sizes.second};
|
||||
|
||||
// If setting the default system values fails, then something seriously wrong has occurred.
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
|
||||
ASSERT(
|
||||
system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
|
||||
system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size);
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133)
|
||||
.IsSuccess());
|
||||
system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size);
|
||||
|
||||
// Reserve secure applet memory, introduced in firmware 5.0.0
|
||||
constexpr u64 secure_applet_memory_size{4_MiB};
|
||||
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
|
||||
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax,
|
||||
secure_applet_memory_size));
|
||||
}
|
||||
|
||||
@@ -253,16 +264,82 @@ struct KernelCore::Impl {
|
||||
system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
|
||||
}
|
||||
|
||||
void InitializeResourceManagers(VAddr address, size_t size) {
|
||||
dynamic_page_manager = std::make_unique<KDynamicPageManager>();
|
||||
memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
|
||||
app_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
|
||||
void InitializeResourceManagers(KernelCore& kernel, VAddr address, size_t size) {
|
||||
// Ensure that the buffer is suitable for our use.
|
||||
ASSERT(Common::IsAligned(address, PageSize));
|
||||
ASSERT(Common::IsAligned(size, PageSize));
|
||||
|
||||
dynamic_page_manager->Initialize(address, size);
|
||||
static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000;
|
||||
memory_block_heap->Initialize(dynamic_page_manager.get(),
|
||||
ApplicationMemoryBlockSlabHeapSize);
|
||||
app_memory_block_manager->Initialize(nullptr, memory_block_heap.get());
|
||||
// Ensure that we have space for our reference counts.
|
||||
const size_t rc_size =
|
||||
Common::AlignUp(KPageTableSlabHeap::CalculateReferenceCountSize(size), PageSize);
|
||||
ASSERT(rc_size < size);
|
||||
size -= rc_size;
|
||||
|
||||
// Initialize the resource managers' shared page manager.
|
||||
resource_manager_page_manager = std::make_unique<KDynamicPageManager>();
|
||||
resource_manager_page_manager->Initialize(
|
||||
address, size, std::max<size_t>(PageSize, KPageBufferSlabHeap::BufferSize));
|
||||
|
||||
// Initialize the KPageBuffer slab heap.
|
||||
page_buffer_slab_heap.Initialize(system);
|
||||
|
||||
// Initialize the fixed-size slabheaps.
|
||||
app_memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
|
||||
sys_memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
|
||||
block_info_heap = std::make_unique<KBlockInfoSlabHeap>();
|
||||
app_memory_block_heap->Initialize(resource_manager_page_manager.get(),
|
||||
ApplicationMemoryBlockSlabHeapSize);
|
||||
sys_memory_block_heap->Initialize(resource_manager_page_manager.get(),
|
||||
SystemMemoryBlockSlabHeapSize);
|
||||
block_info_heap->Initialize(resource_manager_page_manager.get(), BlockInfoSlabHeapSize);
|
||||
|
||||
// Reserve all but a fixed number of remaining pages for the page table heap.
|
||||
const size_t num_pt_pages = resource_manager_page_manager->GetCount() -
|
||||
resource_manager_page_manager->GetUsed() -
|
||||
ReservedDynamicPageCount;
|
||||
page_table_heap = std::make_unique<KPageTableSlabHeap>();
|
||||
|
||||
// TODO(bunnei): Pass in address once we support kernel virtual memory allocations.
|
||||
page_table_heap->Initialize(
|
||||
resource_manager_page_manager.get(), num_pt_pages,
|
||||
/*GetPointer<KPageTableManager::RefCount>(address + size)*/ nullptr);
|
||||
|
||||
// Setup the slab managers.
|
||||
KDynamicPageManager* const app_dynamic_page_manager = nullptr;
|
||||
KDynamicPageManager* const sys_dynamic_page_manager =
|
||||
/*KTargetSystem::IsDynamicResourceLimitsEnabled()*/ true
|
||||
? resource_manager_page_manager.get()
|
||||
: nullptr;
|
||||
app_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
|
||||
sys_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
|
||||
app_block_info_manager = std::make_unique<KBlockInfoManager>();
|
||||
sys_block_info_manager = std::make_unique<KBlockInfoManager>();
|
||||
app_page_table_manager = std::make_unique<KPageTableManager>();
|
||||
sys_page_table_manager = std::make_unique<KPageTableManager>();
|
||||
|
||||
app_memory_block_manager->Initialize(app_dynamic_page_manager, app_memory_block_heap.get());
|
||||
sys_memory_block_manager->Initialize(sys_dynamic_page_manager, sys_memory_block_heap.get());
|
||||
|
||||
app_block_info_manager->Initialize(app_dynamic_page_manager, block_info_heap.get());
|
||||
sys_block_info_manager->Initialize(sys_dynamic_page_manager, block_info_heap.get());
|
||||
|
||||
app_page_table_manager->Initialize(app_dynamic_page_manager, page_table_heap.get());
|
||||
sys_page_table_manager->Initialize(sys_dynamic_page_manager, page_table_heap.get());
|
||||
|
||||
// Check that we have the correct number of dynamic pages available.
|
||||
ASSERT(resource_manager_page_manager->GetCount() -
|
||||
resource_manager_page_manager->GetUsed() ==
|
||||
ReservedDynamicPageCount);
|
||||
|
||||
// Create the system page table managers.
|
||||
app_system_resource = std::make_unique<KSystemResource>(kernel);
|
||||
sys_system_resource = std::make_unique<KSystemResource>(kernel);
|
||||
|
||||
// Set the managers for the system resources.
|
||||
app_system_resource->SetManagers(*app_memory_block_manager, *app_block_info_manager,
|
||||
*app_page_table_manager);
|
||||
sys_system_resource->SetManagers(*sys_memory_block_manager, *sys_block_info_manager,
|
||||
*sys_page_table_manager);
|
||||
}
|
||||
|
||||
void InitializeShutdownThreads() {
|
||||
@@ -300,15 +377,18 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
||||
KThread* GetHostDummyThread() {
|
||||
KThread* GetHostDummyThread(KThread* existing_thread) {
|
||||
auto initialize = [this](KThread* thread) {
|
||||
ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
|
||||
ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
|
||||
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
|
||||
return thread;
|
||||
};
|
||||
|
||||
thread_local auto raw_thread = KThread(system.Kernel());
|
||||
thread_local auto thread = initialize(&raw_thread);
|
||||
thread_local KThread raw_thread{system.Kernel()};
|
||||
thread_local KThread* thread = nullptr;
|
||||
if (thread == nullptr) {
|
||||
thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread;
|
||||
}
|
||||
|
||||
return thread;
|
||||
}
|
||||
@@ -323,9 +403,9 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
/// Registers a new host thread by allocating a host thread ID for it
|
||||
void RegisterHostThread() {
|
||||
void RegisterHostThread(KThread* existing_thread) {
|
||||
[[maybe_unused]] const auto this_id = GetHostThreadId();
|
||||
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
|
||||
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread);
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetCurrentHostThreadID() {
|
||||
@@ -356,7 +436,7 @@ struct KernelCore::Impl {
|
||||
KThread* GetCurrentEmuThread() {
|
||||
const auto thread_id = GetCurrentHostThreadID();
|
||||
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
|
||||
return GetHostDummyThread();
|
||||
return GetHostDummyThread(nullptr);
|
||||
}
|
||||
|
||||
return current_thread;
|
||||
@@ -446,6 +526,9 @@ struct KernelCore::Impl {
|
||||
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
|
||||
misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
|
||||
|
||||
// Determine if we'll use extra thread resources.
|
||||
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
|
||||
|
||||
// Setup the stack region.
|
||||
constexpr size_t StackRegionSize = 14_MiB;
|
||||
constexpr size_t StackRegionAlign = KernelAslrAlignment;
|
||||
@@ -456,7 +539,8 @@ struct KernelCore::Impl {
|
||||
stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack));
|
||||
|
||||
// Determine the size of the resource region.
|
||||
const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit();
|
||||
const size_t resource_region_size =
|
||||
memory_layout->GetResourceRegionSizeForInit(use_extra_resources);
|
||||
|
||||
// Determine the size of the slab region.
|
||||
const size_t slab_region_size =
|
||||
@@ -702,33 +786,31 @@ struct KernelCore::Impl {
|
||||
search->second(system.ServiceManager(), server_port);
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
|
||||
const std::string& name) {
|
||||
auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, name);
|
||||
Kernel::ServiceThread& CreateServiceThread(KernelCore& kernel, const std::string& name) {
|
||||
auto* ptr = new ServiceThread(kernel, name);
|
||||
|
||||
service_threads_manager.QueueWork(
|
||||
[this, service_thread]() { service_threads.emplace(service_thread); });
|
||||
[this, ptr]() { service_threads.emplace(ptr, std::unique_ptr<ServiceThread>(ptr)); });
|
||||
|
||||
return service_thread;
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
|
||||
if (auto strong_ptr = service_thread.lock()) {
|
||||
if (strong_ptr == default_service_thread.lock()) {
|
||||
// Nothing to do here, the service is using default_service_thread, which will be
|
||||
// released on shutdown.
|
||||
return;
|
||||
}
|
||||
void ReleaseServiceThread(Kernel::ServiceThread& service_thread) {
|
||||
auto* ptr = &service_thread;
|
||||
|
||||
service_threads_manager.QueueWork(
|
||||
[this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
|
||||
if (ptr == default_service_thread) {
|
||||
// Nothing to do here, the service is using default_service_thread, which will be
|
||||
// released on shutdown.
|
||||
return;
|
||||
}
|
||||
|
||||
service_threads_manager.QueueWork([this, ptr]() { service_threads.erase(ptr); });
|
||||
}
|
||||
|
||||
void ClearServiceThreads() {
|
||||
service_threads_manager.QueueWork([this] {
|
||||
service_threads.clear();
|
||||
default_service_thread.reset();
|
||||
default_service_thread = nullptr;
|
||||
service_thread_barrier.Sync();
|
||||
});
|
||||
service_thread_barrier.Sync();
|
||||
@@ -751,6 +833,8 @@ struct KernelCore::Impl {
|
||||
Init::KSlabResourceCounts slab_resource_counts{};
|
||||
KResourceLimit* system_resource_limit{};
|
||||
|
||||
KPageBufferSlabHeap page_buffer_slab_heap;
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> preemption_event;
|
||||
|
||||
// This is the kernel's handle table or supervisor handle table which
|
||||
@@ -776,10 +860,20 @@ struct KernelCore::Impl {
|
||||
// Kernel memory management
|
||||
std::unique_ptr<KMemoryManager> memory_manager;
|
||||
|
||||
// Dynamic slab managers
|
||||
std::unique_ptr<KDynamicPageManager> dynamic_page_manager;
|
||||
std::unique_ptr<KMemoryBlockSlabHeap> memory_block_heap;
|
||||
// Resource managers
|
||||
std::unique_ptr<KDynamicPageManager> resource_manager_page_manager;
|
||||
std::unique_ptr<KPageTableSlabHeap> page_table_heap;
|
||||
std::unique_ptr<KMemoryBlockSlabHeap> app_memory_block_heap;
|
||||
std::unique_ptr<KMemoryBlockSlabHeap> sys_memory_block_heap;
|
||||
std::unique_ptr<KBlockInfoSlabHeap> block_info_heap;
|
||||
std::unique_ptr<KPageTableManager> app_page_table_manager;
|
||||
std::unique_ptr<KPageTableManager> sys_page_table_manager;
|
||||
std::unique_ptr<KMemoryBlockSlabManager> app_memory_block_manager;
|
||||
std::unique_ptr<KMemoryBlockSlabManager> sys_memory_block_manager;
|
||||
std::unique_ptr<KBlockInfoManager> app_block_info_manager;
|
||||
std::unique_ptr<KBlockInfoManager> sys_block_info_manager;
|
||||
std::unique_ptr<KSystemResource> app_system_resource;
|
||||
std::unique_ptr<KSystemResource> sys_system_resource;
|
||||
|
||||
// Shared memory for services
|
||||
Kernel::KSharedMemory* hid_shared_mem{};
|
||||
@@ -792,8 +886,8 @@ struct KernelCore::Impl {
|
||||
std::unique_ptr<KMemoryLayout> memory_layout;
|
||||
|
||||
// Threads used for services
|
||||
std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
|
||||
std::weak_ptr<ServiceThread> default_service_thread;
|
||||
std::unordered_map<ServiceThread*, std::unique_ptr<ServiceThread>> service_threads;
|
||||
ServiceThread* default_service_thread{};
|
||||
Common::ThreadWorker service_threads_manager;
|
||||
Common::Barrier service_thread_barrier;
|
||||
|
||||
@@ -1033,8 +1127,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) {
|
||||
impl->RegisterCoreThread(core_id);
|
||||
}
|
||||
|
||||
void KernelCore::RegisterHostThread() {
|
||||
impl->RegisterHostThread();
|
||||
void KernelCore::RegisterHostThread(KThread* existing_thread) {
|
||||
impl->RegisterHostThread(existing_thread);
|
||||
|
||||
if (existing_thread != nullptr) {
|
||||
ASSERT(GetCurrentEmuThread() == existing_thread);
|
||||
}
|
||||
}
|
||||
|
||||
u32 KernelCore::GetCurrentHostThreadID() const {
|
||||
@@ -1057,12 +1155,12 @@ const KMemoryManager& KernelCore::MemoryManager() const {
|
||||
return *impl->memory_manager;
|
||||
}
|
||||
|
||||
KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() {
|
||||
return *impl->app_memory_block_manager;
|
||||
KSystemResource& KernelCore::GetSystemSystemResource() {
|
||||
return *impl->sys_system_resource;
|
||||
}
|
||||
|
||||
const KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() const {
|
||||
return *impl->app_memory_block_manager;
|
||||
const KSystemResource& KernelCore::GetSystemSystemResource() const {
|
||||
return *impl->sys_system_resource;
|
||||
}
|
||||
|
||||
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
|
||||
@@ -1109,16 +1207,28 @@ void KernelCore::Suspend(bool suspended) {
|
||||
const bool should_suspend{exception_exited || suspended};
|
||||
const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
|
||||
|
||||
for (auto* process : GetProcessList()) {
|
||||
process->SetActivity(activity);
|
||||
std::vector<KScopedAutoObject<KThread>> process_threads;
|
||||
{
|
||||
KScopedSchedulerLock sl{*this};
|
||||
|
||||
if (auto* process = CurrentProcess(); process != nullptr) {
|
||||
process->SetActivity(activity);
|
||||
|
||||
if (!should_suspend) {
|
||||
// Runnable now; no need to wait.
|
||||
return;
|
||||
}
|
||||
|
||||
if (should_suspend) {
|
||||
// Wait for execution to stop
|
||||
for (auto* thread : process->GetThreadList()) {
|
||||
thread->WaitUntilSuspended();
|
||||
process_threads.emplace_back(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for execution to stop.
|
||||
for (auto& thread : process_threads) {
|
||||
thread->WaitUntilSuspended();
|
||||
}
|
||||
}
|
||||
|
||||
void KernelCore::ShutdownCores() {
|
||||
@@ -1150,15 +1260,15 @@ void KernelCore::ExitSVCProfile() {
|
||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
|
||||
Kernel::ServiceThread& KernelCore::CreateServiceThread(const std::string& name) {
|
||||
return impl->CreateServiceThread(*this, name);
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
|
||||
return impl->default_service_thread;
|
||||
Kernel::ServiceThread& KernelCore::GetDefaultServiceThread() const {
|
||||
return *impl->default_service_thread;
|
||||
}
|
||||
|
||||
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
|
||||
void KernelCore::ReleaseServiceThread(Kernel::ServiceThread& service_thread) {
|
||||
impl->ReleaseServiceThread(service_thread);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +34,16 @@ class KClientPort;
|
||||
class GlobalSchedulerContext;
|
||||
class KAutoObjectWithListContainer;
|
||||
class KClientSession;
|
||||
class KDebug;
|
||||
class KDynamicPageManager;
|
||||
class KEvent;
|
||||
class KEventInfo;
|
||||
class KHandleTable;
|
||||
class KLinkedListNode;
|
||||
class KMemoryBlockSlabManager;
|
||||
class KMemoryLayout;
|
||||
class KMemoryManager;
|
||||
class KPageBuffer;
|
||||
class KPageBufferSlabHeap;
|
||||
class KPort;
|
||||
class KProcess;
|
||||
class KResourceLimit;
|
||||
@@ -51,6 +54,7 @@ class KSession;
|
||||
class KSessionRequest;
|
||||
class KSharedMemory;
|
||||
class KSharedMemoryInfo;
|
||||
class KSecureSystemResource;
|
||||
class KThread;
|
||||
class KThreadLocalPage;
|
||||
class KTransferMemory;
|
||||
@@ -236,7 +240,7 @@ public:
|
||||
void RegisterCoreThread(std::size_t core_id);
|
||||
|
||||
/// Register the current thread as a non CPU core thread.
|
||||
void RegisterHostThread();
|
||||
void RegisterHostThread(KThread* existing_thread = nullptr);
|
||||
|
||||
/// Gets the virtual memory manager for the kernel.
|
||||
KMemoryManager& MemoryManager();
|
||||
@@ -244,11 +248,11 @@ public:
|
||||
/// Gets the virtual memory manager for the kernel.
|
||||
const KMemoryManager& MemoryManager() const;
|
||||
|
||||
/// Gets the application memory block manager for the kernel.
|
||||
KMemoryBlockSlabManager& GetApplicationMemoryBlockManager();
|
||||
/// Gets the system resource manager.
|
||||
KSystemResource& GetSystemSystemResource();
|
||||
|
||||
/// Gets the application memory block manager for the kernel.
|
||||
const KMemoryBlockSlabManager& GetApplicationMemoryBlockManager() const;
|
||||
/// Gets the system resource manager.
|
||||
const KSystemResource& GetSystemSystemResource() const;
|
||||
|
||||
/// Gets the shared memory object for HID services.
|
||||
Kernel::KSharedMemory& GetHidSharedMem();
|
||||
@@ -305,24 +309,24 @@ public:
|
||||
* See GetDefaultServiceThread.
|
||||
* @param name String name for the ServerSession creating this thread, used for debug
|
||||
* purposes.
|
||||
* @returns The a weak pointer newly created service thread.
|
||||
* @returns A reference to the newly created service thread.
|
||||
*/
|
||||
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
|
||||
Kernel::ServiceThread& CreateServiceThread(const std::string& name);
|
||||
|
||||
/**
|
||||
* Gets the default host service thread, which executes HLE service requests. Unless service
|
||||
* requests need to block on the host, the default service thread should be used in favor of
|
||||
* creating a new service thread.
|
||||
* @returns The a weak pointer for the default service thread.
|
||||
* @returns A reference to the default service thread.
|
||||
*/
|
||||
std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
|
||||
Kernel::ServiceThread& GetDefaultServiceThread() const;
|
||||
|
||||
/**
|
||||
* Releases a HLE service thread, instructing KernelCore to free it. This should be called when
|
||||
* the ServerSession associated with the thread is destroyed.
|
||||
* @param service_thread Service thread to release.
|
||||
*/
|
||||
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
|
||||
void ReleaseServiceThread(Kernel::ServiceThread& service_thread);
|
||||
|
||||
/// Workaround for single-core mode when preempting threads while idle.
|
||||
bool IsPhantomModeForSingleCore() const;
|
||||
@@ -364,6 +368,12 @@ public:
|
||||
return slab_heap_container->thread_local_page;
|
||||
} else if constexpr (std::is_same_v<T, KSessionRequest>) {
|
||||
return slab_heap_container->session_request;
|
||||
} else if constexpr (std::is_same_v<T, KSecureSystemResource>) {
|
||||
return slab_heap_container->secure_system_resource;
|
||||
} else if constexpr (std::is_same_v<T, KEventInfo>) {
|
||||
return slab_heap_container->event_info;
|
||||
} else if constexpr (std::is_same_v<T, KDebug>) {
|
||||
return slab_heap_container->debug;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +437,9 @@ private:
|
||||
KSlabHeap<KPageBuffer> page_buffer;
|
||||
KSlabHeap<KThreadLocalPage> thread_local_page;
|
||||
KSlabHeap<KSessionRequest> session_request;
|
||||
KSlabHeap<KSecureSystemResource> secure_system_resource;
|
||||
KSlabHeap<KEventInfo> event_info;
|
||||
KSlabHeap<KDebug> debug;
|
||||
};
|
||||
|
||||
std::unique_ptr<SlabHeapContainer> slab_heap_container;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Kernel {
|
||||
|
||||
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
|
||||
: core_index{core_index_}, system{system_}, scheduler{scheduler_} {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
|
||||
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
|
||||
auto& kernel = system.Kernel();
|
||||
@@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche
|
||||
PhysicalCore::~PhysicalCore() = default;
|
||||
|
||||
void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
auto& kernel = system.Kernel();
|
||||
if (!is_64_bit) {
|
||||
// We already initialized a 64-bit core, replace with a 32-bit one.
|
||||
|
||||
@@ -36,11 +36,11 @@ public:
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
|
||||
std::jthread m_thread;
|
||||
std::jthread m_host_thread;
|
||||
std::mutex m_session_mutex;
|
||||
std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
|
||||
KEvent* m_wakeup_event;
|
||||
KProcess* m_process;
|
||||
KThread* m_thread;
|
||||
std::atomic<bool> m_shutdown_requested;
|
||||
const std::string m_service_name;
|
||||
};
|
||||
@@ -132,7 +132,7 @@ void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
|
||||
void ServiceThread::Impl::LoopProcess() {
|
||||
Common::SetCurrentThreadName(m_service_name.c_str());
|
||||
|
||||
kernel.RegisterHostThread();
|
||||
kernel.RegisterHostThread(m_thread);
|
||||
|
||||
while (!m_shutdown_requested.load()) {
|
||||
WaitAndProcessImpl();
|
||||
@@ -160,7 +160,7 @@ ServiceThread::Impl::~Impl() {
|
||||
// Shut down the processing thread.
|
||||
m_shutdown_requested.store(true);
|
||||
m_wakeup_event->Signal();
|
||||
m_thread.join();
|
||||
m_host_thread.join();
|
||||
|
||||
// Lock mutex.
|
||||
m_session_mutex.lock();
|
||||
@@ -177,33 +177,22 @@ ServiceThread::Impl::~Impl() {
|
||||
m_wakeup_event->GetReadableEvent().Close();
|
||||
m_wakeup_event->Close();
|
||||
|
||||
// Close process.
|
||||
m_process->Close();
|
||||
// Close thread.
|
||||
m_thread->Close();
|
||||
}
|
||||
|
||||
ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
|
||||
: kernel{kernel_}, m_service_name{service_name} {
|
||||
// Initialize process.
|
||||
m_process = KProcess::Create(kernel);
|
||||
KProcess::Initialize(m_process, kernel.System(), service_name,
|
||||
KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
|
||||
|
||||
// Reserve a new event from the process resource limit
|
||||
KScopedResourceReservation event_reservation(m_process, LimitableResource::Events);
|
||||
ASSERT(event_reservation.Succeeded());
|
||||
|
||||
// Initialize event.
|
||||
m_wakeup_event = KEvent::Create(kernel);
|
||||
m_wakeup_event->Initialize(m_process);
|
||||
m_wakeup_event->Initialize(nullptr);
|
||||
|
||||
// Commit the event reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
// Register the event.
|
||||
KEvent::Register(kernel, m_wakeup_event);
|
||||
// Initialize thread.
|
||||
m_thread = KThread::Create(kernel);
|
||||
ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess());
|
||||
|
||||
// Start thread.
|
||||
m_thread = std::jthread([this] { LoopProcess(); });
|
||||
m_host_thread = std::jthread([this] { LoopProcess(); });
|
||||
}
|
||||
|
||||
ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)
|
||||
|
||||
@@ -52,6 +52,84 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Derived, typename Base>
|
||||
class KAutoObjectWithSlabHeap : public Base {
|
||||
static_assert(std::is_base_of<KAutoObject, Base>::value);
|
||||
|
||||
private:
|
||||
static Derived* Allocate(KernelCore& kernel) {
|
||||
return kernel.SlabHeap<Derived>().Allocate(kernel);
|
||||
}
|
||||
|
||||
static void Free(KernelCore& kernel, Derived* obj) {
|
||||
kernel.SlabHeap<Derived>().Free(obj);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit KAutoObjectWithSlabHeap(KernelCore& kernel_) : Base(kernel_), kernel(kernel_) {}
|
||||
virtual ~KAutoObjectWithSlabHeap() = default;
|
||||
|
||||
virtual void Destroy() override {
|
||||
const bool is_initialized = this->IsInitialized();
|
||||
uintptr_t arg = 0;
|
||||
if (is_initialized) {
|
||||
arg = this->GetPostDestroyArgument();
|
||||
this->Finalize();
|
||||
}
|
||||
Free(kernel, static_cast<Derived*>(this));
|
||||
if (is_initialized) {
|
||||
Derived::PostDestroy(arg);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool IsInitialized() const {
|
||||
return true;
|
||||
}
|
||||
virtual uintptr_t GetPostDestroyArgument() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t GetSlabIndex() const {
|
||||
return SlabHeap<Derived>(kernel).GetObjectIndex(static_cast<const Derived*>(this));
|
||||
}
|
||||
|
||||
public:
|
||||
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
|
||||
kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
|
||||
}
|
||||
|
||||
static Derived* Create(KernelCore& kernel) {
|
||||
Derived* obj = Allocate(kernel);
|
||||
if (obj != nullptr) {
|
||||
KAutoObject::Create(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static size_t GetObjectSize(KernelCore& kernel) {
|
||||
return kernel.SlabHeap<Derived>().GetObjectSize();
|
||||
}
|
||||
|
||||
static size_t GetSlabHeapSize(KernelCore& kernel) {
|
||||
return kernel.SlabHeap<Derived>().GetSlabHeapSize();
|
||||
}
|
||||
|
||||
static size_t GetPeakIndex(KernelCore& kernel) {
|
||||
return kernel.SlabHeap<Derived>().GetPeakIndex();
|
||||
}
|
||||
|
||||
static uintptr_t GetSlabHeapAddress(KernelCore& kernel) {
|
||||
return kernel.SlabHeap<Derived>().GetSlabHeapAddress();
|
||||
}
|
||||
|
||||
static size_t GetNumRemaining(KernelCore& kernel) {
|
||||
return kernel.SlabHeap<Derived>().GetNumRemaining();
|
||||
}
|
||||
|
||||
protected:
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
template <typename Derived, typename Base>
|
||||
class KAutoObjectWithSlabHeapAndContainer : public Base {
|
||||
static_assert(std::is_base_of<KAutoObjectWithList, Base>::value);
|
||||
|
||||
@@ -267,7 +267,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
|
||||
|
||||
// Reserve a new session from the process resource limit.
|
||||
// FIXME: LimitableResource_SessionCountMax
|
||||
KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions);
|
||||
KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
|
||||
if (session_reservation.Succeeded()) {
|
||||
session = T::Create(system.Kernel());
|
||||
} else {
|
||||
@@ -298,7 +298,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
|
||||
|
||||
// We successfully allocated a session, so add the object we allocated to the resource
|
||||
// limit.
|
||||
// system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1);
|
||||
// system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
|
||||
}
|
||||
|
||||
// Check that we successfully created a session.
|
||||
@@ -656,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) {
|
||||
return ArbitrateUnlock(system, address);
|
||||
}
|
||||
|
||||
enum class BreakType : u32 {
|
||||
Panic = 0,
|
||||
AssertionFailed = 1,
|
||||
PreNROLoad = 3,
|
||||
PostNROLoad = 4,
|
||||
PreNROUnload = 5,
|
||||
PostNROUnload = 6,
|
||||
CppException = 7,
|
||||
};
|
||||
|
||||
struct BreakReason {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 30, BreakType> break_type;
|
||||
BitField<31, 1, u32> signal_debugger;
|
||||
};
|
||||
};
|
||||
|
||||
/// Break program execution
|
||||
static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
|
||||
BreakReason break_reason{reason};
|
||||
BreakReason break_reason =
|
||||
static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
|
||||
bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
|
||||
|
||||
bool has_dumped_buffer{};
|
||||
std::vector<u8> debug_buffer;
|
||||
|
||||
@@ -705,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
|
||||
}
|
||||
has_dumped_buffer = true;
|
||||
};
|
||||
switch (break_reason.break_type) {
|
||||
case BreakType::Panic:
|
||||
LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
|
||||
switch (break_reason) {
|
||||
case BreakReason::Panic:
|
||||
LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
|
||||
info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
case BreakReason::Assert:
|
||||
LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
|
||||
info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
case BreakType::AssertionFailed:
|
||||
LOG_CRITICAL(Debug_Emulated,
|
||||
"Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
|
||||
info1, info2);
|
||||
case BreakReason::User:
|
||||
LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
case BreakType::PreNROLoad:
|
||||
LOG_WARNING(
|
||||
Debug_Emulated,
|
||||
"Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
|
||||
info1, info2);
|
||||
case BreakReason::PreLoadDll:
|
||||
LOG_INFO(Debug_Emulated,
|
||||
"Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
|
||||
info2);
|
||||
break;
|
||||
case BreakType::PostNROLoad:
|
||||
LOG_WARNING(Debug_Emulated,
|
||||
"Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
|
||||
info2);
|
||||
case BreakReason::PostLoadDll:
|
||||
LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
|
||||
info2);
|
||||
break;
|
||||
case BreakType::PreNROUnload:
|
||||
LOG_WARNING(
|
||||
Debug_Emulated,
|
||||
"Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
|
||||
info1, info2);
|
||||
case BreakReason::PreUnloadDll:
|
||||
LOG_INFO(Debug_Emulated,
|
||||
"Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
|
||||
info2);
|
||||
break;
|
||||
case BreakType::PostNROUnload:
|
||||
LOG_WARNING(Debug_Emulated,
|
||||
"Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
|
||||
info2);
|
||||
case BreakReason::PostUnloadDll:
|
||||
LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
|
||||
info1, info2);
|
||||
break;
|
||||
case BreakType::CppException:
|
||||
case BreakReason::CppException:
|
||||
LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(
|
||||
Debug_Emulated,
|
||||
"Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
static_cast<u32>(break_reason.break_type.Value()), info1, info2);
|
||||
"Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
}
|
||||
|
||||
system.GetReporter().SaveSvcBreakReport(
|
||||
static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(),
|
||||
info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
|
||||
system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
|
||||
has_dumped_buffer ? std::make_optional(debug_buffer)
|
||||
: std::nullopt);
|
||||
|
||||
if (!break_reason.signal_debugger) {
|
||||
if (!notification_only) {
|
||||
LOG_CRITICAL(
|
||||
Debug_Emulated,
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
@@ -1716,13 +1700,13 @@ static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address
|
||||
auto& memory{system.Memory()};
|
||||
const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
|
||||
|
||||
memory.Write64(memory_info_address + 0x00, memory_info.addr);
|
||||
memory.Write64(memory_info_address + 0x00, memory_info.base_address);
|
||||
memory.Write64(memory_info_address + 0x08, memory_info.size);
|
||||
memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
|
||||
memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr));
|
||||
memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm));
|
||||
memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount);
|
||||
memory.Write32(memory_info_address + 0x20, memory_info.device_refcount);
|
||||
memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
|
||||
memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
|
||||
memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
|
||||
memory.Write32(memory_info_address + 0x20, memory_info.device_count);
|
||||
memory.Write32(memory_info_address + 0x24, 0);
|
||||
|
||||
// Page info appears to be currently unused by the kernel and is always set to zero.
|
||||
@@ -1943,7 +1927,7 @@ static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry
|
||||
|
||||
// Reserve a new thread from the process resource limit (waiting up to 100ms).
|
||||
KScopedResourceReservation thread_reservation(
|
||||
kernel.CurrentProcess(), LimitableResource::Threads, 1,
|
||||
kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
|
||||
system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
|
||||
if (!thread_reservation.Succeeded()) {
|
||||
LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
|
||||
@@ -2247,7 +2231,7 @@ static u64 GetSystemTick(Core::System& system) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
// Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
|
||||
const u64 result{system.CoreTiming().GetClockTicks()};
|
||||
const u64 result{core_timing.GetClockTicks()};
|
||||
|
||||
if (!system.Kernel().IsMulticore()) {
|
||||
core_timing.AddTicks(400U);
|
||||
@@ -2344,7 +2328,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr
|
||||
|
||||
// Reserve a new transfer memory from the process resource limit.
|
||||
KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
|
||||
LimitableResource::TransferMemory);
|
||||
LimitableResource::TransferMemoryCountMax);
|
||||
R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Create the transfer memory.
|
||||
@@ -2496,7 +2480,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r
|
||||
|
||||
// Reserve a new event from the process resource limit
|
||||
KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
|
||||
LimitableResource::Events);
|
||||
LimitableResource::EventCountMax);
|
||||
R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Create a new event.
|
||||
@@ -2539,11 +2523,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out
|
||||
static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
|
||||
|
||||
// This function currently only allows retrieving a process' status.
|
||||
enum class InfoType {
|
||||
Status,
|
||||
};
|
||||
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
|
||||
if (process.IsNull()) {
|
||||
@@ -2552,9 +2531,9 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand
|
||||
return ResultInvalidHandle;
|
||||
}
|
||||
|
||||
const auto info_type = static_cast<InfoType>(type);
|
||||
if (info_type != InfoType::Status) {
|
||||
LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type);
|
||||
const auto info_type = static_cast<ProcessInfoType>(type);
|
||||
if (info_type != ProcessInfoType::ProcessState) {
|
||||
LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
|
||||
return ResultInvalidEnumValue;
|
||||
}
|
||||
|
||||
@@ -2722,14 +2701,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system,
|
||||
[[maybe_unused]] Handle handle, [[maybe_unused]] u32 address,
|
||||
[[maybe_unused]] u32 size) {
|
||||
// Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op,
|
||||
// as all emulation is done in the same cache level in host architecture, thus data cache
|
||||
// does not need flushing.
|
||||
LOG_DEBUG(Kernel_SVC, "called");
|
||||
return ResultSuccess;
|
||||
static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
|
||||
u64 size) {
|
||||
// Validate address/size.
|
||||
R_UNLESS(size > 0, ResultInvalidSize);
|
||||
R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
|
||||
R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Get the process from its handle.
|
||||
KScopedAutoObject process =
|
||||
system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
|
||||
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
// Verify the region is within range.
|
||||
auto& page_table = process->PageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Perform the operation.
|
||||
R_RETURN(system.Memory().FlushDataCache(*process, address, size));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -37,6 +37,7 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
|
||||
constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
|
||||
constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
|
||||
constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
|
||||
constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
|
||||
constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
using Handle = u32;
|
||||
|
||||
enum class MemoryState : u32 {
|
||||
Free = 0x00,
|
||||
Io = 0x01,
|
||||
@@ -22,8 +24,8 @@ enum class MemoryState : u32 {
|
||||
Ipc = 0x0A,
|
||||
Stack = 0x0B,
|
||||
ThreadLocal = 0x0C,
|
||||
Transferred = 0x0D,
|
||||
SharedTransferred = 0x0E,
|
||||
Transfered = 0x0D,
|
||||
SharedTransfered = 0x0E,
|
||||
SharedCode = 0x0F,
|
||||
Inaccessible = 0x10,
|
||||
NonSecureIpc = 0x11,
|
||||
@@ -32,6 +34,7 @@ enum class MemoryState : u32 {
|
||||
GeneratedCode = 0x14,
|
||||
CodeOut = 0x15,
|
||||
Coverage = 0x16,
|
||||
Insecure = 0x17,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
|
||||
|
||||
@@ -54,17 +57,6 @@ enum class MemoryPermission : u32 {
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
|
||||
|
||||
struct MemoryInfo {
|
||||
u64 addr{};
|
||||
u64 size{};
|
||||
MemoryState state{};
|
||||
MemoryAttribute attr{};
|
||||
MemoryPermission perm{};
|
||||
u32 ipc_refcount{};
|
||||
u32 device_refcount{};
|
||||
u32 padding{};
|
||||
};
|
||||
|
||||
enum class SignalType : u32 {
|
||||
Signal = 0,
|
||||
SignalAndIncrementIfEqual = 1,
|
||||
@@ -83,6 +75,13 @@ enum class YieldType : s64 {
|
||||
ToAnyThread = -2,
|
||||
};
|
||||
|
||||
enum class ThreadExitReason : u32 {
|
||||
ExitThread = 0,
|
||||
TerminateThread = 1,
|
||||
ExitProcess = 2,
|
||||
TerminateProcess = 3,
|
||||
};
|
||||
|
||||
enum class ThreadActivity : u32 {
|
||||
Runnable = 0,
|
||||
Paused = 1,
|
||||
@@ -108,6 +107,489 @@ enum class ProcessState : u32 {
|
||||
DebugBreak = 7,
|
||||
};
|
||||
|
||||
enum class ProcessExitReason : u32 {
|
||||
ExitProcess = 0,
|
||||
TerminateProcess = 1,
|
||||
Exception = 2,
|
||||
};
|
||||
|
||||
constexpr inline size_t ThreadLocalRegionSize = 0x200;
|
||||
|
||||
struct PageInfo {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
// Info Types.
|
||||
enum class InfoType : u32 {
|
||||
CoreMask = 0,
|
||||
PriorityMask = 1,
|
||||
AliasRegionAddress = 2,
|
||||
AliasRegionSize = 3,
|
||||
HeapRegionAddress = 4,
|
||||
HeapRegionSize = 5,
|
||||
TotalMemorySize = 6,
|
||||
UsedMemorySize = 7,
|
||||
DebuggerAttached = 8,
|
||||
ResourceLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
AslrRegionAddress = 12,
|
||||
AslrRegionSize = 13,
|
||||
StackRegionAddress = 14,
|
||||
StackRegionSize = 15,
|
||||
SystemResourceSizeTotal = 16,
|
||||
SystemResourceSizeUsed = 17,
|
||||
ProgramId = 18,
|
||||
InitialProcessIdRange = 19,
|
||||
UserExceptionContextAddress = 20,
|
||||
TotalNonSystemMemorySize = 21,
|
||||
UsedNonSystemMemorySize = 22,
|
||||
IsApplication = 23,
|
||||
FreeThreadCount = 24,
|
||||
ThreadTickCount = 25,
|
||||
IsSvcPermitted = 26,
|
||||
|
||||
MesosphereMeta = 65000,
|
||||
MesosphereCurrentProcess = 65001,
|
||||
};
|
||||
|
||||
enum class BreakReason : u32 {
|
||||
Panic = 0,
|
||||
Assert = 1,
|
||||
User = 2,
|
||||
PreLoadDll = 3,
|
||||
PostLoadDll = 4,
|
||||
PreUnloadDll = 5,
|
||||
PostUnloadDll = 6,
|
||||
CppException = 7,
|
||||
|
||||
NotificationOnlyFlag = 0x80000000,
|
||||
};
|
||||
|
||||
enum class DebugEvent : u32 {
|
||||
CreateProcess = 0,
|
||||
CreateThread = 1,
|
||||
ExitProcess = 2,
|
||||
ExitThread = 3,
|
||||
Exception = 4,
|
||||
};
|
||||
|
||||
enum class DebugThreadParam : u32 {
|
||||
Priority = 0,
|
||||
State = 1,
|
||||
IdealCore = 2,
|
||||
CurrentCore = 3,
|
||||
AffinityMask = 4,
|
||||
};
|
||||
|
||||
enum class DebugException : u32 {
|
||||
UndefinedInstruction = 0,
|
||||
InstructionAbort = 1,
|
||||
DataAbort = 2,
|
||||
AlignmentFault = 3,
|
||||
DebuggerAttached = 4,
|
||||
BreakPoint = 5,
|
||||
UserBreak = 6,
|
||||
DebuggerBreak = 7,
|
||||
UndefinedSystemCall = 8,
|
||||
MemorySystemError = 9,
|
||||
};
|
||||
|
||||
enum class DebugEventFlag : u32 {
|
||||
Stopped = (1u << 0),
|
||||
};
|
||||
|
||||
enum class BreakPointType : u32 {
|
||||
HardwareInstruction = 0,
|
||||
HardwareData = 1,
|
||||
};
|
||||
|
||||
enum class HardwareBreakPointRegisterName : u32 {
|
||||
I0 = 0,
|
||||
I1 = 1,
|
||||
I2 = 2,
|
||||
I3 = 3,
|
||||
I4 = 4,
|
||||
I5 = 5,
|
||||
I6 = 6,
|
||||
I7 = 7,
|
||||
I8 = 8,
|
||||
I9 = 9,
|
||||
I10 = 10,
|
||||
I11 = 11,
|
||||
I12 = 12,
|
||||
I13 = 13,
|
||||
I14 = 14,
|
||||
I15 = 15,
|
||||
D0 = 16,
|
||||
D1 = 17,
|
||||
D2 = 18,
|
||||
D3 = 19,
|
||||
D4 = 20,
|
||||
D5 = 21,
|
||||
D6 = 22,
|
||||
D7 = 23,
|
||||
D8 = 24,
|
||||
D9 = 25,
|
||||
D10 = 26,
|
||||
D11 = 27,
|
||||
D12 = 28,
|
||||
D13 = 29,
|
||||
D14 = 30,
|
||||
D15 = 31,
|
||||
};
|
||||
|
||||
namespace lp64 {
|
||||
struct LastThreadContext {
|
||||
u64 fp;
|
||||
u64 sp;
|
||||
u64 lr;
|
||||
u64 pc;
|
||||
};
|
||||
|
||||
struct PhysicalMemoryInfo {
|
||||
PAddr physical_address;
|
||||
u64 virtual_address;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
struct DebugInfoCreateProcess {
|
||||
u64 program_id;
|
||||
u64 process_id;
|
||||
std::array<char, 0xC> name;
|
||||
u32 flags;
|
||||
u64 user_exception_context_address; // 5.0.0+
|
||||
};
|
||||
|
||||
struct DebugInfoCreateThread {
|
||||
u64 thread_id;
|
||||
u64 tls_address;
|
||||
// Removed in 11.0.0 u64 entrypoint;
|
||||
};
|
||||
|
||||
struct DebugInfoExitProcess {
|
||||
ProcessExitReason reason;
|
||||
};
|
||||
|
||||
struct DebugInfoExitThread {
|
||||
ThreadExitReason reason;
|
||||
};
|
||||
|
||||
struct DebugInfoUndefinedInstructionException {
|
||||
u32 insn;
|
||||
};
|
||||
|
||||
struct DebugInfoDataAbortException {
|
||||
u64 address;
|
||||
};
|
||||
|
||||
struct DebugInfoAlignmentFaultException {
|
||||
u64 address;
|
||||
};
|
||||
|
||||
struct DebugInfoBreakPointException {
|
||||
BreakPointType type;
|
||||
u64 address;
|
||||
};
|
||||
|
||||
struct DebugInfoUserBreakException {
|
||||
BreakReason break_reason;
|
||||
u64 address;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
struct DebugInfoDebuggerBreakException {
|
||||
std::array<u64, 4> active_thread_ids;
|
||||
};
|
||||
|
||||
struct DebugInfoUndefinedSystemCallException {
|
||||
u32 id;
|
||||
};
|
||||
|
||||
union DebugInfoSpecificException {
|
||||
DebugInfoUndefinedInstructionException undefined_instruction;
|
||||
DebugInfoDataAbortException data_abort;
|
||||
DebugInfoAlignmentFaultException alignment_fault;
|
||||
DebugInfoBreakPointException break_point;
|
||||
DebugInfoUserBreakException user_break;
|
||||
DebugInfoDebuggerBreakException debugger_break;
|
||||
DebugInfoUndefinedSystemCallException undefined_system_call;
|
||||
u64 raw;
|
||||
};
|
||||
|
||||
struct DebugInfoException {
|
||||
DebugException type;
|
||||
u64 address;
|
||||
DebugInfoSpecificException specific;
|
||||
};
|
||||
|
||||
union DebugInfo {
|
||||
DebugInfoCreateProcess create_process;
|
||||
DebugInfoCreateThread create_thread;
|
||||
DebugInfoExitProcess exit_process;
|
||||
DebugInfoExitThread exit_thread;
|
||||
DebugInfoException exception;
|
||||
};
|
||||
|
||||
struct DebugEventInfo {
|
||||
DebugEvent type;
|
||||
u32 flags;
|
||||
u64 thread_id;
|
||||
DebugInfo info;
|
||||
};
|
||||
static_assert(sizeof(DebugEventInfo) >= 0x40);
|
||||
|
||||
struct SecureMonitorArguments {
|
||||
std::array<u64, 8> r;
|
||||
};
|
||||
static_assert(sizeof(SecureMonitorArguments) == 0x40);
|
||||
} // namespace lp64
|
||||
|
||||
namespace ilp32 {
|
||||
struct LastThreadContext {
|
||||
u32 fp;
|
||||
u32 sp;
|
||||
u32 lr;
|
||||
u32 pc;
|
||||
};
|
||||
|
||||
struct PhysicalMemoryInfo {
|
||||
PAddr physical_address;
|
||||
u32 virtual_address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct DebugInfoCreateProcess {
|
||||
u64 program_id;
|
||||
u64 process_id;
|
||||
std::array<char, 0xC> name;
|
||||
u32 flags;
|
||||
u32 user_exception_context_address; // 5.0.0+
|
||||
};
|
||||
|
||||
struct DebugInfoCreateThread {
|
||||
u64 thread_id;
|
||||
u32 tls_address;
|
||||
// Removed in 11.0.0 u32 entrypoint;
|
||||
};
|
||||
|
||||
struct DebugInfoExitProcess {
|
||||
ProcessExitReason reason;
|
||||
};
|
||||
|
||||
struct DebugInfoExitThread {
|
||||
ThreadExitReason reason;
|
||||
};
|
||||
|
||||
struct DebugInfoUndefinedInstructionException {
|
||||
u32 insn;
|
||||
};
|
||||
|
||||
struct DebugInfoDataAbortException {
|
||||
u32 address;
|
||||
};
|
||||
|
||||
struct DebugInfoAlignmentFaultException {
|
||||
u32 address;
|
||||
};
|
||||
|
||||
struct DebugInfoBreakPointException {
|
||||
BreakPointType type;
|
||||
u32 address;
|
||||
};
|
||||
|
||||
struct DebugInfoUserBreakException {
|
||||
BreakReason break_reason;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct DebugInfoDebuggerBreakException {
|
||||
std::array<u64, 4> active_thread_ids;
|
||||
};
|
||||
|
||||
struct DebugInfoUndefinedSystemCallException {
|
||||
u32 id;
|
||||
};
|
||||
|
||||
union DebugInfoSpecificException {
|
||||
DebugInfoUndefinedInstructionException undefined_instruction;
|
||||
DebugInfoDataAbortException data_abort;
|
||||
DebugInfoAlignmentFaultException alignment_fault;
|
||||
DebugInfoBreakPointException break_point;
|
||||
DebugInfoUserBreakException user_break;
|
||||
DebugInfoDebuggerBreakException debugger_break;
|
||||
DebugInfoUndefinedSystemCallException undefined_system_call;
|
||||
u64 raw;
|
||||
};
|
||||
|
||||
struct DebugInfoException {
|
||||
DebugException type;
|
||||
u32 address;
|
||||
DebugInfoSpecificException specific;
|
||||
};
|
||||
|
||||
union DebugInfo {
|
||||
DebugInfoCreateProcess create_process;
|
||||
DebugInfoCreateThread create_thread;
|
||||
DebugInfoExitProcess exit_process;
|
||||
DebugInfoExitThread exit_thread;
|
||||
DebugInfoException exception;
|
||||
};
|
||||
|
||||
struct DebugEventInfo {
|
||||
DebugEvent type;
|
||||
u32 flags;
|
||||
u64 thread_id;
|
||||
DebugInfo info;
|
||||
};
|
||||
|
||||
struct SecureMonitorArguments {
|
||||
std::array<u32, 8> r;
|
||||
};
|
||||
static_assert(sizeof(SecureMonitorArguments) == 0x20);
|
||||
} // namespace ilp32
|
||||
|
||||
struct ThreadContext {
|
||||
std::array<u64, 29> r;
|
||||
u64 fp;
|
||||
u64 lr;
|
||||
u64 sp;
|
||||
u64 pc;
|
||||
u32 pstate;
|
||||
u32 padding;
|
||||
std::array<u128, 32> v;
|
||||
u32 fpcr;
|
||||
u32 fpsr;
|
||||
u64 tpidr;
|
||||
};
|
||||
static_assert(sizeof(ThreadContext) == 0x320);
|
||||
|
||||
struct MemoryInfo {
|
||||
u64 base_address;
|
||||
u64 size;
|
||||
MemoryState state;
|
||||
MemoryAttribute attribute;
|
||||
MemoryPermission permission;
|
||||
u32 ipc_count;
|
||||
u32 device_count;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
enum class LimitableResource : u32 {
|
||||
PhysicalMemoryMax = 0,
|
||||
ThreadCountMax = 1,
|
||||
EventCountMax = 2,
|
||||
TransferMemoryCountMax = 3,
|
||||
SessionCountMax = 4,
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class IoPoolType : u32 {
|
||||
// Not supported.
|
||||
Count = 0,
|
||||
};
|
||||
|
||||
enum class MemoryMapping : u32 {
|
||||
IoRegister = 0,
|
||||
Uncached = 1,
|
||||
Memory = 2,
|
||||
};
|
||||
|
||||
enum class KernelDebugType : u32 {
|
||||
Thread = 0,
|
||||
ThreadCallStack = 1,
|
||||
KernelObject = 2,
|
||||
Handle_ = 3,
|
||||
Memory = 4,
|
||||
PageTable = 5,
|
||||
CpuUtilization = 6,
|
||||
Process = 7,
|
||||
SuspendProcess = 8,
|
||||
ResumeProcess = 9,
|
||||
Port = 10,
|
||||
};
|
||||
|
||||
enum class KernelTraceState : u32 {
|
||||
Disabled = 0,
|
||||
Enabled = 1,
|
||||
};
|
||||
|
||||
enum class CodeMemoryOperation : u32 {
|
||||
Map = 0,
|
||||
MapToOwner = 1,
|
||||
Unmap = 2,
|
||||
UnmapFromOwner = 3,
|
||||
};
|
||||
|
||||
enum class InterruptType : u32 {
|
||||
Edge = 0,
|
||||
Level = 1,
|
||||
};
|
||||
|
||||
enum class DeviceName {
|
||||
Afi = 0,
|
||||
Avpc = 1,
|
||||
Dc = 2,
|
||||
Dcb = 3,
|
||||
Hc = 4,
|
||||
Hda = 5,
|
||||
Isp2 = 6,
|
||||
MsencNvenc = 7,
|
||||
Nv = 8,
|
||||
Nv2 = 9,
|
||||
Ppcs = 10,
|
||||
Sata = 11,
|
||||
Vi = 12,
|
||||
Vic = 13,
|
||||
XusbHost = 14,
|
||||
XusbDev = 15,
|
||||
Tsec = 16,
|
||||
Ppcs1 = 17,
|
||||
Dc1 = 18,
|
||||
Sdmmc1a = 19,
|
||||
Sdmmc2a = 20,
|
||||
Sdmmc3a = 21,
|
||||
Sdmmc4a = 22,
|
||||
Isp2b = 23,
|
||||
Gpu = 24,
|
||||
Gpub = 25,
|
||||
Ppcs2 = 26,
|
||||
Nvdec = 27,
|
||||
Ape = 28,
|
||||
Se = 29,
|
||||
Nvjpg = 30,
|
||||
Hc1 = 31,
|
||||
Se1 = 32,
|
||||
Axiap = 33,
|
||||
Etr = 34,
|
||||
Tsecb = 35,
|
||||
Tsec1 = 36,
|
||||
Tsecb1 = 37,
|
||||
Nvdec1 = 38,
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class SystemInfoType : u32 {
|
||||
TotalPhysicalMemorySize = 0,
|
||||
UsedPhysicalMemorySize = 1,
|
||||
InitialProcessIdRange = 2,
|
||||
};
|
||||
|
||||
enum class ProcessInfoType : u32 {
|
||||
ProcessState = 0,
|
||||
};
|
||||
|
||||
struct CreateProcessParameter {
|
||||
std::array<char, 12> name;
|
||||
u32 version;
|
||||
u64 program_id;
|
||||
u64 code_address;
|
||||
s32 code_num_pages;
|
||||
u32 flags;
|
||||
Handle reslimit;
|
||||
s32 system_resource_num_pages;
|
||||
};
|
||||
static_assert(sizeof(CreateProcessParameter) == 0x30);
|
||||
|
||||
} // namespace Kernel::Svc
|
||||
|
||||
@@ -722,4 +722,12 @@ void SvcWrap32(Core::System& system) {
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by Invalidate/Store/FlushProcessDataCache32
|
||||
template <Result func(Core::System&, Handle, u64, u64)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
const u64 address = (Param(system, 3) << 32) | Param(system, 2);
|
||||
const u64 size = (Param(system, 4) << 32) | Param(system, 1);
|
||||
FuncReturn32(system, func(system, Param32(system, 0), address, size).raw);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -28,30 +28,49 @@ enum class ErrorModule : u32 {
|
||||
Loader = 9,
|
||||
CMIF = 10,
|
||||
HIPC = 11,
|
||||
TMA = 12,
|
||||
DMNT = 13,
|
||||
GDS = 14,
|
||||
PM = 15,
|
||||
NS = 16,
|
||||
BSDSockets = 17,
|
||||
HTC = 18,
|
||||
TSC = 19,
|
||||
NCMContent = 20,
|
||||
SM = 21,
|
||||
RO = 22,
|
||||
GC = 23,
|
||||
SDMMC = 24,
|
||||
OVLN = 25,
|
||||
SPL = 26,
|
||||
Socket = 27,
|
||||
HTCLOW = 29,
|
||||
DDSF = 30,
|
||||
HTCFS = 31,
|
||||
Async = 32,
|
||||
Util = 33,
|
||||
TIPC = 35,
|
||||
ANIF = 37,
|
||||
ETHC = 100,
|
||||
I2C = 101,
|
||||
GPIO = 102,
|
||||
UART = 103,
|
||||
CPAD = 104,
|
||||
Settings = 105,
|
||||
FTM = 106,
|
||||
WLAN = 107,
|
||||
XCD = 108,
|
||||
TMP451 = 109,
|
||||
NIFM = 110,
|
||||
Hwopus = 111,
|
||||
LSM6DS3 = 112,
|
||||
Bluetooth = 113,
|
||||
VI = 114,
|
||||
NFP = 115,
|
||||
Time = 116,
|
||||
FGM = 117,
|
||||
OE = 118,
|
||||
BH1730FVC = 119,
|
||||
PCIe = 120,
|
||||
Friends = 121,
|
||||
BCAT = 122,
|
||||
@@ -65,7 +84,7 @@ enum class ErrorModule : u32 {
|
||||
AHID = 130,
|
||||
Qlaunch = 132,
|
||||
PCV = 133,
|
||||
OMM = 134,
|
||||
USBPD = 134,
|
||||
BPC = 135,
|
||||
PSM = 136,
|
||||
NIM = 137,
|
||||
@@ -75,18 +94,22 @@ enum class ErrorModule : u32 {
|
||||
NSD = 141,
|
||||
PCTL = 142,
|
||||
BTM = 143,
|
||||
LA = 144,
|
||||
ETicket = 145,
|
||||
NGC = 146,
|
||||
ERPT = 147,
|
||||
APM = 148,
|
||||
CEC = 149,
|
||||
Profiler = 150,
|
||||
ErrorUpload = 151,
|
||||
LIDBE = 152,
|
||||
Audio = 153,
|
||||
NPNS = 154,
|
||||
NPNSHTTPSTREAM = 155,
|
||||
ARP = 157,
|
||||
SWKBD = 158,
|
||||
BOOT = 159,
|
||||
NetDiag = 160,
|
||||
NFCMifare = 161,
|
||||
UserlandAssert = 162,
|
||||
Fatal = 163,
|
||||
@@ -94,17 +117,68 @@ enum class ErrorModule : u32 {
|
||||
SPSM = 165,
|
||||
BGTC = 167,
|
||||
UserlandCrash = 168,
|
||||
SASBUS = 169,
|
||||
PI = 170,
|
||||
AudioCtrl = 172,
|
||||
LBL = 173,
|
||||
JIT = 175,
|
||||
HDCP = 176,
|
||||
OMM = 177,
|
||||
PDM = 178,
|
||||
OLSC = 179,
|
||||
SREPO = 180,
|
||||
Dauth = 181,
|
||||
STDFU = 182,
|
||||
DBG = 183,
|
||||
DHCPS = 186,
|
||||
SPI = 187,
|
||||
AVM = 188,
|
||||
PWM = 189,
|
||||
RTC = 191,
|
||||
Regulator = 192,
|
||||
LED = 193,
|
||||
SIO = 195,
|
||||
PCM = 196,
|
||||
CLKRST = 197,
|
||||
POWCTL = 198,
|
||||
AudioOld = 201,
|
||||
HID = 202,
|
||||
LDN = 203,
|
||||
CS = 204,
|
||||
Irsensor = 205,
|
||||
Capture = 206,
|
||||
Manu = 208,
|
||||
ATK = 209,
|
||||
WEB = 210,
|
||||
LCS = 211,
|
||||
GRC = 212,
|
||||
Repair = 213,
|
||||
Album = 214,
|
||||
RID = 215,
|
||||
Migration = 216,
|
||||
MigrationLdcServ = 217,
|
||||
HIDBUS = 218,
|
||||
ENS = 219,
|
||||
WebSocket = 223,
|
||||
DCDMTP = 227,
|
||||
PGL = 228,
|
||||
Notification = 229,
|
||||
INS = 230,
|
||||
LP2P = 231,
|
||||
RCD = 232,
|
||||
LCM40607 = 233,
|
||||
PRC = 235,
|
||||
TMAHTC = 237,
|
||||
ECTX = 238,
|
||||
MNPP = 239,
|
||||
HSHL = 240,
|
||||
CAPMTP = 242,
|
||||
DP2HDMI = 244,
|
||||
Cradle = 245,
|
||||
SProfile = 246,
|
||||
NDRM = 250,
|
||||
TSPM = 499,
|
||||
DevMenu = 500,
|
||||
GeneralWebApplet = 800,
|
||||
WifiWebAuthApplet = 809,
|
||||
WhitelistedApplet = 810,
|
||||
@@ -423,16 +497,17 @@ constexpr void UpdateCurrentResultReference<const Result>(Result result_referenc
|
||||
} // namespace ResultImpl
|
||||
|
||||
#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \
|
||||
[[maybe_unused]] constexpr bool HasPrevRef_##COUNTER_VALUE = \
|
||||
[[maybe_unused]] constexpr bool CONCAT2(HasPrevRef_, COUNTER_VALUE) = \
|
||||
std::same_as<decltype(__TmpCurrentResultReference), Result&>; \
|
||||
[[maybe_unused]] auto& PrevRef_##COUNTER_VALUE = __TmpCurrentResultReference; \
|
||||
[[maybe_unused]] Result __tmp_result_##COUNTER_VALUE = ResultSuccess; \
|
||||
Result& __TmpCurrentResultReference = \
|
||||
HasPrevRef_##COUNTER_VALUE ? PrevRef_##COUNTER_VALUE : __tmp_result_##COUNTER_VALUE
|
||||
[[maybe_unused]] Result CONCAT2(PrevRef_, COUNTER_VALUE) = __TmpCurrentResultReference; \
|
||||
[[maybe_unused]] Result CONCAT2(__tmp_result_, COUNTER_VALUE) = ResultSuccess; \
|
||||
Result& __TmpCurrentResultReference = CONCAT2(HasPrevRef_, COUNTER_VALUE) \
|
||||
? CONCAT2(PrevRef_, COUNTER_VALUE) \
|
||||
: CONCAT2(__tmp_result_, COUNTER_VALUE)
|
||||
|
||||
#define ON_RESULT_RETURN_IMPL(...) \
|
||||
static_assert(std::same_as<decltype(__TmpCurrentResultReference), Result&>); \
|
||||
auto RESULT_GUARD_STATE_##__COUNTER__ = \
|
||||
auto CONCAT2(RESULT_GUARD_STATE_, __COUNTER__) = \
|
||||
ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \
|
||||
__TmpCurrentResultReference) + \
|
||||
[&]()
|
||||
|
||||
177
src/core/hle/service/am/applets/applet_cabinet.cpp
Normal file
177
src/core/hle/service/am/applets/applet_cabinet.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applet_cabinet.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/nfp/nfp_device.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
|
||||
const Core::Frontend::CabinetApplet& frontend_)
|
||||
: Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{
|
||||
system_,
|
||||
"CabinetApplet"} {
|
||||
|
||||
availability_change_event =
|
||||
service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
|
||||
}
|
||||
|
||||
Cabinet::~Cabinet() = default;
|
||||
|
||||
void Cabinet::Initialize() {
|
||||
Applet::Initialize();
|
||||
|
||||
LOG_INFO(Service_HID, "Initializing Cabinet Applet.");
|
||||
|
||||
LOG_DEBUG(Service_HID,
|
||||
"Initializing Applet with common_args: arg_version={}, lib_version={}, "
|
||||
"play_startup_sound={}, size={}, system_tick={}, theme_color={}",
|
||||
common_args.arguments_version, common_args.library_version,
|
||||
common_args.play_startup_sound, common_args.size, common_args.system_tick,
|
||||
common_args.theme_color);
|
||||
|
||||
const auto storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
|
||||
const auto applet_input_data = storage->GetData();
|
||||
ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings));
|
||||
|
||||
std::memcpy(&applet_input_common, applet_input_data.data(),
|
||||
sizeof(StartParamForAmiiboSettings));
|
||||
}
|
||||
|
||||
bool Cabinet::TransactionComplete() const {
|
||||
return is_complete;
|
||||
}
|
||||
|
||||
Result Cabinet::GetStatus() const {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Cabinet::ExecuteInteractive() {
|
||||
ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
|
||||
}
|
||||
|
||||
void Cabinet::Execute() {
|
||||
if (is_complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto callback = [this](bool apply_changes, const std::string& amiibo_name) {
|
||||
DisplayCompleted(apply_changes, amiibo_name);
|
||||
};
|
||||
|
||||
// TODO: listen on all controllers
|
||||
if (nfp_device == nullptr) {
|
||||
nfp_device = std::make_shared<Service::NFP::NfpDevice>(
|
||||
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
|
||||
nfp_device->Initialize();
|
||||
nfp_device->StartDetection(Service::NFP::TagProtocol::All);
|
||||
}
|
||||
|
||||
const Core::Frontend::CabinetParameters parameters{
|
||||
.tag_info = applet_input_common.tag_info,
|
||||
.register_info = applet_input_common.register_info,
|
||||
.mode = applet_input_common.applet_mode,
|
||||
};
|
||||
|
||||
switch (applet_input_common.applet_mode) {
|
||||
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
|
||||
case Service::NFP::CabinetMode::StartGameDataEraser:
|
||||
case Service::NFP::CabinetMode::StartRestorer:
|
||||
case Service::NFP::CabinetMode::StartFormatter:
|
||||
frontend.ShowCabinetApplet(callback, parameters, nfp_device);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
|
||||
DisplayCompleted(false, {});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) {
|
||||
Service::Mii::MiiManager manager;
|
||||
ReturnValueForAmiiboSettings applet_output{};
|
||||
|
||||
if (!apply_changes) {
|
||||
Cancel();
|
||||
}
|
||||
|
||||
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
|
||||
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
|
||||
Cancel();
|
||||
}
|
||||
|
||||
if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
|
||||
nfp_device->Mount(Service::NFP::MountTarget::All);
|
||||
}
|
||||
|
||||
switch (applet_input_common.applet_mode) {
|
||||
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
|
||||
Service::NFP::AmiiboName name{};
|
||||
std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
|
||||
nfp_device->SetNicknameAndOwner(name);
|
||||
break;
|
||||
}
|
||||
case Service::NFP::CabinetMode::StartGameDataEraser:
|
||||
nfp_device->DeleteApplicationArea();
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartRestorer:
|
||||
nfp_device->RestoreAmiibo();
|
||||
break;
|
||||
case Service::NFP::CabinetMode::StartFormatter:
|
||||
nfp_device->DeleteAllData();
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
applet_output.device_handle = applet_input_common.device_handle;
|
||||
applet_output.result = CabinetResult::Cancel;
|
||||
const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
|
||||
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
|
||||
nfp_device->Finalize();
|
||||
|
||||
if (reg_result.IsSuccess()) {
|
||||
applet_output.result |= CabinetResult::RegisterInfo;
|
||||
}
|
||||
|
||||
if (tag_result.IsSuccess()) {
|
||||
applet_output.result |= CabinetResult::TagInfo;
|
||||
}
|
||||
|
||||
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
|
||||
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
|
||||
|
||||
is_complete = true;
|
||||
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
void Cabinet::Cancel() {
|
||||
ReturnValueForAmiiboSettings applet_output{};
|
||||
applet_output.device_handle = applet_input_common.device_handle;
|
||||
applet_output.result = CabinetResult::Cancel;
|
||||
nfp_device->Finalize();
|
||||
|
||||
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
|
||||
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
|
||||
|
||||
is_complete = true;
|
||||
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
104
src/core/hle/service/am/applets/applet_cabinet.h
Normal file
104
src/core/hle/service/am/applets/applet_cabinet.h
Normal file
@@ -0,0 +1,104 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nfp/nfp_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Service::NFP {
|
||||
class NfpDevice;
|
||||
}
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class CabinetAppletVersion : u32 {
|
||||
Version1 = 0x1,
|
||||
};
|
||||
|
||||
enum class CabinetResult : u8 {
|
||||
Cancel = 0,
|
||||
TagInfo = 1 << 1,
|
||||
RegisterInfo = 1 << 2,
|
||||
All = TagInfo | RegisterInfo,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(CabinetResult)
|
||||
|
||||
// This is nn::nfp::AmiiboSettingsStartParam
|
||||
struct AmiiboSettingsStartParam {
|
||||
u64 device_handle;
|
||||
std::array<u8, 0x20> param_1;
|
||||
u8 param_2;
|
||||
};
|
||||
static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
|
||||
"AmiiboSettingsStartParam is an invalid size");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// This is nn::nfp::StartParamForAmiiboSettings
|
||||
struct StartParamForAmiiboSettings {
|
||||
u8 param_1;
|
||||
Service::NFP::CabinetMode applet_mode;
|
||||
u8 flags;
|
||||
u8 amiibo_settings_1;
|
||||
u64 device_handle;
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
std::array<u8, 0x20> amiibo_settings_3;
|
||||
INSERT_PADDING_BYTES(0x24);
|
||||
};
|
||||
static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8,
|
||||
"StartParamForAmiiboSettings is an invalid size");
|
||||
|
||||
// This is nn::nfp::ReturnValueForAmiiboSettings
|
||||
struct ReturnValueForAmiiboSettings {
|
||||
CabinetResult result;
|
||||
INSERT_PADDING_BYTES(0x3);
|
||||
u64 device_handle;
|
||||
Service::NFP::TagInfo tag_info;
|
||||
Service::NFP::RegisterInfo register_info;
|
||||
INSERT_PADDING_BYTES(0x24);
|
||||
};
|
||||
static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
|
||||
"ReturnValueForAmiiboSettings is an invalid size");
|
||||
#pragma pack(pop)
|
||||
|
||||
class Cabinet final : public Applet {
|
||||
public:
|
||||
explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
|
||||
const Core::Frontend::CabinetApplet& frontend_);
|
||||
~Cabinet() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
Result GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
|
||||
void Cancel();
|
||||
|
||||
private:
|
||||
const Core::Frontend::CabinetApplet& frontend;
|
||||
Core::System& system;
|
||||
|
||||
bool is_complete{false};
|
||||
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
|
||||
Kernel::KEvent* availability_change_event;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
StartParamForAmiiboSettings applet_input_common{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -144,6 +144,7 @@ void Error::Initialize() {
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user