Compare commits

..

10 Commits

Author SHA1 Message Date
Jarek Syrylak
a78a5003fa GDB Stub should work now. 2018-06-04 16:03:01 +01:00
bunnei
049ce242a4 Merge pull request #499 from bunnei/am-stuff
am: Implement CreateStorage, PushInData, etc.
2018-06-03 23:43:52 -04:00
bunnei
876b805e50 am: Implement ILibraryAppletAccessor::PushInData. 2018-06-03 22:10:06 -04:00
bunnei
2dcb98226b am: Implement IStorageAccessor::Write. 2018-06-03 22:10:06 -04:00
bunnei
9fedfbe141 am: Cleanup IStorageAccessor::Read. 2018-06-03 22:10:06 -04:00
bunnei
d73c22bf4d am: Implement ILibraryAppletCreator::CreateStorage. 2018-06-03 22:10:05 -04:00
bunnei
ba117854f9 Merge pull request #500 from Subv/long_queries
GPU: Partial implementation of long GPU queries.
2018-06-03 21:24:50 -04:00
bunnei
527c098ff6 Merge pull request #498 from bunnei/texs-mask
gl_shader_decompiler: Implement TEXS component mask.
2018-06-03 21:22:12 -04:00
Subv
d57333406d GPU: Partial implementation of long GPU queries.
Long queries write a 128-bit result value to memory, which consists of a 64 bit query value and a 64 bit timestamp.

In this implementation, only select=Zero of the Crop unit is implemented, this writes the query sequence as a 64 bit value, and a 0u64 value for the timestamp, since we emulate an infinitely fast GPU.

This specific type was hwtested, but more rigorous tests should be performed in the future for the other types.
2018-06-03 19:17:31 -05:00
bunnei
1efcba346a gl_shader_decompiler: Implement TEXS component mask. 2018-06-03 12:08:17 -04:00
9 changed files with 413 additions and 166 deletions

View File

@@ -10,6 +10,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/gdbstub/gdbstub.h"
// Load Unicorn DLL once on Windows using RAII
#ifdef _MSC_VER
@@ -35,6 +36,19 @@ LoadDll LoadDll::g_load_dll;
} \
} while (0)
static GDBStub::BreakpointAddress bkpt = {0};
static bool bkptHit = false;
static void CodeHook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
{
bkpt = GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute);
if(GDBStub::IsMemoryBreak() || (bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address))
{
bkptHit = true;
uc_emu_stop(uc);
}
}
static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
u32 esr{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
@@ -67,6 +81,10 @@ ARM_Unicorn::ARM_Unicorn() {
uc_hook hook{};
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1));
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1));
if(GDBStub::IsServerEnabled())
{
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1));
}
}
ARM_Unicorn::~ARM_Unicorn() {
@@ -155,7 +173,14 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
}
void ARM_Unicorn::Run() {
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
if(GDBStub::IsServerEnabled())
{
ExecuteInstructions(std::max(4000000, 0));
}
else
{
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
}
}
void ARM_Unicorn::Step() {
@@ -168,6 +193,21 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
CoreTiming::AddTicks(num_instructions);
if(GDBStub::IsServerEnabled())
{
if(bkptHit)
{
uc_reg_write(uc, UC_ARM64_REG_PC, &bkpt.address);
}
Kernel::Thread *thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
if(bkptHit)
{
bkptHit = false;
GDBStub::Break();
}
GDBStub::SendSig(thread, 5);
}
}
void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {

View File

@@ -16,6 +16,7 @@
#include <fcntl.h>
#ifdef _WIN32
#define NTDDI_VERSION NTDDI_WIN8
#include <winsock2.h>
// winsock2.h needs to be included first to prevent winsock.h being included by other includes
#include <io.h>
@@ -31,12 +32,19 @@
#endif
#include "common/logging/log.h"
//#undef NGLOG_INFO
//#define NGLOG_INFO NGLOG_ERROR
//#undef NGLOG_DEBUG
//#define NGLOG_DEBUG NGLOG_ERROR
#include "common/string_util.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
const int GDB_BUFFER_SIZE = 10000;
@@ -137,9 +145,11 @@ static u8 command_buffer[GDB_BUFFER_SIZE];
static u32 command_length;
static u32 latest_signal = 0;
static bool step_break = false;
static bool memory_break = false;
Kernel::Thread *thread = nullptr;
int current_core = 0;
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
// so default to a port outside of that range.
static u16 gdbstub_port = 24689;
@@ -165,6 +175,79 @@ static std::map<u64, Breakpoint> breakpoints_execute;
static std::map<u64, Breakpoint> breakpoints_read;
static std::map<u64, Breakpoint> breakpoints_write;
Kernel::Thread *FindThread(int id, int& current_core)
{
Kernel::Thread *thread = nullptr;
for(int core = 0; core < Core::NUM_CPU_CORES; core++)
{
auto list = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for(auto it = list.begin(); it != list.end(); it++)
{
if((*it)->GetThreadId() == id)
{
thread = &(*(*it));
current_core = core;
break;
}
}
}
return thread;
}
static u64 regr(int id, Kernel::Thread *thread = nullptr)
{
if(!thread)
{
return 0;
}
if(id < SP_REGISTER)
{
return thread->context.cpu_registers[id];
}
else if(id == SP_REGISTER)
{
return thread->context.sp;
}
else if(id == PC_REGISTER)
{
return thread->context.pc;
}
else if(id == CPSR_REGISTER)
{
return thread->context.cpsr;
}
else
{
return 0;
}
}
static void regw(int id, u64 val, Kernel::Thread *thread = nullptr)
{
if(!thread)
{
return;
}
if(id < SP_REGISTER)
{
thread->context.cpu_registers[id] = val;
}
else if(id == SP_REGISTER)
{
thread->context.sp = val;
}
else if(id == PC_REGISTER)
{
thread->context.pc = val;
}
else if(id == CPSR_REGISTER)
{
thread->context.cpsr = val;
}
}
/**
* Turns hex string character into the equivalent byte.
*
@@ -193,7 +276,7 @@ static u8 NibbleToHex(u8 n) {
if (n < 0xA) {
return '0' + n;
} else {
return 'A' + n - 0xA;
return 'a' + n - 0xA;
}
}
@@ -439,6 +522,8 @@ static void SendReply(const char* reply) {
return;
}
NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply);
memset(command_buffer, 0, sizeof(command_buffer));
command_length = static_cast<u32>(strlen(reply));
@@ -470,31 +555,99 @@ static void SendReply(const char* reply) {
}
/// Handle query command from gdb client.
static void HandleQuery() {
static void HandleQuery()
{
NGLOG_DEBUG(Debug_GDBStub, "gdb: query '{}'", command_buffer + 1);
const char* query = reinterpret_cast<const char*>(command_buffer + 1);
if (strcmp(query, "TStatus") == 0) {
if(strcmp(query, "TStatus") == 0)
{
SendReply("T0");
} else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
}
else if(strncmp(query, "Supported", strlen("Supported")) == 0)
{
// PacketSize needs to be large enough for target xml
SendReply("PacketSize=2000;qXfer:features:read+");
} else if (strncmp(query, "Xfer:features:read:target.xml:",
strlen("Xfer:features:read:target.xml:")) == 0) {
}
else if(strncmp(query, "Xfer:features:read:target.xml:",
strlen("Xfer:features:read:target.xml:")) == 0)
{
SendReply(target_xml);
} else {
}
else if(strncmp(query, "Offsets", strlen("Offsets")) == 0)
{
std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR);
SendReply(buffer.c_str());
}
else if(strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0)
{
std::string val = "m";
for(int core = 0; core < Core::NUM_CPU_CORES; core++)
{
auto list = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for(auto it = list.begin(); it != list.end(); it++)
{
char tmp[17] = {0};
memset(tmp, 0, sizeof(tmp));
sprintf(tmp, "%x", (*it)->GetThreadId());
val += (char*)tmp;
val += ",";
}
}
val.pop_back();
SendReply(val.c_str());
}
else if(strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0)
{
SendReply("l");
}
else
{
SendReply("");
}
}
/// Handle set thread command from gdb client.
static void HandleSetThread() {
if (memcmp(command_buffer, "Hg0", 3) == 0 || memcmp(command_buffer, "Hc-1", 4) == 0 ||
memcmp(command_buffer, "Hc0", 4) == 0 || memcmp(command_buffer, "Hc1", 4) == 0) {
return SendReply("OK");
static void HandleSetThread()
{
if(memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0)
{
int threadid = -1;
if(command_buffer[2] != '-')
{
threadid = (int)HexToInt(command_buffer + 2, strlen((char*)command_buffer + 2));
}
if(threadid >= 1)
{
thread = FindThread(threadid, current_core);
}
if(!thread)
{
threadid = 1;
thread = FindThread(threadid, current_core);
}
if(thread)
{
SendReply("OK");
return;
}
}
SendReply("E01");
}
static void isThreadAlive()
{
int threadid = (int)HexToInt(command_buffer + 1, strlen((char*)command_buffer + 1));
if(threadid == 0)
{
threadid = 1;
}
if(FindThread(threadid, current_core))
{
SendReply("OK");
return;
}
SendReply("E01");
}
@@ -503,15 +656,29 @@ static void HandleSetThread() {
*
* @param signal Signal to be sent to client.
*/
static void SendSignal(u32 signal) {
if (gdbserver_socket == -1) {
static void SendSignal(Kernel::Thread *thread, u32 signal, bool full = true)
{
if(gdbserver_socket == -1)
{
return;
}
latest_signal = signal;
std::string buffer = fmt::format("T{:02x}", latest_signal);
NGLOG_DEBUG(Debug_GDBStub, "Response: {}", buffer);
std::string buffer;
if(full)
{
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER, htonll(regr(PC_REGISTER, thread)), SP_REGISTER, htonll(regr(SP_REGISTER, thread)));
}
else
{
buffer = fmt::format("T{:02x};", latest_signal);
}
buffer += fmt::format("thread:{:x};", thread->GetThreadId());
//NGLOG_ERROR(Debug_GDBStub, "Sig: {}", buffer.c_str());
SendReply(buffer.c_str());
}
@@ -527,7 +694,7 @@ static void ReadCommand() {
} else if (c == 0x03) {
NGLOG_INFO(Debug_GDBStub, "gdb: found break command");
halt_loop = true;
SendSignal(SIGTRAP);
SendSignal(thread, SIGTRAP);
return;
} else if (c != GDB_STUB_START) {
NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c);
@@ -598,11 +765,11 @@ static void ReadRegister() {
}
if (id <= SP_REGISTER) {
LongToGdbHex(reply, Core::CurrentArmInterface().GetReg(static_cast<int>(id)));
LongToGdbHex(reply, regr(id, thread));
} else if (id == PC_REGISTER) {
LongToGdbHex(reply, Core::CurrentArmInterface().GetPC());
LongToGdbHex(reply, regr(id, thread));
} else if (id == CPSR_REGISTER) {
IntToGdbHex(reply, Core::CurrentArmInterface().GetCPSR());
IntToGdbHex(reply, (u32)regr(id, thread));
} else {
return SendReply("E01");
}
@@ -618,16 +785,16 @@ static void ReadRegisters() {
u8* bufptr = buffer;
for (int reg = 0; reg <= SP_REGISTER; reg++) {
LongToGdbHex(bufptr + reg * 16, Core::CurrentArmInterface().GetReg(reg));
LongToGdbHex(bufptr + reg * 16, regr(reg, thread));
}
bufptr += (32 * 16);
LongToGdbHex(bufptr, Core::CurrentArmInterface().GetPC());
LongToGdbHex(bufptr, regr(PC_REGISTER, thread));
bufptr += 16;
IntToGdbHex(bufptr, Core::CurrentArmInterface().GetCPSR());
IntToGdbHex(bufptr, (u32)regr(CPSR_REGISTER, thread));
bufptr += 8;
@@ -646,11 +813,11 @@ static void WriteRegister() {
}
if (id <= SP_REGISTER) {
Core::CurrentArmInterface().SetReg(id, GdbHexToLong(buffer_ptr));
regw(id, GdbHexToLong(buffer_ptr), thread);
} else if (id == PC_REGISTER) {
Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr));
regw(id, GdbHexToLong(buffer_ptr), thread);
} else if (id == CPSR_REGISTER) {
Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr));
regw(id, GdbHexToInt(buffer_ptr), thread);
} else {
return SendReply("E01");
}
@@ -667,11 +834,11 @@ static void WriteRegisters() {
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
if (reg <= SP_REGISTER) {
Core::CurrentArmInterface().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16));
regw(reg, GdbHexToLong(buffer_ptr + i * 16), thread);
} else if (reg == PC_REGISTER) {
Core::CurrentArmInterface().SetPC(GdbHexToLong(buffer_ptr + i * 16));
regw(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), thread);
} else if (reg == CPSR_REGISTER) {
Core::CurrentArmInterface().SetCPSR(GdbHexToInt(buffer_ptr + i * 16));
regw(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), thread);
} else {
UNIMPLEMENTED();
}
@@ -731,10 +898,13 @@ static void WriteMemory() {
SendReply("OK");
}
bool send_trap = false;
void Break(bool is_memory_break) {
if (!halt_loop) {
halt_loop = true;
SendSignal(SIGTRAP);
//SendSignal(SIGTRAP);
send_trap = true;
}
memory_break = is_memory_break;
@@ -744,8 +914,8 @@ void Break(bool is_memory_break) {
static void Step() {
step_loop = true;
halt_loop = true;
step_break = true;
SendSignal(SIGTRAP);
//SendSignal(SIGTRAP);
send_trap = true;
}
bool IsMemoryBreak() {
@@ -759,7 +929,6 @@ bool IsMemoryBreak() {
/// Tell the CPU to continue executing.
static void Continue() {
memory_break = false;
step_break = false;
step_loop = false;
halt_loop = false;
}
@@ -898,7 +1067,7 @@ void HandlePacket() {
HandleSetThread();
break;
case '?':
SendSignal(latest_signal);
SendSignal(thread, latest_signal);
break;
case 'k':
Shutdown();
@@ -935,6 +1104,9 @@ void HandlePacket() {
case 'Z':
AddBreakpoint();
break;
case 'T':
isThreadAlive();
break;
default:
SendReply("");
break;
@@ -1079,4 +1251,13 @@ bool GetCpuStepFlag() {
void SetCpuStepFlag(bool is_step) {
step_loop = is_step;
}
void SendSig(void *_thread, int sig)
{
if(send_trap)
{
send_trap = false;
SendSignal((Kernel::Thread *)_thread, sig);
}
}
}; // namespace GDBStub

View File

@@ -91,4 +91,6 @@ bool GetCpuStepFlag();
* @param is_step
*/
void SetCpuStepFlag(bool is_step);
void SendSig(void *thread, int sig);
} // namespace GDBStub

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cinttypes>
#include <stack>
#include "core/file_sys/filesystem.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
@@ -348,6 +349,87 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_AM, "(STUBBED) called");
}
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(std::vector<u8> buffer)
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{10, &IStorageAccessor::Write, "Write"},
{11, &IStorageAccessor::Read, "Read"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void GetSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(buffer.size()));
NGLOG_DEBUG(Service_AM, "called");
}
void Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
const std::vector<u8> data{ctx.ReadBuffer()};
ASSERT(offset + data.size() <= buffer.size());
std::memcpy(&buffer[offset], data.data(), data.size());
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
rb.Push(RESULT_SUCCESS);
NGLOG_DEBUG(Service_AM, "called, offset={}", offset);
}
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
const size_t size{ctx.GetWriteBufferSize()};
ASSERT(offset + size <= buffer.size());
ctx.WriteBuffer(buffer.data() + offset, size);
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
rb.Push(RESULT_SUCCESS);
NGLOG_DEBUG(Service_AM, "called, offset={}", offset);
}
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
{1, nullptr, "OpenTransferStorage"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void Open(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
NGLOG_DEBUG(Service_AM, "called");
}
};
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
public:
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
@@ -359,7 +441,7 @@ public:
{25, nullptr, "Terminate"},
{30, nullptr, "GetResult"},
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
{100, nullptr, "PushInData"},
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
{101, nullptr, "PopOutData"},
{102, nullptr, "PushExtraStorage"},
{103, nullptr, "PushInteractiveInData"},
@@ -388,6 +470,17 @@ private:
NGLOG_WARNING(Service_AM, "(STUBBED) called");
}
void PushInData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
rb.Push(RESULT_SUCCESS);
NGLOG_DEBUG(Service_AM, "called");
}
std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
Kernel::SharedPtr<Kernel::Event> state_changed_event;
};
@@ -396,7 +489,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
{1, nullptr, "TerminateAllLibraryApplets"},
{2, nullptr, "AreAnyLibraryAppletsLeft"},
{10, nullptr, "CreateStorage"},
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
{11, nullptr, "CreateTransferMemoryStorage"},
{12, nullptr, "CreateHandleStorage"},
};
@@ -412,72 +505,17 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
NGLOG_DEBUG(Service_AM, "called");
}
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(std::vector<u8> buffer)
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{10, nullptr, "Write"},
{11, &IStorageAccessor::Read, "Read"},
};
RegisterHandlers(functions);
}
void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size{rp.Pop<u64>()};
std::vector<u8> buffer(size);
private:
std::vector<u8> buffer;
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 1)};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(std::move(buffer));
void GetSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(buffer.size()));
NGLOG_DEBUG(Service_AM, "called");
}
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 offset = rp.Pop<u64>();
const size_t size{ctx.GetWriteBufferSize()};
ASSERT(offset + size <= buffer.size());
ctx.WriteBuffer(buffer.data() + offset, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
NGLOG_DEBUG(Service_AM, "called");
}
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
{1, nullptr, "OpenTransferStorage"},
};
RegisterHandlers(functions);
}
private:
std::vector<u8> buffer;
void Open(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
NGLOG_DEBUG(Service_AM, "called");
}
};
NGLOG_DEBUG(Service_AM, "called, size={}", size);
}
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
static const FunctionInfo functions[] = {

View File

@@ -121,6 +121,7 @@ public:
private:
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
void CreateStorage(Kernel::HLERequestContext& ctx);
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {

View File

@@ -26,19 +26,11 @@ public:
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
IocAllocGPFIFOCommand = 0x40084805,
IocSetClientDataCommand = 0x40084714,
IocGetClientDataCommand = 0x80084715,
IocZCullBind = 0xc010480b,
IocSetErrorNotifierCommand = 0xC018480C,
IocChannelSetPriorityCommand = 0x4004480D,
IocEnableCommand = 0x0000480E,
IocDisableCommand = 0x0000480F,
IocPreemptCommand = 0x00004810,
IocForceResetCommand = 0x00004811,
IocEventIdControlCommand = 0x40084812,
IocGetErrorNotificationCommand = 0xC0104817,
IocAllocGPFIFOExCommand = 0x40204818,
IocAllocGPFIFOEx2Command = 0xC020481A,
IocAllocObjCtxCommand = 0xC0104809,
IocChannelGetWaitbaseCommand = 0xC0080003,
@@ -64,12 +56,6 @@ private:
};
static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
struct IoctlAllocGPFIFO {
u32_le num_entries;
u32_le flags;
};
static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
struct IoctlClientData {
u64_le data;
};
@@ -90,45 +76,12 @@ private:
};
static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
struct IoctlChannelSetPriority {
u32_le priority;
};
static_assert(sizeof(IoctlChannelSetPriority) == 4,
"IoctlChannelSetPriority is incorrect size");
struct IoctlEventIdControl {
u32_le cmd; // 0=disable, 1=enable, 2=clear
u32_le id;
};
static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
struct IoctlGetErrorNotification {
u64_le timestamp;
u32_le info32;
u16_le info16;
u16_le status; // always 0xFFFF
};
static_assert(sizeof(IoctlGetErrorNotification) == 16,
"IoctlGetErrorNotification is incorrect size");
struct IoctlFence {
u32_le id;
u32_le value;
};
static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size");
struct IoctlAllocGpfifoEx {
u32_le num_entries;
u32_le flags;
u32_le unk0;
u32_le unk1;
u32_le unk2;
u32_le unk3;
u32_le unk4;
u32_le unk5;
};
static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
struct IoctlAllocGpfifoEx2 {
u32_le num_entries; // in
u32_le flags; // in

View File

@@ -156,16 +156,15 @@ void Maxwell3D::ProcessQueryGet() {
// TODO(Subv): Support the other query units.
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
"Units other than CROP are unimplemented");
ASSERT_MSG(regs.query.query_get.short_query,
"Writing the entire query result structure is unimplemented");
u32 value = Memory::Read32(*address);
u32 result = 0;
u64 result = 0;
// TODO(Subv): Support the other query variables
switch (regs.query.query_get.select) {
case Regs::QuerySelect::Zero:
result = 0;
// This seems to actually write the query sequence to the query address.
result = regs.query.query_sequence;
break;
default:
UNIMPLEMENTED_MSG("Unimplemented query select type {}",
@@ -174,15 +173,31 @@ void Maxwell3D::ProcessQueryGet() {
// TODO(Subv): Research and implement how query sync conditions work.
struct LongQueryResult {
u64_le value;
u64_le timestamp;
};
static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size");
switch (regs.query.query_get.mode) {
case Regs::QueryMode::Write:
case Regs::QueryMode::Write2: {
// Write the current query sequence to the sequence address.
u32 sequence = regs.query.query_sequence;
Memory::Write32(*address, sequence);
// TODO(Subv): Write the proper query response structure to the address when not using short
// mode.
if (regs.query.query_get.short_query) {
// Write the current query sequence to the sequence address.
// TODO(Subv): Find out what happens if you use a long query type but mark it as a short
// query.
Memory::Write32(*address, sequence);
} else {
// Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
// GPU, this command may actually take a while to complete in real hardware due to GPU
// wait queues.
LongQueryResult query_result{};
query_result.value = result;
// TODO(Subv): Generate a real GPU timestamp and write it here instead of 0
query_result.timestamp = 0;
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
}
break;
}
default:

View File

@@ -270,8 +270,22 @@ union Instruction {
} tex;
union {
// TODO(bunnei): This is just a guess, needs to be verified
BitField<52, 1, u64> enable_g_component;
BitField<50, 3, u64> component_mask_selector;
BitField<28, 8, Register> gpr28;
bool HasTwoDestinations() const {
return gpr28.Value() != Register::ZeroIndex;
}
bool IsComponentEnabled(size_t component) const {
static constexpr std::array<size_t, 5> one_dest_mask{0x1, 0x2, 0x4, 0x8, 0x3};
static constexpr std::array<size_t, 5> two_dest_mask{0x7, 0xb, 0xd, 0xe, 0xf};
const auto& mask{HasTwoDestinations() ? two_dest_mask : one_dest_mask};
ASSERT(component_mask_selector < mask.size());
return ((1 << component) & mask[component_mask_selector]) != 0;
}
} texs;
BitField<61, 1, u64> is_b_imm;

View File

@@ -938,18 +938,21 @@ private:
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA goes
// into gpr28+0 and gpr28+1
size_t offset{};
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
for (unsigned elem = 0; elem < 2; ++elem) {
if (dest + elem >= Register::ZeroIndex) {
// Skip invalid register values
break;
if (!instr.texs.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(dest, elem + offset, texture, 1, 4, false, elem);
if (!instr.texs.enable_g_component) {
// Skip the second component
break;
}
}
if (!instr.texs.HasTwoDestinations()) {
// Skip the second destination
break;
}
offset += 2;
}
--shader.scope;