Compare commits
56 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08c6ac02cf | ||
|
|
2b41c6e573 | ||
|
|
7f0ecbf859 | ||
|
|
fa28dbe0f3 | ||
|
|
944132dbe5 | ||
|
|
1cdc74c5e9 | ||
|
|
6a2197806e | ||
|
|
587f22b610 | ||
|
|
46931a9566 | ||
|
|
678574972a | ||
|
|
7bee3427d0 | ||
|
|
1d491d636d | ||
|
|
23fe6f5be3 | ||
|
|
8db80d8389 | ||
|
|
ad55d22130 | ||
|
|
5ab285f1f9 | ||
|
|
ac81c02ed9 | ||
|
|
c78d495161 | ||
|
|
2d4a6883bc | ||
|
|
cec0d4f191 | ||
|
|
de43db3400 | ||
|
|
ec39c9eb32 | ||
|
|
5babad5de5 | ||
|
|
94ee8fc97b | ||
|
|
b36ce74d18 | ||
|
|
f6e548fbc0 | ||
|
|
416f692f6e | ||
|
|
d758332425 | ||
|
|
2662de6e52 | ||
|
|
1b64160d83 | ||
|
|
bd6432f1ff | ||
|
|
3388208597 | ||
|
|
947831ff1e | ||
|
|
6797d4a907 | ||
|
|
98ffad4303 | ||
|
|
7a1917e0fd | ||
|
|
35d0d06885 | ||
|
|
d18446f63a | ||
|
|
b78ffc4abf | ||
|
|
8dee5663b3 | ||
|
|
df008a159b | ||
|
|
42c062c620 | ||
|
|
fa58d95027 | ||
|
|
e017184445 | ||
|
|
db873a232c | ||
|
|
88bfec37ce | ||
|
|
756e9f1484 | ||
|
|
6fd19f05f1 | ||
|
|
516a95721c | ||
|
|
d6e52581ac | ||
|
|
f16bb1dfcf | ||
|
|
d42e77797e | ||
|
|
8f84665775 | ||
|
|
fc1b425520 | ||
|
|
1ce6fff064 | ||
|
|
4f8ee5e456 |
13
.travis.yml
13
.travis.yml
@@ -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
3
.travis/clang-format/build.sh
Executable 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
3
.travis/clang-format/deps.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
docker pull ubuntu:18.04
|
||||
8
.travis/clang-format/docker.sh
Executable file
8
.travis/clang-format/docker.sh
Executable 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
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: d7323d6799...e585e1d49e
@@ -35,11 +35,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) \
|
||||
|
||||
@@ -51,12 +51,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
|
||||
|
||||
@@ -28,8 +28,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 +53,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 +112,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 +128,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
|
||||
|
||||
@@ -133,22 +133,24 @@ 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;
|
||||
@@ -158,9 +160,9 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
|
||||
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();
|
||||
|
||||
@@ -188,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");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#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"
|
||||
@@ -107,6 +108,10 @@ public:
|
||||
return *gpu_core;
|
||||
}
|
||||
|
||||
Kernel::Scheduler& Scheduler() {
|
||||
return *scheduler;
|
||||
}
|
||||
|
||||
PerfStats perf_stats;
|
||||
FrameLimiter frame_limiter;
|
||||
|
||||
@@ -140,9 +145,8 @@ 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
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
134
src/core/hle/kernel/scheduler.cpp
Normal file
134
src/core/hle/kernel/scheduler.cpp
Normal 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
|
||||
73
src/core/hle/kernel/scheduler.h
Normal file
73
src/core/hle/kernel/scheduler.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -792,7 +803,7 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x11, nullptr, "SignalEvent"},
|
||||
{0x12, nullptr, "ClearEvent"},
|
||||
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
|
||||
{0x14, nullptr, "UnmapSharedMemory"},
|
||||
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
|
||||
{0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"},
|
||||
{0x16, SvcWrap<CloseHandle>, "CloseHandle"},
|
||||
{0x17, SvcWrap<ResetSignal>, "ResetSignal"},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -41,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;
|
||||
|
||||
@@ -63,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
|
||||
@@ -86,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;
|
||||
@@ -109,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;
|
||||
@@ -229,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,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:
|
||||
@@ -307,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.
|
||||
@@ -399,8 +261,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||
|
||||
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;
|
||||
@@ -471,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;
|
||||
}
|
||||
|
||||
@@ -490,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;
|
||||
}
|
||||
|
||||
@@ -520,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;
|
||||
}
|
||||
@@ -561,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -306,11 +306,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 +378,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) {
|
||||
|
||||
@@ -2,16 +2,37 @@
|
||||
// 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, nullptr, "CountAddOnContent"},
|
||||
{3, &AOC_U::ListAddOnContent, "ListAddOnContent"},
|
||||
{4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
|
||||
{5, nullptr, "GetAddOnContentBaseId"},
|
||||
{6, nullptr, "PrepareAddOnContentByApplicationId"},
|
||||
{7, nullptr, "PrepareAddOnContent"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -13,6 +13,9 @@ class AOC_U final : public ServiceFramework<AOC_U> {
|
||||
public:
|
||||
AOC_U();
|
||||
~AOC_U() = default;
|
||||
|
||||
private:
|
||||
void ListAddOnContent(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
/// Registers all AOC services with the specified service manager.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -53,7 +53,8 @@ private:
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -69,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};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
28
src/core/hle/service/friend/friend.cpp
Normal file
28
src/core/hle/service/friend/friend.cpp
Normal 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
|
||||
29
src/core/hle/service/friend/friend.h
Normal file
29
src/core/hle/service/friend/friend.h
Normal 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
|
||||
19
src/core/hle/service/friend/friend_a.cpp
Normal file
19
src/core/hle/service/friend/friend_a.cpp
Normal 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
|
||||
18
src/core/hle/service/friend/friend_a.h
Normal file
18
src/core/hle/service/friend/friend_a.h
Normal 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
|
||||
@@ -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() {}
|
||||
|
||||
16
src/core/hle/service/ns/ns.cpp
Normal file
16
src/core/hle/service/ns/ns.cpp
Normal 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
|
||||
16
src/core/hle/service/ns/ns.h
Normal file
16
src/core/hle/service/ns/ns.h
Normal 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
|
||||
111
src/core/hle/service/ns/pl_u.cpp
Normal file
111
src/core/hle/service/ns/pl_u.cpp
Normal 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
|
||||
33
src/core/hle/service/ns/pl_u.h
Normal file
33
src/core/hle/service/ns/pl_u.h
Normal 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
|
||||
@@ -17,6 +17,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
|
||||
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(¶ms, 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(), ¶ms, sizeof(params));
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Devices
|
||||
} // namespace Nvidia
|
||||
} // namespace Service
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -103,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(), ¶ms, sizeof(params));
|
||||
return 0;
|
||||
|
||||
@@ -15,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};
|
||||
@@ -33,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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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)) {}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,10 +277,11 @@ public:
|
||||
Data data;
|
||||
};
|
||||
|
||||
// TODO(bunnei): Remove this. When set to 1, games will think a fence is valid and boot further.
|
||||
// This will break libnx and potentially other apps that more stringently check this. This is here
|
||||
// purely as a convenience, and should go away once we implement fences.
|
||||
static constexpr u32 FENCE_HACK = 0;
|
||||
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:
|
||||
@@ -274,20 +290,16 @@ public:
|
||||
|
||||
protected:
|
||||
void SerializeData() override {
|
||||
// TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx.
|
||||
Write<u32>(0);
|
||||
Write<u32>(FENCE_HACK);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
Write<u32>(0);
|
||||
// TODO(Subv): Find out how this Fence is used.
|
||||
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);
|
||||
}
|
||||
|
||||
u32_le slot;
|
||||
@@ -316,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>(FENCE_HACK);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -429,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);
|
||||
}
|
||||
@@ -453,95 +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);
|
||||
|
||||
if (transaction == TransactionId::Connect) {
|
||||
IGBPConnectRequestParcel request{input_data};
|
||||
IGBPConnectResponseParcel response{1280, 720};
|
||||
std::vector<u8> response_buffer = response.Serialize();
|
||||
Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
|
||||
} else if (transaction == TransactionId::SetPreallocatedBuffer) {
|
||||
IGBPSetPreallocatedBufferRequestParcel request{input_data};
|
||||
|
||||
buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer);
|
||||
|
||||
IGBPSetPreallocatedBufferResponseParcel response{};
|
||||
std::vector<u8> response_buffer = response.Serialize();
|
||||
Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
|
||||
} 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};
|
||||
std::vector<u8> response_buffer = response.Serialize();
|
||||
Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
|
||||
} else if (transaction == TransactionId::RequestBuffer) {
|
||||
IGBPRequestBufferRequestParcel request{input_data};
|
||||
|
||||
auto& buffer = buffer_queue->RequestBuffer(request.slot);
|
||||
|
||||
IGBPRequestBufferResponseParcel response{buffer};
|
||||
std::vector<u8> response_buffer = response.Serialize();
|
||||
Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
|
||||
} else if (transaction == TransactionId::QueueBuffer) {
|
||||
IGBPQueueBufferRequestParcel request{input_data};
|
||||
|
||||
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
|
||||
|
||||
IGBPQueueBufferResponseParcel response{1280, 720};
|
||||
std::vector<u8> response_buffer = response.Serialize();
|
||||
Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
|
||||
} else if (transaction == TransactionId::Query) {
|
||||
IGBPQueryRequestParcel request{input_data};
|
||||
|
||||
u32 value =
|
||||
buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
|
||||
|
||||
IGBPQueryResponseParcel response{value};
|
||||
std::vector<u8> response_buffer = response.Serialize();
|
||||
Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
|
||||
} else if (transaction == TransactionId::CancelBuffer) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
|
||||
} else {
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -719,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) {
|
||||
@@ -741,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) {
|
||||
@@ -781,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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user