mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-08-09 17:48:48 +00:00
kern: SvcReadDebugProcessMemory, SvcWriteDebugProcessMemory
This commit is contained in:
parent
1ffe08672d
commit
f6f43300e0
7 changed files with 669 additions and 4 deletions
|
@ -48,6 +48,192 @@ namespace ams::kern {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KDebugBase::ReadMemory(KProcessAddress buffer, KProcessAddress address, size_t size) {
|
||||
/* Lock ourselves. */
|
||||
KScopedLightLock lk(this->lock);
|
||||
|
||||
/* Check that we have a valid process. */
|
||||
R_UNLESS(this->process != nullptr, svc::ResultProcessTerminated());
|
||||
R_UNLESS(!this->process->IsTerminated(), svc::ResultProcessTerminated());
|
||||
|
||||
/* Get the page tables. */
|
||||
KProcessPageTable &debugger_pt = GetCurrentProcess().GetPageTable();
|
||||
KProcessPageTable &target_pt = this->process->GetPageTable();
|
||||
|
||||
/* Verify that the regions are in range. */
|
||||
R_UNLESS(target_pt.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(debugger_pt.Contains(buffer, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Iterate over the target process's memory blocks. */
|
||||
KProcessAddress cur_address = address;
|
||||
size_t remaining = size;
|
||||
while (remaining > 0) {
|
||||
/* Get the current memory info. */
|
||||
KMemoryInfo info;
|
||||
ams::svc::PageInfo pi;
|
||||
R_TRY(target_pt.QueryInfo(std::addressof(info), std::addressof(pi), cur_address));
|
||||
|
||||
/* Check that the memory is accessible. */
|
||||
R_UNLESS(info.GetState() != static_cast<KMemoryState>(ams::svc::MemoryState_Inaccessible), svc::ResultInvalidAddress());
|
||||
|
||||
/* Get the current size. */
|
||||
const size_t cur_size = std::min(remaining, info.GetEndAddress() - GetInteger(cur_address));
|
||||
|
||||
/* Read the memory. */
|
||||
if (info.GetState() != KMemoryState_Io) {
|
||||
/* The memory is normal memory. */
|
||||
R_TRY(target_pt.ReadDebugMemory(GetVoidPointer(buffer), cur_address, cur_size));
|
||||
} else {
|
||||
/* The memory is IO memory. */
|
||||
|
||||
/* Verify that the memory is readable. */
|
||||
R_UNLESS((info.GetPermission() & KMemoryPermission_UserRead) == KMemoryPermission_UserRead, svc::ResultInvalidAddress());
|
||||
|
||||
/* Get the physical address of the memory. */
|
||||
/* NOTE: Nintendo does not verify the result of this call. */
|
||||
KPhysicalAddress phys_addr;
|
||||
target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address);
|
||||
|
||||
/* Map the address as IO in the current process. */
|
||||
R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserRead));
|
||||
|
||||
/* Get the address of the newly mapped IO. */
|
||||
KProcessAddress io_address;
|
||||
Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize);
|
||||
MESOSPHERE_R_ASSERT(query_result);
|
||||
R_TRY(query_result);
|
||||
|
||||
/* Ensure we clean up the new mapping on scope exit. */
|
||||
ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); };
|
||||
|
||||
/* Adjust the io address for alignment. */
|
||||
io_address += (GetInteger(cur_address) & (PageSize - 1));
|
||||
|
||||
/* Get the readable size. */
|
||||
const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address));
|
||||
|
||||
/* Read the memory. */
|
||||
switch ((GetInteger(cur_address) | readable_size) & 3) {
|
||||
case 0:
|
||||
{
|
||||
R_UNLESS(UserspaceAccess::ReadIoMemory32Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer());
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
R_UNLESS(UserspaceAccess::ReadIoMemory16Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
R_UNLESS(UserspaceAccess::ReadIoMemory8Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
buffer += cur_size;
|
||||
cur_address += cur_size;
|
||||
remaining -= cur_size;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KDebugBase::WriteMemory(KProcessAddress buffer, KProcessAddress address, size_t size) {
|
||||
/* Lock ourselves. */
|
||||
KScopedLightLock lk(this->lock);
|
||||
|
||||
/* Check that we have a valid process. */
|
||||
R_UNLESS(this->process != nullptr, svc::ResultProcessTerminated());
|
||||
R_UNLESS(!this->process->IsTerminated(), svc::ResultProcessTerminated());
|
||||
|
||||
/* Get the page tables. */
|
||||
KProcessPageTable &debugger_pt = GetCurrentProcess().GetPageTable();
|
||||
KProcessPageTable &target_pt = this->process->GetPageTable();
|
||||
|
||||
/* Verify that the regions are in range. */
|
||||
R_UNLESS(target_pt.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(debugger_pt.Contains(buffer, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Iterate over the target process's memory blocks. */
|
||||
KProcessAddress cur_address = address;
|
||||
size_t remaining = size;
|
||||
while (remaining > 0) {
|
||||
/* Get the current memory info. */
|
||||
KMemoryInfo info;
|
||||
ams::svc::PageInfo pi;
|
||||
R_TRY(target_pt.QueryInfo(std::addressof(info), std::addressof(pi), cur_address));
|
||||
|
||||
/* Check that the memory is accessible. */
|
||||
R_UNLESS(info.GetState() != static_cast<KMemoryState>(ams::svc::MemoryState_Inaccessible), svc::ResultInvalidAddress());
|
||||
|
||||
/* Get the current size. */
|
||||
const size_t cur_size = std::min(remaining, info.GetEndAddress() - GetInteger(cur_address));
|
||||
|
||||
/* Read the memory. */
|
||||
if (info.GetState() != KMemoryState_Io) {
|
||||
/* The memory is normal memory. */
|
||||
R_TRY(target_pt.WriteDebugMemory(cur_address, GetVoidPointer(buffer), cur_size));
|
||||
} else {
|
||||
/* The memory is IO memory. */
|
||||
|
||||
/* Verify that the memory is writable. */
|
||||
R_UNLESS((info.GetPermission() & KMemoryPermission_UserReadWrite) == KMemoryPermission_UserReadWrite, svc::ResultInvalidAddress());
|
||||
|
||||
/* Get the physical address of the memory. */
|
||||
/* NOTE: Nintendo does not verify the result of this call. */
|
||||
KPhysicalAddress phys_addr;
|
||||
target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address);
|
||||
|
||||
/* Map the address as IO in the current process. */
|
||||
R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserReadWrite));
|
||||
|
||||
/* Get the address of the newly mapped IO. */
|
||||
KProcessAddress io_address;
|
||||
Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize);
|
||||
MESOSPHERE_R_ASSERT(query_result);
|
||||
R_TRY(query_result);
|
||||
|
||||
/* Ensure we clean up the new mapping on scope exit. */
|
||||
ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); };
|
||||
|
||||
/* Adjust the io address for alignment. */
|
||||
io_address += (GetInteger(cur_address) & (PageSize - 1));
|
||||
|
||||
/* Get the readable size. */
|
||||
const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address));
|
||||
|
||||
/* Read the memory. */
|
||||
switch ((GetInteger(cur_address) | readable_size) & 3) {
|
||||
case 0:
|
||||
{
|
||||
R_UNLESS(UserspaceAccess::WriteIoMemory32Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer());
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
R_UNLESS(UserspaceAccess::WriteIoMemory16Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
R_UNLESS(UserspaceAccess::WriteIoMemory8Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
buffer += cur_size;
|
||||
cur_address += cur_size;
|
||||
remaining -= cur_size;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result KDebugBase::Attach(KProcess *target) {
|
||||
/* Check that the process isn't null. */
|
||||
MESOSPHERE_ASSERT(target != nullptr);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue