153 lines
7.1 KiB
C++
153 lines
7.1 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "prx/libc/include/GuestAllocations.hpp"
|
|
#include "SceTypes.hpp"
|
|
#include <cstring>
|
|
#include <exception>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <limits>
|
|
|
|
extern "C" {
|
|
void* APS5_VABI mmap_nid_postfix(void*, std::size_t, int, int, int, std::int64_t) noexcept;
|
|
int APS5_VABI munmap_nid_postfix(void*, std::size_t) noexcept;
|
|
int* APS5_VABI __error_nid_postfix();
|
|
int APS5_VABI sceKernelMapNamedFlexibleMemory(void**, std::size_t, int, int, const char*);
|
|
int APS5_VABI sceKernelMapFlexibleMemory(void**, std::size_t, int, int);
|
|
int APS5_VABI sceKernelMunmap(void*, std::size_t);
|
|
int APS5_VABI sceKernelVirtualQuery(const void*, int, VirtualQueryInfo*, std::uint64_t);
|
|
int APS5_VABI sceKernelSetVirtualRangeName(const void*, std::uint64_t, const char*);
|
|
int APS5_VABI sceKernelClearVirtualRangeName(const void*, std::uint64_t);
|
|
int APS5_VABI sceKernelAllocateDirectMemory(std::int64_t, std::int64_t, std::size_t, std::size_t, int, std::int64_t*);
|
|
int APS5_VABI sceKernelMapDirectMemory(void**, std::size_t, int, int, std::int64_t, std::size_t);
|
|
int APS5_VABI sceKernelReleaseDirectMemory(std::int64_t, std::size_t);
|
|
int APS5_VABI sceKernelReserveVirtualRange(void**, std::size_t, int, std::size_t);
|
|
}
|
|
|
|
static void Require(bool condition) {
|
|
if (!condition) {
|
|
std::fputs("Guest memory check failed\n", stderr);
|
|
std::abort();
|
|
}
|
|
}
|
|
|
|
static const char* NameAt(const void* address) {
|
|
static VirtualQueryInfo info;
|
|
Require(sceKernelVirtualQuery(address, 0, &info, sizeof(info)) == 0);
|
|
return info.name;
|
|
}
|
|
|
|
static void CheckNamedAndHintedMappings() {
|
|
constexpr std::size_t length = 0x10000;
|
|
void* first = nullptr;
|
|
Require(sceKernelMapNamedFlexibleMemory(&first, length, 3, 0, "first mapping") == 0);
|
|
Require(std::strcmp(NameAt(first), "first mapping") == 0);
|
|
auto* middle = static_cast<unsigned char*>(first) + 0x4000;
|
|
Require(sceKernelSetVirtualRangeName(middle, 0x4000, "middle") == 0);
|
|
Require(std::strcmp(NameAt(first), "first mapping") == 0);
|
|
Require(std::strcmp(NameAt(middle), "middle") == 0);
|
|
Require(sceKernelClearVirtualRangeName(first, length) == 0);
|
|
Require(NameAt(middle)[0] == '\0');
|
|
Require(sceKernelSetVirtualRangeName(nullptr, length, "x") != 0);
|
|
#if defined(__linux__)
|
|
void* hinted = first;
|
|
Require(sceKernelMapFlexibleMemory(&hinted, length, 3, 0) == 0);
|
|
Require(hinted > first && (reinterpret_cast<std::uintptr_t>(hinted) & 0x3fff) == 0);
|
|
bool rejected = false;
|
|
void* overwrite = first;
|
|
try { sceKernelMapFlexibleMemory(&overwrite, 0x4000, 3, 0x90); } catch (const std::exception&) { rejected = true; }
|
|
Require(rejected && overwrite == first);
|
|
Require(sceKernelMunmap(hinted, length) == 0);
|
|
#endif
|
|
Require(sceKernelMunmap(first, length) == 0);
|
|
}
|
|
|
|
static void CheckDirectMemoryFollowsPhysicalPages() {
|
|
constexpr std::size_t page = 0x4000;
|
|
std::int64_t phys = 0;
|
|
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 2, 0, 0, &phys) == 0);
|
|
void* first = nullptr;
|
|
Require(sceKernelMapDirectMemory(&first, page * 2, 3, 0, phys, 0) == 0);
|
|
static_cast<unsigned char*>(first)[0] = 11;
|
|
static_cast<unsigned char*>(first)[page + 5] = 22;
|
|
bool aliased = false;
|
|
void* alias = nullptr;
|
|
try { sceKernelMapDirectMemory(&alias, page, 3, 0, phys + page, 0); } catch (const std::exception&) { aliased = true; }
|
|
Require(aliased);
|
|
Require(sceKernelMunmap(first, page * 2) == 0);
|
|
void* filler = nullptr;
|
|
Require(sceKernelMapFlexibleMemory(&filler, page * 2, 3, 0) == 0);
|
|
void* second = nullptr;
|
|
Require(sceKernelMapDirectMemory(&second, page, 3, 0, phys + page, 0) == 0);
|
|
Require(second != first && static_cast<unsigned char*>(second)[5] == 22);
|
|
void* reserved = nullptr;
|
|
Require(sceKernelReserveVirtualRange(&reserved, page, 0, 0) == 0);
|
|
void* fixed = reserved;
|
|
Require(sceKernelMapDirectMemory(&fixed, page, 1, 0x10, phys, 0) == 0);
|
|
Require(fixed == reserved && static_cast<unsigned char*>(fixed)[0] == 11);
|
|
Require(sceKernelMunmap(fixed, page) == 0);
|
|
Require(sceKernelMunmap(second, page) == 0);
|
|
Require(sceKernelReleaseDirectMemory(phys, page * 2) == 0);
|
|
std::int64_t again = 0;
|
|
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 2, 0, 0, &again) == 0 && again == phys);
|
|
void* fresh = nullptr;
|
|
Require(sceKernelMapDirectMemory(&fresh, page * 2, 3, 0, again, 0) == 0);
|
|
Require(static_cast<unsigned char*>(fresh)[0] == 0 && static_cast<unsigned char*>(fresh)[page + 5] == 0);
|
|
Require(sceKernelMunmap(fresh, page * 2) == 0);
|
|
Require(sceKernelMunmap(filler, page * 2) == 0);
|
|
Require(sceKernelReleaseDirectMemory(again, page * 2) == 0);
|
|
}
|
|
|
|
int main() {
|
|
CheckNamedAndHintedMappings();
|
|
CheckDirectMemoryFollowsPhysicalPages();
|
|
constexpr std::size_t page = 0x4000;
|
|
const auto failed = reinterpret_cast<void*>(static_cast<std::uintptr_t>(-1));
|
|
const auto reject = [&](std::size_t length, int protection, int flags, int fd,
|
|
std::int64_t offset, int error) {
|
|
*__error_nid_postfix() = 0;
|
|
Require(mmap_nid_postfix(nullptr, length, protection, flags, fd, offset) == failed);
|
|
Require(*__error_nid_postfix() == error);
|
|
};
|
|
reject(0, 3, 0x1002, -1, 0, 22);
|
|
reject(std::numeric_limits<std::size_t>::max(), 3, 0x1002, -1, 0, 22);
|
|
reject(page, 8, 0x1002, -1, 0, 22);
|
|
reject(page, 3, 0x1002, 0, 0, 22);
|
|
reject(page, 3, 0x1002, -1, 1, 22);
|
|
reject(page, 3, 0x1001, -1, 0, 45); // shared
|
|
reject(page, 3, 0x1012, -1, 0, 45); // fixed
|
|
reject(page, 3, 0x2, 0, 0, 45); // file-backed
|
|
reject(page, 3, 0x22, -1, 0, 45); // Linux MAP_ANON is not guest MAP_ANON
|
|
|
|
auto* memory = static_cast<unsigned char*>(mmap_nid_postfix(nullptr, page * 3 - 1, 3, 0x1002, -1, 0));
|
|
Require(memory != failed && (reinterpret_cast<std::uintptr_t>(memory) & (page - 1)) == 0);
|
|
for (std::size_t i = 0; i < page * 3; ++i) Require(memory[i] == 0);
|
|
memory[0] = 42;
|
|
memory[page * 2] = 73;
|
|
{
|
|
GuestAllocations::Mutation mutation;
|
|
const auto range = mutation.Find(memory);
|
|
Require(range.bytes == page * 3 && range.readable && range.writable);
|
|
}
|
|
Require(munmap_nid_postfix(memory + 1, page) == -1 && *__error_nid_postfix() == 22);
|
|
Require(munmap_nid_postfix(memory, 0) == -1 && *__error_nid_postfix() == 22);
|
|
Require(memory[0] == 42);
|
|
Require(munmap_nid_postfix(memory + page, 1) == 0); // round to one guest page
|
|
Require(memory[0] == 42 && memory[page * 2] == 73);
|
|
Require(munmap_nid_postfix(memory, page) == 0);
|
|
Require(memory[page * 2] == 73);
|
|
Require(munmap_nid_postfix(memory + page * 2, page) == 0);
|
|
Require(munmap_nid_postfix(memory, page) == -1);
|
|
for (int protection : {0, 1, 3, 5}) {
|
|
void* mapped = mmap_nid_postfix(memory, 1, protection, 0x1002, -1, 0);
|
|
Require(mapped != failed);
|
|
{
|
|
GuestAllocations::Mutation mutation;
|
|
const auto range = mutation.Find(mapped);
|
|
Require(range.readable == ((protection & 3) != 0));
|
|
Require(range.writable == ((protection & 2) != 0));
|
|
}
|
|
Require(munmap_nid_postfix(mapped, 1) == 0);
|
|
}
|
|
}
|