Browse Source
Merge pull request #11632 from german77/hle_cabinet
Merge pull request #11632 from german77/hle_cabinet
service: am: Add support for LLE Cabinet Appletnce_cpp
committed by
GitHub
23 changed files with 985 additions and 60 deletions
-
2src/core/CMakeLists.txt
-
166src/core/hle/service/am/am.cpp
-
17src/core/hle/service/am/am.h
-
11src/core/hle/service/am/applets/applet_cabinet.h
-
6src/core/hle/service/am/applets/applet_general_backend.cpp
-
16src/core/hle/service/am/applets/applets.cpp
-
49src/core/hle/service/am/applets/applets.h
-
91src/core/hle/service/ldn/ldn.cpp
-
9src/core/hle/service/nfc/common/device.cpp
-
14src/core/hle/service/nvdrv/devices/nvmap.h
-
2src/core/hle/service/nvnflinger/buffer_item.h
-
2src/core/hle/service/nvnflinger/buffer_slot.h
-
351src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
-
65src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
-
2src/core/hle/service/nvnflinger/graphic_buffer_producer.h
-
11src/core/hle/service/nvnflinger/nvnflinger.cpp
-
9src/core/hle/service/nvnflinger/nvnflinger.h
-
3src/core/hle/service/nvnflinger/ui/fence.h
-
4src/core/hle/service/nvnflinger/ui/graphic_buffer.h
-
129src/core/hle/service/vi/vi.cpp
-
51src/yuzu/main.cpp
-
5src/yuzu/main.h
-
30src/yuzu/main.ui
@ -0,0 +1,351 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include <random>
|
|||
|
|||
#include "core/core.h"
|
|||
#include "core/hle/kernel/k_process.h"
|
|||
#include "core/hle/kernel/k_system_resource.h"
|
|||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
|||
#include "core/hle/service/nvdrv/nvdrv.h"
|
|||
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
|
|||
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
|||
#include "core/hle/service/nvnflinger/pixel_format.h"
|
|||
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
|||
#include "core/hle/service/vi/layer/vi_layer.h"
|
|||
#include "core/hle/service/vi/vi_results.h"
|
|||
|
|||
namespace Service::Nvnflinger { |
|||
|
|||
namespace { |
|||
|
|||
Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, |
|||
std::unique_ptr<Kernel::KPageGroup>* out_page_group, |
|||
Core::System& system, u32 size) { |
|||
using Core::Memory::YUZU_PAGESIZE; |
|||
|
|||
// Allocate memory for the system shared buffer.
|
|||
// FIXME: Because the gmmu can only point to cpu addresses, we need
|
|||
// to map this in the application space to allow it to be used.
|
|||
// FIXME: Add proper smmu emulation.
|
|||
// FIXME: This memory belongs to vi's .data section.
|
|||
auto& kernel = system.Kernel(); |
|||
auto* process = system.ApplicationProcess(); |
|||
auto& page_table = process->GetPageTable(); |
|||
|
|||
// Hold a temporary page group reference while we try to map it.
|
|||
auto pg = std::make_unique<Kernel::KPageGroup>( |
|||
kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager())); |
|||
|
|||
// Allocate memory from secure pool.
|
|||
R_TRY(kernel.MemoryManager().AllocateAndOpen( |
|||
pg.get(), size / YUZU_PAGESIZE, |
|||
Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure, |
|||
Kernel::KMemoryManager::Direction::FromBack))); |
|||
|
|||
// Get bounds of where mapping is possible.
|
|||
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); |
|||
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; |
|||
const auto state = Kernel::KMemoryState::Io; |
|||
const auto perm = Kernel::KMemoryPermission::UserReadWrite; |
|||
std::mt19937_64 rng{process->GetRandomEntropy(0)}; |
|||
|
|||
// Retry up to 64 times to map into alias code range.
|
|||
Result res = ResultSuccess; |
|||
int i; |
|||
for (i = 0; i < 64; i++) { |
|||
*out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE); |
|||
res = page_table.MapPageGroup(*out_map_address, *pg, state, perm); |
|||
if (R_SUCCEEDED(res)) { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// Return failure, if necessary
|
|||
R_UNLESS(i < 64, res); |
|||
|
|||
// Return the mapped page group.
|
|||
*out_page_group = std::move(pg); |
|||
|
|||
// We succeeded.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
template <typename T> |
|||
std::span<u8> SerializeIoc(T& params) { |
|||
return std::span(reinterpret_cast<u8*>(std::addressof(params)), sizeof(T)); |
|||
} |
|||
|
|||
Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { |
|||
// Create a handle.
|
|||
Nvidia::Devices::nvmap::IocCreateParams create_in_params{ |
|||
.size = size, |
|||
.handle = 0, |
|||
}; |
|||
Nvidia::Devices::nvmap::IocCreateParams create_out_params{}; |
|||
R_UNLESS(nvmap.IocCreate(SerializeIoc(create_in_params), SerializeIoc(create_out_params)) == |
|||
Nvidia::NvResult::Success, |
|||
VI::ResultOperationFailed); |
|||
|
|||
// Assign the output handle.
|
|||
*out_nv_map_handle = create_out_params.handle; |
|||
|
|||
// We succeeded.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { |
|||
// Free the handle.
|
|||
Nvidia::Devices::nvmap::IocFreeParams free_in_params{ |
|||
.handle = handle, |
|||
}; |
|||
Nvidia::Devices::nvmap::IocFreeParams free_out_params{}; |
|||
R_UNLESS(nvmap.IocFree(SerializeIoc(free_in_params), SerializeIoc(free_out_params)) == |
|||
Nvidia::NvResult::Success, |
|||
VI::ResultOperationFailed); |
|||
|
|||
// We succeeded.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, |
|||
u32 size) { |
|||
// Assign the allocated memory to the handle.
|
|||
Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{ |
|||
.handle = handle, |
|||
.heap_mask = 0, |
|||
.flags = {}, |
|||
.align = 0, |
|||
.kind = 0, |
|||
.address = GetInteger(buffer), |
|||
}; |
|||
Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{}; |
|||
R_UNLESS(nvmap.IocAlloc(SerializeIoc(alloc_in_params), SerializeIoc(alloc_out_params)) == |
|||
Nvidia::NvResult::Success, |
|||
VI::ResultOperationFailed); |
|||
|
|||
// We succeeded.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, |
|||
Common::ProcessAddress buffer, u32 size) { |
|||
// Get the nvmap device.
|
|||
auto nvmap_fd = nvdrv.Open("/dev/nvmap"); |
|||
auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); |
|||
ASSERT(nvmap != nullptr); |
|||
|
|||
// Create a handle.
|
|||
R_TRY(CreateNvMapHandle(out_handle, *nvmap, size)); |
|||
|
|||
// Ensure we maintain a clean state on failure.
|
|||
ON_RESULT_FAILURE { |
|||
ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle))); |
|||
}; |
|||
|
|||
// Assign the allocated memory to the handle.
|
|||
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size)); |
|||
} |
|||
|
|||
constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; |
|||
constexpr u32 SharedBufferBlockLinearBpp = 4; |
|||
|
|||
constexpr u32 SharedBufferBlockLinearWidth = 1280; |
|||
constexpr u32 SharedBufferBlockLinearHeight = 768; |
|||
constexpr u32 SharedBufferBlockLinearStride = |
|||
SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp; |
|||
constexpr u32 SharedBufferNumSlots = 7; |
|||
|
|||
constexpr u32 SharedBufferWidth = 1280; |
|||
constexpr u32 SharedBufferHeight = 720; |
|||
constexpr u32 SharedBufferAsync = false; |
|||
|
|||
constexpr u32 SharedBufferSlotSize = |
|||
SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp; |
|||
constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots; |
|||
|
|||
constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] { |
|||
SharedMemoryPoolLayout layout{}; |
|||
layout.num_slots = SharedBufferNumSlots; |
|||
|
|||
for (u32 i = 0; i < SharedBufferNumSlots; i++) { |
|||
layout.slots[i].buffer_offset = i * SharedBufferSlotSize; |
|||
layout.slots[i].size = SharedBufferSlotSize; |
|||
layout.slots[i].width = SharedBufferWidth; |
|||
layout.slots[i].height = SharedBufferHeight; |
|||
} |
|||
|
|||
return layout; |
|||
}(); |
|||
|
|||
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { |
|||
auto buffer = std::make_shared<android::GraphicBuffer>(); |
|||
buffer->width = SharedBufferWidth; |
|||
buffer->height = SharedBufferHeight; |
|||
buffer->stride = SharedBufferBlockLinearStride; |
|||
buffer->format = SharedBufferBlockLinearFormat; |
|||
buffer->buffer_id = handle; |
|||
buffer->offset = slot * SharedBufferSlotSize; |
|||
ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError); |
|||
} |
|||
|
|||
} // namespace
|
|||
|
|||
FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& flinger, |
|||
std::shared_ptr<Nvidia::Module> nvdrv) |
|||
: m_system(system), m_flinger(flinger), m_nvdrv(std::move(nvdrv)) {} |
|||
|
|||
FbShareBufferManager::~FbShareBufferManager() = default; |
|||
|
|||
Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) { |
|||
std::scoped_lock lk{m_guard}; |
|||
|
|||
// Ensure we have not already created a buffer.
|
|||
R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed); |
|||
|
|||
// Allocate memory and space for the shared buffer.
|
|||
Common::ProcessAddress map_address; |
|||
R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address), |
|||
std::addressof(m_buffer_page_group), m_system, |
|||
SharedBufferSize)); |
|||
|
|||
// Create an nvmap handle for the buffer and assign the memory to it.
|
|||
R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, map_address, |
|||
SharedBufferSize)); |
|||
|
|||
// Record the display id.
|
|||
m_display_id = display_id; |
|||
|
|||
// Create a layer for the display.
|
|||
m_layer_id = m_flinger.CreateLayer(m_display_id).value(); |
|||
|
|||
// Set up the buffer.
|
|||
m_buffer_id = m_next_buffer_id++; |
|||
|
|||
// Get the layer.
|
|||
VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id); |
|||
ASSERT(layer != nullptr); |
|||
|
|||
// Get the producer and set preallocated buffers.
|
|||
auto& producer = layer->GetBufferQueue(); |
|||
MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle); |
|||
MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle); |
|||
|
|||
// Assign outputs.
|
|||
*out_buffer_id = m_buffer_id; |
|||
*out_layer_id = m_layer_id; |
|||
|
|||
// We succeeded.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, |
|||
s32* out_nvmap_handle, |
|||
SharedMemoryPoolLayout* out_pool_layout, |
|||
u64 buffer_id, |
|||
u64 applet_resource_user_id) { |
|||
std::scoped_lock lk{m_guard}; |
|||
|
|||
R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); |
|||
R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound); |
|||
|
|||
*out_pool_layout = SharedBufferPoolLayout; |
|||
*out_buffer_size = SharedBufferSize; |
|||
*out_nvmap_handle = m_buffer_nvmap_handle; |
|||
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { |
|||
// Ensure the layer id is valid.
|
|||
R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound); |
|||
|
|||
// Get the layer.
|
|||
VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id); |
|||
R_UNLESS(layer != nullptr, VI::ResultNotFound); |
|||
|
|||
// We succeeded.
|
|||
*out_layer = layer; |
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence, |
|||
std::array<s32, 4>& out_slot_indexes, |
|||
s64* out_target_slot, u64 layer_id) { |
|||
std::scoped_lock lk{m_guard}; |
|||
|
|||
// Get the layer.
|
|||
VI::Layer* layer; |
|||
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); |
|||
|
|||
// Get the producer.
|
|||
auto& producer = layer->GetBufferQueue(); |
|||
|
|||
// Get the next buffer from the producer.
|
|||
s32 slot; |
|||
R_UNLESS(producer.DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0, |
|||
SharedBufferWidth, SharedBufferHeight, |
|||
SharedBufferBlockLinearFormat, 0) == android::Status::NoError, |
|||
VI::ResultOperationFailed); |
|||
|
|||
// Assign remaining outputs.
|
|||
*out_target_slot = slot; |
|||
out_slot_indexes = {0, 1, -1, -1}; |
|||
|
|||
// We succeeded.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence, |
|||
Common::Rectangle<s32> crop_region, |
|||
u32 transform, s32 swap_interval, |
|||
u64 layer_id, s64 slot) { |
|||
std::scoped_lock lk{m_guard}; |
|||
|
|||
// Get the layer.
|
|||
VI::Layer* layer; |
|||
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); |
|||
|
|||
// Get the producer.
|
|||
auto& producer = layer->GetBufferQueue(); |
|||
|
|||
// Request to queue the buffer.
|
|||
std::shared_ptr<android::GraphicBuffer> buffer; |
|||
R_UNLESS(producer.RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) == |
|||
android::Status::NoError, |
|||
VI::ResultOperationFailed); |
|||
|
|||
// Queue the buffer to the producer.
|
|||
android::QueueBufferInput input{}; |
|||
android::QueueBufferOutput output{}; |
|||
input.crop = crop_region; |
|||
input.fence = fence; |
|||
input.transform = static_cast<android::NativeWindowTransform>(transform); |
|||
input.swap_interval = swap_interval; |
|||
R_UNLESS(producer.QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) == |
|||
android::Status::NoError, |
|||
VI::ResultOperationFailed); |
|||
|
|||
// We succeeded.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, |
|||
u64 layer_id) { |
|||
std::scoped_lock lk{m_guard}; |
|||
|
|||
// Get the layer.
|
|||
VI::Layer* layer; |
|||
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); |
|||
|
|||
// Get the producer.
|
|||
auto& producer = layer->GetBufferQueue(); |
|||
|
|||
// Set the event.
|
|||
*out_event = std::addressof(producer.GetNativeHandle()); |
|||
|
|||
// We succeeded.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
} // namespace Service::Nvnflinger
|
|||
@ -0,0 +1,65 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/math_util.h" |
|||
#include "core/hle/service/nvnflinger/nvnflinger.h" |
|||
#include "core/hle/service/nvnflinger/ui/fence.h" |
|||
|
|||
namespace Kernel { |
|||
class KPageGroup; |
|||
} |
|||
|
|||
namespace Service::Nvnflinger { |
|||
|
|||
struct SharedMemorySlot { |
|||
u64 buffer_offset; |
|||
u64 size; |
|||
s32 width; |
|||
s32 height; |
|||
}; |
|||
static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size"); |
|||
|
|||
struct SharedMemoryPoolLayout { |
|||
s32 num_slots; |
|||
std::array<SharedMemorySlot, 0x10> slots; |
|||
}; |
|||
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); |
|||
|
|||
class FbShareBufferManager final { |
|||
public: |
|||
explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, |
|||
std::shared_ptr<Nvidia::Module> nvdrv); |
|||
~FbShareBufferManager(); |
|||
|
|||
Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id); |
|||
Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, |
|||
SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, |
|||
u64 applet_resource_user_id); |
|||
Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots, |
|||
s64* out_target_slot, u64 layer_id); |
|||
Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region, |
|||
u32 transform, s32 swap_interval, u64 layer_id, s64 slot); |
|||
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); |
|||
|
|||
private: |
|||
Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id); |
|||
|
|||
private: |
|||
u64 m_next_buffer_id = 1; |
|||
u64 m_display_id = 0; |
|||
u64 m_buffer_id = 0; |
|||
u64 m_layer_id = 0; |
|||
u32 m_buffer_nvmap_handle = 0; |
|||
SharedMemoryPoolLayout m_pool_layout = {}; |
|||
|
|||
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; |
|||
|
|||
std::mutex m_guard; |
|||
Core::System& m_system; |
|||
Nvnflinger& m_flinger; |
|||
std::shared_ptr<Nvidia::Module> m_nvdrv; |
|||
}; |
|||
|
|||
} // namespace Service::Nvnflinger |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue