Rework VI + IHOSBinder

VI/IHOSBinder suffered from major inaccuracies in their function due to being quickly thrown together initially with little concern for accuracy, this has now been fixed with them being substantially more accurate now.
This commit is contained in:
PixelyIon 2021-07-04 05:21:19 +05:30 committed by ◱ Mark
parent ca4744c603
commit f1433ad0d9
24 changed files with 425 additions and 262 deletions

View File

@ -121,7 +121,6 @@ add_library(skyline SHARED
${source_DIR}/skyline/vfs/ticket.cpp
${source_DIR}/skyline/services/serviceman.cpp
${source_DIR}/skyline/services/base_service.cpp
${source_DIR}/skyline/services/common/parcel.cpp
${source_DIR}/skyline/services/sm/IUserInterface.cpp
${source_DIR}/skyline/services/fatalsrv/IService.cpp
${source_DIR}/skyline/services/audio/IAudioOutManager.cpp
@ -182,12 +181,13 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/nvdrv/devices/nvhost_channel.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp
${source_DIR}/skyline/services/hosbinder/parcel.cpp
${source_DIR}/skyline/services/hosbinder/IHOSBinderDriver.cpp
${source_DIR}/skyline/services/hosbinder/GraphicBufferProducer.cpp
${source_DIR}/skyline/services/visrv/IDisplayService.cpp
${source_DIR}/skyline/services/visrv/IApplicationDisplayService.cpp
${source_DIR}/skyline/services/visrv/IManagerDisplayService.cpp
${source_DIR}/skyline/services/visrv/IManagerRootService.cpp
${source_DIR}/skyline/services/visrv/IRootService.cpp
${source_DIR}/skyline/services/visrv/ISystemDisplayService.cpp
${source_DIR}/skyline/services/pl/IPlatformServiceManager.cpp
${source_DIR}/skyline/services/aocsrv/IAddOnContentManager.cpp

View File

@ -218,11 +218,16 @@ namespace skyline {
/**
* @brief Returns a std::string_view from the payload
* @param size The length of the string (0 means the string is null terminated)
* @param size The length of the string (0 should only be used with nullTerminated and automatically determines size)
* @param nullTerminated If the returned view should only encapsulate a null terminated substring
*/
std::string_view PopString(size_t size = 0) {
auto view{size ? std::string_view(reinterpret_cast<const char *>(payloadOffset), size) : std::string_view(reinterpret_cast<const char *>(payloadOffset))};
payloadOffset += view.length();
std::string_view PopString(size_t size = 0, bool nullTerminated = true) {
size = size ? size : cmdArgSz - reinterpret_cast<u64>(payloadOffset);
auto view{span(payloadOffset, size).as_string(nullTerminated)};
if (nullTerminated)
payloadOffset += size;
else
payloadOffset += view.length();
return view;
}

View File

@ -2,11 +2,11 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KProcess.h>
#include <services/hosbinder/GraphicBufferProducer.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#include "ISelfController.h"
namespace skyline::service::am {
ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared<type::KEvent>(state, false)), accumulatedSuspendedTickChangedEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {}
ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared<type::KEvent>(state, false)), accumulatedSuspendedTickChangedEvent(std::make_shared<type::KEvent>(state, false)), hosbinder(manager.CreateOrGetService<hosbinder::IHOSBinderDriver>("dispdrv")), BaseService(state, manager) {}
Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return {};
@ -47,18 +47,12 @@ namespace skyline::service::am {
}
Result ISelfController::CreateManagedDisplayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
state.logger->Debug("Creating Managed Layer on Default Display");
auto producer{hosbinder::producer.lock()};
if (producer->layerStatus != hosbinder::LayerStatus::Uninitialized)
throw exception("The application is creating more than one layer");
producer->layerStatus = hosbinder::LayerStatus::Managed;
response.Push<u64>(0);
auto layerId{hosbinder->CreateLayer(hosbinder::DisplayId::Default)};
state.logger->Debug("Creating Managed Layer #{} on 'Default' Display", layerId);
response.Push(layerId);
return {};
}
Result ISelfController::GetAccumulatedSuspendedTickValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
// TODO: Properly handle this after we implement game suspending
response.Push<u64>(0);

View File

@ -5,6 +5,10 @@
#include <services/serviceman.h>
namespace skyline::service::hosbinder {
class IHOSBinderDriver;
}
namespace skyline::service::am {
/**
* @brief This has functions relating to an application's own current status
@ -14,6 +18,7 @@ namespace skyline::service::am {
private:
std::shared_ptr<kernel::type::KEvent> libraryAppletLaunchableEvent; //!< This KEvent is triggered when the library applet is launchable
std::shared_ptr<kernel::type::KEvent> accumulatedSuspendedTickChangedEvent; //!< This KEvent is triggered when the time the system has spent in suspend is updated
std::shared_ptr<hosbinder::IHOSBinderDriver> hosbinder; //!< IHOSBinder service for managed display layers
public:
ISelfController(const DeviceState &state, ServiceManager &manager);

View File

@ -70,7 +70,7 @@ namespace skyline::service::hosbinder {
} else {
size_t index{};
std::string bufferString;
for (auto& bufferSlot : queue)
for (auto &bufferSlot : queue)
bufferString += util::Format("\n#{} - State: {}, Has Graphic Buffer: {}, Frame Number: {}", ++index, ToString(bufferSlot.state), static_cast<bool>(bufferSlot.graphicBuffer), bufferSlot.frameNumber);
state.logger->Warn("Cannot find any free buffers to dequeue:{}", bufferString);
return AndroidStatus::InvalidOperation;
@ -489,31 +489,4 @@ namespace skyline::service::hosbinder {
throw exception("An unimplemented transaction was called: {}", static_cast<u32>(code));
}
}
static frz::unordered_map<frz::string, DisplayId, 5> DisplayTypeMap{
{"Default", DisplayId::Default},
{"External", DisplayId::External},
{"Edid", DisplayId::Edid},
{"Internal", DisplayId::Internal},
{"Null", DisplayId::Null},
};
void GraphicBufferProducer::SetDisplay(const std::string &name) {
try {
if (displayId == DisplayId::Null)
displayId = DisplayTypeMap.at(frz::string(name.data(), name.size()));
else
throw exception("Trying to change display type from non-null type");
} catch (const std::out_of_range &) {
throw exception("The display with name: '{}' doesn't exist", name);
}
}
void GraphicBufferProducer::CloseDisplay() {
if (displayId == DisplayId::Null)
state.logger->Warn("Trying to close uninitiated display");
displayId = DisplayId::Null;
}
std::weak_ptr<GraphicBufferProducer> producer{};
}

View File

@ -6,7 +6,7 @@
#pragma once
#include <kernel/types/KEvent.h>
#include <services/common/parcel.h>
#include "parcel.h"
#include "android_types.h"
#include "native_window.h"
@ -43,24 +43,6 @@ namespace skyline::service::hosbinder {
std::unique_ptr<GraphicBuffer> graphicBuffer{};
};
/**
* @brief An enumeration of all the possible display IDs
* @url https://switchbrew.org/wiki/Display_services#DisplayName
*/
enum class DisplayId : u64 {
Default, //!< Refers to the default display used by most applications
External, //!< Refers to an external display
Edid, //!< Refers to an external display with EDID capabilities
Internal, //!< Refers to the the internal display
Null, //!< Refers to the null display which is used for discarding data
};
enum class LayerStatus {
Uninitialized, //!< The layer hasn't been initialized
Stray, //!< The layer has been initialized as a stray layer
Managed, //!< The layer has been initialized as a managed layer
};
/**
* @brief An endpoint for the GraphicBufferProducer interface, it approximately implements BufferQueueProducer but also implements the functionality of interfaces called into by it such as GraphicBufferConsumer, Gralloc and so on
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
@ -147,8 +129,6 @@ namespace skyline::service::hosbinder {
public:
std::shared_ptr<kernel::type::KEvent> bufferEvent; //!< Signalled every time a buffer in the queue is freed
DisplayId displayId{DisplayId::Null}; //!< The ID of this display
LayerStatus layerStatus{LayerStatus::Uninitialized}; //!< The status of the single layer the display has
/**
* @brief The transactions supported by android.gui.IGraphicBufferProducer
@ -178,18 +158,5 @@ namespace skyline::service::hosbinder {
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp;l=277-426
*/
void OnTransact(TransactionCode code, Parcel &in, Parcel &out);
/**
* @brief Sets displayId to a specific display type
* @note displayId has to be DisplayId::Null or this will throw an exception
*/
void SetDisplay(const std::string &name);
/**
* @brief Closes the display by setting displayId to DisplayId::Null
*/
void CloseDisplay();
};
extern std::weak_ptr<GraphicBufferProducer> producer; //!< A globally shared instance of the GraphicsBufferProducer
}

View File

@ -7,43 +7,166 @@
#include "GraphicBufferProducer.h"
namespace skyline::service::hosbinder {
IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager) : producer(hosbinder::producer.expired() ? std::make_shared<GraphicBufferProducer>(state) : hosbinder::producer.lock()), BaseService(state, manager) {
if (hosbinder::producer.expired())
hosbinder::producer = producer;
}
IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
Result IHOSBinderDriver::TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId{request.Pop<u32>()};
// We opted for just supporting a single layer and display as it's what basically all games use and wasting cycles on it is pointless
// If this was not done then we would need to maintain an array of GraphicBufferProducer objects for each layer and send the request for it specifically
// There would also need to be an external compositor which composites all the graphics buffers submitted to every GraphicBufferProducer
auto binderHandle{request.Pop<u32>()};
if (binderHandle != DefaultBinderLayerHandle)
throw exception("Transaction on unknown binder object: #{}", binderHandle);
auto code{request.Pop<GraphicBufferProducer::TransactionCode>()};
Parcel in(request.inputBuf.at(0), state, true);
Parcel out(state);
// We opted for just supporting a single layer and display as it's what basically all games use and wasting cycles on it is pointless
// If this was not done then we would need to maintain an array of GraphicBufferProducer objects for each layer and send the request for it specifically
// There would also need to be an external compositor which composites all the graphics buffers submitted to every GraphicBufferProducer
state.logger->Debug("Layer ID: {}, Code: {}", layerId, code);
producer->OnTransact(code, in, out);
if (!layer)
throw exception("Transacting parcel with non-existant layer");
layer->OnTransact(code, in, out);
out.WriteParcel(request.outputBuf.at(0));
return {};
}
Result IHOSBinderDriver::AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u32>();
auto addVal{request.Pop<i32>()};
auto type{request.Pop<i32>()};
state.logger->Debug("Reference Change: {} {} reference", addVal, type ? "strong" : "weak");
auto binderHandle{request.Pop<u32>()};
if (binderHandle != DefaultBinderLayerHandle)
throw exception("Adjusting Binder object reference count for unknown object: #{}", binderHandle);
auto value{request.Pop<i32>()};
bool isStrong{static_cast<bool>(request.Pop<u32>())};
if (isStrong) {
if (layerStrongReferenceCount != InitialStrongReferenceCount)
layerStrongReferenceCount += value;
else
layerStrongReferenceCount = value;
if (layerStrongReferenceCount < 0) {
state.logger->Warn("Strong reference count is lower than 0: {} + {} = {}", (layerStrongReferenceCount - value), value, layerStrongReferenceCount);
layerStrongReferenceCount = 0;
}
if (layerStrongReferenceCount == 0)
layer.reset();
} else {
layerWeakReferenceCount += value;
if (layerWeakReferenceCount < 0) {
state.logger->Warn("Weak reference count is lower than 0: {} + {} = {}", (layerWeakReferenceCount - value), value, layerWeakReferenceCount);
layerWeakReferenceCount = 0;
}
if (layerWeakReferenceCount == 0 && layerStrongReferenceCount < 1)
layer.reset();
}
state.logger->Debug("Reference Change: {} {} reference (S{} W{})", value, isStrong ? "strong" : "weak", layerStrongReferenceCount, layerWeakReferenceCount);
return {};
}
Result IHOSBinderDriver::GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
KHandle handle{state.process->InsertItem(producer->bufferEvent)};
auto binderHandle{request.Pop<u32>()};
if (binderHandle != DefaultBinderLayerHandle)
throw exception("Getting handle from unknown binder object: #{}", binderHandle);
constexpr u32 BufferEventHandleId{0xF}; //!< The ID of the buffer event handle in the layer object
auto handleId{request.Pop<u32>()};
if (handleId != BufferEventHandleId)
throw exception("Getting unknown handle from binder object: 0x{:X}", handleId);
KHandle handle{state.process->InsertItem(layer->bufferEvent)};
state.logger->Debug("Display Buffer Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle);
return {};
}
DisplayId IHOSBinderDriver::OpenDisplay(std::string_view name) {
if (name.length() > sizeof(u64))
throw exception("Opening display with name larger than sizeof(u64): '{}' ({})", name, name.length());
auto newDisplayId{[&]() -> DisplayId {
#define DISPLAY_CASE(id) case util::MakeMagic<u64>(#id): { return DisplayId::id; }
switch (util::MakeMagic<u64>(name)) {
DISPLAY_CASE(Default)
DISPLAY_CASE(External)
DISPLAY_CASE(Edid)
DISPLAY_CASE(Internal)
DISPLAY_CASE(Null)
default:
throw exception("Opening non-existent display: '{}'", name);
}
#undef DISPLAY_CASE
}()};
if (displayId != DisplayId::Null && displayId != newDisplayId)
throw exception("Opening a new display ({}) prior to closing opened display ({})", name, ToString(displayId));
return displayId = newDisplayId;
}
void IHOSBinderDriver::CloseDisplay(DisplayId id) {
if (displayId != id)
throw exception("Closing an unopened display: {} (Currently open display: {})", ToString(id), ToString(displayId));
displayId = DisplayId::Null;
}
u64 IHOSBinderDriver::CreateLayer(DisplayId pDisplayId) {
if (pDisplayId != displayId)
throw exception("Creating layer on unopened display: '{}'", ToString(pDisplayId));
else if (layer)
throw exception("Creation of multiple layers is not supported");
layerStrongReferenceCount = InitialStrongReferenceCount;
layerWeakReferenceCount = 0;
layer.emplace(state);
return DefaultLayerId;
}
Parcel IHOSBinderDriver::OpenLayer(DisplayId pDisplayId, u64 layerId) {
if (pDisplayId != displayId)
throw exception("Opening layer #{} with unopened display: '{}'", layerId, ToString(pDisplayId));
else if (layerId != DefaultLayerId)
throw exception("Attempting to open unrecognized layer #{}", layerId);
else if (!layer)
throw exception("Opening layer #{} prior to creation or after destruction", layerId);
Parcel parcel(state);
// Flat Binder with the layer's IGraphicBufferProducer
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:bionic/libc/kernel/uapi/linux/binder.h;l=47-57
parcel.Push<u32>(0x2); // Type of the IBinder
parcel.Push<u32>(0); // Flags
parcel.Push<u64>(DefaultBinderLayerHandle); // Handle
parcel.Push<u64>(0); // Cookie
// Unknown HOS-specific layer properties
parcel.Push(util::MakeMagic<u64>("dispdrv\0"));
parcel.Push<u64>({}); // Unknown
parcel.PushObject(0); // Offset of flattened IBinder relative to Parcel data
layerWeakReferenceCount++; // IBinder represents a weak reference to the layer
return parcel;
}
void IHOSBinderDriver::CloseLayer(u64 layerId) {
if (layerId != DefaultLayerId)
throw exception("Closing non-existent layer #{}", layerId);
else if (layerWeakReferenceCount == 0)
throw exception("Closing layer #{} which has no weak references to it", layerId);
if (--layerWeakReferenceCount == 0 && layerStrongReferenceCount < 1)
layer.reset();
}
void IHOSBinderDriver::DestroyLayer(u64 layerId) {
if (layerId != DefaultLayerId)
throw exception("Destroying non-existent layer #{}", layerId);
else if (layer)
throw exception("Destroying layer #{} which hasn't been closed: Weak References: {}, Strong References: {}", layerWeakReferenceCount, layerStrongReferenceCount);
}
}

View File

@ -3,17 +3,46 @@
#pragma once
#include <services/serviceman.h>
#include <services/base_service.h>
#include "GraphicBufferProducer.h"
namespace skyline::service::hosbinder {
class GraphicBufferProducer;
/**
* @brief A display identifier specific to HOS, translated to a corresponding Android display internally
* @url https://switchbrew.org/wiki/Display_services#DisplayName
*/
enum class DisplayId : u64 {
Default, //!< Automatically determines the default display
External, //!< Refers to an external display, if any
Edid, //!< Refers to an external display with EDID capabilities
Internal, //!< Refers to the internal display on the Switch
Null, //!< A placeholder display which doesn't refer to any display
};
ENUM_STRING(DisplayId, {
ENUM_CASE(Default);
ENUM_CASE(External);
ENUM_CASE(Edid);
ENUM_CASE(Internal);
ENUM_CASE(Null);
})
/**
* @brief nvnflinger:dispdrv or nns::hosbinder::IHOSBinderDriver is a translation layer between Android Binder IPC and HOS IPC to communicate with the Android display stack
*/
class IHOSBinderDriver : public BaseService {
private:
std::shared_ptr<GraphicBufferProducer> producer;
DisplayId displayId{DisplayId::Null}; //!< The ID of the display that the layer is connected to
constexpr static u64 DefaultLayerId{1}; //!< The VI ID of the default (and only) layer in our surface stack
constexpr static u32 DefaultBinderLayerHandle{1}; //!< The handle as assigned by SurfaceFlinger of the default layer
constexpr static i32 InitialStrongReferenceCount{std::numeric_limits<i32>::min()}; //!< Initial value for the strong reference count, weak references will keep the object alive till the strong reference count is first mutated
i32 layerStrongReferenceCount; //!< The amount of strong references to the layer object
i32 layerWeakReferenceCount; //!< The amount of weak references to the layer object
std::optional<GraphicBufferProducer> layer; //!< The IGraphicBufferProducer backing the layer (NativeWindow)
void AddWeakLayerReference(u32 count = 1) {
}
public:
IHOSBinderDriver(const DeviceState &state, ServiceManager &manager);
@ -25,7 +54,7 @@ namespace skyline::service::hosbinder {
Result TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Adjusts the reference counts to the underlying binder, it's stubbed as we aren't using the real symbols
* @brief Adjusts the reference counts to the underlying Android reference counted object
* @url https://switchbrew.org/wiki/Nvnflinger_services#AdjustRefcount
*/
Result AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
@ -36,6 +65,38 @@ namespace skyline::service::hosbinder {
*/
Result GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @note This will throw an exception if another display was opened and not closed
*/
DisplayId OpenDisplay(std::string_view name);
/**
* @note This **must** be called prior to opening a different Display
*/
void CloseDisplay(DisplayId id);
/**
* @return An ID that can be utilized to refer to the layer
* @note This will throw an exception if the specified display has not been opened
*/
u64 CreateLayer(DisplayId displayId);
/**
* @return A parcel with a flattened IBinder to the IGraphicBufferProducer of the layer
* @note This will throw an exception if the specified display has not been opened
*/
Parcel OpenLayer(DisplayId displayId, u64 layerId);
/**
* @note This **must** be called prior to destroying the layer
*/
void CloseLayer(u64 layerId);
/**
* @note This **must** be called prior to opening a different Display
*/
void DestroyLayer(u64 layerId);
SERVICE_DECL(
SFUNC(0x0, IHOSBinderDriver, TransactParcel),
SFUNC(0x1, IHOSBinderDriver, AdjustRefcount),

View File

@ -3,7 +3,7 @@
#include "parcel.h"
namespace skyline::service {
namespace skyline::service::hosbinder {
Parcel::Parcel(span<u8> buffer, const DeviceState &state, bool hasToken) : state(state) {
header = buffer.as<ParcelHeader>();

View File

@ -5,7 +5,7 @@
#include <kernel/ipc.h>
namespace skyline::service {
namespace skyline::service::hosbinder {
/**
* @brief This allows easy access and efficient serialization of an Android Parcel object
* @url https://switchbrew.org/wiki/Display_services#Parcel

View File

@ -18,6 +18,9 @@
#include "services/timesrv/core.h"
#include "fssrv/IFileSystemProxy.h"
#include "services/nvdrv/INvDrvServices.h"
#include "hosbinder/IHOSBinderDriver.h"
#include "visrv/IApplicationRootService.h"
#include "visrv/ISystemRootService.h"
#include "visrv/IManagerRootService.h"
#include "pl/IPlatformServiceManager.h"
#include "aocsrv/IAddOnContentManager.h"
@ -35,7 +38,7 @@
#define SERVICE_CASE(class, name, ...) \
case util::MakeMagic<ServiceName>(name): { \
std::shared_ptr<BaseService> serviceObject = std::make_shared<class>(state, *this __VA_OPT__(,) __VA_ARGS__); \
std::shared_ptr<BaseService> serviceObject{std::make_shared<class>(state, *this, ##__VA_ARGS__)}; \
serviceMap[util::MakeMagic<ServiceName>(name)] = serviceObject; \
return serviceObject; \
}
@ -49,7 +52,7 @@ namespace skyline::service {
ServiceManager::ServiceManager(const DeviceState &state) : state(state), smUserInterface(std::make_shared<sm::IUserInterface>(state, *this)), globalServiceState(std::make_shared<GlobalServiceState>(state)) {}
std::shared_ptr<BaseService> ServiceManager::CreateService(ServiceName name) {
std::shared_ptr<BaseService> ServiceManager::CreateOrGetService(ServiceName name) {
auto serviceIter{serviceMap.find(name)};
if (serviceIter != serviceMap.end())
return (*serviceIter).second;
@ -74,9 +77,10 @@ namespace skyline::service {
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:a")
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:s")
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:t")
SERVICE_CASE(hosbinder::IHOSBinderDriver, "dispdrv")
SERVICE_CASE(visrv::IApplicationRootService, "vi:u")
SERVICE_CASE(visrv::ISystemRootService, "vi:s")
SERVICE_CASE(visrv::IManagerRootService, "vi:m")
SERVICE_CASE(visrv::IManagerRootService, "vi:u")
SERVICE_CASE(visrv::IManagerRootService, "vi:s")
SERVICE_CASE(pl::IPlatformServiceManager, "pl:u")
SERVICE_CASE(aocsrv::IAddOnContentManager, "aoc:u")
SERVICE_CASE(pctl::IParentalControlServiceFactory, "pctl")
@ -100,7 +104,7 @@ namespace skyline::service {
std::shared_ptr<BaseService> ServiceManager::NewService(ServiceName name, type::KSession &session, ipc::IpcResponse &response) {
std::lock_guard serviceGuard(mutex);
auto serviceObject{CreateService(name)};
auto serviceObject{CreateOrGetService(name)};
KHandle handle{};
if (session.isDomain) {
session.domains.push_back(serviceObject);

View File

@ -21,13 +21,6 @@ namespace skyline::service {
std::unordered_map<ServiceName, std::shared_ptr<BaseService>> serviceMap; //!< A mapping from a Service to the underlying object
std::mutex mutex; //!< Synchronizes concurrent access to services to prevent crashes
/**
* @brief Creates an instance of the service if it doesn't already exist, otherwise returns an existing instance
* @param name The name of the service to create
* @return A shared pointer to an instance of the service
*/
std::shared_ptr<BaseService> CreateService(ServiceName name);
public:
std::shared_ptr<BaseService> smUserInterface; //!< Used by applications to open connections to services
std::shared_ptr<GlobalServiceState> globalServiceState;
@ -58,19 +51,13 @@ namespace skyline::service {
}
/**
* @param serviceType The type of the service
* @tparam The class of the service
* @return A shared pointer to an instance of the service
* @note This only works for services created with `NewService` as sub-interfaces used with `RegisterService` can have multiple instances
* @brief Creates an instance of the service if it doesn't already exist, otherwise returns an existing instance
*/
template<typename Type>
std::shared_ptr<Type> GetService(ServiceName name) {
return std::static_pointer_cast<Type>(serviceMap.at(name));
}
std::shared_ptr<BaseService> CreateOrGetService(ServiceName name);
template<typename Type>
constexpr std::shared_ptr<Type> GetService(std::string_view name) {
return GetService<Type>(util::MakeMagic<ServiceName>(name));
constexpr std::shared_ptr<Type> CreateOrGetService(std::string_view name) {
return std::static_pointer_cast<Type>(CreateOrGetService(util::MakeMagic<ServiceName>(name)));
}
/**

View File

@ -3,101 +3,85 @@
#include <gpu.h>
#include <kernel/types/KProcess.h>
#include <services/serviceman.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#include <services/hosbinder/GraphicBufferProducer.h>
#include "IApplicationDisplayService.h"
#include "ISystemDisplayService.h"
#include "IManagerDisplayService.h"
#include "results.h"
namespace skyline::service::visrv {
IApplicationDisplayService::IApplicationDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager) {}
IApplicationDisplayService::IApplicationDisplayService(const DeviceState &state, ServiceManager &manager, PrivilegeLevel level) : level(level), IDisplayService(state, manager) {}
Result IApplicationDisplayService::GetRelayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response);
manager.RegisterService(hosbinder, session, response);
return {};
}
Result IApplicationDisplayService::GetIndirectDisplayTransactionService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response);
if (level < PrivilegeLevel::System)
return result::IllegalOperation;
manager.RegisterService(hosbinder, session, response);
return {};
}
Result IApplicationDisplayService::GetSystemDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
if (level < PrivilegeLevel::System)
return result::IllegalOperation;
manager.RegisterService(SRVREG(ISystemDisplayService), session, response);
return {};
}
Result IApplicationDisplayService::GetManagerDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
if (level < PrivilegeLevel::Manager)
return result::IllegalOperation;
manager.RegisterService(SRVREG(IManagerDisplayService), session, response);
return {};
}
Result IApplicationDisplayService::OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::string displayName(request.PopString());
state.logger->Debug("Setting display as: {}", displayName);
auto producer{hosbinder::producer.lock()};
producer->SetDisplay(displayName);
response.Push<u64>(0); // There's only one display
auto displayName(request.PopString());
state.logger->Debug("Opening display: {}", displayName);
response.Push(hosbinder->OpenDisplay(displayName));
return {};
}
Result IApplicationDisplayService::CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
state.logger->Debug("Closing the display");
auto producer{hosbinder::producer.lock()};
producer->CloseDisplay();
auto displayId{request.Pop<hosbinder::DisplayId>()};
state.logger->Debug("Closing display: {}", hosbinder::ToString(displayId));
hosbinder->CloseDisplay(displayId);
return {};
}
Result IApplicationDisplayService::OpenLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
struct InputStruct {
char displayName[0x40];
u64 layerId;
u64 userId;
} input = request.Pop<InputStruct>();
state.logger->Debug("Opening Layer: Display Name: {}, Layer ID: {}, User ID: {}", input.displayName, input.layerId, input.userId);
std::string name(input.displayName);
Parcel parcel(state);
LayerParcel data{
.type = 0x2,
.pid = 0,
.bufferId = 0, // As we only have one layer and buffer
.string = "dispdrv"
};
parcel.Push(data);
parcel.objects.resize(4);
auto displayName{request.PopString(0x40)};
auto layerId{request.Pop<u64>()};
state.logger->Debug("Opening layer #{} on display: {}", layerId, displayName);
auto displayId{hosbinder->OpenDisplay(displayName)};
auto parcel{hosbinder->OpenLayer(displayId, layerId)};
response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0)));
return {};
}
Result IApplicationDisplayService::CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u64 layerId{request.Pop<u64>()};
state.logger->Debug("Closing Layer: {}", layerId);
auto producer{hosbinder::producer.lock()};
if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized)
state.logger->Warn("The application is destroying an uninitialized layer");
producer->layerStatus = hosbinder::LayerStatus::Uninitialized;
state.logger->Debug("Closing layer #{}", layerId);
hosbinder->CloseLayer(layerId);
return {};
}
Result IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto scalingMode{request.Pop<u64>()};
auto layerId{request.Pop<u64>()};
state.logger->Debug("Setting Layer Scaling mode to '{}' for layer {}", scalingMode, layerId);
return {};
}
Result IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
KHandle handle{state.process->InsertItem(state.gpu->presentation.vsyncEvent)};
state.logger->Debug("VSync Event Handle: 0x{:X}", handle);
state.logger->Debug("V-Sync Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle);
return {};
}

View File

@ -4,6 +4,7 @@
#pragma once
#include "IDisplayService.h"
#include "IRootService.h"
namespace skyline::service::visrv {
/**
@ -11,8 +12,11 @@ namespace skyline::service::visrv {
* @url https://switchbrew.org/wiki/Display_services#IApplicationDisplayService
*/
class IApplicationDisplayService : public IDisplayService {
private:
PrivilegeLevel level;
public:
IApplicationDisplayService(const DeviceState &state, ServiceManager &manager);
IApplicationDisplayService(const DeviceState &state, ServiceManager &manager, PrivilegeLevel level);
/**
* @brief Returns an handle to the 'nvnflinger' service
@ -74,19 +78,19 @@ namespace skyline::service::visrv {
*/
Result GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL(
SFUNC(0x64, IApplicationDisplayService, GetRelayService),
SFUNC(0x65, IApplicationDisplayService, GetSystemDisplayService),
SFUNC(0x66, IApplicationDisplayService, GetManagerDisplayService),
SFUNC(0x67, IApplicationDisplayService, GetIndirectDisplayTransactionService),
SFUNC(0x3F2, IApplicationDisplayService, OpenDisplay),
SFUNC(0x3FC, IApplicationDisplayService, CloseDisplay),
SFUNC(0x7E4, IApplicationDisplayService, OpenLayer),
SFUNC(0x7E5, IApplicationDisplayService, CloseLayer),
SFUNC_BASE(0x7EE, IApplicationDisplayService, IDisplayService, CreateStrayLayer),
SFUNC_BASE(0x7EF, IApplicationDisplayService, IDisplayService, DestroyStrayLayer),
SFUNC(0x835, IApplicationDisplayService, SetLayerScalingMode),
SFUNC(0x1452, IApplicationDisplayService, GetDisplayVsyncEvent)
)
SERVICE_DECL(
SFUNC(0x64, IApplicationDisplayService, GetRelayService),
SFUNC(0x65, IApplicationDisplayService, GetSystemDisplayService),
SFUNC(0x66, IApplicationDisplayService, GetManagerDisplayService),
SFUNC(0x67, IApplicationDisplayService, GetIndirectDisplayTransactionService),
SFUNC(0x3F2, IApplicationDisplayService, OpenDisplay),
SFUNC(0x3FC, IApplicationDisplayService, CloseDisplay),
SFUNC(0x7E4, IApplicationDisplayService, OpenLayer),
SFUNC(0x7E5, IApplicationDisplayService, CloseLayer),
SFUNC_BASE(0x7EE, IApplicationDisplayService, IDisplayService, CreateStrayLayer),
SFUNC_BASE(0x7EF, IApplicationDisplayService, IDisplayService, DestroyStrayLayer),
SFUNC(0x835, IApplicationDisplayService, SetLayerScalingMode),
SFUNC(0x1452, IApplicationDisplayService, GetDisplayVsyncEvent)
)
};
}

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "IRootService.h"
namespace skyline::service::visrv {
/**
* @url https://switchbrew.org/wiki/Display_services#vi:u
*/
class IApplicationRootService : public IRootService {
public:
IApplicationRootService(const DeviceState &state, ServiceManager &manager) : IRootService(state, manager, PrivilegeLevel::Application) {}
SERVICE_DECL(
SFUNC_BASE(0x0, IApplicationRootService, IRootService, GetDisplayService)
)
};
}

View File

@ -1,46 +1,33 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <services/hosbinder/GraphicBufferProducer.h>
#include <services/serviceman.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#include "IDisplayService.h"
namespace skyline::service::visrv {
IDisplayService::IDisplayService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
IDisplayService::IDisplayService(const DeviceState &state, ServiceManager &manager) : hosbinder(manager.CreateOrGetService<hosbinder::IHOSBinderDriver>("dispdrv")), BaseService(state, manager) {}
Result IDisplayService::CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u64>();
auto displayId{request.Pop<u64>()};
request.Skip<u64>(); // VI Layer flags
auto displayId{request.Pop<hosbinder::DisplayId>()};
state.logger->Debug("Creating Stray Layer on Display: {}", displayId);
auto layerId{hosbinder->CreateLayer(displayId)};
response.Push(layerId);
auto producer{hosbinder::producer.lock()};
if (producer->layerStatus == hosbinder::LayerStatus::Stray)
throw exception("The application is creating more than one stray layer");
producer->layerStatus = hosbinder::LayerStatus::Stray;
response.Push<u64>(0); // There's only one layer
Parcel parcel(state);
LayerParcel data{
.type = 0x2,
.pid = 0,
.bufferId = 0, // As we only have one layer and buffer
.string = "dispdrv"
};
parcel.Push(data);
state.logger->Debug("Creating Stray Layer #{} on Display: {}", layerId, hosbinder::ToString(displayId));
auto parcel{hosbinder->OpenLayer(displayId, layerId)};
response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0)));
return {};
}
Result IDisplayService::DestroyStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId{request.Pop<u64>()};
state.logger->Debug("Destroying Stray Layer: {}", layerId);
state.logger->Debug("Destroying Stray Layer #{}", layerId);
auto producer{hosbinder::producer.lock()};
if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized)
state.logger->Warn("The application is destroying an uninitialized layer");
producer->layerStatus = hosbinder::LayerStatus::Uninitialized;
hosbinder->CloseLayer(layerId);
return {};
}

View File

@ -3,7 +3,11 @@
#pragma once
#include <services/serviceman.h>
#include <services/base_service.h>
namespace skyline::service::hosbinder {
class IHOSBinderDriver;
}
namespace skyline::service::visrv {
/**
@ -11,18 +15,7 @@ namespace skyline::service::visrv {
*/
class IDisplayService : public BaseService {
protected:
/**
* @brief This is the parcel used in OpenLayer/CreateStrayLayer
*/
struct LayerParcel {
u32 type; //!< The type of the layer
u32 pid; //!< The PID that the layer belongs to
u32 bufferId; //!< The buffer ID of the layer
u32 _pad0_[3];
u8 string[0x8]; //!< "dispdrv"
u64 _pad1_;
};
static_assert(sizeof(LayerParcel) == 0x28);
std::shared_ptr<hosbinder::IHOSBinderDriver> hosbinder; //!< The IHOSBinder relayed via this display class
public:
IDisplayService(const DeviceState &state, ServiceManager &manager);

View File

@ -1,35 +1,27 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <services/hosbinder/GraphicBufferProducer.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#include "IManagerDisplayService.h"
namespace skyline::service::visrv {
IManagerDisplayService::IManagerDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager) {}
Result IManagerDisplayService::CreateManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u32>();
auto displayId{request.Pop<u64>()};
state.logger->Debug("Creating Managed Layer on Display: {}", displayId);
request.Skip<u64>(); // VI Layer flags
auto displayId{request.Pop<hosbinder::DisplayId>()};
auto producer{hosbinder::producer.lock()};
if (producer->layerStatus != hosbinder::LayerStatus::Uninitialized)
throw exception("The application is creating more than one layer");
producer->layerStatus = hosbinder::LayerStatus::Managed;
auto layerId{hosbinder->CreateLayer(displayId)};
state.logger->Debug("Creating Managed Layer #{} on Display: {}", layerId, hosbinder::ToString(displayId));
response.Push(layerId);
response.Push<u64>(0); // There's only one layer
return {};
}
Result IManagerDisplayService::DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId{request.Pop<u64>()};
state.logger->Debug("Destroying Managed Layer: {}", layerId);
auto producer{hosbinder::producer.lock()};
if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized)
state.logger->Warn("The application is destroying an uninitialized layer");
producer->layerStatus = hosbinder::LayerStatus::Uninitialized;
state.logger->Debug("Destroying Managed Layer #{}", layerId);
hosbinder->DestroyLayer(layerId);
return {};
}

View File

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "IManagerRootService.h"
#include "IApplicationDisplayService.h"
namespace skyline::service::visrv {
IManagerRootService::IManagerRootService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
Result IManagerRootService::GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(SRVREG(IApplicationDisplayService), session, response);
return {};
}
}

View File

@ -3,26 +3,18 @@
#pragma once
#include <gpu.h>
#include <services/serviceman.h>
#include "IRootService.h"
namespace skyline::service::visrv {
/**
* @brief This service is used to get an handle to #IApplicationDisplayService
* @url https://switchbrew.org/wiki/Display_services#vi:m
*/
class IManagerRootService : public BaseService {
class IManagerRootService : public IRootService {
public:
IManagerRootService(const DeviceState &state, ServiceManager &manager);
IManagerRootService(const DeviceState &state, ServiceManager &manager) : IRootService(state, manager, PrivilegeLevel::Manager) {}
/**
* @brief Returns an handle to #IApplicationDisplayService
* @url https://switchbrew.org/wiki/Display_services#GetDisplayService
*/
Result GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL(
SFUNC(0x2, IManagerRootService, GetDisplayService)
)
SERVICE_DECL(
SFUNC_BASE(0x2, IManagerRootService, IRootService, GetDisplayService)
)
};
}

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <services/serviceman.h>
#include "IRootService.h"
#include "IApplicationDisplayService.h"
#include "results.h"
namespace skyline::service::visrv {
IRootService::IRootService(const DeviceState &state, ServiceManager &manager, PrivilegeLevel level) : level(level), BaseService(state, manager) {}
Result IRootService::GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto requestedPrivileges{request.Pop<u32>()}; //!< A boolean indicating if the returned service should have higher privileges or not
if (requestedPrivileges && level < PrivilegeLevel::System)
return result::IllegalOperation;
manager.RegisterService(SRVREG(IApplicationDisplayService, level), session, response);
return {};
}
}

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/base_service.h>
namespace skyline::service::visrv {
/**
* @brief All privilege-based variants that a single service can have
*/
enum class PrivilegeLevel {
Application, //!< The service used by user applications (Lowest)
System, //!< The service used by system applications (Higher)
Manager, //!< The service used by system services internally (Highest)
};
/**
* @brief Manages allocation of VI to display services
* @url https://switchbrew.org/wiki/Display_services#vi:u
* @url https://switchbrew.org/wiki/Display_services#vi:s
* @url https://switchbrew.org/wiki/Display_services#vi:m
*/
class IRootService : public BaseService {
private:
PrivilegeLevel level;
public:
IRootService(const DeviceState &state, ServiceManager &manager, PrivilegeLevel level);
/**
* @brief Returns an handle to #IApplicationDisplayService
* @url https://switchbrew.org/wiki/Display_services#GetDisplayService
*/
Result GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
}

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "IRootService.h"
namespace skyline::service::visrv {
/**
* @url https://switchbrew.org/wiki/Display_services#vi:s
*/
class ISystemRootService : public IRootService {
public:
ISystemRootService(const DeviceState &state, ServiceManager &manager) : IRootService(state, manager, PrivilegeLevel::System) {}
SERVICE_DECL(
SFUNC_BASE(0x3, ISystemRootService, IRootService, GetDisplayService)
)
};
}

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
namespace skyline::service::visrv::result {
constexpr Result IllegalOperation(114, 6);
}