0
0
mirror of https://github.com/Ishan09811/pine.git synced 2025-04-24 08:55:10 +00:00

Implement Android 15 16kib page size support (#40)

This commit is contained in:
Ishan09811 2024-12-26 20:30:27 +05:30 committed by GitHub
parent 3ff78af28e
commit 55efa7dc96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 158 additions and 133 deletions

View File

@ -19,7 +19,7 @@ jobs:
IS_BUILD_SIGNED: ${{ secrets.KEYSTORE != '' }} IS_BUILD_SIGNED: ${{ secrets.KEYSTORE != '' }}
UPLOAD_ARTIFACTS: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci') }} UPLOAD_ARTIFACTS: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'ci') }}
CMAKE_VERSION: "3.22.1" CMAKE_VERSION: "3.22.1"
NDK_VERSION: "26.1.10909125" NDK_VERSION: "27.2.12479018"
BUILD_TYPE: release BUILD_TYPE: release
steps: steps:

View File

@ -15,7 +15,7 @@ idea.module {
android { android {
namespace 'emu.skyline' namespace 'emu.skyline'
compileSdk 34 compileSdk 35
var isBuildSigned = (System.getenv("CI") == "true") && (System.getenv("IS_BUILD_SIGNED") == "true") var isBuildSigned = (System.getenv("CI") == "true") && (System.getenv("IS_BUILD_SIGNED") == "true")
@ -23,7 +23,7 @@ android {
applicationId "io.github.pine.emu" applicationId "io.github.pine.emu"
minSdk 29 minSdk 29
targetSdk 34 targetSdk 35
versionCode 1 versionCode 1
versionName "1.0.0" versionName "1.0.0"
@ -76,7 +76,8 @@ android {
debuggable true debuggable true
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments "-DCMAKE_BUILD_TYPE=RELEASE" arguments "-DCMAKE_BUILD_TYPE=RELEASE",
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
} }
} }
minifyEnabled true minifyEnabled true
@ -90,7 +91,8 @@ android {
debuggable true debuggable true
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments "-DCMAKE_BUILD_TYPE=RELWITHDEBINFO" arguments "-DCMAKE_BUILD_TYPE=RELWITHDEBINFO",
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
} }
} }
minifyEnabled false minifyEnabled false
@ -132,7 +134,7 @@ android {
} }
/* NDK and CMake */ /* NDK and CMake */
ndkVersion '26.1.10909125' ndkVersion '27.2.12479018'
externalNativeBuild { externalNativeBuild {
cmake { cmake {
version '3.22.1+' version '3.22.1+'

@ -1 +1 @@
Subproject commit f99f0b337c97a84abdf152710a9b81e10f3d4ae8 Subproject commit e1ccbd4d4f9757856e4998b1c6087c265c62605d

View File

@ -1,43 +1,62 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <variant> #include <unistd.h>
#include <stdexcept>
namespace skyline { #include <variant>
using u128 = __uint128_t; //!< Unsigned 128-bit integer
using u64 = __uint64_t; //!< Unsigned 64-bit integer namespace skyline {
using u32 = __uint32_t; //!< Unsigned 32-bit integer using u128 = __uint128_t; //!< Unsigned 128-bit integer
using u16 = __uint16_t; //!< Unsigned 16-bit integer using u64 = __uint64_t; //!< Unsigned 64-bit integer
using u8 = __uint8_t; //!< Unsigned 8-bit integer using u32 = __uint32_t; //!< Unsigned 32-bit integer
using i128 = __int128_t; //!< Signed 128-bit integer using u16 = __uint16_t; //!< Unsigned 16-bit integer
using i64 = __int64_t; //!< Signed 64-bit integer using u8 = __uint8_t; //!< Unsigned 8-bit integer
using i32 = __int32_t; //!< Signed 32-bit integer using i128 = __int128_t; //!< Signed 128-bit integer
using i16 = __int16_t; //!< Signed 16-bit integer using i64 = __int64_t; //!< Signed 64-bit integer
using i8 = __int8_t; //!< Signed 8-bit integer using i32 = __int32_t; //!< Signed 32-bit integer
using i16 = __int16_t; //!< Signed 16-bit integer
using KHandle = u32; //!< The type of a kernel handle using i8 = __int8_t; //!< Signed 8-bit integer
namespace constant { using KHandle = u32; //!< The type of a kernel handle
// Time
constexpr i64 NsInMicrosecond{1000}; //!< The amount of nanoseconds in a microsecond namespace constant {
constexpr i64 NsInSecond{1000000000}; //!< The amount of nanoseconds in a second // Time
constexpr i64 NsInMillisecond{1000000}; //!< The amount of nanoseconds in a millisecond constexpr i64 NsInMicrosecond{1000}; //!< The amount of nanoseconds in a microsecond
constexpr i64 NsInDay{86400000000000UL}; //!< The amount of nanoseconds in a day constexpr i64 NsInSecond{1000000000}; //!< The amount of nanoseconds in a second
constexpr i64 NsInMillisecond{1000000}; //!< The amount of nanoseconds in a millisecond
constexpr size_t AddressSpaceSize{1ULL << 39}; //!< The size of the host CPU AS in bytes constexpr i64 NsInDay{86400000000000UL}; //!< The amount of nanoseconds in a day
constexpr size_t PageSize{0x1000}; //!< The size of a host page
constexpr size_t PageSizeBits{12}; //!< log2(PageSize) constexpr size_t AddressSpaceSize{1ULL << 39}; //!< The size of the host CPU AS in bytes
static_assert(PageSize == PAGE_SIZE); constexpr u16 TlsSlotSize{0x200};
}
inline size_t getDynamicPageSize() {
/** size_t pageSize = getpagesize();
* @brief A deduction guide for overloads required for std::visit with std::variant if (pageSize == 0) {
*/ throw std::runtime_error("Failed to retrieve page size");
template<class... Ts> }
struct VariantVisitor : Ts ... { using Ts::operator()...; }; return pageSize;
template<class... Ts> VariantVisitor(Ts...) -> VariantVisitor<Ts...>; }
}
inline u8 getTlsSlots() {
size_t slots = getDynamicPageSize() / TlsSlotSize;
if (slots > 255) {
throw std::runtime_error("TlsSlots exceeds u8 capacity!");
}
return static_cast<u8>(slots);
}
const size_t PageSize{getDynamicPageSize()}; //!< The size of a host page
constexpr size_t PageSizeBits{12}; //!< log2(PageSize)
}
/**
* @brief A deduction guide for overloads required for std::visit with std::variant
*/
template<class... Ts>
struct VariantVisitor : Ts ... { using Ts::operator()...; };
template<class... Ts> VariantVisitor(Ts...) -> VariantVisitor<Ts...>;
}

View File

@ -158,8 +158,7 @@ namespace skyline {
* @return All entries overlapping with the given interval and a list of intervals they recursively cover with alignment for page-based lookup semantics * @return All entries overlapping with the given interval and a list of intervals they recursively cover with alignment for page-based lookup semantics
* @note This function is specifically designed for memory faulting lookups and has design-decisions that correspond to that which might not work for other uses * @note This function is specifically designed for memory faulting lookups and has design-decisions that correspond to that which might not work for other uses
*/ */
template<size_t Alignment> std::pair<std::vector<std::reference_wrapper<EntryType>>, std::vector<Interval>> GetAlignedRecursiveRange(std::size_t Alignment, Interval interval) {
std::pair<std::vector<std::reference_wrapper<EntryType>>, std::vector<Interval>> GetAlignedRecursiveRange(Interval interval) {
std::vector<std::reference_wrapper<EntryType>> queryEntries; std::vector<std::reference_wrapper<EntryType>> queryEntries;
std::vector<Interval> intervals; std::vector<Interval> intervals;
@ -242,9 +241,8 @@ namespace skyline {
return std::pair{queryEntries, intervals}; return std::pair{queryEntries, intervals};
} }
template<size_t Alignment> std::pair<std::vector<std::reference_wrapper<EntryType>>, std::vector<Interval>> GetAlignedRecursiveRange(std::size_t Alignment, AddressType address) {
std::pair<std::vector<std::reference_wrapper<EntryType>>, std::vector<Interval>> GetAlignedRecursiveRange(AddressType address) { return GetAlignedRecursiveRange(Alignment, Interval{address, address + 1});
return GetAlignedRecursiveRange<Alignment>(Interval{address, address + 1});
} }
}; };
} }

View File

@ -121,7 +121,7 @@ namespace skyline {
std::scoped_lock lock(trapMutex); std::scoped_lock lock(trapMutex);
// Retrieve any callbacks for the page that was faulted // Retrieve any callbacks for the page that was faulted
auto [entries, intervals]{trapMap.GetAlignedRecursiveRange<constant::PageSize>(address)}; auto [entries, intervals]{trapMap.GetAlignedRecursiveRange(constant::PageSize, address)};
if (entries.empty()) if (entries.empty())
return false; // There's no callbacks associated with this page return false; // There's no callbacks associated with this page

View File

@ -5,6 +5,7 @@
#include <gpu/interconnect/command_executor.h> #include <gpu/interconnect/command_executor.h>
#include "samplers.h" #include "samplers.h"
#include <unistd.h>
#include "textures.h" #include "textures.h"
namespace skyline::gpu::interconnect { namespace skyline::gpu::interconnect {
@ -23,7 +24,7 @@ namespace skyline::gpu::interconnect {
vk::PipelineStageFlagBits dstStage, vk::PipelineStageFlagBits dstStage,
vk::PipelineStageFlags &srcStageMask, vk::PipelineStageFlags &dstStageMask) { vk::PipelineStageFlags &srcStageMask, vk::PipelineStageFlags &dstStageMask) {
if (!view) // Return a dummy buffer if the constant buffer isn't bound if (!view) // Return a dummy buffer if the constant buffer isn't bound
return BufferBinding{ctx.gpu.megaBufferAllocator.Allocate(ctx.executor.cycle, PAGE_SIZE).buffer, 0, PAGE_SIZE}; return BufferBinding{ctx.gpu.megaBufferAllocator.Allocate(ctx.executor.cycle, getpagesize()).buffer, 0, static_cast<vk::DeviceSize>(getpagesize())};
ctx.executor.AttachBuffer(view); ctx.executor.AttachBuffer(view);
view.GetBuffer()->PopulateReadBarrier(dstStage, srcStageMask, dstStageMask); view.GetBuffer()->PopulateReadBarrier(dstStage, srcStageMask, dstStageMask);
@ -47,12 +48,12 @@ namespace skyline::gpu::interconnect {
}; };
auto ssbo{cbuf.Read<SsboDescriptor>(ctx.executor, desc.cbuf_offset)}; auto ssbo{cbuf.Read<SsboDescriptor>(ctx.executor, desc.cbuf_offset)};
if (ssbo.size == 0) if (ssbo.size == 0)
return BufferBinding{ctx.gpu.megaBufferAllocator.Allocate(ctx.executor.cycle, PAGE_SIZE).buffer, 0, PAGE_SIZE}; return BufferBinding{ctx.gpu.megaBufferAllocator.Allocate(ctx.executor.cycle, getpagesize()).buffer, 0, static_cast<vk::DeviceSize>(getpagesize())};
size_t padding{ssbo.address & (ctx.gpu.traits.minimumStorageBufferAlignment - 1)}; size_t padding{ssbo.address & (ctx.gpu.traits.minimumStorageBufferAlignment - 1)};
cachedView.Update(ctx, ssbo.address - padding, util::AlignUp(ssbo.size + padding, ctx.gpu.traits.minimumStorageBufferAlignment)); cachedView.Update(ctx, ssbo.address - padding, util::AlignUp(ssbo.size + padding, ctx.gpu.traits.minimumStorageBufferAlignment));
if (!cachedView.view) // Return a dummy buffer if the SSBO isn't bound if (!cachedView.view) // Return a dummy buffer if the SSBO isn't bound
return BufferBinding{ctx.gpu.megaBufferAllocator.Allocate(ctx.executor.cycle, PAGE_SIZE).buffer, 0, PAGE_SIZE}; return BufferBinding{ctx.gpu.megaBufferAllocator.Allocate(ctx.executor.cycle, getpagesize()).buffer, 0, static_cast<vk::DeviceSize>(getpagesize())};
auto view{cachedView.view}; auto view{cachedView.view};
ctx.executor.AttachBuffer(view); ctx.executor.AttachBuffer(view);

View File

@ -6,6 +6,7 @@
#include <gpu/interconnect/conversion/quads.h> #include <gpu/interconnect/conversion/quads.h>
#include <gpu/interconnect/common/state_updater.h> #include <gpu/interconnect/common/state_updater.h>
#include <soc/gm20b/channel.h> #include <soc/gm20b/channel.h>
#include <unistd.h>
#include "common/utils.h" #include "common/utils.h"
#include "maxwell_3d.h" #include "maxwell_3d.h"
#include "common.h" #include "common.h"
@ -53,7 +54,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
vk::DeviceSize size{conversion::quads::GetRequiredBufferSize(count, sizeof(u32)) + offset}; vk::DeviceSize size{conversion::quads::GetRequiredBufferSize(count, sizeof(u32)) + offset};
if (!quadConversionBuffer || quadConversionBuffer->size_bytes() < size) { if (!quadConversionBuffer || quadConversionBuffer->size_bytes() < size) {
quadConversionBuffer = std::make_shared<memory::Buffer>(ctx.gpu.memory.AllocateBuffer(util::AlignUp(size, PAGE_SIZE))); quadConversionBuffer = std::make_shared<memory::Buffer>(ctx.gpu.memory.AllocateBuffer(util::AlignUp(size, getpagesize())));
conversion::quads::GenerateQuadListConversionBuffer(quadConversionBuffer->cast<u32>().data(), firstVertex + count); conversion::quads::GenerateQuadListConversionBuffer(quadConversionBuffer->cast<u32>().data(), firstVertex + count);
quadConversionBufferAttached = false; quadConversionBufferAttached = false;
} }

View File

@ -1,69 +1,70 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <gpu.h> #include <gpu.h>
#include "megabuffer.h" #include <unistd.h>
#include "megabuffer.h"
namespace skyline::gpu {
MegaBufferChunk::MegaBufferChunk(GPU &gpu) : backing{gpu.memory.AllocateBuffer(MegaBufferChunkSize)}, freeRegion{backing.subspan(PAGE_SIZE)} {} namespace skyline::gpu {
MegaBufferChunk::MegaBufferChunk(GPU &gpu) : backing{gpu.memory.AllocateBuffer(MegaBufferChunkSize)}, freeRegion{backing.subspan(getpagesize())} {}
bool MegaBufferChunk::TryReset() {
if (cycle && cycle->Poll(true)) { bool MegaBufferChunk::TryReset() {
freeRegion = backing.subspan(PAGE_SIZE); if (cycle && cycle->Poll(true)) {
cycle = nullptr; freeRegion = backing.subspan(getpagesize());
return true; cycle = nullptr;
} return true;
}
return cycle == nullptr;
} return cycle == nullptr;
}
vk::Buffer MegaBufferChunk::GetBacking() const {
return backing.vkBuffer; vk::Buffer MegaBufferChunk::GetBacking() const {
} return backing.vkBuffer;
}
std::pair<vk::DeviceSize, span<u8>> MegaBufferChunk::Allocate(const std::shared_ptr<FenceCycle> &newCycle, vk::DeviceSize size, bool pageAlign) {
if (pageAlign) { std::pair<vk::DeviceSize, span<u8>> MegaBufferChunk::Allocate(const std::shared_ptr<FenceCycle> &newCycle, vk::DeviceSize size, bool pageAlign) {
// If page aligned data was requested then align the free if (pageAlign) {
auto alignedFreeBase{util::AlignUp(static_cast<size_t>(freeRegion.data() - backing.data()), PAGE_SIZE)}; // If page aligned data was requested then align the free
freeRegion = backing.subspan(alignedFreeBase); auto alignedFreeBase{util::AlignUp(static_cast<size_t>(freeRegion.data() - backing.data()), getpagesize())};
} freeRegion = backing.subspan(alignedFreeBase);
}
if (size > freeRegion.size())
return {0, {}}; if (size > freeRegion.size())
return {0, {}};
if (cycle != newCycle) {
newCycle->ChainCycle(cycle); if (cycle != newCycle) {
cycle = newCycle; newCycle->ChainCycle(cycle);
} cycle = newCycle;
}
// Allocate space for data from the free region
auto resultSpan{freeRegion.subspan(0, size)}; // Allocate space for data from the free region
auto resultSpan{freeRegion.subspan(0, size)};
// Move the free region along
freeRegion = freeRegion.subspan(size); // Move the free region along
freeRegion = freeRegion.subspan(size);
return {static_cast<vk::DeviceSize>(resultSpan.data() - backing.data()), resultSpan};
} return {static_cast<vk::DeviceSize>(resultSpan.data() - backing.data()), resultSpan};
}
MegaBufferAllocator::MegaBufferAllocator(GPU &gpu) : gpu{gpu}, activeChunk{chunks.emplace(chunks.end(), gpu)} {}
MegaBufferAllocator::MegaBufferAllocator(GPU &gpu) : gpu{gpu}, activeChunk{chunks.emplace(chunks.end(), gpu)} {}
MegaBufferAllocator::Allocation MegaBufferAllocator::Allocate(const std::shared_ptr<FenceCycle> &cycle, vk::DeviceSize size, bool pageAlign) {
if (auto allocation{activeChunk->Allocate(cycle, size, pageAlign)}; allocation.first) MegaBufferAllocator::Allocation MegaBufferAllocator::Allocate(const std::shared_ptr<FenceCycle> &cycle, vk::DeviceSize size, bool pageAlign) {
return {activeChunk->GetBacking(), allocation.first, allocation.second}; if (auto allocation{activeChunk->Allocate(cycle, size, pageAlign)}; allocation.first)
return {activeChunk->GetBacking(), allocation.first, allocation.second};
activeChunk = ranges::find_if(chunks, [&](auto &chunk) { return chunk.TryReset(); });
if (activeChunk == chunks.end()) // If there are no chunks available, allocate a new one activeChunk = ranges::find_if(chunks, [&](auto &chunk) { return chunk.TryReset(); });
activeChunk = chunks.emplace(chunks.end(), gpu); if (activeChunk == chunks.end()) // If there are no chunks available, allocate a new one
activeChunk = chunks.emplace(chunks.end(), gpu);
if (auto allocation{activeChunk->Allocate(cycle, size, pageAlign)}; allocation.first)
return {activeChunk->GetBacking(), allocation.first, allocation.second}; if (auto allocation{activeChunk->Allocate(cycle, size, pageAlign)}; allocation.first)
else return {activeChunk->GetBacking(), allocation.first, allocation.second};
throw exception("Failed to to allocate megabuffer space for size: 0x{:X}", size); else
} throw exception("Failed to to allocate megabuffer space for size: 0x{:X}", size);
}
MegaBufferAllocator::Allocation MegaBufferAllocator::Push(const std::shared_ptr<FenceCycle> &cycle, span<u8> data, bool pageAlign) {
auto allocation{Allocate(cycle, data.size(), pageAlign)}; MegaBufferAllocator::Allocation MegaBufferAllocator::Push(const std::shared_ptr<FenceCycle> &cycle, span<u8> data, bool pageAlign) {
allocation.region.copy_from(data); auto allocation{Allocate(cycle, data.size(), pageAlign)};
return allocation; allocation.region.copy_from(data);
} return allocation;
} }
}

View File

@ -78,11 +78,14 @@ namespace skyline::gpu {
try { try {
choreographerLooper = ALooper_prepare(0); choreographerLooper = ALooper_prepare(0);
AChoreographer_postFrameCallback64(AChoreographer_getInstance(), reinterpret_cast<AChoreographer_frameCallback64>(&ChoreographerCallback), this); AChoreographer_postFrameCallback64(AChoreographer_getInstance(), reinterpret_cast<AChoreographer_frameCallback64>(&ChoreographerCallback), this);
while (ALooper_pollAll(-1, nullptr, nullptr, nullptr) == ALOOPER_POLL_WAKE && !choreographerStop) {
while (ALooper_pollOnce(-1, nullptr, nullptr, nullptr) == ALOOPER_POLL_WAKE && !choreographerStop) {
while (paused.load(std::memory_order_acquire)) { while (paused.load(std::memory_order_acquire)) {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
} }
} catch (const signal::SignalException &e) { } catch (const signal::SignalException &e) {
LOGE("{}\nStack Trace:{}", e.what(), state.loader->GetStackTrace(e.frames)); LOGE("{}\nStack Trace:{}", e.what(), state.loader->GetStackTrace(e.frames));
if (state.process) if (state.process)

View File

@ -12,8 +12,7 @@
namespace skyline { namespace skyline {
namespace constant { namespace constant {
constexpr u16 TlsSlotSize{0x200}; //!< The size of a single TLS slot const u8 TlsSlots{constant::getTlsSlots()}; //!< The amount of TLS slots in a single page
constexpr u8 TlsSlots{constant::PageSize / TlsSlotSize}; //!< The amount of TLS slots in a single page
constexpr KHandle BaseHandleIndex{0xD000}; //!< The index of the base handle constexpr KHandle BaseHandleIndex{0xD000}; //!< The index of the base handle
} }

View File

@ -7,6 +7,7 @@
#include <os.h> #include <os.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <kernel/memory.h> #include <kernel/memory.h>
#include <unistd.h>
#include "loader.h" #include "loader.h"
namespace skyline::loader { namespace skyline::loader {
@ -45,7 +46,7 @@ namespace skyline::loader {
size_t hookSize{0}; size_t hookSize{0};
if (enableSymbolHooking && dynamicallyLinked) { if (enableSymbolHooking && dynamicallyLinked) {
executableSymbols = hle::GetExecutableSymbols(dynsym.cast<Elf64_Sym>(), dynstr); executableSymbols = hle::GetExecutableSymbols(dynsym.cast<Elf64_Sym>(), dynstr);
hookSize = util::AlignUp(state.nce->GetHookSectionSize(executableSymbols), PAGE_SIZE); hookSize = util::AlignUp(state.nce->GetHookSectionSize(executableSymbols), getpagesize());
} }
// Reserve patch + hook size only if we need to patch // Reserve patch + hook size only if we need to patch