Compare commits
53 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a27a11770 | ||
|
|
21d9519032 | ||
|
|
b2d7c92cae | ||
|
|
dcae0c9a4f | ||
|
|
cff7b29bba | ||
|
|
23a0d2d7b7 | ||
|
|
2dc3a56e96 | ||
|
|
2332a44b68 | ||
|
|
c1c92c30f9 | ||
|
|
c86af6939c | ||
|
|
2faa83ca13 | ||
|
|
019f1a0cf0 | ||
|
|
e353b9fb3d | ||
|
|
03156d0c9a | ||
|
|
6317a0b2ca | ||
|
|
7b6868e908 | ||
|
|
85d820b1b4 | ||
|
|
a64b936cbe | ||
|
|
46f9d4b4a3 | ||
|
|
aa586fa268 | ||
|
|
7ac8657432 | ||
|
|
29981fa2eb | ||
|
|
ccb8da1512 | ||
|
|
88698c156f | ||
|
|
66dae22790 | ||
|
|
1d9d9c16e8 | ||
|
|
579000e747 | ||
|
|
516ef4f19f | ||
|
|
c286921739 | ||
|
|
f93d769a1c | ||
|
|
d2888f7e90 | ||
|
|
cd4e8a989c | ||
|
|
29feece4b8 | ||
|
|
0eff775264 | ||
|
|
e453b09a61 | ||
|
|
bf310a41b8 | ||
|
|
cbec739e7b | ||
|
|
494275fd38 | ||
|
|
e7ba2a4447 | ||
|
|
cc6f22e0e4 | ||
|
|
e9a857ce82 | ||
|
|
403f8e79ea | ||
|
|
34a29ad051 | ||
|
|
8581404482 | ||
|
|
69ee9edd8d | ||
|
|
3923b0f589 | ||
|
|
8be7131033 | ||
|
|
07ae1f972d | ||
|
|
fbfa7ddd62 | ||
|
|
9289255314 | ||
|
|
ec030a542f | ||
|
|
cde9386e0f | ||
|
|
bc88cae0c7 |
@@ -25,22 +25,18 @@ public:
|
||||
VAddr tls_address;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the CPU for the given number of instructions
|
||||
* @param num_instructions Number of instructions to run
|
||||
*/
|
||||
void Run(int num_instructions) {
|
||||
ExecuteInstructions(num_instructions);
|
||||
this->num_instructions += num_instructions;
|
||||
}
|
||||
/// Runs the CPU until an event happens
|
||||
virtual void Run() = 0;
|
||||
|
||||
/// Step CPU by one instruction
|
||||
void Step() {
|
||||
Run(1);
|
||||
}
|
||||
virtual void Step() = 0;
|
||||
|
||||
/// Maps a backing memory region for the CPU
|
||||
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) {}
|
||||
Kernel::VMAPermission perms) = 0;
|
||||
|
||||
/// Unmaps a region of memory that was previously mapped using MapBackingMemory
|
||||
virtual void UnmapMemory(VAddr address, size_t size) = 0;
|
||||
|
||||
/// Clear all instruction cache
|
||||
virtual void ClearInstructionCache() = 0;
|
||||
@@ -122,19 +118,4 @@ public:
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// Getter for num_instructions
|
||||
u64 GetNumInstructions() const {
|
||||
return num_instructions;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Executes the given number of instructions
|
||||
* @param num_instructions Number of instructions to executes
|
||||
*/
|
||||
virtual void ExecuteInstructions(int num_instructions) = 0;
|
||||
|
||||
private:
|
||||
u64 num_instructions = 0; ///< Number of instructions executed
|
||||
};
|
||||
|
||||
@@ -122,11 +122,22 @@ std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_C
|
||||
return std::make_unique<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Run() {
|
||||
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
|
||||
|
||||
jit->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic()
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
|
||||
ARM_Interface::ThreadContext ctx;
|
||||
inner_unicorn.SaveContext(ctx);
|
||||
LoadContext(ctx);
|
||||
PageTableChanged();
|
||||
}
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
@@ -136,6 +147,10 @@ void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory,
|
||||
inner_unicorn.MapBackingMemory(address, size, memory, perms);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) {
|
||||
inner_unicorn.UnmapMemory(address, size);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetPC(u64 pc) {
|
||||
jit->SetPC(pc);
|
||||
}
|
||||
@@ -185,13 +200,6 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
|
||||
cb->tpidrro_el0 = address;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
|
||||
cb->ticks_remaining = num_instructions;
|
||||
jit->Run();
|
||||
CoreTiming::AddTicks(num_instructions - cb->num_interpreted_instructions);
|
||||
cb->num_interpreted_instructions = 0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
@@ -224,4 +232,5 @@ void ARM_Dynarmic::ClearInstructionCache() {
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged() {
|
||||
jit = MakeJit(cb);
|
||||
current_page_table = Memory::GetCurrentPageTable();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
|
||||
void UnmapMemory(u64 address, size_t size) override;
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
@@ -29,6 +29,8 @@ public:
|
||||
u32 GetVFPReg(int index) const override;
|
||||
void SetVFPReg(int index, u32 value) override;
|
||||
u32 GetCPSR() const override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void SetCPSR(u32 cpsr) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
@@ -37,7 +39,6 @@ public:
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override;
|
||||
@@ -47,4 +48,6 @@ private:
|
||||
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
|
||||
std::unique_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
Memory::PageTable* current_page_table = nullptr;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unicorn/arm64.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
@@ -77,6 +78,10 @@ void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) {
|
||||
CHECKED(uc_mem_unmap(uc, address, size));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetPC(u64 pc) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
|
||||
}
|
||||
@@ -149,6 +154,14 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Step() {
|
||||
ExecuteInstructions(1);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
~ARM_Unicorn();
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
void UnmapMemory(VAddr address, size_t size) override;
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
@@ -29,7 +30,9 @@ public:
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
void ExecuteInstructions(int num_instructions);
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override{};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Core {
|
||||
|
||||
/*static*/ System System::s_instance;
|
||||
|
||||
System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
if (!cpu_core) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
@@ -40,7 +40,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
tight_loop = 1;
|
||||
tight_loop = false;
|
||||
} else {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
@@ -56,7 +56,11 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
CoreTiming::Advance();
|
||||
cpu_core->Run(tight_loop);
|
||||
if (tight_loop) {
|
||||
cpu_core->Run();
|
||||
} else {
|
||||
cpu_core->Step();
|
||||
}
|
||||
}
|
||||
|
||||
HW::Update();
|
||||
@@ -66,7 +70,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
}
|
||||
|
||||
System::ResultStatus System::SingleStep() {
|
||||
return RunLoop(1);
|
||||
return RunLoop(false);
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
|
||||
|
||||
@@ -53,10 +53,10 @@ public:
|
||||
* is not required to do a full dispatch with each instruction. NOTE: the number of instructions
|
||||
* requested is not guaranteed to run, as this will be interrupted preemptively if a hardware
|
||||
* update is requested (e.g. on a thread switch).
|
||||
* @param tight_loop Number of instructions to execute.
|
||||
* @param tight_loop If false, the CPU single-steps.
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
ResultStatus RunLoop(int tight_loop = 100000);
|
||||
ResultStatus RunLoop(bool tight_loop = true);
|
||||
|
||||
/**
|
||||
* Step the CPU one instruction
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#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"
|
||||
@@ -26,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
|
||||
boost::range::remove_erase(connected_sessions, server_session);
|
||||
}
|
||||
|
||||
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);
|
||||
context.WriteToOutgoingCommandBuffer(*thread);
|
||||
return true;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
|
||||
: server_session(std::move(server_session)) {
|
||||
cmd_buf[0] = 0;
|
||||
@@ -35,7 +62,7 @@ HLERequestContext::~HLERequestContext() = default;
|
||||
|
||||
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>());
|
||||
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
|
||||
|
||||
if (command_header->type == IPC::CommandType::Close) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
@@ -45,7 +72,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
// If handle descriptor is present, add size of it
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
handle_descriptor_header =
|
||||
std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
|
||||
std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
|
||||
if (handle_descriptor_header->send_current_pid) {
|
||||
rp.Skip(2, false);
|
||||
}
|
||||
@@ -88,7 +115,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
// 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>());
|
||||
std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
|
||||
} else {
|
||||
if (Session()->IsDomain())
|
||||
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
||||
@@ -96,7 +123,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
}
|
||||
|
||||
data_payload_header =
|
||||
std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
|
||||
std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
|
||||
|
||||
data_payload_offset = rp.GetCurrentOffset();
|
||||
|
||||
@@ -159,8 +186,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
|
||||
HandleTable& dst_table) {
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
// The header was already built in the internal command buffer. Attempt to parse it to verify
|
||||
// the integrity and then copy it over to the target command buffer.
|
||||
ParseCommandBuffer(cmd_buf.data(), false);
|
||||
@@ -171,7 +201,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
|
||||
if (domain_message_header)
|
||||
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
|
||||
|
||||
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf);
|
||||
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data());
|
||||
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
|
||||
@@ -213,6 +243,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
|
||||
dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
@@ -13,6 +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 +26,7 @@ class Domain;
|
||||
class HandleTable;
|
||||
class HLERequestContext;
|
||||
class Process;
|
||||
class Event;
|
||||
|
||||
/**
|
||||
* Interface implemented by HLE Session handlers.
|
||||
@@ -102,14 +105,31 @@ public:
|
||||
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.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
|
||||
HandleTable& src_table);
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
|
||||
HandleTable& dst_table);
|
||||
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
|
||||
|
||||
u32_le GetCommand() const {
|
||||
return command;
|
||||
@@ -139,7 +159,7 @@ public:
|
||||
return buffer_c_desciptors;
|
||||
}
|
||||
|
||||
const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
|
||||
const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
|
||||
return domain_message_header;
|
||||
}
|
||||
|
||||
@@ -212,10 +232,10 @@ private:
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
|
||||
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
|
||||
|
||||
std::unique_ptr<IPC::CommandHeader> command_header;
|
||||
std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::unique_ptr<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::unique_ptr<IPC::DomainMessageHeader> domain_message_header;
|
||||
std::shared_ptr<IPC::CommandHeader> command_header;
|
||||
std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
|
||||
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
|
||||
|
||||
@@ -33,10 +33,6 @@ enum class HandleType : u32 {
|
||||
ServerSession,
|
||||
};
|
||||
|
||||
enum {
|
||||
DEFAULT_STACK_SIZE = 0x10000,
|
||||
};
|
||||
|
||||
enum class ResetType {
|
||||
OneShot,
|
||||
Sticky,
|
||||
|
||||
@@ -117,11 +117,12 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
|
||||
}
|
||||
|
||||
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
|
||||
// Allocate and map stack
|
||||
// Allocate and map the main thread stack
|
||||
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
|
||||
// of the user address space.
|
||||
vm_manager
|
||||
.MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size,
|
||||
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
|
||||
MemoryState::Heap)
|
||||
.MapMemoryBlock(Memory::STACK_VADDR, std::make_shared<std::vector<u8>>(stack_size, 0), 0,
|
||||
stack_size, MemoryState::Mapped)
|
||||
.Unwrap();
|
||||
misc_memory_used += stack_size;
|
||||
memory_region->used += stack_size;
|
||||
@@ -153,9 +154,9 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
|
||||
};
|
||||
|
||||
// Map CodeSet segments
|
||||
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::Code);
|
||||
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static);
|
||||
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static);
|
||||
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic);
|
||||
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable);
|
||||
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable);
|
||||
}
|
||||
|
||||
VAddr Process::GetLinearHeapAreaAddress() const {
|
||||
@@ -182,6 +183,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>();
|
||||
heap_start = heap_end = target;
|
||||
} else {
|
||||
vm_manager.UnmapRange(heap_start, heap_end - heap_start);
|
||||
}
|
||||
|
||||
// If necessary, expand backing vector to cover new heap extents.
|
||||
@@ -201,7 +204,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
|
||||
size, MemoryState::Heap));
|
||||
vm_manager.Reprotect(vma, perms);
|
||||
|
||||
heap_used += size;
|
||||
heap_used = size;
|
||||
memory_region->used += size;
|
||||
|
||||
return MakeResult<VAddr>(heap_end - size);
|
||||
@@ -288,7 +291,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
|
||||
CASCADE_RESULT(auto new_vma,
|
||||
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
|
||||
vma->second.meminfo_state));
|
||||
MemoryState::Mapped));
|
||||
// Protect mirror with permissions from old region
|
||||
vm_manager.Reprotect(new_vma, vma->second.permissions);
|
||||
// Remove permissions from old region
|
||||
|
||||
@@ -317,13 +317,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
*result = Core::CurrentProcess()->allowed_thread_priority_mask;
|
||||
break;
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
*result = vm_manager.GetMapRegionBaseAddr();
|
||||
*result = Memory::MAP_REGION_VADDR;
|
||||
break;
|
||||
case GetInfoType::MapRegionSize:
|
||||
*result = vm_manager.GetAddressSpaceSize();
|
||||
*result = Memory::MAP_REGION_SIZE;
|
||||
break;
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
*result = vm_manager.GetNewMapRegionBaseAddr() + vm_manager.GetNewMapRegionSize();
|
||||
*result = Memory::HEAP_VADDR;
|
||||
break;
|
||||
case GetInfoType::HeapRegionSize:
|
||||
*result = Memory::HEAP_SIZE;
|
||||
@@ -347,10 +347,10 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
*result = vm_manager.GetAddressSpaceSize();
|
||||
break;
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
*result = vm_manager.GetNewMapRegionBaseAddr();
|
||||
*result = Memory::NEW_MAP_REGION_VADDR;
|
||||
break;
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
*result = vm_manager.GetNewMapRegionSize();
|
||||
*result = Memory::NEW_MAP_REGION_SIZE;
|
||||
break;
|
||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
||||
*result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
|
||||
@@ -468,7 +468,7 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
|
||||
memory_info->base_address = 0;
|
||||
memory_info->permission = static_cast<u32>(VMAPermission::None);
|
||||
memory_info->size = 0;
|
||||
memory_info->type = static_cast<u32>(MemoryState::Free);
|
||||
memory_info->type = static_cast<u32>(MemoryState::Unmapped);
|
||||
} else {
|
||||
memory_info->base_address = vma->second.base;
|
||||
memory_info->permission = static_cast<u32>(vma->second.permissions);
|
||||
|
||||
@@ -55,16 +55,6 @@ inline static u32 const NewThreadId() {
|
||||
Thread::Thread() {}
|
||||
Thread::~Thread() {}
|
||||
|
||||
/**
|
||||
* Check if the specified thread is waiting on the specified address to be arbitrated
|
||||
* @param thread The thread to test
|
||||
* @param wait_address The address to test against
|
||||
* @return True if the thread is waiting, false otherwise
|
||||
*/
|
||||
static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
|
||||
return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
|
||||
}
|
||||
|
||||
void Thread::Stop() {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
|
||||
@@ -102,12 +92,6 @@ void WaitCurrentThread_Sleep() {
|
||||
thread->status = THREADSTATUS_WAIT_SLEEP;
|
||||
}
|
||||
|
||||
void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->wait_address = wait_address;
|
||||
thread->status = THREADSTATUS_WAIT_ARB;
|
||||
}
|
||||
|
||||
void ExitCurrentThread() {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->Stop();
|
||||
@@ -129,7 +113,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_HLE_EVENT) {
|
||||
|
||||
// Remove the thread from each of its waiting objects' waitlists
|
||||
for (auto& object : thread->wait_objects)
|
||||
@@ -163,7 +148,7 @@ void Thread::ResumeFromWait() {
|
||||
switch (status) {
|
||||
case THREADSTATUS_WAIT_SYNCH_ALL:
|
||||
case THREADSTATUS_WAIT_SYNCH_ANY:
|
||||
case THREADSTATUS_WAIT_ARB:
|
||||
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||
case THREADSTATUS_WAIT_SLEEP:
|
||||
case THREADSTATUS_WAIT_IPC:
|
||||
break;
|
||||
@@ -314,7 +299,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||
// TODO(Subv): Find the correct MemoryState for this region.
|
||||
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
|
||||
linheap_memory, offset, Memory::PAGE_SIZE,
|
||||
MemoryState::ThreadLocalStorage);
|
||||
MemoryState::ThreadLocal);
|
||||
}
|
||||
|
||||
// Mark the slot as used
|
||||
@@ -357,7 +342,7 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
|
||||
|
||||
// Initialize new "main" thread
|
||||
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
|
||||
Memory::HEAP_VADDR_END, owner_process);
|
||||
Memory::STACK_VADDR_END, owner_process);
|
||||
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ enum ThreadProcessorId : s32 {
|
||||
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
|
||||
|
||||
@@ -18,8 +18,26 @@ namespace Kernel {
|
||||
|
||||
static const char* GetMemoryStateName(MemoryState state) {
|
||||
static const char* names[] = {
|
||||
"Free", "Reserved", "IO", "Static", "Code", "Private",
|
||||
"Shared", "Continuous", "Aliased", "Alias", "AliasCode", "Locked",
|
||||
"Unmapped",
|
||||
"Io",
|
||||
"Normal",
|
||||
"CodeStatic",
|
||||
"CodeMutable",
|
||||
"Heap",
|
||||
"Shared",
|
||||
"Unknown1"
|
||||
"ModuleCodeStatic",
|
||||
"ModuleCodeMutable",
|
||||
"IpcBuffer0",
|
||||
"Mapped",
|
||||
"ThreadLocal",
|
||||
"TransferMemoryIsolated",
|
||||
"TransferMemory",
|
||||
"ProcessMemory",
|
||||
"Unknown2"
|
||||
"IpcBuffer1",
|
||||
"IpcBuffer3",
|
||||
"KernelStack",
|
||||
};
|
||||
|
||||
return names[(int)state];
|
||||
@@ -142,7 +160,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
|
||||
VirtualMemoryArea& vma = vma_handle->second;
|
||||
vma.type = VMAType::Free;
|
||||
vma.permissions = VMAPermission::None;
|
||||
vma.meminfo_state = MemoryState::Free;
|
||||
vma.meminfo_state = MemoryState::Unmapped;
|
||||
|
||||
vma.backing_block = nullptr;
|
||||
vma.offset = 0;
|
||||
@@ -166,6 +184,9 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
|
||||
}
|
||||
|
||||
ASSERT(FindVMA(target)->second.size >= size);
|
||||
|
||||
Core::CPU().UnmapMemory(target, size);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -377,19 +398,4 @@ u64 VMManager::GetAddressSpaceSize() {
|
||||
return MAX_ADDRESS;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetMapRegionBaseAddr() {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return Memory::HEAP_VADDR;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetNewMapRegionBaseAddr() {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0x8000000;
|
||||
}
|
||||
|
||||
u64 VMManager::GetNewMapRegionSize() {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0x8000000;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -41,15 +41,24 @@ enum class VMAPermission : u8 {
|
||||
|
||||
/// Set of values returned in MemoryInfo.state by svcQueryMemory.
|
||||
enum class MemoryState : u32 {
|
||||
Free = 0,
|
||||
IO = 1,
|
||||
Normal = 2,
|
||||
Code = 3,
|
||||
Static = 4,
|
||||
Heap = 5,
|
||||
Shared = 6,
|
||||
Mapped = 6,
|
||||
ThreadLocalStorage = 12,
|
||||
Unmapped = 0x0,
|
||||
Io = 0x1,
|
||||
Normal = 0x2,
|
||||
CodeStatic = 0x3,
|
||||
CodeMutable = 0x4,
|
||||
Heap = 0x5,
|
||||
Shared = 0x6,
|
||||
ModuleCodeStatic = 0x8,
|
||||
ModuleCodeMutable = 0x9,
|
||||
IpcBuffer0 = 0xA,
|
||||
Mapped = 0xB,
|
||||
ThreadLocal = 0xC,
|
||||
TransferMemoryIsolated = 0xD,
|
||||
TransferMemory = 0xE,
|
||||
ProcessMemory = 0xF,
|
||||
IpcBuffer1 = 0x11,
|
||||
IpcBuffer3 = 0x12,
|
||||
KernelStack = 0x13,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -66,7 +75,7 @@ struct VirtualMemoryArea {
|
||||
VMAType type = VMAType::Free;
|
||||
VMAPermission permissions = VMAPermission::None;
|
||||
/// Tag returned by svcQueryMemory. Not otherwise used.
|
||||
MemoryState meminfo_state = MemoryState::Free;
|
||||
MemoryState meminfo_state = MemoryState::Unmapped;
|
||||
|
||||
// Settings for type = AllocatedMemoryBlock
|
||||
/// Memory block backing this VMA.
|
||||
@@ -192,15 +201,6 @@ public:
|
||||
/// Gets the total address space address size, used by svcGetInfo
|
||||
u64 GetAddressSpaceSize();
|
||||
|
||||
/// Gets the map region base address, used by svcGetInfo
|
||||
VAddr GetMapRegionBaseAddr();
|
||||
|
||||
/// Gets the base address for a new memory region, used by svcGetInfo
|
||||
VAddr GetNewMapRegionBaseAddr();
|
||||
|
||||
/// Gets the size for a new memory region, used by svcGetInfo
|
||||
u64 GetNewMapRegionSize();
|
||||
|
||||
/// Each VMManager has its own page table, which is set as the main one when the owning process
|
||||
/// is scheduled.
|
||||
Memory::PageTable page_table;
|
||||
|
||||
@@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
|
||||
for (const auto& thread : waiting_threads) {
|
||||
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
||||
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
|
||||
thread->status == THREADSTATUS_WAIT_HLE_EVENT,
|
||||
"Inconsistent thread statuses in waiting_threads");
|
||||
|
||||
if (thread->current_priority >= candidate_priority)
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
{0, &IRequest::GetRequestState, "GetRequestState"},
|
||||
{1, &IRequest::GetResult, "GetResult"},
|
||||
{2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
|
||||
{3, nullptr, "Cancel"},
|
||||
{3, &IRequest::Cancel, "Cancel"},
|
||||
{4, nullptr, "Submit"},
|
||||
{5, nullptr, "SetRequirement"},
|
||||
{6, nullptr, "SetRequirementPreset"},
|
||||
@@ -80,6 +80,11 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(event1, event2);
|
||||
}
|
||||
void Cancel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> event1, event2;
|
||||
};
|
||||
@@ -96,13 +101,56 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class IGeneralService final : public ServiceFramework<IGeneralService> {
|
||||
public:
|
||||
IGeneralService();
|
||||
|
||||
private:
|
||||
void GetClientId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
void CreateScanRequest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IScanRequest>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
void CreateRequest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IRequest>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<INetworkProfile>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
};
|
||||
|
||||
IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IGeneralService::GetClientId, "GetClientId"},
|
||||
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
|
||||
{4, &IGeneralService::CreateRequest, "CreateRequest"},
|
||||
{6, nullptr, "GetCurrentNetworkProfile"},
|
||||
{7, nullptr, "EnumerateNetworkInterfaces"},
|
||||
{5, nullptr, "GetCurrentNetworkProfile"},
|
||||
{6, nullptr, "EnumerateNetworkInterfaces"},
|
||||
{7, nullptr, "EnumerateNetworkProfiles"},
|
||||
{8, nullptr, "GetNetworkProfile"},
|
||||
{9, nullptr, "SetNetworkProfile"},
|
||||
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
|
||||
@@ -137,50 +185,28 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
|
||||
void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IScanRequest>();
|
||||
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IRequest>();
|
||||
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<INetworkProfile>();
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<NIFM_A>()->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_S>()->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_U>()->InstallAsService(service_manager);
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<NIFM_A>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_S>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<NIFM_U>(module)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace NIFM
|
||||
|
||||
@@ -9,16 +9,18 @@
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
class IGeneralService final : public ServiceFramework<IGeneralService> {
|
||||
class Module final {
|
||||
public:
|
||||
IGeneralService();
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
private:
|
||||
void GetClientId(Kernel::HLERequestContext& ctx);
|
||||
void CreateScanRequest(Kernel::HLERequestContext& ctx);
|
||||
void CreateRequest(Kernel::HLERequestContext& ctx);
|
||||
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
|
||||
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
};
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
@@ -2,29 +2,12 @@
|
||||
// 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/nifm/nifm.h"
|
||||
#include "core/hle/service/nifm/nifm_a.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
void NIFM_A::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void NIFM_A::CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
NIFM_A::NIFM_A() : ServiceFramework("nifm:a") {
|
||||
NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:a") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{4, &NIFM_A::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
|
||||
{5, &NIFM_A::CreateGeneralService, "CreateGeneralService"},
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
class NIFM_A final : public ServiceFramework<NIFM_A> {
|
||||
class NIFM_A final : public Module::Interface {
|
||||
public:
|
||||
NIFM_A();
|
||||
~NIFM_A() = default;
|
||||
|
||||
private:
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
explicit NIFM_A(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace NIFM
|
||||
|
||||
@@ -2,29 +2,12 @@
|
||||
// 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/nifm/nifm.h"
|
||||
#include "core/hle/service/nifm/nifm_s.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
void NIFM_S::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void NIFM_S::CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
NIFM_S::NIFM_S() : ServiceFramework("nifm:s") {
|
||||
NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:s") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{4, &NIFM_S::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
|
||||
{5, &NIFM_S::CreateGeneralService, "CreateGeneralService"},
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
class NIFM_S final : public ServiceFramework<NIFM_S> {
|
||||
class NIFM_S final : public Module::Interface {
|
||||
public:
|
||||
NIFM_S();
|
||||
~NIFM_S() = default;
|
||||
|
||||
private:
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
explicit NIFM_S(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace NIFM
|
||||
|
||||
@@ -2,29 +2,12 @@
|
||||
// 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/nifm/nifm.h"
|
||||
#include "core/hle/service/nifm/nifm_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
void NIFM_U::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
void NIFM_U::CreateGeneralService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGeneralService>();
|
||||
LOG_DEBUG(Service_NIFM, "called");
|
||||
}
|
||||
|
||||
NIFM_U::NIFM_U() : ServiceFramework("nifm:u") {
|
||||
NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{4, &NIFM_U::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
|
||||
{5, &NIFM_U::CreateGeneralService, "CreateGeneralService"},
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NIFM {
|
||||
|
||||
class NIFM_U final : public ServiceFramework<NIFM_U> {
|
||||
class NIFM_U final : public Module::Interface {
|
||||
public:
|
||||
NIFM_U();
|
||||
~NIFM_U() = default;
|
||||
|
||||
private:
|
||||
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
|
||||
void CreateGeneralService(Kernel::HLERequestContext& ctx);
|
||||
explicit NIFM_U(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace NIFM
|
||||
|
||||
@@ -26,26 +26,30 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
LOG_WARNING(Service, "Adding graphics buffer %u", slot);
|
||||
|
||||
queue.emplace_back(buffer);
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
|
||||
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
|
||||
// Only consider free buffers. Buffers become free once again after they've been Acquired
|
||||
// and Released by the compositor, see the NVFlinger::Compose method.
|
||||
if (buffer.status != Buffer::Status::Free)
|
||||
if (buffer.status != Buffer::Status::Free) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure that the parameters match.
|
||||
auto& igbp_buffer = buffer.igbp_buffer;
|
||||
return igbp_buffer.format == pixel_format && igbp_buffer.width == width &&
|
||||
igbp_buffer.height == height;
|
||||
return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height;
|
||||
});
|
||||
|
||||
if (itr == queue.end()) {
|
||||
LOG_CRITICAL(Service_NVDRV, "no free buffers for pixel_format=%d, width=%d, height=%d",
|
||||
pixel_format, width, height);
|
||||
itr = queue.begin();
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
buffer_wait_event = nullptr;
|
||||
|
||||
itr->status = Buffer::Status::Dequeued;
|
||||
return itr->slot;
|
||||
}
|
||||
@@ -83,6 +87,10 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
ASSERT(itr != queue.end());
|
||||
ASSERT(itr->status == Buffer::Status::Acquired);
|
||||
itr->status = Buffer::Status::Free;
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
u32 BufferQueue::Query(QueryType type) {
|
||||
@@ -98,5 +106,10 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
|
||||
ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
|
||||
buffer_wait_event = std::move(wait_event);
|
||||
}
|
||||
|
||||
} // namespace NVFlinger
|
||||
} // namespace Service
|
||||
|
||||
@@ -69,12 +69,13 @@ public:
|
||||
};
|
||||
|
||||
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
|
||||
u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
|
||||
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const;
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform);
|
||||
boost::optional<const Buffer&> AcquireBuffer();
|
||||
void ReleaseBuffer(u32 slot);
|
||||
u32 Query(QueryType type);
|
||||
void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
|
||||
|
||||
u32 GetId() const {
|
||||
return id;
|
||||
@@ -90,6 +91,9 @@ private:
|
||||
|
||||
std::vector<Buffer> queue;
|
||||
Kernel::SharedPtr<Kernel::Event> native_handle;
|
||||
|
||||
/// Used to signal waiting thread when no buffers are available
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
|
||||
};
|
||||
|
||||
} // namespace NVFlinger
|
||||
|
||||
@@ -152,8 +152,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType());
|
||||
}
|
||||
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
|
||||
context.WriteToOutgoingCommandBuffer(cmd_buf, *Core::CurrentProcess(), Kernel::g_handle_table);
|
||||
context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread());
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -486,12 +486,30 @@ private:
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else if (transaction == TransactionId::DequeueBuffer) {
|
||||
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
const u32 width{request.data.width};
|
||||
const u32 height{request.data.height};
|
||||
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
|
||||
u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width,
|
||||
request.data.height);
|
||||
|
||||
IGBPDequeueBufferResponseParcel response{slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
if (slot != boost::none) {
|
||||
// Buffer is available
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else {
|
||||
// Wait the current thread until a buffer becomes available
|
||||
auto wait_event = ctx.SleepClientThread(
|
||||
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
});
|
||||
buffer_queue->SetBufferWaitEvent(std::move(wait_event));
|
||||
}
|
||||
} else if (transaction == TransactionId::RequestBuffer) {
|
||||
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
|
||||
|
||||
@@ -414,7 +414,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
|
||||
process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE);
|
||||
process->Run(codeset->entrypoint, 48, Memory::STACK_SIZE);
|
||||
|
||||
is_loaded = true;
|
||||
return ResultStatus::Success;
|
||||
|
||||
@@ -137,7 +137,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
process->address_mappings = default_address_mappings;
|
||||
process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE);
|
||||
process->Run(base_addr, 48, Memory::STACK_SIZE);
|
||||
|
||||
is_loaded = true;
|
||||
return ResultStatus::Success;
|
||||
|
||||
@@ -165,7 +165,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
process->address_mappings = default_address_mappings;
|
||||
process->resource_limit =
|
||||
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
|
||||
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE);
|
||||
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::STACK_SIZE);
|
||||
|
||||
is_loaded = true;
|
||||
return ResultStatus::Success;
|
||||
|
||||
@@ -129,21 +129,6 @@ enum : VAddr {
|
||||
PROCESS_IMAGE_MAX_SIZE = 0x08000000,
|
||||
PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE,
|
||||
|
||||
/// Area where IPC buffers are mapped onto.
|
||||
IPC_MAPPING_VADDR = 0x04000000,
|
||||
IPC_MAPPING_SIZE = 0x04000000,
|
||||
IPC_MAPPING_VADDR_END = IPC_MAPPING_VADDR + IPC_MAPPING_SIZE,
|
||||
|
||||
/// Application heap (includes stack).
|
||||
HEAP_VADDR = 0x108000000,
|
||||
HEAP_SIZE = 0xF0000000,
|
||||
HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE,
|
||||
|
||||
/// Area where shared memory buffers are mapped onto.
|
||||
SHARED_MEMORY_VADDR = 0x10000000,
|
||||
SHARED_MEMORY_SIZE = 0x04000000,
|
||||
SHARED_MEMORY_VADDR_END = SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE,
|
||||
|
||||
/// Maps 1:1 to an offset in FCRAM. Used for HW allocations that need to be linear in physical
|
||||
/// memory.
|
||||
LINEAR_HEAP_VADDR = 0x14000000,
|
||||
@@ -176,14 +161,39 @@ enum : VAddr {
|
||||
SHARED_PAGE_SIZE = 0x00001000,
|
||||
SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
|
||||
|
||||
/// Area where TLS (Thread-Local Storage) buffers are allocated.
|
||||
TLS_AREA_VADDR = 0x228000000,
|
||||
TLS_ENTRY_SIZE = 0x200,
|
||||
|
||||
/// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS.
|
||||
NEW_LINEAR_HEAP_VADDR = 0x30000000,
|
||||
NEW_LINEAR_HEAP_SIZE = 0x10000000,
|
||||
NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
|
||||
|
||||
/// Area where TLS (Thread-Local Storage) buffers are allocated.
|
||||
TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END,
|
||||
TLS_ENTRY_SIZE = 0x200,
|
||||
TLS_AREA_SIZE = 0x10000000,
|
||||
TLS_ADREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
|
||||
|
||||
/// Application stack
|
||||
STACK_VADDR = TLS_ADREA_VADDR_END,
|
||||
STACK_SIZE = 0x10000,
|
||||
STACK_VADDR_END = STACK_VADDR + STACK_SIZE,
|
||||
|
||||
/// Application heap
|
||||
/// Size is confirmed to be a static value on fw 3.0.0
|
||||
HEAP_VADDR = 0x108000000,
|
||||
HEAP_SIZE = 0x180000000,
|
||||
HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE,
|
||||
|
||||
/// New map region
|
||||
/// Size is confirmed to be a static value on fw 3.0.0
|
||||
NEW_MAP_REGION_VADDR = HEAP_VADDR_END,
|
||||
NEW_MAP_REGION_SIZE = 0x80000000,
|
||||
NEW_MAP_REGION_VADDR_END = NEW_MAP_REGION_VADDR + NEW_MAP_REGION_SIZE,
|
||||
|
||||
/// Map region
|
||||
/// Size is confirmed to be a static value on fw 3.0.0
|
||||
MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END,
|
||||
MAP_REGION_SIZE = 0x1000000000,
|
||||
MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE,
|
||||
};
|
||||
|
||||
/// Currently active page table
|
||||
|
||||
@@ -7,6 +7,7 @@ add_library(video_core STATIC
|
||||
engines/maxwell_3d.h
|
||||
engines/maxwell_compute.cpp
|
||||
engines/maxwell_compute.h
|
||||
gpu.cpp
|
||||
gpu.h
|
||||
memory_manager.cpp
|
||||
memory_manager.h
|
||||
|
||||
@@ -24,12 +24,37 @@ namespace Tegra {
|
||||
|
||||
enum class BufferMethods {
|
||||
BindObject = 0,
|
||||
SetGraphMacroCode = 0x45,
|
||||
SetGraphMacroCodeArg = 0x46,
|
||||
SetGraphMacroEntry = 0x47,
|
||||
CountBufferMethods = 0x100,
|
||||
};
|
||||
|
||||
void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
|
||||
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X", method, subchannel,
|
||||
value);
|
||||
void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) {
|
||||
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X remaining params %u",
|
||||
method, subchannel, value, remaining_params);
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::SetGraphMacroEntry)) {
|
||||
// Prepare to upload a new macro, reset the upload counter.
|
||||
LOG_DEBUG(HW_GPU, "Uploading GPU macro %08X", value);
|
||||
current_macro_entry = value;
|
||||
current_macro_code.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::SetGraphMacroCodeArg)) {
|
||||
// Append a new code word to the current macro.
|
||||
current_macro_code.push_back(value);
|
||||
|
||||
// There are no more params remaining, submit the code to the 3D engine.
|
||||
if (remaining_params == 0) {
|
||||
maxwell_3d->SubmitMacroCode(current_macro_entry, std::move(current_macro_code));
|
||||
current_macro_entry = InvalidGraphMacroEntry;
|
||||
current_macro_code.clear();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
@@ -54,7 +79,7 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
|
||||
fermi_2d->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::MAXWELL_B:
|
||||
maxwell_3d->WriteReg(method, value);
|
||||
maxwell_3d->WriteReg(method, value, remaining_params);
|
||||
break;
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->WriteReg(method, value);
|
||||
@@ -78,7 +103,8 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
|
||||
case SubmissionMode::Increasing: {
|
||||
// Increase the method value with each argument.
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr));
|
||||
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
@@ -87,27 +113,31 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
|
||||
case SubmissionMode::NonIncreasing: {
|
||||
// Use the same method value for all arguments.
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::IncreaseOnce: {
|
||||
ASSERT(header.arg_count.Value() >= 1);
|
||||
|
||||
// Use the original method for the first argument and then the next method for all other
|
||||
// arguments.
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - 1);
|
||||
current_addr += sizeof(u32);
|
||||
// Use the same method value for all arguments.
|
||||
|
||||
for (unsigned i = 1; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr));
|
||||
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::Inline: {
|
||||
// The register value is stored in the bits 16-28 as an immediate
|
||||
WriteReg(header.method, header.subchannel, header.inline_data);
|
||||
WriteReg(header.method, header.subchannel, header.inline_data, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -34,6 +34,4 @@ static_assert(std::is_standard_layout<CommandHeader>::value == true,
|
||||
"CommandHeader does not use standard layout");
|
||||
static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
|
||||
|
||||
void ProcessCommandList(VAddr address, u32 size);
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -8,17 +8,122 @@
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
const std::unordered_map<u32, Maxwell3D::MethodInfo> Maxwell3D::method_handlers = {
|
||||
{0xE1A, {"BindTextureInfoBuffer", 1, &Maxwell3D::BindTextureInfoBuffer}},
|
||||
{0xE24, {"SetShader", 5, &Maxwell3D::SetShader}},
|
||||
{0xE2A, {"BindStorageBuffer", 1, &Maxwell3D::BindStorageBuffer}},
|
||||
};
|
||||
|
||||
Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
|
||||
|
||||
void Maxwell3D::WriteReg(u32 method, u32 value) {
|
||||
void Maxwell3D::SubmitMacroCode(u32 entry, std::vector<u32> code) {
|
||||
uploaded_macros[entry * 2 + MacroRegistersStart] = std::move(code);
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
|
||||
// TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47
|
||||
|
||||
// The requested macro must have been uploaded already.
|
||||
ASSERT_MSG(uploaded_macros.find(method) != uploaded_macros.end(), "Macro %08X was not uploaded",
|
||||
method);
|
||||
|
||||
auto itr = method_handlers.find(method);
|
||||
ASSERT_MSG(itr != method_handlers.end(), "Unhandled method call %08X", method);
|
||||
|
||||
ASSERT(itr->second.arguments == parameters.size());
|
||||
|
||||
(this->*itr->second.handler)(parameters);
|
||||
|
||||
// Reset the current macro and its parameters.
|
||||
executing_macro = 0;
|
||||
macro_params.clear();
|
||||
}
|
||||
|
||||
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Maxwell3D register, increase the size of the Regs structure");
|
||||
|
||||
// It is an error to write to a register other than the current macro's ARG register before it
|
||||
// has finished execution.
|
||||
if (executing_macro != 0) {
|
||||
ASSERT(method == executing_macro + 1);
|
||||
}
|
||||
|
||||
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
||||
// uploaded to the GPU during initialization.
|
||||
if (method >= MacroRegistersStart) {
|
||||
// We're trying to execute a macro
|
||||
if (executing_macro == 0) {
|
||||
// A macro call must begin by writing the macro method's register, not its argument.
|
||||
ASSERT_MSG((method % 2) == 0,
|
||||
"Can't start macro execution by writing to the ARGS register");
|
||||
executing_macro = method;
|
||||
}
|
||||
|
||||
macro_params.push_back(value);
|
||||
|
||||
// Call the macro when there are no more parameters in the command buffer
|
||||
if (remaining_params == 0) {
|
||||
CallMacroMethod(executing_macro, macro_params);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
regs.reg_array[method] = value;
|
||||
|
||||
#define MAXWELL3D_REG_INDEX(field_name) (offsetof(Regs, field_name) / sizeof(u32))
|
||||
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(code_address.code_address_high):
|
||||
case MAXWELL3D_REG_INDEX(code_address.code_address_low): {
|
||||
// Note: For some reason games (like Puyo Puyo Tetris) seem to write 0 to the CODE_ADDRESS
|
||||
// register, we do not currently know if that's intended or a bug, so we assert it lest
|
||||
// stuff breaks in other places (like the shader address calculation).
|
||||
ASSERT_MSG(regs.code_address.CodeAddress() == 0, "Unexpected CODE_ADDRESS register value.");
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
|
||||
ProcessCBData(value);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[0].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::Vertex);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[1].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::TesselationControl);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[2].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::TesselationEval);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[3].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::Geometry);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[4].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::Fragment);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): {
|
||||
DrawArrays();
|
||||
break;
|
||||
@@ -56,5 +161,113 @@ void Maxwell3D::DrawArrays() {
|
||||
LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring");
|
||||
}
|
||||
|
||||
void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
|
||||
/**
|
||||
* Parameters description:
|
||||
* [0] = Shader stage, usually 4 for FragmentShader
|
||||
*/
|
||||
|
||||
u32 stage = parameters[0];
|
||||
|
||||
// Perform the same operations as the real macro code.
|
||||
GPUVAddr address = static_cast<GPUVAddr>(regs.tex_info_buffers.address[stage]) << 8;
|
||||
u32 size = regs.tex_info_buffers.size[stage];
|
||||
|
||||
regs.const_buffer.cb_size = size;
|
||||
regs.const_buffer.cb_address_high = address >> 32;
|
||||
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
void Maxwell3D::SetShader(const std::vector<u32>& parameters) {
|
||||
/**
|
||||
* Parameters description:
|
||||
* [0] = Shader Program.
|
||||
* [1] = Unknown, presumably the shader id.
|
||||
* [2] = Offset to the start of the shader, after the 0x30 bytes header.
|
||||
* [3] = Shader Stage.
|
||||
* [4] = Const Buffer Address >> 8.
|
||||
*/
|
||||
auto shader_program = static_cast<Regs::ShaderProgram>(parameters[0]);
|
||||
// TODO(Subv): This address is probably an offset from the CODE_ADDRESS register.
|
||||
GPUVAddr address = parameters[2];
|
||||
auto shader_stage = static_cast<Regs::ShaderStage>(parameters[3]);
|
||||
GPUVAddr cb_address = parameters[4] << 8;
|
||||
|
||||
auto& shader = state.shader_programs[static_cast<size_t>(shader_program)];
|
||||
shader.program = shader_program;
|
||||
shader.stage = shader_stage;
|
||||
shader.address = address;
|
||||
|
||||
// Perform the same operations as the real macro code.
|
||||
// TODO(Subv): Early exit if register 0xD1C + shader_program contains the same as params[1].
|
||||
auto& shader_regs = regs.shader_config[static_cast<size_t>(shader_program)];
|
||||
shader_regs.start_id = address;
|
||||
// TODO(Subv): Write params[1] to register 0xD1C + shader_program.
|
||||
// TODO(Subv): Write params[2] to register 0xD22 + shader_program.
|
||||
|
||||
// Note: This value is hardcoded in the macro's code.
|
||||
static constexpr u32 DefaultCBSize = 0x10000;
|
||||
regs.const_buffer.cb_size = DefaultCBSize;
|
||||
regs.const_buffer.cb_address_high = cb_address >> 32;
|
||||
regs.const_buffer.cb_address_low = cb_address & 0xFFFFFFFF;
|
||||
|
||||
// Write a hardcoded 0x11 to CB_BIND, this binds the current const buffer to buffer c1[] in the
|
||||
// shader. It's likely that these are the constants for the shader.
|
||||
regs.cb_bind[static_cast<size_t>(shader_stage)].valid.Assign(1);
|
||||
regs.cb_bind[static_cast<size_t>(shader_stage)].index.Assign(1);
|
||||
|
||||
ProcessCBBind(shader_stage);
|
||||
}
|
||||
|
||||
void Maxwell3D::BindStorageBuffer(const std::vector<u32>& parameters) {
|
||||
/**
|
||||
* Parameters description:
|
||||
* [0] = Buffer offset >> 2
|
||||
*/
|
||||
|
||||
u32 buffer_offset = parameters[0] << 2;
|
||||
|
||||
// Perform the same operations as the real macro code.
|
||||
// Note: This value is hardcoded in the macro's code.
|
||||
static constexpr u32 DefaultCBSize = 0x5F00;
|
||||
regs.const_buffer.cb_size = DefaultCBSize;
|
||||
|
||||
GPUVAddr address = regs.ssbo_info.BufferAddress();
|
||||
regs.const_buffer.cb_address_high = address >> 32;
|
||||
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
|
||||
|
||||
regs.const_buffer.cb_pos = buffer_offset;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
|
||||
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
|
||||
auto& shader = state.shader_stages[static_cast<size_t>(stage)];
|
||||
auto& bind_data = regs.cb_bind[static_cast<size_t>(stage)];
|
||||
|
||||
auto& buffer = shader.const_buffers[bind_data.index];
|
||||
|
||||
buffer.enabled = bind_data.valid.Value() != 0;
|
||||
buffer.index = bind_data.index;
|
||||
buffer.address = regs.const_buffer.BufferAddress();
|
||||
buffer.size = regs.const_buffer.cb_size;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBData(u32 value) {
|
||||
// Write the input value to the current const buffer at the current position.
|
||||
GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
|
||||
ASSERT(buffer_address != 0);
|
||||
|
||||
// Don't allow writing past the end of the buffer.
|
||||
ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
|
||||
|
||||
VAddr address =
|
||||
memory_manager.PhysicalToVirtualAddress(buffer_address + regs.const_buffer.cb_pos);
|
||||
|
||||
Memory::Write32(address, value);
|
||||
|
||||
// Increment the current buffer position.
|
||||
regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -18,21 +21,129 @@ public:
|
||||
~Maxwell3D() = default;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void WriteReg(u32 method, u32 value);
|
||||
void WriteReg(u32 method, u32 value, u32 remaining_params);
|
||||
|
||||
/// Uploads the code for a GPU macro program associated with the specified entry.
|
||||
void SubmitMacroCode(u32 entry, std::vector<u32> code);
|
||||
|
||||
/// 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;
|
||||
|
||||
static constexpr size_t NumRenderTargets = 8;
|
||||
static constexpr size_t NumCBData = 16;
|
||||
static constexpr size_t NumVertexArrays = 32;
|
||||
static constexpr size_t MaxShaderProgram = 6;
|
||||
static constexpr size_t MaxShaderStage = 5;
|
||||
// Maximum number of const buffers per shader stage.
|
||||
static constexpr size_t MaxConstBuffers = 16;
|
||||
|
||||
enum class QueryMode : u32 {
|
||||
Write = 0,
|
||||
Sync = 1,
|
||||
};
|
||||
|
||||
enum class ShaderProgram : u32 {
|
||||
VertexA = 0,
|
||||
VertexB = 1,
|
||||
TesselationControl = 2,
|
||||
TesselationEval = 3,
|
||||
Geometry = 4,
|
||||
Fragment = 5,
|
||||
};
|
||||
|
||||
enum class ShaderStage : u32 {
|
||||
Vertex = 0,
|
||||
TesselationControl = 1,
|
||||
TesselationEval = 2,
|
||||
Geometry = 3,
|
||||
Fragment = 4,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0x585);
|
||||
INSERT_PADDING_WORDS(0x200);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
u32 horiz;
|
||||
u32 vert;
|
||||
u32 format;
|
||||
u32 block_dimensions;
|
||||
u32 array_mode;
|
||||
u32 layer_stride;
|
||||
u32 base_layer;
|
||||
INSERT_PADDING_WORDS(7);
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} rt[NumRenderTargets];
|
||||
|
||||
INSERT_PADDING_WORDS(0x178);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
u32 format;
|
||||
u32 block_dimensions;
|
||||
u32 layer_stride;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} zeta;
|
||||
|
||||
INSERT_PADDING_WORDS(0x8A);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 4, u32> count;
|
||||
};
|
||||
} rt_control;
|
||||
|
||||
INSERT_PADDING_WORDS(0xCF);
|
||||
|
||||
struct {
|
||||
u32 tsc_address_high;
|
||||
u32 tsc_address_low;
|
||||
u32 tsc_limit;
|
||||
|
||||
GPUVAddr TSCAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(tsc_address_high) << 32) | tsc_address_low);
|
||||
}
|
||||
} tsc;
|
||||
|
||||
INSERT_PADDING_WORDS(0x3);
|
||||
|
||||
struct {
|
||||
u32 tic_address_high;
|
||||
u32 tic_address_low;
|
||||
u32 tic_limit;
|
||||
|
||||
GPUVAddr TICAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(tic_address_high) << 32) | tic_address_low);
|
||||
}
|
||||
} tic;
|
||||
|
||||
INSERT_PADDING_WORDS(0x22);
|
||||
|
||||
struct {
|
||||
u32 code_address_high;
|
||||
u32 code_address_low;
|
||||
|
||||
GPUVAddr CodeAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(code_address_high) << 32) | code_address_low);
|
||||
}
|
||||
} code_address;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
struct {
|
||||
u32 vertex_end_gl;
|
||||
u32 vertex_begin_gl;
|
||||
@@ -54,7 +165,98 @@ public:
|
||||
(static_cast<GPUVAddr>(query_address_high) << 32) | query_address_low);
|
||||
}
|
||||
} query;
|
||||
INSERT_PADDING_WORDS(0x772);
|
||||
|
||||
INSERT_PADDING_WORDS(0x3C);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 12, u32> stride;
|
||||
BitField<12, 1, u32> enable;
|
||||
};
|
||||
u32 start_high;
|
||||
u32 start_low;
|
||||
u32 divisor;
|
||||
|
||||
GPUVAddr StartAddress() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(start_high) << 32) |
|
||||
start_low);
|
||||
}
|
||||
} vertex_array[NumVertexArrays];
|
||||
|
||||
INSERT_PADDING_WORDS(0x40);
|
||||
|
||||
struct {
|
||||
u32 limit_high;
|
||||
u32 limit_low;
|
||||
|
||||
GPUVAddr LimitAddress() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_high) << 32) |
|
||||
limit_low);
|
||||
}
|
||||
} vertex_array_limit[NumVertexArrays];
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 1, u32> enable;
|
||||
BitField<4, 4, ShaderProgram> program;
|
||||
};
|
||||
u32 start_id;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 gpr_alloc;
|
||||
ShaderStage type;
|
||||
INSERT_PADDING_WORDS(9);
|
||||
} shader_config[MaxShaderProgram];
|
||||
|
||||
INSERT_PADDING_WORDS(0x8C);
|
||||
|
||||
struct {
|
||||
u32 cb_size;
|
||||
u32 cb_address_high;
|
||||
u32 cb_address_low;
|
||||
u32 cb_pos;
|
||||
u32 cb_data[NumCBData];
|
||||
|
||||
GPUVAddr BufferAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(cb_address_high) << 32) | cb_address_low);
|
||||
}
|
||||
} const_buffer;
|
||||
|
||||
INSERT_PADDING_WORDS(0x10);
|
||||
|
||||
struct {
|
||||
union {
|
||||
u32 raw_config;
|
||||
BitField<0, 1, u32> valid;
|
||||
BitField<4, 5, u32> index;
|
||||
};
|
||||
INSERT_PADDING_WORDS(7);
|
||||
} cb_bind[MaxShaderStage];
|
||||
|
||||
INSERT_PADDING_WORDS(0x56);
|
||||
|
||||
u32 tex_cb_index;
|
||||
|
||||
INSERT_PADDING_WORDS(0x395);
|
||||
|
||||
struct {
|
||||
/// Compressed address of a buffer that holds information about bound SSBOs.
|
||||
/// This address is usually bound to c0 in the shaders.
|
||||
u32 buffer_address;
|
||||
|
||||
GPUVAddr BufferAddress() const {
|
||||
return static_cast<GPUVAddr>(buffer_address) << 8;
|
||||
}
|
||||
} ssbo_info;
|
||||
|
||||
INSERT_PADDING_WORDS(0x11);
|
||||
|
||||
struct {
|
||||
u32 address[MaxShaderStage];
|
||||
u32 size[MaxShaderStage];
|
||||
} tex_info_buffers;
|
||||
|
||||
INSERT_PADDING_WORDS(0x102);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
@@ -62,21 +264,94 @@ public:
|
||||
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
|
||||
|
||||
struct State {
|
||||
struct ConstBufferInfo {
|
||||
GPUVAddr address;
|
||||
u32 index;
|
||||
u32 size;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct ShaderProgramInfo {
|
||||
Regs::ShaderStage stage;
|
||||
Regs::ShaderProgram program;
|
||||
GPUVAddr address;
|
||||
};
|
||||
|
||||
struct ShaderStageInfo {
|
||||
std::array<ConstBufferInfo, Regs::MaxConstBuffers> const_buffers;
|
||||
};
|
||||
|
||||
std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
|
||||
std::array<ShaderProgramInfo, Regs::MaxShaderProgram> shader_programs;
|
||||
};
|
||||
|
||||
State state{};
|
||||
|
||||
private:
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
|
||||
|
||||
/// Macro method that is currently being executed / being fed parameters.
|
||||
u32 executing_macro = 0;
|
||||
/// Parameters that have been submitted to the macro call so far.
|
||||
std::vector<u32> macro_params;
|
||||
|
||||
/**
|
||||
* Call a macro on this engine.
|
||||
* @param method Method to call
|
||||
* @param parameters Arguments to the method call
|
||||
*/
|
||||
void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
|
||||
|
||||
/// Handles a write to the QUERY_GET register.
|
||||
void ProcessQueryGet();
|
||||
|
||||
/// Handles a write to the CB_DATA[i] register.
|
||||
void ProcessCBData(u32 value);
|
||||
|
||||
/// Handles a write to the CB_BIND register.
|
||||
void ProcessCBBind(Regs::ShaderStage stage);
|
||||
|
||||
/// Handles a write to the VERTEX_END_GL register, triggering a draw.
|
||||
void DrawArrays();
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
/// Method call handlers
|
||||
void BindTextureInfoBuffer(const std::vector<u32>& parameters);
|
||||
void SetShader(const std::vector<u32>& parameters);
|
||||
void BindStorageBuffer(const std::vector<u32>& parameters);
|
||||
|
||||
struct MethodInfo {
|
||||
const char* name;
|
||||
u32 arguments;
|
||||
void (Maxwell3D::*handler)(const std::vector<u32>& parameters);
|
||||
};
|
||||
|
||||
static const std::unordered_map<u32, MethodInfo> method_handlers;
|
||||
};
|
||||
|
||||
#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(rt, 0x200);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
ASSERT_REG_POSITION(draw, 0x585);
|
||||
ASSERT_REG_POSITION(query, 0x6C0);
|
||||
ASSERT_REG_POSITION(vertex_array[0], 0x700);
|
||||
ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
|
||||
ASSERT_REG_POSITION(shader_config[0], 0x800);
|
||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
|
||||
ASSERT_REG_POSITION(cb_bind[0], 0x904);
|
||||
ASSERT_REG_POSITION(tex_cb_index, 0x982);
|
||||
ASSERT_REG_POSITION(ssbo_info, 0xD18);
|
||||
ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
|
||||
ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
|
||||
21
src/video_core/gpu.cpp
Normal file
21
src/video_core/gpu.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
GPU::GPU() {
|
||||
memory_manager = std::make_unique<MemoryManager>();
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>();
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
}
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
} // namespace Tegra
|
||||
@@ -6,14 +6,18 @@
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
namespace Engines {
|
||||
class Fermi2D;
|
||||
class Maxwell3D;
|
||||
class MaxwellCompute;
|
||||
} // namespace Engines
|
||||
|
||||
enum class EngineID {
|
||||
FERMI_TWOD_A = 0x902D, // 2D Engine
|
||||
MAXWELL_B = 0xB197, // 3D Engine
|
||||
@@ -24,13 +28,8 @@ enum class EngineID {
|
||||
|
||||
class GPU final {
|
||||
public:
|
||||
GPU() {
|
||||
memory_manager = std::make_unique<MemoryManager>();
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>();
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
}
|
||||
~GPU() = default;
|
||||
GPU();
|
||||
~GPU();
|
||||
|
||||
/// Processes a command list stored at the specified address in GPU memory.
|
||||
void ProcessCommandList(GPUVAddr address, u32 size);
|
||||
@@ -38,8 +37,10 @@ public:
|
||||
std::unique_ptr<MemoryManager> memory_manager;
|
||||
|
||||
private:
|
||||
static constexpr u32 InvalidGraphMacroEntry = 0xFFFFFFFF;
|
||||
|
||||
/// Writes a single register in the engine bound to the specified subchannel
|
||||
void WriteReg(u32 method, u32 subchannel, u32 value);
|
||||
void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);
|
||||
|
||||
/// Mapping of command subchannels to their bound engine ids.
|
||||
std::unordered_map<u32, EngineID> bound_engines;
|
||||
@@ -50,6 +51,11 @@ private:
|
||||
std::unique_ptr<Engines::Fermi2D> fermi_2d;
|
||||
/// Compute engine
|
||||
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
|
||||
|
||||
/// Entry of the macro that is currently being uploaded
|
||||
u32 current_macro_entry = InvalidGraphMacroEntry;
|
||||
/// Code being uploaded for the current macro
|
||||
std::vector<u32> current_macro_code;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -150,8 +150,8 @@ QString WaitTreeThread::GetText() const {
|
||||
case THREADSTATUS_READY:
|
||||
status = tr("ready");
|
||||
break;
|
||||
case THREADSTATUS_WAIT_ARB:
|
||||
status = tr("waiting for address 0x%1").arg(thread.wait_address, 8, 16, QLatin1Char('0'));
|
||||
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||
status = tr("waiting for HLE return");
|
||||
break;
|
||||
case THREADSTATUS_WAIT_SLEEP:
|
||||
status = tr("sleeping");
|
||||
@@ -180,7 +180,7 @@ QColor WaitTreeThread::GetColor() const {
|
||||
return QColor(Qt::GlobalColor::darkGreen);
|
||||
case THREADSTATUS_READY:
|
||||
return QColor(Qt::GlobalColor::darkBlue);
|
||||
case THREADSTATUS_WAIT_ARB:
|
||||
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||
return QColor(Qt::GlobalColor::darkRed);
|
||||
case THREADSTATUS_WAIT_SLEEP:
|
||||
return QColor(Qt::GlobalColor::darkYellow);
|
||||
|
||||
Reference in New Issue
Block a user