Fix the behaviour of svcQueryMemory and allow getting the extents of

unmapped regions

svcQueryMemory will return a valid descriptor for anything in the
address space, from 0 to 1 << addrSpaceBits, this was handled
incorrectly before and we were only returning descriptors if the
address was in a mapped region.

If an address in an unmapped region is requested then the extents of the
unmapped region up to the address space end are returned. If the address
requested is outside of the address space then the extents of the
inaccessible address space are returned.

To facilitate this support was added to MemoryManager::Get for
generating the extents of unmapped regions using the chunk list.
This commit is contained in:
Billy Laws 2020-07-05 21:21:08 +01:00 committed by ◱ PixelyIon
parent 670a80d2c4
commit f1a28f7a1c
3 changed files with 53 additions and 10 deletions

View File

@ -124,6 +124,8 @@ namespace skyline::kernel {
throw exception("32-bit address spaces are not supported");
case memory::AddressSpaceType::AddressSpace36Bit: {
addressSpace.address = 0;
addressSpace.size = 1UL << 36;
base.address = constant::BaseAddress;
base.size = 0xFF8000000;
code.address = base.address;
@ -142,6 +144,8 @@ namespace skyline::kernel {
}
case memory::AddressSpaceType::AddressSpace39Bit: {
addressSpace.address = 0;
addressSpace.size = 1UL << 39;
base.address = constant::BaseAddress;
base.size = 0x7FF8000000;
code.address = util::AlignDown(address, 0x200000);
@ -164,12 +168,50 @@ namespace skyline::kernel {
MemoryManager::MemoryManager(const DeviceState &state) : state(state) {}
std::optional<DescriptorPack> MemoryManager::Get(u64 address) {
std::optional<DescriptorPack> MemoryManager::Get(u64 address, bool requireMapped) {
auto chunk = GetChunk(address);
if (chunk)
return DescriptorPack{*GetBlock(address, chunk), *chunk};
// If the requested address is in the address space but no chunks are present then we return a new unmapped region
if (addressSpace.IsInside(address) && !requireMapped) {
auto upperChunk = std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
return address < chunk.address;
});
u64 upperAddress{};
u64 lowerAddress{};
if (upperChunk != chunkList.end()) {
upperAddress = upperChunk->address;
if (upperChunk == chunkList.begin()) {
lowerAddress = addressSpace.address;
} else {
upperChunk--;
lowerAddress = upperChunk->address + upperChunk->size;
}
} else {
upperAddress = addressSpace.address + addressSpace.size;
lowerAddress = chunkList.back().address + chunkList.back().size;
}
u64 size = upperAddress - lowerAddress;
return DescriptorPack{
.chunk = {
.address = lowerAddress,
.size = size,
.state = memory::states::Unmapped
},
.block = {
.address = lowerAddress,
.size = size,
}
};
}
return std::nullopt;
}

View File

@ -312,7 +312,8 @@ namespace skyline {
friend void svc::MapMemory(skyline::DeviceState &state);
memory::Region base{}; //!< The Region object for the entire address space
memory::Region addressSpace{}; //!< The Region object for the entire address space
memory::Region base{}; //!< The Region object for the entire address space accessible to the application
memory::Region code{}; //!< The Region object for the code memory region
memory::Region alias{}; //!< The Region object for the alias memory region
memory::Region heap{}; //!< The Region object for the heap memory region
@ -323,9 +324,10 @@ namespace skyline {
/**
* @param address The address to query in the memory map
* @param requireMapped This specifies if only mapped regions should be returned otherwise unmapped but valid regions will also be returned
* @return A DescriptorPack retrieved from the memory map
*/
std::optional<DescriptorPack> Get(u64 address);
std::optional<DescriptorPack> Get(u64 address, bool requireMapped = true);
/**
* @brief The total amount of space in bytes occupied by all memory mappings

View File

@ -180,7 +180,7 @@ namespace skyline::kernel::svc {
memory::MemoryInfo memInfo{};
auto address = state.ctx->registers.x2;
auto descriptor = state.os->memory.Get(address);
auto descriptor = state.os->memory.Get(address, false);
if (descriptor) {
memInfo = {
@ -195,16 +195,15 @@ namespace skyline::kernel::svc {
state.logger->Debug("svcQueryMemory: Address: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", memInfo.address, memInfo.size, memInfo.type, static_cast<bool>(descriptor->block.attributes.isUncached), descriptor->block.permission.r ? "R" : "-", descriptor->block.permission.w ? "W" : "-", descriptor->block.permission.x ? "X" : "-");
} else {
auto region = state.os->memory.base;
auto baseEnd = region.address + region.size;
auto addressSpaceEnd = state.os->memory.addressSpace.address + state.os->memory.addressSpace.size;
memInfo = {
.address = region.address,
.size = ~baseEnd + 1,
.type = static_cast<u32>(memory::MemoryType::Unmapped),
.address = addressSpaceEnd,
.size = ~addressSpaceEnd + 1,
.type = static_cast<u32>(memory::MemoryType::Reserved),
};
state.logger->Debug("svcQueryMemory: Cannot find block of address: 0x{:X}", address);
state.logger->Debug("svcQueryMemory: Trying to query memory outside of the application's address space: 0x{:X}", address);
}
state.process->WriteMemory(memInfo, state.ctx->registers.x0);