kern: add SvcCreateDeviceAddressSpace, SvcAttachDeviceAddressSpace

This commit is contained in:
Michael Scire 2020-07-14 09:49:49 -07:00 committed by SciresM
parent 04f325cf5a
commit cfddb75398
6 changed files with 311 additions and 15 deletions

View file

@ -53,6 +53,12 @@ namespace ams::kern::board::nintendo::nx {
public:
constexpr KDevicePageTable() : tables(), table_asids(), attached_device(), attached_value(), detached_value(), hs_attached_value(), hs_detached_value() { /* ... */ }
Result Initialize(u64 space_address, u64 space_size);
void Finalize();
Result Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size);
Result Detach(ams::svc::DeviceName device_name);
public:
static void Initialize();
};

View file

@ -23,10 +23,39 @@ namespace ams::kern {
class KDeviceAddressSpace final : public KAutoObjectWithSlabHeapAndContainer<KDeviceAddressSpace, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KDeviceAddressSpace, KAutoObject);
private:
KLightLock lock;
KDevicePageTable table;
u64 space_address;
u64 space_size;
bool is_initialized;
public:
constexpr KDeviceAddressSpace() : lock(), table(), space_address(), space_size(), is_initialized() { /* ... */ }
virtual ~KDeviceAddressSpace() { /* ... */ }
Result Initialize(u64 address, u64 size);
virtual void Finalize() override;
virtual bool IsInitialized() const override { return this->is_initialized; }
static void PostDestroy(uintptr_t arg) { /* ... */ }
Result Attach(ams::svc::DeviceName device_name);
Result Detach(ams::svc::DeviceName device_name);
Result Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
return this->Map(out_mapped_size, page_table, process_address, size, device_address, device_perm, false, refresh_mappings);
}
Result MapAligned(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm) {
size_t dummy;
return this->Map(std::addressof(dummy), page_table, process_address, size, device_address, device_perm, true, false);
}
Result Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address);
private:
Result Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool refresh_mappings);
public:
static void Initialize();
/* TODO: This is a placeholder definition. */
};
}

View file

@ -37,6 +37,8 @@ namespace ams::kern::board::nintendo::nx {
constexpr size_t DeviceLargePageSize = (1ul << DevicePageBits);
static_assert(DevicePageSize == PageSize);
constexpr size_t DeviceRegionSize = (1ul << 32);
constexpr size_t DeviceAsidRegisterOffsets[] = {
[ams::svc::DeviceName_Afi] = MC_SMMU_AFI_ASID,
[ams::svc::DeviceName_Avpc] = MC_SMMU_AVPC_ASID,
@ -140,6 +142,57 @@ namespace ams::kern::board::nintendo::nx {
return (static_cast<u64>(GetInteger(addr)) & ~PhysicalAddressMask) == 0;
}
constexpr struct { u64 start; u64 end; } SmmuSupportedRanges[] = {
[ams::svc::DeviceName_Afi] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Avpc] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Dc] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Dcb] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Hc] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Hda] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Isp2] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_MsencNvenc] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Nv] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Nv2] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Ppcs] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Sata] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Vi] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Vic] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_XusbHost] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_XusbDev] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Tsec] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Ppcs1] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Dc1] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Sdmmc1a] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Sdmmc2a] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Sdmmc3a] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Sdmmc4a] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Isp2b] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Gpu] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Gpub] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Ppcs2] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Nvdec] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Ape] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Se] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Nvjpg] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Hc1] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Se1] = { 0x00000000ul, 0x0FFFFFFFFul },
[ams::svc::DeviceName_Axiap] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Etr] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Tsecb] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Tsec1] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Tsecb1] = { 0x00000000ul, 0x3FFFFFFFFul },
[ams::svc::DeviceName_Nvdec1] = { 0x00000000ul, 0x0FFFFFFFFul },
};
static_assert(util::size(SmmuSupportedRanges) == ams::svc::DeviceName_Count);
constexpr bool IsAttachable(ams::svc::DeviceName device_name, u64 space_address, u64 space_size) {
if (0 <= device_name && device_name < ams::svc::DeviceName_Count) {
const auto &range = SmmuSupportedRanges[device_name];
return range.start <= space_address && (space_address + space_size - 1) <= range.end;
}
return false;
}
/* Types. */
class EntryBase {
protected:
@ -404,4 +457,130 @@ namespace ams::kern::board::nintendo::nx {
/* TODO: Install interrupt handler. */
}
/* Member functions. */
Result KDevicePageTable::Initialize(u64 space_address, u64 space_size) {
/* Ensure space is valid. */
R_UNLESS(((space_address + space_size - 1) & ~DeviceVirtualAddressMask) == 0, svc::ResultInvalidMemoryRegion());
/* Determine extents. */
const size_t start_index = space_address / DeviceRegionSize;
const size_t end_index = (space_address + space_size - 1) / DeviceRegionSize;
/* Get the page table manager. */
auto &ptm = Kernel::GetPageTableManager();
/* Clear the tables. */
static_assert(TableCount == (1ul << DeviceVirtualAddressBits) / DeviceRegionSize);
for (size_t i = 0; i < TableCount; ++i) {
this->tables[i] = Null<KVirtualAddress>;
}
/* Ensure that we clean up the tables on failure. */
auto table_guard = SCOPE_GUARD {
for (size_t i = start_index; i <= end_index; ++i) {
if (this->tables[i] != Null<KVirtualAddress> && ptm.Close(this->tables[i], 1)) {
ptm.Free(this->tables[i]);
}
}
};
/* Allocate a table for all required indices. */
for (size_t i = start_index; i <= end_index; ++i) {
const KVirtualAddress table_vaddr = ptm.Allocate();
R_UNLESS(table_vaddr != Null<KVirtualAddress>, svc::ResultOutOfMemory());
MESOSPHERE_ASSERT((static_cast<u64>(GetInteger(GetPageTablePhysicalAddress(table_vaddr))) & ~PhysicalAddressMask) == 0);
ptm.Open(table_vaddr, 1);
cpu::StoreDataCache(GetVoidPointer(table_vaddr), PageDirectorySize);
this->tables[i] = table_vaddr;
}
/* Clear asids. */
for (size_t i = 0; i < TableCount; ++i) {
this->table_asids[i] = g_reserved_asid;
}
/* Reserve asids for the tables. */
R_TRY(g_asid_manager.Reserve(std::addressof(this->table_asids[start_index]), end_index - start_index + 1));
/* Associate tables with asids. */
for (size_t i = start_index; i <= end_index; ++i) {
SetTable(this->table_asids[i], GetPageTablePhysicalAddress(this->tables[i]));
}
/* Set member variables. */
this->attached_device = 0;
this->attached_value = (1u << 31) | this->table_asids[0];
this->detached_value = (1u << 31) | g_reserved_asid;
this->hs_attached_value = (1u << 31);
this->hs_detached_value = (1u << 31);
for (size_t i = 0; i < TableCount; ++i) {
this->hs_attached_value |= (this->table_asids[i] << (i * BITSIZEOF(u8)));
this->hs_detached_value |= (g_reserved_asid << (i * BITSIZEOF(u8)));
}
/* We succeeded. */
table_guard.Cancel();
return ResultSuccess();
}
void KDevicePageTable::Finalize() {
MESOSPHERE_UNIMPLEMENTED();
}
Result KDevicePageTable::Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size) {
/* Validate the device name. */
R_UNLESS(0 <= device_name, svc::ResultNotFound());
R_UNLESS(device_name < ams::svc::DeviceName_Count, svc::ResultNotFound());
/* Check that the device isn't already attached. */
R_UNLESS((this->attached_device & (1ul << device_name)) == 0, svc::ResultBusy());
/* Validate that the space is allowed for the device. */
const size_t end_index = (space_address + space_size - 1) / DeviceRegionSize;
R_UNLESS(end_index == 0 || IsHsSupported(device_name), svc::ResultInvalidCombination());
/* Validate that the device can be attached. */
R_UNLESS(IsAttachable(device_name, space_address, space_size), svc::ResultInvalidCombination());
/* Get the device asid register offset. */
const int reg_offset = GetDeviceAsidRegisterOffset(device_name);
R_UNLESS(reg_offset >= 0, svc::ResultNotFound());
/* Determine the old/new values. */
const u32 old_val = IsHsSupported(device_name) ? this->hs_detached_value : this->detached_value;
const u32 new_val = IsHsSupported(device_name) ? this->hs_attached_value : this->attached_value;
/* Attach the device. */
{
KScopedLightLock lk(g_lock);
/* Validate that the device is unclaimed. */
R_UNLESS((ReadMcRegister(reg_offset) | (1u << 31)) == (old_val | (1u << 31)), svc::ResultBusy());
/* Claim the device. */
WriteMcRegister(reg_offset, new_val);
SmmuSynchronizationBarrier();
/* Ensure that we claimed it successfully. */
if (ReadMcRegister(reg_offset) != new_val) {
WriteMcRegister(reg_offset, old_val);
SmmuSynchronizationBarrier();
return svc::ResultNotFound();
}
}
/* Mark the device as attached. */
this->attached_device |= (1ul << device_name);
return ResultSuccess();
}
Result KDevicePageTable::Detach(ams::svc::DeviceName device_name) {
MESOSPHERE_UNIMPLEMENTED();
}
}

View file

@ -17,9 +17,55 @@
namespace ams::kern {
/* Static initializer. */
void KDeviceAddressSpace::Initialize() {
/* This just forwards to the device page table manager. */
KDevicePageTable::Initialize();
}
/* Member functions. */
Result KDeviceAddressSpace::Initialize(u64 address, u64 size) {
MESOSPHERE_ASSERT_THIS();
/* Initialize the device page table. */
R_TRY(this->table.Initialize(address, size));
/* Set member variables. */
this->space_address = address;
this->space_size = size;
this->is_initialized = true;
return ResultSuccess();
}
void KDeviceAddressSpace::Finalize() {
MESOSPHERE_ASSERT_THIS();
/* Finalize the table. */
this->table.Finalize();
/* Finalize base. */
KAutoObjectWithSlabHeapAndContainer<KDeviceAddressSpace, KAutoObjectWithList>::Finalize();
}
Result KDeviceAddressSpace::Attach(ams::svc::DeviceName device_name) {
/* Lock the address space. */
KScopedLightLock lk(this->lock);
/* Attach. */
return this->table.Attach(device_name, this->space_address, this->space_size);
}
Result KDeviceAddressSpace::Detach(ams::svc::DeviceName device_name) {
MESOSPHERE_UNIMPLEMENTED();
}
Result KDeviceAddressSpace::Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool refresh_mappings) {
MESOSPHERE_UNIMPLEMENTED();
}
Result KDeviceAddressSpace::Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address) {
MESOSPHERE_UNIMPLEMENTED();
}
}

View file

@ -21,22 +21,61 @@ namespace ams::kern::svc {
namespace {
Result CreateDeviceAddressSpace(ams::svc::Handle *out, uint64_t das_address, uint64_t das_size) {
/* Validate input. */
R_UNLESS(util::IsAligned(das_address, PageSize), svc::ResultInvalidMemoryRegion());
R_UNLESS(util::IsAligned(das_size, PageSize), svc::ResultInvalidMemoryRegion());
R_UNLESS(das_size > 0, svc::ResultInvalidMemoryRegion());
R_UNLESS((das_address < das_address + das_size), svc::ResultInvalidMemoryRegion());
/* Create the device address space. */
KScopedAutoObject das = KDeviceAddressSpace::Create();
R_UNLESS(das.IsNotNull(), svc::ResultOutOfResource());
/* Initialize the device address space. */
R_TRY(das->Initialize(das_address, das_size));
/* Register the device address space. */
R_TRY(KDeviceAddressSpace::Register(das.GetPointerUnsafe()));
/* Add to the handle table. */
R_TRY(GetCurrentProcess().GetHandleTable().Add(out, das.GetPointerUnsafe()));
return ResultSuccess();
}
Result AttachDeviceAddressSpace(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
/* Get the device address space. */
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
/* Attach. */
return das->Attach(device_name);
}
Result DetachDeviceAddressSpace(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
/* Get the device address space. */
KScopedAutoObject das = GetCurrentProcess().GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
R_UNLESS(das.IsNotNull(), svc::ResultInvalidHandle());
/* Detach. */
return das->Detach(device_name);
}
}
/* ============================= 64 ABI ============================= */
Result CreateDeviceAddressSpace64(ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) {
MESOSPHERE_PANIC("Stubbed SvcCreateDeviceAddressSpace64 was called.");
return CreateDeviceAddressSpace(out_handle, das_address, das_size);
}
Result AttachDeviceAddressSpace64(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
MESOSPHERE_PANIC("Stubbed SvcAttachDeviceAddressSpace64 was called.");
return AttachDeviceAddressSpace(device_name, das_handle);
}
Result DetachDeviceAddressSpace64(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
MESOSPHERE_PANIC("Stubbed SvcDetachDeviceAddressSpace64 was called.");
return DetachDeviceAddressSpace(device_name, das_handle);
}
Result MapDeviceAddressSpaceByForce64(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {
@ -58,15 +97,15 @@ namespace ams::kern::svc {
/* ============================= 64From32 ABI ============================= */
Result CreateDeviceAddressSpace64From32(ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) {
MESOSPHERE_PANIC("Stubbed SvcCreateDeviceAddressSpace64From32 was called.");
return CreateDeviceAddressSpace(out_handle, das_address, das_size);
}
Result AttachDeviceAddressSpace64From32(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
MESOSPHERE_PANIC("Stubbed SvcAttachDeviceAddressSpace64From32 was called.");
return AttachDeviceAddressSpace(device_name, das_handle);
}
Result DetachDeviceAddressSpace64From32(ams::svc::DeviceName device_name, ams::svc::Handle das_handle) {
MESOSPHERE_PANIC("Stubbed SvcDetachDeviceAddressSpace64From32 was called.");
return DetachDeviceAddressSpace(device_name, das_handle);
}
Result MapDeviceAddressSpaceByForce64From32(ams::svc::Handle das_handle, ams::svc::Handle process_handle, uint64_t process_address, ams::svc::Size size, uint64_t device_address, ams::svc::MemoryPermission device_perm) {

View file

@ -43,20 +43,17 @@ namespace ams::kern::svc {
auto &handle_table = process.GetHandleTable();
/* Create the interrupt event. */
KInterruptEvent *event = KInterruptEvent::Create();
R_UNLESS(event != nullptr, svc::ResultOutOfResource());
/* Ensure that we cleanup the event on exit, leaving the only reference as the one in the handle table. */
ON_SCOPE_EXIT { event->Close(); };
KScopedAutoObject event = KInterruptEvent::Create();
R_UNLESS(event.IsNotNull(), svc::ResultOutOfResource());
/* Initialize the event. */
R_TRY(event->Initialize(interrupt_id, type));
/* Register the event. */
R_TRY(KInterruptEvent::Register(event));
R_TRY(KInterruptEvent::Register(event.GetPointerUnsafe()));
/* Add the event to the handle table. */
R_TRY(handle_table.Add(out, event));
R_TRY(handle_table.Add(out, event.GetPointerUnsafe()));
return ResultSuccess();
}