Extend NvServices and implement IDirectory (#107)

* Fix alignment handling in NvHostAsGpu::AllocSpace

* Implement Ioctl{2,3} ioctls

These were added in HOS 3.0.0 in order to ease handling ioctl buffers.

* Introduce support for GPU address space remapping

* Fix nvdrv and am service bugs

Syncpoints are supposed to be allocated from ID 1, they were allocated
at 0 before. The ioctl functions were also missing from the service map

* Fix friend:u service name

* Stub NVGPU_IOCTL_CHANNEL_SET_TIMESLICE

* Stub IManagerForApplication::CheckAvailability

* Add OsFileSystem Directory support and add a size field to directory entries

The size field will be needed by the incoming HOS IDirectory support.

* Implement support for IDirectory

This is used by applications to list the contents of a directory.

* Address feedback
This commit is contained in:
Billy Laws 2020-11-03 09:40:42 +00:00 committed by GitHub
parent 7ad86ec46f
commit 85d5dd3619
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 311 additions and 61 deletions

View File

@ -114,6 +114,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/fssrv/IFileSystem.cpp ${source_DIR}/skyline/services/fssrv/IFileSystem.cpp
${source_DIR}/skyline/services/fssrv/IFile.cpp ${source_DIR}/skyline/services/fssrv/IFile.cpp
${source_DIR}/skyline/services/fssrv/IStorage.cpp ${source_DIR}/skyline/services/fssrv/IStorage.cpp
${source_DIR}/skyline/services/fssrv/IDirectory.cpp
${source_DIR}/skyline/services/nvdrv/INvDrvServices.cpp ${source_DIR}/skyline/services/nvdrv/INvDrvServices.cpp
${source_DIR}/skyline/services/nvdrv/driver.cpp ${source_DIR}/skyline/services/nvdrv/driver.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvdevice.cpp ${source_DIR}/skyline/services/nvdrv/devices/nvdevice.cpp

View File

@ -14,9 +14,9 @@ namespace skyline::gpu::vmm {
chunks.push_back(baseChunk); chunks.push_back(baseChunk);
} }
std::optional<ChunkDescriptor> MemoryManager::FindChunk(u64 size, ChunkState state) { std::optional<ChunkDescriptor> MemoryManager::FindChunk(ChunkState state, u64 size, u64 alignment) {
auto chunk{std::find_if(chunks.begin(), chunks.end(), [size, state](const ChunkDescriptor &chunk) -> bool { auto chunk{std::find_if(chunks.begin(), chunks.end(), [state, size, alignment](const ChunkDescriptor &chunk) -> bool {
return chunk.size > size && chunk.state == state; return (alignment ? util::IsAligned(chunk.address, alignment) : true) && chunk.size > size && chunk.state == state;
})}; })};
if (chunk != chunks.end()) if (chunk != chunks.end())
@ -83,9 +83,9 @@ namespace skyline::gpu::vmm {
throw exception("Failed to insert chunk into GPU address space!"); throw exception("Failed to insert chunk into GPU address space!");
} }
u64 MemoryManager::ReserveSpace(u64 size) { u64 MemoryManager::ReserveSpace(u64 size, u64 alignment) {
size = util::AlignUp(size, constant::GpuPageSize); size = util::AlignUp(size, constant::GpuPageSize);
auto newChunk{FindChunk(size, ChunkState::Unmapped)}; auto newChunk{FindChunk(ChunkState::Unmapped, size, alignment)};
if (!newChunk) if (!newChunk)
return 0; return 0;
@ -98,7 +98,7 @@ namespace skyline::gpu::vmm {
u64 MemoryManager::ReserveFixed(u64 address, u64 size) { u64 MemoryManager::ReserveFixed(u64 address, u64 size) {
if (!util::IsAligned(address, constant::GpuPageSize)) if (!util::IsAligned(address, constant::GpuPageSize))
return false; return 0;
size = util::AlignUp(size, constant::GpuPageSize); size = util::AlignUp(size, constant::GpuPageSize);
@ -107,7 +107,7 @@ namespace skyline::gpu::vmm {
u64 MemoryManager::MapAllocate(u64 address, u64 size) { u64 MemoryManager::MapAllocate(u64 address, u64 size) {
size = util::AlignUp(size, constant::GpuPageSize); size = util::AlignUp(size, constant::GpuPageSize);
auto mappedChunk{FindChunk(size, ChunkState::Unmapped)}; auto mappedChunk{FindChunk(ChunkState::Unmapped, size)};
if (!mappedChunk) if (!mappedChunk)
return 0; return 0;
@ -121,26 +121,22 @@ namespace skyline::gpu::vmm {
u64 MemoryManager::MapFixed(u64 address, u64 cpuAddress, u64 size) { u64 MemoryManager::MapFixed(u64 address, u64 cpuAddress, u64 size) {
if (!util::IsAligned(address, constant::GpuPageSize)) if (!util::IsAligned(address, constant::GpuPageSize))
return false; return 0;
size = util::AlignUp(size, constant::GpuPageSize); size = util::AlignUp(size, constant::GpuPageSize);
return InsertChunk(ChunkDescriptor(address, size, cpuAddress, ChunkState::Mapped)); return InsertChunk(ChunkDescriptor(address, size, cpuAddress, ChunkState::Mapped));
} }
bool MemoryManager::Unmap(u64 address) { bool MemoryManager::Unmap(u64 address, u64 size) {
if (!util::IsAligned(address, constant::GpuPageSize)) if (!util::IsAligned(address, constant::GpuPageSize))
return false; return false;
auto chunk{std::find_if(chunks.begin(), chunks.end(), [address](const ChunkDescriptor &chunk) -> bool { try {
return chunk.address == address; InsertChunk(ChunkDescriptor(address, size, 0, ChunkState::Unmapped));
})}; } catch (const std::exception &e) {
if (chunk == chunks.end())
return false; return false;
}
chunk->state = ChunkState::Reserved;
chunk->cpuAddress = 0;
return true; return true;
} }

View File

@ -43,11 +43,12 @@ namespace skyline {
/** /**
* @brief Finds a chunk of the specified type in the GPU address space that is larger than the given size * @brief Finds a chunk of the specified type in the GPU address space that is larger than the given size
* @param state The state of the chunk to find
* @param size The minimum size of the chunk to find * @param size The minimum size of the chunk to find
* @param state The state desired state of the chunk to find * @param alignment The alignment of the chunk to find
* @return The first unmapped chunk in the GPU address space that fits the requested size * @return The first unmapped chunk in the GPU address space that fulfils the requested conditions
*/ */
std::optional<ChunkDescriptor> FindChunk(u64 size, ChunkState state); std::optional<ChunkDescriptor> FindChunk(ChunkState state, u64 size, u64 alignment = 0);
/** /**
* @brief Inserts a chunk into the chunk list, resizing and splitting as necessary * @brief Inserts a chunk into the chunk list, resizing and splitting as necessary
@ -62,9 +63,10 @@ namespace skyline {
/** /**
* @brief Reserves a region of the GPU address space so it will not be chosen automatically when mapping * @brief Reserves a region of the GPU address space so it will not be chosen automatically when mapping
* @param size The size of the region to reserve * @param size The size of the region to reserve
* @param alignment The alignment of the region to reserve
* @return The virtual GPU base address of the region base * @return The virtual GPU base address of the region base
*/ */
u64 ReserveSpace(u64 size); u64 ReserveSpace(u64 size, u64 alignment);
/** /**
* @brief Reserves a fixed region of the GPU address space so it will not be chosen automatically when mapping * @brief Reserves a fixed region of the GPU address space so it will not be chosen automatically when mapping
@ -92,10 +94,10 @@ namespace skyline {
u64 MapFixed(u64 address, u64 cpuAddress, u64 size); u64 MapFixed(u64 address, u64 cpuAddress, u64 size);
/** /**
* @brief Unmaps the chunk that starts at 'offset' from the GPU address space * @brief Unmaps all chunks in the given region from the GPU address space
* @return Whether the operation succeeded * @return Whether the operation succeeded
*/ */
bool Unmap(u64 address); bool Unmap(u64 address, u64 size);
void Read(u8 *destination, u64 address, u64 size) const; void Read(u8 *destination, u64 address, u64 size) const;

View File

@ -5,4 +5,8 @@
namespace skyline::service::account { namespace skyline::service::account {
IManagerForApplication::IManagerForApplication(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} IManagerForApplication::IManagerForApplication(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
Result IManagerForApplication::CheckAvailability(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return {};
}
} }

View File

@ -13,5 +13,14 @@ namespace skyline::service::account {
class IManagerForApplication : public BaseService { class IManagerForApplication : public BaseService {
public: public:
IManagerForApplication(const DeviceState &state, ServiceManager &manager); IManagerForApplication(const DeviceState &state, ServiceManager &manager);
/**
* @brief This checks if the given user has access to online services
*/
Result CheckAvailability(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL(
SFUNC(0x0, IManagerForApplication, CheckAvailability)
)
}; };
} }

View File

@ -27,7 +27,7 @@ namespace skyline::service::am {
Result IStorageAccessor::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IStorageAccessor::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto offset{request.Pop<i64>()}; auto offset{request.Pop<i64>()};
auto size{std::min(static_cast<i64>(request.inputBuf.at(0).size()), static_cast<i64>(parent->content.size()) - offset)}; auto size{std::min(static_cast<i64>(request.outputBuf.at(0).size()), static_cast<i64>(parent->content.size()) - offset)};
if (offset > parent->content.size()) if (offset > parent->content.size())
return result::OutOfBounds; return result::OutOfBounds;

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KProcess.h>
#include "results.h"
#include "IDirectory.h"
namespace skyline::service::fssrv {
struct __attribute__((packed)) DirectoryEntry {
std::array<char, 0x301> name;
struct {
bool directory : 1;
bool archive : 1;
u8 _pad_ : 6;
} attributes;
u16 _pad0_;
vfs::Directory::EntryType type;
u8 _pad1_[3];
u64 size;
};
IDirectory::IDirectory(std::shared_ptr<vfs::Directory> backing, std::shared_ptr<vfs::FileSystem> backingFs, const DeviceState &state, ServiceManager &manager) : backing(backing), backingFs(backingFs), BaseService(state, manager) {}
Result IDirectory::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto entries{backing->Read()};
auto outputEntries{request.inputBuf.at(0).cast<DirectoryEntry>()};
size_t i{};
for (; i < std::min(entries.size(), outputEntries.size()); i++) {
auto &entry{entries.at(i)};
outputEntries[i] = {
.type = entry.type,
.attributes.directory = (entry.type == vfs::Directory::EntryType::Directory),
.size = entry.size,
};
span(outputEntries[i].name).copy_from(entry.name);
}
response.Push<u64>(i);
return {};
}
}

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/base_service.h>
#include <services/serviceman.h>
#include <vfs/directory.h>
#include <vfs/filesystem.h>
namespace skyline::service::fssrv {
/**
* @brief IDirectory is an interface for accessing directory contents
* @url https://switchbrew.org/wiki/Filesystem_services#IDirectory
*/
class IDirectory : public BaseService {
private:
std::shared_ptr<vfs::Directory> backing; //!< Backing directory of the IDirectory
std::shared_ptr<vfs::FileSystem> backingFs; //!< Backing filesystem of the IDirectory
public:
IDirectory(std::shared_ptr<vfs::Directory> backing, std::shared_ptr<vfs::FileSystem> backingFs, const DeviceState &state, ServiceManager &manager);
/**
* @brief Reads the contents of an IDirectory
*/
Result Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL(
SFUNC(0x0, IDirectory, Read),
)
};
}

View File

@ -3,13 +3,14 @@
#include "results.h" #include "results.h"
#include "IFile.h" #include "IFile.h"
#include "IDirectory.h"
#include "IFileSystem.h" #include "IFileSystem.h"
namespace skyline::service::fssrv { namespace skyline::service::fssrv {
IFileSystem::IFileSystem(std::shared_ptr<vfs::FileSystem> backing, const DeviceState &state, ServiceManager &manager) : backing(backing), BaseService(state, manager) {} IFileSystem::IFileSystem(std::shared_ptr<vfs::FileSystem> backing, const DeviceState &state, ServiceManager &manager) : backing(backing), BaseService(state, manager) {}
Result IFileSystem::CreateFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IFileSystem::CreateFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::string path{request.inputBuf.at(0).as<char>()}; std::string path(request.inputBuf.at(0).as_string(true));
auto mode{request.Pop<u64>()}; auto mode{request.Pop<u64>()};
auto size{request.Pop<u32>()}; auto size{request.Pop<u32>()};
@ -17,7 +18,7 @@ namespace skyline::service::fssrv {
} }
Result IFileSystem::GetEntryType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IFileSystem::GetEntryType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::string path{request.inputBuf.at(0).as<char>()}; std::string path(request.inputBuf.at(0).as_string(true));
auto type{backing->GetEntryType(path)}; auto type{backing->GetEntryType(path)};
@ -31,7 +32,7 @@ namespace skyline::service::fssrv {
} }
Result IFileSystem::OpenFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IFileSystem::OpenFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::string path{request.inputBuf.at(0).as<char>()}; std::string path(request.inputBuf.at(0).as_string(true));
auto mode{request.Pop<vfs::Backing::Mode>()}; auto mode{request.Pop<vfs::Backing::Mode>()};
if (!backing->FileExists(path)) if (!backing->FileExists(path))
@ -46,6 +47,19 @@ namespace skyline::service::fssrv {
return {}; return {};
} }
Result IFileSystem::OpenDirectory(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::string path(request.inputBuf.at(0).as_string(true));
if (!path.ends_with("/"))
path += "/";
auto listMode{request.Pop<vfs::Directory::ListMode>()};
auto directory{backing->OpenDirectory(path, listMode)};
manager.RegisterService(std::make_shared<IDirectory>(directory, backing, state, manager), session, response);
return {};
}
Result IFileSystem::Commit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IFileSystem::Commit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return {}; return {};
} }

View File

@ -35,6 +35,11 @@ namespace skyline::service::fssrv {
*/ */
Result OpenFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result OpenFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief This returns an IDirectory handle for the requested path and mask (https://switchbrew.org/wiki/Filesystem_services#OpenDirectory)
*/
Result OpenDirectory(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/** /**
* @brief Commits all changes to the filesystem * @brief Commits all changes to the filesystem
* @url https://switchbrew.org/wiki/Filesystem_services#Commit * @url https://switchbrew.org/wiki/Filesystem_services#Commit
@ -45,6 +50,7 @@ namespace skyline::service::fssrv {
SFUNC(0x0, IFileSystem, CreateFile), SFUNC(0x0, IFileSystem, CreateFile),
SFUNC(0x7, IFileSystem, GetEntryType), SFUNC(0x7, IFileSystem, GetEntryType),
SFUNC(0x8, IFileSystem, OpenFile), SFUNC(0x8, IFileSystem, OpenFile),
SFUNC(0x9, IFileSystem, OpenDirectory),
SFUNC(0xA, IFileSystem, Commit) SFUNC(0xA, IFileSystem, Commit)
) )
}; };

View File

@ -80,6 +80,8 @@ namespace skyline::service::nvdrv {
SFUNC(0x3, INvDrvServices, Initialize), SFUNC(0x3, INvDrvServices, Initialize),
SFUNC(0x4, INvDrvServices, QueryEvent), SFUNC(0x4, INvDrvServices, QueryEvent),
SFUNC(0x8, INvDrvServices, SetAruid), SFUNC(0x8, INvDrvServices, SetAruid),
SFUNC(0xB, INvDrvServices, Ioctl2),
SFUNC(0xC, INvDrvServices, Ioctl3),
SFUNC(0xD, INvDrvServices, SetGraphicsFirmwareMemoryMarginEnabled) SFUNC(0xD, INvDrvServices, SetGraphicsFirmwareMemoryMarginEnabled)
) )
}; };

View File

@ -78,7 +78,7 @@ namespace skyline::service::nvdrv::device {
* @return The name of the class * @return The name of the class
* @note The lifetime of the returned string is tied to that of the class * @note The lifetime of the returned string is tied to that of the class
*/ */
const std::string& GetName(); const std::string &GetName();
/** /**
* @brief Handles IOCTL calls for devices * @brief Handles IOCTL calls for devices

View File

@ -7,6 +7,14 @@
#include "nvhost_as_gpu.h" #include "nvhost_as_gpu.h"
namespace skyline::service::nvdrv::device { namespace skyline::service::nvdrv::device {
struct MappingFlags {
bool fixed : 1;
u8 _pad0_ : 7;
bool remap : 1;
u32 _pad1_ : 23;
};
static_assert(sizeof(MappingFlags) == sizeof(u32));
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state) {} NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state) {}
NvStatus NvHostAsGpu::BindChannel(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) { NvStatus NvHostAsGpu::BindChannel(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
@ -15,22 +23,22 @@ namespace skyline::service::nvdrv::device {
NvStatus NvHostAsGpu::AllocSpace(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) { NvStatus NvHostAsGpu::AllocSpace(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
struct Data { struct Data {
u32 pages; // In u32 pages; // In
u32 pageSize; // In u32 pageSize; // In
u32 flags; // In MappingFlags flags; // In
u32 _pad_; u32 _pad_;
union { union {
u64 offset; // InOut u64 offset; // InOut
u64 align; // In u64 align; // In
}; };
} region = buffer.as<Data>(); } &region = buffer.as<Data>();
u64 size{static_cast<u64>(region.pages) * static_cast<u64>(region.pageSize)}; u64 size{static_cast<u64>(region.pages) * static_cast<u64>(region.pageSize)};
if (region.flags & 1) if (region.flags.fixed)
region.offset = state.gpu->memoryManager.ReserveFixed(region.offset, size); region.offset = state.gpu->memoryManager.ReserveFixed(region.offset, size);
else else
region.offset = state.gpu->memoryManager.ReserveSpace(size); region.offset = state.gpu->memoryManager.ReserveSpace(size, region.align);
if (region.offset == 0) { if (region.offset == 0) {
state.logger->Warn("Failed to allocate GPU address space region!"); state.logger->Warn("Failed to allocate GPU address space region!");
@ -43,32 +51,67 @@ namespace skyline::service::nvdrv::device {
NvStatus NvHostAsGpu::UnmapBuffer(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) { NvStatus NvHostAsGpu::UnmapBuffer(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
u64 offset{buffer.as<u64>()}; u64 offset{buffer.as<u64>()};
if (!state.gpu->memoryManager.Unmap(offset)) try {
state.logger->Warn("Failed to unmap chunk at 0x{:X}", offset); auto region{regionMap.at(offset)};
// Non-fixed regions are unmapped so that they can be used by future non-fixed mappings
if (!region.fixed)
if (!state.gpu->memoryManager.Unmap(offset, region.size))
state.logger->Warn("Failed to unmap region at 0x{:X}", offset);
regionMap.erase(offset);
} catch (const std::out_of_range &e) {
state.logger->Warn("Couldn't find region to unmap at 0x{:X}", offset);
}
return NvStatus::Success; return NvStatus::Success;
} }
NvStatus NvHostAsGpu::Modify(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) { NvStatus NvHostAsGpu::Modify(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
struct Data { struct Data {
u32 flags; // In MappingFlags flags; // In
u32 kind; // In u32 kind; // In
u32 nvmapHandle; // In u32 nvmapHandle; // In
u32 pageSize; // InOut u32 pageSize; // InOut
u64 bufferOffset; // In u64 bufferOffset; // In
u64 mappingSize; // In u64 mappingSize; // In
u64 offset; // InOut u64 offset; // InOut
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
try { try {
auto driver{nvdrv::driver.lock()}; auto driver{nvdrv::driver.lock()};
auto nvmap{driver->nvMap.lock()}; auto nvmap{driver->nvMap.lock()};
auto mapping{nvmap->handleTable.at(data.nvmapHandle)}; auto mapping{nvmap->handleTable.at(data.nvmapHandle)};
if (data.flags.remap) {
auto region{regionMap.upper_bound(data.offset)};
if ((region == regionMap.begin()) || (region == regionMap.end())) {
state.logger->Warn("Cannot remap an unmapped GPU address space region: 0x{:X}", data.offset);
return NvStatus::BadParameter;
}
region--; // Upper bound gives us the region after the one we want
if (region->second.size < data.mappingSize) {
state.logger->Warn("Cannot remap an partially mapped GPU address space region: 0x{:X}", data.offset);
return NvStatus::BadParameter;
}
u64 gpuAddress{data.offset + data.bufferOffset};
u64 cpuAddress{region->second.cpuAddress + data.bufferOffset};
if (state.gpu->memoryManager.MapFixed(gpuAddress, cpuAddress, data.mappingSize)) {
state.logger->Warn("Failed to remap GPU address space region: 0x{:X}", gpuAddress);
return NvStatus::BadParameter;
}
return NvStatus::Success;
}
u64 mapPhysicalAddress{data.bufferOffset + mapping->address}; u64 mapPhysicalAddress{data.bufferOffset + mapping->address};
u64 mapSize{data.mappingSize ? data.mappingSize : mapping->size}; u64 mapSize{data.mappingSize ? data.mappingSize : mapping->size};
if (data.flags & 1) if (data.flags.fixed)
data.offset = state.gpu->memoryManager.MapFixed(data.offset, mapPhysicalAddress, mapSize); data.offset = state.gpu->memoryManager.MapFixed(data.offset, mapPhysicalAddress, mapSize);
else else
data.offset = state.gpu->memoryManager.MapAllocate(mapPhysicalAddress, mapSize); data.offset = state.gpu->memoryManager.MapAllocate(mapPhysicalAddress, mapSize);
@ -78,6 +121,8 @@ namespace skyline::service::nvdrv::device {
return NvStatus::BadParameter; return NvStatus::BadParameter;
} }
regionMap[data.offset] = {mapPhysicalAddress, mapSize, data.flags.fixed};
return NvStatus::Success; return NvStatus::Success;
} catch (const std::out_of_range &) { } catch (const std::out_of_range &) {
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.nvmapHandle); state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.nvmapHandle);

View File

@ -11,6 +11,15 @@ namespace skyline::service::nvdrv::device {
* @url https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu * @url https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu
*/ */
class NvHostAsGpu : public NvDevice { class NvHostAsGpu : public NvDevice {
private:
struct AddressSpaceRegion {
u64 cpuAddress;
u64 size;
bool fixed;
};
std::map<u64, AddressSpaceRegion> regionMap; //!< This maps the base addresses of mapped buffers to their total sizes and mapping type, this is needed as what was originally a single buffer may have been split into multiple GPU side buffers with the remap flag.
public: public:
NvHostAsGpu(const DeviceState &state); NvHostAsGpu(const DeviceState &state);

View File

@ -54,7 +54,12 @@ namespace skyline::service::nvdrv::device {
throw exception("Waiting on a fence through SubmitGpfifo is unimplemented"); throw exception("Waiting on a fence through SubmitGpfifo is unimplemented");
} }
state.gpu->gpfifo.Push(span(state.process->GetPointer<gpu::gpfifo::GpEntry>(data.address), data.numEntries)); state.gpu->gpfifo.Push([&]() {
if (type == IoctlType::Ioctl2)
return inlineBuffer.cast<gpu::gpfifo::GpEntry>();
else
return span(state.process->GetPointer<gpu::gpfifo::GpEntry>(data.address), data.numEntries);
}());
data.fence.id = channelFence.id; data.fence.id = channelFence.id;
@ -113,6 +118,10 @@ namespace skyline::service::nvdrv::device {
return NvStatus::Success; return NvStatus::Success;
} }
NvStatus NvHostChannel::SetTimeslice(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer) {
return NvStatus::Success;
}
NvStatus NvHostChannel::SetUserData(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) { NvStatus NvHostChannel::SetUserData(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
return NvStatus::Success; return NvStatus::Success;
} }

View File

@ -76,6 +76,12 @@ namespace skyline::service::nvdrv::device {
*/ */
NvStatus AllocGpfifoEx2(IoctlType type, span<u8> buffer, span<u8> inlineBuffer); NvStatus AllocGpfifoEx2(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
/**
* @brief Sets the timeslice of the channel
* @url https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_TIMESLICE)
*/
NvStatus SetTimeslice(IoctlType type, std::span<u8> buffer, std::span<u8> inlineBuffer);
/** /**
* @brief Sets the user specific data * @brief Sets the user specific data
* @url https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_USER_DATA * @url https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_USER_DATA
@ -93,6 +99,7 @@ namespace skyline::service::nvdrv::device {
NVFUNC(0x480C, NvHostChannel, SetErrorNotifier), NVFUNC(0x480C, NvHostChannel, SetErrorNotifier),
NVFUNC(0x480D, NvHostChannel, SetPriority), NVFUNC(0x480D, NvHostChannel, SetPriority),
NVFUNC(0x481A, NvHostChannel, AllocGpfifoEx2), NVFUNC(0x481A, NvHostChannel, AllocGpfifoEx2),
NVFUNC(0x481B, NvHostChannel, SubmitGpfifo), // Our SubmitGpfifo implementation also handles SubmitGpfifoEx
NVFUNC(0x4714, NvHostChannel, SetUserData) NVFUNC(0x4714, NvHostChannel, SetUserData)
) )
}; };

View File

@ -112,7 +112,7 @@ namespace skyline::service::nvdrv::device {
userEventId = FindFreeEvent(data.fence.id); userEventId = FindFreeEvent(data.fence.id);
} }
auto& event{*events.at(userEventId)}; auto &event{*events.at(userEventId)};
if (event.state == NvHostEvent::State::Cancelled || event.state == NvHostEvent::State::Available || event.state == NvHostEvent::State::Signaled) { if (event.state == NvHostEvent::State::Cancelled || event.state == NvHostEvent::State::Available || event.state == NvHostEvent::State::Signaled) {
state.logger->Debug("Now waiting on nvhost event: {} with fence: {}", userEventId, data.fence.id); state.logger->Debug("Now waiting on nvhost event: {} with fence: {}", userEventId, data.fence.id);
event.Wait(state.gpu, data.fence); event.Wait(state.gpu, data.fence);

View File

@ -77,7 +77,13 @@ namespace skyline::service::nvdrv::device {
if (data.gpuCharacteristicsBufSize < sizeof(GpuCharacteristics)) if (data.gpuCharacteristicsBufSize < sizeof(GpuCharacteristics))
return NvStatus::InvalidSize; return NvStatus::InvalidSize;
data.gpuCharacteristics = GpuCharacteristics{}; // The IOCTL3 version of GetCharacteristics additionally outputs to the inline output buffer
if (type == IoctlType::Ioctl3) {
auto &inlineCharacteristics{inlineBuffer.as<GpuCharacteristics>()};
data.gpuCharacteristics = inlineCharacteristics = GpuCharacteristics{};
} else {
data.gpuCharacteristics = GpuCharacteristics{};
}
data.gpuCharacteristicsBufSize = sizeof(GpuCharacteristics); data.gpuCharacteristicsBufSize = sizeof(GpuCharacteristics);
return NvStatus::Success; return NvStatus::Success;
@ -90,8 +96,14 @@ namespace skyline::service::nvdrv::device {
u64 maskBuf; // Out u64 maskBuf; // Out
} &data = buffer.as<Data>(); } &data = buffer.as<Data>();
if (data.maskBufSize) if (data.maskBufSize) {
data.maskBuf = 0x3; if (type == IoctlType::Ioctl3) {
auto &inlineMask{inlineBuffer.as<u32>()};
data.maskBuf = inlineMask = 0x3;
} else {
data.maskBuf = 0x3;
}
}
return NvStatus::Success; return NvStatus::Success;
} }

View File

@ -28,7 +28,7 @@ namespace skyline::service::nvdrv {
} }
u32 NvHostSyncpoint::FindFreeSyncpoint() { u32 NvHostSyncpoint::FindFreeSyncpoint() {
for (u32 i{}; i < constant::MaxHwSyncpointCount; i++) for (u32 i{1}; i < constant::MaxHwSyncpointCount; i++)
if (!syncpoints[i].reserved) if (!syncpoints[i].reserved)
return i; return i;

View File

@ -73,7 +73,7 @@ namespace skyline::service {
SERVICE_CASE(pctl::IParentalControlServiceFactory, "pctl:r") SERVICE_CASE(pctl::IParentalControlServiceFactory, "pctl:r")
SERVICE_CASE(lm::ILogService, "lm") SERVICE_CASE(lm::ILogService, "lm")
SERVICE_CASE(account::IAccountServiceForApplication, "acc:u0") SERVICE_CASE(account::IAccountServiceForApplication, "acc:u0")
SERVICE_CASE(friends::IServiceCreator, "friend") SERVICE_CASE(friends::IServiceCreator, "friend:u")
SERVICE_CASE(nfp::IUserManager, "nfp:user") SERVICE_CASE(nfp::IUserManager, "nfp:user")
SERVICE_CASE(nifm::IStaticService, "nifm:u") SERVICE_CASE(nifm::IStaticService, "nifm:u")
SERVICE_CASE(socket::IClient, "bsd:u") SERVICE_CASE(socket::IClient, "bsd:u")

View File

@ -19,6 +19,7 @@ namespace skyline::vfs {
struct Entry { struct Entry {
std::string name; std::string name;
EntryType type; EntryType type;
size_t size; //!< 0 if a directory
}; };
/** /**
@ -29,7 +30,7 @@ namespace skyline::vfs {
bool directory : 1; //!< The directory listing will contain subdirectories bool directory : 1; //!< The directory listing will contain subdirectories
bool file : 1; //!< The directory listing will contain files bool file : 1; //!< The directory listing will contain files
}; };
u32 raw; u32 raw{};
}; };
static_assert(sizeof(ListMode) == 0x4); static_assert(sizeof(ListMode) == 0x4);

View File

@ -3,7 +3,6 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include "os_backing.h" #include "os_backing.h"
@ -84,4 +83,45 @@ namespace skyline::vfs {
return std::nullopt; return std::nullopt;
} }
std::shared_ptr<Directory> OsFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) {
return std::make_shared<OsFileSystemDirectory>(basePath + path, listMode);
}
OsFileSystemDirectory::OsFileSystemDirectory(const std::string &path, Directory::ListMode listMode) : Directory(listMode), path(path) {}
std::vector<Directory::Entry> OsFileSystemDirectory::Read() {
if (!listMode.file && !listMode.directory)
return {};
std::vector<Directory::Entry> outputEntries;
struct dirent *entry;
auto directory{opendir(path.c_str())};
if (!directory)
throw exception("Failed to open directory: {}, error: {}", path, strerror(errno));
while ((entry = readdir(directory))) {
struct stat entryInfo;
if (stat((path + std::string(entry->d_name)).c_str(), &entryInfo))
throw exception("Failed to stat directory entry: {}, error: {}", entry->d_name, strerror(errno));
std::string name(entry->d_name);
if (S_ISDIR(entryInfo.st_mode) && listMode.directory && (name != ".") && (name != "..")) {
outputEntries.push_back(Directory::Entry{
.type = Directory::EntryType::Directory,
.name = std::string(entry->d_name),
.size = 0,
});
} else if (S_ISREG(entryInfo.st_mode) && listMode.file) {
outputEntries.push_back(Directory::Entry{
.type = Directory::EntryType::File,
.name = std::string(entry->d_name),
.size = static_cast<size_t>(entryInfo.st_size),
});
}
}
return outputEntries;
}
} }

View File

@ -23,5 +23,19 @@ namespace skyline::vfs {
std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}); std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false});
std::optional<Directory::EntryType> GetEntryType(const std::string &path); std::optional<Directory::EntryType> GetEntryType(const std::string &path);
std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode);
}; };
}
/**
* @brief OsFileSystemDirectory abstracts access to a native linux directory through the VFS APIs
*/
class OsFileSystemDirectory : public Directory {
private:
std::string path;
public:
OsFileSystemDirectory(const std::string &path, ListMode listMode);
std::vector<Entry> Read();
};}

View File

@ -54,7 +54,7 @@ namespace skyline::vfs {
std::vector<Directory::Entry> fileList; std::vector<Directory::Entry> fileList;
for (const auto &file : fileMap) for (const auto &file : fileMap)
fileList.emplace_back(Directory::Entry{file.first, Directory::EntryType::File}); fileList.emplace_back(Directory::Entry{file.first, Directory::EntryType::File, file.second.size});
return std::make_shared<PartitionFileSystemDirectory>(fileList, listMode); return std::make_shared<PartitionFileSystemDirectory>(fileList, listMode);
} }

View File

@ -22,7 +22,7 @@ namespace skyline::vfs {
*/ */
RegionBacking(const std::shared_ptr<vfs::Backing> &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {}; RegionBacking(const std::shared_ptr<vfs::Backing> &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {};
virtual size_t Read(span<u8> output, size_t offset = 0) { size_t Read(span<u8> output, size_t offset = 0) {
if (!mode.read) if (!mode.read)
throw exception("Attempting to read a backing that is not readable"); throw exception("Attempting to read a backing that is not readable");
if (size - offset < output.size()) if (size - offset < output.size())

View File

@ -92,7 +92,7 @@ namespace skyline::vfs {
std::vector<char> name(romFsFileEntry.nameSize); std::vector<char> name(romFsFileEntry.nameSize);
backing->Read(span(name).cast<u8>(), header.fileMetaTableOffset + offset + sizeof(RomFileSystem::RomFsFileEntry)); backing->Read(span(name).cast<u8>(), header.fileMetaTableOffset + offset + sizeof(RomFileSystem::RomFsFileEntry));
contents.emplace_back(Entry{std::string(name.data(), romFsFileEntry.nameSize), EntryType::File}); contents.emplace_back(Entry{std::string(name.data(), romFsFileEntry.nameSize), EntryType::File, romFsFileEntry.size});
} }
offset = romFsFileEntry.siblingOffset; offset = romFsFileEntry.siblingOffset;