Framebuffer and NativeActivity

What was added:
* Framebuffer
* NativeActivity
* NV Services
* IOCTL Handler
* NV Devices:
* * /dev/nvmap - 0xC0080101, 0xC0080103, 0xC0200104, 0xC0180105, 0xC00C0109, 0xC008010E
* * /dev/nvhost-as-gpu
* * /dev/nvhost-channel - 0x40044801, 0xC0104809, 0xC010480B, 0xC018480C, 0x4004480D, 0xC020481A, 0x40084714
* * /dev/nvhost-ctrl
* * /dev/nvhost-ctrl-gpu - 0x80044701, 0x80284702, 0xC0184706, 0xC0B04705, 0x80084714
* SVCs:
* * SetMemoryAttribute
* * CreateTransferMemory
* * ResetSignal
* * GetSystemTick
* Addition of Compact Logger
What was fixed:
* SVCs:
* * SetHeapSize
* * SetMemoryAttribute
* * QueryMemory
* A release build would not set CMAKE_BUILD_TYPE to "RELEASE"
* The logger code was simplified
This commit is contained in:
◱ PixelyIon
2019-11-14 01:39:31 +05:30
committed by ◱ PixelyIon
parent 2e0014ba7c
commit c005d7df74
96 changed files with 3783 additions and 951 deletions

View File

@ -0,0 +1,227 @@
#pragma once
#include <common.h>
#include <kernel/ipc.h>
#define NFUNC(function) std::bind(&function, this, std::placeholders::_1)
namespace skyline::gpu::device {
/**
* @brief An enumeration of all the devices that can be opened by nvdrv
*/
enum class NvDeviceType {
nvhost_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl
nvhost_gpu, //!< https://switchbrew.org/wiki/NV_services#Channels
nvhost_nvdec, //!< https://switchbrew.org/wiki/NV_services#Channels
nvhost_vic, //!< https://switchbrew.org/wiki/NV_services#Channels
nvmap, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvmap
nvdisp_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdisp-ctrl
nvdisp_disp0, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdisp-disp0.2C_.2Fdev.2Fnvdisp-disp1
nvdisp_disp1, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdisp-disp0.2C_.2Fdev.2Fnvdisp-disp1
nvcec_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvcec-ctrl
nvhdcp_up_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhdcp_up-ctrl
nvdcutil_disp0, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdcutil-disp0.2C_.2Fdev.2Fnvdcutil-disp1
nvdcutil_disp1, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdcutil-disp0.2C_.2Fdev.2Fnvdcutil-disp1
nvsched_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvsched-ctrl
nverpt_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnverpt-ctrl
nvhost_as_gpu, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu
nvhost_dbg_gpu, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-dbg-gpu
nvhost_prof_gpu, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-prof-gpu
nvhost_ctrl_gpu //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl-gpu
};
/**
* @brief A mapping from a device's path to it's nvDevice entry
*/
const static std::unordered_map<std::string, NvDeviceType> nvDeviceMap{
{"/dev/nvhost-ctrl", NvDeviceType::nvhost_ctrl},
{"/dev/nvhost-gpu", NvDeviceType::nvhost_gpu},
{"/dev/nvhost-nvdec", NvDeviceType::nvhost_nvdec},
{"/dev/nvhost-vic", NvDeviceType::nvhost_vic},
{"/dev/nvmap", NvDeviceType::nvmap},
{"/dev/nvdisp-ctrl", NvDeviceType::nvdisp_ctrl},
{"/dev/nvdisp-disp0", NvDeviceType::nvdisp_disp0},
{"/dev/nvdisp-disp1", NvDeviceType::nvdisp_disp1},
{"/dev/nvcec-ctrl", NvDeviceType::nvcec_ctrl},
{"/dev/nvhdcp_up-ctrl", NvDeviceType::nvhdcp_up_ctrl},
{"/dev/nvdcutil-disp0", NvDeviceType::nvdcutil_disp0},
{"/dev/nvdcutil-disp1", NvDeviceType::nvdcutil_disp1},
{"/dev/nvsched-ctrl", NvDeviceType::nvsched_ctrl},
{"/dev/nverpt-ctrl", NvDeviceType::nverpt_ctrl},
{"/dev/nvhost-as-gpu", NvDeviceType::nvhost_as_gpu},
{"/dev/nvhost-dbg-gpu", NvDeviceType::nvhost_dbg_gpu},
{"/dev/nvhost-prof-gpu", NvDeviceType::nvhost_prof_gpu},
{"/dev/nvhost-ctrl-gpu", NvDeviceType::nvhost_ctrl_gpu}
};
/**
* @brief Describes a buffer by holding the address and size
*/
struct IoctlBuffer {
u64 address; //!< The address of the buffer
size_t size; //!< The size of the buffer
/**
* @param address The address of the buffer
* @param size The size of the buffer
*/
IoctlBuffer(u64 address, size_t size) : address(address), size(size) {}
};
/**
* @brief Wrapper around IoctlBuffer that loads in the address from a A Buffer Descriptor
*/
struct InputBuffer : public IoctlBuffer {
/**
* @param aBuf The A Buffer Descriptor that has contains the input data
*/
InputBuffer(kernel::ipc::BufferDescriptorABW *aBuf) : IoctlBuffer(aBuf->Address(), aBuf->Size()) {}
/**
* @param aBuf The X Buffer Descriptor that has contains the input data
*/
InputBuffer(kernel::ipc::BufferDescriptorX *xBuf) : IoctlBuffer(xBuf->Address(), xBuf->size) {}
};
/**
* @brief Wrapper around IoctlBuffer that loads in the address from a B Buffer Descriptor
*/
struct OutputBuffer : public IoctlBuffer {
/**
* @param aBuf The B Buffer Descriptor that has to be outputted to
*/
OutputBuffer(kernel::ipc::BufferDescriptorABW *bBuf) : IoctlBuffer(bBuf->Address(), bBuf->Size()) {}
/**
* @param xBuf The C Buffer Descriptor that has to be outputted to
*/
OutputBuffer(kernel::ipc::BufferDescriptorC *cBuf) : IoctlBuffer(cBuf->address, cBuf->size) {}
};
/**
* @brief This enumerates all the possible error codes returned by the Nvidia driver (https://switchbrew.org/wiki/NV_services#Errors)
*/
enum NvStatus : u32 {
Success = 0x0, //!< The operation has succeeded
NotImplemented = 0x1, //!< The operation is not implemented
NotSupported = 0x2, //!< The operation is not supported
NotInitialized = 0x3, //!< The operation uses an uninitialized object
BadParameter = 0x4, //!< The operation was provided a bad parameter
Timeout = 0x5, //!< The operation has timed out
InsufficientMemory = 0x6, //!< The device ran out of memory during the operation
ReadOnlyAttribute = 0x7, //!< The mutating operation was performed on a read only section
InvalidState = 0x8, //!< The state of the device was invalid
InvalidAddress = 0x9, //!< The provided address is invalid
InvalidSize = 0xA, //!< The provided size is invalid
BadValue = 0xB, //!< The operation was provided a bad value
AlreadyAllocated = 0xD, //!< An object was tried to be reallocated
Busy = 0xE, //!< The device is busy
ResourceError = 0xF, //!< There was an error accessing the resource
CountMismatch = 0x10, //!< ?
SharedMemoryTooSmall = 0x1000, //!< The shared memory segment is too small
FileOperationFailed = 0x30003, //!< The file operation has failed
DirOperationFailed = 0x30004, //!< The directory operation has failed
IoctlFailed = 0x3000F, //!< The IOCTL operation has failed
AccessDenied = 0x30010, //!< The access to a resource was denied
FileNotFound = 0x30013, //!< A file was not found
ModuleNotPresent = 0xA000E, //!< A module was not present
};
/**
* @brief This holds all the IoctlBuffer objects in a coherent container
*/
struct IoctlBuffers {
std::vector<InputBuffer> input; //!< A vector of all input IOCTL buffers
std::vector<OutputBuffer> output; //!< A vector of all output IOCTL buffers
NvStatus status{NvStatus::Success}; //!< The error code that is returned to the application
/**
* @brief This constructor takes 1 input buffer and 1 output buffer, it's used for Ioctl
* @param input An input buffer
* @param output An output buffer
*/
IoctlBuffers(InputBuffer input, OutputBuffer output) : input({input}), output({output}) {}
/**
* @brief This constructor takes 1 input buffer, it's used for Ioctl sometimes
* @param output An output buffer
*/
IoctlBuffers(InputBuffer input) : input({input}) {}
/**
* @brief This constructor takes 1 output buffer, it's used for Ioctl sometimes
* @param output An output buffer
*/
IoctlBuffers(OutputBuffer output) : output({output}) {}
/**
* @brief This constructor takes 2 input buffers and 1 output buffer, it's used for Ioctl1
* @param input1 The first input buffer
* @param input2 The second input buffer
* @param output An output buffer
*/
IoctlBuffers(InputBuffer input1, InputBuffer input2, OutputBuffer output) : input({input1, input2}), output({output}) {}
/**
* @brief This constructor takes 1 input buffer and 2 output buffers, it's used for Ioctl2
* @param input An input buffer
* @param output1 The first output buffer
* @param output2 The second output buffer
*/
IoctlBuffers(InputBuffer input, OutputBuffer output1, OutputBuffer output2) : input({input}), output({output1, output2}) {}
};
/**
* @brief NvDevice is the base class all /dev/nv* devices inherit from
*/
class NvDevice {
protected:
const DeviceState &state; //!< The state of the device
std::unordered_map<u32, std::function<void(IoctlBuffers &)>> vTable; //!< This holds the mapping from an Ioctl to the actual function
public:
u16 refCount{1}; //!< The amount of handles to the device
NvDeviceType deviceType; //!< The type of the device
/**
* @param state The state of the device
* @param deviceType The type of the device
* @param vTable The functions in this device
*/
NvDevice(const DeviceState &state, NvDeviceType deviceType, std::unordered_map<u32, std::function<void(IoctlBuffers &)>> vTable) : state(state), deviceType(deviceType), vTable(vTable) {}
/**
* @brief This returns the name of the current service
* @note It may not return the exact name the service was initialized with if there are multiple entries in ServiceString
* @return The name of the service
*/
std::string getName() {
std::string serviceName;
for (const auto&[name, type] : nvDeviceMap)
if (type == deviceType)
serviceName = name;
return serviceName;
}
/**
* @brief This handles IOCTL calls for devices
* @param cmd The IOCTL command that was called
* @param input The input to the IOCTL call
*/
void HandleIoctl(u32 cmd, IoctlBuffers &input) {
std::function<void(IoctlBuffers &)> function;
try {
function = vTable.at(cmd);
} catch (std::out_of_range &) {
state.logger->Warn("Cannot find IOCTL for device '{}': 0x{:X}", getName(), deviceType, cmd);
input.status = NvStatus::NotImplemented;
return;
}
try {
function(input);
} catch (std::exception &e) {
throw exception("{} (Device: {})", e.what(), getName());
}
}
};
}

View File

@ -0,0 +1,5 @@
#include "nvhost_as_gpu.h"
namespace skyline::gpu::device {
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_as_gpu, {}) {}
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "nvdevice.h"
namespace skyline::gpu::device {
/**
* @brief NvHostAsGpu (/dev/nvhost-as-gpu) is used to access GPU virtual address spaces (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu)
*/
class NvHostAsGpu : public NvDevice {
public:
NvHostAsGpu(const DeviceState &state);
};
}

View File

@ -0,0 +1,42 @@
#include "nvhost_channel.h"
#include <kernel/types/KProcess.h>
namespace skyline::gpu::device {
NvHostChannel::NvHostChannel(const DeviceState &state, NvDeviceType type) : NvDevice(state, type, {
{0x40044801, NFUNC(NvHostChannel::SetNvmapFd)},
{0xC0104809, NFUNC(NvHostChannel::AllocObjCtx)},
{0xC010480B, NFUNC(NvHostChannel::ZcullBind)},
{0xC018480C, NFUNC(NvHostChannel::SetErrorNotifier)},
{0x4004480D, NFUNC(NvHostChannel::SetPriority)},
{0xC020481A, NFUNC(NvHostChannel::AllocGpfifoEx2)},
{0x40084714, NFUNC(NvHostChannel::SetUserData)}
}) {}
void NvHostChannel::SetNvmapFd(skyline::gpu::device::IoctlBuffers &buffer) {}
void NvHostChannel::AllocObjCtx(skyline::gpu::device::IoctlBuffers &buffer) {}
void NvHostChannel::ZcullBind(IoctlBuffers &buffer) {}
void NvHostChannel::SetErrorNotifier(skyline::gpu::device::IoctlBuffers &buffer) {}
void NvHostChannel::SetPriority(skyline::gpu::device::IoctlBuffers &buffer) {
auto priority = state.thisProcess->ReadMemory<NvChannelPriority>(buffer.input[0].address);
switch (priority) {
case NvChannelPriority::Low:
timeslice = 1300;
break;
case NvChannelPriority::Medium:
timeslice = 2600;
break;
case NvChannelPriority::High:
timeslice = 5200;
break;
}
}
void NvHostChannel::AllocGpfifoEx2(skyline::gpu::device::IoctlBuffers &buffer) {}
void NvHostChannel::SetUserData(skyline::gpu::device::IoctlBuffers &buffer) {}
}

View File

@ -0,0 +1,57 @@
#pragma once
#include "nvdevice.h"
namespace skyline::gpu::device {
/**
* @brief NvHostChannel is used as a common interface for all Channel devices (https://switchbrew.org/wiki/NV_services#Channels)
*/
class NvHostChannel : public NvDevice {
private:
enum class NvChannelPriority : u32 {
Low = 0x32,
Medium = 0x64,
High = 0x94
};
u32 timeslice{};
public:
NvHostChannel(const DeviceState &state, NvDeviceType type);
/**
* @brief This sets the nvmap file descriptor (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_NVMAP_FD)
*/
void SetNvmapFd(IoctlBuffers &buffer);
/**
* @brief This allocates a graphic context object (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_OBJ_CTX)
*/
void AllocObjCtx(IoctlBuffers &buffer);
/**
* @brief This initializes the error notifier for this channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ZCULL_BIND)
*/
void ZcullBind(IoctlBuffers &buffer);
/**
* @brief This initializes the error notifier for this channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_ERROR_NOTIFIER)
*/
void SetErrorNotifier(IoctlBuffers &buffer);
/**
* @brief This sets the priority of the channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_PRIORITY)
*/
void SetPriority(IoctlBuffers &buffer);
/**
* @brief This allocates a GPFIFO entry (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_GPFIFO_EX2)
*/
void AllocGpfifoEx2(IoctlBuffers &buffer);
/**
* @brief This sets the user specific data (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_USER_DATA)
*/
void SetUserData(IoctlBuffers &buffer);
};
}

View File

@ -0,0 +1,5 @@
#include "nvhost_ctrl.h"
namespace skyline::gpu::device {
NvHostCtrl::NvHostCtrl(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_ctrl, {}) {}
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "nvdevice.h"
namespace skyline::gpu::device {
/**
* @brief NvHostCtrl (/dev/nvhost-ctrl) is used for GPU synchronization (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl)
*/
class NvHostCtrl : public NvDevice {
public:
NvHostCtrl(const DeviceState &state);
};
}

View File

@ -0,0 +1,138 @@
#include "nvhost_ctrl_gpu.h"
#include <kernel/types/KProcess.h>
namespace skyline::gpu::device {
NvHostCtrlGpu::NvHostCtrlGpu(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_ctrl_gpu, {
{0x80044701, NFUNC(NvHostCtrlGpu::ZCullGetCtxSize)},
{0x80284702, NFUNC(NvHostCtrlGpu::ZCullGetInfo)},
{0xC0184706, NFUNC(NvHostCtrlGpu::GetTpcMasks)},
{0xC0B04705, NFUNC(NvHostCtrlGpu::GetCharacteristics)},
{0x80084714, NFUNC(NvHostCtrlGpu::GetActiveSlotMask)}
}) {}
void NvHostCtrlGpu::ZCullGetCtxSize(IoctlBuffers &buffer) {
u32 size = 0x1;
state.thisProcess->WriteMemory(size, buffer.output[0].address);
}
void NvHostCtrlGpu::ZCullGetInfo(skyline::gpu::device::IoctlBuffers &buffer) {
struct {
u32 widthAlignPixels{0x20};
u32 heightAlignPixels{0x20};
u32 pixelSquaresByAliquots{0x400};
u32 aliquotTotal{0x800};
u32 regionByteMultiplier{0x20};
u32 regionHeaderSize{0x20};
u32 subregionHeaderSize{0xC0};
u32 subregionWidthAlignPixels{0x20};
u32 subregionHeightAlignPixels{0x40};
u32 subregionCount{0x10};
} zCullInfo;
state.thisProcess->WriteMemory(zCullInfo, buffer.output[0].address);
}
void NvHostCtrlGpu::GetCharacteristics(IoctlBuffers &buffer) {
struct GpuCharacteristics {
u32 arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
u32 impl; // 0xB (NVGPU_GPU_IMPL_GM20B) or 0xE (NVGPU_GPU_IMPL_GM20B_B)
u32 rev; // 0xA1 (Revision A1)
u32 numGpc; // 0x1
u64 l2CacheSize; // 0x40000
u64 onBoardVideoMemorySize; // 0x0 (not used)
u32 numTpcPerGpc; // 0x2
u32 busType; // 0x20 (NVGPU_GPU_BUS_TYPE_AXI)
u32 bigPageSize; // 0x20000
u32 compressionPageSize; // 0x20000
u32 pdeCoverageBitCount; // 0x1B
u32 availableBigPageSizes; // 0x30000
u32 gpcMask; // 0x1
u32 smArchSmVersion; // 0x503 (Maxwell Generation 5.0.3)
u32 smArchSpaVersion; // 0x503 (Maxwell Generation 5.0.3)
u32 smArchWarpCount; // 0x80
u32 gpuVaBitCount; // 0x28
u32 reserved; // NULL
u64 flags; // 0x55 (HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT)
u32 twodClass; // 0x902D (FERMI_TWOD_A)
u32 threedClass; // 0xB197 (MAXWELL_B)
u32 computeClass; // 0xB1C0 (MAXWELL_COMPUTE_B)
u32 gpfifoClass; // 0xB06F (MAXWELL_CHANNEL_GPFIFO_A)
u32 inlineToMemoryClass; // 0xA140 (KEPLER_INLINE_TO_MEMORY_B)
u32 dmaCopyClass; // 0xB0B5 (MAXWELL_DMA_COPY_A)
u32 maxFbpsCount; // 0x1
u32 fbpEnMask; // 0x0 (disabled)
u32 maxLtcPerFbp; // 0x2
u32 maxLtsPerLtc; // 0x1
u32 maxTexPerTpc; // 0x0 (not supported)
u32 maxGpcCount; // 0x1
u32 ropL2EnMask0; // 0x21D70 (fuse_status_opt_rop_l2_fbp_r)
u32 ropL2EnMask1; // 0x0
u64 chipName; // 0x6230326D67 ("gm20b")
u64 grCompbitStoreBaseHw; // 0x0 (not supported)
};
struct Data {
u64 gpuCharacteristicsBufSize; // InOut
u64 gpuCharacteristicsBufAddr; // In
GpuCharacteristics gpuCharacteristics; // Out
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
data.gpuCharacteristics = {
.arch = 0x120,
.impl = 0xB,
.rev = 0xA1,
.numGpc = 0x1,
.l2CacheSize = 0x40000,
.onBoardVideoMemorySize = 0x0,
.numTpcPerGpc = 0x2,
.busType = 0x20,
.bigPageSize = 0x20000,
.compressionPageSize = 0x20000,
.pdeCoverageBitCount = 0x1B,
.availableBigPageSizes = 0x30000,
.gpcMask = 0x1,
.smArchSmVersion = 0x503,
.smArchSpaVersion = 0x503,
.smArchWarpCount = 0x80,
.gpuVaBitCount = 0x2,
.flags = 0x55,
.twodClass = 0x902D,
.threedClass = 0xB197,
.computeClass = 0xB1C0,
.gpfifoClass = 0xB06F,
.inlineToMemoryClass = 0xA140,
.dmaCopyClass = 0xB0B5,
.maxFbpsCount = 0x1,
.fbpEnMask = 0x0,
.maxLtcPerFbp = 0x2,
.maxLtsPerLtc = 0x1,
.maxTexPerTpc = 0x0,
.maxGpcCount = 0x1,
.ropL2EnMask0 = 0x21D70,
.ropL2EnMask1 = 0x0,
.chipName = 0x6230326D67,
.grCompbitStoreBaseHw = 0x0
};
data.gpuCharacteristicsBufSize = 0xA0;
state.thisProcess->WriteMemory(data, buffer.output[0].address);
}
void NvHostCtrlGpu::GetTpcMasks(IoctlBuffers &buffer) {
struct Data {
u32 maskBufSize; // In
u32 reserved[3]; // In
u64 maskBuf; // Out
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
if (data.maskBufSize)
data.maskBuf = 0x3;
state.thisProcess->WriteMemory(data, buffer.output[0].address);
}
void NvHostCtrlGpu::GetActiveSlotMask(IoctlBuffers &buffer) {
struct Data {
u32 slot; // Out
u32 mask; // Out
} data = {
.slot = 0x07,
.mask = 0x01
};
state.thisProcess->WriteMemory(data, buffer.output[0].address);
}
}

View File

@ -0,0 +1,38 @@
#pragma once
#include "nvdevice.h"
namespace skyline::gpu::device {
/**
* @brief NvHostCtrlGpu (/dev/nvhost-ctrl-gpu) is used for context independent operations on the underlying GPU (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl-gpu)
*/
class NvHostCtrlGpu : public NvDevice {
public:
NvHostCtrlGpu(const DeviceState &state);
/**
* @brief This returns a u32 GPU ZCULL Context Size (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZCULL_GET_CTX_SIZE)
*/
void ZCullGetCtxSize(IoctlBuffers &buffer);
/**
* @brief This returns a the GPU ZCULL Information (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZCULL_GET_INFO)
*/
void ZCullGetInfo(IoctlBuffers &buffer);
/**
* @brief This returns a struct with certain GPU characteristics (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_CHARACTERISTICS)
*/
void GetCharacteristics(IoctlBuffers &buffer);
/**
* @brief This returns the TPC mask value for each GPC (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_TPC_MASKS)
*/
void GetTpcMasks(IoctlBuffers &buffer);
/**
* @brief This returns the mask value for a ZBC slot (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZBC_GET_ACTIVE_SLOT_MASK)
*/
void GetActiveSlotMask(IoctlBuffers &buffer);
};
}

View File

@ -0,0 +1,140 @@
#include "nvmap.h"
#include <kernel/types/KProcess.h>
namespace skyline::gpu::device {
NvMap::NvMapObject::NvMapObject(u32 id, u32 size) : id(id), size(size) {}
NvMap::NvMap(const DeviceState &state) : NvDevice(state, NvDeviceType::nvmap, {
{0xC0080101, NFUNC(NvMap::Create)},
{0xC0080103, NFUNC(NvMap::FromId)},
{0xC0200104, NFUNC(NvMap::Alloc)},
{0xC0180105, NFUNC(NvMap::Free)},
{0xC00C0109, NFUNC(NvMap::Param)},
{0xC008010E, NFUNC(NvMap::GetId)}
}) {}
void NvMap::Create(IoctlBuffers &buffer) {
struct Data {
u32 size; // In
u32 handle; // Out
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size);
data.handle = handleIndex++;
state.thisProcess->WriteMemory(data, buffer.output[0].address);
state.logger->Info("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status);
}
void NvMap::FromId(skyline::gpu::device::IoctlBuffers &buffer) {
struct Data {
u32 id; // In
u32 handle; // Out
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
bool found{};
for (const auto &object : handleTable) {
if (object.second->id == data.id) {
data.handle = object.first;
found = true;
break;
}
}
if (found)
state.thisProcess->WriteMemory(data, buffer.output[0].address);
else
buffer.status = NvStatus::BadValue;
state.logger->Info("FromId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
}
void NvMap::Alloc(IoctlBuffers &buffer) {
struct Data {
u32 handle; // In
u32 heapMask; // In
u32 flags; // In
u32 align; // In
u8 kind; // In
u8 _pad0_[7];
u64 address; // InOut
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
auto &object = handleTable.at(data.handle);
object->heapMask = data.heapMask;
object->flags = data.flags;
object->align = data.align;
object->kind = data.kind;
object->address = data.address;
object->status = NvMapObject::Status::Allocated;
state.logger->Info("Alloc: Input: Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}, Output: Status: {}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address, buffer.status);
}
void NvMap::Free(skyline::gpu::device::IoctlBuffers &buffer) {
struct Data {
u32 handle; // In
u32 _pad0_;
u32 address; // Out
u32 size; // Out
u64 flags; // Out
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
const auto &object = handleTable.at(data.handle);
if (object.use_count() > 1) {
data.address = static_cast<u32>(object->address);
data.flags = 0x0;
} else {
data.address = 0x0;
data.flags = 0x1; // Not free yet
}
data.size = object->size;
handleTable.erase(data.handle);
state.thisProcess->WriteMemory(data, buffer.output[0].address);
}
void NvMap::Param(IoctlBuffers &buffer) {
enum class Parameter : u32 { Size = 1, Alignment = 2, Base = 3, HeapMask = 4, Kind = 5, Compr = 6 }; // https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h#102
struct Data {
u32 handle; // In
Parameter parameter; // In
u32 result; // Out
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
auto &object = handleTable.at(data.handle);
switch (data.parameter) {
case Parameter::Size:
data.result = object->size;
break;
case Parameter::Alignment:
case Parameter::HeapMask:
case Parameter::Kind: {
if (object->status != NvMapObject::Status::Allocated)
data.result = NvStatus::BadParameter;
switch (data.parameter) {
case Parameter::Alignment:
data.result = object->align;
break;
case Parameter::HeapMask:
data.result = object->heapMask;
break;
case Parameter::Kind:
data.result = object->kind;
break;
default:
break;
}
break;
}
case Parameter::Base:
buffer.status = NvStatus::NotImplemented;
break;
case Parameter::Compr:
buffer.status = NvStatus::NotImplemented;
break;
}
state.thisProcess->WriteMemory(data, buffer.output[0].address);
state.logger->Info("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status);
}
void NvMap::GetId(skyline::gpu::device::IoctlBuffers &buffer) {
struct Data {
u32 id; // Out
u32 handle; // In
} data = state.thisProcess->ReadMemory<Data>(buffer.input[0].address);
data.id = handleTable.at(data.handle)->id;
state.thisProcess->WriteMemory(data, buffer.output[0].address);
state.logger->Info("GetId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
}
}

View File

@ -0,0 +1,71 @@
#pragma once
#include "nvdevice.h"
namespace skyline::gpu::device {
/**
* @brief NvMap (/dev/nvmap) is used to map certain CPU memory as GPU memory (https://switchbrew.org/wiki/NV_services) (https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h)
*/
class NvMap : public NvDevice {
public:
/**
* @brief NvMapObject is used to hold the state of held objects
*/
struct NvMapObject {
u32 id; //!< The ID of this object
u32 size; //!< The size of this object
u64 address{}; //!< The address of the allocation
u32 flags{}; //!< The flag of the memory (0 = Read Only, 1 = Read-Write)
u32 align{}; //!< The alignment of the allocation
u32 heapMask{}; //!< This is set during Alloc and returned during Param
u8 kind{}; //!< This is same as heapMask
enum class Status {
Created, //!< The object has been created but memory has not been allocated
Allocated //!< The object has been allocated
} status{Status::Created}; //!< This holds the status of the object
/**
* @param handle The ID of this object
* @param size The size of the object in bytes
*/
NvMapObject(u32 id, u32 size);
};
std::unordered_map<handle_t, std::shared_ptr<NvMapObject>> handleTable; //!< A mapping from a handle to it's corresponding NvMapObject
handle_t handleIndex{1}; //!< This is used to keep track of the next handle to allocate
u32 idIndex{1}; //!< This is used to keep track of the next ID to allocate
NvMap(const DeviceState &state);
/**
* @brief This creates an NvMapObject and returns an handle to it (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_CREATE)
*/
void Create(IoctlBuffers &buffer);
/**
* @brief This returns the handle of an NvMapObject from it's ID (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FROM_ID)
*/
void FromId(IoctlBuffers &buffer);
/**
* @brief This allocates memory for an NvMapObject (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_ALLOC)
*/
void Alloc(IoctlBuffers &buffer);
/**
* @brief This frees previously allocated memory (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FREE)
*/
void Free(IoctlBuffers &buffer);
/**
* @brief This returns a particular parameter from an NvMapObject (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_PARAM)
*/
void Param(IoctlBuffers &buffer);
/**
* @brief This returns the ID of an NvMapObject from it's handle (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_GET_ID)
*/
void GetId(IoctlBuffers &buffer);
};
}

View File

@ -0,0 +1,138 @@
#include "display.h"
#include <kernel/types/KProcess.h>
#include <gpu.h>
namespace skyline::gpu {
Buffer::Buffer(const DeviceState &state, u32 slot, GbpBuffer &gbpBuffer) : state(state), slot(slot), gbpBuffer(gbpBuffer), resolution{gbpBuffer.width, gbpBuffer.height}, dataBuffer(gbpBuffer.size) {
if (gbpBuffer.nvmapHandle)
nvBuffer = state.gpu->GetDevice<device::NvMap>(device::NvDeviceType::nvmap)->handleTable.at(gbpBuffer.nvmapHandle);
else {
auto nvmap = state.gpu->GetDevice<device::NvMap>(device::NvDeviceType::nvmap);
for (const auto &object : nvmap->handleTable) {
if (object.second->id == gbpBuffer.nvmapId) {
nvBuffer = object.second;
break;
}
}
if (!nvBuffer)
throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer.nvmapHandle, gbpBuffer.nvmapId);
}
}
void Buffer::UpdateBuffer() {
state.thisProcess->ReadMemory(dataBuffer.data(), nvBuffer->address + gbpBuffer.offset, gbpBuffer.size);
}
BufferQueue::WaitContext::WaitContext(std::shared_ptr<kernel::type::KThread> thread, DequeueIn input, u64 address, u64 size) : thread(std::move(thread)), input(input), address(address), size(size) {}
BufferQueue::DequeueOut::DequeueOut(u32 slot) : slot(slot), _unk0_(0x1), _unk1_(0x24) {}
BufferQueue::BufferQueue(const DeviceState &state) : state(state) {}
void BufferQueue::RequestBuffer(Parcel &in, Parcel &out) {
u32 slot = *reinterpret_cast<u32 *>(in.data.data() + constant::TokenLength);
auto buffer = queue.at(slot);
out.WriteData<u32>(1);
out.WriteData<u32>(sizeof(GbpBuffer));
out.WriteData<u32>(0);
out.WriteData(buffer->gbpBuffer);
state.logger->Debug("RequestBuffer: Slot: {}, Size: {}", slot, sizeof(GbpBuffer));
}
bool BufferQueue::DequeueBuffer(Parcel &in, Parcel &out, u64 address, u64 size) {
auto *data = reinterpret_cast<DequeueIn *>(in.data.data() + constant::TokenLength);
i64 slot{-1};
for (auto &buffer : queue) {
if (buffer.second->status == BufferStatus::Free && buffer.second->resolution.width == data->width && buffer.second->resolution.height == data->height && buffer.second->gbpBuffer.format == data->format && buffer.second->gbpBuffer.usage == data->usage) {
slot = buffer.first;
buffer.second->status = BufferStatus::Dequeued;
}
}
if (slot == -1) {
state.thisThread->Sleep();
waitVec.emplace_back(state.thisThread, *data, address, size);
state.logger->Debug("DequeueBuffer: No Free Buffers");
return true;
}
DequeueOut output(static_cast<u32>(slot));
out.WriteData(output);
state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot);
return false;
}
void BufferQueue::QueueBuffer(Parcel &in, Parcel &out) {
struct Data {
u32 slot;
u64 timestamp;
u32 autoTimestamp;
ARect crop;
u32 scalingMode;
u32 transform;
u32 stickyTransform;
u64 _unk0_;
u32 swapInterval;
Fence fence[4];
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength);
auto buffer = queue.at(data->slot);
buffer->status = BufferStatus::Queued;
displayQueue.emplace(buffer);
state.gpu->bufferEvent->Signal();
struct {
u32 width;
u32 height;
u32 _pad0_[3];
} output{
.width = buffer->gbpBuffer.width,
.height = buffer->gbpBuffer.height
};
out.WriteData(output);
state.logger->Debug("QueueBuffer: Timestamp: {}, Auto Timestamp: {}, Crop: [T: {}, B: {}, L: {}, R: {}], Scaling Mode: {}, Transform: {}, Sticky Transform: {}, Swap Interval: {}, Slot: {}", data->timestamp, data->autoTimestamp, data->crop.top, data->crop.bottom, data->crop.left, data->crop.right, data->scalingMode, data->transform, data->stickyTransform, data->swapInterval,
data->slot);
}
void BufferQueue::CancelBuffer(Parcel &parcel) {
struct Data {
u32 slot;
Fence fence[4];
} *data = reinterpret_cast<Data *>(parcel.data.data() + constant::TokenLength);
FreeBuffer(data->slot);
}
void BufferQueue::SetPreallocatedBuffer(Parcel &parcel) {
auto pointer = parcel.data.data() + constant::TokenLength;
struct Data {
u32 slot;
u32 _unk0_;
u32 length;
u32 _pad0_;
} *data = reinterpret_cast<Data *>(pointer);
pointer += sizeof(Data);
auto gbpBuffer = reinterpret_cast<GbpBuffer *>(pointer);
queue[data->slot] = std::make_shared<Buffer>(state, data->slot, *gbpBuffer);
state.gpu->bufferEvent->Signal();
state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Length: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}", data->slot, data->length, gbpBuffer->magic, gbpBuffer->width, gbpBuffer->height, gbpBuffer->stride, gbpBuffer->format, gbpBuffer->usage, gbpBuffer->index, gbpBuffer->nvmapId,
gbpBuffer->nvmapHandle, gbpBuffer->offset, (1U << gbpBuffer->blockHeightLog2));
}
void BufferQueue::FreeBuffer(u32 slotNo) {
auto &slot = queue.at(slotNo);
if (waitVec.empty())
slot->status = BufferStatus::Free;
else {
auto context = waitVec.begin();
while (context != waitVec.end()) {
if (slot->resolution.width == context->input.width && slot->resolution.height == context->input.height && slot->gbpBuffer.format == context->input.format && slot->gbpBuffer.usage == context->input.usage) {
context->thread->WakeUp();
gpu::Parcel out(state);
DequeueOut output(slotNo);
out.WriteData(output);
out.WriteParcel(context->address, context->size, context->thread->pid);
slot->status = BufferStatus::Dequeued;
waitVec.erase(context);
break;
}
context++;
}
}
}
}

View File

@ -0,0 +1,218 @@
#pragma once
#include <common.h>
#include <queue>
#include <gpu/devices/nvmap.h>
#include "parcel.h"
namespace skyline::gpu {
/**
* @brief A struct that encapsulates a resolution
*/
struct Resolution {
u32 width; //!< The width component of the resolution
u32 height; //!< The height component of the resolution
bool operator==(const Resolution &r) {
return (width == r.width) && (height == r.height);
}
bool operator!=(const Resolution &r) {
return !operator==(r);
}
};
/**
* @brief An enumeration of all the possible display IDs (https://switchbrew.org/wiki/Display_services#DisplayName)
*/
enum class DisplayId : u64 {
Default,
External,
Edid,
Internal,
Null
};
/**
* @brief A mapping from a display's name to it's displayType entry
*/
static const std::unordered_map<std::string, DisplayId> displayTypeMap{
{"Default", DisplayId::Default},
{"External", DisplayId::External},
{"Edid", DisplayId::Edid},
{"Internal", DisplayId::Internal},
{"Null", DisplayId::Null},
};
/**
* @brief The status of a specific layer
*/
enum class LayerStatus {
Uninitialized,
Initialized
};
/**
* @brief The status of a specific buffer
*/
enum class BufferStatus {
Free,
Dequeued,
Queued,
Acquired
};
/**
* @brief This struct holds information about the graphics buffer (https://github.com/reswitched/libtransistor/blob/0f0c36227842c344d163922fc98ee76229e9f0ee/lib/display/graphic_buffer_queue.c#L66)
*/
struct GbpBuffer {
u32 magic; //!< The magic of the graphics buffer: 0x47424652
u32 width; //!< The width of the buffer
u32 height; //!< The height of the buffer
u32 stride; //!< The stride of the buffer
u32 format; //!< The format of the buffer, this corresponds to AHardwareBuffer_Format
u32 usage; //!< The usage flags for the buffer
u32 _pad0_;
u32 index; //!< The index of the buffer
u32 _pad1_[3];
u32 nvmapId; //!< The ID of the buffer in regards to /dev/nvmap
u32 _pad2_[8];
u32 size; //!< The size of the buffer
u32 _pad3_[8];
u32 nvmapHandle; //!< The handle of the buffer in regards to /dev/nvmap
u32 offset; //!< This is the offset of the pixel data in the GPU Buffer
u32 _pad4_;
u32 blockHeightLog2; //!< The log2 of the block height
u32 _pad5_[58];
};
/**
* @brief This represents conditions for the completion of an asynchronous graphics operation
*/
struct Fence {
u32 syncptId;
u32 syncptValue;
};
/**
* @brief This holds the state and describes a single Buffer
*/
class Buffer {
public:
const DeviceState &state; //!< The state of the device
u32 slot; //!< The slot the buffer is in
Resolution resolution; //!< The resolution of this buffer
GbpBuffer gbpBuffer; //!< The information about the underlying buffer
BufferStatus status{BufferStatus::Free}; //!< The status of this buffer
std::vector<u8> dataBuffer; //!< The vector holding the actual pixel data
std::shared_ptr<device::NvMap::NvMapObject> nvBuffer{}; //!< A shared pointer to the buffer's nvmap object
/**
* @param state The state of the device
* @param slot The slot this buffer is in
* @param gpBuffer The GbpBuffer object for this buffer
*/
Buffer(const DeviceState &state, u32 slot, GbpBuffer &gbpBuffer);
/**
* @brief This reads the buffer from the process into the dataBuffer vector
*/
void UpdateBuffer();
};
/**
* @brief This holds the state of all the buffers used by the guest application
*/
class BufferQueue {
private:
const DeviceState &state; //!< The state of the device
/**
* @brief This is the input struct for DequeueBuffer
*/
struct DequeueIn {
u32 format;
u32 width;
u32 height;
u32 timestamps;
u32 usage;
};
/**
* @brief This is the output struct for DequeueBuffer
*/
struct DequeueOut {
u32 slot; //!< The slot of the dequeued buffer
u32 _unk0_;
u32 _unk1_;
u32 _unk2_[11]{};
/**
* @param slot The slot of the dequeued buffer
*/
DequeueOut(u32 slot);
};
/**
* @brief This holds the context of a thread waiting on a buffer
*/
struct WaitContext {
std::shared_ptr<kernel::type::KThread> thread; //!< The thread that is waiting on a buffer
DequeueIn input; //!< The input of DequeueBuffer
u64 address; //!< The address of the parcel buffer
u64 size; //!< The size of the parcel buffer
/**
* @param thread The thread that is waiting on a buffer
* @param input The input of DequeueBuffer
* @param address The address of the parcel buffer
* @param size The size of the parcel buffer
*/
WaitContext(std::shared_ptr<kernel::type::KThread> thread, DequeueIn input, u64 address, u64 size);
};
std::vector<WaitContext> waitVec; //!< A vector of shared pointers to threads waiting on a buffer
public:
std::unordered_map<u32, std::shared_ptr<Buffer>> queue; //!< A vector of shared pointers to all the queued buffers
std::queue<std::shared_ptr<Buffer>> displayQueue; //!< A queue of all the buffers to be posted to the display
/**
* @param state The state of the device
*/
BufferQueue(const DeviceState &state);
/**
* @brief This the GbpBuffer struct of the specified buffer
*/
void RequestBuffer(Parcel &in, Parcel &out);
/**
* @brief This returns the slot of a free buffer
* @param address The address of the parcel buffer
* @param size The size of the parcel buffer
* @return If the process is waiting for a buffer or not
*/
bool DequeueBuffer(Parcel &in, Parcel &out, u64 address, u64 size);
/**
* @brief This queues a buffer to be displayed
*/
void QueueBuffer(Parcel &in, Parcel &out);
/**
* @brief This removes a previously queued buffer
*/
void CancelBuffer(Parcel &parcel);
/**
* @brief This adds a pre-existing buffer to the queue
*/
void SetPreallocatedBuffer(Parcel &parcel);
/**
* @brief This frees a buffer which is currently queued
* @param slotNo The slot of the buffer
*/
void FreeBuffer(u32 slotNo);
};
}

View File

@ -0,0 +1,50 @@
#include "parcel.h"
#include <os.h>
#include <kernel/types/KProcess.h>
namespace skyline::gpu {
Parcel::Parcel(kernel::ipc::BufferDescriptorABW *buffer, const DeviceState &state) : Parcel(buffer->Address(), buffer->Size(), state) {}
Parcel::Parcel(kernel::ipc::BufferDescriptorX *buffer, const DeviceState &state) : Parcel(buffer->Address(), buffer->size, state) {}
Parcel::Parcel(u64 address, u64 size, const DeviceState &state) : state(state) {
state.thisProcess->ReadMemory(&header, address, sizeof(ParcelHeader));
if (size < (sizeof(ParcelHeader) + header.dataSize + header.objectsSize))
throw exception("The size of the parcel according to the header exceeds the specified size");
data.resize(header.dataSize);
state.thisProcess->ReadMemory(data.data(), address + header.dataOffset, header.dataSize);
objects.resize(header.objectsSize);
state.thisProcess->ReadMemory(objects.data(), address + header.objectsOffset, header.objectsSize);
}
Parcel::Parcel(const DeviceState &state) : state(state) {}
u64 Parcel::WriteParcel(kernel::ipc::BufferDescriptorABW *buffer, pid_t process) {
return WriteParcel(buffer->Address(), buffer->Size(), process);
}
u64 Parcel::WriteParcel(kernel::ipc::BufferDescriptorC *buffer, pid_t process) {
return WriteParcel(buffer->address, buffer->size, process);
}
u64 Parcel::WriteParcel(u64 address, u64 maxSize, pid_t process) {
header.dataSize = static_cast<u32>(data.size());
header.dataOffset = sizeof(ParcelHeader);
header.objectsSize = static_cast<u32>(objects.size());
header.objectsOffset = sizeof(ParcelHeader) + data.size();
u64 totalSize = sizeof(ParcelHeader) + header.dataSize + header.objectsSize;
if (maxSize < totalSize)
throw exception("The size of the parcel exceeds maxSize");
if (process) {
auto &object = state.os->processMap.at(process);
object->WriteMemory(header, address);
object->WriteMemory(data.data(), address + header.dataOffset, data.size());
object->WriteMemory(objects.data(), address + header.objectsOffset, objects.size());
} else {
state.thisProcess->WriteMemory(header, address);
state.thisProcess->WriteMemory(data.data(), address + header.dataOffset, data.size());
state.thisProcess->WriteMemory(objects.data(), address + header.objectsOffset, objects.size());
}
return totalSize;
}
}

View File

@ -0,0 +1,112 @@
#pragma once
#include <common.h>
#include <kernel/ipc.h>
namespace skyline::gpu {
/**
* @brief This class encapsulates a Parcel object (https://switchbrew.org/wiki/Display_services#Parcel)
*/
class Parcel {
private:
/**
* @brief This holds the header of a parcel
*/
struct ParcelHeader {
u32 dataSize;
u32 dataOffset;
u32 objectsSize;
u32 objectsOffset;
} header{};
static_assert(sizeof(ParcelHeader) == 0x10);
const DeviceState &state; //!< The state of the device
public:
std::vector<u8> data; //!< A vector filled with data in the parcel
std::vector<u8> objects; //!< A vector filled with objects in the parcel
/**
* @brief This constructor fills in the Parcel object with data from a IPC buffer
* @param buffer The buffer that contains the parcel
* @param state The state of the device
*/
Parcel(kernel::ipc::BufferDescriptorABW *buffer, const DeviceState &state);
/**
* @brief This constructor fills in the Parcel object with data from a IPC buffer
* @param buffer The buffer that contains the parcel
* @param state The state of the device
*/
Parcel(kernel::ipc::BufferDescriptorX *buffer, const DeviceState &state);
/**
* @brief This constructor fills in the Parcel object with data from a Parcel on a remote process
* @param address The remote address of the parcel
* @param size The size of the parcel
* @param state The state of the device
*/
Parcel(u64 address, u64 size, const DeviceState &state);
/**
* @brief This constructor is used to create an empty parcel then write to a process
* @param state The state of the device
*/
Parcel(const DeviceState &state);
/**
* @brief Writes some data to the Parcel
* @tparam ValueType The type of the object to write
* @param value The object to be written
*/
template<typename ValueType>
void WriteData(const ValueType &value) {
data.reserve(data.size() + sizeof(ValueType));
auto item = reinterpret_cast<const u8 *>(&value);
for (uint index = 0; sizeof(ValueType) > index; index++) {
data.push_back(*item);
item++;
}
}
/**
* @brief Writes an object to the Parcel
* @tparam ValueType The type of the object to write
* @param value The object to be written
*/
template<typename ValueType>
void WriteObject(const ValueType &value) {
objects.reserve(objects.size() + sizeof(ValueType));
auto item = reinterpret_cast<const u8 *>(&value);
for (uint index = 0; sizeof(ValueType) > index; index++) {
objects.push_back(*item);
item++;
}
}
/**
* @brief Writes the Parcel object into a particular B buffer on a process
* @param buffer The buffer to write into
* @param process The process to write the Parcel to
* @return The total size of the message
*/
u64 WriteParcel(kernel::ipc::BufferDescriptorABW *buffer, pid_t process = 0);
/**
* @brief Writes the Parcel object into a particular C buffer on a process
* @param buffer The buffer to write into
* @param process The process to write the Parcel to
* @return The total size of the message
*/
u64 WriteParcel(kernel::ipc::BufferDescriptorC *buffer, pid_t process = 0);
/**
* @brief Writes the Parcel object into the process's memory
* @param address The address to write the Parcel object to
* @param maxSize The maximum size of the Parcel
* @param process The process to write the Parcel to
* @return The total size of the message
*/
u64 WriteParcel(u64 address, u64 maxSize, pid_t process = 0);
};
}