Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
13cc97f
refactor(libc): reach the guest FPU state through GuestCpu
oberonlift Oct 28, 2026
d600baa
refactor(libkernel): read the timestamp counter through GuestCpu
oberonlift Nov 1, 2026
5842bfb
refactor(agc): spin and diff guest memory portably
oberonlift Oct 28, 2026
dcc5531
refactor(libSceJobManager): read the timestamp and pause through port…
oberonlift Oct 28, 2026
90c2dd1
refactor(libwebrtc): read the CPU features through GuestCpu
oberonlift Nov 3, 2026
af1d810
feat(ps5lift): add the runtime for lifted programs
oberonlift Nov 2, 2026
7ffeca3
ci(ps5lift): build ps5lift's runtime on Linux arm64
oberonlift Nov 2, 2026
1867566
feat(ps5lift): answer cpuid and xgetbv for AnyPS5's libraries
oberonlift Nov 3, 2026
fe7f034
fix(libc): cast the category vtable entry away from const in every build
oberonlift Nov 4, 2026
43d45a6
feat(build): add ANYPS5_GUEST_LIFTED to build the libraries for ps5lift
oberonlift Oct 28, 2026
65feea8
build: take the FFmpeg package for the target's architecture
oberonlift Oct 28, 2026
4d6893d
build(libs): link lifted libraries against the ps5lift runtime
oberonlift Oct 31, 2026
d376d44
refactor(libc): build the x86-only helpers portably for a lifted guest
oberonlift Oct 28, 2026
046371d
build(libs): find the ps5lift runtime from the lifted libraries
oberonlift Nov 2, 2026
d61c2e2
test(libs): hand the ps5lift runtime an empty program in lifted tests
oberonlift Nov 2, 2026
868d77d
ci(ps5lift): run AnyPS5's tests that pass in a lifted build on Linux …
oberonlift Nov 2, 2026
b4880be
fix(libSceNKWebKit): leave the forwarded exports out of lifted builds
oberonlift Nov 6, 2026
e9ec734
feat(libc): unwind guest frames from the lifted guest state
oberonlift Oct 28, 2026
5b76d21
feat(libc): iterate the lifted program's guest images in dl_iterate_phdr
oberonlift Oct 28, 2026
41c7aed
fix(libc): keep the host's exception runtime in lifted builds
oberonlift Oct 29, 2026
7d407b0
feat(libc): read guest va_lists portably in lifted builds
oberonlift Oct 28, 2026
b38d5f5
fix(libSceLibcInternal): start err and warn va_lists with APS5_VA_BEGIN
oberonlift Oct 28, 2026
44acfc8
fix(libSceAudio3d): hand report handlers a SysV va_list in lifted builds
oberonlift Oct 31, 2026
281a69e
fix(libSceAudioPropagation): leave the host-side report out of lifted…
oberonlift Nov 4, 2026
39b1373
feat(libc): convert guest long double at its boundaries
oberonlift Oct 28, 2026
9a4129d
feat(libc): keep the guest's long double exactly in lifted builds
oberonlift Oct 31, 2026
4478cbf
feat(libkernel): build for a lifted guest
oberonlift Oct 28, 2026
caa4db3
fix(libkernel): call a lifted thread entry as the native callback it is
oberonlift Oct 28, 2026
3a52434
feat(libkernel): run a lifted guest thread on the stack it asked for
oberonlift Oct 29, 2026
d37f240
fix(libkernel): allocate a lifted guest's thread objects in guest memory
oberonlift Oct 31, 2026
df80f50
feat(libkernel): report guest ranges made writable to the lifted runtime
oberonlift Nov 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/ps5lift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: ps5lift

on:
schedule:
- cron: '23 4 * * *'
pull_request:
paths:
- ps5lift/**
- .github/workflows/ps5lift.yml
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ps5lift-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
runtime:
runs-on: ubuntu-24.04-arm
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- name: Check out SoftFloat
run: git submodule update --init --depth 1 3rdparty/softfloat
- name: Install the toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends clang-18 lld-18 llvm-18 python3
- name: Build the runtime
run: python3 ps5lift/tools/ps5lift.py runtime --triple aarch64-unknown-linux-gnu --output build/runtime --clang /usr/lib/llvm-18/bin/clang --clangxx /usr/lib/llvm-18/bin/clang++

libraries:
runs-on: ubuntu-24.04-arm
timeout-minutes: 300
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 5G
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Install the toolchain, the libraries and lavapipe
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends cmake ninja-build ccache python3 clang-18 lld-18 llvm-18 libx11-dev libxext-dev libxrandr-dev libwayland-dev libxkbcommon-dev libegl-dev libdecor-0-dev libpipewire-0.3-dev libpulse-dev libasound2-dev libvulkan1 mesa-vulkan-drivers
- uses: actions/cache/restore@v6
with:
path: .ccache
key: lifted-libraries-ccache-${{ github.sha }}
restore-keys: lifted-libraries-ccache-
- name: Build the runtime
run: python3 ps5lift/tools/ps5lift.py runtime --triple aarch64-unknown-linux-gnu --output build/runtime --clang /usr/lib/llvm-18/bin/clang --clangxx /usr/lib/llvm-18/bin/clang++
- name: Configure AnyPS5 for a lifted guest
run: cmake -S . -B build/anyps5 -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_GUEST_LIFTED=ON -DANYPS5_LIFTED_RUNTIME=$PWD/build/runtime/libps5lift_runtime.so -DCMAKE_C_COMPILER=/usr/lib/llvm-18/bin/clang -DCMAKE_CXX_COMPILER=/usr/lib/llvm-18/bin/clang++ -DCMAKE_C_FLAGS=-mcpu=neoverse-v2 -DCMAKE_CXX_FLAGS=-mcpu=neoverse-v2
- name: Build and run the tests that pass in a lifted build
run: python3 ps5lift/tools/lifted_tests.py --build build/anyps5 --jobs 4
- if: always()
run: ccache --show-stats
- if: always()
uses: actions/cache/save@v6
with:
path: .ccache
key: lifted-libraries-ccache-${{ github.sha }}
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,6 @@
[submodule "3rdparty/sqlite"]
path = 3rdparty/sqlite
url = https://github.com/sqlite/sqlite.git
[submodule "3rdparty/softfloat"]
path = 3rdparty/softfloat
url = https://github.com/ucb-bar/berkeley-softfloat-3
1 change: 1 addition & 0 deletions 3rdparty/softfloat
Submodule softfloat added at 0a5ad2
12 changes: 11 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ function(add_test_executable target)
target_sources(${target} PRIVATE $<TARGET_OBJECTS:anyps5_windows_test_exit>)
target_link_options(${target} PRIVATE LINKER:--wrap=main)
endif()
if(ANYPS5_LIFTED_TEST_PROGRAM)
target_link_libraries(${target} PRIVATE ${ANYPS5_LIFTED_TEST_PROGRAM})
endif()
endfunction()

function(configure_host_test_executable target)
Expand Down Expand Up @@ -68,6 +71,7 @@ option(APS5_ENABLE_TIMING_LOG "Write frame timings asynchronously to frame-timin
option(APS5_ENABLE_TRACY "Build the Tracy profiler client and report performance timer scopes as Tracy zones" OFF)
option(ANYPS5_LIBRESSL_ORACLE "Build guest_libressl_oracle, which compares libSceLibreSsl with LibreSSL 2.5.4 downloaded from ftp.openbsd.org" OFF)
option(ANYPS5_USE_SYSTEM_DEPS "Resolve SDL2, freetype, Vulkan-Headers, SPIRV-Headers, glslang and SPIRV-Tools from the system instead of the bundled submodules" OFF)
option(ANYPS5_GUEST_LIFTED "Build the system libraries for a guest whose code ps5lift recompiled for the host" OFF)
add_compile_definitions(APS5_ENABLE_TIMING_LOG=$<BOOL:${APS5_ENABLE_TIMING_LOG}>)
include(cmake/SystemDeps.cmake)

Expand Down Expand Up @@ -677,7 +681,13 @@ if(NOT ANYPS5_USE_SYSTEM_DEPS)
endif()
endif()
endif()
set(ARCHITECTURE x86_64)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
set(ARCHITECTURE x86_64)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$" AND NOT MINGW)
set(ARCHITECTURE arm64)
else()
message(FATAL_ERROR "There is no FFmpeg package for ${CMAKE_SYSTEM_PROCESSOR} with this toolchain")
endif()
add_subdirectory(3rdparty/ffmpeg-core)
if(MINGW)
target_link_libraries(ffmpeg INTERFACE bcrypt ole32 user32 ws2_32)
Expand Down
22 changes: 22 additions & 0 deletions core/libs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,30 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()

if(ANYPS5_GUEST_LIFTED)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" OR NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(FATAL_ERROR "ANYPS5_GUEST_LIFTED builds the libraries for Linux with Clang, not for ${CMAKE_SYSTEM_NAME} with ${CMAKE_CXX_COMPILER_ID}")
endif()
add_compile_definitions(ANYPS5_GUEST_LIFTED)
set(ANYPS5_LIFTED_RUNTIME "" CACHE FILEPATH "The ps5lift runtime library that provides the GuestCpu functions")
if(NOT EXISTS "${ANYPS5_LIFTED_RUNTIME}")
message(FATAL_ERROR "ANYPS5_GUEST_LIFTED needs ANYPS5_LIFTED_RUNTIME, the ps5lift runtime library the libraries link against, not \"${ANYPS5_LIFTED_RUNTIME}\"")
endif()
endif()

if(APPLE)
set(ANYPS5_PRX_RPATH "@loader_path")
else()
set(ANYPS5_PRX_RPATH "$ORIGIN")
endif()
if(ANYPS5_GUEST_LIFTED)
get_filename_component(ANYPS5_LIFTED_RUNTIME_DIRECTORY "${ANYPS5_LIFTED_RUNTIME}" DIRECTORY)
list(APPEND ANYPS5_PRX_RPATH "${ANYPS5_LIFTED_RUNTIME_DIRECTORY}")
add_library(anyps5_lifted_test_program OBJECT ${CMAKE_CURRENT_SOURCE_DIR}/tests/LiftedEmptyProgram.cpp)
target_include_directories(anyps5_lifted_test_program PRIVATE ${CMAKE_SOURCE_DIR}/ps5lift/runtime/include)
target_link_libraries(anyps5_lifted_test_program INTERFACE ${ANYPS5_LIFTED_RUNTIME})
set(ANYPS5_LIFTED_TEST_PROGRAM anyps5_lifted_test_program)
endif()

add_executable(nid_patcher
${CMAKE_CURRENT_SOURCE_DIR}/nid/src/Sha1.cpp
Expand Down Expand Up @@ -113,6 +132,9 @@ if(WIN32)
endif()
configure_windows_unwind(libc)
configure_macos_unwind(libc)
if(ANYPS5_GUEST_LIFTED)
target_link_libraries(libc PUBLIC ${ANYPS5_LIFTED_RUNTIME})
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_options(libc PRIVATE LINKER:-z,now)
target_link_options(libc INTERFACE LINKER:--no-as-needed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@
#include <cstdint>
#include <cstdlib>
#include <thread>
#if defined(__x86_64__) || defined(_M_X64)
#include <immintrin.h>
#endif
#include "prx/libc/include/PreciseWait.hpp"

namespace AgcDriver {

inline void SpinPause() {
#if defined(__x86_64__) || defined(_M_X64)
_mm_pause();
#endif
PreciseSpinPause();
}

inline unsigned UsableProcessors() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#ifdef _WIN32
#include <windows.h>
#endif
#include <immintrin.h>
#include "prx/libc/include/PreciseWait.hpp"
#include <algorithm>
#include <tuple>
#include <vector>
Expand Down Expand Up @@ -366,7 +366,7 @@ void Driver::waitMemory(std::span<const std::uint32_t> packet, std::uint32_t que
}
++polls;
if (spinning) {
_mm_pause();
PreciseSpinPause();
if ((polls & 63u) != 0) continue;
if (std::chrono::steady_clock::now() - spinStart > spinLimit) spinning = false;
} else if (pauseSpin || polls >= 4000) {
Expand Down
20 changes: 15 additions & 5 deletions core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
#include <atomic>
#include <algorithm>
#include <bit>
#if defined(__x86_64__) || defined(_M_X64)
#include <emmintrin.h>
#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
#else
#include <cpuid.h>
#endif
#endif
#include <functional>
#include <cstdio>
#include <cstdlib>
Expand Down Expand Up @@ -49,14 +51,14 @@
#include <cstdint>
#include <fcntl.h>
#include <fstream>
#ifndef __APPLE__
#include <link.h>
#endif
#include <pthread.h>
#include <sstream>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <unistd.h>
#ifndef __APPLE__
#include "prx/libc/include/specifics/linux/ElfTypes.hpp"
#endif
#include "prx/libc/include/specifics/linux/HostMapping.hpp"
#ifdef __APPLE__
#include <mach/mach.h>
Expand Down Expand Up @@ -3057,12 +3059,15 @@ void WriteChangedCommitted(std::uint64_t address, std::span<const std::byte> cur
}
}

#if defined(__x86_64__) || defined(_M_X64)
static std::uint32_t changedByteCount(__m128i same) {
const auto sums = _mm_sad_epu8(_mm_and_si128(same, _mm_set1_epi8(1)), _mm_setzero_si128());
return 16u - static_cast<std::uint32_t>(_mm_cvtsi128_si32(_mm_add_epi32(sums, _mm_unpackhi_epi64(sums, sums))));
}
#endif

std::uint32_t ChangedByteStoreLimit() {
#if defined(__x86_64__) || defined(_M_X64)
static const auto limit = [] {
#if defined(_MSC_VER) && !defined(__clang__)
int registers[4]{};
Expand All @@ -3075,13 +3080,16 @@ std::uint32_t ChangedByteStoreLimit() {
return ebx == 0x68747541u && edx == 0x69746e65u && ecx == 0x444d4163u ? 16u : 4u;
}();
return limit;
#else
return 16u;
#endif
}

void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original) {
WriteChanged(address, current, original, ChangedByteStoreLimit());
}

static void writeChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, std::uint32_t byteStoreLimit, bool keepGuest) {
static void writeChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, [[maybe_unused]] std::uint32_t byteStoreLimit, bool keepGuest) {
require(current.size() == original.size(), "write-back snapshot sizes differ");
BreakProofs();
if (current.empty()) return;
Expand All @@ -3097,8 +3105,9 @@ static void writeChanged(std::uint64_t address, std::span<const std::byte> curre
CheckRange(destination, size, 1, true);
std::size_t firstChanged = size;
std::size_t lastChanged = 0;
bool masked = false;
std::size_t at = 0;
#if defined(__x86_64__) || defined(_M_X64)
bool masked = false;
for (; at + 16 <= size; at += 16) {
const auto bytes = _mm_loadu_si128(reinterpret_cast<const __m128i*>(current.data() + at));
const auto uploaded = _mm_loadu_si128(reinterpret_cast<const __m128i*>(original.data() + at));
Expand All @@ -3121,6 +3130,7 @@ static void writeChanged(std::uint64_t address, std::span<const std::byte> curre
lastChanged = std::max(lastChanged, at + 32 - static_cast<std::size_t>(std::countl_zero(changed)));
}
if (masked) _mm_sfence();
#endif
for (; at < size; ++at) {
if (current[at] == original[at] || (keepGuest && destination[at] != original[at])) continue;
destination[at] = current[at];
Expand Down
8 changes: 2 additions & 6 deletions core/libs/prx/libSceAgcDriver/Graphics/include/SlimMutex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

#include <atomic>
#include <thread>
#if defined(__x86_64__) || defined(_M_X64)
#include <immintrin.h>
#endif
#include "prx/libc/include/PreciseWait.hpp"

namespace AgcDriver::Graphics {

Expand All @@ -21,9 +19,7 @@ class SlimMutex {
while (locked.load(std::memory_order_relaxed)) {
if (spins < 128) {
++spins;
#if defined(__x86_64__) || defined(_M_X64)
_mm_pause();
#endif
PreciseSpinPause();
} else {
std::this_thread::yield();
}
Expand Down
37 changes: 29 additions & 8 deletions core/libs/prx/libSceAudio3d/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "prx/libSceAudioOut/include/AudioOut.hpp"
#include "prx/libSceAudio3d/include/Audio3d.hpp"
#include <algorithm>
#include <array>
#include <atomic>
#include <cmath>
#include <cstdarg>
Expand Down Expand Up @@ -250,6 +251,28 @@ int DrainAssociatedAudioOuts() {
}
}

void DeliverReport(const Audio3dReport& report) {
Audio3dReportSlot slots[AUDIO3D_REPORT_SLOTS];
{
std::lock_guard lock(g_report_mutex);
std::copy(std::begin(g_report_slots[report.type]), std::end(g_report_slots[report.type]), slots);
}
for (const Audio3dReportSlot& slot : slots) {
if (slot.handler != nullptr) slot.handler(&report, slot.user_data);
}
}

#ifdef ANYPS5_GUEST_LIFTED
constexpr std::uint32_t SysVGeneralRegisterBytes = 6 * 8;
constexpr std::size_t SysVRegisterSaveBytes = 6 * 8 + 8 * 16;

template <std::size_t Count>
void DispatchReport(std::uint32_t type, std::uint32_t port_id, std::uint32_t object_id, int result, const char* function, const char* argument_types, std::array<std::uint64_t, Count> values) {
alignas(16) std::array<std::uint8_t, SysVRegisterSaveBytes> registers{};
const Audio3dReportArguments arguments{SysVGeneralRegisterBytes, SysVGeneralRegisterBytes, values.data(), registers.data()};
DeliverReport({sizeof(Audio3dReport), port_id, object_id, type, result, function, argument_types, arguments});
}
#else
void APS5_VABI DispatchReport_nid_no_patch(std::uint32_t type, std::uint32_t port_id, std::uint32_t object_id, int result, const char* function, const char* argument_types, ...) {
#ifdef _WIN32
__builtin_sysv_va_list arguments;
Expand All @@ -261,20 +284,14 @@ void APS5_VABI DispatchReport_nid_no_patch(std::uint32_t type, std::uint32_t por
static_assert(sizeof(arguments) == sizeof(Audio3dReportArguments));
Audio3dReport report{sizeof(Audio3dReport), port_id, object_id, type, result, function, argument_types, {}};
std::memcpy(&report.arguments, arguments, sizeof(report.arguments));
Audio3dReportSlot slots[AUDIO3D_REPORT_SLOTS];
{
std::lock_guard lock(g_report_mutex);
std::copy(std::begin(g_report_slots[type]), std::end(g_report_slots[type]), slots);
}
for (const Audio3dReportSlot& slot : slots) {
if (slot.handler != nullptr) slot.handler(&report, slot.user_data);
}
DeliverReport(report);
#ifdef _WIN32
__builtin_sysv_va_end(arguments);
#else
va_end(arguments);
#endif
}
#endif

template <typename T>
std::uint64_t ReportArgument(T value) {
Expand All @@ -288,7 +305,11 @@ std::uint64_t ReportArgument(T value) {
template <typename... Arguments>
void Report(std::uint32_t port_id, std::uint32_t object_id, int result, const char* function, const char* argument_types, Arguments... arguments) {
if (g_call_report_handlers.load() == 0) return;
#ifdef ANYPS5_GUEST_LIFTED
DispatchReport(AUDIO3D_REPORT_TYPE_CALL, port_id, object_id, result, function, argument_types, std::array<std::uint64_t, sizeof...(Arguments)>{ReportArgument(arguments)...});
#else
DispatchReport_nid_no_patch(AUDIO3D_REPORT_TYPE_CALL, port_id, object_id, result, function, argument_types, ReportArgument(arguments)...);
#endif
}

}
Expand Down
3 changes: 3 additions & 0 deletions core/libs/prx/libSceAudioPropagation/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
add_library(libSceAudioPropagation SHARED EXCLUDE_FROM_ALL Export.cpp InventoryExports.cpp)
if(ANYPS5_GUEST_LIFTED)
target_sources(libSceAudioPropagation PRIVATE LiftedReport.cpp)
endif()
target_include_directories(libSceAudioPropagation PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceAudioPropagation)

Expand Down
Loading
Loading