Compare commits

..

108 Commits

Author SHA1 Message Date
Daniel Lim Wee Soong
8529d84f31 Remove dependency chrono
Earlier chrono was included but after some code changed it was no longer needed

Forgot to remove it so I'm removing it now
2018-03-22 21:53:51 +08:00
Daniel Lim Wee Soong
47f96fe13a Change "yuzu starting..." to be logged with the new macro
Just as a proof that it works
2018-03-22 18:26:43 +08:00
Daniel Lim Wee Soong
3b558eebee Logging: Create logging macros based on fmtlib
Add a new set of logging macros based on fmtlib
Similar but not exactly the same as https://github.com/citra-emu/citra/pull/3533

Citra currently uses a different version of fmt, which does not support FMT_VARIADIC so
make_args is used instead. On the other hand, yuzu uses fmt 4.1.0 which doesn't have make_args yet
so FMT_VARIADIC is used.
2018-03-22 18:21:29 +08:00
bunnei
f1beb69899 Merge pull request #215 from N00byKing/umapsharedmmry
UnmapSharedMemory
2018-02-25 21:04:24 -08:00
bunnei
7e45669ccb Merge pull request #222 from shinyquagsire23/npdm-parsing
NPDM Parsing
2018-02-25 16:44:51 -08:00
shinyquagsire23
e29710818f file_sys: Style tweaks
Asdf
2018-02-25 16:34:29 -07:00
shinyquagsire23
487f8bc018 loader: Check error on NPDM load, use TID for CodeSet 2018-02-25 07:41:36 -07:00
shinyquagsire23
fd3806fd30 loader: Use NPDM information when loading NSOs 2018-02-25 07:02:47 -07:00
shinyquagsire23
2b28fd7809 file_sys: Add support for parsing NPDM files 2018-02-25 07:02:39 -07:00
N00byKing
08c6ac02cf (Hopefully) Fix MinGW Build 2018-02-25 13:40:22 +01:00
N00byKing
2b41c6e573 Add UnmapSharedMemory
C++11 requires spaces on the Identifier

Add inttypes include

clang
2018-02-25 11:38:06 +01:00
bunnei
c45173c9a6 Merge pull request #212 from mailwl/stubs
Stub some functions
2018-02-23 21:09:56 -08:00
bunnei
32c509b82d Merge pull request #217 from shinyquagsire23/time-s-missing
time: Add missing time:s functions, used for libnx
2018-02-23 08:52:37 -08:00
bunnei
6bf7108545 Merge pull request #210 from MerryMage/f/dynarmic/sysreg
arm_dynarmic: Implement system registers and provide more hooks
2018-02-23 08:51:52 -08:00
shinyquagsire23
a63d4fa5b4 time: Add missing time:s functions, used for libnx 2018-02-23 00:34:15 -07:00
mailwl
e4f94ee30b Stub more functions 2018-02-22 17:28:15 +03:00
mailwl
910198a29a Stub am::SetScreenShotPermission, and bsd::StartMonitoring functions 2018-02-22 13:04:23 +03:00
bunnei
7f0ecbf859 Merge pull request #211 from shinyquagsire23/time_local
time: Add GetStandardLocalSystemClock, used by libnx
2018-02-22 01:09:01 -05:00
bunnei
fa28dbe0f3 Merge pull request #209 from MerryMage/f/scheduler-shutdown
core: Fix scheduler-shutdown related crash
2018-02-22 01:07:54 -05:00
shinyquagsire23
944132dbe5 time: Add GetStandardLocalSystemClock, used by libnx 2018-02-21 18:43:05 -07:00
MerryMage
32d127ad3e dynarmic: Update to 6b4c6b0
6b4c6b0 impl: Update PC when raising exception
7a1313a A64: Implement FDIV (vector)
b2d781d system: Raise exception for YIELD, WFE, WFI, SEV, SEVL
b277bf5 Correct FPSR and FPCR
7673933 A64: Implement USHL
8d0e558 A64: Implement UCVTF (vector, integer), scalar variant
da9a4f8 A64: Partially implement FCVTZU (scalar, fixed-point) and FCVTZS (scalar, fixed-point)
7479684 A64: Implement system register TPIDR_EL0
0fd75fd A64: Implement system registers FPCR and FPSR
31e370c A64: Implement system register CNTPCT_EL0
9a88fd3 A64: Implement system register CTR_EL0
1d16896 A64: Implement NEG (vector)
3184edf IR: Add IR instruction ZeroVector
31f8fbc emit_x64_floating_point: Add maybe_unused to preprocess parameter
567eb1a A64: Implement FMINNM (scalar)
c6d8fa1 A64: Implement FMAXNM (scalar)
616056d constant_pool: Add frame parameter
a3747cb A64: Implement ADDP (scalar)
5cd5d9f reg_alloc: Only exchange GPRs
dd0452a A64: Implement DUP (element), scalar variant
e5732ea emit_x64_floating_point: Correct FP{Max,Min}{32,64} implementations for -0/+0
40eb9c3 A64: Implement FMAX (scalar), FMIN (scalar)
7cef39b fuzz_with_unicorn: QEMU's implementation of FCVT is incorrect
826dce2 travis: Switch unicorn repository
9605f28 a64/config: Allow NaN emulation accuracy to be set
e9435bc a64_emit_x64: Add conf to A64EmitContext
30b596d fuzz_with_unicorn: Explicitly test floating point instructions
be292a8 A64: Implement FSQRT (scalar)
3c42d48 backend_x64: Accurately handle NaNs
4aefed0 fuzz_with_unicorn: Print AArch64 disassembly
2018-02-21 21:39:07 +00:00
MerryMage
e8b9731af3 arm_dynarmic: LOG_INFO on unicorn fallback 2018-02-21 21:39:07 +00:00
MerryMage
cc368de1a0 memory: LOG_ERROR when falling off end of page table 2018-02-21 21:39:07 +00:00
MerryMage
1cdc74c5e9 core: Fix scheduler-shutdown related crash 2018-02-21 16:38:18 +00:00
bunnei
6a2197806e Merge pull request #206 from mailwl/aoc-listaddoncontent
Service/AOC: stub ListAddOnContent function
2018-02-20 10:45:50 -05:00
bunnei
587f22b610 Merge pull request #205 from bunnei/more-puyo-stubs
Stub several friend:a and acc:u0 service functions
2018-02-20 10:44:55 -05:00
mailwl
46931a9566 Service/AOC: stub ListAddOnContent function 2018-02-20 10:30:12 +03:00
bunnei
678574972a acc_u0: Stub ListOpenUsers service function. 2018-02-19 17:39:41 -05:00
bunnei
7bee3427d0 service: Add Friend service interface. 2018-02-19 17:34:02 -05:00
bunnei
1d491d636d logging: Add category for Friend service. 2018-02-19 17:31:54 -05:00
bunnei
23fe6f5be3 Merge pull request #202 from bunnei/scheduler-cleanup
Scheduler cleanup
2018-02-19 17:23:05 -05:00
bunnei
8db80d8389 scheduler: Cleanup based on PR feedback. 2018-02-19 16:46:42 -05:00
bunnei
ad55d22130 Merge pull request #203 from Subv/ensure_save_data
AM: Corrected the response in EnsureSaveData.
2018-02-19 15:32:54 -05:00
Subv
5ab285f1f9 AM: Corrected the response in EnsureSaveData.
The values are still unknown and the function is still considered a stub.
Puyo Puyo Tetris now tries to call fsp-srv:MountSaveData.
2018-02-18 18:09:52 -05:00
bunnei
ac81c02ed9 kernel: Use Scheduler class for threading. 2018-02-18 15:17:16 -05:00
bunnei
c78d495161 kernel: Add Scheduler, which encapsulates the scheduling loading from Thread module. 2018-02-18 14:58:40 -05:00
bunnei
2d4a6883bc core: Use shared_ptr for cpu_core. 2018-02-18 14:52:09 -05:00
bunnei
cec0d4f191 kernel: Remove unused address_arbiter code. 2018-02-18 14:46:11 -05:00
bunnei
de43db3400 Merge pull request #198 from N00byKing/clang
Use Docker for Build Target clang-format for travis.
2018-02-18 14:12:11 -05:00
bunnei
ec39c9eb32 Merge pull request #201 from Subv/ipc_delay_
Kernel/IPC: Add a small delay after each SyncRequest to prevent thread starvation.
2018-02-18 14:11:34 -05:00
bunnei
5babad5de5 Merge pull request #200 from Subv/bufferproducerfence
Make the fence handling in Vi a little less of a hack.
2018-02-18 14:11:04 -05:00
Subv
94ee8fc97b Kernel/IPC: Add a small delay after each SyncRequest to prevent thread starvation.
Ported from citra PR #3091

The delay specified here is from a Nintendo 3DS, and should be measured in a Nintendo Switch.

This change is enough to prevent Puyo Puyo Tetris's main thread starvation.
2018-02-18 13:25:48 -05:00
N00byKing
b36ce74d18 Update build.sh 2018-02-18 01:01:24 +01:00
bunnei
f6e548fbc0 Merge pull request #199 from FernandoS27/update_dynarmic
Updated Dynarmic
2018-02-17 14:32:17 -05:00
Subv
416f692f6e nvmap: Make IocFromId return the same existing handle instead of creating a new one.
Games like Puyo Puyo Tetris and BOTW seem to depend on the buffer always having the same handle
2018-02-17 14:01:01 -05:00
Subv
d758332425 Parcel: Ensure we don't read past the end of the parcels in Vi. 2018-02-17 14:00:44 -05:00
Subv
2662de6e52 Vi: Mark all fences as NO_FENCE in the DequeueBuffer response parcel. 2018-02-17 14:00:30 -05:00
Subv
1b64160d83 Vi: Always write the IGBPBuffer in the RequestBuffer response parcel.
This may break libnx homebrew due to a bug in libnx but is required by official games since they always assume that the buffer will be there.
2018-02-17 13:59:45 -05:00
FernandoS27
bd6432f1ff updated dynarmic 2018-02-17 14:57:32 -04:00
bunnei
3388208597 Merge pull request #197 from mailwl/hid
Service/hid: stub some functions
2018-02-16 20:55:26 -05:00
N00byKing
947831ff1e Use Docker for Build Target clang-format for travis.
This uses the (apparently) more stable Ubuntu Repo instead of the LLVM one.
2018-02-16 22:21:31 +01:00
mailwl
6797d4a907 Service/hid: stub some functions 2018-02-16 06:15:05 +03:00
bunnei
98ffad4303 Merge pull request #195 from bunnei/shared-font
pl_u: Add basic support for shared fonts.
2018-02-15 17:22:59 -05:00
Subv
7a1917e0fd nvhost-ctrl: Stub NVHOST_IOCTL_CTRL_EVENT_WAIT. 2018-02-14 22:57:57 -05:00
Subv
35d0d06885 Vi: Mark the fences as valid in the DequeueBuffer response parcel. 2018-02-14 22:57:56 -05:00
Subv
d18446f63a Vi: Added a missing u32 in the DequeueBuffer response parcel. 2018-02-14 22:57:56 -05:00
Subv
b78ffc4abf Vi: Don't write the IGBPBuffer in the IGBPRequestBufferResponseParcel. 2018-02-14 22:57:55 -05:00
Subv
8dee5663b3 Vi: Properly write the BufferProducerFence object in the DequeueBuffer response parcel. 2018-02-14 22:57:54 -05:00
bunnei
df008a159b shared_memory: Remove some checks. 2018-02-14 22:24:06 -05:00
bunnei
42c062c620 pl_u: Implement basic shared font loading from RAM dump. 2018-02-14 22:22:41 -05:00
bunnei
fa58d95027 log: Add logging category for NS services. 2018-02-14 21:43:11 -05:00
bunnei
e017184445 hid: Stub GetVibrationDeviceInfo and SendVibrationValues. 2018-02-14 21:16:27 -05:00
bunnei
db873a232c Merge pull request #188 from bunnei/refactor-buffer-descriptor
Refactor IPC buffer descriptor interface
2018-02-14 18:31:53 -05:00
bunnei
88bfec37ce hle_ipc: Remove const from WriteBuffer size. 2018-02-14 14:21:10 -05:00
bunnei
756e9f1484 Merge pull request #192 from jroweboy/fix-fps
Fix fps counter to correctly measure frame end when there was no frame to draw
2018-02-14 13:48:33 -05:00
James Rowe
61c39f0fdd Fix fps counter to correctly measure frame end when there was no frame to draw 2018-02-14 10:16:39 -07:00
bunnei
b65c096be5 Merge pull request #190 from bunnei/fix-qt-waittree
debugger: Fix wait_tree crash.
2018-02-14 10:18:56 -05:00
bunnei
d939792b9b Merge pull request #191 from lioncash/log
core: Silence formatting specifier warnings
2018-02-14 10:07:03 -05:00
Lioncash
fe0775d2f4 memory: Silence formatting sepecifier warnings 2018-02-14 01:54:54 -05:00
Lioncash
e6bf72877f nso: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
2ade136ff4 deconstructed_rom_directory: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
eba57fce88 nvdrv/interface: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
ee170cbcea nvmap: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
983777a317 nvhost_gpu: Silence formatting specifier warnings 2018-02-14 01:52:55 -05:00
Lioncash
2d388a75f0 nvhost_ctrl: Silence formatting specifier warnings 2018-02-14 01:52:54 -05:00
Lioncash
ce1fe0387f nvhost_ctrl_gpu: Silence formatting specifier warnings 2018-02-14 01:52:54 -05:00
Lioncash
dc97117a0b nvhost_as_gpu: Silence formatting specifier warnings 2018-02-14 01:52:49 -05:00
Lioncash
c1146d2a5f thread: Silence formatting specifier warnings 2018-02-14 01:50:14 -05:00
Lioncash
1e33db8573 vm_manager: Silence formatting specifier warnings 2018-02-14 01:50:14 -05:00
Lioncash
51ce224a96 gdbstub: Silence formatting specifier warnings 2018-02-14 01:50:01 -05:00
bunnei
f1b82634bc Merge pull request #189 from lioncash/misc
maxwell_3d: Make constructor explicit
2018-02-14 00:49:03 -05:00
bunnei
6fd19f05f1 hle_ipc: Add GetReadBufferSize and check write buffer size. 2018-02-14 00:14:17 -05:00
bunnei
c85e3a2234 debugger: Fix wait_tree crash. 2018-02-14 00:02:14 -05:00
bunnei
516a95721c service: Remove remaining uses of BufferDescriptor*. 2018-02-13 23:54:13 -05:00
bunnei
d6e52581ac audio: Use WriteBuffer instead of BufferDescriptorB. 2018-02-13 23:54:12 -05:00
bunnei
f16bb1dfcf vi: Eliminate direct usage of BufferDescriptorB. 2018-02-13 23:54:12 -05:00
bunnei
d42e77797e nvdrv: Use ReadBuffer/WriteBuffer functions for Ioctl. 2018-02-13 23:54:12 -05:00
bunnei
8f84665775 vi: Use ReadBuffer/WriteBuffer functions for TransactParcel. 2018-02-13 23:54:11 -05:00
bunnei
fc1b425520 hle_ipc: Add helper functions for reading and writing buffers. 2018-02-13 23:54:07 -05:00
Lioncash
490d0e36a0 maxwell_3d: Make constructor explicit 2018-02-13 23:47:51 -05:00
bunnei
1ce6fff064 hle_ipc: Add helper functions for reading and writing buffers. 2018-02-13 23:26:03 -05:00
bunnei
4f8ee5e456 vi: Fix TransactParcelAuto to support both buffer formats. 2018-02-13 23:26:01 -05:00
bunnei
af8ae770ef Merge pull request #187 from Subv/maxwell3d_query
GPU: Partially implemented the QUERY_* registers in the Maxwell3D engine.
2018-02-13 23:25:07 -05:00
bunnei
0a55eb588b audren_u: Schedule reoccuring event. (#183)
* audren_u: Schedule reoccuring event.

* audren_u: Stub GetAudioRenderersProcessMasterVolume, and misc. changes.
2018-02-13 20:47:33 -05:00
bunnei
826e9c9782 Merge pull request #181 from bunnei/vi-fixes-2
VI cleanup and add a hack for booting games
2018-02-13 19:17:27 -05:00
bunnei
87c3c93464 Merge pull request #184 from mailwl/lm
Service/lm: add support to multiline logs
2018-02-13 10:29:33 -05:00
mailwl
55de13efcc Service/lm: add support to multiline logs 2018-02-13 09:44:53 +03:00
bunnei
91e19deb39 vi: Add FENCE_HACK, which is useful for booting BOTW. 2018-02-12 21:24:40 -05:00
bunnei
a9e4e8294a vi: Stub TransactParcel CancelBuffer. 2018-02-12 21:24:39 -05:00
bunnei
4f969e2271 TransactParcel: Move WriteBlock to narrowest scope. 2018-02-12 21:24:38 -05:00
bunnei
0a87eb71ba Merge pull request #180 from MerryMage/f/dynarmic/direct-page-table
arm_dynarmic: Support direct page table access
2018-02-12 19:17:31 -05:00
MerryMage
6085d32cf5 arm_dynarmic: Support direct page table access 2018-02-12 21:53:32 +00:00
bunnei
ce8006e851 Merge pull request #179 from gdkchan/audren_stubs
Stub RequestUpdateAudioRenderer, StartAudioRenderer and StopAudioRenderer
2018-02-12 16:33:30 -05:00
bunnei
be5ba4d952 Merge pull request #178 from Subv/command_buffers
GPU: Added a command processor to decode the GPU pushbuffers and forward the commands to their respective engines
2018-02-12 13:51:52 -05:00
Subv
ac61a7d1e6 GPU: Partially implemented the QUERY_* registers in the Maxwell3D engine.
Only QueryMode::Write is supported at the moment.
2018-02-12 12:34:41 -05:00
Subv
6cddf9d88e Make a GPU class in VideoCore to contain the GPU state.
Also moved the GPU MemoryManager class to video_core since it makes more sense for it to be there.
2018-02-11 23:44:12 -05:00
Subv
e01a8f2187 GPU: Added a command processor to decode the GPU pushbuffers and forward the commands to their respective engines. 2018-02-11 22:42:48 -05:00
Subv
ba2426aa3f nvdrv: Make the GPU memory manager available to nvhost-gpu. 2018-02-11 21:30:23 -05:00
101 changed files with 2186 additions and 805 deletions

View File

@@ -3,16 +3,11 @@ matrix:
include:
- os: linux
env: NAME="clang-format"
sudo: required
dist: trusty
addons:
apt:
sources:
- sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
- sourceline: 'deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main'
packages:
- clang-format-6.0
script: "./.travis/clang-format/script.sh"
services: docker
install: "./.travis/clang-format/deps.sh"
script: "./.travis/clang-format/build.sh"
- os: linux
env: NAME="linux build"
sudo: required

3
.travis/clang-format/build.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash -ex
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh

3
.travis/clang-format/deps.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh -ex
docker pull ubuntu:18.04

8
.travis/clang-format/docker.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash -ex
apt-get update
apt-get install -y clang-format-6.0
# Run clang-format
cd /yuzu
./.travis/clang-format/script.sh

View File

@@ -90,7 +90,7 @@ endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::boost microprofile)
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
if (ARCHITECTURE_x86_64)
target_link_libraries(common PRIVATE xbyak)
endif()

View File

@@ -11,6 +11,7 @@
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
namespace Log {
@@ -35,11 +36,14 @@ namespace Log {
SUB(Service, ACC) \
SUB(Service, Audio) \
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, HID) \
SUB(Service, LM) \
SUB(Service, NIFM) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, PCTL) \
SUB(Service, SET) \
@@ -99,25 +103,20 @@ const char* GetLevelName(Level log_level) {
}
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, const char* format, va_list args) {
const char* function, std::string message) {
using std::chrono::duration_cast;
using std::chrono::steady_clock;
static steady_clock::time_point time_origin = steady_clock::now();
std::array<char, 4 * 1024> formatting_buffer;
Entry entry;
entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
entry.log_class = log_class;
entry.log_level = log_level;
snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function,
line_nr);
entry.location = std::string(formatting_buffer.data());
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
entry.message = std::string(formatting_buffer.data());
entry.filename = Common::TrimSourcePath(filename);
entry.line_num = line_nr;
entry.function = function;
entry.message = std::move(message);
return entry;
}
@@ -128,15 +127,27 @@ void SetFilter(Filter* new_filter) {
filter = new_filter;
}
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, ...) {
if (filter != nullptr && !filter->CheckMessage(log_class, log_level))
if (filter && !filter->CheckMessage(log_class, log_level))
return;
std::array<char, 4 * 1024> formatting_buffer;
va_list args;
va_start(args, format);
Entry entry = CreateEntry(log_class, log_level, filename, line_nr, function, format, args);
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
va_end(args);
Entry entry = CreateEntry(log_class, log_level, filename, line_num, function,
std::string(formatting_buffer.data()));
PrintColoredMessage(entry);
}
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, const fmt::ArgList& args) {
if (filter && !filter->CheckMessage(log_class, log_level))
return;
Entry entry =
CreateEntry(log_class, log_level, filename, line_num, function, fmt::format(format, args));
PrintColoredMessage(entry);
}

View File

@@ -22,13 +22,16 @@ struct Entry {
std::chrono::microseconds timestamp;
Class log_class;
Level log_level;
std::string location;
std::string filename;
unsigned int line_num;
std::string function;
std::string message;
Entry() = default;
Entry(Entry&& o) = default;
Entry& operator=(Entry&& o) = default;
Entry& operator=(const Entry& o) = default;
};
/**
@@ -44,7 +47,7 @@ const char* GetLevelName(Level log_level);
/// Creates a log entry by formatting the given source location, and message.
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, const char* format, va_list args);
const char* function, std::string message);
void SetFilter(Filter* filter);
} // namespace Log

View File

@@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const std::string::const_iterator end) {
auto level_separator = std::find(begin, end, ':');
if (level_separator == end) {
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
std::string(begin, end).c_str());
NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
std::string(begin, end).c_str());
return false;
}
const Level level = GetLevelByName(level_separator + 1, end);
if (level == Level::Count) {
LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
return false;
}
@@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const Class log_class = GetClassByName(begin, level_separator);
if (log_class == Class::Count) {
LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
return false;
}

View File

@@ -19,7 +19,7 @@ namespace Log {
class Filter {
public:
/// Initializes the filter with all classes having `default_level` as the minimum level.
Filter(Level default_level);
Filter(Level default_level = Level::Info);
/// Resets the filter so that all classes have `level` as the minimum displayed level.
void ResetAll(Level level);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <fmt/format.h>
#include "common/common_types.h"
namespace Log {
@@ -51,12 +52,15 @@ enum class Class : ClassType {
/// should have its own subclass.
Service_ACC, ///< The ACC (Accounts) service
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_Audio, ///< The Audio (Audio control) service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_HID, ///< The HID (Human interface device) service
Service_LM, ///< The LM (Logger) service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_PCTL, ///< The PCTL (Parental control) service
Service_SET, ///< The SET (Settings) service
@@ -84,7 +88,7 @@ enum class Class : ClassType {
};
/// Logs a message to the global logger.
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function,
#ifdef _MSC_VER
_Printf_format_string_
@@ -96,6 +100,10 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
#endif
;
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, const fmt::ArgList& args);
FMT_VARIADIC(void, FmtLogMessage, Class, Level, const char*, unsigned int, const char*, const char*)
} // namespace Log
#define LOG_GENERIC(log_class, log_level, ...) \
@@ -118,3 +126,28 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
#define LOG_CRITICAL(log_class, ...) \
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)
// Define the fmt lib macros
#ifdef _DEBUG
#define NGLOG_TRACE(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#else
#define NGLOG_TRACE(log_class, fmt, ...) (void(0))
#endif
#define NGLOG_DEBUG(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_INFO(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_WARNING(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_ERROR(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_CRITICAL(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \
__func__, __VA_ARGS__)

View File

@@ -18,50 +18,29 @@
namespace Log {
// TODO(bunnei): This should be moved to a generic path manipulation library
const char* TrimSourcePath(const char* path, const char* root) {
const char* p = path;
while (*p != '\0') {
const char* next_slash = p;
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
++next_slash;
}
bool is_src = Common::ComparePartialString(p, next_slash, root);
p = next_slash;
if (*p != '\0') {
++p;
}
if (is_src) {
path = p;
}
}
return path;
}
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
std::string FormatLogMessage(const Entry& entry) {
unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
const char* class_name = GetLogClassName(entry.log_class);
const char* level_name = GetLevelName(entry.log_level);
snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s", time_seconds, time_fractional,
class_name, level_name, TrimSourcePath(entry.location.c_str()), entry.message.c_str());
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional,
class_name, level_name, entry.filename, entry.function, entry.line_num,
entry.message);
}
void PrintMessage(const Entry& entry) {
std::array<char, 4 * 1024> format_buffer;
FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
fputs(format_buffer.data(), stderr);
fputc('\n', stderr);
auto str = FormatLogMessage(entry) + '\n';
fputs(str.c_str(), stderr);
}
void PrintColoredMessage(const Entry& entry) {
#ifdef _WIN32
static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
if (console_handle == INVALID_HANDLE_VALUE) {
return;
}
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
GetConsoleScreenBufferInfo(console_handle, &original_info);

View File

@@ -10,20 +10,8 @@ namespace Log {
struct Entry;
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
* leaving only the path relative to the sources root.
*
* @param path The input file path as a null-terminated string
* @param root The name of the root source directory as a null-terminated string. Path up to and
* including the last occurrence of this name will be stripped
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
*/
const char* TrimSourcePath(const char* path, const char* root = "src");
/// Formats a log entry into the provided text buffer.
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
std::string FormatLogMessage(const Entry& entry);
/// Formats and prints a log entry to stderr.
void PrintMessage(const Entry& entry);
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.

View File

@@ -462,4 +462,27 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_l
return std::string(buffer, len);
}
const char* TrimSourcePath(const char* path, const char* root) {
const char* p = path;
while (*p != '\0') {
const char* next_slash = p;
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
++next_slash;
}
bool is_src = Common::ComparePartialString(p, next_slash, root);
p = next_slash;
if (*p != '\0') {
++p;
}
if (is_src) {
path = p;
}
}
return path;
}
} // namespace Common

View File

@@ -134,4 +134,17 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
* NUL-terminated then the string ends at max_len characters.
*/
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len);
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
* leaving only the path relative to the sources root.
*
* @param path The input file path as a null-terminated string
* @param root The name of the root source directory as a null-terminated string. Path up to and
* including the last occurrence of this name will be stripped
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
*/
const char* TrimSourcePath(const char* path, const char* root = "src");
} // namespace Common

View File

@@ -12,6 +12,8 @@ add_library(core STATIC
file_sys/filesystem.h
file_sys/path_parser.cpp
file_sys/path_parser.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/romfs_factory.cpp
file_sys/romfs_factory.h
file_sys/romfs_filesystem.cpp
@@ -28,8 +30,6 @@ add_library(core STATIC
hle/config_mem.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/address_arbiter.cpp
hle/kernel/address_arbiter.h
hle/kernel/client_port.cpp
hle/kernel/client_port.h
hle/kernel/client_session.cpp
@@ -55,6 +55,8 @@ add_library(core STATIC
hle/kernel/process.h
hle/kernel/resource_limit.cpp
hle/kernel/resource_limit.h
hle/kernel/scheduler.cpp
hle/kernel/scheduler.h
hle/kernel/server_port.cpp
hle/kernel/server_port.h
hle/kernel/server_session.cpp
@@ -112,6 +114,10 @@ add_library(core STATIC
hle/service/filesystem/filesystem.h
hle/service/filesystem/fsp_srv.cpp
hle/service/filesystem/fsp_srv.h
hle/service/friend/friend.cpp
hle/service/friend/friend.h
hle/service/friend/friend_a.cpp
hle/service/friend/friend_a.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/lm/lm.cpp
@@ -124,6 +130,10 @@ add_library(core STATIC
hle/service/nifm/nifm_s.h
hle/service/nifm/nifm_u.cpp
hle/service/nifm/nifm_u.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pl_u.cpp
hle/service/ns/pl_u.h
hle/service/nvdrv/devices/nvdevice.h
hle/service/nvdrv/devices/nvdisp_disp0.cpp
hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -139,8 +149,6 @@ add_library(core STATIC
hle/service/nvdrv/devices/nvmap.h
hle/service/nvdrv/interface.cpp
hle/service/nvdrv/interface.h
hle/service/nvdrv/memory_manager.cpp
hle/service/nvdrv/memory_manager.h
hle/service/nvdrv/nvdrv.cpp
hle/service/nvdrv/nvdrv.h
hle/service/nvdrv/nvmemp.cpp

View File

@@ -6,6 +6,7 @@
#include <memory>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/config.h>
#include "common/logging/log.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/core_timing.h"
#include "core/hle/kernel/memory.h"
@@ -53,6 +54,9 @@ public:
}
void InterpreterFallback(u64 pc, size_t num_instructions) override {
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x%" PRIx64 " for %zu instructions (instr = %08x)",
pc, num_instructions, MemoryReadCode(pc));
ARM_Interface::ThreadContext ctx;
parent.SaveContext(ctx);
parent.inner_unicorn.LoadContext(ctx);
@@ -63,8 +67,17 @@ public:
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
ASSERT_MSG(false, "ExceptionRaised(exception = %zu, pc = %" PRIx64 ")",
static_cast<size_t>(exception), pc);
switch (exception) {
case Dynarmic::A64::Exception::WaitForInterrupt:
case Dynarmic::A64::Exception::WaitForEvent:
case Dynarmic::A64::Exception::SendEvent:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
default:
ASSERT_MSG(false, "ExceptionRaised(exception = %zu, pc = %" PRIx64 ")",
static_cast<size_t>(exception), pc);
}
}
void CallSVC(u32 swi) override {
@@ -81,15 +94,30 @@ public:
u64 GetTicksRemaining() override {
return ticks_remaining;
}
u64 GetCNTPCT() override {
return CoreTiming::GetTicks();
}
ARM_Dynarmic& parent;
size_t ticks_remaining = 0;
size_t num_interpreted_instructions = 0;
u64 tpidrr0_el0 = 0;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
Dynarmic::A64::UserConfig config{cb.get()};
const auto page_table = Kernel::g_current_process->vm_manager.page_table.pointers.data();
Dynarmic::A64::UserConfig config;
config.callbacks = cb.get();
config.tpidrro_el0 = &cb->tpidrro_el0;
config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.silently_mirror_page_table = false;
return std::make_unique<Dynarmic::A64::Jit>(config);
}
@@ -149,11 +177,11 @@ void ARM_Dynarmic::SetCPSR(u32 cpsr) {
}
u64 ARM_Dynarmic::GetTlsAddress() const {
return cb->tpidrr0_el0;
return cb->tpidrro_el0;
}
void ARM_Dynarmic::SetTlsAddress(u64 address) {
cb->tpidrr0_el0 = address;
cb->tpidrro_el0 = address;
}
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
@@ -170,7 +198,7 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.cpsr = jit->GetPstate();
ctx.fpu_registers = jit->GetVectors();
ctx.fpscr = jit->GetFpcr();
ctx.tls_address = cb->tpidrr0_el0;
ctx.tls_address = cb->tpidrro_el0;
}
void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
@@ -180,7 +208,7 @@ void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
jit->SetPstate(static_cast<u32>(ctx.cpsr));
jit->SetVectors(ctx.fpu_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
cb->tpidrr0_el0 = ctx.tls_address;
cb->tpidrro_el0 = ctx.tls_address;
}
void ARM_Dynarmic::PrepareReschedule() {

View File

@@ -52,7 +52,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
void* user_data) {
ARM_Interface::ThreadContext ctx{};
Core::CPU().SaveContext(ctx);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx", addr);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx, pc=0x%llx, lr=0x%llx", addr,
ctx.pc, ctx.cpu_registers[30]);
return {};
}

View File

@@ -133,32 +133,36 @@ void System::Reschedule() {
}
reschedule_pending = false;
Kernel::Reschedule();
Core::System::GetInstance().Scheduler().Reschedule();
}
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
switch (Settings::values.cpu_core) {
case Settings::CpuCore::Unicorn:
cpu_core = std::make_unique<ARM_Unicorn>();
cpu_core = std::make_shared<ARM_Unicorn>();
break;
case Settings::CpuCore::Dynarmic:
default:
#ifdef ARCHITECTURE_x86_64
cpu_core = std::make_unique<ARM_Dynarmic>();
cpu_core = std::make_shared<ARM_Dynarmic>();
#else
cpu_core = std::make_unique<ARM_Unicorn>();
cpu_core = std::make_shared<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
break;
}
gpu_core = std::make_unique<Tegra::GPU>();
telemetry_session = std::make_unique<Core::TelemetrySession>();
CoreTiming::Init();
HW::Init();
Kernel::Init(system_mode);
scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get());
Service::Init();
GDBStub::Init();
@@ -186,15 +190,18 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
GDBStub::Shutdown();
VideoCore::Shutdown();
GDBStub::Shutdown();
Service::Shutdown();
scheduler = nullptr;
Kernel::Shutdown();
HW::Shutdown();
CoreTiming::Shutdown();
cpu_core = nullptr;
app_loader = nullptr;
telemetry_session = nullptr;
gpu_core = nullptr;
cpu_core = nullptr;
CoreTiming::Shutdown();
app_loader = nullptr;
LOG_DEBUG(Core, "Shutdown OK");
}

View File

@@ -7,10 +7,12 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "video_core/gpu.h"
class EmuWindow;
class ARM_Interface;
@@ -102,6 +104,14 @@ public:
return *cpu_core;
}
Tegra::GPU& GPU() {
return *gpu_core;
}
Kernel::Scheduler& Scheduler() {
return *scheduler;
}
PerfStats perf_stats;
FrameLimiter frame_limiter;
@@ -135,8 +145,9 @@ private:
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
///< ARM11 CPU core
std::unique_ptr<ARM_Interface> cpu_core;
std::shared_ptr<ARM_Interface> cpu_core;
std::unique_ptr<Kernel::Scheduler> scheduler;
std::unique_ptr<Tegra::GPU> gpu_core;
/// When true, signals that a reschedule should happen
bool reschedule_pending{};

View File

@@ -0,0 +1,114 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/program_metadata.h"
#include "core/loader/loader.h"
namespace FileSys {
Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) {
FileUtil::IOFile file(file_path, "rb");
if (!file.IsOpen())
return Loader::ResultStatus::Error;
std::vector<u8> file_data(file.GetSize());
if (!file.ReadBytes(file_data.data(), file_data.size()))
return Loader::ResultStatus::Error;
Loader::ResultStatus result = Load(file_data);
if (result != Loader::ResultStatus::Success)
LOG_ERROR(Service_FS, "Failed to load NPDM from file %s!", file_path.c_str());
return result;
}
Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) {
size_t total_size = static_cast<size_t>(file_data.size() - offset);
if (total_size < sizeof(Header))
return Loader::ResultStatus::Error;
size_t header_offset = offset;
memcpy(&npdm_header, &file_data[offset], sizeof(Header));
size_t aci_offset = header_offset + npdm_header.aci_offset;
size_t acid_offset = header_offset + npdm_header.acid_offset;
memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader));
memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader));
size_t fac_offset = acid_offset + acid_header.fac_offset;
size_t fah_offset = aci_offset + aci_header.fah_offset;
memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl));
memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader));
return Loader::ResultStatus::Success;
}
bool ProgramMetadata::Is64BitProgram() const {
return npdm_header.has_64_bit_instructions;
}
ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const {
return npdm_header.address_space_type;
}
u8 ProgramMetadata::GetMainThreadPriority() const {
return npdm_header.main_thread_priority;
}
u8 ProgramMetadata::GetMainThreadCore() const {
return npdm_header.main_thread_cpu;
}
u32 ProgramMetadata::GetMainThreadStackSize() const {
return npdm_header.main_stack_size;
}
u64 ProgramMetadata::GetTitleID() const {
return aci_header.title_id;
}
u64 ProgramMetadata::GetFilesystemPermissions() const {
return aci_file_access.permissions;
}
void ProgramMetadata::Print() const {
LOG_DEBUG(Service_FS, "Magic: %.4s", npdm_header.magic.data());
LOG_DEBUG(Service_FS, "Main thread priority: 0x%02x", npdm_header.main_thread_priority);
LOG_DEBUG(Service_FS, "Main thread core: %u", npdm_header.main_thread_cpu);
LOG_DEBUG(Service_FS, "Main thread stack size: 0x%x bytes", npdm_header.main_stack_size);
LOG_DEBUG(Service_FS, "Process category: %u", npdm_header.process_category);
LOG_DEBUG(Service_FS, "Flags: %02x", npdm_header.flags);
LOG_DEBUG(Service_FS, " > 64-bit instructions: %s",
npdm_header.has_64_bit_instructions ? "YES" : "NO");
auto address_space = "Unknown";
switch (npdm_header.address_space_type) {
case ProgramAddressSpaceType::Is64Bit:
address_space = "64-bit";
break;
case ProgramAddressSpaceType::Is32Bit:
address_space = "32-bit";
break;
}
LOG_DEBUG(Service_FS, " > Address space: %s\n", address_space);
// Begin ACID printing (potential perms, signed)
LOG_DEBUG(Service_FS, "Magic: %.4s", acid_header.magic.data());
LOG_DEBUG(Service_FS, "Flags: %02x", acid_header.flags);
LOG_DEBUG(Service_FS, " > Is Retail: %s", acid_header.is_retail ? "YES" : "NO");
LOG_DEBUG(Service_FS, "Title ID Min: %016" PRIX64, acid_header.title_id_min);
LOG_DEBUG(Service_FS, "Title ID Max: %016" PRIX64, acid_header.title_id_max);
LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", acid_file_access.permissions);
// Begin ACI0 printing (actual perms, unsigned)
LOG_DEBUG(Service_FS, "Magic: %.4s", aci_header.magic.data());
LOG_DEBUG(Service_FS, "Title ID: %016" PRIX64, aci_header.title_id);
LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", aci_file_access.permissions);
}
} // namespace FileSys

View File

@@ -0,0 +1,154 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
namespace Loader {
enum class ResultStatus;
}
namespace FileSys {
enum class ProgramAddressSpaceType : u8 {
Is64Bit = 1,
Is32Bit = 2,
};
enum class ProgramFilePermission : u64 {
MountContent = 1ULL << 0,
SaveDataBackup = 1ULL << 5,
SdCard = 1ULL << 21,
Calibration = 1ULL << 34,
Bit62 = 1ULL << 62,
Everything = 1ULL << 63,
};
/**
* Helper which implements an interface to parse Program Description Metadata (NPDM)
* Data can either be loaded from a file path or with data and an offset into it.
*/
class ProgramMetadata {
public:
Loader::ResultStatus Load(const std::string& file_path);
Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0);
bool Is64BitProgram() const;
ProgramAddressSpaceType GetAddressSpaceType() const;
u8 GetMainThreadPriority() const;
u8 GetMainThreadCore() const;
u32 GetMainThreadStackSize() const;
u64 GetTitleID() const;
u64 GetFilesystemPermissions() const;
void Print() const;
private:
struct Header {
std::array<char, 4> magic;
std::array<u8, 8> reserved;
union {
u8 flags;
BitField<0, 1, u8> has_64_bit_instructions;
BitField<1, 3, ProgramAddressSpaceType> address_space_type;
BitField<4, 4, u8> reserved_2;
};
u8 reserved_3;
u8 main_thread_priority;
u8 main_thread_cpu;
std::array<u8, 8> reserved_4;
u32_le process_category;
u32_le main_stack_size;
std::array<u8, 0x10> application_name;
std::array<u8, 0x40> reserved_5;
u32_le aci_offset;
u32_le aci_size;
u32_le acid_offset;
u32_le acid_size;
};
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
struct AcidHeader {
std::array<u8, 0x100> signature;
std::array<u8, 0x100> nca_modulus;
std::array<char, 4> magic;
u32_le nca_size;
std::array<u8, 0x4> reserved;
union {
u32 flags;
BitField<0, 1, u32> is_retail;
BitField<1, 31, u32> flags_unk;
};
u64_le title_id_min;
u64_le title_id_max;
u32_le fac_offset;
u32_le fac_size;
u32_le sac_offset;
u32_le sac_size;
u32_le kac_offset;
u32_le kac_size;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(AcidHeader) == 0x240, "ACID header structure size is wrong");
struct AciHeader {
std::array<char, 4> magic;
std::array<u8, 0xC> reserved;
u64_le title_id;
INSERT_PADDING_BYTES(0x8);
u32_le fah_offset;
u32_le fah_size;
u32_le sac_offset;
u32_le sac_size;
u32_le kac_offset;
u32_le kac_size;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong");
#pragma pack(push, 1)
struct FileAccessControl {
u8 version;
INSERT_PADDING_BYTES(3);
u64_le permissions;
std::array<u8, 0x20> unknown;
};
static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong");
struct FileAccessHeader {
u8 version;
INSERT_PADDING_BYTES(3);
u64_le permissions;
u32_le unk_offset;
u32_le unk_size;
u32_le unk_offset_2;
u32_le unk_size_2;
};
static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong");
#pragma pack(pop)
Header npdm_header;
AciHeader aci_header;
AcidHeader acid_header;
FileAccessControl acid_file_access;
FileAccessHeader aci_file_access;
};
} // namespace FileSys

View File

@@ -6,6 +6,7 @@
#include <algorithm>
#include <atomic>
#include <cinttypes>
#include <climits>
#include <csignal>
#include <cstdarg>
@@ -360,8 +361,9 @@ static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n",
bp->second.len, bp->second.addr, type);
LOG_DEBUG(Debug_GDBStub,
"gdb: removed a breakpoint: %016" PRIx64 " bytes at %016" PRIx64 " of type %d\n",
bp->second.len, bp->second.addr, static_cast<int>(type));
p.erase(static_cast<u64>(addr));
}
}
@@ -407,8 +409,9 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) {
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type,
addr, bp->second.addr, bp->second.addr + len, len);
"Found breakpoint type %d @ %016" PRIx64 ", range: %016" PRIx64
" - %016" PRIx64 " (%" PRIx64 " bytes)\n",
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true;
}
}
@@ -778,8 +781,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) {
breakpoint.len = len;
p.insert({addr, breakpoint});
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len,
breakpoint.addr);
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %016" PRIx64 " bytes at %016" PRIx64 "\n",
static_cast<int>(type), breakpoint.len, breakpoint.addr);
return true;
}

View File

@@ -91,6 +91,10 @@ struct BufferDescriptorX {
address |= static_cast<VAddr>(address_bits_36_38) << 36;
return address;
}
u64 Size() const {
return static_cast<u64>(size);
}
};
static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect");

View File

@@ -1,91 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
namespace Kernel {
AddressArbiter::AddressArbiter() {}
AddressArbiter::~AddressArbiter() {}
SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) {
SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter);
address_arbiter->name = std::move(name);
return address_arbiter;
}
ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value,
u64 nanoseconds) {
switch (type) {
// Signal thread(s) waiting for arbitrate address...
case ArbitrationType::Signal:
// Negative value means resume all threads
if (value < 0) {
ArbitrateAllThreads(address);
} else {
// Resume first N threads
for (int i = 0; i < value; i++)
ArbitrateHighestPriorityThread(address);
}
break;
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) < value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
}
break;
case ArbitrationType::WaitIfLessThanWithTimeout:
if ((s32)Memory::Read32(address) < value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
break;
case ArbitrationType::DecrementAndWaitIfLessThan: {
s32 memory_value = Memory::Read32(address);
if (memory_value < value) {
// Only change the memory value if the thread should wait
Memory::Write32(address, (s32)memory_value - 1);
Kernel::WaitCurrentThread_ArbitrateAddress(address);
}
break;
}
case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: {
s32 memory_value = Memory::Read32(address);
if (memory_value < value) {
// Only change the memory value if the thread should wait
Memory::Write32(address, (s32)memory_value - 1);
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
break;
}
default:
LOG_ERROR(Kernel, "unknown type=%d", type);
return ERR_INVALID_ENUM_VALUE_FND;
}
// The calls that use a timeout seem to always return a Timeout error even if they did not put
// the thread to sleep
if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
return RESULT_TIMEOUT;
}
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -1,60 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
// Address arbiters are an underlying kernel synchronization object that can be created/used via
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
// semphores.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
namespace Kernel {
enum class ArbitrationType : u32 {
Signal,
WaitIfLessThan,
DecrementAndWaitIfLessThan,
WaitIfLessThanWithTimeout,
DecrementAndWaitIfLessThanWithTimeout,
};
class AddressArbiter final : public Object {
public:
/**
* Creates an address arbiter.
*
* @param name Optional name used for debugging.
* @returns The created AddressArbiter.
*/
static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
std::string GetTypeName() const override {
return "Arbiter";
}
std::string GetName() const override {
return name;
}
static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
std::string name; ///< Name of address arbiter object (optional)
ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds);
private:
AddressArbiter();
~AddressArbiter() override;
};
} // namespace Kernel

View File

@@ -12,6 +12,7 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/memory.h"
namespace Kernel {
@@ -210,4 +211,98 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
return RESULT_SUCCESS;
}
std::vector<u8> HLERequestContext::ReadBuffer() const {
std::vector<u8> buffer;
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
if (is_buffer_a) {
buffer.resize(BufferDescriptorA()[0].Size());
Memory::ReadBlock(BufferDescriptorA()[0].Address(), buffer.data(), buffer.size());
} else {
buffer.resize(BufferDescriptorX()[0].Size());
Memory::ReadBlock(BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
}
return buffer;
}
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %d is too big", size);
if (is_buffer_b) {
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);
} else {
Memory::WriteBlock(BufferDescriptorC()[0].Address(), buffer, size);
}
return size;
}
size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer) const {
return WriteBuffer(buffer.data(), buffer.size());
}
size_t HLERequestContext::GetReadBufferSize() const {
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
return is_buffer_a ? BufferDescriptorA()[0].Size() : BufferDescriptorX()[0].Size();
}
size_t HLERequestContext::GetWriteBufferSize() const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size();
}
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";
}
std::ostringstream s;
s << "IPC::CommandHeader: Type:" << static_cast<u32>(command_header->type.Value());
s << ", X(Pointer):" << command_header->num_buf_x_descriptors;
if (command_header->num_buf_x_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorX()[i].Size();
if (i < command_header->num_buf_x_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", A(Send):" << command_header->num_buf_a_descriptors;
if (command_header->num_buf_a_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_a_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorA()[i].Size();
if (i < command_header->num_buf_a_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", B(Receive):" << command_header->num_buf_b_descriptors;
if (command_header->num_buf_b_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_b_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorB()[i].Size();
if (i < command_header->num_buf_b_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", C(ReceiveList):" << BufferDescriptorC().size();
if (!BufferDescriptorC().empty()) {
s << '[';
for (u64 i = 0; i < BufferDescriptorC().size(); ++i) {
s << "0x" << std::hex << BufferDescriptorC()[i].Size();
if (i < BufferDescriptorC().size() - 1)
s << ", ";
}
s << ']';
}
s << ", data_size:" << command_header->data_size.Value();
return s.str();
}
} // namespace Kernel

View File

@@ -143,6 +143,21 @@ public:
return domain_message_header;
}
/// Helper function to read a buffer using the appropriate buffer descriptor
std::vector<u8> ReadBuffer() const;
/// Helper function to write a buffer using the appropriate buffer descriptor
size_t WriteBuffer(const void* buffer, size_t size) const;
/// Helper function to write a buffer using the appropriate buffer descriptor
size_t WriteBuffer(const std::vector<u8>& buffer) const;
/// Helper function to get the size of the input buffer
size_t GetReadBufferSize() const;
/// Helper function to get the size of the output buffer
size_t GetWriteBufferSize() const;
template <typename T>
SharedPtr<T> GetCopyObject(size_t index) {
ASSERT(index < copy_objects.size());
@@ -187,6 +202,8 @@ public:
return domain_objects.size();
}
std::string Description() const;
private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::ServerSession> server_session;

View File

@@ -0,0 +1,134 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
namespace Kernel {
Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
thread->Stop();
}
}
bool Scheduler::HaveReadyThreads() {
return ready_queue.get_first() != nullptr;
}
Thread* Scheduler::GetCurrentThread() const {
return current_thread.get();
}
Thread* Scheduler::PopNextReadyThread() {
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
if (thread && thread->status == THREADSTATUS_RUNNING) {
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.pop_first_better(thread->current_priority);
if (!next) {
// Otherwise just keep going with the current thread
next = thread;
}
} else {
next = ready_queue.pop_first();
}
return next;
}
void Scheduler::SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread();
// Save context for previous thread
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
cpu_core->SaveContext(previous_thread->context);
if (previous_thread->status == THREADSTATUS_RUNNING) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.push_front(previous_thread->current_priority, previous_thread);
previous_thread->status = THREADSTATUS_READY;
}
}
// Load context of new thread
if (new_thread) {
ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
"Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
new_thread->CancelWakeupTimer();
auto previous_process = Kernel::g_current_process;
current_thread = new_thread;
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = THREADSTATUS_RUNNING;
if (previous_process != current_thread->owner_process) {
Kernel::g_current_process = current_thread->owner_process;
SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
}
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
// technically we haven't changed processes, our threads are just paused.
}
}
void Scheduler::Reschedule() {
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
if (cur && next) {
LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
} else if (cur) {
LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
} else if (next) {
LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
}
SwitchContext(next);
}
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
thread_list.push_back(thread);
ready_queue.prepare(priority);
}
void Scheduler::RemoveThread(Thread* thread) {
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
ASSERT(thread->status == THREADSTATUS_READY);
ready_queue.push_back(priority, thread);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
ASSERT(thread->status == THREADSTATUS_READY);
ready_queue.remove(priority, thread);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
// If thread was ready, adjust queues
if (thread->status == THREADSTATUS_READY)
ready_queue.move(thread, thread->current_priority, priority);
else
ready_queue.prepare(priority);
}
} // namespace Kernel

View File

@@ -0,0 +1,73 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
class Scheduler final {
public:
explicit Scheduler(ARM_Interface* cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
bool HaveReadyThreads();
/// Reschedules to the next available thread (call after current thread is suspended)
void Reschedule();
/// Gets the current running thread
Thread* GetCurrentThread() const;
/// Adds a new thread to the scheduler
void AddThread(SharedPtr<Thread> thread, u32 priority);
/// Removes a thread from the scheduler
void RemoveThread(Thread* thread);
/// Schedules a thread that has become "ready"
void ScheduleThread(Thread* thread, u32 priority);
/// Unschedules a thread that was already scheduled
void UnscheduleThread(Thread* thread, u32 priority);
/// Sets the priority of a thread in the scheduler
void SetThreadPriority(Thread* thread, u32 priority);
/// Returns a list of all threads managed by the scheduler
const std::vector<SharedPtr<Thread>>& GetThreadList() const {
return thread_list;
}
private:
/**
* Pops and returns the next thread from the thread queue
* @return A pointer to the next ready thread
*/
Thread* PopNextReadyThread();
/**
* Switches the CPU's active thread context to that of the specified thread
* @param new_thread The thread to switch to
*/
void SwitchContext(Thread* new_thread);
/// Lists all thread ids that aren't deleted/etc.
std::vector<SharedPtr<Thread>> thread_list;
/// Lists only ready thread ids.
Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
SharedPtr<Thread> current_thread = nullptr;
ARM_Interface* cpu_core;
};
} // namespace Kernel

View File

@@ -57,6 +57,33 @@ void ServerSession::Acquire(Thread* thread) {
pending_requesting_threads.pop_back();
}
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
auto& domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// If there is a DomainMessageHeader, then this is CommandType "Request"
const u32 object_id{context.GetDomainMessageHeader()->object_id};
switch (domain_message_header->command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
domain_request_handlers[object_id - 1] = nullptr;
IPC::ResponseBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
return RESULT_SUCCESS;
}
}
LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
ASSERT(false);
}
return RESULT_SUCCESS;
}
ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// The ServerSession received a sync request, this means that there's new data available
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
@@ -67,46 +94,39 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
// If the session has been converted to a domain, handle the doomain request
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
if (IsDomain()) {
auto& domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// If there is a DomainMessageHeader, then this is CommandType "Request"
const u32 object_id{context.GetDomainMessageHeader()->object_id};
switch (domain_message_header->command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
domain_request_handlers[object_id - 1] = nullptr;
IPC::ResponseBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
return RESULT_SUCCESS;
}
}
LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
ASSERT(false);
}
result = HandleDomainSyncRequest(context);
// If there is no domain header, the regular session handler is used
} else if (hle_handler != nullptr) {
// If this ServerSession has an associated HLE handler, forward the request to it.
result = hle_handler->HandleSyncRequest(context);
}
// If this ServerSession has an associated HLE handler, forward the request to it.
ResultCode result{RESULT_SUCCESS};
if (hle_handler != nullptr) {
// Attempt to translate the incoming request's command buffer.
ResultCode translate_result = TranslateHLERequest(this);
if (translate_result.IsError())
return translate_result;
if (thread->status == THREADSTATUS_RUNNING) {
// Put the thread to sleep until the server replies, it will be awoken in
// svcReplyAndReceive for LLE servers.
thread->status = THREADSTATUS_WAIT_IPC;
result = hle_handler->HandleSyncRequest(context);
} else {
// Add the thread to the list of threads that have issued a sync request with this
// server.
pending_requesting_threads.push_back(std::move(thread));
if (hle_handler != nullptr) {
// For HLE services, we put the request threads to sleep for a short duration to
// simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
// other reasons like an async callback. The IPC overhead is needed to prevent
// starvation when a thread only does sync requests to HLE services while a
// lower-priority thread is waiting to run.
// This delay was approximated in a homebrew application by measuring the average time
// it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
// request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
// a high variance and vary between models.
static constexpr u64 IPCDelayNanoseconds = 39000;
thread->WakeAfterDelay(IPCDelayNanoseconds);
} else {
// Add the thread to the list of threads that have issued a sync request with this
// server.
pending_requesting_threads.push_back(std::move(thread));
}
}
// If this ServerSession does not have an HLE implementation, just wake up the threads waiting
@@ -140,9 +160,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n
return std::make_tuple(std::move(server_session), std::move(client_session));
}
ResultCode TranslateHLERequest(ServerSession* server_session) {
// TODO(Subv): Implement this function once multiple concurrent processes are supported.
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -21,6 +21,7 @@ class ServerSession;
class Session;
class SessionRequestHandler;
class Thread;
class HLERequestContext;
/**
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -116,17 +117,12 @@ private:
*/
static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown");
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
/// object handle.
ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
/// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{};
};
/**
* Performs command buffer translation for an HLE IPC request.
* The command buffer from the ServerSession thread's TLS is copied into a
* buffer and all descriptors in the buffer are processed.
* TODO(Subv): Implement this function, currently we do not support multiple processes running at
* once, but once that is implemented we'll need to properly translate all descriptors
* in the command buffer.
*/
ResultCode TranslateHLERequest(ServerSession* server_session);
} // namespace Kernel

View File

@@ -111,13 +111,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return ERR_INVALID_COMBINATION;
}
// Heap-backed memory blocks can not be mapped with other_permissions = DontCare
if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
return ERR_INVALID_COMBINATION;
}
// Error out if the provided permissions are not compatible with what the creator process needs.
if (other_permissions != MemoryPermission::DontCare &&
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
@@ -126,12 +119,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return ERR_WRONG_PERMISSION;
}
// TODO(Subv): Check for the Shared Device Mem flag in the creator process.
/*if (was_created_with_shared_device_mem && address != 0) {
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}*/
// TODO(Subv): The same process that created a SharedMemory object
// can not map it in its own address space unless it was created with addr=0, result 0xD900182C.

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -443,6 +444,16 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return RESULT_SUCCESS;
}
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
LOG_WARNING(Kernel_SVC,
"called, shared_memory_handle=0x%08X, addr=0x%" PRIx64 ", size=0x%" PRIx64 "",
shared_memory_handle, addr, size);
SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
return shared_memory->Unmap(g_current_process.get(), addr);
}
/// Query process memory
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
Handle process_handle, u64 addr) {
@@ -483,7 +494,7 @@ static void ExitProcess() {
g_current_process->status = ProcessStatus::Exited;
// Stop all the process threads that are currently waiting for objects.
auto& thread_list = GetThreadList();
auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
for (auto& thread : thread_list) {
if (thread->owner_process != g_current_process)
continue;
@@ -585,7 +596,7 @@ static void SleepThread(s64 nanoseconds) {
// Don't attempt to yield execution if there are no available threads to run,
// this way we avoid a useless reschedule to the idle thread.
if (nanoseconds == 0 && !HaveReadyThreads())
if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads())
return;
// Sleep current thread and check for next thread to schedule
@@ -761,6 +772,16 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
return RESULT_SUCCESS;
}
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0xX", handle);
SharedPtr<Event> evt = g_handle_table.Get<Event>(handle);
if (evt == nullptr)
return ERR_INVALID_HANDLE;
evt->Clear();
return RESULT_SUCCESS;
}
namespace {
struct FunctionDef {
using Func = void();
@@ -790,9 +811,9 @@ static const FunctionDef SVC_Table[] = {
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
{0x11, nullptr, "SignalEvent"},
{0x12, nullptr, "ClearEvent"},
{0x12, SvcWrap<ClearEvent>, "ClearEvent"},
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
{0x14, nullptr, "UnmapSharedMemory"},
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
{0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"},
{0x16, SvcWrap<CloseHandle>, "CloseHandle"},
{0x17, SvcWrap<ResetSignal>, "ResetSignal"},

View File

@@ -91,6 +91,11 @@ void SvcWrap() {
FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw);
}
template <ResultCode func(u32, u64, u64)>
void SvcWrap() {
FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw);
}
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
u32 param_1 = 0;

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <list>
#include <vector>
#include "common/assert.h"
@@ -40,14 +41,6 @@ void Thread::Acquire(Thread* thread) {
// us to simply use a pool index or similar.
static Kernel::HandleTable wakeup_callback_handle_table;
// Lists all thread ids that aren't deleted/etc.
static std::vector<SharedPtr<Thread>> thread_list;
// Lists only ready thread ids.
static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
static SharedPtr<Thread> current_thread;
// The first available thread id at startup
static u32 next_thread_id;
@@ -62,10 +55,6 @@ inline static u32 const NewThreadId() {
Thread::Thread() {}
Thread::~Thread() {}
Thread* GetCurrentThread() {
return current_thread.get();
}
/**
* Check if the specified thread is waiting on the specified address to be arbitrated
* @param thread The thread to test
@@ -85,7 +74,7 @@ void Thread::Stop() {
// Clean up thread from ready queue
// This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
if (status == THREADSTATUS_READY) {
ready_queue.remove(current_priority, this);
Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority);
}
status = THREADSTATUS_DEAD;
@@ -108,112 +97,6 @@ void Thread::Stop() {
Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
}
Thread* ArbitrateHighestPriorityThread(u32 address) {
Thread* highest_priority_thread = nullptr;
u32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
for (auto& thread : thread_list) {
if (!CheckWait_AddressArbiter(thread.get(), address))
continue;
if (thread == nullptr)
continue;
if (thread->current_priority <= priority) {
highest_priority_thread = thread.get();
priority = thread->current_priority;
}
}
// If a thread was arbitrated, resume it
if (nullptr != highest_priority_thread) {
highest_priority_thread->ResumeFromWait();
}
return highest_priority_thread;
}
void ArbitrateAllThreads(u32 address) {
// Resume all threads found to be waiting on the address
for (auto& thread : thread_list) {
if (CheckWait_AddressArbiter(thread.get(), address))
thread->ResumeFromWait();
}
}
/**
* Switches the CPU's active thread context to that of the specified thread
* @param new_thread The thread to switch to
*/
static void SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread();
// Save context for previous thread
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
Core::CPU().SaveContext(previous_thread->context);
if (previous_thread->status == THREADSTATUS_RUNNING) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.push_front(previous_thread->current_priority, previous_thread);
previous_thread->status = THREADSTATUS_READY;
}
}
// Load context of new thread
if (new_thread) {
ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
"Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
auto previous_process = Kernel::g_current_process;
current_thread = new_thread;
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = THREADSTATUS_RUNNING;
if (previous_process != current_thread->owner_process) {
Kernel::g_current_process = current_thread->owner_process;
SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
}
Core::CPU().LoadContext(new_thread->context);
Core::CPU().SetTlsAddress(new_thread->GetTLSAddress());
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
// technically we haven't changed processes, our threads are just paused.
}
}
/**
* Pops and returns the next thread from the thread queue
* @return A pointer to the next ready thread
*/
static Thread* PopNextReadyThread() {
Thread* next;
Thread* thread = GetCurrentThread();
if (thread && thread->status == THREADSTATUS_RUNNING) {
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.pop_first_better(thread->current_priority);
if (!next) {
// Otherwise just keep going with the current thread
next = thread;
}
} else {
next = ready_queue.pop_first();
}
return next;
}
void WaitCurrentThread_Sleep() {
Thread* thread = GetCurrentThread();
thread->status = THREADSTATUS_WAIT_SLEEP;
@@ -228,8 +111,7 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
void ExitCurrentThread() {
Thread* thread = GetCurrentThread();
thread->Stop();
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
Core::System::GetInstance().Scheduler().RemoveThread(thread);
}
/**
@@ -283,6 +165,7 @@ void Thread::ResumeFromWait() {
case THREADSTATUS_WAIT_SYNCH_ANY:
case THREADSTATUS_WAIT_ARB:
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
break;
case THREADSTATUS_READY:
@@ -306,31 +189,11 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
ready_queue.push_back(current_priority, this);
status = THREADSTATUS_READY;
Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority);
Core::System::GetInstance().PrepareReschedule();
}
/**
* Prints the thread queue for debugging purposes
*/
static void DebugThreadQueue() {
Thread* thread = GetCurrentThread();
if (!thread) {
LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD");
} else {
LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority,
GetCurrentThread()->GetObjectId());
}
for (auto& t : thread_list) {
u32 priority = ready_queue.contains(t.get());
if (priority != -1) {
LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
}
}
}
/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
@@ -379,7 +242,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
SharedPtr<Process> owner_process) {
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority);
LOG_ERROR(Kernel_SVC, "Invalid thread priority: %u", priority);
return ERR_OUT_OF_RANGE;
}
@@ -391,15 +254,14 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
// TODO(yuriks): Other checks, returning 0xD9001BEA
if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %016" PRIx64, name.c_str(), entry_point);
// TODO (bunnei): Find the correct error code to use here
return ResultCode(-1);
}
SharedPtr<Thread> thread(new Thread);
thread_list.push_back(thread);
ready_queue.prepare(priority);
Core::System::GetInstance().Scheduler().AddThread(thread, priority);
thread->thread_id = NewThreadId();
thread->status = THREADSTATUS_DORMANT;
@@ -470,12 +332,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
void Thread::SetPriority(u32 priority) {
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value.");
// If thread was ready, adjust queues
if (status == THREADSTATUS_READY)
ready_queue.move(this, current_priority, priority);
else
ready_queue.prepare(priority);
Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
nominal_priority = current_priority = priority;
}
@@ -489,11 +346,7 @@ void Thread::UpdatePriority() {
}
void Thread::BoostPriority(u32 priority) {
// If thread was ready, adjust queues
if (status == THREADSTATUS_READY)
ready_queue.move(this, current_priority, priority);
else
ready_queue.prepare(priority);
Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
current_priority = priority;
}
@@ -519,25 +372,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
return thread;
}
bool HaveReadyThreads() {
return ready_queue.get_first() != nullptr;
}
void Reschedule() {
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
if (cur && next) {
LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
} else if (cur) {
LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
} else if (next) {
LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
}
SwitchContext(next);
}
void Thread::SetWaitSynchronizationResult(ResultCode result) {
context.cpu_registers[0] = result.raw;
}
@@ -560,25 +394,18 @@ VAddr Thread::GetCommandBufferAddress() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the current thread
*/
Thread* GetCurrentThread() {
return Core::System::GetInstance().Scheduler().GetCurrentThread();
}
void ThreadingInit() {
ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
current_thread = nullptr;
next_thread_id = 1;
}
void ThreadingShutdown() {
current_thread = nullptr;
for (auto& t : thread_list) {
t->Stop();
}
thread_list.clear();
ready_queue.clear();
}
const std::vector<SharedPtr<Thread>>& GetThreadList() {
return thread_list;
}
void ThreadingShutdown() {}
} // namespace Kernel

View File

@@ -40,6 +40,7 @@ enum ThreadStatus {
THREADSTATUS_READY, ///< Ready to run
THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
THREADSTATUS_DORMANT, ///< Created but not yet made ready
@@ -248,28 +249,6 @@ private:
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process);
/**
* Returns whether there are any threads that are ready to run.
*/
bool HaveReadyThreads();
/**
* Reschedules to the next available thread (call after current thread is suspended)
*/
void Reschedule();
/**
* Arbitrate the highest priority thread that is waiting
* @param address The address for which waiting threads should be arbitrated
*/
Thread* ArbitrateHighestPriorityThread(VAddr address);
/**
* Arbitrate all threads currently waiting.
* @param address The address for which waiting threads should be arbitrated
*/
void ArbitrateAllThreads(VAddr address);
/**
* Gets the current thread
*/
@@ -301,9 +280,4 @@ void ThreadingInit();
*/
void ThreadingShutdown();
/**
* Get a const reference to the thread list for debug use
*/
const std::vector<SharedPtr<Thread>>& GetThreadList();
} // namespace Kernel

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <iterator>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -206,7 +207,8 @@ void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
void VMManager::LogLayout(Log::Level log_level) const {
for (const auto& p : vma_map) {
const VirtualMemoryArea& vma = p.second;
LOG_GENERIC(Log::Class::Kernel, log_level, "%08X - %08X size: %8X %c%c%c %s", vma.base,
LOG_GENERIC(Log::Class::Kernel, log_level,
"%016" PRIx64 " - %016" PRIx64 " size: %16" PRIx64 " %c%c%c %s", vma.base,
vma.base + vma.size, vma.size,
(u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-',
(u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-',
@@ -222,8 +224,8 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle& iter) {
}
ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) {
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size);
ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base);
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%16" PRIx64, size);
ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%016" PRIx64, base);
VMAIter vma_handle = StripIterConstness(FindVMA(base));
if (vma_handle == vma_map.end()) {
@@ -258,8 +260,8 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) {
}
ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) {
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size);
ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target);
ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%16" PRIx64, size);
ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%016" PRIx64, target);
VAddr target_end = target + size;
ASSERT(target_end >= target);

View File

@@ -65,12 +65,19 @@ void ACC_U0::GetUserExistence(Kernel::HLERequestContext& ctx) {
}
void ACC_U0::ListAllUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
const auto& output_buffer = ctx.BufferDescriptorC()[0];
Memory::WriteBlock(output_buffer.Address(), user_ids.data(), user_ids.size());
ctx.WriteBuffer(user_ids.data(), user_ids.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ACC_U0::ListOpenUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
ctx.WriteBuffer(user_ids.data(), user_ids.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_ACC, "called");
}
void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) {
@@ -104,6 +111,7 @@ ACC_U0::ACC_U0() : ServiceFramework("acc:u0") {
static const FunctionInfo functions[] = {
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
{2, &ACC_U0::ListAllUsers, "ListAllUsers"},
{3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},
{4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_U0::GetProfile, "GetProfile"},
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},

View File

@@ -29,6 +29,7 @@ public:
private:
void GetUserExistence(Kernel::HLERequestContext& ctx);
void ListAllUsers(Kernel::HLERequestContext& ctx);
void ListOpenUsers(Kernel::HLERequestContext& ctx);
void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
void GetProfile(Kernel::HLERequestContext& ctx);
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);

View File

@@ -34,7 +34,38 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx)
rb.Push(RESULT_SUCCESS);
}
IAudioController::IAudioController() : ServiceFramework("IAudioController") {}
IAudioController::IAudioController() : ServiceFramework("IAudioController") {
static const FunctionInfo functions[] = {
{0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
{1, &IAudioController::GetMainAppletExpectedMasterVolume,
"GetMainAppletExpectedMasterVolume"},
{2, &IAudioController::GetLibraryAppletExpectedMasterVolume,
"GetLibraryAppletExpectedMasterVolume"},
{3, nullptr, "ChangeMainAppletMasterVolume"},
{4, nullptr, "SetTransparentVolumeRate"},
};
RegisterHandlers(functions);
}
void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(volume);
}
void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(volume);
}
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {}
@@ -46,6 +77,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
{11, &ISelfController::SetOperationModeChangedNotification,
"SetOperationModeChangedNotification"},
{12, &ISelfController::SetPerformanceModeChangedNotification,
@@ -98,6 +130,13 @@ void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestCo
LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag));
}
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -306,11 +345,11 @@ private:
u64 offset = rp.Pop<u64>();
const auto& output_buffer = ctx.BufferDescriptorC()[0];
const size_t size{ctx.GetWriteBufferSize()};
ASSERT(offset + output_buffer.Size() <= buffer.size());
ASSERT(offset + size <= buffer.size());
Memory::WriteBlock(output_buffer.Address(), buffer.data() + offset, output_buffer.Size());
ctx.WriteBuffer(buffer.data() + offset, size);
IPC::ResponseBuilder rb{ctx, 2};
@@ -378,8 +417,9 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
}
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {

View File

@@ -36,6 +36,13 @@ private:
class IAudioController final : public ServiceFramework<IAudioController> {
public:
IAudioController();
private:
void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
u32 volume{100};
};
class IDisplayController final : public ServiceFramework<IDisplayController> {
@@ -62,6 +69,7 @@ private:
void UnlockExit(Kernel::HLERequestContext& ctx);
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::SharedPtr<Kernel::Event> launchable_event;

View File

@@ -2,16 +2,44 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/aoc/aoc_u.h"
namespace Service {
namespace AOC {
AOC_U::AOC_U() : ServiceFramework("aoc:u") {
static const FunctionInfo functions[] = {
{0, nullptr, "CountAddOnContentByApplicationId"},
{1, nullptr, "ListAddOnContentByApplicationId"},
{2, &AOC_U::CountAddOnContent, "CountAddOnContent"},
{3, &AOC_U::ListAddOnContent, "ListAddOnContent"},
{4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
{5, nullptr, "GetAddOnContentBaseId"},
{6, nullptr, "PrepareAddOnContentByApplicationId"},
{7, nullptr, "PrepareAddOnContent"},
};
RegisterHandlers(functions);
}
void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
LOG_WARNING(Service_AOC, "(STUBBED) called");
}
void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
LOG_WARNING(Service_AOC, "(STUBBED) called");
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AOC_U>()->InstallAsService(service_manager);
}
AOC_U::AOC_U() : ServiceFramework("aoc:u") {}
} // namespace AOC
} // namespace Service

View File

@@ -13,6 +13,10 @@ class AOC_U final : public ServiceFramework<AOC_U> {
public:
AOC_U();
~AOC_U() = default;
private:
void CountAddOnContent(Kernel::HLERequestContext& ctx);
void ListAddOnContent(Kernel::HLERequestContext& ctx);
};
/// Registers all AOC services with the specified service manager.

View File

@@ -99,8 +99,6 @@ private:
void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
const auto& buffer = ctx.BufferDescriptorB()[0];
// TODO(st4rk): This is how libtransistor currently implements the
// GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address
// is used to know which buffer should be filled with data and send again to the service
@@ -112,7 +110,7 @@ private:
queue_keys.pop_back();
}
Memory::WriteBlock(buffer.Address(), &key, sizeof(u64));
ctx.WriteBuffer(&key, sizeof(u64));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -158,10 +156,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto& buffer = ctx.BufferDescriptorB()[0];
const std::string audio_interface = "AudioInterface";
Memory::WriteBlock(buffer.Address(), &audio_interface[0], audio_interface.size());
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -11,6 +12,9 @@
namespace Service {
namespace Audio {
/// TODO(bunnei): Find a proper value for the audio_ticks
constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 200)};
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
IAudioRenderer() : ServiceFramework("IAudioRenderer") {
@@ -30,12 +34,27 @@ public:
system_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent");
// Register event callback to update the Audio Buffer
audio_event = CoreTiming::RegisterEvent(
"IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
UpdateAudioCallback();
CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
});
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
~IAudioRenderer() = default;
private:
void UpdateAudioCallback() {
system_event->Signal();
}
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
AudioRendererResponseData response_data = {0};
LOG_DEBUG(Service_Audio, "%s", ctx.Description().c_str());
AudioRendererResponseData response_data{};
response_data.section_0_size =
response_data.state_entries.size() * sizeof(AudioRendererStateEntry);
@@ -51,9 +70,7 @@ private:
response_data.state_entries[i].state = 5;
}
auto& buffer = ctx.BufferDescriptorB()[0];
Memory::WriteBlock(buffer.Address(), &response_data, response_data.total_size);
ctx.WriteBuffer(&response_data, response_data.total_size);
IPC::ResponseBuilder rb{ctx, 2};
@@ -126,15 +143,18 @@ private:
static_assert(sizeof(AudioRendererResponseData) == 0x20e0,
"AudioRendererResponseData has wrong size");
/// This is used to trigger the audio event callback.
CoreTiming::EventType* audio_event;
Kernel::SharedPtr<Kernel::Event> system_event;
};
AudRenU::AudRenU() : ServiceFramework("audren:u") {
static const FunctionInfo functions[] = {
{0x00000000, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
{0x00000001, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
{0x00000002, nullptr, "GetAudioRenderersProcessMasterVolume"},
{0x00000003, nullptr, "SetAudioRenderersProcessMasterVolume"},
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
{1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
{2, &AudRenU::GetAudioRenderersProcessMasterVolume, "GetAudioRenderersProcessMasterVolume"},
{3, nullptr, "SetAudioRenderersProcessMasterVolume"},
};
RegisterHandlers(functions);
}
@@ -152,9 +172,17 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0x1000);
rb.Push<u64>(0x400);
LOG_WARNING(Service_Audio, "called");
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void AudRenU::GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(100);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
} // namespace Audio

View File

@@ -21,6 +21,7 @@ public:
private:
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx);
};
} // namespace Audio

View File

@@ -33,12 +33,10 @@ private:
IPC::RequestParser rp{ctx};
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
const auto& descriptor = ctx.BufferDescriptorB()[0];
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
// Error checking
ASSERT_MSG(length == descriptor.Size(), "unexpected size difference");
if (length < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
@@ -60,7 +58,7 @@ private:
}
// Write the data to memory
Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size());
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -0,0 +1,28 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/friend/friend_a.h"
namespace Service {
namespace Friend {
void Module::Interface::Unknown(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Friend, "(STUBBED) called");
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<Friend_A>(module)->InstallAsService(service_manager);
}
} // namespace Friend
} // namespace Service

View File

@@ -0,0 +1,29 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service {
namespace Friend {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> module, const char* name);
void Unknown(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
};
};
/// Registers all Friend services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace Friend
} // namespace Service

View File

@@ -0,0 +1,19 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/friend/friend_a.h"
namespace Service {
namespace Friend {
Friend_A::Friend_A(std::shared_ptr<Module> module)
: Module::Interface(std::move(module), "friend:a") {
static const FunctionInfo functions[] = {
{0, &Friend_A::Unknown, "Unknown"},
};
RegisterHandlers(functions);
}
} // namespace Friend
} // namespace Service

View File

@@ -0,0 +1,18 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/friend/friend.h"
namespace Service {
namespace Friend {
class Friend_A final : public Module::Interface {
public:
explicit Friend_A(std::shared_ptr<Module> module);
};
} // namespace Friend
} // namespace Service

View File

@@ -176,7 +176,10 @@ public:
{0, &Hid::CreateAppletResource, "CreateAppletResource"},
{1, &Hid::ActivateDebugPad, "ActivateDebugPad"},
{11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
{21, &Hid::ActivateMouse, "ActivateMouse"},
{31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
{103, &Hid::ActivateNpad, "ActivateNpad"},
@@ -184,9 +187,15 @@ public:
"AcquireNpadStyleSetUpdateEventHandle"},
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
"SetNpadJoyAssignmentModeSingleByDefault"},
{124, nullptr, "SetNpadJoyAssignmentModeDual"},
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
{203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
};
RegisterHandlers(functions);
@@ -222,12 +231,30 @@ private:
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateMouse(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -266,18 +293,49 @@ private:
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IActiveVibrationDeviceList>();
LOG_DEBUG(Service_HID, "called");
}
void SendVibrationValues(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
void ReloadInputDevices() {}

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sstream>
#include <string>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
@@ -28,19 +29,28 @@ private:
IsHead = 1,
IsTail = 2,
};
enum Severity : u32_le {
Trace,
Info,
Warning,
Error,
Critical,
};
u64_le pid;
u64_le threadContext;
union {
BitField<0, 16, Flags> flags;
BitField<16, 8, u32_le> severity;
BitField<16, 8, Severity> severity;
BitField<24, 8, u32_le> verbosity;
};
u32_le payload_size;
/// Returns true if this is part of a single log message
bool IsSingleMessage() const {
return (flags & Flags::IsHead) && (flags & Flags::IsTail);
bool IsHeadLog() const {
return flags & Flags::IsHead;
}
bool IsTailLog() const {
return flags & Flags::IsTail;
}
};
static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
@@ -57,7 +67,7 @@ private:
};
/**
* LM::Initialize service function
* LM::Log service function
* Inputs:
* 0: 0x00000000
* Outputs:
@@ -75,9 +85,9 @@ private:
Memory::ReadBlock(addr, &header, sizeof(MessageHeader));
addr += sizeof(MessageHeader);
if (!header.IsSingleMessage()) {
LOG_WARNING(Service_LM, "Multi message logs are unimplemeneted");
return;
if (header.IsHeadLog()) {
log_stream.str("");
log_stream.clear();
}
// Parse out log metadata
@@ -85,7 +95,7 @@ private:
std::string message, filename, function;
while (addr < end_addr) {
const Field field{static_cast<Field>(Memory::Read8(addr++))};
size_t length{Memory::Read8(addr++)};
const size_t length{Memory::Read8(addr++)};
if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
++addr;
@@ -110,28 +120,47 @@ private:
}
// Empty log - nothing to do here
if (message.empty()) {
if (log_stream.str().empty() && message.empty()) {
return;
}
// Format a nicely printable string out of the log metadata
std::string output;
if (filename.size()) {
output += filename + ':';
if (!filename.empty()) {
log_stream << filename << ':';
}
if (function.size()) {
output += function + ':';
if (!function.empty()) {
log_stream << function << ':';
}
if (line) {
output += std::to_string(line) + ':';
log_stream << std::to_string(line) << ':';
}
if (output.length() > 0 && output.back() == ':') {
output += ' ';
if (log_stream.str().length() > 0 && log_stream.str().back() == ':') {
log_stream << ' ';
}
output += message;
log_stream << message;
LOG_INFO(Debug_Emulated, "%s", output.c_str());
if (header.IsTailLog()) {
switch (header.severity) {
case MessageHeader::Severity::Trace:
LOG_TRACE(Debug_Emulated, "%s", log_stream.str().c_str());
break;
case MessageHeader::Severity::Info:
LOG_INFO(Debug_Emulated, "%s", log_stream.str().c_str());
break;
case MessageHeader::Severity::Warning:
LOG_WARNING(Debug_Emulated, "%s", log_stream.str().c_str());
break;
case MessageHeader::Severity::Error:
LOG_ERROR(Debug_Emulated, "%s", log_stream.str().c_str());
break;
case MessageHeader::Severity::Critical:
LOG_CRITICAL(Debug_Emulated, "%s", log_stream.str().c_str());
break;
}
}
}
std::ostringstream log_stream;
};
void InstallInterfaces(SM::ServiceManager& service_manager) {

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nifm/nifm_a.h"
#include "core/hle/service/nifm/nifm_s.h"
@@ -28,9 +29,9 @@ class IRequest final : public ServiceFramework<IRequest> {
public:
explicit IRequest() : ServiceFramework("IRequest") {
static const FunctionInfo functions[] = {
{0, nullptr, "GetRequestState"},
{1, nullptr, "GetResult"},
{2, nullptr, "GetSystemEventReadableHandles"},
{0, &IRequest::GetRequestState, "GetRequestState"},
{1, &IRequest::GetResult, "GetResult"},
{2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
{3, nullptr, "Cancel"},
{4, nullptr, "Submit"},
{5, nullptr, "SetRequirement"},
@@ -55,7 +56,32 @@ public:
{25, nullptr, "UnregisterSocketDescriptor"},
};
RegisterHandlers(functions);
event1 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event1");
event2 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event2");
}
private:
void GetRequestState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void GetResult(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event1, event2);
}
Kernel::SharedPtr<Kernel::Event> event1, event2;
};
class INetworkProfile final : public ServiceFramework<INetworkProfile> {

View File

@@ -0,0 +1,16 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/ns/pl_u.h"
namespace Service {
namespace NS {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<PL_U>()->InstallAsService(service_manager);
}
} // namespace NS
} // namespace Service

View File

@@ -0,0 +1,16 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service {
namespace NS {
/// Registers all NS services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace NS
} // namespace Service

View File

@@ -0,0 +1,111 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_paths.h"
#include "common/file_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/ns/pl_u.h"
namespace Service {
namespace NS {
struct FontRegion {
u32 offset;
u32 size;
};
// The below data is specific to shared font data dumped from Switch on f/w 2.2
// Virtual address and offsets/sizes likely will vary by dump
static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
static constexpr std::array<FontRegion, 6> SHARED_FONT_REGIONS{
FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58},
FontRegion{0x009725d4, 0x0001aca8}, FontRegion{0x0098d284, 0x00369cec},
FontRegion{0x00cf6f78, 0x0039b858}, FontRegion{0x010927d8, 0x00019e80},
};
enum class LoadState : u32 {
Loading = 0,
Done = 1,
};
PL_U::PL_U() : ServiceFramework("pl:u") {
static const FunctionInfo functions[] = {
{1, &PL_U::GetLoadState, "GetLoadState"},
{2, &PL_U::GetSize, "GetSize"},
{3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
{4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}};
RegisterHandlers(functions);
// Attempt to load shared font data from disk
const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT};
FileUtil::CreateFullPath(filepath); // Create path if not already created
FileUtil::IOFile file(filepath, "rb");
if (file.IsOpen()) {
// Read shared font data
ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE);
shared_font = std::make_shared<std::vector<u8>>(static_cast<size_t>(file.GetSize()));
file.ReadBytes(shared_font->data(), shared_font->size());
} else {
LOG_WARNING(Service_NS, "Unable to load shared font: %s", filepath.c_str());
}
}
void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id=%d", font_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(LoadState::Done));
}
void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id=%d", font_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size);
}
void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id=%d", font_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset);
}
void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
if (shared_font != nullptr) {
// TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared
// font data. This (likely) relies on exact address, size, and offsets from the original
// dump. In the future, we need to replace this with a more robust solution.
// Map backing memory for the font data
Kernel::g_current_process->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0,
SHARED_FONT_MEM_SIZE,
Kernel::MemoryState::Shared);
// Create shared font memory object
shared_font_mem = Kernel::SharedMemory::Create(
Kernel::g_current_process, SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
"PL_U:shared_font_mem");
}
LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(shared_font_mem);
}
} // namespace NS
} // namespace Service

View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/service.h"
namespace Service {
namespace NS {
class PL_U final : public ServiceFramework<PL_U> {
public:
PL_U();
~PL_U() = default;
private:
void GetLoadState(Kernel::HLERequestContext& ctx);
void GetSize(Kernel::HLERequestContext& ctx);
void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx);
void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
/// Handle to shared memory region designated for a shared font
Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
/// Backing memory for the shared font data
std::shared_ptr<std::vector<u8>> shared_font;
};
} // namespace NS
} // namespace Service

View File

@@ -2,8 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
@@ -12,8 +14,8 @@ namespace Nvidia {
namespace Devices {
u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%llx, output_size=0x%llx",
command, input.size(), output.size());
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocInitalizeExCommand:
@@ -44,11 +46,12 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
LOG_DEBUG(Service_NVDRV, "called, pages=%x, page_size=%x, flags=%x", params.pages,
params.page_size, params.flags);
auto& gpu = Core::System::GetInstance().GPU();
const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
if (params.flags & 1) {
params.offset = memory_manager->AllocateSpace(params.offset, size, 1);
params.offset = gpu.memory_manager->AllocateSpace(params.offset, size, 1);
} else {
params.offset = memory_manager->AllocateSpace(size, params.align);
params.offset = gpu.memory_manager->AllocateSpace(size, params.align);
}
std::memcpy(output.data(), &params, output.size());
@@ -60,7 +63,8 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV,
"called, flags=%x, nvmap_handle=%x, buffer_offset=%lx, mapping_size=%lx, offset=%lx",
"called, flags=%x, nvmap_handle=%x, buffer_offset=%" PRIu64 ", mapping_size=%" PRIu64
", offset=%" PRIu64,
params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
params.offset);
@@ -71,10 +75,12 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
auto object = nvmap_dev->GetObject(params.nvmap_handle);
ASSERT(object);
auto& gpu = Core::System::GetInstance().GPU();
if (params.flags & 1) {
params.offset = memory_manager->MapBufferEx(object->addr, params.offset, object->size);
params.offset = gpu.memory_manager->MapBufferEx(object->addr, params.offset, object->size);
} else {
params.offset = memory_manager->MapBufferEx(object->addr, object->size);
params.offset = gpu.memory_manager->MapBufferEx(object->addr, object->size);
}
std::memcpy(output.data(), &params, output.size());
@@ -93,8 +99,8 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou
u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr=%lx, buf_size=%x", params.buf_addr,
params.buf_size);
LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr=%" PRIu64 ", buf_size=%x",
params.buf_addr, params.buf_size);
params.buf_size = 0x30;
params.regions[0].offset = 0x04000000;

View File

@@ -10,7 +10,6 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/memory_manager.h"
namespace Service {
namespace Nvidia {
@@ -20,9 +19,7 @@ class nvmap;
class nvhost_as_gpu final : public nvdevice {
public:
nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvdevice(), nvmap_dev(std::move(nvmap_dev)) {
memory_manager = std::make_shared<MemoryManager>();
}
nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
~nvhost_as_gpu() override = default;
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
@@ -101,7 +98,6 @@ private:
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
std::shared_ptr<nvmap> nvmap_dev;
std::shared_ptr<MemoryManager> memory_manager;
};
} // namespace Devices

View File

@@ -11,12 +11,14 @@ namespace Nvidia {
namespace Devices {
u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%lx, output_size=0x%lx", command,
input.size(), output.size());
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocGetConfigCommand:
return NvOsGetConfigU32(input, output);
case IoctlCommand::IocCtrlEventWaitCommand:
return IocCtrlEventWait(input, output);
}
UNIMPLEMENTED();
return 0;
@@ -45,6 +47,18 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
return 0;
}
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id=%u threshold=%u timeout=%d",
params.syncpt_id, params.threshold, params.timeout);
// TODO(Subv): Implement actual syncpt waiting.
params.value = 0;
std::memcpy(output.data(), &params, sizeof(params));
return 0;
}
} // namespace Devices
} // namespace Nvidia
} // namespace Service

View File

@@ -31,6 +31,7 @@ private:
IocModuleRegRDWRCommand = 0xC008010E,
IocSyncptWaitexCommand = 0xC0100019,
IocSyncptReadMaxCommand = 0xC008001A,
IocCtrlEventWaitCommand = 0xC010001D,
IocGetConfigCommand = 0xC183001B,
};
@@ -41,7 +42,17 @@ private:
};
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
struct IocCtrlEventWaitParams {
u32_le syncpt_id;
u32_le threshold;
s32_le timeout;
u32_le value;
};
static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Devices

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
@@ -11,8 +12,8 @@ namespace Nvidia {
namespace Devices {
u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%llx, output_size=0x%llx",
command, input.size(), output.size());
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocGetCharacteristicsCommand:
@@ -78,7 +79,7 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, mask=0x%x, mask_buf_addr=0x%lx",
LOG_WARNING(Service_NVDRV, "(STUBBED) called, mask=0x%x, mask_buf_addr=0x%" PRIx64,
params.mask_buf_size, params.mask_buf_addr);
std::memcpy(output.data(), &params, sizeof(params));
return 0;

View File

@@ -2,9 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <map>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
namespace Service {
@@ -12,8 +14,8 @@ namespace Nvidia {
namespace Devices {
u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%llx, output_size=0x%llx",
command, input.size(), output.size());
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
@@ -73,7 +75,8 @@ u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& out
u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&zcull_params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, gpu_va=%lx, mode=%x", zcull_params.gpu_va, zcull_params.mode);
LOG_DEBUG(Service_NVDRV, "called, gpu_va=%" PRIx64 ", mode=%x", zcull_params.gpu_va,
zcull_params.mode);
std::memcpy(output.data(), &zcull_params, output.size());
return 0;
}
@@ -81,8 +84,8 @@ u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output)
u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetErrorNotifier params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset=%lx, size=%lx, mem=%x", params.offset,
params.size, params.mem);
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset=%" PRIx64 ", size=%" PRIx64 ", mem=%x",
params.offset, params.size, params.mem);
std::memcpy(output.data(), &params, output.size());
return 0;
}
@@ -122,7 +125,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
UNIMPLEMENTED();
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo=%lx, num_entries=%x, flags=%x",
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo=%" PRIx64 ", num_entries=%x, flags=%x",
params.gpfifo, params.num_entries, params.flags);
auto entries = std::vector<IoctlGpfifoEntry>();
@@ -131,7 +134,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
params.num_entries * sizeof(IoctlGpfifoEntry));
for (auto entry : entries) {
VAddr va_addr = entry.Address();
// TODO(ogniK): Process these
Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
}
params.fence_out.id = 0;
params.fence_out.value = 0;

View File

@@ -4,6 +4,7 @@
#pragma once
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
@@ -12,12 +13,14 @@
namespace Service {
namespace Nvidia {
namespace Devices {
class nvmap;
constexpr u32 NVGPU_IOCTL_MAGIC('H');
constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
class nvhost_gpu final : public nvdevice {
public:
nvhost_gpu() = default;
nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
~nvhost_gpu() override = default;
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
@@ -132,6 +135,8 @@ private:
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
std::shared_ptr<nvmap> nvmap_dev;
};
} // namespace Devices

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -71,7 +72,7 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
object->addr = params.addr;
object->status = Object::Status::Allocated;
LOG_DEBUG(Service_NVDRV, "called, addr=0x%llx", params.addr);
LOG_DEBUG(Service_NVDRV, "called, addr=0x%" PRIx64, params.addr);
std::memcpy(output.data(), &params, sizeof(params));
return 0;
@@ -102,11 +103,8 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
[&](const auto& entry) { return entry.second->id == params.id; });
ASSERT(itr != handles.end());
// Make a new handle for the object
u32 handle = next_handle++;
handles[handle] = itr->second;
params.handle = handle;
// Return the existing handle instead of creating a new one.
params.handle = itr->first;
std::memcpy(output.data(), &params, sizeof(params));
return 0;

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
@@ -14,9 +15,8 @@ namespace Nvidia {
void NVDRV::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
auto buffer = ctx.BufferDescriptorA()[0];
std::string device_name = Memory::ReadCString(buffer.Address(), buffer.Size());
const auto& buffer = ctx.ReadBuffer();
std::string device_name(buffer.begin(), buffer.end());
u32 fd = nvdrv->Open(device_name);
IPC::ResponseBuilder rb{ctx, 4};
@@ -32,25 +32,13 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
u32 fd = rp.Pop<u32>();
u32 command = rp.Pop<u32>();
std::vector<u8> output(ctx.GetWriteBufferSize());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
if (ctx.BufferDescriptorA()[0].Size() != 0) {
auto input_buffer = ctx.BufferDescriptorA()[0];
auto output_buffer = ctx.BufferDescriptorB()[0];
std::vector<u8> input(input_buffer.Size());
std::vector<u8> output(output_buffer.Size());
Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.Size());
rb.Push(nvdrv->Ioctl(fd, command, input, output));
Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.Size());
} else {
auto input_buffer = ctx.BufferDescriptorX()[0];
auto output_buffer = ctx.BufferDescriptorC()[0];
std::vector<u8> input(input_buffer.size);
std::vector<u8> output(output_buffer.size);
Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.size);
rb.Push(nvdrv->Ioctl(fd, command, input, output));
Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.size);
}
rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output));
ctx.WriteBuffer(output);
}
void NVDRV::Close(Kernel::HLERequestContext& ctx) {
@@ -88,7 +76,7 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
pid = rp.Pop<u64>();
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x%lx", pid);
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x%" PRIx64, pid);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);

View File

@@ -32,11 +32,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
Module::Module() {
auto nvmap_dev = std::make_shared<Devices::nvmap>();
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev);
devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev);
devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>();
devices["/dev/nvmap"] = nvmap_dev;
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev);
devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>();
devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>();
}
u32 Module::Open(std::string device_name) {

View File

@@ -17,6 +17,13 @@ namespace Devices {
class nvdevice;
}
struct IoctlFence {
u32 id;
u32 value;
};
static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size");
class Module final {
public:
Module();

View File

@@ -6,6 +6,7 @@
#include "common/alignment.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
@@ -129,6 +130,7 @@ void NVFlinger::Compose() {
if (buffer == boost::none) {
// There was no queued buffer to draw, render previous frame
Core::System::GetInstance().perf_stats.EndGameFrame();
VideoCore::g_renderer->SwapBuffers({});
continue;
}

View File

@@ -20,9 +20,11 @@
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/pctl/pctl.h"
#include "core/hle/service/service.h"
@@ -179,9 +181,11 @@ void Init() {
APM::InstallInterfaces(*SM::g_service_manager);
Audio::InstallInterfaces(*SM::g_service_manager);
FileSystem::InstallInterfaces(*SM::g_service_manager);
Friend::InstallInterfaces(*SM::g_service_manager);
HID::InstallInterfaces(*SM::g_service_manager);
LM::InstallInterfaces(*SM::g_service_manager);
NIFM::InstallInterfaces(*SM::g_service_manager);
NS::InstallInterfaces(*SM::g_service_manager);
Nvidia::InstallInterfaces(*SM::g_service_manager);
PCTL::InstallInterfaces(*SM::g_service_manager);
Sockets::InstallInterfaces(*SM::g_service_manager);

View File

@@ -17,9 +17,7 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
u32 id = rp.Pop<u32>();
constexpr std::array<u8, 13> lang_codes{};
const auto& output_buffer = ctx.BufferDescriptorC()[0];
Memory::WriteBlock(output_buffer.Address(), lang_codes.data(), lang_codes.size());
ctx.WriteBuffer(lang_codes.data(), lang_codes.size());
IPC::ResponseBuilder rb{ctx, 2};

View File

@@ -17,6 +17,15 @@ void BSD_U::RegisterClient(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno
}
void BSD_U::StartMonitoring(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // bsd errno
}
void BSD_U::Socket(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -67,6 +76,7 @@ void BSD_U::Close(Kernel::HLERequestContext& ctx) {
BSD_U::BSD_U() : ServiceFramework("bsd:u") {
static const FunctionInfo functions[] = {{0, &BSD_U::RegisterClient, "RegisterClient"},
{1, &BSD_U::StartMonitoring, "StartMonitoring"},
{2, &BSD_U::Socket, "Socket"},
{11, &BSD_U::SendTo, "SendTo"},
{14, &BSD_U::Connect, "Connect"},

View File

@@ -17,6 +17,7 @@ public:
private:
void RegisterClient(Kernel::HLERequestContext& ctx);
void StartMonitoring(Kernel::HLERequestContext& ctx);
void Socket(Kernel::HLERequestContext& ctx);
void Connect(Kernel::HLERequestContext& ctx);
void SendTo(Kernel::HLERequestContext& ctx);

View File

@@ -146,6 +146,13 @@ void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
}
void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>();
LOG_DEBUG(Service_Time, "called");
}
Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
: ServiceFramework(name), time(std::move(time)) {}

View File

@@ -56,6 +56,7 @@ public:
void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx);
void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> time;

View File

@@ -10,6 +10,10 @@ namespace Time {
TIME_S::TIME_S(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:s") {
static const FunctionInfo functions[] = {
{0, &TIME_S::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
{1, &TIME_S::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
{2, &TIME_S::GetStandardSteadyClock, "GetStandardSteadyClock"},
{3, &TIME_S::GetTimeZoneService, "GetTimeZoneService"},
{4, &TIME_S::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
};
RegisterHandlers(functions);
}

View File

@@ -13,6 +13,7 @@ TIME_U::TIME_U(std::shared_ptr<Module> time) : Module::Interface(std::move(time)
{1, &TIME_U::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
{2, &TIME_U::GetStandardSteadyClock, "GetStandardSteadyClock"},
{3, &TIME_U::GetTimeZoneService, "GetTimeZoneService"},
{4, &TIME_U::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
};
RegisterHandlers(functions);
}

View File

@@ -8,6 +8,7 @@
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
@@ -38,6 +39,7 @@ public:
template <typename T>
T Read() {
ASSERT(read_index + sizeof(T) <= buffer.size());
T val;
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
read_index += sizeof(T);
@@ -47,6 +49,7 @@ public:
template <typename T>
T ReadUnaligned() {
ASSERT(read_index + sizeof(T) <= buffer.size());
T val;
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
read_index += sizeof(T);
@@ -54,6 +57,7 @@ public:
}
std::vector<u8> ReadBlock(size_t length) {
ASSERT(read_index + length <= buffer.size());
const u8* const begin = buffer.data() + read_index;
const u8* const end = begin + length;
std::vector<u8> data(begin, end);
@@ -86,7 +90,18 @@ public:
write_index = Common::AlignUp(write_index, 4);
}
template <typename T>
void WriteObject(const T& val) {
u32_le size = static_cast<u32>(sizeof(val));
Write(size);
// TODO(Subv): Support file descriptors.
Write<u32_le>(0); // Fd count.
Write(val);
}
void Deserialize() {
ASSERT(buffer.size() > sizeof(Header));
Header header{};
std::memcpy(&header, buffer.data(), sizeof(Header));
@@ -262,6 +277,12 @@ public:
Data data;
};
struct BufferProducerFence {
u32 is_valid;
std::array<Nvidia::IoctlFence, 4> fences;
};
static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size");
class IGBPDequeueBufferResponseParcel : public Parcel {
public:
explicit IGBPDequeueBufferResponseParcel(u32 slot) : Parcel(), slot(slot) {}
@@ -269,10 +290,15 @@ public:
protected:
void SerializeData() override {
Write(slot);
// TODO(Subv): Find out how this Fence is used.
std::array<u32_le, 11> fence = {};
Write(fence);
BufferProducerFence fence = {};
fence.is_valid = 1;
for (auto& fence_ : fence.fences)
fence_.id = -1;
Write(slot);
Write<u32_le>(1);
WriteObject(fence);
Write<u32_le>(0);
}
@@ -302,11 +328,10 @@ public:
protected:
void SerializeData() override {
// TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx.
Write<u32_le>(0);
Write<u32_le>(0);
Write<u32_le>(0);
Write(buffer);
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try
// to read an IGBPBuffer object from the parcel.
Write<u32_le>(1);
WriteObject(buffer);
Write<u32_le>(0);
}
@@ -415,7 +440,7 @@ public:
{0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
{1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
{2, &IHOSBinderDriver::GetNativeHandle, "GetNativeHandle"},
{3, &IHOSBinderDriver::TransactParcelAuto, "TransactParcelAuto"},
{3, &IHOSBinderDriver::TransactParcel, "TransactParcelAuto"},
};
RegisterHandlers(functions);
}
@@ -439,90 +464,61 @@ private:
SetPreallocatedBuffer = 14
};
void TransactParcel(u32 id, TransactionId transaction, const std::vector<u8>& input_data,
VAddr output_addr, u64 output_size) {
auto buffer_queue = nv_flinger->GetBufferQueue(id);
std::vector<u8> response_buffer;
if (transaction == TransactionId::Connect) {
IGBPConnectRequestParcel request{input_data};
IGBPConnectResponseParcel response{1280, 720};
response_buffer = response.Serialize();
} else if (transaction == TransactionId::SetPreallocatedBuffer) {
IGBPSetPreallocatedBufferRequestParcel request{input_data};
buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer);
IGBPSetPreallocatedBufferResponseParcel response{};
response_buffer = response.Serialize();
} else if (transaction == TransactionId::DequeueBuffer) {
IGBPDequeueBufferRequestParcel request{input_data};
u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width,
request.data.height);
IGBPDequeueBufferResponseParcel response{slot};
response_buffer = response.Serialize();
} else if (transaction == TransactionId::RequestBuffer) {
IGBPRequestBufferRequestParcel request{input_data};
auto& buffer = buffer_queue->RequestBuffer(request.slot);
IGBPRequestBufferResponseParcel response{buffer};
response_buffer = response.Serialize();
} else if (transaction == TransactionId::QueueBuffer) {
IGBPQueueBufferRequestParcel request{input_data};
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
IGBPQueueBufferResponseParcel response{1280, 720};
response_buffer = response.Serialize();
} else if (transaction == TransactionId::Query) {
IGBPQueryRequestParcel request{input_data};
u32 value =
buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
IGBPQueryResponseParcel response{value};
response_buffer = response.Serialize();
} else {
ASSERT_MSG(false, "Unimplemented");
}
Memory::WriteBlock(output_addr, response_buffer.data(), output_size);
}
void TransactParcel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 id = rp.Pop<u32>();
auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
u32 flags = rp.Pop<u32>();
auto buffer_queue = nv_flinger->GetBufferQueue(id);
LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
auto& input_buffer = ctx.BufferDescriptorA()[0];
auto& output_buffer = ctx.BufferDescriptorB()[0];
std::vector<u8> input_data(input_buffer.Size());
Memory::ReadBlock(input_buffer.Address(), input_data.data(), input_buffer.Size());
if (transaction == TransactionId::Connect) {
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
IGBPConnectResponseParcel response{1280, 720};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::SetPreallocatedBuffer) {
IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
TransactParcel(id, transaction, input_data, output_buffer.Address(), output_buffer.Size());
buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
IGBPSetPreallocatedBufferResponseParcel response{};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::DequeueBuffer) {
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
void TransactParcelAuto(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 id = rp.Pop<u32>();
auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width,
request.data.height);
auto& input_buffer = ctx.BufferDescriptorX()[0];
auto& output_buffer = ctx.BufferDescriptorC()[0];
std::vector<u8> input_data(input_buffer.size);
Memory::ReadBlock(input_buffer.Address(), input_data.data(), input_buffer.size);
IGBPDequeueBufferResponseParcel response{slot};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::RequestBuffer) {
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
TransactParcel(id, transaction, input_data, output_buffer.Address(), output_buffer.Size());
auto& buffer = buffer_queue->RequestBuffer(request.slot);
IGBPRequestBufferResponseParcel response{buffer};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::QueueBuffer) {
IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
IGBPQueueBufferResponseParcel response{1280, 720};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::Query) {
IGBPQueryRequestParcel request{ctx.ReadBuffer()};
u32 value =
buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
IGBPQueryResponseParcel response{value};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::CancelBuffer) {
LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
} else {
ASSERT_MSG(false, "Unimplemented");
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -555,7 +551,7 @@ private:
}
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
}; // namespace VI
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
public:
@@ -700,18 +696,13 @@ void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) {
u64 layer_id = rp.Pop<u64>();
u64 aruid = rp.Pop<u64>();
auto& buffer = ctx.BufferDescriptorB()[0];
u64 display_id = nv_flinger->OpenDisplay(display_name);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
auto data = native_window.Serialize();
Memory::WriteBlock(buffer.Address(), data.data(), data.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(data.size());
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) {
@@ -722,21 +713,16 @@ void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx
rp.Pop<u32>(); // padding
u64 display_id = rp.Pop<u64>();
auto& buffer = ctx.BufferDescriptorB()[0];
// TODO(Subv): What's the difference between a Stray and a Managed layer?
u64 layer_id = nv_flinger->CreateLayer(display_id);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
auto data = native_window.Serialize();
Memory::WriteBlock(buffer.Address(), data.data(), data.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
rb.Push<u64>(data.size());
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
@@ -762,8 +748,7 @@ void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext&
void IApplicationDisplayService::ListDisplays(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
DisplayInfo display_info;
auto& buffer = ctx.BufferDescriptorB()[0];
Memory::WriteBlock(buffer.Address(), &display_info, sizeof(DisplayInfo));
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(1);

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
@@ -52,6 +53,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUti
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file,
const std::string& filepath) {
bool is_main_found{};
bool is_npdm_found{};
bool is_rtld_found{};
bool is_sdk_found{};
@@ -66,6 +68,9 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& fil
// Verify filename
if (Common::ToLower(virtual_name) == "main") {
is_main_found = true;
} else if (Common::ToLower(virtual_name) == "main.npdm") {
is_npdm_found = true;
return true;
} else if (Common::ToLower(virtual_name) == "rtld") {
is_rtld_found = true;
} else if (Common::ToLower(virtual_name) == "sdk") {
@@ -82,14 +87,14 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& fil
}
// We are done if we've found and verified all required NSOs
return !(is_main_found && is_rtld_found && is_sdk_found);
return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found);
};
// Search the directory recursively, looking for the required modules
const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
FileUtil::ForeachDirectoryEntry(nullptr, directory, callback);
if (is_main_found && is_rtld_found && is_sdk_found) {
if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) {
return FileType::DeconstructedRomDirectory;
}
@@ -107,16 +112,24 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
process = Kernel::Process::Create("main");
const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
const std::string npdm_path = directory + DIR_SEP + "main.npdm";
ResultStatus result = metadata.Load(npdm_path);
if (result != ResultStatus::Success) {
return result;
}
metadata.Print();
// Load NSO modules
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const std::string path = directory + DIR_SEP + module;
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);
next_load_addr = AppLoader_NSO::LoadModule(path, load_addr, metadata.GetTitleID());
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module %s @ 0x%llx", module, load_addr);
LOG_DEBUG(Loader, "loaded module %s @ 0x%" PRIx64, module, load_addr);
} else {
next_load_addr = load_addr;
}
@@ -126,7 +139,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE);
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
// Find the RomFS by searching for a ".romfs" file in this directory
filepath_romfs = FindRomFS(directory);
@@ -158,8 +172,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(
offset = 0;
size = romfs_file->GetSize();
LOG_DEBUG(Loader, "RomFS offset: 0x%08X", offset);
LOG_DEBUG(Loader, "RomFS size: 0x%08X", size);
LOG_DEBUG(Loader, "RomFS offset: 0x%016" PRIX64, offset);
LOG_DEBUG(Loader, "RomFS size: 0x%016" PRIX64, size);
// Reset read pointer
file.Seek(0, SEEK_SET);

View File

@@ -6,6 +6,7 @@
#include <string>
#include "common/common_types.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/kernel.h"
#include "core/loader/loader.h"
@@ -41,6 +42,7 @@ public:
private:
std::string filepath_romfs;
std::string filepath;
FileSys::ProgramMetadata metadata;
};
} // namespace Loader

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <vector>
#include <lz4.h>
#include "common/common_funcs.h"
@@ -82,7 +83,7 @@ static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeade
reinterpret_cast<char*>(uncompressed_data.data()), compressed_size, header.size);
ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(),
"%d != %d != %d", bytes_uncompressed, header.size, uncompressed_data.size());
"%d != %u != %zu", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
@@ -91,7 +92,7 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base, u64 tid) {
FileUtil::IOFile file(path, "rb");
if (!file.IsOpen()) {
return {};
@@ -108,7 +109,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
}
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0);
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", tid);
std::vector<u8> program_image;
for (int i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> data =
@@ -157,8 +158,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process = Kernel::Process::Create("main");
// Load module
LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR);
LOG_DEBUG(Loader, "loaded module %s @ 0x%llx", filepath.c_str(), Memory::PROCESS_IMAGE_VADDR);
LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR, 0);
LOG_DEBUG(Loader, "loaded module %s @ 0x%" PRIx64, filepath.c_str(),
Memory::PROCESS_IMAGE_VADDR);
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;

View File

@@ -29,7 +29,7 @@ public:
return IdentifyType(file, filepath);
}
static VAddr LoadModule(const std::string& path, VAddr load_base);
static VAddr LoadModule(const std::string& path, VAddr load_base, u64 tid);
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstring>
#include <boost/optional.hpp>
#include "common/assert.h"
@@ -38,12 +39,12 @@ PageTable* GetCurrentPageTable() {
}
static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
LOG_DEBUG(HW_Memory, "Mapping %p onto %016" PRIX64 "-%016" PRIX64, memory, base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
VAddr end = base + size;
while (base != end) {
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %016" PRIX64, base);
page_table.attributes[base] = type;
page_table.pointers[base] = memory;
@@ -55,14 +56,14 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
}
void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %016" PRIX64, size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %016" PRIX64, base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
}
void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %016" PRIX64, size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %016" PRIX64, base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
@@ -71,8 +72,8 @@ void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer
}
void UnmapRegion(PageTable& page_table, VAddr base, u64 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %016" PRIX64, size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %016" PRIX64, base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
@@ -117,10 +118,15 @@ boost::optional<T> ReadSpecial(VAddr addr);
template <typename T>
T Read(const VAddr vaddr) {
if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES) {
LOG_ERROR(HW_Memory, "Read%lu after page table @ 0x%016" PRIX64, sizeof(T) * 8, vaddr);
return 0;
}
const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case PageType::Unmapped:
LOG_ERROR(HW_Memory, "unmapped Read%lu @ 0x%016llX", sizeof(T) * 8, vaddr);
LOG_ERROR(HW_Memory, "unmapped Read%zu @ 0x%016" PRIX64, sizeof(T) * 8, vaddr);
return 0;
case PageType::Special: {
if (auto result = ReadSpecial<T>(vaddr))
@@ -129,7 +135,7 @@ T Read(const VAddr vaddr) {
}
case PageType::Memory: {
const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
ASSERT_MSG(page_pointer, "Mapped memory page without a pointer @ %08X", vaddr);
ASSERT_MSG(page_pointer, "Mapped memory page without a pointer @ %016" PRIX64, vaddr);
T value;
std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
@@ -145,11 +151,17 @@ bool WriteSpecial(VAddr addr, const T data);
template <typename T>
void Write(const VAddr vaddr, const T data) {
if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES) {
LOG_ERROR(HW_Memory, "Write%lu after page table 0x%08X @ 0x%016" PRIX64, sizeof(data) * 8,
(u32)data, vaddr);
return;
}
const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case PageType::Unmapped:
LOG_ERROR(HW_Memory, "unmapped Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data,
vaddr);
LOG_ERROR(HW_Memory, "unmapped Write%zu 0x%08X @ 0x%016" PRIX64, sizeof(data) * 8,
static_cast<u32>(data), vaddr);
return;
case PageType::Special: {
if (WriteSpecial<T>(vaddr, data))
@@ -158,7 +170,7 @@ void Write(const VAddr vaddr, const T data) {
}
case PageType::Memory: {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
ASSERT_MSG(page_pointer, "Mapped memory page without a pointer @ %08X", vaddr);
ASSERT_MSG(page_pointer, "Mapped memory page without a pointer @ %016" PRIX64, vaddr);
std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
return;
}
@@ -203,7 +215,7 @@ u8* GetPointer(const VAddr vaddr) {
return page_pointer + (vaddr & PAGE_MASK);
}
LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr);
LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%016" PRIx64, vaddr);
return nullptr;
}
@@ -241,12 +253,13 @@ u8* GetPhysicalPointer(PAddr address) {
});
if (area == std::end(memory_areas)) {
LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x%08X", address);
LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x%016" PRIX64, address);
return nullptr;
}
if (area->paddr_base == IO_AREA_PADDR) {
LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr=0x%08X", address);
LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr=0x%016" PRIX64,
address);
return nullptr;
}
@@ -321,7 +334,9 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
switch (page_table.attributes[page_index]) {
case PageType::Unmapped:
LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0xllx, size = %zu)",
LOG_ERROR(HW_Memory,
"unmapped ReadBlock @ 0x%016" PRIX64 " (start address = 0x%" PRIx64
", size = %zu)",
current_vaddr, src_addr, size);
std::memset(dest_buffer, 0, copy_amount);
break;
@@ -393,7 +408,8 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
switch (page_table.attributes[page_index]) {
case PageType::Unmapped:
LOG_ERROR(HW_Memory,
"unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
"unmapped WriteBlock @ 0x%016" PRIX64 " (start address = 0x%016" PRIX64
", size = %zu)",
current_vaddr, dest_addr, size);
break;
case PageType::Special:
@@ -437,7 +453,9 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
switch (current_page_table->attributes[page_index]) {
case PageType::Unmapped:
LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
LOG_ERROR(HW_Memory,
"unmapped ZeroBlock @ 0x%016" PRIX64 " (start address = 0x%016" PRIX64
", size = %zu)",
current_vaddr, dest_addr, size);
break;
case PageType::Special:
@@ -474,7 +492,9 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
switch (current_page_table->attributes[page_index]) {
case PageType::Unmapped:
LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
LOG_ERROR(HW_Memory,
"unmapped CopyBlock @ 0x%016" PRIX64 " (start address = 0x%016" PRIX64
", size = %zu)",
current_vaddr, src_addr, size);
ZeroBlock(dest_addr, copy_amount);
break;
@@ -599,7 +619,7 @@ boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) {
PAddr VirtualToPhysicalAddress(const VAddr addr) {
auto paddr = TryVirtualToPhysicalAddress(addr);
if (!paddr) {
LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08X", addr);
LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%016" PRIX64, addr);
// To help with debugging, set bit on address so that it's obviously invalid.
return addr | 0x80000000;
}

View File

@@ -25,10 +25,11 @@ namespace Memory {
* Page size used by the ARM architecture. This is the smallest granularity with which memory can
* be mapped.
*/
const int PAGE_BITS = 12;
const u64 PAGE_SIZE = 1 << PAGE_BITS;
const u64 PAGE_MASK = PAGE_SIZE - 1;
const size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (36 - PAGE_BITS);
constexpr size_t PAGE_BITS = 12;
constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
constexpr size_t ADDRESS_SPACE_BITS = 36;
constexpr size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS);
enum class PageType : u8 {
/// Page is unmapped and should cause an access error.

View File

@@ -1,4 +1,15 @@
add_library(video_core STATIC
command_processor.cpp
command_processor.h
engines/fermi_2d.cpp
engines/fermi_2d.h
engines/maxwell_3d.cpp
engines/maxwell_3d.h
engines/maxwell_compute.cpp
engines/maxwell_compute.h
gpu.h
memory_manager.cpp
memory_manager.h
renderer_base.cpp
renderer_base.h
renderer_opengl/gl_resource_manager.h

View File

@@ -0,0 +1,119 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstddef>
#include <memory>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/vector_math.h"
#include "core/memory.h"
#include "core/tracer/recorder.h"
#include "video_core/command_processor.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_compute.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Tegra {
enum class BufferMethods {
BindObject = 0,
CountBufferMethods = 0x100,
};
void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X", method, subchannel,
value);
if (method == static_cast<u32>(BufferMethods::BindObject)) {
// Bind the current subchannel to the desired engine id.
LOG_DEBUG(HW_GPU, "Binding subchannel %u to engine %u", subchannel, value);
ASSERT(bound_engines.find(subchannel) == bound_engines.end());
bound_engines[subchannel] = static_cast<EngineID>(value);
return;
}
if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) {
// TODO(Subv): Research and implement these methods.
LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
return;
}
ASSERT(bound_engines.find(subchannel) != bound_engines.end());
const EngineID engine = bound_engines[subchannel];
switch (engine) {
case EngineID::FERMI_TWOD_A:
fermi_2d->WriteReg(method, value);
break;
case EngineID::MAXWELL_B:
maxwell_3d->WriteReg(method, value);
break;
case EngineID::MAXWELL_COMPUTE_B:
maxwell_compute->WriteReg(method, value);
break;
default:
UNIMPLEMENTED();
}
}
void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
// TODO(Subv): PhysicalToVirtualAddress is a misnomer, it converts a GPU VAddr into an
// application VAddr.
const VAddr head_address = memory_manager->PhysicalToVirtualAddress(address);
VAddr current_addr = head_address;
while (current_addr < head_address + size * sizeof(CommandHeader)) {
const CommandHeader header = {Memory::Read32(current_addr)};
current_addr += sizeof(u32);
switch (header.mode.Value()) {
case SubmissionMode::IncreasingOld:
case SubmissionMode::Increasing: {
// Increase the method value with each argument.
for (unsigned i = 0; i < header.arg_count; ++i) {
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr));
current_addr += sizeof(u32);
}
break;
}
case SubmissionMode::NonIncreasingOld:
case SubmissionMode::NonIncreasing: {
// Use the same method value for all arguments.
for (unsigned i = 0; i < header.arg_count; ++i) {
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
current_addr += sizeof(u32);
}
break;
}
case SubmissionMode::IncreaseOnce: {
ASSERT(header.arg_count.Value() >= 1);
// Use the original method for the first argument and then the next method for all other
// arguments.
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
current_addr += sizeof(u32);
// Use the same method value for all arguments.
for (unsigned i = 1; i < header.arg_count; ++i) {
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr));
current_addr += sizeof(u32);
}
break;
}
case SubmissionMode::Inline: {
// The register value is stored in the bits 16-28 as an immediate
WriteReg(header.method, header.subchannel, header.inline_data);
break;
}
default:
UNIMPLEMENTED();
}
}
}
} // namespace Tegra

View File

@@ -0,0 +1,39 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <type_traits>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Tegra {
enum class SubmissionMode : u32 {
IncreasingOld = 0,
Increasing = 1,
NonIncreasingOld = 2,
NonIncreasing = 3,
Inline = 4,
IncreaseOnce = 5
};
union CommandHeader {
u32 hex;
BitField<0, 13, u32> method;
BitField<13, 3, u32> subchannel;
BitField<16, 13, u32> arg_count;
BitField<16, 13, u32> inline_data;
BitField<29, 3, SubmissionMode> mode;
};
static_assert(std::is_standard_layout<CommandHeader>::value == true,
"CommandHeader does not use standard layout");
static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
void ProcessCommandList(VAddr address, u32 size);
} // namespace Tegra

View File

@@ -0,0 +1,13 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/engines/fermi_2d.h"
namespace Tegra {
namespace Engines {
void Fermi2D::WriteReg(u32 method, u32 value) {}
} // namespace Engines
} // namespace Tegra

View File

@@ -0,0 +1,22 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Tegra {
namespace Engines {
class Fermi2D final {
public:
Fermi2D() = default;
~Fermi2D() = default;
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value);
};
} // namespace Engines
} // namespace Tegra

View File

@@ -0,0 +1,51 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "video_core/engines/maxwell_3d.h"
namespace Tegra {
namespace Engines {
Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
void Maxwell3D::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid Maxwell3D register, increase the size of the Regs structure");
regs.reg_array[method] = value;
#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32))
switch (method) {
case MAXWELL3D_REG_INDEX(query.query_get): {
ProcessQueryGet();
break;
}
default:
break;
}
#undef MAXWELL3D_REG_INDEX
}
void Maxwell3D::ProcessQueryGet() {
GPUVAddr sequence_address = regs.query.QueryAddress();
// Since the sequence address is given as a GPU VAddr, we have to convert it to an application
// VAddr before writing.
VAddr address = memory_manager.PhysicalToVirtualAddress(sequence_address);
switch (regs.query.query_get.mode) {
case Regs::QueryMode::Write: {
// Write the current query sequence to the sequence address.
u32 sequence = regs.query.query_sequence;
Memory::Write32(address, sequence);
break;
}
default:
UNIMPLEMENTED_MSG("Query mode %u not implemented", regs.query.query_get.mode.Value());
}
}
} // namespace Engines
} // namespace Tegra

View File

@@ -0,0 +1,76 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/memory_manager.h"
namespace Tegra {
namespace Engines {
class Maxwell3D final {
public:
explicit Maxwell3D(MemoryManager& memory_manager);
~Maxwell3D() = default;
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value);
/// Register structure of the Maxwell3D engine.
/// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
struct Regs {
static constexpr size_t NUM_REGS = 0xE36;
enum class QueryMode : u32 {
Write = 0,
Sync = 1,
};
union {
struct {
INSERT_PADDING_WORDS(0x6C0);
struct {
u32 query_address_high;
u32 query_address_low;
u32 query_sequence;
union {
u32 raw;
BitField<0, 2, QueryMode> mode;
BitField<4, 1, u32> fence;
BitField<12, 4, u32> unit;
} query_get;
GPUVAddr QueryAddress() const {
return static_cast<GPUVAddr>(
(static_cast<GPUVAddr>(query_address_high) << 32) | query_address_low);
}
} query;
INSERT_PADDING_WORDS(0x772);
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
private:
/// Handles a write to the QUERY_GET register.
void ProcessQueryGet();
MemoryManager& memory_manager;
};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(Maxwell3D::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(query, 0x6C0);
#undef ASSERT_REG_POSITION
} // namespace Engines
} // namespace Tegra

View File

@@ -0,0 +1,13 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/engines/maxwell_compute.h"
namespace Tegra {
namespace Engines {
void MaxwellCompute::WriteReg(u32 method, u32 value) {}
} // namespace Engines
} // namespace Tegra

View File

@@ -0,0 +1,22 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Tegra {
namespace Engines {
class MaxwellCompute final {
public:
MaxwellCompute() = default;
~MaxwellCompute() = default;
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value);
};
} // namespace Engines
} // namespace Tegra

55
src/video_core/gpu.h Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <unordered_map>
#include "common/common_types.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_compute.h"
#include "video_core/memory_manager.h"
namespace Tegra {
enum class EngineID {
FERMI_TWOD_A = 0x902D, // 2D Engine
MAXWELL_B = 0xB197, // 3D Engine
MAXWELL_COMPUTE_B = 0xB1C0,
KEPLER_INLINE_TO_MEMORY_B = 0xA140,
MAXWELL_DMA_COPY_A = 0xB0B5,
};
class GPU final {
public:
GPU() {
memory_manager = std::make_unique<MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>();
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
}
~GPU() = default;
/// Processes a command list stored at the specified address in GPU memory.
void ProcessCommandList(GPUVAddr address, u32 size);
std::unique_ptr<MemoryManager> memory_manager;
private:
/// Writes a single register in the engine bound to the specified subchannel
void WriteReg(u32 method, u32 subchannel, u32 value);
/// Mapping of command subchannels to their bound engine ids.
std::unordered_map<u32, EngineID> bound_engines;
/// 3D engine
std::unique_ptr<Engines::Maxwell3D> maxwell_3d;
/// 2D engine
std::unique_ptr<Engines::Fermi2D> fermi_2d;
/// Compute engine
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
};
} // namespace Tegra

View File

@@ -3,10 +3,9 @@
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/service/nvdrv/memory_manager.h"
#include "video_core/memory_manager.h"
namespace Service {
namespace Nvidia {
namespace Tegra {
PAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
boost::optional<PAddr> paddr = FindFreeBlock(size, align);
@@ -108,5 +107,4 @@ VAddr& MemoryManager::PageSlot(PAddr paddr) {
return (*block)[(paddr >> Memory::PAGE_BITS) & PAGE_BLOCK_MASK];
}
} // namespace Nvidia
} // namespace Service
} // namespace Tegra

View File

@@ -9,8 +9,10 @@
#include "common/common_types.h"
#include "core/memory.h"
namespace Service {
namespace Nvidia {
namespace Tegra {
/// Virtual addresses in the GPU's memory map are 64 bit.
using GPUVAddr = u64;
class MemoryManager final {
public:
@@ -44,5 +46,4 @@ private:
std::array<std::unique_ptr<PageBlock>, PAGE_TABLE_SIZE> page_table{};
};
} // namespace Nvidia
} // namespace Service
} // namespace Tegra

View File

@@ -5,6 +5,7 @@
#include "yuzu/debugger/wait_tree.h"
#include "yuzu/util/util.h"
#include "core/core.h"
#include "core/hle/kernel/condition_variable.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
@@ -50,7 +51,7 @@ std::size_t WaitTreeItem::Row() const {
}
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
const auto& threads = Kernel::GetThreadList();
const auto& threads = Core::System::GetInstance().Scheduler().GetThreadList();
std::vector<std::unique_ptr<WaitTreeThread>> item_list;
item_list.reserve(threads.size());
for (std::size_t i = 0; i < threads.size(); ++i) {
@@ -168,7 +169,7 @@ QString WaitTreeThread::GetText() const {
}
QString pc_info = tr(" PC = 0x%1 LR = 0x%2")
.arg(thread.context.pc, 8, 16, QLatin1Char('0'))
.arg(thread.context.cpu_registers[31], 8, 16, QLatin1Char('0'));
.arg(thread.context.cpu_registers[30], 8, 16, QLatin1Char('0'));
return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") ";
}

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