Compare commits

..

5 Commits

Author SHA1 Message Date
Vishal Sharma
3e5ffa3e31 Two params per line for travis 2018-03-09 01:30:36 +05:30
Vishal Sharma
3ab0a703b5 Completes all the changes for hle handler building successfully 2018-03-09 01:20:38 +05:30
Vishal Sharma
6f35472e41 Corrections for travis 2018-02-21 20:47:15 +05:30
Vishal Sharma
943a895ae2 Corrections for travis fail 2018-02-21 20:40:02 +05:30
visha
b42faa8f85 Putting guest thread to sleep inside handler 2018-02-13 22:24:30 +05:30
81 changed files with 769 additions and 1783 deletions

View File

@@ -3,11 +3,16 @@ matrix:
include:
- os: linux
env: NAME="clang-format"
sudo: required
dist: trusty
services: docker
install: "./.travis/clang-format/deps.sh"
script: "./.travis/clang-format/build.sh"
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"
- os: linux
env: NAME="linux build"
sudo: required

View File

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

View File

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

View File

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

View File

@@ -35,14 +35,11 @@ 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) \

View File

@@ -51,15 +51,12 @@ 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

View File

@@ -12,8 +12,6 @@ add_library(core STATIC
file_sys/filesystem.h
file_sys/path_parser.cpp
file_sys/path_parser.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/romfs_factory.cpp
file_sys/romfs_factory.h
file_sys/romfs_filesystem.cpp
@@ -30,6 +28,8 @@ 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,8 +55,6 @@ 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
@@ -114,10 +112,6 @@ 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
@@ -130,10 +124,6 @@ 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

View File

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

View File

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

View File

@@ -133,24 +133,22 @@ void System::Reschedule() {
}
reschedule_pending = false;
Core::System::GetInstance().Scheduler().Reschedule();
Kernel::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_shared<ARM_Unicorn>();
cpu_core = std::make_unique<ARM_Unicorn>();
break;
case Settings::CpuCore::Dynarmic:
default:
#ifdef ARCHITECTURE_x86_64
cpu_core = std::make_shared<ARM_Dynarmic>();
cpu_core = std::make_unique<ARM_Dynarmic>();
#else
cpu_core = std::make_shared<ARM_Unicorn>();
cpu_core = std::make_unique<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
break;
@@ -160,9 +158,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();
@@ -190,18 +188,15 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
VideoCore::Shutdown();
GDBStub::Shutdown();
VideoCore::Shutdown();
Service::Shutdown();
scheduler = nullptr;
Kernel::Shutdown();
HW::Shutdown();
telemetry_session = nullptr;
gpu_core = nullptr;
cpu_core = nullptr;
CoreTiming::Shutdown();
cpu_core = nullptr;
app_loader = nullptr;
telemetry_session = nullptr;
LOG_DEBUG(Core, "Shutdown OK");
}

View File

@@ -7,7 +7,6 @@
#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"
@@ -108,10 +107,6 @@ public:
return *gpu_core;
}
Kernel::Scheduler& Scheduler() {
return *scheduler;
}
PerfStats perf_stats;
FrameLimiter frame_limiter;
@@ -145,8 +140,9 @@ private:
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::shared_ptr<ARM_Interface> cpu_core;
std::unique_ptr<Kernel::Scheduler> scheduler;
///< ARM11 CPU core
std::unique_ptr<ARM_Interface> cpu_core;
std::unique_ptr<Tegra::GPU> gpu_core;
/// When true, signals that a reschedule should happen

View File

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

View File

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

View File

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

View File

@@ -14,6 +14,8 @@ namespace IPC {
/// Size of the command buffer area, in 32-bit words.
constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
/// Maximum number of static buffers per thread
constexpr size_t MAX_STATIC_BUFFERS = 16;
// These errors are commonly returned by invalid IPC translations, so alias them here for
// convenience.
// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
@@ -91,10 +93,6 @@ struct BufferDescriptorX {
address |= static_cast<VAddr>(address_bits_36_38) << 36;
return address;
}
u64 Size() const {
return static_cast<u64>(size);
}
};
static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect");

View File

@@ -118,8 +118,7 @@ public:
AlignWithPadding();
const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
if (context.Session()->IsDomain() && request_has_domain_header) {
if (context.Session()->IsDomain()) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);

View File

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

View File

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

View File

@@ -7,12 +7,12 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#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 {
@@ -26,13 +26,47 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
boost::range::remove_erase(connected_sessions, server_session);
}
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
HLERequestContext::HLERequestContext(SharedPtr<ServerSession> server_session)
: server_session(std::move(server_session)) {
cmd_buf[0] = 0;
}
HLERequestContext::~HLERequestContext() = default;
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
const std::string& reason, u64 timeout,
WakeupCallback&& callback) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->wakeup_callback =
[context = this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, size_t index) mutable -> bool {
ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
callback(thread, *context, reason);
auto& process = thread->owner_process;
// We must copy the entire command buffer *plus* the entire static buffers area, since
// the translation might need to read from it in order to retrieve the StaticBuffer
// target addresses.
std::array<u32, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buff;
Memory::ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
cmd_buff.size() * sizeof(u32));
context->WriteToOutgoingCommandBuffer(cmd_buff.data(), *process, Kernel::g_handle_table);
// Copy the translated command buffer back into the thread's command buffer area.
Memory::WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
cmd_buff.size() * sizeof(u32));
};
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
thread->wait_objects = {event};
event->AddWaitingThread(thread);
if (timeout > 0)
thread->WakeAfterDelay(timeout);
return event;
}
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
@@ -85,14 +119,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
// If this is an incoming message, only CommandType "Request" has a domain header
// All outgoing domain messages have the domain header, if only incoming has it
if (incoming || domain_message_header) {
domain_message_header =
std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
} else {
if (Session()->IsDomain())
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
}
// All outgoing domain messages have the domain header
domain_message_header =
std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
}
data_payload_header =
@@ -201,7 +230,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
// TODO(Subv): Translate the X/A/B/W buffers.
if (Session()->IsDomain() && domain_message_header) {
if (Session()->IsDomain()) {
ASSERT(domain_message_header->num_objects == domain_objects.size());
// Write the domain objects to the command buffer, these go after the raw untranslated data.
// TODO(Subv): This completely ignores C buffers.
@@ -216,98 +245,4 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
return RESULT_SUCCESS;
}
std::vector<u8> HLERequestContext::ReadBuffer() const {
std::vector<u8> buffer;
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
if (is_buffer_a) {
buffer.resize(BufferDescriptorA()[0].Size());
Memory::ReadBlock(BufferDescriptorA()[0].Address(), buffer.data(), buffer.size());
} else {
buffer.resize(BufferDescriptorX()[0].Size());
Memory::ReadBlock(BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
}
return buffer;
}
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %d is too big", size);
if (is_buffer_b) {
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);
} else {
Memory::WriteBlock(BufferDescriptorC()[0].Address(), buffer, size);
}
return size;
}
size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer) const {
return WriteBuffer(buffer.data(), buffer.size());
}
size_t HLERequestContext::GetReadBufferSize() const {
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
return is_buffer_a ? BufferDescriptorA()[0].Size() : BufferDescriptorX()[0].Size();
}
size_t HLERequestContext::GetWriteBufferSize() const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size();
}
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";
}
std::ostringstream s;
s << "IPC::CommandHeader: Type:" << static_cast<u32>(command_header->type.Value());
s << ", X(Pointer):" << command_header->num_buf_x_descriptors;
if (command_header->num_buf_x_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorX()[i].Size();
if (i < command_header->num_buf_x_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", A(Send):" << command_header->num_buf_a_descriptors;
if (command_header->num_buf_a_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_a_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorA()[i].Size();
if (i < command_header->num_buf_a_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", B(Receive):" << command_header->num_buf_b_descriptors;
if (command_header->num_buf_b_descriptors) {
s << '[';
for (u64 i = 0; i < command_header->num_buf_b_descriptors; ++i) {
s << "0x" << std::hex << BufferDescriptorB()[i].Size();
if (i < command_header->num_buf_b_descriptors - 1)
s << ", ";
}
s << ']';
}
s << ", C(ReceiveList):" << BufferDescriptorC().size();
if (!BufferDescriptorC().empty()) {
s << '[';
for (u64 i = 0; i < BufferDescriptorC().size(); ++i) {
s << "0x" << std::hex << BufferDescriptorC()[i].Size();
if (i < BufferDescriptorC().size() - 1)
s << ", ";
}
s << ']';
}
s << ", data_size:" << command_header->data_size.Value();
return s.str();
}
} // namespace Kernel

View File

@@ -6,6 +6,7 @@
#include <array>
#include <memory>
#include <string>
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
@@ -13,7 +14,7 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
namespace Service {
class ServiceFrameworkBase;
}
@@ -24,6 +25,8 @@ class Domain;
class HandleTable;
class HLERequestContext;
class Process;
class Thread;
class Event;
/**
* Interface implemented by HLE Session handlers.
@@ -98,10 +101,26 @@ public:
* Returns the session through which this request was made. This can be used as a map key to
* access per-client data on services.
*/
const SharedPtr<Kernel::ServerSession>& Session() const {
SharedPtr<Kernel::ServerSession> Session() const {
return server_session;
}
using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context,
ThreadWakeupReason reason)>;
/*
* Puts the specified guest thread to sleep until the returned event is signaled or until the
* specified timeout expires.
* @param thread Thread to be put to sleep.
* @param reason Reason for pausing the thread, to be used for debugging purposes.
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
* invoked with a Timeout reason.
* @param callback Callback to be invoked when the thread is resumed. This callback must write
* the entire command response once again, regardless of the state of it before this function
* was called.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
u64 timeout, WakeupCallback&& callback);
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
/// Populates this context with data from the requesting process/thread.
@@ -143,21 +162,6 @@ 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());
@@ -202,8 +206,6 @@ public:
return domain_objects.size();
}
std::string Description() const;
private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::ServerSession> server_session;

View File

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

View File

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

View File

@@ -57,33 +57,6 @@ 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
@@ -94,39 +67,46 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
if (IsDomain() && context.GetDomainMessageHeader()) {
result = HandleDomainSyncRequest(context);
// If the session has been converted to a domain, handle the doomain 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);
}
// 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 (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;
// 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 (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));
}
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 this ServerSession does not have an HLE implementation, just wake up the threads waiting
@@ -160,4 +140,9 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n
return std::make_tuple(std::move(server_session), std::move(client_session));
}
ResultCode TranslateHLERequest(ServerSession* server_session) {
// TODO(Subv): Implement this function once multiple concurrent processes are supported.
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

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

View File

@@ -111,6 +111,13 @@ 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)) {
@@ -119,6 +126,12 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return ERR_WRONG_PERMISSION;
}
// TODO(Subv): Check for the Shared Device Mem flag in the creator process.
/*if (was_created_with_shared_device_mem && address != 0) {
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}*/
// TODO(Subv): The same process that created a SharedMemory object
// can not map it in its own address space unless it was created with addr=0, result 0xD900182C.

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -444,16 +443,6 @@ 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) {
@@ -494,7 +483,7 @@ static void ExitProcess() {
g_current_process->status = ProcessStatus::Exited;
// Stop all the process threads that are currently waiting for objects.
auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
auto& thread_list = GetThreadList();
for (auto& thread : thread_list) {
if (thread->owner_process != g_current_process)
continue;
@@ -596,7 +585,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 && !Core::System::GetInstance().Scheduler().HaveReadyThreads())
if (nanoseconds == 0 && !HaveReadyThreads())
return;
// Sleep current thread and check for next thread to schedule
@@ -772,16 +761,6 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
return RESULT_SUCCESS;
}
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0xX", handle);
SharedPtr<Event> evt = g_handle_table.Get<Event>(handle);
if (evt == nullptr)
return ERR_INVALID_HANDLE;
evt->Clear();
return RESULT_SUCCESS;
}
namespace {
struct FunctionDef {
using Func = void();
@@ -811,9 +790,9 @@ static const FunctionDef SVC_Table[] = {
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
{0x11, nullptr, "SignalEvent"},
{0x12, SvcWrap<ClearEvent>, "ClearEvent"},
{0x12, nullptr, "ClearEvent"},
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
{0x14, nullptr, "UnmapSharedMemory"},
{0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"},
{0x16, SvcWrap<CloseHandle>, "CloseHandle"},
{0x17, SvcWrap<ResetSignal>, "ResetSignal"},

View File

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

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <list>
#include <vector>
#include "common/assert.h"
@@ -41,6 +40,14 @@ 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;
@@ -55,6 +62,10 @@ 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
@@ -74,7 +85,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) {
Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority);
ready_queue.remove(current_priority, this);
}
status = THREADSTATUS_DEAD;
@@ -97,6 +108,112 @@ 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;
@@ -111,7 +228,8 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
void ExitCurrentThread() {
Thread* thread = GetCurrentThread();
thread->Stop();
Core::System::GetInstance().Scheduler().RemoveThread(thread);
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
/**
@@ -129,7 +247,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
bool resume = true;
if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB ||
thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
@@ -165,7 +284,6 @@ void Thread::ResumeFromWait() {
case THREADSTATUS_WAIT_SYNCH_ANY:
case THREADSTATUS_WAIT_ARB:
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
break;
case THREADSTATUS_READY:
@@ -189,11 +307,31 @@ 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.
@@ -242,7 +380,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
SharedPtr<Process> owner_process) {
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: %u", priority);
LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority);
return ERR_OUT_OF_RANGE;
}
@@ -254,14 +392,15 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
// TODO(yuriks): Other checks, returning 0xD9001BEA
if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %016" PRIx64, name.c_str(), entry_point);
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
// TODO (bunnei): Find the correct error code to use here
return ResultCode(-1);
}
SharedPtr<Thread> thread(new Thread);
Core::System::GetInstance().Scheduler().AddThread(thread, priority);
thread_list.push_back(thread);
ready_queue.prepare(priority);
thread->thread_id = NewThreadId();
thread->status = THREADSTATUS_DORMANT;
@@ -332,7 +471,12 @@ 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.");
Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
// If thread was ready, adjust queues
if (status == THREADSTATUS_READY)
ready_queue.move(this, current_priority, priority);
else
ready_queue.prepare(priority);
nominal_priority = current_priority = priority;
}
@@ -346,7 +490,11 @@ void Thread::UpdatePriority() {
}
void Thread::BoostPriority(u32 priority) {
Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
// If thread was ready, adjust queues
if (status == THREADSTATUS_READY)
ready_queue.move(this, current_priority, priority);
else
ready_queue.prepare(priority);
current_priority = priority;
}
@@ -372,6 +520,25 @@ 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;
}
@@ -394,20 +561,25 @@ 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() {
Kernel::ClearProcessList();
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;
}
} // namespace Kernel

View File

@@ -39,8 +39,8 @@ enum ThreadStatus {
THREADSTATUS_RUNNING, ///< Currently running
THREADSTATUS_READY, ///< Ready to run
THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish
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
@@ -249,6 +249,28 @@ 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
*/
@@ -280,4 +302,9 @@ void ThreadingInit();
*/
void ThreadingShutdown();
/**
* Get a const reference to the thread list for debug use
*/
const std::vector<SharedPtr<Thread>>& GetThreadList();
} // namespace Kernel

View File

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

View File

@@ -65,19 +65,12 @@ 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};
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());
const auto& output_buffer = ctx.BufferDescriptorC()[0];
Memory::WriteBlock(output_buffer.Address(), 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) {
@@ -111,7 +104,6 @@ ACC_U0::ACC_U0() : ServiceFramework("acc:u0") {
static const FunctionInfo functions[] = {
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
{2, &ACC_U0::ListAllUsers, "ListAllUsers"},
{3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},
{4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_U0::GetProfile, "GetProfile"},
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -99,6 +99,8 @@ 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
@@ -110,7 +112,7 @@ private:
queue_keys.pop_back();
}
ctx.WriteBuffer(&key, sizeof(u64));
Memory::WriteBlock(buffer.Address(), &key, sizeof(u64));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -156,8 +158,10 @@ 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";
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
Memory::WriteBlock(buffer.Address(), &audio_interface[0], audio_interface.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -12,9 +11,6 @@
namespace Service {
namespace Audio {
/// TODO(bunnei): Find a proper value for the audio_ticks
constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 200)};
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
IAudioRenderer() : ServiceFramework("IAudioRenderer") {
@@ -24,8 +20,8 @@ public:
{0x2, nullptr, "GetAudioRendererMixBufferCount"},
{0x3, nullptr, "GetAudioRendererState"},
{0x4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"},
{0x5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"},
{0x6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"},
{0x5, nullptr, "StartAudioRenderer"},
{0x6, nullptr, "StopAudioRenderer"},
{0x7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
{0x8, nullptr, "SetAudioRendererRenderingTimeLimit"},
{0x9, nullptr, "GetAudioRendererRenderingTimeLimit"},
@@ -34,60 +30,11 @@ public:
system_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent");
// Register event callback to update the Audio Buffer
audio_event = CoreTiming::RegisterEvent(
"IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
UpdateAudioCallback();
CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
});
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
~IAudioRenderer() = default;
private:
void UpdateAudioCallback() {
system_event->Signal();
}
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "%s", ctx.Description().c_str());
AudioRendererResponseData response_data{};
response_data.section_0_size =
response_data.state_entries.size() * sizeof(AudioRendererStateEntry);
response_data.section_1_size = response_data.section_1.size();
response_data.section_2_size = response_data.section_2.size();
response_data.section_3_size = response_data.section_3.size();
response_data.section_4_size = response_data.section_4.size();
response_data.section_5_size = response_data.section_5.size();
response_data.total_size = sizeof(AudioRendererResponseData);
for (unsigned i = 0; i < response_data.state_entries.size(); i++) {
// 4 = Busy and 5 = Ready?
response_data.state_entries[i].state = 5;
}
ctx.WriteBuffer(&response_data, response_data.total_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void StartAudioRenderer(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void StopAudioRenderer(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -105,56 +52,15 @@ private:
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
struct AudioRendererStateEntry {
u32_le state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
static_assert(sizeof(AudioRendererStateEntry) == 0x10,
"AudioRendererStateEntry has wrong size");
struct AudioRendererResponseData {
u32_le unknown_0;
u32_le section_5_size;
u32_le section_0_size;
u32_le section_1_size;
u32_le unknown_10;
u32_le section_2_size;
u32_le unknown_18;
u32_le section_3_size;
u32_le section_4_size;
u32_le unknown_24;
u32_le unknown_28;
u32_le unknown_2c;
u32_le unknown_30;
u32_le unknown_34;
u32_le unknown_38;
u32_le total_size;
std::array<AudioRendererStateEntry, 0x18e> state_entries;
std::array<u8, 0x600> section_1;
std::array<u8, 0xe0> section_2;
std::array<u8, 0x20> section_3;
std::array<u8, 0x10> section_4;
std::array<u8, 0xb0> section_5;
};
static_assert(sizeof(AudioRendererResponseData) == 0x20e0,
"AudioRendererResponseData has wrong size");
/// This is used to trigger the audio event callback.
CoreTiming::EventType* audio_event;
Kernel::SharedPtr<Kernel::Event> system_event;
};
AudRenU::AudRenU() : ServiceFramework("audren:u") {
static const FunctionInfo functions[] = {
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
{1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
{2, &AudRenU::GetAudioRenderersProcessMasterVolume, "GetAudioRenderersProcessMasterVolume"},
{3, nullptr, "SetAudioRenderersProcessMasterVolume"},
{0x00000000, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
{0x00000001, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
{0x00000002, nullptr, "GetAudioRenderersProcessMasterVolume"},
{0x00000003, nullptr, "SetAudioRenderersProcessMasterVolume"},
};
RegisterHandlers(functions);
}
@@ -172,17 +78,9 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0x400);
rb.Push<u64>(0x1000);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void AudRenU::GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(100);
LOG_WARNING(Service_Audio, "(STUBBED) called");
LOG_WARNING(Service_Audio, "called");
}
} // namespace Audio

View File

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

View File

@@ -33,10 +33,12 @@ 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));
@@ -58,7 +60,7 @@ private:
}
// Write the data to memory
ctx.WriteBuffer(output);
Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

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

View File

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

View File

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

View File

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

View File

@@ -176,10 +176,7 @@ 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"},
@@ -187,15 +184,9 @@ 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);
@@ -231,30 +222,12 @@ 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);
@@ -293,49 +266,18 @@ private:
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IActiveVibrationDeviceList>();
LOG_DEBUG(Service_HID, "called");
}
void SendVibrationValues(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
void ReloadInputDevices() {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -14,8 +13,8 @@ namespace Nvidia {
namespace Devices {
u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx",
command.raw, input.size(), output.size());
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%llx, output_size=0x%llx",
command, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocInitalizeExCommand:
@@ -63,8 +62,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV,
"called, flags=%x, nvmap_handle=%x, buffer_offset=%" PRIu64 ", mapping_size=%" PRIu64
", offset=%" PRIu64,
"called, flags=%x, nvmap_handle=%x, buffer_offset=%lx, mapping_size=%lx, offset=%lx",
params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
params.offset);
@@ -99,8 +97,8 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou
u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr=%" PRIu64 ", buf_size=%x",
params.buf_addr, params.buf_size);
LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr=%lx, buf_size=%x", params.buf_addr,
params.buf_size);
params.buf_size = 0x30;
params.regions[0].offset = 0x04000000;

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <map>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -14,8 +13,8 @@ namespace Nvidia {
namespace Devices {
u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx",
command.raw, input.size(), output.size());
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%llx, output_size=0x%llx",
command, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
@@ -75,8 +74,7 @@ u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& out
u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&zcull_params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, gpu_va=%" PRIx64 ", mode=%x", zcull_params.gpu_va,
zcull_params.mode);
LOG_DEBUG(Service_NVDRV, "called, gpu_va=%lx, mode=%x", zcull_params.gpu_va, zcull_params.mode);
std::memcpy(output.data(), &zcull_params, output.size());
return 0;
}
@@ -84,8 +82,8 @@ u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output)
u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetErrorNotifier params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset=%" PRIx64 ", size=%" PRIx64 ", mem=%x",
params.offset, params.size, params.mem);
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset=%lx, size=%lx, mem=%x", params.offset,
params.size, params.mem);
std::memcpy(output.data(), &params, output.size());
return 0;
}
@@ -125,7 +123,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
UNIMPLEMENTED();
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo=%" PRIx64 ", num_entries=%x, flags=%x",
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo=%lx, num_entries=%x, flags=%x",
params.gpfifo, params.num_entries, params.flags);
auto entries = std::vector<IoctlGpfifoEntry>();

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,11 +20,9 @@
#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"
@@ -152,9 +150,15 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
}
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
auto thread = Kernel::GetCurrentThread();
ASSERT(thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_WAIT_HLE_EVENT);
// Only write the response immediately if the thread is still running. If the HLE handler put
// the thread to sleep then the writing of the command buffer will be deferred to the wakeup
// callback.
if (thread->status == THREADSTATUS_RUNNING) {
context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
}
return RESULT_SUCCESS;
}
@@ -181,11 +185,9 @@ void Init() {
APM::InstallInterfaces(*SM::g_service_manager);
Audio::InstallInterfaces(*SM::g_service_manager);
FileSystem::InstallInterfaces(*SM::g_service_manager);
Friend::InstallInterfaces(*SM::g_service_manager);
HID::InstallInterfaces(*SM::g_service_manager);
LM::InstallInterfaces(*SM::g_service_manager);
NIFM::InstallInterfaces(*SM::g_service_manager);
NS::InstallInterfaces(*SM::g_service_manager);
Nvidia::InstallInterfaces(*SM::g_service_manager);
PCTL::InstallInterfaces(*SM::g_service_manager);
Sockets::InstallInterfaces(*SM::g_service_manager);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,6 @@
#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"
@@ -39,7 +38,6 @@ 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);
@@ -49,7 +47,6 @@ 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);
@@ -57,7 +54,6 @@ 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);
@@ -90,18 +86,7 @@ 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));
@@ -277,12 +262,6 @@ public:
Data data;
};
struct BufferProducerFence {
u32 is_valid;
std::array<Nvidia::IoctlFence, 4> fences;
};
static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size");
class IGBPDequeueBufferResponseParcel : public Parcel {
public:
explicit IGBPDequeueBufferResponseParcel(u32 slot) : Parcel(), slot(slot) {}
@@ -290,15 +269,10 @@ public:
protected:
void SerializeData() override {
// 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);
// TODO(Subv): Find out how this Fence is used.
std::array<u32_le, 11> fence = {};
Write(fence);
Write<u32_le>(0);
}
@@ -328,10 +302,11 @@ public:
protected:
void SerializeData() override {
// 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);
// TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx.
Write<u32_le>(0);
Write<u32_le>(0);
Write<u32_le>(0);
Write(buffer);
Write<u32_le>(0);
}
@@ -440,7 +415,7 @@ public:
{0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
{1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
{2, &IHOSBinderDriver::GetNativeHandle, "GetNativeHandle"},
{3, &IHOSBinderDriver::TransactParcel, "TransactParcelAuto"},
{3, &IHOSBinderDriver::TransactParcelAuto, "TransactParcelAuto"},
};
RegisterHandlers(functions);
}
@@ -464,62 +439,91 @@ private:
SetPreallocatedBuffer = 14
};
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>();
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);
LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
std::vector<u8> response_buffer;
if (transaction == TransactionId::Connect) {
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
IGBPConnectRequestParcel request{input_data};
IGBPConnectResponseParcel response{1280, 720};
ctx.WriteBuffer(response.Serialize());
response_buffer = response.Serialize();
} else if (transaction == TransactionId::SetPreallocatedBuffer) {
IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
IGBPSetPreallocatedBufferRequestParcel request{input_data};
buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer);
IGBPSetPreallocatedBufferResponseParcel response{};
ctx.WriteBuffer(response.Serialize());
response_buffer = response.Serialize();
} else if (transaction == TransactionId::DequeueBuffer) {
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
IGBPDequeueBufferRequestParcel request{input_data};
u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width,
request.data.height);
IGBPDequeueBufferResponseParcel response{slot};
ctx.WriteBuffer(response.Serialize());
response_buffer = response.Serialize();
} else if (transaction == TransactionId::RequestBuffer) {
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
IGBPRequestBufferRequestParcel request{input_data};
auto& buffer = buffer_queue->RequestBuffer(request.slot);
IGBPRequestBufferResponseParcel response{buffer};
ctx.WriteBuffer(response.Serialize());
response_buffer = response.Serialize();
} else if (transaction == TransactionId::QueueBuffer) {
IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
IGBPQueueBufferRequestParcel request{input_data};
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
IGBPQueueBufferResponseParcel response{1280, 720};
ctx.WriteBuffer(response.Serialize());
response_buffer = response.Serialize();
} else if (transaction == TransactionId::Query) {
IGBPQueryRequestParcel request{ctx.ReadBuffer()};
IGBPQueryRequestParcel request{input_data};
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");
response_buffer = response.Serialize();
} else {
ASSERT_MSG(false, "Unimplemented");
}
Memory::WriteBlock(output_addr, response_buffer.data(), output_size);
}
void TransactParcel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u32 id = rp.Pop<u32>();
auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
u32 flags = rp.Pop<u32>();
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());
TransactParcel(id, transaction, input_data, output_buffer.Address(), output_buffer.Size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
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);
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);
TransactParcel(id, transaction, input_data, output_buffer.Address(), output_buffer.Size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -551,7 +555,7 @@ private:
}
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
}; // namespace VI
};
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
public:
@@ -696,13 +700,18 @@ 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>(ctx.WriteBuffer(native_window.Serialize()));
rb.Push<u64>(data.size());
}
void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) {
@@ -713,16 +722,21 @@ 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>(ctx.WriteBuffer(native_window.Serialize()));
rb.Push<u64>(data.size());
}
void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
@@ -748,7 +762,8 @@ void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext&
void IApplicationDisplayService::ListDisplays(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
DisplayInfo display_info;
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
auto& buffer = ctx.BufferDescriptorB()[0];
Memory::WriteBlock(buffer.Address(), &display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(1);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ class GPU final {
public:
GPU() {
memory_manager = std::make_unique<MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
maxwell_3d = std::make_unique<Engines::Maxwell3D>();
fermi_2d = std::make_unique<Engines::Fermi2D>();
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
}

View File

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