mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-10 07:06:34 +00:00
kern: implement all device address space svcs
This commit is contained in:
parent
9beb05da50
commit
9c4c058307
6 changed files with 427 additions and 35 deletions
|
@ -35,6 +35,14 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
u32 hs_attached_value;
|
u32 hs_attached_value;
|
||||||
u32 hs_detached_value;
|
u32 hs_detached_value;
|
||||||
private:
|
private:
|
||||||
|
static ALWAYS_INLINE bool IsHeapVirtualAddress(KVirtualAddress addr) {
|
||||||
|
return KMemoryLayout::IsHeapVirtualAddress(nullptr, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE bool IsHeapPhysicalAddress(KPhysicalAddress addr) {
|
||||||
|
return KMemoryLayout::IsHeapPhysicalAddress(nullptr, addr);
|
||||||
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
|
static ALWAYS_INLINE KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
|
||||||
return KPageTable::GetHeapVirtualAddress(addr);
|
return KPageTable::GetHeapVirtualAddress(addr);
|
||||||
}
|
}
|
||||||
|
@ -62,9 +70,9 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
Result Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings);
|
Result Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings);
|
||||||
Result Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address);
|
Result Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address);
|
||||||
private:
|
private:
|
||||||
Result MapDevicePage(size_t *out_mapped_size, s32 *out_num_pt, s32 max_pt, KPhysicalAddress phys_addr, u64 size, KDeviceVirtualAddress address, ams::svc::MemoryPermission device_perm);
|
Result MapDevicePage(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KPhysicalAddress phys_addr, u64 size, KDeviceVirtualAddress address, ams::svc::MemoryPermission device_perm);
|
||||||
|
|
||||||
Result MapImpl(size_t *out_mapped_size, s32 *out_num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm);
|
Result MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm);
|
||||||
void UnmapImpl(KDeviceVirtualAddress address, u64 size, bool force);
|
void UnmapImpl(KDeviceVirtualAddress address, u64 size, bool force);
|
||||||
|
|
||||||
bool IsFree(KDeviceVirtualAddress address, u64 size) const;
|
bool IsFree(KDeviceVirtualAddress address, u64 size) const;
|
||||||
|
|
|
@ -33,11 +33,15 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
|
|
||||||
constexpr size_t DevicePageBits = 12;
|
constexpr size_t DevicePageBits = 12;
|
||||||
constexpr size_t DevicePageSize = (1ul << DevicePageBits);
|
constexpr size_t DevicePageSize = (1ul << DevicePageBits);
|
||||||
constexpr size_t DeviceLargePageBits = 22;
|
|
||||||
constexpr size_t DeviceLargePageSize = (1ul << DevicePageBits);
|
|
||||||
static_assert(DevicePageSize == PageSize);
|
static_assert(DevicePageSize == PageSize);
|
||||||
|
|
||||||
constexpr size_t DeviceRegionSize = (1ul << 32);
|
constexpr size_t DeviceLargePageBits = 22;
|
||||||
|
constexpr size_t DeviceLargePageSize = (1ul << DeviceLargePageBits);
|
||||||
|
static_assert(DeviceLargePageSize % DevicePageSize == 0);
|
||||||
|
|
||||||
|
constexpr size_t DeviceRegionBits = 32;
|
||||||
|
constexpr size_t DeviceRegionSize = (1ul << DeviceRegionBits);
|
||||||
|
static_assert(DeviceRegionSize % DeviceLargePageSize == 0);
|
||||||
|
|
||||||
constexpr size_t DeviceAsidRegisterOffsets[] = {
|
constexpr size_t DeviceAsidRegisterOffsets[] = {
|
||||||
[ams::svc::DeviceName_Afi] = MC_SMMU_AFI_ASID,
|
[ams::svc::DeviceName_Afi] = MC_SMMU_AFI_ASID,
|
||||||
|
@ -239,7 +243,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBit(Bit_NonSecure) | this->SelectBit(Bit_Writeable) | this->SelectBit(Bit_Readable); }
|
constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBit(Bit_NonSecure) | this->SelectBit(Bit_Writeable) | this->SelectBit(Bit_Readable); }
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE KPhysicalAddress GetPhysicalAddress() { return (static_cast<u64>(this->value) << DevicePageBits) & PhysicalAddressMask; }
|
constexpr ALWAYS_INLINE KPhysicalAddress GetPhysicalAddress() const { return (static_cast<u64>(this->value) << DevicePageBits) & PhysicalAddressMask; }
|
||||||
|
|
||||||
ALWAYS_INLINE void Invalidate() { this->SetValue(0); }
|
ALWAYS_INLINE void Invalidate() { this->SetValue(0); }
|
||||||
};
|
};
|
||||||
|
@ -351,12 +355,10 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
WriteMcRegister(MC_SMMU_PTC_FLUSH_0, 0);
|
WriteMcRegister(MC_SMMU_PTC_FLUSH_0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void InvalidatePtc(KPhysicalAddress address) {
|
void InvalidatePtc(KPhysicalAddress address) {
|
||||||
WriteMcRegister(MC_SMMU_PTC_FLUSH_1, (static_cast<u64>(GetInteger(address)) >> 32));
|
WriteMcRegister(MC_SMMU_PTC_FLUSH_1, (static_cast<u64>(GetInteger(address)) >> 32));
|
||||||
WriteMcRegister(MC_SMMU_PTC_FLUSH_0, (GetInteger(address) & 0xFFFFFFF0u) | 1u);
|
WriteMcRegister(MC_SMMU_PTC_FLUSH_0, (GetInteger(address) & 0xFFFFFFF0u) | 1u);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
enum TlbFlushVaMatch : u32 {
|
enum TlbFlushVaMatch : u32 {
|
||||||
TlbFlushVaMatch_All = 0,
|
TlbFlushVaMatch_All = 0,
|
||||||
|
@ -376,11 +378,9 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
return WriteMcRegister(MC_SMMU_TLB_FLUSH, EncodeTlbFlushValue(true, asid, 0, TlbFlushVaMatch_All));
|
return WriteMcRegister(MC_SMMU_TLB_FLUSH, EncodeTlbFlushValue(true, asid, 0, TlbFlushVaMatch_All));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void InvalidateTlbSection(u8 asid, KDeviceVirtualAddress address) {
|
void InvalidateTlbSection(u8 asid, KDeviceVirtualAddress address) {
|
||||||
return WriteMcRegister(MC_SMMU_TLB_FLUSH, EncodeTlbFlushValue(true, asid, address, TlbFlushVaMatch_Section));
|
return WriteMcRegister(MC_SMMU_TLB_FLUSH, EncodeTlbFlushValue(true, asid, address, TlbFlushVaMatch_Section));
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
void SetTable(u8 asid, KPhysicalAddress address) {
|
void SetTable(u8 asid, KPhysicalAddress address) {
|
||||||
/* Write the table address. */
|
/* Write the table address. */
|
||||||
|
@ -490,7 +490,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
const KVirtualAddress table_vaddr = ptm.Allocate();
|
const KVirtualAddress table_vaddr = ptm.Allocate();
|
||||||
R_UNLESS(table_vaddr != Null<KVirtualAddress>, svc::ResultOutOfMemory());
|
R_UNLESS(table_vaddr != Null<KVirtualAddress>, svc::ResultOutOfMemory());
|
||||||
|
|
||||||
MESOSPHERE_ASSERT((static_cast<u64>(GetInteger(GetPageTablePhysicalAddress(table_vaddr))) & ~PhysicalAddressMask) == 0);
|
MESOSPHERE_ASSERT(IsValidPhysicalAddress(GetPageTablePhysicalAddress(table_vaddr)));
|
||||||
|
|
||||||
ptm.Open(table_vaddr, 1);
|
ptm.Open(table_vaddr, 1);
|
||||||
cpu::StoreDataCache(GetVoidPointer(table_vaddr), PageDirectorySize);
|
cpu::StoreDataCache(GetVoidPointer(table_vaddr), PageDirectorySize);
|
||||||
|
@ -619,24 +619,417 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KDevicePageTable::MapDevicePage(size_t *out_mapped_size, s32 *out_num_pt, s32 max_pt, KPhysicalAddress phys_addr, u64 size, KDeviceVirtualAddress address, ams::svc::MemoryPermission device_perm) {
|
bool KDevicePageTable::IsFree(KDeviceVirtualAddress address, u64 size) const {
|
||||||
MESOSPHERE_UNIMPLEMENTED();
|
MESOSPHERE_ASSERT((address & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
MESOSPHERE_ASSERT(((address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
|
||||||
|
/* Walk the directory, looking for entries. */
|
||||||
|
u64 remaining = size;
|
||||||
|
while (remaining > 0) {
|
||||||
|
const size_t l0_index = (address / DeviceRegionSize);
|
||||||
|
const size_t l1_index = (address % DeviceRegionSize) / DeviceLargePageSize;
|
||||||
|
const size_t l2_index = (address % DeviceLargePageSize) / DevicePageSize;
|
||||||
|
|
||||||
|
const PageDirectoryEntry *l1 = GetPointer<PageDirectoryEntry>(this->tables[l0_index]);
|
||||||
|
if (l1 == nullptr || !l1[l1_index].IsValid()) {
|
||||||
|
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
|
||||||
|
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
|
||||||
|
|
||||||
|
address += DevicePageSize * map_count;
|
||||||
|
remaining -= DevicePageSize * map_count;
|
||||||
|
} else if (l1[l1_index].IsTable()) {
|
||||||
|
const PageTableEntry *l2 = GetPointer<PageTableEntry>(GetPageTableVirtualAddress(l1[l1_index].GetPhysicalAddress()));
|
||||||
|
|
||||||
|
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
|
||||||
|
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < map_count; ++i) {
|
||||||
|
if (l2[l2_index + i].IsValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KDevicePageTable::MapImpl(size_t *out_mapped_size, s32 *out_num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm) {
|
address += DevicePageSize * map_count;
|
||||||
MESOSPHERE_UNIMPLEMENTED();
|
remaining -= DevicePageSize * map_count;
|
||||||
|
} else {
|
||||||
|
/* If we have an entry, we're not free. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KDevicePageTable::MapDevicePage(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KPhysicalAddress phys_addr, u64 size, KDeviceVirtualAddress address, ams::svc::MemoryPermission device_perm) {
|
||||||
|
/* Clear the output size. */
|
||||||
|
*out_mapped_size = 0;
|
||||||
|
|
||||||
|
/* Ensure that the physical address is valid. */
|
||||||
|
R_UNLESS(IsValidPhysicalAddress(static_cast<u64>(GetInteger(phys_addr)) + size - 1), svc::ResultInvalidCurrentMemory());
|
||||||
|
MESOSPHERE_ASSERT((address & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
MESOSPHERE_ASSERT(((address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
|
||||||
|
/* Get the memory manager and page table manager. */
|
||||||
|
KMemoryManager &mm = Kernel::GetMemoryManager();
|
||||||
|
KPageTableManager &ptm = Kernel::GetPageTableManager();
|
||||||
|
|
||||||
|
/* Cache permissions. */
|
||||||
|
const bool read = (device_perm & ams::svc::MemoryPermission_Read) != 0;
|
||||||
|
const bool write = (device_perm & ams::svc::MemoryPermission_Write) != 0;
|
||||||
|
|
||||||
|
/* Walk the directory. */
|
||||||
|
u64 remaining = size;
|
||||||
|
while (remaining > 0) {
|
||||||
|
const size_t l0_index = (address / DeviceRegionSize);
|
||||||
|
const size_t l1_index = (address % DeviceRegionSize) / DeviceLargePageSize;
|
||||||
|
const size_t l2_index = (address % DeviceLargePageSize) / DevicePageSize;
|
||||||
|
|
||||||
|
/* Get and validate l1. */
|
||||||
|
PageDirectoryEntry *l1 = GetPointer<PageDirectoryEntry>(this->tables[l0_index]);
|
||||||
|
MESOSPHERE_ASSERT(l1 != nullptr);
|
||||||
|
|
||||||
|
/* Setup an l1 table/entry, if needed. */
|
||||||
|
if (!l1[l1_index].IsTable()) {
|
||||||
|
/* Check that an entry doesn't already exist. */
|
||||||
|
MESOSPHERE_ASSERT(!l1[l1_index].IsValid());
|
||||||
|
|
||||||
|
/* If we can make an l1 entry, do so. */
|
||||||
|
if (l2_index == 0 && util::IsAligned(GetInteger(phys_addr), DeviceLargePageSize) && remaining >= DeviceLargePageSize) {
|
||||||
|
/* Set the large page. */
|
||||||
|
l1[l1_index].SetLargePage(read, write, true, phys_addr);
|
||||||
|
cpu::StoreDataCache(std::addressof(l1[l1_index]), sizeof(PageDirectoryEntry));
|
||||||
|
|
||||||
|
/* Synchronize. */
|
||||||
|
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l1[l1_index]))));
|
||||||
|
InvalidateTlbSection(this->table_asids[l0_index], address);
|
||||||
|
SmmuSynchronizationBarrier();
|
||||||
|
|
||||||
|
/* Open references to the pages. */
|
||||||
|
mm.Open(GetHeapVirtualAddress(phys_addr), DeviceLargePageSize / PageSize);
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
phys_addr += DeviceLargePageSize;
|
||||||
|
address += DeviceLargePageSize;
|
||||||
|
*out_mapped_size += DeviceLargePageSize;
|
||||||
|
remaining -= DeviceLargePageSize;
|
||||||
|
continue;
|
||||||
|
} else if (num_pt == max_pt) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* Make an l1 table. */
|
||||||
|
const KVirtualAddress table_vaddr = ptm.Allocate();
|
||||||
|
R_UNLESS(table_vaddr != Null<KVirtualAddress>, svc::ResultOutOfMemory());
|
||||||
|
MESOSPHERE_ASSERT(IsValidPhysicalAddress(GetPageTablePhysicalAddress(table_vaddr)));
|
||||||
|
cpu::StoreDataCache(GetVoidPointer(table_vaddr), PageTableSize);
|
||||||
|
|
||||||
|
/* Set the l1 table. */
|
||||||
|
l1[l1_index].SetTable(true, true, true, GetPageTablePhysicalAddress(table_vaddr));
|
||||||
|
cpu::StoreDataCache(std::addressof(l1[l1_index]), sizeof(PageDirectoryEntry));
|
||||||
|
|
||||||
|
/* Synchronize. */
|
||||||
|
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l1[l1_index]))));
|
||||||
|
InvalidateTlbSection(this->table_asids[l0_index], address);
|
||||||
|
SmmuSynchronizationBarrier();
|
||||||
|
|
||||||
|
/* Increment the page table count. */
|
||||||
|
++num_pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we get to this point, l1 must be a table. */
|
||||||
|
MESOSPHERE_ASSERT(l1[l1_index].IsTable());
|
||||||
|
|
||||||
|
/* Map l2 entries. */
|
||||||
|
{
|
||||||
|
PageTableEntry *l2 = GetPointer<PageTableEntry>(GetPageTableVirtualAddress(l1[l1_index].GetPhysicalAddress()));
|
||||||
|
|
||||||
|
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
|
||||||
|
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
|
||||||
|
|
||||||
|
/* Set the entries. */
|
||||||
|
for (size_t i = 0; i < map_count; ++i) {
|
||||||
|
MESOSPHERE_ASSERT(!l2[l2_index + i].IsValid());
|
||||||
|
l2[l2_index + i].SetPage(read, write, true, phys_addr + DevicePageSize * i);
|
||||||
|
|
||||||
|
/* Add a reference to the l2 page (from the l2 entry page). */
|
||||||
|
ptm.Open(KVirtualAddress(l2), 1);
|
||||||
|
}
|
||||||
|
cpu::StoreDataCache(std::addressof(l2[l2_index]), map_count * sizeof(PageTableEntry));
|
||||||
|
|
||||||
|
/* Invalidate the page table cache. */
|
||||||
|
for (size_t i = util::AlignDown(l2_index, 4); i <= util::AlignDown(l2_index + map_count - 1, 4); i += 4) {
|
||||||
|
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l2[i]))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Synchronize. */
|
||||||
|
InvalidateTlbSection(this->table_asids[l0_index], address);
|
||||||
|
SmmuSynchronizationBarrier();
|
||||||
|
|
||||||
|
/* Open references to the pages. */
|
||||||
|
mm.Open(GetHeapVirtualAddress(phys_addr), (map_count * DevicePageSize) / PageSize);
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
phys_addr += map_count * DevicePageSize;
|
||||||
|
address += map_count * DevicePageSize;
|
||||||
|
*out_mapped_size += map_count * DevicePageSize;
|
||||||
|
remaining -= map_count * DevicePageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KDevicePageTable::MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm) {
|
||||||
|
/* Clear the output size. */
|
||||||
|
*out_mapped_size = 0;
|
||||||
|
|
||||||
|
/* Get the size, and validate the address. */
|
||||||
|
const u64 size = pg.GetNumPages() * PageSize;
|
||||||
|
MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
|
||||||
|
/* Ensure that the region we're mapping to is free. */
|
||||||
|
R_UNLESS(this->IsFree(device_address, size), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
/* Ensure that if we fail, we unmap anything we mapped. */
|
||||||
|
auto unmap_guard = SCOPE_GUARD { this->UnmapImpl(device_address, size, true); };
|
||||||
|
|
||||||
|
/* Iterate, mapping device pages. */
|
||||||
|
KDeviceVirtualAddress cur_addr = device_address;
|
||||||
|
for (auto it = pg.begin(); it != pg.end(); ++it) {
|
||||||
|
/* Require that we be able to map the device page. */
|
||||||
|
R_UNLESS(IsHeapVirtualAddress(it->GetAddress()), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
/* Get the physical address for the page. */
|
||||||
|
const KPhysicalAddress phys_addr = GetHeapPhysicalAddress(it->GetAddress());
|
||||||
|
|
||||||
|
/* Map the device page. */
|
||||||
|
const u64 block_size = it->GetSize();
|
||||||
|
size_t mapped_size = 0;
|
||||||
|
R_TRY(this->MapDevicePage(std::addressof(mapped_size), num_pt, max_pt, phys_addr, block_size, cur_addr, device_perm));
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
cur_addr += block_size;
|
||||||
|
*out_mapped_size += mapped_size;
|
||||||
|
|
||||||
|
/* If we didn't map as much as we wanted, break. */
|
||||||
|
if (mapped_size < block_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're done, so cancel our guard. */
|
||||||
|
unmap_guard.Cancel();
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KDevicePageTable::UnmapImpl(KDeviceVirtualAddress address, u64 size, bool force) {
|
void KDevicePageTable::UnmapImpl(KDeviceVirtualAddress address, u64 size, bool force) {
|
||||||
MESOSPHERE_UNIMPLEMENTED();
|
MESOSPHERE_ASSERT((address & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
MESOSPHERE_ASSERT(((address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
|
||||||
|
/* Get the memory manager and page table manager. */
|
||||||
|
KMemoryManager &mm = Kernel::GetMemoryManager();
|
||||||
|
KPageTableManager &ptm = Kernel::GetPageTableManager();
|
||||||
|
|
||||||
|
/* Make a page group for the pages we're closing. */
|
||||||
|
KPageGroup pg(std::addressof(Kernel::GetBlockInfoManager()));
|
||||||
|
|
||||||
|
/* Walk the directory. */
|
||||||
|
u64 remaining = size;
|
||||||
|
while (remaining > 0) {
|
||||||
|
const size_t l0_index = (address / DeviceRegionSize);
|
||||||
|
const size_t l1_index = (address % DeviceRegionSize) / DeviceLargePageSize;
|
||||||
|
const size_t l2_index = (address % DeviceLargePageSize) / DevicePageSize;
|
||||||
|
|
||||||
|
/* Get and validate l1. */
|
||||||
|
PageDirectoryEntry *l1 = GetPointer<PageDirectoryEntry>(this->tables[l0_index]);
|
||||||
|
|
||||||
|
/* Check if there's nothing mapped at l1. */
|
||||||
|
if (l1 == nullptr || !l1[l1_index].IsValid()) {
|
||||||
|
MESOSPHERE_ASSERT(force);
|
||||||
|
|
||||||
|
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
|
||||||
|
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
address += map_count * DevicePageSize;
|
||||||
|
remaining -= map_count * DevicePageSize;
|
||||||
|
} else if (l1[l1_index].IsTable()) {
|
||||||
|
/* Dealing with an l1 table. */
|
||||||
|
PageTableEntry *l2 = GetPointer<PageTableEntry>(GetPageTableVirtualAddress(l1[l1_index].GetPhysicalAddress()));
|
||||||
|
|
||||||
|
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
|
||||||
|
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
|
||||||
|
size_t num_closed = 0;
|
||||||
|
bool invalidated_tlb = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < map_count; ++i) {
|
||||||
|
if (l2[l2_index + i].IsValid()) {
|
||||||
|
/* Get the physical address. */
|
||||||
|
const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress();
|
||||||
|
MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr));
|
||||||
|
|
||||||
|
/* Invalidate the entry. */
|
||||||
|
l2[l2_index + i].Invalidate();
|
||||||
|
++num_closed;
|
||||||
|
|
||||||
|
/* Try to add the page to the group. */
|
||||||
|
if (R_FAILED(pg.AddBlock(GetHeapVirtualAddress(phys_addr), DevicePageSize / PageSize))) {
|
||||||
|
/* If we can't add it for deferred close, close it now. */
|
||||||
|
cpu::StoreDataCache(std::addressof(l2[l2_index + i]), sizeof(PageTableEntry));
|
||||||
|
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l2[l2_index + i]))));
|
||||||
|
SmmuSynchronizationBarrier();
|
||||||
|
|
||||||
|
/* Close the page's reference. */
|
||||||
|
mm.Close(GetHeapVirtualAddress(phys_addr), 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MESOSPHERE_ASSERT(force);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cpu::StoreDataCache(std::addressof(l2[l2_index]), map_count * sizeof(PageTableEntry));
|
||||||
|
|
||||||
|
/* Invalidate the page table cache. */
|
||||||
|
for (size_t i = util::AlignDown(l2_index, 4); i <= util::AlignDown(l2_index + map_count - 1, 4); i += 4) {
|
||||||
|
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l2[i]))));
|
||||||
|
}
|
||||||
|
SmmuSynchronizationBarrier();
|
||||||
|
|
||||||
|
/* Close the pages. */
|
||||||
|
if (ptm.Close(KVirtualAddress(l2), num_closed)) {
|
||||||
|
/* Invalidate the l1 entry. */
|
||||||
|
l1[l1_index].Invalidate();
|
||||||
|
cpu::StoreDataCache(std::addressof(l1[l1_index]), sizeof(PageDirectoryEntry));
|
||||||
|
|
||||||
|
/* Synchronize. */
|
||||||
|
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l1[l1_index]))));
|
||||||
|
InvalidateTlbSection(this->table_asids[l0_index], address);
|
||||||
|
SmmuSynchronizationBarrier();
|
||||||
|
|
||||||
|
/* We invalidated the tlb. */
|
||||||
|
invalidated_tlb = true;
|
||||||
|
|
||||||
|
/* Free the l2 page. */
|
||||||
|
ptm.Free(KVirtualAddress(l2));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KDevicePageTable::IsFree(KDeviceVirtualAddress address, u64 size) const {
|
/* Invalidate the tlb if we haven't already. */
|
||||||
MESOSPHERE_UNIMPLEMENTED();
|
if (!invalidated_tlb) {
|
||||||
|
InvalidateTlbSection(this->table_asids[l0_index], address);
|
||||||
|
SmmuSynchronizationBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
address += map_count * DevicePageSize;
|
||||||
|
remaining -= map_count * DevicePageSize;
|
||||||
|
} else {
|
||||||
|
/* Dealing with an l1 entry. */
|
||||||
|
MESOSPHERE_ASSERT(l2_index == 0);
|
||||||
|
|
||||||
|
/* Get the physical address. */
|
||||||
|
const KPhysicalAddress phys_addr = l1[l1_index].GetPhysicalAddress();
|
||||||
|
MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr));
|
||||||
|
|
||||||
|
/* Invalidate the entry. */
|
||||||
|
l1[l1_index].Invalidate();
|
||||||
|
cpu::StoreDataCache(std::addressof(l1[l1_index]), sizeof(PageDirectoryEntry));
|
||||||
|
|
||||||
|
/* Synchronize. */
|
||||||
|
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l1[l1_index]))));
|
||||||
|
InvalidateTlbSection(this->table_asids[l0_index], address);
|
||||||
|
SmmuSynchronizationBarrier();
|
||||||
|
|
||||||
|
/* Close references. */
|
||||||
|
mm.Close(GetHeapVirtualAddress(phys_addr), DeviceLargePageSize / PageSize);
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
address += DeviceLargePageSize;
|
||||||
|
remaining -= DeviceLargePageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close references to the pages in the group. */
|
||||||
|
pg.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KDevicePageTable::MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const {
|
Result KDevicePageTable::MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const {
|
||||||
MESOSPHERE_UNIMPLEMENTED();
|
MESOSPHERE_ASSERT((address & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
MESOSPHERE_ASSERT(((address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
|
||||||
|
/* Walk the directory. */
|
||||||
|
u64 remaining = size;
|
||||||
|
bool first = false;
|
||||||
|
u32 attr = 0;
|
||||||
|
while (remaining > 0) {
|
||||||
|
const size_t l0_index = (address / DeviceRegionSize);
|
||||||
|
const size_t l1_index = (address % DeviceRegionSize) / DeviceLargePageSize;
|
||||||
|
const size_t l2_index = (address % DeviceLargePageSize) / DevicePageSize;
|
||||||
|
|
||||||
|
/* Get and validate l1. */
|
||||||
|
const PageDirectoryEntry *l1 = GetPointer<PageDirectoryEntry>(this->tables[l0_index]);
|
||||||
|
R_UNLESS(l1 != nullptr, svc::ResultInvalidCurrentMemory());
|
||||||
|
R_UNLESS(l1[l1_index].IsValid(), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
if (l1[l1_index].IsTable()) {
|
||||||
|
/* We're acting on an l2 entry. */
|
||||||
|
const PageTableEntry *l2 = GetPointer<PageTableEntry>(GetPageTableVirtualAddress(l1[l1_index].GetPhysicalAddress()));
|
||||||
|
|
||||||
|
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
|
||||||
|
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < map_count; ++i) {
|
||||||
|
/* Ensure the l2 entry is valid. */
|
||||||
|
R_UNLESS(l2[l2_index + i].IsValid(), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
/* Get the physical address. */
|
||||||
|
const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress();
|
||||||
|
MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr));
|
||||||
|
|
||||||
|
/* Add to the group. */
|
||||||
|
R_TRY(out->AddBlock(GetHeapVirtualAddress(phys_addr), DevicePageSize / PageSize));
|
||||||
|
|
||||||
|
/* If this is our first entry, get the attribute. */
|
||||||
|
if (first) {
|
||||||
|
attr = l2[l2_index + i].GetAttributes();
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
/* Validate the attributes match the first entry. */
|
||||||
|
R_UNLESS(l2[l2_index + i].GetAttributes() == attr, svc::ResultInvalidCurrentMemory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
address += DevicePageSize * map_count;
|
||||||
|
remaining -= DevicePageSize * map_count;
|
||||||
|
} else {
|
||||||
|
/* We're acting on an l1 entry. */
|
||||||
|
R_UNLESS(l2_index == 0, svc::ResultInvalidCurrentMemory());
|
||||||
|
R_UNLESS(remaining >= DeviceLargePageSize, svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
/* Get the physical address. */
|
||||||
|
const KPhysicalAddress phys_addr = l1[l1_index].GetPhysicalAddress();
|
||||||
|
MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr));
|
||||||
|
|
||||||
|
/* Add to the group. */
|
||||||
|
R_TRY(out->AddBlock(GetHeapVirtualAddress(phys_addr), DeviceLargePageSize / PageSize));
|
||||||
|
|
||||||
|
/* If this is our first entry, get the attribute. */
|
||||||
|
if (first) {
|
||||||
|
attr = l1[l1_index].GetAttributes();
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
/* Validate the attributes match the first entry. */
|
||||||
|
R_UNLESS(l1[l1_index].GetAttributes() == attr, svc::ResultInvalidCurrentMemory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
address += DeviceLargePageSize;
|
||||||
|
remaining -= DeviceLargePageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KDevicePageTable::Compare(const KPageGroup &compare_pg, KDeviceVirtualAddress device_address) const {
|
bool KDevicePageTable::Compare(const KPageGroup &compare_pg, KDeviceVirtualAddress device_address) const {
|
||||||
|
@ -652,7 +1045,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||||
|
|
||||||
/* Map the pages. */
|
/* Map the pages. */
|
||||||
s32 num_pt = 0;
|
s32 num_pt = 0;
|
||||||
return this->MapImpl(out_mapped_size, std::addressof(num_pt), refresh_mappings ? 1 : std::numeric_limits<s32>::max(), pg, device_address, device_perm);
|
return this->MapImpl(out_mapped_size, num_pt, refresh_mappings ? 1 : std::numeric_limits<s32>::max(), pg, device_address, device_perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KDevicePageTable::Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) {
|
Result KDevicePageTable::Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) {
|
||||||
|
|
|
@ -606,10 +606,10 @@ namespace ams::kern {
|
||||||
bool cur_valid = false;
|
bool cur_valid = false;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool next_valid;
|
bool next_valid;
|
||||||
size_t tot_size = false;
|
size_t tot_size = 0;
|
||||||
|
|
||||||
next_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
|
next_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
|
||||||
next_entry.block_size = (next_entry.block_size - (GetInteger(address) & (next_entry.block_size - 1)));
|
next_entry.block_size = (next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
|
||||||
|
|
||||||
/* Iterate, looking for entry. */
|
/* Iterate, looking for entry. */
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
@ -31,7 +31,6 @@ namespace ams::kern::svc {
|
||||||
R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange());
|
R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange());
|
||||||
|
|
||||||
if (max_sessions > 0) {
|
if (max_sessions > 0) {
|
||||||
MESOSPHERE_LOG("Creating Named Port %s (max sessions = %d)\n", name, max_sessions);
|
|
||||||
/* Get the current handle table. */
|
/* Get the current handle table. */
|
||||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||||
|
|
||||||
|
@ -62,8 +61,6 @@ namespace ams::kern::svc {
|
||||||
port->GetClientPort().Close();
|
port->GetClientPort().Close();
|
||||||
register_guard.Cancel();
|
register_guard.Cancel();
|
||||||
} else /* if (max_sessions == 0) */ {
|
} else /* if (max_sessions == 0) */ {
|
||||||
MESOSPHERE_LOG("Deleting Named Port %s\n", name);
|
|
||||||
|
|
||||||
/* Ensure that this else case is correct. */
|
/* Ensure that this else case is correct. */
|
||||||
MESOSPHERE_AUDIT(max_sessions == 0);
|
MESOSPHERE_AUDIT(max_sessions == 0);
|
||||||
|
|
||||||
|
@ -122,8 +119,6 @@ namespace ams::kern::svc {
|
||||||
/* Validate that name is valid. */
|
/* Validate that name is valid. */
|
||||||
R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange());
|
R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange());
|
||||||
|
|
||||||
MESOSPHERE_LOG("%s: ConnectToNamedPort(%s) was called\n", GetCurrentProcess().GetName(), name);
|
|
||||||
|
|
||||||
/* Get the current handle table. */
|
/* Get the current handle table. */
|
||||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,6 @@ namespace ams::kern::svc {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Result QueryProcessMemory(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle process_handle, uintptr_t address) {
|
Result QueryProcessMemory(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, ams::svc::Handle process_handle, uintptr_t address) {
|
||||||
MESOSPHERE_LOG("%s: QueryProcessMemory(0x%08x, 0x%zx) was called\n", GetCurrentProcess().GetName(), process_handle, address);
|
|
||||||
|
|
||||||
/* Get the process. */
|
/* Get the process. */
|
||||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(process_handle);
|
||||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||||
|
|
|
@ -115,9 +115,7 @@ namespace ams::kern::svc {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result WaitSynchronization64(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, int64_t timeout_ns) {
|
Result WaitSynchronization64(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, int64_t timeout_ns) {
|
||||||
Result result = WaitSynchronization(out_index, handles, num_handles, timeout_ns);
|
return WaitSynchronization(out_index, handles, num_handles, timeout_ns);
|
||||||
MESOSPHERE_LOG("WaitSynchronization returned %08x\n", result.GetValue());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result CancelSynchronization64(ams::svc::Handle handle) {
|
Result CancelSynchronization64(ams::svc::Handle handle) {
|
||||||
|
|
Loading…
Reference in a new issue