diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 718b49e9..e184244b 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -114,6 +114,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/fssrv/IFileSystem.cpp ${source_DIR}/skyline/services/fssrv/IFile.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/driver.cpp ${source_DIR}/skyline/services/nvdrv/devices/nvdevice.cpp diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.cpp b/app/src/main/cpp/skyline/gpu/memory_manager.cpp index ec54ad3a..b0ecbe7c 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/memory_manager.cpp @@ -14,9 +14,9 @@ namespace skyline::gpu::vmm { chunks.push_back(baseChunk); } - std::optional MemoryManager::FindChunk(u64 size, ChunkState state) { - auto chunk{std::find_if(chunks.begin(), chunks.end(), [size, state](const ChunkDescriptor &chunk) -> bool { - return chunk.size > size && chunk.state == state; + std::optional MemoryManager::FindChunk(ChunkState state, u64 size, u64 alignment) { + auto chunk{std::find_if(chunks.begin(), chunks.end(), [state, size, alignment](const ChunkDescriptor &chunk) -> bool { + return (alignment ? util::IsAligned(chunk.address, alignment) : true) && chunk.size > size && chunk.state == state; })}; if (chunk != chunks.end()) @@ -83,9 +83,9 @@ namespace skyline::gpu::vmm { 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); - auto newChunk{FindChunk(size, ChunkState::Unmapped)}; + auto newChunk{FindChunk(ChunkState::Unmapped, size, alignment)}; if (!newChunk) return 0; @@ -98,7 +98,7 @@ namespace skyline::gpu::vmm { u64 MemoryManager::ReserveFixed(u64 address, u64 size) { if (!util::IsAligned(address, constant::GpuPageSize)) - return false; + return 0; size = util::AlignUp(size, constant::GpuPageSize); @@ -107,7 +107,7 @@ namespace skyline::gpu::vmm { u64 MemoryManager::MapAllocate(u64 address, u64 size) { size = util::AlignUp(size, constant::GpuPageSize); - auto mappedChunk{FindChunk(size, ChunkState::Unmapped)}; + auto mappedChunk{FindChunk(ChunkState::Unmapped, size)}; if (!mappedChunk) return 0; @@ -121,26 +121,22 @@ namespace skyline::gpu::vmm { u64 MemoryManager::MapFixed(u64 address, u64 cpuAddress, u64 size) { if (!util::IsAligned(address, constant::GpuPageSize)) - return false; + return 0; size = util::AlignUp(size, constant::GpuPageSize); 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)) return false; - auto chunk{std::find_if(chunks.begin(), chunks.end(), [address](const ChunkDescriptor &chunk) -> bool { - return chunk.address == address; - })}; - - if (chunk == chunks.end()) + try { + InsertChunk(ChunkDescriptor(address, size, 0, ChunkState::Unmapped)); + } catch (const std::exception &e) { return false; - - chunk->state = ChunkState::Reserved; - chunk->cpuAddress = 0; + } return true; } diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.h b/app/src/main/cpp/skyline/gpu/memory_manager.h index 68740015..3fe60224 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.h +++ b/app/src/main/cpp/skyline/gpu/memory_manager.h @@ -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 + * @param state The state 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 - * @return The first unmapped chunk in the GPU address space that fits the requested size + * @param alignment The alignment of the chunk to find + * @return The first unmapped chunk in the GPU address space that fulfils the requested conditions */ - std::optional FindChunk(u64 size, ChunkState state); + std::optional FindChunk(ChunkState state, u64 size, u64 alignment = 0); /** * @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 * @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 */ - 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 @@ -92,10 +94,10 @@ namespace skyline { 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 */ - bool Unmap(u64 address); + bool Unmap(u64 address, u64 size); void Read(u8 *destination, u64 address, u64 size) const; diff --git a/app/src/main/cpp/skyline/services/account/IManagerForApplication.cpp b/app/src/main/cpp/skyline/services/account/IManagerForApplication.cpp index d98cef7b..2cd7b406 100644 --- a/app/src/main/cpp/skyline/services/account/IManagerForApplication.cpp +++ b/app/src/main/cpp/skyline/services/account/IManagerForApplication.cpp @@ -5,4 +5,8 @@ namespace skyline::service::account { IManagerForApplication::IManagerForApplication(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} + + Result IManagerForApplication::CheckAvailability(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + return {}; + } } diff --git a/app/src/main/cpp/skyline/services/account/IManagerForApplication.h b/app/src/main/cpp/skyline/services/account/IManagerForApplication.h index 0d188a7b..5ac2c1a1 100644 --- a/app/src/main/cpp/skyline/services/account/IManagerForApplication.h +++ b/app/src/main/cpp/skyline/services/account/IManagerForApplication.h @@ -13,5 +13,14 @@ namespace skyline::service::account { class IManagerForApplication : public BaseService { public: 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) + ) }; -} +} \ No newline at end of file diff --git a/app/src/main/cpp/skyline/services/am/storage/IStorageAccessor.cpp b/app/src/main/cpp/skyline/services/am/storage/IStorageAccessor.cpp index 6612c8ba..bdf0dbdd 100644 --- a/app/src/main/cpp/skyline/services/am/storage/IStorageAccessor.cpp +++ b/app/src/main/cpp/skyline/services/am/storage/IStorageAccessor.cpp @@ -27,7 +27,7 @@ namespace skyline::service::am { Result IStorageAccessor::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto offset{request.Pop()}; - auto size{std::min(static_cast(request.inputBuf.at(0).size()), static_cast(parent->content.size()) - offset)}; + auto size{std::min(static_cast(request.outputBuf.at(0).size()), static_cast(parent->content.size()) - offset)}; if (offset > parent->content.size()) return result::OutOfBounds; diff --git a/app/src/main/cpp/skyline/services/fssrv/IDirectory.cpp b/app/src/main/cpp/skyline/services/fssrv/IDirectory.cpp new file mode 100644 index 00000000..eeb08a93 --- /dev/null +++ b/app/src/main/cpp/skyline/services/fssrv/IDirectory.cpp @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include "results.h" +#include "IDirectory.h" + +namespace skyline::service::fssrv { + struct __attribute__((packed)) DirectoryEntry { + std::array 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 backing, std::shared_ptr 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()}; + 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(i); + return {}; + } +} diff --git a/app/src/main/cpp/skyline/services/fssrv/IDirectory.h b/app/src/main/cpp/skyline/services/fssrv/IDirectory.h new file mode 100644 index 00000000..e98305ac --- /dev/null +++ b/app/src/main/cpp/skyline/services/fssrv/IDirectory.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include +#include +#include + +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 backing; //!< Backing directory of the IDirectory + std::shared_ptr backingFs; //!< Backing filesystem of the IDirectory + + public: + IDirectory(std::shared_ptr backing, std::shared_ptr 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), + ) + }; +} diff --git a/app/src/main/cpp/skyline/services/fssrv/IFileSystem.cpp b/app/src/main/cpp/skyline/services/fssrv/IFileSystem.cpp index 37161fa8..179aa47f 100644 --- a/app/src/main/cpp/skyline/services/fssrv/IFileSystem.cpp +++ b/app/src/main/cpp/skyline/services/fssrv/IFileSystem.cpp @@ -3,13 +3,14 @@ #include "results.h" #include "IFile.h" +#include "IDirectory.h" #include "IFileSystem.h" namespace skyline::service::fssrv { IFileSystem::IFileSystem(std::shared_ptr backing, const DeviceState &state, ServiceManager &manager) : backing(backing), BaseService(state, manager) {} Result IFileSystem::CreateFile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - std::string path{request.inputBuf.at(0).as()}; + std::string path(request.inputBuf.at(0).as_string(true)); auto mode{request.Pop()}; auto size{request.Pop()}; @@ -17,7 +18,7 @@ namespace skyline::service::fssrv { } Result IFileSystem::GetEntryType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - std::string path{request.inputBuf.at(0).as()}; + std::string path(request.inputBuf.at(0).as_string(true)); 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) { - std::string path{request.inputBuf.at(0).as()}; + std::string path(request.inputBuf.at(0).as_string(true)); auto mode{request.Pop()}; if (!backing->FileExists(path)) @@ -46,6 +47,19 @@ namespace skyline::service::fssrv { 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()}; + auto directory{backing->OpenDirectory(path, listMode)}; + + manager.RegisterService(std::make_shared(directory, backing, state, manager), session, response); + return {}; + } + Result IFileSystem::Commit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { return {}; } diff --git a/app/src/main/cpp/skyline/services/fssrv/IFileSystem.h b/app/src/main/cpp/skyline/services/fssrv/IFileSystem.h index f4925944..ca099116 100644 --- a/app/src/main/cpp/skyline/services/fssrv/IFileSystem.h +++ b/app/src/main/cpp/skyline/services/fssrv/IFileSystem.h @@ -35,6 +35,11 @@ namespace skyline::service::fssrv { */ 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 * @url https://switchbrew.org/wiki/Filesystem_services#Commit @@ -45,6 +50,7 @@ namespace skyline::service::fssrv { SFUNC(0x0, IFileSystem, CreateFile), SFUNC(0x7, IFileSystem, GetEntryType), SFUNC(0x8, IFileSystem, OpenFile), + SFUNC(0x9, IFileSystem, OpenDirectory), SFUNC(0xA, IFileSystem, Commit) ) }; diff --git a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h index eaf101cb..bc7f609e 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h +++ b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h @@ -80,6 +80,8 @@ namespace skyline::service::nvdrv { SFUNC(0x3, INvDrvServices, Initialize), SFUNC(0x4, INvDrvServices, QueryEvent), SFUNC(0x8, INvDrvServices, SetAruid), + SFUNC(0xB, INvDrvServices, Ioctl2), + SFUNC(0xC, INvDrvServices, Ioctl3), SFUNC(0xD, INvDrvServices, SetGraphicsFirmwareMemoryMarginEnabled) ) }; diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h index 170a6cb6..9d4a85f8 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h @@ -78,7 +78,7 @@ namespace skyline::service::nvdrv::device { * @return The name 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 diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp index 8fe5cec9..fdc47a3a 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp @@ -7,6 +7,14 @@ #include "nvhost_as_gpu.h" 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) {} NvStatus NvHostAsGpu::BindChannel(IoctlType type, span buffer, span inlineBuffer) { @@ -15,22 +23,22 @@ namespace skyline::service::nvdrv::device { NvStatus NvHostAsGpu::AllocSpace(IoctlType type, span buffer, span inlineBuffer) { struct Data { - u32 pages; // In - u32 pageSize; // In - u32 flags; // In + u32 pages; // In + u32 pageSize; // In + MappingFlags flags; // In u32 _pad_; union { - u64 offset; // InOut - u64 align; // In + u64 offset; // InOut + u64 align; // In }; - } region = buffer.as(); + } ®ion = buffer.as(); u64 size{static_cast(region.pages) * static_cast(region.pageSize)}; - if (region.flags & 1) + if (region.flags.fixed) region.offset = state.gpu->memoryManager.ReserveFixed(region.offset, size); else - region.offset = state.gpu->memoryManager.ReserveSpace(size); + region.offset = state.gpu->memoryManager.ReserveSpace(size, region.align); if (region.offset == 0) { 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 buffer, span inlineBuffer) { u64 offset{buffer.as()}; - if (!state.gpu->memoryManager.Unmap(offset)) - state.logger->Warn("Failed to unmap chunk at 0x{:X}", offset); + try { + 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; } NvStatus NvHostAsGpu::Modify(IoctlType type, span buffer, span inlineBuffer) { struct Data { - u32 flags; // In - u32 kind; // In - u32 nvmapHandle; // In - u32 pageSize; // InOut - u64 bufferOffset; // In - u64 mappingSize; // In - u64 offset; // InOut - } &data = buffer.as(); + MappingFlags flags; // In + u32 kind; // In + u32 nvmapHandle; // In + u32 pageSize; // InOut + u64 bufferOffset; // In + u64 mappingSize; // In + u64 offset; // InOut + } &data = buffer.as(); try { auto driver{nvdrv::driver.lock()}; auto nvmap{driver->nvMap.lock()}; 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 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); else data.offset = state.gpu->memoryManager.MapAllocate(mapPhysicalAddress, mapSize); @@ -78,6 +121,8 @@ namespace skyline::service::nvdrv::device { return NvStatus::BadParameter; } + regionMap[data.offset] = {mapPhysicalAddress, mapSize, data.flags.fixed}; + return NvStatus::Success; } catch (const std::out_of_range &) { state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.nvmapHandle); diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h index 8425eedb..3b130c31 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h @@ -11,6 +11,15 @@ namespace skyline::service::nvdrv::device { * @url https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu */ class NvHostAsGpu : public NvDevice { + private: + struct AddressSpaceRegion { + u64 cpuAddress; + u64 size; + bool fixed; + }; + + std::map 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: NvHostAsGpu(const DeviceState &state); diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp index 4d994fa5..29b67250 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp @@ -54,7 +54,12 @@ namespace skyline::service::nvdrv::device { throw exception("Waiting on a fence through SubmitGpfifo is unimplemented"); } - state.gpu->gpfifo.Push(span(state.process->GetPointer(data.address), data.numEntries)); + state.gpu->gpfifo.Push([&]() { + if (type == IoctlType::Ioctl2) + return inlineBuffer.cast(); + else + return span(state.process->GetPointer(data.address), data.numEntries); + }()); data.fence.id = channelFence.id; @@ -113,6 +118,10 @@ namespace skyline::service::nvdrv::device { return NvStatus::Success; } + NvStatus NvHostChannel::SetTimeslice(IoctlType type, std::span buffer, std::span inlineBuffer) { + return NvStatus::Success; + } + NvStatus NvHostChannel::SetUserData(IoctlType type, span buffer, span inlineBuffer) { return NvStatus::Success; } diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.h index ea65abfa..0da5632c 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.h @@ -76,6 +76,12 @@ namespace skyline::service::nvdrv::device { */ NvStatus AllocGpfifoEx2(IoctlType type, span buffer, span 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 buffer, std::span inlineBuffer); + /** * @brief Sets the user specific 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(0x480D, NvHostChannel, SetPriority), NVFUNC(0x481A, NvHostChannel, AllocGpfifoEx2), + NVFUNC(0x481B, NvHostChannel, SubmitGpfifo), // Our SubmitGpfifo implementation also handles SubmitGpfifoEx NVFUNC(0x4714, NvHostChannel, SetUserData) ) }; diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp index 22ffca90..f058abf4 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp @@ -112,7 +112,7 @@ namespace skyline::service::nvdrv::device { 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) { state.logger->Debug("Now waiting on nvhost event: {} with fence: {}", userEventId, data.fence.id); event.Wait(state.gpu, data.fence); diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp index 1f60e2b7..dad1a7c8 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -77,7 +77,13 @@ namespace skyline::service::nvdrv::device { if (data.gpuCharacteristicsBufSize < sizeof(GpuCharacteristics)) 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()}; + data.gpuCharacteristics = inlineCharacteristics = GpuCharacteristics{}; + } else { + data.gpuCharacteristics = GpuCharacteristics{}; + } data.gpuCharacteristicsBufSize = sizeof(GpuCharacteristics); return NvStatus::Success; @@ -90,8 +96,14 @@ namespace skyline::service::nvdrv::device { u64 maskBuf; // Out } &data = buffer.as(); - if (data.maskBufSize) - data.maskBuf = 0x3; + if (data.maskBufSize) { + if (type == IoctlType::Ioctl3) { + auto &inlineMask{inlineBuffer.as()}; + data.maskBuf = inlineMask = 0x3; + } else { + data.maskBuf = 0x3; + } + } return NvStatus::Success; } diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp index f166ccd3..3e84092d 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp @@ -28,7 +28,7 @@ namespace skyline::service::nvdrv { } u32 NvHostSyncpoint::FindFreeSyncpoint() { - for (u32 i{}; i < constant::MaxHwSyncpointCount; i++) + for (u32 i{1}; i < constant::MaxHwSyncpointCount; i++) if (!syncpoints[i].reserved) return i; diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp index 7ee882e5..ee10afa8 100644 --- a/app/src/main/cpp/skyline/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/services/serviceman.cpp @@ -73,7 +73,7 @@ namespace skyline::service { SERVICE_CASE(pctl::IParentalControlServiceFactory, "pctl:r") SERVICE_CASE(lm::ILogService, "lm") 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(nifm::IStaticService, "nifm:u") SERVICE_CASE(socket::IClient, "bsd:u") diff --git a/app/src/main/cpp/skyline/vfs/directory.h b/app/src/main/cpp/skyline/vfs/directory.h index 50b1d223..4f6e5864 100644 --- a/app/src/main/cpp/skyline/vfs/directory.h +++ b/app/src/main/cpp/skyline/vfs/directory.h @@ -19,6 +19,7 @@ namespace skyline::vfs { struct Entry { std::string name; 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 file : 1; //!< The directory listing will contain files }; - u32 raw; + u32 raw{}; }; static_assert(sizeof(ListMode) == 0x4); diff --git a/app/src/main/cpp/skyline/vfs/os_filesystem.cpp b/app/src/main/cpp/skyline/vfs/os_filesystem.cpp index b2a4e217..d66250fa 100644 --- a/app/src/main/cpp/skyline/vfs/os_filesystem.cpp +++ b/app/src/main/cpp/skyline/vfs/os_filesystem.cpp @@ -3,7 +3,6 @@ #include #include - #include #include #include "os_backing.h" @@ -84,4 +83,45 @@ namespace skyline::vfs { return std::nullopt; } + + std::shared_ptr OsFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) { + return std::make_shared(basePath + path, listMode); + } + + OsFileSystemDirectory::OsFileSystemDirectory(const std::string &path, Directory::ListMode listMode) : Directory(listMode), path(path) {} + + std::vector OsFileSystemDirectory::Read() { + if (!listMode.file && !listMode.directory) + return {}; + + std::vector 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(entryInfo.st_size), + }); + } + } + + return outputEntries; + } } diff --git a/app/src/main/cpp/skyline/vfs/os_filesystem.h b/app/src/main/cpp/skyline/vfs/os_filesystem.h index 121ebf81..ca40f195 100644 --- a/app/src/main/cpp/skyline/vfs/os_filesystem.h +++ b/app/src/main/cpp/skyline/vfs/os_filesystem.h @@ -23,5 +23,19 @@ namespace skyline::vfs { std::shared_ptr OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}); std::optional GetEntryType(const std::string &path); + + std::shared_ptr 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 Read(); + };} diff --git a/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp b/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp index 5397e7fd..462e998f 100644 --- a/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp +++ b/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp @@ -54,7 +54,7 @@ namespace skyline::vfs { std::vector fileList; 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(fileList, listMode); } diff --git a/app/src/main/cpp/skyline/vfs/region_backing.h b/app/src/main/cpp/skyline/vfs/region_backing.h index 510e0b26..d1c8ecaf 100644 --- a/app/src/main/cpp/skyline/vfs/region_backing.h +++ b/app/src/main/cpp/skyline/vfs/region_backing.h @@ -22,7 +22,7 @@ namespace skyline::vfs { */ RegionBacking(const std::shared_ptr &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {}; - virtual size_t Read(span output, size_t offset = 0) { + size_t Read(span output, size_t offset = 0) { if (!mode.read) throw exception("Attempting to read a backing that is not readable"); if (size - offset < output.size()) diff --git a/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp b/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp index a9cbe6ad..cbb797b9 100644 --- a/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp +++ b/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp @@ -92,7 +92,7 @@ namespace skyline::vfs { std::vector name(romFsFileEntry.nameSize); backing->Read(span(name).cast(), 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;