mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
Compare commits
51 commits
089c388019
...
e27926d629
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e27926d629 | ||
|
|
8cfb4e8d16 | ||
|
|
014012c219 | ||
|
|
f71e3410c1 | ||
|
|
ecfb47ecaf | ||
|
|
5f109c0e34 | ||
|
|
aee92cce57 | ||
|
|
05dee2c8e3 | ||
|
|
63e9a3f597 | ||
|
|
2589143798 | ||
|
|
b358d6b2c9 | ||
|
|
3986f77869 | ||
|
|
e66ce512d2 | ||
|
|
fd9bf42538 | ||
|
|
be56f0745a | ||
|
|
37f423aec3 | ||
|
|
640df36c48 | ||
|
|
bd215fab92 | ||
|
|
ac853e0817 | ||
|
|
7b03b695f5 | ||
|
|
e73a0b962d | ||
|
|
30469f7fb9 | ||
|
|
2965aaf3e3 | ||
|
|
3f14b99f24 | ||
|
|
3230bfefd8 | ||
|
|
77dd5d7d10 | ||
|
|
47e164d2ad | ||
|
|
a7152c7ad7 | ||
|
|
5428d2bc5e | ||
|
|
5cc0f4eeda | ||
|
|
c4bb3e2577 | ||
|
|
a918f6fdbe | ||
|
|
15ac6278de | ||
|
|
0370d628e4 | ||
|
|
b17f73de88 | ||
|
|
19b314e018 | ||
|
|
24ad8607fa | ||
|
|
b8e08c1470 | ||
|
|
dbfa5002e5 | ||
|
|
fce4127c2e | ||
|
|
ecaf607a8f | ||
|
|
7419457efd | ||
|
|
3e694d6bab | ||
|
|
2f70a5b6dd | ||
|
|
2df7b3871c | ||
|
|
593297153a | ||
|
|
825c92b135 | ||
|
|
5904079a87 | ||
|
|
d2b9b7c1f6 | ||
|
|
b1080c40de | ||
|
|
e87d8349ea |
|
|
@ -1,27 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# Pull all the submodules except llvm and opencv
|
||||
# Note: Tried to use git submodule status, but it takes over 20 seconds
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules)
|
||||
|
||||
CONFIGURE_ARGS="
|
||||
-DWITH_LLVM=ON
|
||||
-DUSE_SDL=OFF
|
||||
-DUSE_PRECOMPILED_HEADERS=OFF
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF
|
||||
-DUSE_SYSTEM_FFMPEG=ON
|
||||
-DUSE_SYSTEM_CURL=ON
|
||||
-DUSE_SYSTEM_LIBPNG=ON
|
||||
-DUSE_SYSTEM_OPENCV=ON
|
||||
"
|
||||
|
||||
# base Clang workaround (missing clang-scan-deps)
|
||||
CONFIGURE_ARGS="$CONFIGURE_ARGS -DCMAKE_CXX_SCAN_FOR_MODULES=OFF"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
cmake -B build -G Ninja $CONFIGURE_ARGS
|
||||
cmake --build build
|
||||
|
||||
ccache --show-stats
|
||||
ccache --zero-stats
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
git config --global --add safe.directory '*'
|
||||
|
||||
# Pull all the submodules except llvm and opencv
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules)
|
||||
|
||||
if [ "$COMPILER" = "gcc" ]; then
|
||||
export CC=gcc-14
|
||||
export CXX=g++-14
|
||||
else
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
export CFLAGS="$CFLAGS -fuse-ld=lld"
|
||||
fi
|
||||
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_C_FLAGS="$CFLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CFLAGS" \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_PRECOMPILED_HEADERS=OFF \
|
||||
-DUSE_SYSTEM_CURL=ON \
|
||||
-DUSE_SDL=OFF \
|
||||
-DUSE_SYSTEM_FFMPEG=OFF \
|
||||
-DUSE_SYSTEM_CURL=OFF \
|
||||
-DUSE_SYSTEM_OPENAL=OFF \
|
||||
-DUSE_SYSTEM_FFMPEG=OFF \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DOpenGL_GL_PREFERENCE=LEGACY \
|
||||
-DSTATIC_LINK_LLVM=ON \
|
||||
-DBUILD_LLVM=on \
|
||||
-DWITH_RPCSX=off \
|
||||
-DWITH_RPCS3=on \
|
||||
-DWITH_RPCS3_QT_UI=on \
|
||||
-G Ninja
|
||||
|
||||
cmake --build build; build_status=$?;
|
||||
|
||||
shellcheck .ci/*.sh
|
||||
|
||||
# If it compiled succesfully let's deploy.
|
||||
# Azure and Cirrus publish PRs as artifacts only.
|
||||
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
.ci/deploy-linux.sh "aarch64"
|
||||
fi
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
git config --global --add safe.directory '*'
|
||||
|
||||
# Pull all the submodules except llvm and opencv
|
||||
# Note: Tried to use git submodule status, but it takes over 20 seconds
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules)
|
||||
|
||||
if [ "$COMPILER" = "gcc" ]; then
|
||||
# These are set in the dockerfile
|
||||
export CC=gcc-14
|
||||
export CXX=g++-14
|
||||
else
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
export LD=clang
|
||||
export CFLAGS="$CFLAGS -fuse-ld=lld"
|
||||
fi
|
||||
|
||||
cmake -B build \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_C_FLAGS="$CFLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$CFLAGS" \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_PRECOMPILED_HEADERS=OFF \
|
||||
-DUSE_SDL=OFF \
|
||||
-DUSE_SYSTEM_CURL=OFF \
|
||||
-DUSE_SYSTEM_OPENAL=OFF \
|
||||
-DUSE_SYSTEM_FFMPEG=OFF \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DOpenGL_GL_PREFERENCE=LEGACY \
|
||||
-DSTATIC_LINK_LLVM=ON \
|
||||
-DBUILD_LLVM=on \
|
||||
-DWITH_RPCSX=off \
|
||||
-DWITH_RPCS3=on \
|
||||
-DWITH_RPCS3_QT_UI=on \
|
||||
-G Ninja
|
||||
|
||||
cmake --build build; build_status=$?;
|
||||
|
||||
shellcheck .ci/*.sh
|
||||
|
||||
# If it compiled succesfully let's deploy.
|
||||
# Azure and Cirrus publish PRs as artifacts only.
|
||||
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
.ci/deploy-linux.sh "x86_64"
|
||||
fi
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
brew_arm64_install_packages() {
|
||||
for pkg in "$@"; do
|
||||
echo "Fetching bottle for $pkg (arm64)..."
|
||||
bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")"
|
||||
if [ ! -f "$bottle_path" ]; then
|
||||
if ! "$BREW_ARM64_PATH/bin/brew" fetch --force --verbose --debug --bottle-tag=arm64_sonoma "$pkg"; then
|
||||
echo "Failed to fetch bottle for $pkg"
|
||||
return 1
|
||||
fi
|
||||
bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")"
|
||||
fi
|
||||
|
||||
echo "Installing $pkg (arm64)..."
|
||||
"$BREW_ARM64_PATH/bin/brew" install --force --force-bottle --ignore-dependencies "$bottle_path" || true
|
||||
done
|
||||
}
|
||||
|
||||
export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
|
||||
/usr/local/bin/brew update
|
||||
sudo rm -rf /usr/local/Cellar/curl /usr/local/opt/curl
|
||||
/usr/local/bin/brew install -f --overwrite curl
|
||||
/usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg
|
||||
/usr/local/bin/brew install -f --build-from-source ffmpeg@5 || true
|
||||
/usr/local/bin/brew install -f --overwrite python || true
|
||||
/usr/local/bin/brew link --overwrite python || true
|
||||
/usr/local/bin/brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg
|
||||
/usr/local/bin/brew link -f curl || true
|
||||
/usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils
|
||||
/usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5 || true
|
||||
|
||||
export BREW_ARM64_PATH="/opt/homebrew1"
|
||||
sudo mkdir -p "$BREW_ARM64_PATH"
|
||||
sudo chmod 777 "$BREW_ARM64_PATH"
|
||||
curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C "$BREW_ARM64_PATH"
|
||||
|
||||
#"$BREW_ARM64_PATH/bin/brew" update
|
||||
# libvorbis requires Homebrew-installed curl, but we can't run it on x64, and we also need the aarch64 libs, so we swap the binary
|
||||
brew_arm64_install_packages curl
|
||||
mv /opt/homebrew1/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl.bak
|
||||
ln -s /usr/local/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl
|
||||
|
||||
brew_arm64_install_packages 0mq aom aribb24 ca-certificates cjson dav1d ffmpeg@5 fontconfig freetype freetype2 gettext glew gmp gnutls lame libbluray libidn2 libnettle libogg libpng librist libsodium libsoxr libtasn libtasn1 libunistring libvmaf libvorbis libvpx libx11 libxau libxcb libxdmcp llvm@$LLVM_COMPILER_VER mbedtls molten-vk nettle opencore-amr openjpeg openssl opus p11-kit pkg-config pkgconfig pzstd rav1e sdl3 snappy speex srt svt-av1 theora vulkan-headers webp x264 x265 xz z3 zeromq zmq zstd
|
||||
"$BREW_ARM64_PATH/bin/brew" link -f ffmpeg@5
|
||||
ln -s "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib" "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib"
|
||||
|
||||
# moltenvk based on commit for 1.2.11 release
|
||||
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb
|
||||
/usr/local/bin/brew install -f --overwrite ./molten-vk.rb
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
export BREW_PATH;
|
||||
BREW_PATH="$(brew --prefix)"
|
||||
export BREW_X64_PATH;
|
||||
BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)"
|
||||
export BREW_BIN="/usr/local/bin"
|
||||
export BREW_SBIN="/usr/local/sbin"
|
||||
export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=arm64'
|
||||
|
||||
export WORKDIR;
|
||||
WORKDIR="$(pwd)"
|
||||
|
||||
# Get Qt
|
||||
if [ ! -d "/tmp/Qt/$QT_VER" ]; then
|
||||
mkdir -p "/tmp/Qt"
|
||||
git clone https://github.com/engnr/qt-downloader.git
|
||||
cd qt-downloader
|
||||
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
|
||||
# nested Qt 6.8.3 URL workaround
|
||||
# sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
|
||||
# sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
|
||||
cd "/tmp/Qt"
|
||||
"$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml
|
||||
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
|
||||
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.3 workaround
|
||||
"$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64"
|
||||
fi
|
||||
|
||||
cd "$WORKDIR"
|
||||
ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER"
|
||||
|
||||
export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN"
|
||||
export SDL3_DIR="$BREW_ARM64_PATH/opt/sdl3/lib/cmake/SDL3"
|
||||
|
||||
export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH"
|
||||
export LDFLAGS="-L$BREW_ARM64_PATH/lib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavcodec.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavformat.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavutil.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswscale.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswresample.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++.1.dylib $BREW_ARM64_PATH/lib/libSDL3.dylib $BREW_ARM64_PATH/lib/libGLEW.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib -Wl,-rpath,$BREW_ARM64_PATH/lib"
|
||||
export CPPFLAGS="-I$BREW_ARM64_PATH/include -I$BREW_X64_PATH/include -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export LIBRARY_PATH="$BREW_ARM64_PATH/lib"
|
||||
export LD_LIBRARY_PATH="$BREW_ARM64_PATH/lib"
|
||||
|
||||
export VULKAN_SDK
|
||||
VULKAN_SDK="$BREW_ARM64_PATH/opt/molten-vk"
|
||||
ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" || true
|
||||
export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json"
|
||||
|
||||
export LLVM_DIR
|
||||
LLVM_DIR="$BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER"
|
||||
# exclude ffmpeg, LLVM, and sdl from submodule update
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/SDL/ { print $3 }' .gitmodules)
|
||||
|
||||
# 3rdparty fixes
|
||||
sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c
|
||||
|
||||
rm -rf build
|
||||
mkdir build && cd build || exit 1
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=14.0
|
||||
|
||||
"$BREW_X64_PATH/bin/cmake" .. \
|
||||
-DUSE_SDL=ON \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DUSE_VULKAN=ON \
|
||||
-DUSE_ALSA=OFF \
|
||||
-DUSE_PULSE=OFF \
|
||||
-DUSE_AUDIOUNIT=ON \
|
||||
-DUSE_SYSTEM_FFMPEG=ON \
|
||||
-DLLVM_CCACHE_BUILD=OFF \
|
||||
-DLLVM_BUILD_RUNTIME=OFF \
|
||||
-DLLVM_BUILD_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_DOCS=OFF \
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF \
|
||||
-DLLVM_INCLUDE_TESTS=OFF \
|
||||
-DLLVM_INCLUDE_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_UTILS=OFF \
|
||||
-DLLVM_USE_PERF=OFF \
|
||||
-DLLVM_ENABLE_Z3_SOLVER=OFF \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_SYSTEM_MVK=ON \
|
||||
-DUSE_SYSTEM_FAUDIO=OFF \
|
||||
-DUSE_SYSTEM_SDL=ON \
|
||||
-DUSE_SYSTEM_OPENCV=ON \
|
||||
$CMAKE_EXTRA_OPTS \
|
||||
-DLLVM_TARGET_ARCH=arm64 \
|
||||
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||
-DCMAKE_IGNORE_PATH="$BREW_X64_PATH/lib" \
|
||||
-DCMAKE_IGNORE_PREFIX_PATH=/usr/local/opt \
|
||||
-DCMAKE_SYSTEM_PROCESSOR=arm64 \
|
||||
-DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinARM64.cmake \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-G Ninja
|
||||
|
||||
"$BREW_PATH/bin/ninja"; build_status=$?;
|
||||
|
||||
cd ..
|
||||
|
||||
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
.ci/deploy-mac-arm64.sh
|
||||
fi
|
||||
123
.ci/build-mac.sh
123
.ci/build-mac.sh
|
|
@ -1,123 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
|
||||
brew unlink certifi
|
||||
brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg
|
||||
|
||||
#/usr/sbin/softwareupdate --install-rosetta --agree-to-license
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
arch -x86_64 /usr/local/bin/brew update
|
||||
arch -x86_64 /usr/local/bin/brew install -f --overwrite python || arch -x86_64 /usr/local/bin/brew link --overwrite python
|
||||
arch -x86_64 /usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg
|
||||
arch -x86_64 /usr/local/bin/brew install -f --build-from-source ffmpeg@5
|
||||
arch -x86_64 /usr/local/bin/brew reinstall -f --build-from-source gnutls freetype
|
||||
arch -x86_64 /usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils
|
||||
arch -x86_64 /usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5
|
||||
|
||||
# moltenvk based on commit for 1.2.11 release
|
||||
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb
|
||||
arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
export BREW_PATH;
|
||||
BREW_PATH="$(brew --prefix)"
|
||||
export BREW_X64_PATH;
|
||||
BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)"
|
||||
export BREW_BIN="/usr/local/bin"
|
||||
export BREW_SBIN="/usr/local/sbin"
|
||||
export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=X86'
|
||||
|
||||
export WORKDIR;
|
||||
WORKDIR="$(pwd)"
|
||||
|
||||
# Get Qt
|
||||
if [ ! -d "/tmp/Qt/$QT_VER" ]; then
|
||||
mkdir -p "/tmp/Qt"
|
||||
git clone https://github.com/engnr/qt-downloader.git
|
||||
cd qt-downloader
|
||||
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
|
||||
# nested Qt 6.8.3 URL workaround
|
||||
# sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
|
||||
# sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
|
||||
cd "/tmp/Qt"
|
||||
"$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml
|
||||
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
|
||||
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.3 workaround
|
||||
"$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64"
|
||||
fi
|
||||
|
||||
cd "$WORKDIR"
|
||||
ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER"
|
||||
|
||||
export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN"
|
||||
export SDL3_DIR="$BREW_X64_PATH/opt/sdl3/lib/cmake/SDL3"
|
||||
|
||||
export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH"
|
||||
export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib"
|
||||
export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export LIBRARY_PATH="$BREW_X64_PATH/lib"
|
||||
export LD_LIBRARY_PATH="$BREW_X64_PATH/lib"
|
||||
|
||||
export VULKAN_SDK
|
||||
VULKAN_SDK="$BREW_X64_PATH/opt/molten-vk"
|
||||
ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib"
|
||||
export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json"
|
||||
|
||||
export LLVM_DIR
|
||||
LLVM_DIR="BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER"
|
||||
# exclude ffmpeg, LLVM, opencv, and sdl from submodule update
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ { print $3 }' .gitmodules)
|
||||
|
||||
# 3rdparty fixes
|
||||
sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c
|
||||
|
||||
mkdir build && cd build || exit 1
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=14.0
|
||||
|
||||
"$BREW_X64_PATH/bin/cmake" .. \
|
||||
-DUSE_SDL=ON \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DUSE_VULKAN=ON \
|
||||
-DUSE_ALSA=OFF \
|
||||
-DUSE_PULSE=OFF \
|
||||
-DUSE_AUDIOUNIT=ON \
|
||||
-DUSE_SYSTEM_FFMPEG=ON \
|
||||
-DLLVM_CCACHE_BUILD=OFF \
|
||||
-DLLVM_BUILD_RUNTIME=OFF \
|
||||
-DLLVM_BUILD_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_DOCS=OFF \
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF \
|
||||
-DLLVM_INCLUDE_TESTS=OFF \
|
||||
-DLLVM_INCLUDE_TOOLS=OFF \
|
||||
-DLLVM_INCLUDE_UTILS=OFF \
|
||||
-DLLVM_USE_PERF=OFF \
|
||||
-DLLVM_ENABLE_Z3_SOLVER=OFF \
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF \
|
||||
-DUSE_SYSTEM_MVK=ON \
|
||||
-DUSE_SYSTEM_FAUDIO=OFF \
|
||||
-DUSE_SYSTEM_SDL=ON \
|
||||
-DUSE_SYSTEM_OPENCV=ON \
|
||||
$CMAKE_EXTRA_OPTS \
|
||||
-DLLVM_TARGET_ARCH=X86_64 \
|
||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-G Ninja
|
||||
|
||||
"$BREW_PATH/bin/ninja"; build_status=$?;
|
||||
|
||||
cd ..
|
||||
|
||||
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
.ci/deploy-mac.sh
|
||||
fi
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
cd build || exit 1
|
||||
|
||||
CPU_ARCH="${1:-x86_64}"
|
||||
|
||||
if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
DESTDIR=AppDir ninja install
|
||||
|
||||
sudo curl -fsSLo /usr/bin/linuxdeploy "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$CPU_ARCH.AppImage"
|
||||
sudo chmod a+x /usr/bin/linuxdeploy
|
||||
sudo curl -fsSLo /usr/bin/linuxdeploy-plugin-qt "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-$CPU_ARCH.AppImage"
|
||||
sudo chmod a+x /usr/bin/linuxdeploy-plugin-qt
|
||||
curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh
|
||||
chmod +x ./linuxdeploy-plugin-checkrt.sh
|
||||
|
||||
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
|
||||
export EXTRA_QT_PLUGINS="svg;wayland-decoration-client;wayland-graphics-integration-client;wayland-shell-integration;waylandcompositor"
|
||||
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt --plugin checkrt
|
||||
|
||||
# Remove libwayland-client because it has platform-dependent exports and breaks other OSes
|
||||
rm -f ./AppDir/usr/lib/libwayland-client.so*
|
||||
|
||||
# Remove libvulkan because it causes issues with gamescope
|
||||
rm -f ./AppDir/usr/lib/libvulkan.so*
|
||||
|
||||
# Remove git directory containing local commit history file
|
||||
rm -rf ./AppDir/usr/share/rpcs3/git
|
||||
|
||||
linuxdeploy --appimage-extract
|
||||
./squashfs-root/plugins/linuxdeploy-plugin-appimage/usr/bin/appimagetool AppDir -g
|
||||
|
||||
mv ./*.AppImage ../RPCS3-Qt-UI.AppImage
|
||||
fi
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
cd build || exit 1
|
||||
|
||||
# Gather explicit version number and number of commits
|
||||
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp)
|
||||
COMM_COUNT=$(git rev-list --count HEAD)
|
||||
COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
|
||||
AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
|
||||
# AVVER is used for GitHub releases, it is the version number.
|
||||
echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
|
||||
|
||||
cd bin
|
||||
mkdir "rpcs3.app/Contents/lib/" || true
|
||||
|
||||
cp "$(realpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
|
||||
cp "$(realpath /opt/homebrew1/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib"
|
||||
cp "$(realpath /opt/homebrew1/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib"
|
||||
|
||||
rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQml.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQuick.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \
|
||||
"rpcs3.app/Contents/Plugins/platforminputcontexts" \
|
||||
"rpcs3.app/Contents/Plugins/virtualkeyboard" \
|
||||
"rpcs3.app/Contents/Resources/git"
|
||||
|
||||
../../.ci/optimize-mac.sh rpcs3.app
|
||||
|
||||
# Hack
|
||||
install_name_tool \
|
||||
-delete_rpath /opt/homebrew1/lib \
|
||||
-delete_rpath /opt/homebrew/lib \
|
||||
-delete_rpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib \
|
||||
-delete_rpath /usr/local/lib RPCS3.app/Contents/MacOS/rpcs3
|
||||
#-delete_rpath /opt/homebrew1/Cellar/sdl3/3.2.8/lib
|
||||
|
||||
# Need to do this rename hack due to case insensitive filesystem
|
||||
mv rpcs3.app RPCS3_.app
|
||||
mv RPCS3_.app RPCS3.app
|
||||
|
||||
# NOTE: "--deep" is deprecated
|
||||
codesign --deep -fs - RPCS3.app
|
||||
|
||||
echo "[InternetShortcut]" > Quickstart.url
|
||||
echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url
|
||||
echo "IconIndex=0" >> Quickstart.url
|
||||
|
||||
#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.dmg"
|
||||
#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \
|
||||
#--window-size 800 400 \
|
||||
#--icon-size 100 \
|
||||
#--icon rpcs3.app 200 190 \
|
||||
#--add-file Quickstart.url Quickstart.url 400 20 \
|
||||
#--hide-extension rpcs3.app \
|
||||
#--hide-extension Quickstart.url \
|
||||
#--app-drop-link 600 185 \
|
||||
#--skip-jenkins \
|
||||
#--format ULMO \
|
||||
#"$DMG_FILEPATH" \
|
||||
#RPCS3.app
|
||||
#FILESIZE=$(stat -f %z "$DMG_FILEPATH")
|
||||
#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }')
|
||||
|
||||
ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.7z"
|
||||
"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url
|
||||
FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH")
|
||||
SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }')
|
||||
|
||||
cd ..
|
||||
echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE"
|
||||
cd bin
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
cd build || exit 1
|
||||
|
||||
# Gather explicit version number and number of commits
|
||||
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp)
|
||||
COMM_COUNT=$(git rev-list --count HEAD)
|
||||
COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
|
||||
AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
|
||||
# AVVER is used for GitHub releases, it is the version number.
|
||||
echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
|
||||
|
||||
cd bin
|
||||
mkdir "rpcs3.app/Contents/lib/"
|
||||
|
||||
cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib"
|
||||
cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib"
|
||||
cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib"
|
||||
|
||||
rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQml.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQuick.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \
|
||||
"rpcs3.app/Contents/Plugins/platforminputcontexts" \
|
||||
"rpcs3.app/Contents/Plugins/virtualkeyboard" \
|
||||
"rpcs3.app/Contents/Resources/git"
|
||||
|
||||
../../.ci/optimize-mac.sh rpcs3.app
|
||||
|
||||
# Need to do this rename hack due to case insensitive filesystem
|
||||
mv rpcs3.app RPCS3_.app
|
||||
mv RPCS3_.app RPCS3.app
|
||||
|
||||
# Hack
|
||||
install_name_tool \
|
||||
-delete_rpath /usr/local/lib \
|
||||
-delete_rpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3
|
||||
#-delete_rpath /usr/local/Cellar/sdl3/3.2.8/lib
|
||||
|
||||
# NOTE: "--deep" is deprecated
|
||||
codesign --deep -fs - RPCS3.app
|
||||
|
||||
echo "[InternetShortcut]" > Quickstart.url
|
||||
echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url
|
||||
echo "IconIndex=0" >> Quickstart.url
|
||||
|
||||
#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.dmg"
|
||||
#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \
|
||||
#--window-size 800 400 \
|
||||
#--icon-size 100 \
|
||||
#--icon rpcs3.app 200 190 \
|
||||
#--add-file Quickstart.url Quickstart.url 400 20 \
|
||||
#--hide-extension rpcs3.app \
|
||||
#--hide-extension Quickstart.url \
|
||||
#--app-drop-link 600 185 \
|
||||
#--skip-jenkins \
|
||||
#--format ULMO \
|
||||
#"$DMG_FILEPATH" \
|
||||
#RPCS3.app
|
||||
#FILESIZE=$(stat -f %z "$DMG_FILEPATH")
|
||||
#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }')
|
||||
|
||||
ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.7z"
|
||||
"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url
|
||||
FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH")
|
||||
SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }')
|
||||
|
||||
cd ..
|
||||
echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE"
|
||||
cd bin
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# First let's see print some info about our caches
|
||||
"$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v
|
||||
|
||||
# Remove unecessary files
|
||||
rm -f ./rpcs3/bin/rpcs3.exp ./rpcs3/bin/rpcs3.lib ./rpcs3/bin/rpcs3.pdb ./rpcs3/bin/vc_redist.x64.exe
|
||||
rm -rf ./rpcs3/bin/git
|
||||
|
||||
# Prepare compatibility and SDL database for packaging
|
||||
mkdir -p ./rpcs3/bin/config/input_configs
|
||||
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./rpcs3/bin/config/input_configs/gamecontrollerdb.txt
|
||||
cp -rf ./build-msvc/bin/ ./rpcs3/bin/
|
||||
|
||||
# Package artifacts
|
||||
7z a -m0=LZMA2 -mx9 "$BUILD" ./rpcs3/bin/*
|
||||
|
||||
# Move files to publishing directory
|
||||
cp -- "$BUILD" ./RPCS3-Qt-UI.7z
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# Variables set by Azure Pipelines
|
||||
CI_HAS_ARTIFACTS
|
||||
BUILD_REASON
|
||||
BUILD_SOURCEVERSION
|
||||
BUILD_ARTIFACTSTAGINGDIRECTORY
|
||||
BUILD_REPOSITORY_NAME
|
||||
BUILD_SOURCEBRANCHNAME
|
||||
APPDIR
|
||||
ARTDIR
|
||||
RELEASE_MESSAGE
|
||||
# Variables for build matrix
|
||||
COMPILER
|
||||
DEPLOY_APPIMAGE
|
||||
# Private variables
|
||||
GITHUB_TOKEN
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
# Export variables for later stages of the Azure pipeline
|
||||
# Values done in this manner will appear as environment variables
|
||||
# in later stages.
|
||||
|
||||
# From pure-sh-bible
|
||||
# Setting 'IFS' tells 'read' where to split the string.
|
||||
while IFS='=' read -r key val; do
|
||||
# Skip over lines containing comments.
|
||||
[ "${key##\#*}" ] || continue
|
||||
echo "##vso[task.setvariable variable=$key]$val"
|
||||
done < ".ci/ci-vars.env"
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
# Export variables for later stages of the Cirrus pipeline
|
||||
# Values done in this manner will appear as environment variables
|
||||
# in later stages.
|
||||
|
||||
# From pure-sh-bible
|
||||
# Setting 'IFS' tells 'read' where to split the string.
|
||||
while IFS='=' read -r key val; do
|
||||
# Skip over lines containing comments.
|
||||
[ "${key##\#*}" ] || continue
|
||||
export "$key"="$val"
|
||||
done < ".ci/ci-vars.env"
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
mkdir -p ../translations
|
||||
|
||||
LUPDATE_PATH=$(find /usr -name lupdate -type f 2>/dev/null | head -n 1)
|
||||
if [ -z "$LUPDATE_PATH" ]; then
|
||||
echo "Error: lupdate not found!"
|
||||
exit 1
|
||||
else
|
||||
echo "lupdate found at: $LUPDATE_PATH"
|
||||
$LUPDATE_PATH -recursive . -ts ../translations/rpcs3_template.ts
|
||||
sed -i 's|filename="\.\./|filename="./|g' ../translations/rpcs3_template.ts
|
||||
fi
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
curl -fLo "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z.sha256"
|
||||
curl -fLo "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256"
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY"
|
||||
generate_post_data()
|
||||
{
|
||||
body=$(cat GitHubReleaseMessage.txt)
|
||||
cat <<EOF
|
||||
{
|
||||
"tag_name": "build-${BUILD_SOURCEVERSION}",
|
||||
"target_commitish": "${UPLOAD_COMMIT_HASH}",
|
||||
"name": "${AVVER}",
|
||||
"body": "$body",
|
||||
"draft": false,
|
||||
"prerelease": false
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
curl -fsS \
|
||||
-H "Authorization: token ${RPCS3_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
--data "$(generate_post_data)" "https://api.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases" >> release.json
|
||||
|
||||
cat release.json
|
||||
id=$(grep '"id"' release.json | cut -d ':' -f2 | head -n1 | awk '{$1=$1;print}')
|
||||
id=${id%?}
|
||||
echo "${id:?}"
|
||||
|
||||
upload_file()
|
||||
{
|
||||
curl -fsS \
|
||||
-H "Authorization: token ${RPCS3_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"$2"/"$3" \
|
||||
"https://uploads.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases/$1/assets?name=$3"
|
||||
}
|
||||
|
||||
for file in "$ARTIFACT_DIR"/*; do
|
||||
name=$(basename "$file")
|
||||
upload_file "$id" "$ARTIFACT_DIR" "$name"
|
||||
done
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env -S su -m root -ex
|
||||
# NOTE: this script is run under root permissions
|
||||
# shellcheck shell=sh disable=SC2096
|
||||
|
||||
# RPCS3 often needs recent Qt and Vulkan-Headers
|
||||
sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||
|
||||
export ASSUME_ALWAYS_YES=true
|
||||
pkg info # debug
|
||||
|
||||
# WITH_LLVM
|
||||
pkg install "llvm$LLVM_COMPILER_VER"
|
||||
|
||||
# Mandatory dependencies (qtX-base is pulled via qtX-multimedia)
|
||||
pkg install git ccache cmake ninja "qt$QT_VER_MAIN-multimedia" "qt$QT_VER_MAIN-svg" glew openal-soft ffmpeg
|
||||
|
||||
# Optional dependencies (libevdev is pulled by qtX-base)
|
||||
pkg install pkgconf alsa-lib pulseaudio sdl3 evdev-proto vulkan-headers vulkan-loader
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
file_path=$(find "$1/Contents/MacOS" -type f -print0 | head -n 1)
|
||||
|
||||
if [ -z "$file_path" ]; then
|
||||
echo "No executable file found in $1/Contents/MacOS" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
target_architecture="$(lipo "$file_path" -archs)"
|
||||
|
||||
if [ -z "$target_architecture" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC3045
|
||||
find "$1" -type f -print0 | while IFS= read -r -d '' file; do
|
||||
echo Thinning "$file" -> "$target_architecture"
|
||||
lipo "$file" -thin "$target_architecture" -output "$file" || true
|
||||
done
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# These are Azure specific, so we wrap them for portability
|
||||
REPO_NAME="$BUILD_REPOSITORY_NAME"
|
||||
REPO_BRANCH="$SYSTEM_PULLREQUEST_SOURCEBRANCH"
|
||||
PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID"
|
||||
|
||||
# Resource/dependency URLs
|
||||
# Qt mirrors can be volatile and slow, so we list 2
|
||||
#QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
|
||||
QT_HOST="http://qt.mirror.constant.com/"
|
||||
QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g")
|
||||
QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]')
|
||||
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}."
|
||||
QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}"
|
||||
QT_SUFFIX="-Windows-Windows_11_23H2-${QT_VER_MSVC_UP}-Windows-Windows_11_23H2-X86_64.7z"
|
||||
QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}"
|
||||
QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}"
|
||||
QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}"
|
||||
QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}"
|
||||
QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
|
||||
# LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z"
|
||||
# GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z'
|
||||
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
|
||||
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip"
|
||||
|
||||
DEP_URLS=" \
|
||||
$QT_BASE_URL \
|
||||
$QT_DECL_URL \
|
||||
$QT_TOOL_URL \
|
||||
$QT_MM_URL \
|
||||
$QT_SVG_URL \
|
||||
$LLVMLIBS_URL \
|
||||
$GLSLANG_URL \
|
||||
$VULKAN_SDK_URL\
|
||||
$CCACHE_URL"
|
||||
|
||||
# Azure pipelines doesn't make a cache dir if it doesn't exist, so we do it manually
|
||||
[ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR"
|
||||
|
||||
# Pull all the submodules except llvm, since it is built separately and we just download that build
|
||||
# Note: Tried to use git submodule status, but it takes over 20 seconds
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/FAudio/ { print $3 }' .gitmodules)
|
||||
|
||||
# Git bash doesn't have rev, so here it is
|
||||
rev()
|
||||
{
|
||||
echo "$1" | awk '{ for(i = length($0); i != 0; --i) { a = a substr($0, i, 1); } } END { print a }'
|
||||
}
|
||||
|
||||
# Usage: download_and_verify url checksum algo file
|
||||
# Check to see if a file is already cached, and the checksum matches. If not, download it.
|
||||
# Tries up to 3 times
|
||||
download_and_verify()
|
||||
{
|
||||
url="$1"
|
||||
correctChecksum="$2"
|
||||
algo="$3"
|
||||
fileName="$4"
|
||||
|
||||
for _ in 1 2 3; do
|
||||
[ -e "$DEPS_CACHE_DIR/$fileName" ] || curl -fLo "$DEPS_CACHE_DIR/$fileName" "$url"
|
||||
fileChecksum=$("${algo}sum" "$DEPS_CACHE_DIR/$fileName" | awk '{ print $1 }')
|
||||
[ "$fileChecksum" = "$correctChecksum" ] && return 0
|
||||
done
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Some dependencies install here
|
||||
[ -d "./rpcs3/build/lib_ext/Release-x64" ] || mkdir -p "./rpcs3/build/lib_ext/Release-x64"
|
||||
|
||||
for url in $DEP_URLS; do
|
||||
# Get the filename from the URL and remove query strings (?arg=something).
|
||||
fileName="$(rev "$(rev "$url" | cut -d'/' -f1)" | cut -d'?' -f1)"
|
||||
[ -z "$fileName" ] && echo "Unable to parse url: $url" && exit 1
|
||||
|
||||
# shellcheck disable=SC1003
|
||||
case "$url" in
|
||||
*qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir="$QTDIR/" ;;
|
||||
*llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./rpcs3/build/lib_ext/Release-x64" ;;
|
||||
*glslang*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./rpcs3/build/lib_ext/Release-x64" ;;
|
||||
*ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;;
|
||||
*Vulkan*)
|
||||
# Vulkan setup needs to be run in batch environment
|
||||
# Need to subshell this or else it doesn't wait
|
||||
download_and_verify "$url" "$VULKAN_SDK_SHA" "sha256" "$fileName"
|
||||
cp "$DEPS_CACHE_DIR/$fileName" .
|
||||
_=$(echo "$fileName --accept-licenses --default-answer --confirm-command install" | cmd)
|
||||
continue
|
||||
;;
|
||||
*) echo "Unknown url resource: $url"; exit 1 ;;
|
||||
esac
|
||||
|
||||
download_and_verify "$url" "$checksum" "$algo" "$fileName"
|
||||
7z x -y "$DEPS_CACHE_DIR/$fileName" -aos -o"$outDir"
|
||||
done
|
||||
|
||||
# Setup ccache tool
|
||||
[ -d "$CCACHE_DIR" ] || mkdir -p "$(cygpath -u "$CCACHE_DIR")"
|
||||
CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR")
|
||||
mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR"
|
||||
cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe
|
||||
|
||||
# Gather explicit version number and number of commits
|
||||
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
|
||||
COMM_COUNT=$(git rev-list --count HEAD)
|
||||
COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
|
||||
# Format the above into filenames
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
AVVER="${COMM_TAG}-${COMM_HASH}"
|
||||
BUILD_RAW="rpcs3-v${AVVER}_win64"
|
||||
BUILD="${BUILD_RAW}.7z"
|
||||
else
|
||||
AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_win64"
|
||||
BUILD="${BUILD_RAW}.7z"
|
||||
fi
|
||||
|
||||
# BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp.
|
||||
# BUILD is the name of the release artifact
|
||||
# BUILD_RAW is just filename
|
||||
# AVVER is used for GitHub releases, it is the version number.
|
||||
BRANCH="${REPO_NAME}/${REPO_BRANCH}"
|
||||
|
||||
# SC2129
|
||||
{
|
||||
echo "BRANCH=$BRANCH"
|
||||
echo "BUILD=$BUILD"
|
||||
echo "BUILD_RAW=$BUILD_RAW"
|
||||
echo "AVVER=$AVVER"
|
||||
} >> .ci/ci-vars.env
|
||||
6
.github/BUILDING.md
vendored
6
.github/BUILDING.md
vendored
|
|
@ -3,19 +3,19 @@
|
|||
|
||||
### The dependencies for Debian-like distributions.
|
||||
```
|
||||
sudo apt install build-essential cmake libunwind-dev libglfw3-dev libvulkan-dev libsox-dev git libasound2-dev nasm g++-14
|
||||
sudo apt install build-essential cmake libunwind-dev libsox-dev git libasound2-dev nasm g++-14
|
||||
```
|
||||
|
||||
### The dependencies for Fedora distributions:
|
||||
|
||||
```
|
||||
sudo dnf install cmake libunwind-devel glfw-devel vulkan-devel gcc-c++ gcc sox-devel alsa-lib-devel nasm
|
||||
sudo dnf install cmake libunwind-devel gcc-c++ gcc sox-devel alsa-lib-devel nasm
|
||||
```
|
||||
|
||||
### The dependencies for Arch distributions:
|
||||
|
||||
```
|
||||
sudo pacman -S libunwind glfw-x11 vulkan-devel sox git cmake alsa-lib nasm
|
||||
sudo pacman -S libunwind sox git cmake alsa-lib nasm
|
||||
```
|
||||
|
||||
## Cloning the Repo
|
||||
|
|
|
|||
222
.github/workflows/rpcs3.yml
vendored
222
.github/workflows/rpcs3.yml
vendored
|
|
@ -1,222 +0,0 @@
|
|||
name: Build RPCS3 Qt UI (Legacy)
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "**/*.md"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**/*.md"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
BUILD_REPOSITORY_NAME: ${{ github.repository }}
|
||||
BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }}
|
||||
BUILD_SOURCEVERSION: ${{ github.sha }}
|
||||
BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/
|
||||
|
||||
jobs:
|
||||
Linux_Build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
build_sh: ".ci/build-linux.sh"
|
||||
compiler: clang
|
||||
- os: ubuntu-24.04
|
||||
build_sh: ".ci/build-linux.sh"
|
||||
compiler: gcc
|
||||
- os: ubuntu-24.04-arm
|
||||
build_sh: ".ci/build-linux-aarch64.sh"
|
||||
compiler: clang
|
||||
name: RPCS3 Qt UI (Legacy) for Linux ${{ matrix.os }} ${{ matrix.compiler }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/ccache
|
||||
CI_HAS_ARTIFACTS: true
|
||||
DEPLOY_APPIMAGE: true
|
||||
APPDIR: "./appdir"
|
||||
ARTDIR: "./artifacts"
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
RX_VERSION: "Unknown"
|
||||
RX_SHA: "Unknown"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: actions/cache@main
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{github.run_id}}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-
|
||||
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
echo "Types: deb" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
echo "URIs: ${{ matrix.os == 'ubuntu-24.04-arm' && 'http://ports.ubuntu.com/ubuntu-ports' || 'http://azure.archive.ubuntu.com/ubuntu/' }}" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
echo "Suites: plucky plucky-updates plucky-security" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
echo "Components: main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
echo "Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y cmake build-essential libunwind-dev \
|
||||
libvulkan-dev vulkan-validationlayers \
|
||||
libsox-dev ninja-build libasound2-dev libglfw3-dev nasm libudev-dev \
|
||||
libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev \
|
||||
libevdev-dev libjack-dev libsndio-dev libglvnd-dev \
|
||||
qt6-base-dev qt6-svg-dev qt6-base-private-dev qt6-multimedia-dev \
|
||||
clang lld gcc-14 g++-14 \
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
${{ matrix.build_sh }}
|
||||
|
||||
RX_VERSION=`cat .rx.version | awk -F'-' '{print $1}'`
|
||||
RX_SHA=`cat .rx.version | awk -F'-' '{print $5}'`
|
||||
|
||||
echo "RX_VERSION=$RX_VERSION" >> "${{ github.env }}"
|
||||
echo "RX_SHA=$RX_SHA" >> "${{ github.env }}"
|
||||
mv RPCS3-Qt-UI.AppImage RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: RPCS3 Qt UI (Legacy) for Linux (${{ runner.arch }}, ${{ matrix.compiler }})
|
||||
path: RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage
|
||||
compression-level: 0
|
||||
|
||||
- name: Deploy build
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: |
|
||||
github.event_name != 'pull_request' &&
|
||||
github.ref == 'refs/heads/master' &&
|
||||
github.repository == 'RPCSX/rpcsx'
|
||||
with:
|
||||
prerelease: false
|
||||
make_latest: true
|
||||
repository: RPCSX/rpcsx-build
|
||||
token: ${{ secrets.BUILD_TOKEN }}
|
||||
tag_name: v${{ env.RX_VERSION }}-${{ env.RX_SHA }}
|
||||
files: RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage
|
||||
body: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
|
||||
|
||||
|
||||
Windows_Build:
|
||||
name: RPCS3 Qt UI (Legacy) for Windows
|
||||
runs-on: windows-2025
|
||||
env:
|
||||
COMPILER: msvc
|
||||
QT_VER_MAIN: '6'
|
||||
QT_VER: '6.8.3'
|
||||
QT_VER_MSVC: 'msvc2022'
|
||||
QT_DATE: '202503201308'
|
||||
LLVM_VER: '19.1.7'
|
||||
VULKAN_VER: '1.3.268.0'
|
||||
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c'
|
||||
CCACHE_BIN_DIR: 'C:\ccache_bin'
|
||||
CCACHE_DIR: 'C:\ccache'
|
||||
CCACHE_INODECACHE: 'true'
|
||||
CCACHE_SLOPPINESS: 'time_macros'
|
||||
DEPS_CACHE_DIR: ./dependency_cache
|
||||
RX_VERSION: 'Unknown'
|
||||
RX_SHA: 'Unknown'
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup env
|
||||
run: |
|
||||
echo "QTDIR=C:\Qt\${{ env.QT_VER }}\${{ env.QT_VER_MSVC }}_64" >> ${{ github.env }}
|
||||
echo "VULKAN_SDK=C:\VulkanSDK\${{ env.VULKAN_VER }}" >> ${{ github.env }}
|
||||
|
||||
# - name: Get Cache Keys
|
||||
# run: .ci/get_keys-windows.sh
|
||||
|
||||
# - name: Setup Build Ccache
|
||||
# uses: actions/cache@main
|
||||
# with:
|
||||
# path: ${{ env.CCACHE_DIR }}
|
||||
# key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}"
|
||||
# restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}-
|
||||
|
||||
# - name: Setup Dependencies Cache
|
||||
# uses: actions/cache@main
|
||||
# with:
|
||||
# path: ${{ env.DEPS_CACHE_DIR }}
|
||||
# key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}-${{ hashFiles('glslang.lock') }}"
|
||||
# restore-keys: ${{ runner.os }}-${{ env.COMPILER }}-
|
||||
|
||||
- name: Download and unpack dependencies
|
||||
run: .ci/setup-windows.sh
|
||||
|
||||
- name: Export Variables
|
||||
run: |
|
||||
while IFS='=' read -r key val; do
|
||||
# Skip lines that are empty or start with '#'
|
||||
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
||||
echo "$key=$val" >> "${{ github.env }}"
|
||||
done < .ci/ci-vars.env
|
||||
|
||||
# - name: Add msbuild to PATH
|
||||
# uses: microsoft/setup-msbuild@main
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -B build-msvc -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DWITH_RPCSX=off -DWITH_RPCS3=on -DWITH_RPCS3_QT_UI=on -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DCMAKE_INSTALL_PREFIX="${sourceDir}/out/install/${presetName}" -DUSE_NATIVE_INSTRUCTIONS=on -DUSE_PRECOMPILED_HEADERS=on -DUSE_FAUDIO=off -DUSE_SYSTEM_CURL=off -DUSE_SYSTEM_ZLIB=off -DUSE_SYSTEM_OPENAL=off -DUSE_SYSTEM_OPENCV=off -DBUILD_LLVM=on -DSTATIC_LINK_LLVM=on
|
||||
|
||||
- name: Export Version
|
||||
run: |
|
||||
RX_VERSION=`cat .rx.version | awk -F'-' '{print $1}'`
|
||||
RX_SHA=`cat .rx.version | awk -F'-' '{print $5}'`
|
||||
echo "RX_VERSION=$RX_VERSION" >> "${{ github.env }}"
|
||||
echo "RX_SHA=$RX_SHA" >> "${{ github.env }}"
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake --build build-msvc --config Release
|
||||
|
||||
|
||||
- name: Pack up build artifacts
|
||||
run: |
|
||||
mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}"
|
||||
.ci/deploy-windows.sh
|
||||
mv RPCS3-Qt-UI.7z RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z
|
||||
|
||||
- name: Upload artifacts (7z)
|
||||
uses: actions/upload-artifact@main
|
||||
with:
|
||||
name: RPCS3 Qt UI (Legacy) for Windows (MSVC)
|
||||
path: RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z
|
||||
compression-level: 0
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Deploy build
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: |
|
||||
github.event_name != 'pull_request' &&
|
||||
github.ref == 'refs/heads/master' &&
|
||||
github.repository == 'RPCSX/rpcsx'
|
||||
with:
|
||||
prerelease: false
|
||||
make_latest: true
|
||||
repository: RPCSX/rpcsx-build
|
||||
token: ${{ secrets.BUILD_TOKEN }}
|
||||
tag_name: v${{ env.RX_VERSION }}-${{ env.RX_SHA }}
|
||||
files: RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z
|
||||
body: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
|
||||
5
.github/workflows/rpcsx.yml
vendored
5
.github/workflows/rpcsx.yml
vendored
|
|
@ -23,8 +23,9 @@ jobs:
|
|||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y cmake build-essential libunwind-dev \
|
||||
libvulkan-dev vulkan-validationlayers \
|
||||
libsox-dev g++-14 ninja-build libasound2-dev libglfw3-dev nasm libudev-dev
|
||||
libsox-dev g++-14 ninja-build libasound2-dev nasm libudev-dev \
|
||||
libxcb1-dev libx11-dev libwayland-dev libxkbcommon-dev libxrandr-dev \
|
||||
libxinerama-dev libxcursor-dev libxi-dev libxext-dev
|
||||
cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_INSTALL_PREFIX=/usr
|
||||
cmake --build build -j$(($(nproc) + 2))
|
||||
sudo cmake --build build
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -61,3 +61,6 @@ build*/
|
|||
.cache/
|
||||
rpcs3/git-version.h
|
||||
.rx.version
|
||||
|
||||
log-*.txt
|
||||
tty.txt
|
||||
|
|
|
|||
9
.gitmodules
vendored
9
.gitmodules
vendored
|
|
@ -131,3 +131,12 @@
|
|||
[submodule "3rdparty/Vulkan-Headers"]
|
||||
path = 3rdparty/Vulkan-Headers
|
||||
url = ../../KhronosGroup/Vulkan-Headers.git
|
||||
[submodule "3rdparty/fmtlib"]
|
||||
path = 3rdparty/fmtlib
|
||||
url = ../../fmtlib/fmt.git
|
||||
[submodule "3rdparty/Vulkan-Loader"]
|
||||
path = 3rdparty/Vulkan-Loader
|
||||
url = ../../KhronosGroup/Vulkan-Loader.git
|
||||
[submodule "3rdparty/glfw"]
|
||||
path = 3rdparty/glfw
|
||||
url = ../../glfw/glfw.git
|
||||
|
|
|
|||
167
3rdparty/CMakeLists.txt
vendored
167
3rdparty/CMakeLists.txt
vendored
|
|
@ -235,60 +235,80 @@ endif()
|
|||
|
||||
# Vulkan
|
||||
set(VULKAN_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_VULKAN)
|
||||
if(APPLE)
|
||||
if(USE_SYSTEM_MVK)
|
||||
message(STATUS "RPCS3: Using system MoltenVK")
|
||||
else()
|
||||
message(STATUS "RPCS3: MoltenVK submodule")
|
||||
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK"
|
||||
)
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" --build .
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK"
|
||||
)
|
||||
|
||||
add_library(moltenvk_lib SHARED IMPORTED)
|
||||
add_dependencies(moltenvk_lib moltenvk)
|
||||
set_target_properties(moltenvk_lib
|
||||
PROPERTIES IMPORTED_LOCATION "{Vulkan_LIBRARY}"
|
||||
)
|
||||
|
||||
set(VULKAN_SDK "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK")
|
||||
set(VK_ICD_FILENAMES "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json")
|
||||
set(Vulkan_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK/include")
|
||||
set(Vulkan_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/Build/Products/Release/dynamic/libMoltenVK.dylib")
|
||||
set(Vulkan_TOOLS "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/Build/Products/Release")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Vulkan)
|
||||
if(VULKAN_FOUND)
|
||||
add_library(3rdparty_vulkan INTERFACE)
|
||||
target_compile_definitions(3rdparty_vulkan INTERFACE -DHAVE_VULKAN)
|
||||
target_link_libraries(3rdparty_vulkan INTERFACE Vulkan::Vulkan)
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
find_package(Wayland)
|
||||
if (WAYLAND_FOUND)
|
||||
target_include_directories(3rdparty_vulkan
|
||||
INTERFACE ${WAYLAND_INCLUDE_DIR})
|
||||
|
||||
target_compile_definitions(3rdparty_vulkan
|
||||
INTERFACE -DVK_USE_PLATFORM_WAYLAND_KHR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(VULKAN_TARGET 3rdparty_vulkan)
|
||||
if(APPLE)
|
||||
if(USE_SYSTEM_MVK)
|
||||
message(STATUS "Using system MoltenVK")
|
||||
else()
|
||||
message(WARNING "USE_VULKAN was enabled, but libvulkan was not found. RPCS3 will be compiled without Vulkan support.")
|
||||
if(APPLE)
|
||||
message(FATAL_ERROR "To build without Vulkan support on macOS, please disable USE_VULKAN.")
|
||||
endif()
|
||||
message(STATUS "MoltenVK submodule")
|
||||
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK"
|
||||
)
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" --build .
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK"
|
||||
)
|
||||
|
||||
add_library(moltenvk_lib SHARED IMPORTED)
|
||||
add_dependencies(moltenvk_lib moltenvk)
|
||||
set_target_properties(moltenvk_lib
|
||||
PROPERTIES IMPORTED_LOCATION "{Vulkan_LIBRARY}"
|
||||
)
|
||||
|
||||
set(VULKAN_SDK "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK")
|
||||
set(VK_ICD_FILENAMES "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json")
|
||||
set(Vulkan_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK/include")
|
||||
set(Vulkan_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/Build/Products/Release/dynamic/libMoltenVK.dylib")
|
||||
set(Vulkan_TOOLS "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/Build/Products/Release")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(3rdparty_vulkan INTERFACE)
|
||||
target_compile_definitions(3rdparty_vulkan INTERFACE -DHAVE_VULKAN)
|
||||
|
||||
if (NOT COMPILE_VULKAN_LOADER OR ANDROID OR APPLE)
|
||||
find_package(Vulkan 1.3 GLOBAL)
|
||||
|
||||
if (Vulkan_FOUND)
|
||||
message(STATUS "Using system Vulkan Loader")
|
||||
target_link_libraries(3rdparty_vulkan INTERFACE Vulkan::Vulkan)
|
||||
add_library(Vulkan::Loader ALIAS Vulkan::Vulkan)
|
||||
else()
|
||||
message(WARNING "Vulkan not found, using submodule")
|
||||
set(COMPILE_VULKAN_LOADER on)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (COMPILE_VULKAN_LOADER)
|
||||
find_package(xcb)
|
||||
if (NOT xcb_FOUND)
|
||||
message(WARNING "Compiling Vulkan-Loader without XCB support")
|
||||
set(BUILD_WSI_XCB_SUPPORT off)
|
||||
endif()
|
||||
|
||||
find_package(x11)
|
||||
if (NOT x11_FOUND)
|
||||
message(WARNING "Compiling Vulkan-Loader without X11 support")
|
||||
set(BUILD_WSI_XLIB_SUPPORT off)
|
||||
endif()
|
||||
|
||||
add_subdirectory(Vulkan-Loader)
|
||||
target_link_libraries(3rdparty_vulkan INTERFACE Vulkan::Loader)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
find_package(Wayland)
|
||||
|
||||
if (WAYLAND_FOUND)
|
||||
target_include_directories(3rdparty_vulkan
|
||||
INTERFACE ${WAYLAND_INCLUDE_DIR})
|
||||
|
||||
target_compile_definitions(3rdparty_vulkan
|
||||
INTERFACE -DVK_USE_PLATFORM_WAYLAND_KHR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(VULKAN_TARGET 3rdparty_vulkan)
|
||||
|
||||
# AsmJit
|
||||
add_subdirectory(asmjit EXCLUDE_FROM_ALL)
|
||||
|
||||
|
|
@ -308,11 +328,11 @@ if(USE_FAUDIO)
|
|||
if (USE_SYSTEM_FAUDIO)
|
||||
if (NOT SDL3_FOUND OR SDL3_VERSION VERSION_LESS 3.2.0)
|
||||
message(WARNING
|
||||
"RPCS3: System FAudio requires SDL 3.2.0 or newer. Since a valid SDL3"
|
||||
"System FAudio requires SDL 3.2.0 or newer. Since a valid SDL3"
|
||||
">=3.2.0 version cannot be found, building with FAudio will be skipped.")
|
||||
set(USE_FAUDIO OFF CACHE BOOL "Disabled using system FAudio with SDL < 3.2.0" FORCE)
|
||||
else()
|
||||
message(STATUS "RPCS3: Using system FAudio")
|
||||
message(STATUS "Using system FAudio")
|
||||
find_package(FAudio REQUIRED CONFIGS FAudioConfig.cmake FAudio-config.cmake)
|
||||
add_library(3rdparty_FAudio INTERFACE)
|
||||
target_link_libraries(3rdparty_FAudio INTERFACE FAudio)
|
||||
|
|
@ -322,11 +342,11 @@ if(USE_FAUDIO)
|
|||
else()
|
||||
if (NOT SDL3_FOUND OR SDL3_VERSION VERSION_LESS 3.2.0)
|
||||
message(WARNING
|
||||
"-- RPCS3: 3rdparty FAudio requires SDL 3.2.0 or newer. Since a valid SDL3"
|
||||
"-- 3rdparty FAudio requires SDL 3.2.0 or newer. Since a valid SDL3"
|
||||
">=3.2.0 version cannot be found, building with FAudio will be skipped.")
|
||||
set(USE_FAUDIO OFF CACHE BOOL "Disabled FAudio with SDL < 3.2.0" FORCE)
|
||||
else()
|
||||
message(STATUS "RPCS3: Using builtin FAudio")
|
||||
message(STATUS "Using builtin FAudio")
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared library")
|
||||
add_subdirectory(FAudio EXCLUDE_FROM_ALL)
|
||||
target_compile_definitions(FAudio-static INTERFACE -DHAVE_FAUDIO)
|
||||
|
|
@ -391,6 +411,7 @@ add_subdirectory(opencv EXCLUDE_FROM_ALL)
|
|||
# FUSION
|
||||
add_subdirectory(fusion EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(fmtlib)
|
||||
|
||||
# add nice ALIAS targets for ease of use
|
||||
if(USE_SYSTEM_LIBUSB)
|
||||
|
|
@ -399,6 +420,44 @@ else()
|
|||
add_library(3rdparty::libusb ALIAS usb-1.0-static)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
add_library(glfw INTERFACE)
|
||||
target_compile_definitions(glfw INTERFACE WITHOUT_GLFW)
|
||||
add_library(3rdparty::glfw ALIAS glfw)
|
||||
else()
|
||||
if (NOT COMPILE_GLFW)
|
||||
find_package(glfw3 3.3 GLOBAL)
|
||||
if (glfw3_FOUND)
|
||||
message(STATUS "Using system glfw")
|
||||
else()
|
||||
message(WARNING "glfw not found, using submodule")
|
||||
set(COMPILE_GLFW on)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (COMPILE_GLFW)
|
||||
set(GLFW_BUILD_DOCS off)
|
||||
set(GLFW_INSTALL off)
|
||||
|
||||
if (WIN32)
|
||||
set(GLFW_BUILD_WIN32 on)
|
||||
elseif(LINUX)
|
||||
set(GLFW_BUILD_WAYLAND on)
|
||||
set(GLFW_BUILD_X11 on)
|
||||
endif()
|
||||
|
||||
add_subdirectory(glfw)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET glfw)
|
||||
message(FATAL_ERROR "where is glfw?")
|
||||
endif()
|
||||
|
||||
|
||||
add_library(3rdparty::glfw ALIAS glfw)
|
||||
endif()
|
||||
|
||||
|
||||
add_library(3rdparty::zlib ALIAS 3rdparty_zlib)
|
||||
add_library(3rdparty::zstd ALIAS 3rdparty_zstd)
|
||||
add_library(3rdparty::7zip ALIAS 3rdparty_7zip)
|
||||
|
|
|
|||
2
3rdparty/Vulkan-Headers
vendored
2
3rdparty/Vulkan-Headers
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 5ceb9ed481e58e705d0d9b5326537daedd06b97d
|
||||
Subproject commit a4f8ada9f4f97c45b8c89c57997be9cebaae65d2
|
||||
1
3rdparty/Vulkan-Loader
vendored
Submodule
1
3rdparty/Vulkan-Loader
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit ae0461b671558197a9a50e5fcfcc3b2d3f406b42
|
||||
1
3rdparty/fmtlib
vendored
Submodule
1
3rdparty/fmtlib
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 486e7ba579a2c677772d004ecd0311142ba481be
|
||||
1
3rdparty/glfw
vendored
Submodule
1
3rdparty/glfw
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8e15281d34a8b9ee9271ccce38177a3d812456f8
|
||||
2
3rdparty/hidapi/hidapi
vendored
2
3rdparty/hidapi/hidapi
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 6bfdcf7368169efe1b745cd4468d45cda05ef8de
|
||||
Subproject commit f42423643ec9011c98cccc0bb790722bbbd3f30b
|
||||
3
3rdparty/llvm/CMakeLists.txt
vendored
3
3rdparty/llvm/CMakeLists.txt
vendored
|
|
@ -185,12 +185,13 @@ if(WITH_LLVM)
|
|||
message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \
|
||||
Enable BUILD_LLVM option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
|
||||
if (LLVM_VERSION VERSION_LESS 18)
|
||||
message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 18 or above.")
|
||||
endif()
|
||||
|
||||
if (NOT MLIR_FOUND)
|
||||
message(FATAL_ERROR "Can't find MLIR libraries from the CMAKE_PREFIX_PATH path or MLIR_DIR")
|
||||
message(FATAL_ERROR "Can't find MLIR libraries from the CMAKE_PREFIX_PATH path or MLIR_DIR: ${MLIR_DIR}")
|
||||
endif()
|
||||
|
||||
|
||||
|
|
|
|||
199
CMakeLists.txt
199
CMakeLists.txt
|
|
@ -1,9 +1,142 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
project(rpcsx)
|
||||
|
||||
option(WITH_RPCSX "Enable RPCSX" ON)
|
||||
option(WITH_RPCS3 "Enable RPCS3" OFF)
|
||||
option(WITH_RPCS3_QT_UI "Enable RPCS3 UI" OFF)
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND NOT ${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows" AND ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||
message(STATUS "!!! Cross compilation for Windows !!!")
|
||||
add_link_options(-fuse-ld=lld -static-libgcc -static-libstdc++)
|
||||
else()
|
||||
message(FATAL "Cross compilation for Windows is supported with Clang compiler only")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
function(rpcsx_setup_crosscompiling)
|
||||
add_custom_command(
|
||||
OUTPUT "${RPCSX_NATIVE_ROOT_DIR}/CMakeCache.txt"
|
||||
COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
|
||||
-B ${RPCSX_NATIVE_ROOT_DIR}
|
||||
-DCMAKE_MAKE_PROGRAM="${CMAKE_MAKE_PROGRAM}"
|
||||
-DCMAKE_C_COMPILER_LAUNCHER="${CMAKE_C_COMPILER_LAUNCHER}"
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER="${CMAKE_CXX_COMPILER_LAUNCHER}"
|
||||
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY="${RPCSX_NATIVE_ROOT_DIR}/bin"
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Configuring native project..."
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
unset(CMAKE_EXECUTABLE_SUFFIX)
|
||||
|
||||
include(Platform/${CMAKE_HOST_SYSTEM_NAME} OPTIONAL RESULT_VARIABLE platformIncluded)
|
||||
if (platformIncluded)
|
||||
set(RPCSX_HOST_EXECUTABLE_SUFFIX ${CMAKE_EXECUTABLE_SUFFIX} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
set(RPCSX_NATIVE_ROOT_DIR ${CMAKE_BINARY_DIR}/native)
|
||||
rpcsx_setup_crosscompiling()
|
||||
add_custom_target(RPCSX_CONFIGURE_NATIVE DEPENDS "${RPCSX_NATIVE_ROOT_DIR}/CMakeCache.txt")
|
||||
endif()
|
||||
|
||||
function(import_native_executable_target target)
|
||||
set(options)
|
||||
set(one_value_keywords EXECUTABLE)
|
||||
set(multi_value_keywords)
|
||||
|
||||
cmake_parse_arguments(
|
||||
arg
|
||||
"${options}"
|
||||
"${one_value_keywords}"
|
||||
"${multi_value_keywords}"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
if (NOT DEFINED arg_EXECUTABLE)
|
||||
set(arg_EXECUTABLE ${target})
|
||||
endif()
|
||||
set(binary_path "${RPCSX_NATIVE_ROOT_DIR}/bin/${arg_EXECUTABLE}${RPCSX_HOST_EXECUTABLE_SUFFIX}")
|
||||
set(${target}_BINARY_PATH "${binary_path}" PARENT_SCOPE)
|
||||
|
||||
if (NOT TARGET ${target}_NATIVE)
|
||||
if (CMAKE_GENERATOR MATCHES "Make")
|
||||
set(build_command "$(MAKE)" "-C" "${RPCSX_NATIVE_ROOT_DIR}" "${target}")
|
||||
else()
|
||||
set(build_command ${CMAKE_COMMAND} --build ${bin_dir} --target ${target} --parallel)
|
||||
endif()
|
||||
|
||||
add_custom_command(OUTPUT "${binary_path}"
|
||||
COMMAND ${build_command}
|
||||
DEPENDS RPCSX_CONFIGURE_NATIVE
|
||||
WORKING_DIRECTORY "${RPCSX_NATIVE_ROOT_DIR}"
|
||||
COMMENT "Building native ${target}..."
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(${target}_NATIVE DEPENDS ${binary_path})
|
||||
endif()
|
||||
else()
|
||||
set(${target}_BINARY_PATH "$<TARGET_FILE:${target}>" PARENT_SCOPE)
|
||||
|
||||
if (NOT TARGET ${target}_NATIVE)
|
||||
add_custom_target(${target}_NATIVE)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(add_precompiled_vulkan_spirv target)
|
||||
import_native_executable_target(glslang-standalone EXECUTABLE glslang)
|
||||
|
||||
add_library(${target} INTERFACE)
|
||||
set(SPIRV_GEN_ROOT_DIR "spirv-gen/include/")
|
||||
set(SPIRV_GEN_DIR "${SPIRV_GEN_ROOT_DIR}/shaders")
|
||||
|
||||
cmake_path(ABSOLUTE_PATH SPIRV_GEN_ROOT_DIR BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE outputrootdir)
|
||||
cmake_path(ABSOLUTE_PATH SPIRV_GEN_DIR BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE outputdir)
|
||||
file(MAKE_DIRECTORY ${outputrootdir})
|
||||
file(MAKE_DIRECTORY ${outputdir})
|
||||
target_include_directories(${target} INTERFACE ${outputrootdir})
|
||||
|
||||
foreach(input IN LISTS ARGN)
|
||||
cmake_path(GET input FILENAME inputname)
|
||||
cmake_path(REPLACE_EXTENSION inputname LAST_ONLY .h OUTPUT_VARIABLE outputname)
|
||||
cmake_path(APPEND outputdir ${outputname} OUTPUT_VARIABLE outputpath)
|
||||
cmake_path(REMOVE_EXTENSION inputname LAST_ONLY OUTPUT_VARIABLE varname)
|
||||
|
||||
string(REPLACE "." "_" varname ${varname})
|
||||
string(PREPEND varname "spirv_")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${outputpath}
|
||||
COMMAND ${glslang-standalone_BINARY_PATH} -V --target-env vulkan1.2 --vn "${varname}" -o "${outputpath}" "${CMAKE_CURRENT_SOURCE_DIR}/${input}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${input}" glslang-standalone_NATIVE
|
||||
COMMENT "Generating ${outputname}..."
|
||||
)
|
||||
|
||||
set(subtarget ".${target}-subtarget-${outputname}")
|
||||
add_custom_target(${subtarget} DEPENDS ${outputpath})
|
||||
add_dependencies(${target} ${subtarget})
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
function(target_base_address target address)
|
||||
set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE off)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_LINKER_ID MATCHES "^(LLD|MOLD)$")
|
||||
target_link_options(${target} PUBLIC "LINKER:--image-base=${address}")
|
||||
else()
|
||||
target_link_options(${target} PUBLIC "LINKER:-Ttext-segment,${address}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
option(WITH_PS3 "Enable PS3 emulation support" OFF)
|
||||
option(WITH_PS4 "Enable PS4 emulation support" ON)
|
||||
|
||||
option(COMPILE_GLFW "Compile GLFW" OFF)
|
||||
option(COMPILE_VULKAN_LOADER "Compile Vulkan Loader" OFF)
|
||||
|
||||
option(WITHOUT_OPENGL "Disable OpenGL" OFF)
|
||||
option(WITHOUT_OPENGLEW "Disable OpenGLEW" OFF)
|
||||
option(WITHOUT_OPENAL "Disable OpenAL" OFF)
|
||||
|
|
@ -29,8 +162,7 @@ option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON)
|
|||
option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF)
|
||||
option(USE_LTO "Use LTO for building" ON)
|
||||
|
||||
|
||||
if (NOT WITH_RPCS3)
|
||||
if (NOT WITH_PS3)
|
||||
set(WITHOUT_OPENGL on)
|
||||
set(WITHOUT_OPENGLEW on)
|
||||
set(WITHOUT_OPENAL on)
|
||||
|
|
@ -117,6 +249,7 @@ if(MSVC)
|
|||
add_compile_options(/wd4530 /utf-8) # C++ exception handler used, but unwind semantics are not enabled
|
||||
endif()
|
||||
|
||||
include(CheckFunctionExists)
|
||||
|
||||
add_subdirectory(3rdparty EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(rx EXCLUDE_FROM_ALL)
|
||||
|
|
@ -136,67 +269,17 @@ target_compile_definitions(rx PRIVATE
|
|||
RX_TAG_VERSION=${RX_TAG_VERSION}
|
||||
)
|
||||
|
||||
if (WITH_RPCSX)
|
||||
if (WITH_PS4)
|
||||
find_package(nlohmann_json CONFIG)
|
||||
|
||||
function(add_precompiled_vulkan_spirv target)
|
||||
add_library(${target} INTERFACE)
|
||||
set(SPIRV_GEN_ROOT_DIR "spirv-gen/include/")
|
||||
set(SPIRV_GEN_DIR "${SPIRV_GEN_ROOT_DIR}/shaders")
|
||||
|
||||
cmake_path(ABSOLUTE_PATH SPIRV_GEN_ROOT_DIR BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE outputrootdir)
|
||||
cmake_path(ABSOLUTE_PATH SPIRV_GEN_DIR BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE outputdir)
|
||||
file(MAKE_DIRECTORY ${outputrootdir})
|
||||
file(MAKE_DIRECTORY ${outputdir})
|
||||
target_include_directories(${target} INTERFACE ${outputrootdir})
|
||||
|
||||
foreach(input IN LISTS ARGN)
|
||||
cmake_path(GET input FILENAME inputname)
|
||||
cmake_path(REPLACE_EXTENSION inputname LAST_ONLY .h OUTPUT_VARIABLE outputname)
|
||||
cmake_path(APPEND outputdir ${outputname} OUTPUT_VARIABLE outputpath)
|
||||
cmake_path(REMOVE_EXTENSION inputname LAST_ONLY OUTPUT_VARIABLE varname)
|
||||
|
||||
string(REPLACE "." "_" varname ${varname})
|
||||
string(PREPEND varname "spirv_")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${outputpath}
|
||||
COMMAND $<TARGET_FILE:glslang::glslang-standalone> -V --target-env vulkan1.2 --vn "${varname}" -o "${outputpath}" "${CMAKE_CURRENT_SOURCE_DIR}/${input}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${input}" glslang::glslang-standalone
|
||||
COMMENT "Generating ${outputname}..."
|
||||
)
|
||||
|
||||
set(subtarget ".${target}-subtarget-${outputname}")
|
||||
add_custom_target(${subtarget} DEPENDS ${outputpath})
|
||||
add_dependencies(${target} ${subtarget})
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
function(target_base_address target address)
|
||||
set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE off)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_LINKER_ID MATCHES "^(LLD|MOLD)$")
|
||||
target_link_options(${target} PUBLIC "LINKER:--image-base=${address}")
|
||||
else()
|
||||
target_link_options(${target} PUBLIC "LINKER:-Ttext-segment,${address}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
add_subdirectory(tools)
|
||||
|
||||
add_subdirectory(orbis-kernel)
|
||||
endif()
|
||||
|
||||
add_subdirectory(rpcsx)
|
||||
add_subdirectory(kernel)
|
||||
|
||||
if (WITH_RPCS3)
|
||||
if (WITH_PS3)
|
||||
include(ConfigureCompiler)
|
||||
include(CheckFunctionExists)
|
||||
|
||||
add_subdirectory(rpcs3)
|
||||
add_subdirectory(ps3fw)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND WITH_RPCS3_QT_UI AND WITH_RPCS3)
|
||||
add_subdirectory(rpcs3qt-legacy)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ target_link_libraries(3rdparty_ffmpeg INTERFACE
|
|||
|
||||
add_dependencies(3rdparty_ffmpeg ffmpeg-unpack)
|
||||
|
||||
set(WITH_RPCSX off)
|
||||
set(WITH_RPCS3 on)
|
||||
set(WITH_PS4 off)
|
||||
set(WITH_PS3 on)
|
||||
set(USE_SYSTEM_LIBUSB off)
|
||||
set(USE_SYSTEM_CURL off)
|
||||
set(USE_DISCORD_RPC off)
|
||||
|
|
@ -113,8 +113,6 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
|
|||
src/rpcsx-android.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC rpcs3/rpcs3)
|
||||
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
rpcs3
|
||||
rpcsx::fw::ps3
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#include "Emu/Audio/Null/NullAudioBackend.h"
|
||||
#include "Emu/Cell/PPUAnalyser.h"
|
||||
#include "Emu/Cell/SPURecompiler.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Io/KeyboardHandler.h"
|
||||
#include "Emu/Io/Null/NullKeyboardHandler.h"
|
||||
|
|
@ -34,6 +33,7 @@
|
|||
#include "Loader/PSF.h"
|
||||
#include "Loader/PUP.h"
|
||||
#include "Loader/TAR.h"
|
||||
#include "cellos/sys_sync.h"
|
||||
#include "dev/block_dev.hpp"
|
||||
#include "dev/iso.hpp"
|
||||
#include "hidapi_libusb.h"
|
||||
|
|
@ -41,12 +41,13 @@
|
|||
#include "rpcs3_version.h"
|
||||
#include "rpcsx/fw/ps3/cellMsgDialog.h"
|
||||
#include "rpcsx/fw/ps3/cellSysutil.h"
|
||||
#include "rx/asm.hpp"
|
||||
#include "rx/debug.hpp"
|
||||
#include "util/File.h"
|
||||
#include "util/JIT.h"
|
||||
#include "util/StrFmt.h"
|
||||
#include "util/StrUtil.h"
|
||||
#include "util/Thread.h"
|
||||
#include "util/asm.hpp"
|
||||
#include "util/console.h"
|
||||
#include "util/fixed_typemap.hpp"
|
||||
#include "util/logs.hpp"
|
||||
|
|
@ -241,7 +242,7 @@ void jit_announce(uptr, usz, std::string_view);
|
|||
__android_log_write(ANDROID_LOG_FATAL, "RPCS3", buf.c_str());
|
||||
|
||||
jit_announce(0, 0, "");
|
||||
utils::trap();
|
||||
rx::breakpoint();
|
||||
std::abort();
|
||||
std::terminate();
|
||||
}
|
||||
|
|
|
|||
10
kernel/CMakeLists.txt
Normal file
10
kernel/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
add_library(kernel INTERFACE)
|
||||
target_include_directories(kernel INTERFACE include)
|
||||
|
||||
if (WITH_PS3)
|
||||
add_subdirectory(cellos)
|
||||
endif()
|
||||
|
||||
if (WITH_PS4)
|
||||
add_subdirectory(orbis)
|
||||
endif()
|
||||
70
kernel/cellos/CMakeLists.txt
Normal file
70
kernel/cellos/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
add_library(cellos-kernel STATIC
|
||||
src/lv2.cpp
|
||||
src/sys_bdemu.cpp
|
||||
src/sys_btsetting.cpp
|
||||
src/sys_cond.cpp
|
||||
src/sys_console.cpp
|
||||
src/sys_crypto_engine.cpp
|
||||
src/sys_config.cpp
|
||||
src/sys_dbg.cpp
|
||||
src/sys_event.cpp
|
||||
src/sys_event_flag.cpp
|
||||
src/sys_fs.cpp
|
||||
src/sys_game.cpp
|
||||
src/sys_gamepad.cpp
|
||||
src/sys_gpio.cpp
|
||||
src/sys_hid.cpp
|
||||
src/sys_interrupt.cpp
|
||||
src/sys_io.cpp
|
||||
src/sys_lwcond.cpp
|
||||
src/sys_lwmutex.cpp
|
||||
src/sys_memory.cpp
|
||||
src/sys_mmapper.cpp
|
||||
src/sys_mutex.cpp
|
||||
src/sys_net.cpp
|
||||
src/sys_net/lv2_socket.cpp
|
||||
src/sys_net/lv2_socket_native.cpp
|
||||
src/sys_net/lv2_socket_raw.cpp
|
||||
src/sys_net/lv2_socket_p2p.cpp
|
||||
src/sys_net/lv2_socket_p2ps.cpp
|
||||
src/sys_net/network_context.cpp
|
||||
src/sys_net/nt_p2p_port.cpp
|
||||
src/sys_net/sys_net_helpers.cpp
|
||||
src/sys_overlay.cpp
|
||||
src/sys_ppu_thread.cpp
|
||||
src/sys_process.cpp
|
||||
src/sys_prx.cpp
|
||||
src/sys_rsx.cpp
|
||||
src/sys_rsxaudio.cpp
|
||||
src/sys_rwlock.cpp
|
||||
src/sys_semaphore.cpp
|
||||
src/sys_spu.cpp
|
||||
src/sys_sm.cpp
|
||||
src/sys_ss.cpp
|
||||
src/sys_storage.cpp
|
||||
src/sys_time.cpp
|
||||
src/sys_timer.cpp
|
||||
src/sys_trace.cpp
|
||||
src/sys_tty.cpp
|
||||
src/sys_uart.cpp
|
||||
src/sys_usbd.cpp
|
||||
src/sys_vm.cpp
|
||||
)
|
||||
|
||||
target_include_directories(cellos-kernel
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cellos
|
||||
)
|
||||
|
||||
target_link_libraries(cellos-kernel PUBLIC
|
||||
rpcs3_core # FIXME: remove
|
||||
3rdparty::soundtouch # FIXME: remove
|
||||
3rdparty::flatbuffers # FIXME: remove
|
||||
3rdparty::wolfssl # FIXME: remove
|
||||
3rdparty::miniupnpc # FIXME: remove
|
||||
3rdparty::libusb # FIXME: remove
|
||||
3rdparty::rtmidi # FIXME: remove
|
||||
)
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_bdemu_send_command(u64 cmd, u64 a2, u64 a3, vm::ptr<void> buf, u64 buf_len);
|
||||
error_code sys_bdemu_send_command(u64 cmd, u64 a2, u64 a3, vm::ptr<void> buf,
|
||||
u64 buf_len);
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// SysCalls
|
||||
|
||||
49
kernel/cellos/include/cellos/sys_cond.h
Normal file
49
kernel/cellos/include/cellos/sys_cond.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_mutex.h"
|
||||
#include "sys_sync.h"
|
||||
|
||||
struct lv2_mutex;
|
||||
|
||||
struct sys_cond_attribute_t {
|
||||
be_t<u32> pshared;
|
||||
be_t<s32> flags;
|
||||
be_t<u64> ipc_key;
|
||||
|
||||
union {
|
||||
nse_t<u64, 1> name_u64;
|
||||
char name[sizeof(u64)];
|
||||
};
|
||||
};
|
||||
|
||||
struct lv2_cond final : lv2_obj {
|
||||
static const u32 id_base = 0x86000000;
|
||||
|
||||
const u64 key;
|
||||
const u64 name;
|
||||
const u32 mtx_id;
|
||||
|
||||
lv2_mutex *mutex; // Associated Mutex
|
||||
shared_ptr<lv2_obj> _mutex;
|
||||
ppu_thread *sq{};
|
||||
|
||||
lv2_cond(u64 key, u64 name, u32 mtx_id, shared_ptr<lv2_obj> mutex0) noexcept;
|
||||
|
||||
lv2_cond(utils::serial &ar) noexcept;
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
CellError on_id_create();
|
||||
};
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_cond_create(ppu_thread &ppu, vm::ptr<u32> cond_id, u32 mutex_id,
|
||||
vm::ptr<sys_cond_attribute_t> attr);
|
||||
error_code sys_cond_destroy(ppu_thread &ppu, u32 cond_id);
|
||||
error_code sys_cond_wait(ppu_thread &ppu, u32 cond_id, u64 timeout);
|
||||
error_code sys_cond_signal(ppu_thread &ppu, u32 cond_id);
|
||||
error_code sys_cond_signal_all(ppu_thread &ppu, u32 cond_id);
|
||||
error_code sys_cond_signal_to(ppu_thread &ppu, u32 cond_id, u32 thread_id);
|
||||
444
kernel/cellos/include/cellos/sys_config.h
Normal file
444
kernel/cellos/include/cellos/sys_config.h
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/timers.hpp"
|
||||
#include "sys_event.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "util/mutex.h"
|
||||
#include "util/shared_ptr.hpp"
|
||||
|
||||
/*
|
||||
* sys_config is a "subscription-based data storage API"
|
||||
*
|
||||
* It has the concept of services and listeners. Services provide data,
|
||||
* listeners subscribe to registration/unregistration events from specific
|
||||
* services.
|
||||
*
|
||||
* Services are divided into two classes: LV2 services (positive service IDs)
|
||||
* and User services (negative service IDs). LV2 services seem to be implictly
|
||||
* "available", probably constructed on-demand with internal LV2 code generating
|
||||
* the data. An example is PadManager (service ID 0x11). User services may be
|
||||
* registered through a syscall, and have negative IDs. An example is libPad
|
||||
* (service ID 0x8000'0000'0000'0001). Note that user-mode *cannot* register
|
||||
* positive service IDs.
|
||||
*
|
||||
* To start with, you have to get a sys_config handle by calling sys_config_open
|
||||
* and providing an event queue. This event queue will be used for sys_config
|
||||
* notifications if a subscribed config event is registered.
|
||||
*
|
||||
* With a sys_config handle, listeners can be added to specific services using
|
||||
* sys_config_add_service_listener. This syscall returns a service listener
|
||||
* handle, which can be used to close the listener and stop further
|
||||
* notifications. Once subscribed, any matching past service registrations will
|
||||
* be automatically sent to the supplied queue (thus the "data storage").
|
||||
*
|
||||
* Services exist "implicitly", and data may be registered *onto* a service by
|
||||
* calling sys_config_register_service. You can remove config events by calling
|
||||
* sys_config_unregister_service and providing the handle returned when
|
||||
* registering a service.
|
||||
*
|
||||
* If a service is registered (or unregistered) and matches any active listener,
|
||||
* that listener will get an event sent to the event queue provided in the call
|
||||
* to sys_config_open.
|
||||
*
|
||||
* This event will contain the type of config event ("service event" or "IO
|
||||
* event", in event.source), the corresponding sys_config handle (event.data1),
|
||||
* the config event ID (event.data2 & 0xffff'ffff), whether the service was
|
||||
* registered or unregistered ('data2 >> 32'), and what buffer size will be
|
||||
* needed to read the corresponding service event (event.data3).
|
||||
*
|
||||
* NOTE: if multiple listeners exist, each gets a separate event ID even though
|
||||
* all events are the same!
|
||||
*
|
||||
* After receiving such an event from the event queue, the user should allocate
|
||||
* enough buffer and call sys_config_get_service_event (or sys_config_io_event)
|
||||
* with the given event ID, in order to obtain a sys_config_service_event_t (or
|
||||
* sys_config_io_event_t) structure with the contents of the service that was
|
||||
* (un)registered.
|
||||
*/
|
||||
|
||||
class lv2_config_handle;
|
||||
class lv2_config_service;
|
||||
class lv2_config_service_listener;
|
||||
class lv2_config_service_event;
|
||||
|
||||
// Known sys_config service IDs
|
||||
enum sys_config_service_id : s64 {
|
||||
SYS_CONFIG_SERVICE_PADMANAGER = 0x11,
|
||||
SYS_CONFIG_SERVICE_PADMANAGER2 =
|
||||
0x12, // lv2 seems to send padmanager events to both 0x11 and 0x12
|
||||
SYS_CONFIG_SERVICE_0x20 = 0x20,
|
||||
SYS_CONFIG_SERVICE_0x30 = 0x30,
|
||||
|
||||
SYS_CONFIG_SERVICE_USER_BASE =
|
||||
static_cast<s64>(UINT64_C(0x8000'0000'0000'0000)),
|
||||
SYS_CONFIG_SERVICE_USER_LIBPAD = SYS_CONFIG_SERVICE_USER_BASE + 1,
|
||||
SYS_CONFIG_SERVICE_USER_LIBKB = SYS_CONFIG_SERVICE_USER_BASE + 2,
|
||||
SYS_CONFIG_SERVICE_USER_LIBMOUSE = SYS_CONFIG_SERVICE_USER_BASE + 3,
|
||||
SYS_CONFIG_SERVICE_USER_0x1000 = SYS_CONFIG_SERVICE_USER_BASE + 0x1000,
|
||||
SYS_CONFIG_SERVICE_USER_0x1010 = SYS_CONFIG_SERVICE_USER_BASE + 0x1010,
|
||||
SYS_CONFIG_SERVICE_USER_0x1011 = SYS_CONFIG_SERVICE_USER_BASE + 0x1011,
|
||||
SYS_CONFIG_SERVICE_USER_0x1013 = SYS_CONFIG_SERVICE_USER_BASE + 0x1013,
|
||||
SYS_CONFIG_SERVICE_USER_0x1020 = SYS_CONFIG_SERVICE_USER_BASE + 0x1020,
|
||||
SYS_CONFIG_SERVICE_USER_0x1030 = SYS_CONFIG_SERVICE_USER_BASE + 0x1030,
|
||||
};
|
||||
|
||||
enum sys_config_service_listener_type : u32 {
|
||||
SYS_CONFIG_SERVICE_LISTENER_ONCE = 0,
|
||||
SYS_CONFIG_SERVICE_LISTENER_REPEATING = 1
|
||||
};
|
||||
|
||||
enum sys_config_event_source : u64 {
|
||||
SYS_CONFIG_EVENT_SOURCE_SERVICE = 1,
|
||||
SYS_CONFIG_EVENT_SOURCE_IO = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* Dynamic-sized struct to describe a sys_config_service_event
|
||||
* We never allocate it - the guest does it for us and provides a pointer
|
||||
*/
|
||||
struct sys_config_service_event_t {
|
||||
// Handle to the service listener for whom this event is destined
|
||||
be_t<u32> service_listener_handle;
|
||||
|
||||
// 1 if this service is currently registered or unregistered
|
||||
be_t<u32> registered;
|
||||
|
||||
// Service ID that triggered this event
|
||||
be_t<u64> service_id;
|
||||
|
||||
// Custom ID provided by the user, used to uniquely identify service events
|
||||
// (provided to sys_config_register_event) When a service is unregistered,
|
||||
// this is the only value available to distinguish which service event was
|
||||
// unregistered.
|
||||
be_t<u64> user_id;
|
||||
|
||||
/* if added==0, the structure ends here */
|
||||
|
||||
// Verbosity of this service event (provided to sys_config_register_event)
|
||||
be_t<u64> verbosity;
|
||||
|
||||
// Size of 'data'
|
||||
be_t<u32> data_size;
|
||||
|
||||
// Ignored, seems to be simply 32-bits of padding
|
||||
be_t<u32> padding;
|
||||
|
||||
// Buffer containing event data (copy of the buffer supplied to
|
||||
// sys_config_register_service) NOTE: This buffer size is dynamic, according
|
||||
// to 'data_size', and can be 0. Here it is set to 1 since zero-sized buffers
|
||||
// are not standards-compliant
|
||||
u8 data[1];
|
||||
};
|
||||
|
||||
/*
|
||||
* Event data structure for SYS_CONFIG_SERVICE_PADMANAGER
|
||||
* This is a guess
|
||||
*/
|
||||
struct sys_config_padmanager_data_t {
|
||||
be_t<u16> unk[5]; // hid device type ?
|
||||
be_t<u16> vid;
|
||||
be_t<u16> pid;
|
||||
be_t<u16> unk2[6]; // bluetooth address?
|
||||
};
|
||||
static_assert(sizeof(sys_config_padmanager_data_t) == 26);
|
||||
|
||||
/*
|
||||
* Global sys_config state
|
||||
*/
|
||||
|
||||
class lv2_config {
|
||||
atomic_t<u32> m_state = 0;
|
||||
|
||||
// LV2 Config mutex
|
||||
shared_mutex m_mutex;
|
||||
|
||||
// Map of LV2 Service Events
|
||||
std::unordered_map<u32, shared_ptr<lv2_config_service_event>> events;
|
||||
|
||||
public:
|
||||
void initialize();
|
||||
|
||||
// Service Events
|
||||
void add_service_event(shared_ptr<lv2_config_service_event> event);
|
||||
void remove_service_event(u32 id);
|
||||
|
||||
shared_ptr<lv2_config_service_event> find_event(u32 id) {
|
||||
reader_lock lock(m_mutex);
|
||||
|
||||
const auto it = events.find(id);
|
||||
|
||||
if (it == events.cend())
|
||||
return null_ptr;
|
||||
|
||||
if (it->second) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return null_ptr;
|
||||
}
|
||||
|
||||
~lv2_config() noexcept;
|
||||
};
|
||||
|
||||
/*
|
||||
* LV2 Config Handle object, managed by IDM
|
||||
*/
|
||||
class lv2_config_handle {
|
||||
public:
|
||||
static const u32 id_base = 0x41000000;
|
||||
static const u32 id_step = 0x100;
|
||||
static const u32 id_count = 2048;
|
||||
SAVESTATE_INIT_POS(37);
|
||||
|
||||
private:
|
||||
u32 idm_id;
|
||||
|
||||
// queue for service/io event notifications
|
||||
const shared_ptr<lv2_event_queue> queue;
|
||||
|
||||
bool send_queue_event(u64 source, u64 d1, u64 d2, u64 d3) const {
|
||||
if (auto sptr = queue) {
|
||||
return sptr->send(source, d1, d2, d3) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_handle(shared_ptr<lv2_event_queue> _queue) noexcept
|
||||
: queue(std::move(_queue)) {}
|
||||
|
||||
// Factory
|
||||
template <typename... Args>
|
||||
static shared_ptr<lv2_config_handle> create(Args &&...args) {
|
||||
if (auto cfg =
|
||||
idm::make_ptr<lv2_config_handle>(std::forward<Args>(args)...)) {
|
||||
cfg->idm_id = idm::last_id();
|
||||
return cfg;
|
||||
}
|
||||
return null_ptr;
|
||||
}
|
||||
|
||||
// Notify event queue for this handle
|
||||
bool notify(u64 source, u64 data2, u64 data3) const {
|
||||
return send_queue_event(source, idm_id, data2, data3);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* LV2 Service object, managed by IDM
|
||||
*/
|
||||
class lv2_config_service {
|
||||
public:
|
||||
static const u32 id_base = 0x43000000;
|
||||
static const u32 id_step = 0x100;
|
||||
static const u32 id_count = 2048;
|
||||
SAVESTATE_INIT_POS(38);
|
||||
|
||||
private:
|
||||
// IDM data
|
||||
u32 idm_id;
|
||||
|
||||
// Whether this service is currently registered or not
|
||||
bool registered = true;
|
||||
|
||||
public:
|
||||
const u64 timestamp;
|
||||
const sys_config_service_id id;
|
||||
|
||||
const u64 user_id;
|
||||
const u64 verbosity;
|
||||
const u32 padding; // not used, but stored here just in case
|
||||
const std::vector<u8> data;
|
||||
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_service(sys_config_service_id _id, u64 _user_id, u64 _verbosity,
|
||||
u32 _padding, const u8 *_data, usz size) noexcept
|
||||
: timestamp(get_system_time()), id(_id), user_id(_user_id),
|
||||
verbosity(_verbosity), padding(_padding),
|
||||
data(&_data[0], &_data[size]) {}
|
||||
|
||||
// Factory
|
||||
template <typename... Args>
|
||||
static shared_ptr<lv2_config_service> create(Args &&...args) {
|
||||
if (auto service =
|
||||
idm::make_ptr<lv2_config_service>(std::forward<Args>(args)...)) {
|
||||
service->idm_id = idm::last_id();
|
||||
return service;
|
||||
}
|
||||
|
||||
return null_ptr;
|
||||
}
|
||||
|
||||
// Registration
|
||||
bool is_registered() const { return registered; }
|
||||
void unregister();
|
||||
|
||||
// Notify listeners
|
||||
void notify() const;
|
||||
|
||||
// Utilities
|
||||
usz get_size() const {
|
||||
return sizeof(sys_config_service_event_t) - 1 + data.size();
|
||||
}
|
||||
shared_ptr<lv2_config_service> get_shared_ptr() const {
|
||||
return stx::make_shared_from_this<lv2_config_service>(this);
|
||||
}
|
||||
u32 get_id() const { return idm_id; }
|
||||
};
|
||||
|
||||
/*
|
||||
* LV2 Service Event Listener object, managed by IDM
|
||||
*/
|
||||
class lv2_config_service_listener {
|
||||
public:
|
||||
static const u32 id_base = 0x42000000;
|
||||
static const u32 id_step = 0x100;
|
||||
static const u32 id_count = 2048;
|
||||
SAVESTATE_INIT_POS(39);
|
||||
|
||||
private:
|
||||
// IDM data
|
||||
u32 idm_id;
|
||||
|
||||
// The service listener owns the service events - service events will not be
|
||||
// freed as long as their corresponding listener exists This has been
|
||||
// confirmed to be the case in realhw
|
||||
std::vector<shared_ptr<lv2_config_service_event>> service_events;
|
||||
shared_ptr<lv2_config_handle> handle;
|
||||
|
||||
bool notify(const shared_ptr<lv2_config_service_event> &event);
|
||||
|
||||
public:
|
||||
const sys_config_service_id service_id;
|
||||
const u64 min_verbosity;
|
||||
const sys_config_service_listener_type type;
|
||||
|
||||
const std::vector<u8> data;
|
||||
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_service_listener(shared_ptr<lv2_config_handle> _handle,
|
||||
sys_config_service_id _service_id,
|
||||
u64 _min_verbosity,
|
||||
sys_config_service_listener_type _type,
|
||||
const u8 *_data, usz size) noexcept
|
||||
: handle(std::move(_handle)), service_id(_service_id),
|
||||
min_verbosity(_min_verbosity), type(_type),
|
||||
data(&_data[0], &_data[size]) {}
|
||||
|
||||
// Factory
|
||||
template <typename... Args>
|
||||
static shared_ptr<lv2_config_service_listener> create(Args &&...args) {
|
||||
if (auto listener = idm::make_ptr<lv2_config_service_listener>(
|
||||
std::forward<Args>(args)...)) {
|
||||
listener->idm_id = idm::last_id();
|
||||
return listener;
|
||||
}
|
||||
|
||||
return null_ptr;
|
||||
}
|
||||
|
||||
// Check whether service matches
|
||||
bool check_service(const lv2_config_service &service) const;
|
||||
|
||||
// Register new event, and notify queue
|
||||
bool notify(const shared_ptr<lv2_config_service> &service);
|
||||
|
||||
// (Re-)notify about all still-registered past events
|
||||
void notify_all();
|
||||
|
||||
// Utilities
|
||||
u32 get_id() const { return idm_id; }
|
||||
shared_ptr<lv2_config_service_listener> get_shared_ptr() const {
|
||||
return stx::make_shared_from_this<lv2_config_service_listener>(this);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* LV2 Service Event object (*not* managed by IDM)
|
||||
*/
|
||||
class lv2_config_service_event {
|
||||
static u32 get_next_id() {
|
||||
struct service_event_id {
|
||||
atomic_t<u32> next_id = 0;
|
||||
};
|
||||
|
||||
return g_fxo->get<service_event_id>().next_id++;
|
||||
}
|
||||
|
||||
atomic_t<bool> m_destroyed = false;
|
||||
|
||||
friend class lv2_config;
|
||||
|
||||
public:
|
||||
const u32 id;
|
||||
|
||||
// Note: Events hold a shared_ptr to their corresponding service - services
|
||||
// only get freed once there are no more pending service events This has been
|
||||
// confirmed to be the case in realhw
|
||||
const shared_ptr<lv2_config_handle> handle;
|
||||
const shared_ptr<lv2_config_service> service;
|
||||
const lv2_config_service_listener &listener;
|
||||
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_service_event(
|
||||
shared_ptr<lv2_config_handle> _handle,
|
||||
shared_ptr<lv2_config_service> _service,
|
||||
const lv2_config_service_listener &_listener) noexcept
|
||||
: id(get_next_id()), handle(std::move(_handle)),
|
||||
service(std::move(_service)), listener(_listener) {}
|
||||
|
||||
// Factory
|
||||
template <typename... Args>
|
||||
static shared_ptr<lv2_config_service_event> create(Args &&...args) {
|
||||
auto ev =
|
||||
make_shared<lv2_config_service_event>(std::forward<Args>(args)...);
|
||||
|
||||
g_fxo->get<lv2_config>().add_service_event(ev);
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
lv2_config_service_event &operator=(thread_state s) noexcept;
|
||||
~lv2_config_service_event() noexcept;
|
||||
|
||||
// Notify queue that this event exists
|
||||
bool notify() const;
|
||||
|
||||
// Write event to buffer
|
||||
void write(sys_config_service_event_t *dst) const;
|
||||
|
||||
// Check if the buffer can fit the current event, return false otherwise
|
||||
bool check_buffer_size(usz size) const { return service->get_size() <= size; }
|
||||
};
|
||||
|
||||
/*
|
||||
* Syscalls
|
||||
*/
|
||||
/*516*/ error_code sys_config_open(u32 equeue_hdl, vm::ptr<u32> out_config_hdl);
|
||||
/*517*/ error_code sys_config_close(u32 config_hdl);
|
||||
/*518*/ error_code
|
||||
sys_config_get_service_event(u32 config_hdl, u32 event_id,
|
||||
vm::ptr<sys_config_service_event_t> dst, u64 size);
|
||||
/*519*/ error_code sys_config_add_service_listener(
|
||||
u32 config_hdl, sys_config_service_id service_id, u64 min_verbosity,
|
||||
vm::ptr<void> in, u64 size, sys_config_service_listener_type type,
|
||||
vm::ptr<u32> out_listener_hdl);
|
||||
/*520*/ error_code sys_config_remove_service_listener(u32 config_hdl,
|
||||
u32 listener_hdl);
|
||||
/*521*/ error_code sys_config_register_service(u32 config_hdl,
|
||||
sys_config_service_id service_id,
|
||||
u64 user_id, u64 verbosity,
|
||||
vm::ptr<u8> data_buf, u64 size,
|
||||
vm::ptr<u32> out_service_hdl);
|
||||
/*522*/ error_code sys_config_unregister_service(u32 config_hdl,
|
||||
u32 service_hdl);
|
||||
|
||||
// Following syscalls have not been REd yet
|
||||
/*523*/ error_code sys_config_get_io_event(u32 config_hdl, u32 event_id /*?*/,
|
||||
vm::ptr<void> out_buf /*?*/,
|
||||
u64 size /*?*/);
|
||||
/*524*/ error_code sys_config_register_io_error_listener(u32 config_hdl);
|
||||
/*525*/ error_code sys_config_unregister_io_error_listener(u32 config_hdl);
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// SysCalls
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_crypto_engine_create(vm::ptr<u32> id);
|
||||
error_code sys_crypto_engine_destroy(u32 id);
|
||||
error_code sys_crypto_engine_random_generate(vm::ptr<void> buffer, u64 buffer_size);
|
||||
error_code sys_crypto_engine_random_generate(vm::ptr<void> buffer,
|
||||
u64 buffer_size);
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_dbg_read_process_memory(s32 pid, u32 address, u32 size, vm::ptr<void> data);
|
||||
error_code sys_dbg_write_process_memory(s32 pid, u32 address, u32 size, vm::cptr<void> data);
|
||||
error_code sys_dbg_read_process_memory(s32 pid, u32 address, u32 size,
|
||||
vm::ptr<void> data);
|
||||
error_code sys_dbg_write_process_memory(s32 pid, u32 address, u32 size,
|
||||
vm::cptr<void> data);
|
||||
153
kernel/cellos/include/cellos/sys_event.h
Normal file
153
kernel/cellos/include/cellos/sys_event.h
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
#include <deque>
|
||||
|
||||
class cpu_thread;
|
||||
class spu_thrread;
|
||||
|
||||
// Event Queue Type
|
||||
enum : u32 {
|
||||
SYS_PPU_QUEUE = 1,
|
||||
SYS_SPU_QUEUE = 2,
|
||||
};
|
||||
|
||||
// Event Queue Destroy Mode
|
||||
enum : s32 {
|
||||
SYS_EVENT_QUEUE_DESTROY_FORCE = 1,
|
||||
};
|
||||
|
||||
// Event Queue Ipc Key
|
||||
enum : u64 {
|
||||
SYS_EVENT_QUEUE_LOCAL = 0,
|
||||
};
|
||||
|
||||
// Event Port Type
|
||||
enum : s32 {
|
||||
SYS_EVENT_PORT_LOCAL = 1,
|
||||
SYS_EVENT_PORT_IPC = 3, // Unofficial name
|
||||
};
|
||||
|
||||
// Event Port Name
|
||||
enum : u64 {
|
||||
SYS_EVENT_PORT_NO_NAME = 0,
|
||||
};
|
||||
|
||||
// Event Source Type
|
||||
enum : u32 {
|
||||
SYS_SPU_THREAD_EVENT_USER = 1,
|
||||
SYS_SPU_THREAD_EVENT_DMA = 2, // not supported
|
||||
};
|
||||
|
||||
// Event Source Key
|
||||
enum : u64 {
|
||||
SYS_SPU_THREAD_EVENT_USER_KEY = 0xFFFFFFFF53505501ull,
|
||||
SYS_SPU_THREAD_EVENT_DMA_KEY = 0xFFFFFFFF53505502ull,
|
||||
SYS_SPU_THREAD_EVENT_EXCEPTION_KEY = 0xFFFFFFFF53505503ull,
|
||||
};
|
||||
|
||||
struct sys_event_queue_attribute_t {
|
||||
be_t<u32> protocol; // SYS_SYNC_PRIORITY or SYS_SYNC_FIFO
|
||||
be_t<s32> type; // SYS_PPU_QUEUE or SYS_SPU_QUEUE
|
||||
|
||||
union {
|
||||
nse_t<u64, 1> name_u64;
|
||||
char name[sizeof(u64)];
|
||||
};
|
||||
};
|
||||
|
||||
struct sys_event_t {
|
||||
be_t<u64> source;
|
||||
be_t<u64> data1;
|
||||
be_t<u64> data2;
|
||||
be_t<u64> data3;
|
||||
};
|
||||
|
||||
// Source, data1, data2, data3
|
||||
using lv2_event = std::tuple<u64, u64, u64, u64>;
|
||||
|
||||
struct lv2_event_port;
|
||||
|
||||
struct lv2_event_queue final : public lv2_obj {
|
||||
static const u32 id_base = 0x8d000000;
|
||||
|
||||
const u32 id;
|
||||
const lv2_protocol protocol;
|
||||
const u8 type;
|
||||
const u8 size;
|
||||
const u64 name;
|
||||
const u64 key;
|
||||
|
||||
shared_mutex mutex;
|
||||
std::deque<lv2_event> events;
|
||||
spu_thread *sq{};
|
||||
ppu_thread *pq{};
|
||||
|
||||
lv2_event_queue(u32 protocol, s32 type, s32 size, u64 name,
|
||||
u64 ipc_key) noexcept;
|
||||
|
||||
lv2_event_queue(utils::serial &ar) noexcept;
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
static void save_ptr(utils::serial &, lv2_event_queue *);
|
||||
static shared_ptr<lv2_event_queue>
|
||||
load_ptr(utils::serial &ar, shared_ptr<lv2_event_queue> &queue,
|
||||
std::string_view msg = {});
|
||||
|
||||
CellError send(lv2_event event, bool *notified_thread = nullptr,
|
||||
lv2_event_port *port = nullptr);
|
||||
|
||||
CellError send(u64 source, u64 d1, u64 d2, u64 d3,
|
||||
bool *notified_thread = nullptr,
|
||||
lv2_event_port *port = nullptr) {
|
||||
return send(std::make_tuple(source, d1, d2, d3), notified_thread, port);
|
||||
}
|
||||
|
||||
// Get event queue by its global key
|
||||
static shared_ptr<lv2_event_queue> find(u64 ipc_key);
|
||||
};
|
||||
|
||||
struct lv2_event_port final : lv2_obj {
|
||||
static const u32 id_base = 0x0e000000;
|
||||
|
||||
const s32 type; // Port type, either IPC or local
|
||||
const u64 name; // Event source (generated from id and process id if not set)
|
||||
|
||||
atomic_t<usz> is_busy = 0; // Counts threads waiting on event sending
|
||||
shared_ptr<lv2_event_queue> queue; // Event queue this port is connected to
|
||||
|
||||
lv2_event_port(s32 type, u64 name) : type(type), name(name) {}
|
||||
|
||||
lv2_event_port(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_event_queue_create(cpu_thread &cpu, vm::ptr<u32> equeue_id,
|
||||
vm::ptr<sys_event_queue_attribute_t> attr,
|
||||
u64 event_queue_key, s32 size);
|
||||
error_code sys_event_queue_destroy(ppu_thread &ppu, u32 equeue_id, s32 mode);
|
||||
error_code sys_event_queue_receive(ppu_thread &ppu, u32 equeue_id,
|
||||
vm::ptr<sys_event_t> dummy_event,
|
||||
u64 timeout);
|
||||
error_code sys_event_queue_tryreceive(ppu_thread &ppu, u32 equeue_id,
|
||||
vm::ptr<sys_event_t> event_array,
|
||||
s32 size, vm::ptr<u32> number);
|
||||
error_code sys_event_queue_drain(ppu_thread &ppu, u32 event_queue_id);
|
||||
|
||||
error_code sys_event_port_create(cpu_thread &cpu, vm::ptr<u32> eport_id,
|
||||
s32 port_type, u64 name);
|
||||
error_code sys_event_port_destroy(ppu_thread &ppu, u32 eport_id);
|
||||
error_code sys_event_port_connect_local(cpu_thread &cpu, u32 event_port_id,
|
||||
u32 event_queue_id);
|
||||
error_code sys_event_port_connect_ipc(ppu_thread &ppu, u32 eport_id,
|
||||
u64 ipc_key);
|
||||
error_code sys_event_port_disconnect(ppu_thread &ppu, u32 eport_id);
|
||||
error_code sys_event_port_send(u32 event_port_id, u64 data1, u64 data2,
|
||||
u64 data3);
|
||||
118
kernel/cellos/include/cellos/sys_event_flag.h
Normal file
118
kernel/cellos/include/cellos/sys_event_flag.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
enum {
|
||||
SYS_SYNC_WAITER_SINGLE = 0x10000,
|
||||
SYS_SYNC_WAITER_MULTIPLE = 0x20000,
|
||||
|
||||
SYS_EVENT_FLAG_WAIT_AND = 0x01,
|
||||
SYS_EVENT_FLAG_WAIT_OR = 0x02,
|
||||
|
||||
SYS_EVENT_FLAG_WAIT_CLEAR = 0x10,
|
||||
SYS_EVENT_FLAG_WAIT_CLEAR_ALL = 0x20,
|
||||
};
|
||||
|
||||
struct sys_event_flag_attribute_t {
|
||||
be_t<u32> protocol;
|
||||
be_t<u32> pshared;
|
||||
be_t<u64> ipc_key;
|
||||
be_t<s32> flags;
|
||||
be_t<s32> type;
|
||||
|
||||
union {
|
||||
nse_t<u64, 1> name_u64;
|
||||
char name[sizeof(u64)];
|
||||
};
|
||||
};
|
||||
|
||||
struct lv2_event_flag final : lv2_obj {
|
||||
static const u32 id_base = 0x98000000;
|
||||
|
||||
const lv2_protocol protocol;
|
||||
const u64 key;
|
||||
const s32 type;
|
||||
const u64 name;
|
||||
|
||||
shared_mutex mutex;
|
||||
atomic_t<u64> pattern;
|
||||
ppu_thread *sq{};
|
||||
|
||||
lv2_event_flag(u32 protocol, u64 key, s32 type, u64 name,
|
||||
u64 pattern) noexcept
|
||||
: protocol{static_cast<u8>(protocol)}, key(key), type(type), name(name),
|
||||
pattern(pattern) {}
|
||||
|
||||
lv2_event_flag(utils::serial &ar);
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
// Check mode arg
|
||||
static bool check_mode(u32 mode) {
|
||||
switch (mode & 0xf) {
|
||||
case SYS_EVENT_FLAG_WAIT_AND:
|
||||
break;
|
||||
case SYS_EVENT_FLAG_WAIT_OR:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode & ~0xf) {
|
||||
case 0:
|
||||
break;
|
||||
case SYS_EVENT_FLAG_WAIT_CLEAR:
|
||||
break;
|
||||
case SYS_EVENT_FLAG_WAIT_CLEAR_ALL:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check and clear pattern (must be atomic op)
|
||||
static bool check_pattern(u64 &pattern, u64 bitptn, u64 mode, u64 *result) {
|
||||
// Write pattern
|
||||
if (result) {
|
||||
*result = pattern;
|
||||
}
|
||||
|
||||
// Check pattern
|
||||
if (((mode & 0xf) == SYS_EVENT_FLAG_WAIT_AND &&
|
||||
(pattern & bitptn) != bitptn) ||
|
||||
((mode & 0xf) == SYS_EVENT_FLAG_WAIT_OR && (pattern & bitptn) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear pattern if necessary
|
||||
if ((mode & ~0xf) == SYS_EVENT_FLAG_WAIT_CLEAR) {
|
||||
pattern &= ~bitptn;
|
||||
} else if ((mode & ~0xf) == SYS_EVENT_FLAG_WAIT_CLEAR_ALL) {
|
||||
pattern = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Aux
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_event_flag_create(ppu_thread &ppu, vm::ptr<u32> id,
|
||||
vm::ptr<sys_event_flag_attribute_t> attr,
|
||||
u64 init);
|
||||
error_code sys_event_flag_destroy(ppu_thread &ppu, u32 id);
|
||||
error_code sys_event_flag_wait(ppu_thread &ppu, u32 id, u64 bitptn, u32 mode,
|
||||
vm::ptr<u64> result, u64 timeout);
|
||||
error_code sys_event_flag_trywait(ppu_thread &ppu, u32 id, u64 bitptn, u32 mode,
|
||||
vm::ptr<u64> result);
|
||||
error_code sys_event_flag_set(cpu_thread &cpu, u32 id, u64 bitptn);
|
||||
error_code sys_event_flag_clear(ppu_thread &ppu, u32 id, u64 bitptn);
|
||||
error_code sys_event_flag_cancel(ppu_thread &ppu, u32 id, vm::ptr<u32> num);
|
||||
error_code sys_event_flag_get(ppu_thread &ppu, u32 id, vm::ptr<u64> flags);
|
||||
665
kernel/cellos/include/cellos/sys_fs.h
Normal file
665
kernel/cellos/include/cellos/sys_fs.h
Normal file
|
|
@ -0,0 +1,665 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "util/File.h"
|
||||
#include "util/StrUtil.h"
|
||||
#include "util/mutex.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// Open Flags
|
||||
enum : s32 {
|
||||
CELL_FS_O_RDONLY = 000000,
|
||||
CELL_FS_O_WRONLY = 000001,
|
||||
CELL_FS_O_RDWR = 000002,
|
||||
CELL_FS_O_ACCMODE = 000003,
|
||||
CELL_FS_O_CREAT = 000100,
|
||||
CELL_FS_O_EXCL = 000200,
|
||||
CELL_FS_O_TRUNC = 001000,
|
||||
CELL_FS_O_APPEND = 002000,
|
||||
CELL_FS_O_MSELF = 010000,
|
||||
CELL_FS_O_UNK =
|
||||
01000000, // Tests have shown this is independent of other flags. Only
|
||||
// known to be called in Rockband games.
|
||||
};
|
||||
|
||||
// Seek Mode
|
||||
enum : s32 {
|
||||
CELL_FS_SEEK_SET,
|
||||
CELL_FS_SEEK_CUR,
|
||||
CELL_FS_SEEK_END,
|
||||
};
|
||||
|
||||
enum : s32 {
|
||||
CELL_FS_MAX_FS_PATH_LENGTH = 1024,
|
||||
CELL_FS_MAX_FS_FILE_NAME_LENGTH = 255,
|
||||
CELL_FS_MAX_MP_LENGTH = 31,
|
||||
};
|
||||
|
||||
enum : s32 {
|
||||
CELL_FS_S_IFMT = 0170000,
|
||||
CELL_FS_S_IFDIR = 0040000, // directory
|
||||
CELL_FS_S_IFREG = 0100000, // regular
|
||||
CELL_FS_S_IFLNK = 0120000, // symbolic link
|
||||
CELL_FS_S_IFWHT = 0160000, // unknown
|
||||
|
||||
CELL_FS_S_IRUSR = 0000400, // R for owner
|
||||
CELL_FS_S_IWUSR = 0000200, // W for owner
|
||||
CELL_FS_S_IXUSR = 0000100, // X for owner
|
||||
|
||||
CELL_FS_S_IRGRP = 0000040, // R for group
|
||||
CELL_FS_S_IWGRP = 0000020, // W for group
|
||||
CELL_FS_S_IXGRP = 0000010, // X for group
|
||||
|
||||
CELL_FS_S_IROTH = 0000004, // R for other
|
||||
CELL_FS_S_IWOTH = 0000002, // W for other
|
||||
CELL_FS_S_IXOTH = 0000001, // X for other
|
||||
};
|
||||
|
||||
// CellFsDirent.d_type
|
||||
enum : u8 {
|
||||
CELL_FS_TYPE_UNKNOWN = 0,
|
||||
CELL_FS_TYPE_DIRECTORY = 1,
|
||||
CELL_FS_TYPE_REGULAR = 2,
|
||||
CELL_FS_TYPE_SYMLINK = 3,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
CELL_FS_IO_BUFFER_PAGE_SIZE_64KB = 0x0002,
|
||||
CELL_FS_IO_BUFFER_PAGE_SIZE_1MB = 0x0004,
|
||||
};
|
||||
|
||||
struct CellFsDirent {
|
||||
u8 d_type;
|
||||
u8 d_namlen;
|
||||
char d_name[256];
|
||||
};
|
||||
|
||||
struct CellFsStat {
|
||||
be_t<s32> mode;
|
||||
be_t<s32> uid;
|
||||
be_t<s32> gid;
|
||||
be_t<s64, 4> atime;
|
||||
be_t<s64, 4> mtime;
|
||||
be_t<s64, 4> ctime;
|
||||
be_t<u64, 4> size;
|
||||
be_t<u64, 4> blksize;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(CellFsStat, 52, 4);
|
||||
|
||||
struct CellFsDirectoryEntry {
|
||||
CellFsStat attribute;
|
||||
CellFsDirent entry_name;
|
||||
};
|
||||
|
||||
struct CellFsUtimbuf {
|
||||
be_t<s64, 4> actime;
|
||||
be_t<s64, 4> modtime;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(CellFsUtimbuf, 16, 4);
|
||||
|
||||
// MSelf file structs
|
||||
struct FsMselfHeader {
|
||||
be_t<u32> m_magic;
|
||||
be_t<u32> m_format_version;
|
||||
be_t<u64> m_file_size;
|
||||
be_t<u32> m_entry_num;
|
||||
be_t<u32> m_entry_size;
|
||||
u8 m_reserve[40];
|
||||
};
|
||||
|
||||
struct FsMselfEntry {
|
||||
char m_name[32];
|
||||
be_t<u64> m_offset;
|
||||
be_t<u64> m_size;
|
||||
u8 m_reserve[16];
|
||||
};
|
||||
|
||||
enum class lv2_mp_flag {
|
||||
read_only,
|
||||
no_uid_gid,
|
||||
strict_get_block_size,
|
||||
cache,
|
||||
|
||||
bitset_last = cache,
|
||||
};
|
||||
|
||||
enum class lv2_file_type {
|
||||
regular = 0,
|
||||
sdata,
|
||||
edata,
|
||||
};
|
||||
|
||||
struct lv2_fs_mount_point {
|
||||
const std::string_view root;
|
||||
const std::string_view file_system;
|
||||
const std::string_view device;
|
||||
const u32 sector_size = 512;
|
||||
const u64 sector_count = 256;
|
||||
const u32 block_size = 4096;
|
||||
const rx::EnumBitSet<lv2_mp_flag> flags{};
|
||||
lv2_fs_mount_point *const next = nullptr;
|
||||
|
||||
mutable shared_mutex mutex;
|
||||
};
|
||||
|
||||
extern lv2_fs_mount_point g_mp_sys_dev_hdd0;
|
||||
extern lv2_fs_mount_point g_mp_sys_no_device;
|
||||
|
||||
struct lv2_fs_mount_info {
|
||||
lv2_fs_mount_point *const mp;
|
||||
const std::string device;
|
||||
const std::string file_system;
|
||||
const bool read_only;
|
||||
|
||||
lv2_fs_mount_info(lv2_fs_mount_point *mp = nullptr,
|
||||
std::string_view device = {},
|
||||
std::string_view file_system = {}, bool read_only = false)
|
||||
: mp(mp ? mp : &g_mp_sys_no_device),
|
||||
device(device.empty() ? this->mp->device : device),
|
||||
file_system(file_system.empty() ? this->mp->file_system : file_system),
|
||||
read_only(
|
||||
(this->mp->flags & lv2_mp_flag::read_only) ||
|
||||
read_only) // respect the original flags of the mount point as well
|
||||
{}
|
||||
|
||||
constexpr bool operator==(const lv2_fs_mount_info &rhs) const noexcept {
|
||||
return this == &rhs;
|
||||
}
|
||||
constexpr bool
|
||||
operator==(const lv2_fs_mount_point *const &rhs) const noexcept {
|
||||
return mp == rhs;
|
||||
}
|
||||
constexpr lv2_fs_mount_point *operator->() const noexcept { return mp; }
|
||||
};
|
||||
|
||||
extern lv2_fs_mount_info g_mi_sys_not_found;
|
||||
|
||||
struct CellFsMountInfo; // Forward Declaration
|
||||
|
||||
struct lv2_fs_mount_info_map {
|
||||
public:
|
||||
SAVESTATE_INIT_POS(40);
|
||||
|
||||
lv2_fs_mount_info_map();
|
||||
lv2_fs_mount_info_map(const lv2_fs_mount_info_map &) = delete;
|
||||
lv2_fs_mount_info_map &operator=(const lv2_fs_mount_info_map &) = delete;
|
||||
~lv2_fs_mount_info_map();
|
||||
|
||||
// Forwarding arguments to map.try_emplace(): refer to the constructor of
|
||||
// lv2_fs_mount_info
|
||||
template <typename... Args> bool add(Args &&...args) {
|
||||
return map.try_emplace(std::forward<Args>(args)...).second;
|
||||
}
|
||||
bool remove(std::string_view path);
|
||||
const lv2_fs_mount_info &lookup(std::string_view path,
|
||||
bool no_cell_fs_path = false,
|
||||
std::string *mount_path = nullptr) const;
|
||||
u64 get_all(CellFsMountInfo *info = nullptr, u64 len = 0) const;
|
||||
bool is_device_mounted(std::string_view device_name) const;
|
||||
|
||||
static bool vfs_unmount(std::string_view vpath, bool remove_from_map = true);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, lv2_fs_mount_info, fmt::string_hash,
|
||||
std::equal_to<>>
|
||||
map;
|
||||
};
|
||||
|
||||
struct lv2_fs_object {
|
||||
static constexpr u32 id_base = 3;
|
||||
static constexpr u32 id_step = 1;
|
||||
static constexpr u32 id_count = 255 - id_base;
|
||||
static constexpr bool id_lowest = true;
|
||||
SAVESTATE_INIT_POS(49);
|
||||
|
||||
// File Name (max 1055)
|
||||
const std::array<char, 0x420> name;
|
||||
|
||||
// Mount Info
|
||||
const lv2_fs_mount_info ∓
|
||||
|
||||
protected:
|
||||
lv2_fs_object(std::string_view filename);
|
||||
lv2_fs_object(utils::serial &ar, bool dummy);
|
||||
|
||||
public:
|
||||
lv2_fs_object(const lv2_fs_object &) = delete;
|
||||
|
||||
lv2_fs_object &operator=(const lv2_fs_object &) = delete;
|
||||
|
||||
// Normalize a virtual path
|
||||
static std::string get_normalized_path(std::string_view path);
|
||||
|
||||
// Get the device's root path (e.g. "/dev_hdd0") from a given path
|
||||
static std::string get_device_root(std::string_view filename);
|
||||
|
||||
// Filename can be either a path starting with '/' or a CELL_FS device name
|
||||
// This should be used only when handling devices that are not mounted
|
||||
// Otherwise, use g_fxo->get<lv2_fs_mount_info_map>().lookup() to look up
|
||||
// mounted devices accurately
|
||||
static lv2_fs_mount_point *get_mp(std::string_view filename,
|
||||
std::string *vfs_path = nullptr);
|
||||
|
||||
static std::array<char, 0x420> get_name(std::string_view filename) {
|
||||
std::array<char, 0x420> name;
|
||||
|
||||
if (filename.size() >= 0x420) {
|
||||
filename = filename.substr(0, 0x420 - 1);
|
||||
}
|
||||
|
||||
filename.copy(name.data(), filename.size());
|
||||
name[filename.size()] = 0;
|
||||
return name;
|
||||
}
|
||||
|
||||
void save(utils::serial &) {}
|
||||
};
|
||||
|
||||
struct lv2_file final : lv2_fs_object {
|
||||
static constexpr u32 id_type = 1;
|
||||
|
||||
fs::file file;
|
||||
const s32 mode;
|
||||
const s32 flags;
|
||||
std::string real_path;
|
||||
const lv2_file_type type;
|
||||
|
||||
// IO Container
|
||||
u32 ct_id{}, ct_used{};
|
||||
|
||||
// Stream lock
|
||||
atomic_t<u32> lock{0};
|
||||
|
||||
// Some variables for convenience of data restoration
|
||||
struct save_restore_t {
|
||||
u64 seek_pos;
|
||||
u64 atime;
|
||||
u64 mtime;
|
||||
} restore_data{};
|
||||
|
||||
lv2_file(std::string_view filename, fs::file &&file, s32 mode, s32 flags,
|
||||
const std::string &real_path, lv2_file_type type = {})
|
||||
: lv2_fs_object(filename), file(std::move(file)), mode(mode),
|
||||
flags(flags), real_path(real_path), type(type) {}
|
||||
|
||||
lv2_file(const lv2_file &host, fs::file &&file, s32 mode, s32 flags,
|
||||
const std::string &real_path, lv2_file_type type = {})
|
||||
: lv2_fs_object(host.name.data()), file(std::move(file)), mode(mode),
|
||||
flags(flags), real_path(real_path), type(type) {}
|
||||
|
||||
lv2_file(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
struct open_raw_result_t {
|
||||
CellError error;
|
||||
fs::file file;
|
||||
};
|
||||
|
||||
struct open_result_t {
|
||||
CellError error;
|
||||
std::string ppath;
|
||||
std::string real_path;
|
||||
fs::file file;
|
||||
lv2_file_type type;
|
||||
};
|
||||
|
||||
// Open a file with wrapped logic of sys_fs_open
|
||||
static open_raw_result_t
|
||||
open_raw(const std::string &path, s32 flags, s32 mode,
|
||||
lv2_file_type type = lv2_file_type::regular,
|
||||
const lv2_fs_mount_info &mp = g_mi_sys_not_found);
|
||||
static open_result_t open(std::string_view vpath, s32 flags, s32 mode,
|
||||
const void *arg = {}, u64 size = 0);
|
||||
|
||||
// File reading with intermediate buffer
|
||||
static u64 op_read(const fs::file &file, vm::ptr<void> buf, u64 size,
|
||||
u64 opt_pos = umax);
|
||||
|
||||
u64 op_read(vm::ptr<void> buf, u64 size, u64 opt_pos = umax) const {
|
||||
return op_read(file, buf, size, opt_pos);
|
||||
}
|
||||
|
||||
// File writing with intermediate buffer
|
||||
static u64 op_write(const fs::file &file, vm::cptr<void> buf, u64 size);
|
||||
|
||||
u64 op_write(vm::cptr<void> buf, u64 size) const {
|
||||
return op_write(file, buf, size);
|
||||
}
|
||||
|
||||
// For MSELF support
|
||||
struct file_view;
|
||||
|
||||
// Make file view from lv2_file object (for MSELF support)
|
||||
static fs::file make_view(const shared_ptr<lv2_file> &_file, u64 offset);
|
||||
};
|
||||
|
||||
struct lv2_dir final : lv2_fs_object {
|
||||
static constexpr u32 id_type = 2;
|
||||
|
||||
const std::vector<fs::dir_entry> entries;
|
||||
|
||||
// Current reading position
|
||||
atomic_t<u64> pos{0};
|
||||
|
||||
lv2_dir(std::string_view filename, std::vector<fs::dir_entry> &&entries)
|
||||
: lv2_fs_object(filename), entries(std::move(entries)) {}
|
||||
|
||||
lv2_dir(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
// Read next
|
||||
const fs::dir_entry *dir_read() {
|
||||
const u64 old_pos = pos;
|
||||
|
||||
if (const u64 cur = (old_pos < entries.size() ? pos++ : old_pos);
|
||||
cur < entries.size()) {
|
||||
return &entries[cur];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// sys_fs_fcntl arg base class (left empty for PODness)
|
||||
struct lv2_file_op {};
|
||||
|
||||
namespace vtable {
|
||||
struct lv2_file_op {
|
||||
// Speculation
|
||||
vm::bptrb<vm::ptrb<void>(vm::ptrb<lv2_file_op>)> get_data;
|
||||
vm::bptrb<u32(vm::ptrb<lv2_file_op>)> get_size;
|
||||
vm::bptrb<void(vm::ptrb<lv2_file_op>)> _dtor1;
|
||||
vm::bptrb<void(vm::ptrb<lv2_file_op>)> _dtor2;
|
||||
};
|
||||
} // namespace vtable
|
||||
|
||||
// sys_fs_fcntl: read with offset, write with offset
|
||||
struct lv2_file_op_rw : lv2_file_op {
|
||||
vm::bptrb<vtable::lv2_file_op> _vtable;
|
||||
|
||||
be_t<u32> op;
|
||||
be_t<u32> _x8; // ???
|
||||
be_t<u32> _xc; // ???
|
||||
|
||||
be_t<u32> fd; // File descriptor (3..255)
|
||||
vm::bptrb<void> buf; // Buffer for data
|
||||
be_t<u64> offset; // File offset
|
||||
be_t<u64> size; // Access size
|
||||
|
||||
be_t<s32> out_code; // Op result
|
||||
be_t<u64> out_size; // Size processed
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_op_rw, 0x38);
|
||||
|
||||
// sys_fs_fcntl: cellFsSdataOpenByFd
|
||||
struct lv2_file_op_09 : lv2_file_op {
|
||||
vm::bptrb<vtable::lv2_file_op> _vtable;
|
||||
|
||||
be_t<u32> op;
|
||||
be_t<u32> _x8;
|
||||
be_t<u32> _xc;
|
||||
|
||||
be_t<u32> fd;
|
||||
be_t<u64> offset;
|
||||
be_t<u32> _vtabl2;
|
||||
be_t<u32> arg1; // 0x180
|
||||
be_t<u32> arg2; // 0x10
|
||||
be_t<u32> arg_size; // 6th arg
|
||||
be_t<u32> arg_ptr; // 5th arg
|
||||
|
||||
be_t<u32> _x34;
|
||||
be_t<s32> out_code;
|
||||
be_t<u32> out_fd;
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_op_09, 0x40);
|
||||
|
||||
struct lv2_file_e0000025 : lv2_file_op {
|
||||
be_t<u32> size; // 0x30
|
||||
be_t<u32> _x4; // 0x10
|
||||
be_t<u32> _x8; // 0x28 - offset of out_code
|
||||
be_t<u32> name_size;
|
||||
vm::bcptr<char> name;
|
||||
be_t<u32> _x14;
|
||||
be_t<u32> _x18; // 0
|
||||
be_t<u32> _x1c; // 0
|
||||
be_t<u32> _x20; // 16
|
||||
be_t<u32> _x24; // unk, seems to be memory location
|
||||
be_t<u32> out_code; // out_code
|
||||
be_t<u32> fd; // 0xffffffff - likely fd out
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_e0000025, 0x30);
|
||||
|
||||
// sys_fs_fnctl: cellFsGetDirectoryEntries
|
||||
struct lv2_file_op_dir : lv2_file_op {
|
||||
struct dir_info : lv2_file_op {
|
||||
be_t<s32> _code; // Op result
|
||||
be_t<u32> _size; // Number of entries written
|
||||
vm::bptrb<CellFsDirectoryEntry> ptr;
|
||||
be_t<u32> max;
|
||||
};
|
||||
|
||||
CHECK_SIZE(dir_info, 0x10);
|
||||
|
||||
vm::bptrb<vtable::lv2_file_op> _vtable;
|
||||
|
||||
be_t<u32> op;
|
||||
be_t<u32> _x8;
|
||||
dir_info arg;
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_op_dir, 0x1c);
|
||||
|
||||
// sys_fs_fcntl: cellFsGetFreeSize (for dev_hdd0)
|
||||
struct lv2_file_c0000002 : lv2_file_op {
|
||||
vm::bptrb<vtable::lv2_file_op> _vtable;
|
||||
|
||||
be_t<u32> op;
|
||||
be_t<u32> _x8;
|
||||
vm::bcptr<char> path;
|
||||
be_t<u32> _x10; // 0
|
||||
be_t<u32> _x14;
|
||||
|
||||
be_t<u32> out_code; // CELL_ENOSYS
|
||||
be_t<u32> out_block_size;
|
||||
be_t<u64> out_block_count;
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_c0000002, 0x28);
|
||||
|
||||
// sys_fs_fcntl: unknown (called before cellFsOpen, for example)
|
||||
struct lv2_file_c0000006 : lv2_file_op {
|
||||
be_t<u32> size; // 0x20
|
||||
be_t<u32> _x4; // 0x10
|
||||
be_t<u32> _x8; // 0x18 - offset of out_code
|
||||
be_t<u32> name_size;
|
||||
vm::bcptr<char> name;
|
||||
be_t<u32> _x14; // 0
|
||||
be_t<u32> out_code; // 0x80010003
|
||||
be_t<u32> out_id; // set to 0, may return 0x1b5
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_c0000006, 0x20);
|
||||
|
||||
// sys_fs_fcntl: cellFsArcadeHddSerialNumber
|
||||
struct lv2_file_c0000007 : lv2_file_op {
|
||||
be_t<u32> out_code; // set to 0
|
||||
vm::bcptr<char> device; // CELL_FS_IOS:ATA_HDD
|
||||
be_t<u32> device_size; // 0x14
|
||||
vm::bptr<char> model;
|
||||
be_t<u32> model_size; // 0x29
|
||||
vm::bptr<char> serial;
|
||||
be_t<u32> serial_size; // 0x15
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_c0000007, 0x1c);
|
||||
|
||||
struct lv2_file_c0000008 : lv2_file_op {
|
||||
u8 _x0[4];
|
||||
be_t<u32> op; // 0xC0000008
|
||||
u8 _x8[8];
|
||||
be_t<u64> container_id;
|
||||
be_t<u32> size;
|
||||
be_t<u32> page_type; // 0x4000 for cellFsSetDefaultContainer
|
||||
// 0x4000 | page_type given by user, valid values seem to
|
||||
// be: CELL_FS_IO_BUFFER_PAGE_SIZE_64KB 0x0002
|
||||
// CELL_FS_IO_BUFFER_PAGE_SIZE_1MB 0x0004
|
||||
be_t<u32> out_code;
|
||||
u8 _x24[4];
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_c0000008, 0x28);
|
||||
|
||||
struct lv2_file_c0000015 : lv2_file_op {
|
||||
be_t<u32> size; // 0x20
|
||||
be_t<u32> _x4; // 0x10
|
||||
be_t<u32> _x8; // 0x18 - offset of out_code
|
||||
be_t<u32> path_size;
|
||||
vm::bcptr<char> path;
|
||||
be_t<u32> _x14; //
|
||||
be_t<u16> vendorID;
|
||||
be_t<u16> productID;
|
||||
be_t<u32> out_code; // set to 0
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_c0000015, 0x20);
|
||||
|
||||
struct lv2_file_c000001a : lv2_file_op {
|
||||
be_t<u32>
|
||||
disc_retry_type; // CELL_FS_DISC_READ_RETRY_NONE results in a 0 here
|
||||
// CELL_FS_DISC_READ_RETRY_DEFAULT results in a 0x63 here
|
||||
be_t<u32> _x4; // 0
|
||||
be_t<u32> _x8; // 0x000186A0
|
||||
be_t<u32> _xC; // 0
|
||||
be_t<u32> _x10; // 0
|
||||
be_t<u32> _x14; // 0
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_c000001a, 0x18);
|
||||
|
||||
struct lv2_file_c000001c : lv2_file_op {
|
||||
be_t<u32> size; // 0x60
|
||||
be_t<u32> _x4; // 0x10
|
||||
be_t<u32> _x8; // 0x18 - offset of out_code
|
||||
be_t<u32> path_size;
|
||||
vm::bcptr<char> path;
|
||||
be_t<u32> unk1;
|
||||
be_t<u16> vendorID;
|
||||
be_t<u16> productID;
|
||||
be_t<u32> out_code; // set to 0
|
||||
be_t<u16> serial[32];
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_c000001c, 0x60);
|
||||
|
||||
// sys_fs_fcntl: cellFsAllocateFileAreaWithoutZeroFill
|
||||
struct lv2_file_e0000017 : lv2_file_op {
|
||||
be_t<u32> size; // 0x28
|
||||
be_t<u32> _x4; // 0x10, offset
|
||||
be_t<u32> _x8; // 0x20, offset
|
||||
be_t<u32> _xc; // -
|
||||
vm::bcptr<char> file_path;
|
||||
be_t<u64> file_size;
|
||||
be_t<u32> out_code;
|
||||
};
|
||||
|
||||
CHECK_SIZE(lv2_file_e0000017, 0x28);
|
||||
|
||||
struct CellFsMountInfo {
|
||||
char mount_path[0x20]; // 0x0
|
||||
char filesystem[0x20]; // 0x20
|
||||
char dev_name[0x40]; // 0x40
|
||||
be_t<u32> unk[5]; // 0x80, probably attributes
|
||||
};
|
||||
|
||||
CHECK_SIZE(CellFsMountInfo, 0x94);
|
||||
|
||||
// Default IO container
|
||||
struct default_sys_fs_container {
|
||||
shared_mutex mutex;
|
||||
u32 id = 0;
|
||||
u32 cap = 0;
|
||||
u32 used = 0;
|
||||
};
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_fs_test(ppu_thread &ppu, u32 arg1, u32 arg2, vm::ptr<u32> arg3,
|
||||
u32 arg4, vm::ptr<char> buf, u32 buf_size);
|
||||
error_code sys_fs_open(ppu_thread &ppu, vm::cptr<char> path, s32 flags,
|
||||
vm::ptr<u32> fd, s32 mode, vm::cptr<void> arg, u64 size);
|
||||
error_code sys_fs_read(ppu_thread &ppu, u32 fd, vm::ptr<void> buf, u64 nbytes,
|
||||
vm::ptr<u64> nread);
|
||||
error_code sys_fs_write(ppu_thread &ppu, u32 fd, vm::cptr<void> buf, u64 nbytes,
|
||||
vm::ptr<u64> nwrite);
|
||||
error_code sys_fs_close(ppu_thread &ppu, u32 fd);
|
||||
error_code sys_fs_opendir(ppu_thread &ppu, vm::cptr<char> path,
|
||||
vm::ptr<u32> fd);
|
||||
error_code sys_fs_readdir(ppu_thread &ppu, u32 fd, vm::ptr<CellFsDirent> dir,
|
||||
vm::ptr<u64> nread);
|
||||
error_code sys_fs_closedir(ppu_thread &ppu, u32 fd);
|
||||
error_code sys_fs_stat(ppu_thread &ppu, vm::cptr<char> path,
|
||||
vm::ptr<CellFsStat> sb);
|
||||
error_code sys_fs_fstat(ppu_thread &ppu, u32 fd, vm::ptr<CellFsStat> sb);
|
||||
error_code sys_fs_link(ppu_thread &ppu, vm::cptr<char> from, vm::cptr<char> to);
|
||||
error_code sys_fs_mkdir(ppu_thread &ppu, vm::cptr<char> path, s32 mode);
|
||||
error_code sys_fs_rename(ppu_thread &ppu, vm::cptr<char> from,
|
||||
vm::cptr<char> to);
|
||||
error_code sys_fs_rmdir(ppu_thread &ppu, vm::cptr<char> path);
|
||||
error_code sys_fs_unlink(ppu_thread &ppu, vm::cptr<char> path);
|
||||
error_code sys_fs_access(ppu_thread &ppu, vm::cptr<char> path, s32 mode);
|
||||
error_code sys_fs_fcntl(ppu_thread &ppu, u32 fd, u32 op, vm::ptr<void> arg,
|
||||
u32 size);
|
||||
error_code sys_fs_lseek(ppu_thread &ppu, u32 fd, s64 offset, s32 whence,
|
||||
vm::ptr<u64> pos);
|
||||
error_code sys_fs_fdatasync(ppu_thread &ppu, u32 fd);
|
||||
error_code sys_fs_fsync(ppu_thread &ppu, u32 fd);
|
||||
error_code sys_fs_fget_block_size(ppu_thread &ppu, u32 fd,
|
||||
vm::ptr<u64> sector_size,
|
||||
vm::ptr<u64> block_size, vm::ptr<u64> arg4,
|
||||
vm::ptr<s32> out_flags);
|
||||
error_code sys_fs_get_block_size(ppu_thread &ppu, vm::cptr<char> path,
|
||||
vm::ptr<u64> sector_size,
|
||||
vm::ptr<u64> block_size, vm::ptr<u64> arg4);
|
||||
error_code sys_fs_truncate(ppu_thread &ppu, vm::cptr<char> path, u64 size);
|
||||
error_code sys_fs_ftruncate(ppu_thread &ppu, u32 fd, u64 size);
|
||||
error_code sys_fs_symbolic_link(ppu_thread &ppu, vm::cptr<char> target,
|
||||
vm::cptr<char> linkpath);
|
||||
error_code sys_fs_chmod(ppu_thread &ppu, vm::cptr<char> path, s32 mode);
|
||||
error_code sys_fs_chown(ppu_thread &ppu, vm::cptr<char> path, s32 uid, s32 gid);
|
||||
error_code sys_fs_disk_free(ppu_thread &ppu, vm::cptr<char> path,
|
||||
vm::ptr<u64> total_free, vm::ptr<u64> avail_free);
|
||||
error_code sys_fs_utime(ppu_thread &ppu, vm::cptr<char> path,
|
||||
vm::cptr<CellFsUtimbuf> timep);
|
||||
error_code sys_fs_acl_read(ppu_thread &ppu, vm::cptr<char> path, vm::ptr<void>);
|
||||
error_code sys_fs_acl_write(ppu_thread &ppu, vm::cptr<char> path,
|
||||
vm::ptr<void>);
|
||||
error_code sys_fs_lsn_get_cda_size(ppu_thread &ppu, u32 fd, vm::ptr<u64> ptr);
|
||||
error_code sys_fs_lsn_get_cda(ppu_thread &ppu, u32 fd, vm::ptr<void>, u64,
|
||||
vm::ptr<u64>);
|
||||
error_code sys_fs_lsn_lock(ppu_thread &ppu, u32 fd);
|
||||
error_code sys_fs_lsn_unlock(ppu_thread &ppu, u32 fd);
|
||||
error_code sys_fs_lsn_read(ppu_thread &ppu, u32 fd, vm::cptr<void>, u64);
|
||||
error_code sys_fs_lsn_write(ppu_thread &ppu, u32 fd, vm::cptr<void>, u64);
|
||||
error_code sys_fs_mapped_allocate(ppu_thread &ppu, u32 fd, u64,
|
||||
vm::pptr<void> out_ptr);
|
||||
error_code sys_fs_mapped_free(ppu_thread &ppu, u32 fd, vm::ptr<void> ptr);
|
||||
error_code sys_fs_truncate2(ppu_thread &ppu, u32 fd, u64 size);
|
||||
error_code sys_fs_newfs(ppu_thread &ppu, vm::cptr<char> dev_name,
|
||||
vm::cptr<char> file_system, s32 unk1,
|
||||
vm::cptr<char> str1);
|
||||
error_code sys_fs_mount(ppu_thread &ppu, vm::cptr<char> dev_name,
|
||||
vm::cptr<char> file_system, vm::cptr<char> path,
|
||||
s32 unk1, s32 prot, s32 unk2, vm::cptr<char> str1,
|
||||
u32 str_len);
|
||||
error_code sys_fs_unmount(ppu_thread &ppu, vm::cptr<char> path, s32 unk1,
|
||||
s32 force);
|
||||
error_code sys_fs_get_mount_info_size(ppu_thread &ppu, vm::ptr<u64> len);
|
||||
error_code sys_fs_get_mount_info(ppu_thread &ppu, vm::ptr<CellFsMountInfo> info,
|
||||
u64 len, vm::ptr<u64> out_len);
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "rx/types.hpp"
|
||||
|
||||
void abort_lv2_watchdog();
|
||||
|
||||
error_code _sys_game_watchdog_start(u32 timeout);
|
||||
|
|
@ -8,5 +12,6 @@ error_code _sys_game_watchdog_clear();
|
|||
error_code _sys_game_set_system_sw_version(u64 version);
|
||||
u64 _sys_game_get_system_sw_version();
|
||||
error_code _sys_game_board_storage_read(vm::ptr<u8> buffer, vm::ptr<u8> status);
|
||||
error_code _sys_game_board_storage_write(vm::ptr<u8> buffer, vm::ptr<u8> status);
|
||||
error_code _sys_game_board_storage_write(vm::ptr<u8> buffer,
|
||||
vm::ptr<u8> status);
|
||||
error_code _sys_game_get_rtc_status(vm::ptr<s32> status);
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
enum : u64
|
||||
{
|
||||
SYS_GPIO_UNKNOWN_DEVICE_ID = 0x0,
|
||||
SYS_GPIO_LED_DEVICE_ID = 0x1,
|
||||
SYS_GPIO_DIP_SWITCH_DEVICE_ID = 0x2,
|
||||
enum : u64 {
|
||||
SYS_GPIO_UNKNOWN_DEVICE_ID = 0x0,
|
||||
SYS_GPIO_LED_DEVICE_ID = 0x1,
|
||||
SYS_GPIO_DIP_SWITCH_DEVICE_ID = 0x2,
|
||||
};
|
||||
|
||||
error_code sys_gpio_get(u64 device_id, vm::ptr<u64> value);
|
||||
44
kernel/cellos/include/cellos/sys_hid.h
Normal file
44
kernel/cellos/include/cellos/sys_hid.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// set sensor mode? also getinfo?
|
||||
struct sys_hid_info_5 {
|
||||
le_t<u16> vid;
|
||||
le_t<u16> pid;
|
||||
u8 status;
|
||||
// todo: more in this, not sure what tho
|
||||
};
|
||||
|
||||
struct sys_hid_info_2 {
|
||||
be_t<u16> vid;
|
||||
be_t<u16> pid;
|
||||
u8 unk[17];
|
||||
};
|
||||
|
||||
struct sys_hid_ioctl_68 {
|
||||
u8 unk;
|
||||
u8 unk2;
|
||||
};
|
||||
|
||||
// unk
|
||||
struct sys_hid_manager_514_pkg_d {
|
||||
be_t<u32> unk1;
|
||||
u8 unk2;
|
||||
};
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_hid_manager_open(ppu_thread &ppu, u64 device_type, u64 port_no,
|
||||
vm::ptr<u32> handle);
|
||||
error_code sys_hid_manager_ioctl(u32 hid_handle, u32 pkg_id, vm::ptr<void> buf,
|
||||
u64 buf_size);
|
||||
error_code sys_hid_manager_add_hot_key_observer(u32 event_queue,
|
||||
vm::ptr<u32> unk);
|
||||
error_code sys_hid_manager_check_focus();
|
||||
error_code sys_hid_manager_is_process_permission_root(u32 pid);
|
||||
error_code sys_hid_manager_513(u64 a1, u64 a2, vm::ptr<void> buf, u64 buf_size);
|
||||
error_code sys_hid_manager_514(u32 pkg_id, vm::ptr<void> buf, u64 buf_size);
|
||||
error_code sys_hid_manager_read(u32 handle, u32 pkg_id, vm::ptr<void> buf,
|
||||
u64 buf_size);
|
||||
46
kernel/cellos/include/cellos/sys_interrupt.h
Normal file
46
kernel/cellos/include/cellos/sys_interrupt.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
struct lv2_int_tag final : public lv2_obj {
|
||||
static const u32 id_base = 0x0a000000;
|
||||
|
||||
const u32 id;
|
||||
shared_ptr<struct lv2_int_serv> handler;
|
||||
|
||||
lv2_int_tag() noexcept;
|
||||
lv2_int_tag(utils::serial &ar) noexcept;
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
struct lv2_int_serv final : public lv2_obj {
|
||||
static const u32 id_base = 0x0b000000;
|
||||
|
||||
const u32 id;
|
||||
const shared_ptr<named_thread<ppu_thread>> thread;
|
||||
const u64 arg1;
|
||||
const u64 arg2;
|
||||
|
||||
lv2_int_serv(shared_ptr<named_thread<ppu_thread>> thread, u64 arg1,
|
||||
u64 arg2) noexcept;
|
||||
lv2_int_serv(utils::serial &ar) noexcept;
|
||||
void save(utils::serial &ar);
|
||||
|
||||
void exec() const;
|
||||
void join() const;
|
||||
};
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_interrupt_tag_destroy(ppu_thread &ppu, u32 intrtag);
|
||||
error_code _sys_interrupt_thread_establish(ppu_thread &ppu, vm::ptr<u32> ih,
|
||||
u32 intrtag, u32 intrthread,
|
||||
u64 arg1, u64 arg2);
|
||||
error_code _sys_interrupt_thread_disestablish(ppu_thread &ppu, u32 ih,
|
||||
vm::ptr<u64> r13);
|
||||
void sys_interrupt_thread_eoi(ppu_thread &ppu);
|
||||
28
kernel/cellos/include/cellos/sys_io.h
Normal file
28
kernel/cellos/include/cellos/sys_io.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
struct lv2_io_buf {
|
||||
static const u32 id_base = 0x44000000;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 2048;
|
||||
SAVESTATE_INIT_POS(41);
|
||||
|
||||
const u32 block_count;
|
||||
const u32 block_size;
|
||||
const u32 blocks;
|
||||
const u32 unk1;
|
||||
|
||||
lv2_io_buf(u32 block_count, u32 block_size, u32 blocks, u32 unk1)
|
||||
: block_count(block_count), block_size(block_size), blocks(blocks),
|
||||
unk1(unk1) {}
|
||||
};
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_io_buffer_create(u32 block_count, u32 block_size, u32 blocks,
|
||||
u32 unk1, vm::ptr<u32> handle);
|
||||
error_code sys_io_buffer_destroy(u32 handle);
|
||||
error_code sys_io_buffer_allocate(u32 handle, vm::ptr<u32> block);
|
||||
error_code sys_io_buffer_free(u32 handle, u32 block);
|
||||
57
kernel/cellos/include/cellos/sys_lwcond.h
Normal file
57
kernel/cellos/include/cellos/sys_lwcond.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
struct sys_lwmutex_t;
|
||||
|
||||
struct sys_lwcond_attribute_t {
|
||||
union {
|
||||
nse_t<u64, 1> name_u64;
|
||||
char name[sizeof(u64)];
|
||||
};
|
||||
};
|
||||
|
||||
struct sys_lwcond_t {
|
||||
vm::bptr<sys_lwmutex_t> lwmutex;
|
||||
be_t<u32> lwcond_queue; // lwcond pseudo-id
|
||||
};
|
||||
|
||||
struct lv2_lwcond final : lv2_obj {
|
||||
static const u32 id_base = 0x97000000;
|
||||
|
||||
const be_t<u64> name;
|
||||
const u32 lwid;
|
||||
const lv2_protocol protocol;
|
||||
vm::ptr<sys_lwcond_t> control;
|
||||
|
||||
shared_mutex mutex;
|
||||
ppu_thread *sq{};
|
||||
|
||||
atomic_t<s32> lwmutex_waiters = 0;
|
||||
|
||||
lv2_lwcond(u64 name, u32 lwid, u32 protocol,
|
||||
vm::ptr<sys_lwcond_t> control) noexcept
|
||||
: name(std::bit_cast<be_t<u64>>(name)), lwid(lwid),
|
||||
protocol{static_cast<u8>(protocol)}, control(control) {}
|
||||
|
||||
lv2_lwcond(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
// Aux
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code _sys_lwcond_create(ppu_thread &ppu, vm::ptr<u32> lwcond_id,
|
||||
u32 lwmutex_id, vm::ptr<sys_lwcond_t> control,
|
||||
u64 name);
|
||||
error_code _sys_lwcond_destroy(ppu_thread &ppu, u32 lwcond_id);
|
||||
error_code _sys_lwcond_signal(ppu_thread &ppu, u32 lwcond_id, u32 lwmutex_id,
|
||||
u64 ppu_thread_id, u32 mode);
|
||||
error_code _sys_lwcond_signal_all(ppu_thread &ppu, u32 lwcond_id,
|
||||
u32 lwmutex_id, u32 mode);
|
||||
error_code _sys_lwcond_queue_wait(ppu_thread &ppu, u32 lwcond_id,
|
||||
u32 lwmutex_id, u64 timeout);
|
||||
180
kernel/cellos/include/cellos/sys_lwmutex.h
Normal file
180
kernel/cellos/include/cellos/sys_lwmutex.h
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
struct sys_lwmutex_attribute_t {
|
||||
be_t<u32> protocol;
|
||||
be_t<u32> recursive;
|
||||
|
||||
union {
|
||||
nse_t<u64, 1> name_u64;
|
||||
char name[sizeof(u64)];
|
||||
};
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
lwmutex_free = 0xffffffffu,
|
||||
lwmutex_dead = 0xfffffffeu,
|
||||
lwmutex_reserved = 0xfffffffdu,
|
||||
};
|
||||
|
||||
struct sys_lwmutex_t {
|
||||
struct alignas(8) sync_var_t {
|
||||
be_t<u32> owner;
|
||||
be_t<u32> waiter;
|
||||
};
|
||||
|
||||
union {
|
||||
atomic_t<sync_var_t> lock_var;
|
||||
|
||||
struct {
|
||||
atomic_be_t<u32> owner;
|
||||
atomic_be_t<u32> waiter;
|
||||
} vars;
|
||||
|
||||
atomic_be_t<u64> all_info;
|
||||
};
|
||||
|
||||
be_t<u32> attribute;
|
||||
be_t<u32> recursive_count;
|
||||
be_t<u32> sleep_queue; // lwmutex pseudo-id
|
||||
be_t<u32> pad;
|
||||
};
|
||||
|
||||
struct lv2_lwmutex final : lv2_obj {
|
||||
static const u32 id_base = 0x95000000;
|
||||
|
||||
const lv2_protocol protocol;
|
||||
const vm::ptr<sys_lwmutex_t> control;
|
||||
const be_t<u64> name;
|
||||
|
||||
shared_mutex mutex;
|
||||
atomic_t<s32> lwcond_waiters{0};
|
||||
|
||||
struct alignas(16) control_data_t {
|
||||
s32 signaled{0};
|
||||
u32 reserved{};
|
||||
ppu_thread *sq{};
|
||||
};
|
||||
|
||||
atomic_t<control_data_t> lv2_control{};
|
||||
|
||||
lv2_lwmutex(u32 protocol, vm::ptr<sys_lwmutex_t> control, u64 name) noexcept
|
||||
: protocol{static_cast<u8>(protocol)}, control(control),
|
||||
name(std::bit_cast<be_t<u64>>(name)) {}
|
||||
|
||||
lv2_lwmutex(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
ppu_thread *load_sq() const {
|
||||
return atomic_storage<ppu_thread *>::load(lv2_control.raw().sq);
|
||||
}
|
||||
|
||||
template <typename T> s32 try_own(T *cpu, bool wait_only = false) {
|
||||
const s32 signal =
|
||||
lv2_control
|
||||
.fetch_op([&](control_data_t &data) {
|
||||
if (!data.signaled) {
|
||||
cpu->prio.atomic_op(
|
||||
[tag = ++g_priority_order_tag](
|
||||
std::common_type_t<decltype(T::prio)> &prio) {
|
||||
prio.order = tag;
|
||||
});
|
||||
|
||||
cpu->next_cpu = data.sq;
|
||||
data.sq = cpu;
|
||||
} else {
|
||||
ensure(!wait_only);
|
||||
data.signaled = 0;
|
||||
}
|
||||
})
|
||||
.signaled;
|
||||
|
||||
if (signal) {
|
||||
cpu->next_cpu = nullptr;
|
||||
} else {
|
||||
const bool notify = lwcond_waiters
|
||||
.fetch_op([](s32 &val) {
|
||||
if (val + 0u <= 1u << 31) {
|
||||
// Value was either positive or INT32_MIN
|
||||
return false;
|
||||
}
|
||||
|
||||
// lwmutex was set to be destroyed, but there
|
||||
// are lwcond waiters
|
||||
// Turn off the "lwcond_waiters notification"
|
||||
// bit as we are adding an lwmutex waiter
|
||||
val &= 0x7fff'ffff;
|
||||
return true;
|
||||
})
|
||||
.second;
|
||||
|
||||
if (notify) {
|
||||
// Notify lwmutex destroyer (may cause EBUSY to be returned for it)
|
||||
lwcond_waiters.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
bool try_unlock(bool unlock2) {
|
||||
if (!load_sq()) {
|
||||
control_data_t old{};
|
||||
old.signaled = atomic_storage<s32>::load(lv2_control.raw().signaled);
|
||||
control_data_t store = old;
|
||||
store.signaled |= (unlock2 ? s32{smin} : 1);
|
||||
|
||||
if (lv2_control.compare_exchange(old, store)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T> T *reown(bool unlock2 = false) {
|
||||
T *res = nullptr;
|
||||
|
||||
lv2_control.fetch_op([&](control_data_t &data) {
|
||||
res = nullptr;
|
||||
|
||||
if (auto sq = static_cast<T *>(data.sq)) {
|
||||
res = schedule<T>(data.sq, protocol, false);
|
||||
|
||||
if (sq == data.sq) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
data.signaled |= (unlock2 ? s32{smin} : 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (res && cpu_flag::again - res->state) {
|
||||
// Detach manually (fetch_op can fail, so avoid side-effects on the first
|
||||
// node in this case)
|
||||
res->next_cpu = nullptr;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
// Aux
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code _sys_lwmutex_create(ppu_thread &ppu, vm::ptr<u32> lwmutex_id,
|
||||
u32 protocol, vm::ptr<sys_lwmutex_t> control,
|
||||
s32 has_name, u64 name);
|
||||
error_code _sys_lwmutex_destroy(ppu_thread &ppu, u32 lwmutex_id);
|
||||
error_code _sys_lwmutex_lock(ppu_thread &ppu, u32 lwmutex_id, u64 timeout);
|
||||
error_code _sys_lwmutex_trylock(ppu_thread &ppu, u32 lwmutex_id);
|
||||
error_code _sys_lwmutex_unlock(ppu_thread &ppu, u32 lwmutex_id);
|
||||
error_code _sys_lwmutex_unlock2(ppu_thread &ppu, u32 lwmutex_id);
|
||||
135
kernel/cellos/include/cellos/sys_memory.h
Normal file
135
kernel/cellos/include/cellos/sys_memory.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
class cpu_thread;
|
||||
|
||||
enum lv2_mem_container_id : u32 {
|
||||
SYS_MEMORY_CONTAINER_ID_INVALID = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_MEMORY_ACCESS_RIGHT_NONE = 0x00000000000000F0ULL,
|
||||
SYS_MEMORY_ACCESS_RIGHT_ANY = 0x000000000000000FULL,
|
||||
SYS_MEMORY_ACCESS_RIGHT_PPU_THR = 0x0000000000000008ULL,
|
||||
SYS_MEMORY_ACCESS_RIGHT_HANDLER = 0x0000000000000004ULL,
|
||||
SYS_MEMORY_ACCESS_RIGHT_SPU_THR = 0x0000000000000002ULL,
|
||||
SYS_MEMORY_ACCESS_RIGHT_RAW_SPU = 0x0000000000000001ULL,
|
||||
|
||||
SYS_MEMORY_ATTR_READ_ONLY = 0x0000000000080000ULL,
|
||||
SYS_MEMORY_ATTR_READ_WRITE = 0x0000000000040000ULL,
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_MEMORY_PAGE_SIZE_4K = 0x100ull,
|
||||
SYS_MEMORY_PAGE_SIZE_64K = 0x200ull,
|
||||
SYS_MEMORY_PAGE_SIZE_1M = 0x400ull,
|
||||
SYS_MEMORY_PAGE_SIZE_MASK = 0xf00ull,
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_MEMORY_GRANULARITY_64K = 0x0000000000000200,
|
||||
SYS_MEMORY_GRANULARITY_1M = 0x0000000000000400,
|
||||
SYS_MEMORY_GRANULARITY_MASK = 0x0000000000000f00,
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_MEMORY_PROT_READ_WRITE = 0x0000000000040000,
|
||||
SYS_MEMORY_PROT_READ_ONLY = 0x0000000000080000,
|
||||
SYS_MEMORY_PROT_MASK = 0x00000000000f0000,
|
||||
};
|
||||
|
||||
struct sys_memory_info_t {
|
||||
be_t<u32> total_user_memory;
|
||||
be_t<u32> available_user_memory;
|
||||
};
|
||||
|
||||
struct sys_page_attr_t {
|
||||
be_t<u64> attribute;
|
||||
be_t<u64> access_right;
|
||||
be_t<u32> page_size;
|
||||
be_t<u32> pad;
|
||||
};
|
||||
|
||||
struct lv2_memory_container {
|
||||
static const u32 id_base = 0x3F000000;
|
||||
static const u32 id_step = 0x1;
|
||||
static const u32 id_count = 16;
|
||||
|
||||
const u32 size; // Amount of "physical" memory in this container
|
||||
const lv2_mem_container_id id; // ID of the container in if placed at IDM,
|
||||
// otherwise SYS_MEMORY_CONTAINER_ID_INVALID
|
||||
atomic_t<u32> used{}; // Amount of "physical" memory currently used
|
||||
|
||||
SAVESTATE_INIT_POS(1);
|
||||
|
||||
lv2_memory_container(u32 size, bool from_idm = false) noexcept;
|
||||
lv2_memory_container(utils::serial &ar, bool from_idm = false) noexcept;
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
static lv2_memory_container *search(u32 id);
|
||||
|
||||
// Try to get specified amount of "physical" memory
|
||||
// Values greater than UINT32_MAX will fail
|
||||
u32 take(u64 amount) {
|
||||
auto [_, result] = used.fetch_op([&](u32 &value) -> u32 {
|
||||
if (size - value >= amount) {
|
||||
value += static_cast<u32>(amount);
|
||||
return static_cast<u32>(amount);
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 free(u64 amount) {
|
||||
auto [_, result] = used.fetch_op([&](u32 &value) -> u32 {
|
||||
if (value >= amount) {
|
||||
value -= static_cast<u32>(amount);
|
||||
return static_cast<u32>(amount);
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Sanity check
|
||||
ensure(result == amount);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct sys_memory_user_memory_stat_t {
|
||||
be_t<u32> a; // 0x0
|
||||
be_t<u32> b; // 0x4
|
||||
be_t<u32> c; // 0x8
|
||||
be_t<u32> d; // 0xc
|
||||
be_t<u32> e; // 0x10
|
||||
be_t<u32> f; // 0x14
|
||||
be_t<u32> g; // 0x18
|
||||
};
|
||||
|
||||
// SysCalls
|
||||
error_code sys_memory_allocate(cpu_thread &cpu, u64 size, u64 flags,
|
||||
vm::ptr<u32> alloc_addr);
|
||||
error_code sys_memory_allocate_from_container(cpu_thread &cpu, u64 size,
|
||||
u32 cid, u64 flags,
|
||||
vm::ptr<u32> alloc_addr);
|
||||
error_code sys_memory_free(cpu_thread &cpu, u32 start_addr);
|
||||
error_code sys_memory_get_page_attribute(cpu_thread &cpu, u32 addr,
|
||||
vm::ptr<sys_page_attr_t> attr);
|
||||
error_code sys_memory_get_user_memory_size(cpu_thread &cpu,
|
||||
vm::ptr<sys_memory_info_t> mem_info);
|
||||
error_code sys_memory_get_user_memory_stat(
|
||||
cpu_thread &cpu, vm::ptr<sys_memory_user_memory_stat_t> mem_stat);
|
||||
error_code sys_memory_container_create(cpu_thread &cpu, vm::ptr<u32> cid,
|
||||
u64 size);
|
||||
error_code sys_memory_container_destroy(cpu_thread &cpu, u32 cid);
|
||||
error_code sys_memory_container_get_size(cpu_thread &cpu,
|
||||
vm::ptr<sys_memory_info_t> mem_info,
|
||||
u32 cid);
|
||||
error_code sys_memory_container_destroy_parent_with_childs(
|
||||
cpu_thread &cpu, u32 cid, u32 must_0, vm::ptr<u32> mc_child);
|
||||
125
kernel/cellos/include/cellos/sys_mmapper.h
Normal file
125
kernel/cellos/include/cellos/sys_mmapper.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct lv2_memory_container;
|
||||
|
||||
namespace utils {
|
||||
class shm;
|
||||
}
|
||||
|
||||
struct lv2_memory : lv2_obj {
|
||||
static const u32 id_base = 0x08000000;
|
||||
|
||||
const u32 size; // Memory size
|
||||
const u32 align; // Alignment required
|
||||
const u64 flags;
|
||||
const u64 key; // IPC key
|
||||
const bool pshared; // Process shared flag
|
||||
lv2_memory_container *const ct; // Associated memory container
|
||||
const std::shared_ptr<utils::shm> shm;
|
||||
|
||||
atomic_t<u32> counter{0};
|
||||
|
||||
lv2_memory(u32 size, u32 align, u64 flags, u64 key, bool pshared,
|
||||
lv2_memory_container *ct);
|
||||
|
||||
lv2_memory(utils::serial &ar);
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
CellError on_id_create();
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_MEMORY_PAGE_FAULT_EVENT_KEY = 0xfffe000000000000ULL,
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_MMAPPER_NO_SHM_KEY = 0xffff000000000000ull, // Unofficial name
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_MEMORY_PAGE_FAULT_CAUSE_NON_MAPPED = 0x2ULL,
|
||||
SYS_MEMORY_PAGE_FAULT_CAUSE_READ_ONLY = 0x1ULL,
|
||||
SYS_MEMORY_PAGE_FAULT_TYPE_PPU_THREAD = 0x0ULL,
|
||||
SYS_MEMORY_PAGE_FAULT_TYPE_SPU_THREAD = 0x1ULL,
|
||||
SYS_MEMORY_PAGE_FAULT_TYPE_RAW_SPU = 0x2ULL,
|
||||
};
|
||||
|
||||
struct page_fault_notification_entry {
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
u32 start_addr; // Starting address of region to monitor.
|
||||
u32 event_queue_id; // Queue to be notified.
|
||||
u32 port_id; // Port used to notify the queue.
|
||||
};
|
||||
|
||||
// Used to hold list of queues to be notified on page fault event.
|
||||
struct page_fault_notification_entries {
|
||||
std::vector<page_fault_notification_entry> entries;
|
||||
shared_mutex mutex;
|
||||
|
||||
SAVESTATE_INIT_POS(44);
|
||||
|
||||
page_fault_notification_entries() = default;
|
||||
page_fault_notification_entries(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
struct page_fault_event_entries {
|
||||
// First = thread, second = addr
|
||||
std::unordered_map<class cpu_thread *, u32> events;
|
||||
shared_mutex pf_mutex;
|
||||
};
|
||||
|
||||
struct mmapper_unk_entry_struct0 {
|
||||
be_t<u32> a; // 0x0
|
||||
be_t<u32> b; // 0x4
|
||||
be_t<u32> c; // 0x8
|
||||
be_t<u32> d; // 0xc
|
||||
be_t<u64> type; // 0x10
|
||||
};
|
||||
|
||||
// Aux
|
||||
class ppu_thread;
|
||||
|
||||
error_code mmapper_thread_recover_page_fault(cpu_thread *cpu);
|
||||
|
||||
// SysCalls
|
||||
error_code sys_mmapper_allocate_address(ppu_thread &, u64 size, u64 flags,
|
||||
u64 alignment, vm::ptr<u32> alloc_addr);
|
||||
error_code sys_mmapper_allocate_fixed_address(ppu_thread &);
|
||||
error_code sys_mmapper_allocate_shared_memory(ppu_thread &, u64 ipc_key,
|
||||
u64 size, u64 flags,
|
||||
vm::ptr<u32> mem_id);
|
||||
error_code
|
||||
sys_mmapper_allocate_shared_memory_from_container(ppu_thread &, u64 ipc_key,
|
||||
u64 size, u32 cid, u64 flags,
|
||||
vm::ptr<u32> mem_id);
|
||||
error_code sys_mmapper_allocate_shared_memory_ext(
|
||||
ppu_thread &, u64 ipc_key, u64 size, u32 flags,
|
||||
vm::ptr<mmapper_unk_entry_struct0> entries, s32 entry_count,
|
||||
vm::ptr<u32> mem_id);
|
||||
error_code sys_mmapper_allocate_shared_memory_from_container_ext(
|
||||
ppu_thread &, u64 ipc_key, u64 size, u64 flags, u32 cid,
|
||||
vm::ptr<mmapper_unk_entry_struct0> entries, s32 entry_count,
|
||||
vm::ptr<u32> mem_id);
|
||||
error_code sys_mmapper_change_address_access_right(ppu_thread &, u32 addr,
|
||||
u64 flags);
|
||||
error_code sys_mmapper_free_address(ppu_thread &, u32 addr);
|
||||
error_code sys_mmapper_free_shared_memory(ppu_thread &, u32 mem_id);
|
||||
error_code sys_mmapper_map_shared_memory(ppu_thread &, u32 addr, u32 mem_id,
|
||||
u64 flags);
|
||||
error_code sys_mmapper_search_and_map(ppu_thread &, u32 start_addr, u32 mem_id,
|
||||
u64 flags, vm::ptr<u32> alloc_addr);
|
||||
error_code sys_mmapper_unmap_shared_memory(ppu_thread &, u32 addr,
|
||||
vm::ptr<u32> mem_id);
|
||||
error_code sys_mmapper_enable_page_fault_notification(ppu_thread &,
|
||||
u32 start_addr,
|
||||
u32 event_queue_id);
|
||||
174
kernel/cellos/include/cellos/sys_mutex.h
Normal file
174
kernel/cellos/include/cellos/sys_mutex.h
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
|
||||
struct sys_mutex_attribute_t {
|
||||
be_t<u32>
|
||||
protocol; // SYS_SYNC_FIFO, SYS_SYNC_PRIORITY or SYS_SYNC_PRIORITY_INHERIT
|
||||
be_t<u32> recursive; // SYS_SYNC_RECURSIVE or SYS_SYNC_NOT_RECURSIVE
|
||||
be_t<u32> pshared;
|
||||
be_t<u32> adaptive;
|
||||
be_t<u64> ipc_key;
|
||||
be_t<s32> flags;
|
||||
be_t<u32> pad;
|
||||
|
||||
union {
|
||||
nse_t<u64, 1> name_u64;
|
||||
char name[sizeof(u64)];
|
||||
};
|
||||
};
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
struct lv2_mutex final : lv2_obj {
|
||||
static const u32 id_base = 0x85000000;
|
||||
|
||||
const lv2_protocol protocol;
|
||||
const u32 recursive;
|
||||
const u32 adaptive;
|
||||
const u64 key;
|
||||
const u64 name;
|
||||
|
||||
u32 cond_count = 0; // Condition Variables
|
||||
shared_mutex mutex;
|
||||
atomic_t<u32> lock_count{0}; // Recursive Locks
|
||||
|
||||
struct alignas(16) control_data_t {
|
||||
u32 owner{};
|
||||
u32 reserved{};
|
||||
ppu_thread *sq{};
|
||||
};
|
||||
|
||||
atomic_t<control_data_t> control{};
|
||||
|
||||
lv2_mutex(u32 protocol, u32 recursive, u32 adaptive, u64 key,
|
||||
u64 name) noexcept
|
||||
: protocol{static_cast<u8>(protocol)}, recursive(recursive),
|
||||
adaptive(adaptive), key(key), name(name) {}
|
||||
|
||||
lv2_mutex(utils::serial &ar);
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
template <typename T> CellError try_lock(T &cpu) {
|
||||
auto it = control.load();
|
||||
|
||||
if (!it.owner) {
|
||||
auto store = it;
|
||||
store.owner = cpu.id;
|
||||
if (!control.compare_and_swap_test(it, store)) {
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if (it.owner == cpu.id) {
|
||||
// Recursive locking
|
||||
if (recursive == SYS_SYNC_RECURSIVE) {
|
||||
if (lock_count == 0xffffffffu) {
|
||||
return CELL_EKRESOURCE;
|
||||
}
|
||||
|
||||
lock_count++;
|
||||
return {};
|
||||
}
|
||||
|
||||
return CELL_EDEADLK;
|
||||
}
|
||||
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
template <typename T> bool try_own(T &cpu) {
|
||||
if (control.atomic_op([&](control_data_t &data) {
|
||||
if (data.owner) {
|
||||
cpu.prio.atomic_op(
|
||||
[tag = ++g_priority_order_tag](
|
||||
std::common_type_t<decltype(T::prio)> &prio) {
|
||||
prio.order = tag;
|
||||
});
|
||||
|
||||
cpu.next_cpu = data.sq;
|
||||
data.sq = &cpu;
|
||||
return false;
|
||||
} else {
|
||||
data.owner = cpu.id;
|
||||
return true;
|
||||
}
|
||||
})) {
|
||||
cpu.next_cpu = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T> CellError try_unlock(T &cpu) {
|
||||
auto it = control.load();
|
||||
|
||||
if (it.owner != cpu.id) {
|
||||
return CELL_EPERM;
|
||||
}
|
||||
|
||||
if (lock_count) {
|
||||
lock_count--;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!it.sq) {
|
||||
auto store = it;
|
||||
store.owner = 0;
|
||||
|
||||
if (control.compare_and_swap_test(it, store)) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
template <typename T> T *reown() {
|
||||
T *res{};
|
||||
|
||||
control.fetch_op([&](control_data_t &data) {
|
||||
res = nullptr;
|
||||
|
||||
if (auto sq = static_cast<T *>(data.sq)) {
|
||||
res = schedule<T>(data.sq, protocol, false);
|
||||
|
||||
if (sq == data.sq) {
|
||||
atomic_storage<u32>::release(control.raw().owner, res->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
data.owner = res->id;
|
||||
return true;
|
||||
} else {
|
||||
data.owner = 0;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (res && cpu_flag::again - res->state) {
|
||||
// Detach manually (fetch_op can fail, so avoid side-effects on the first
|
||||
// node in this case)
|
||||
res->next_cpu = nullptr;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_mutex_create(ppu_thread &ppu, vm::ptr<u32> mutex_id,
|
||||
vm::ptr<sys_mutex_attribute_t> attr);
|
||||
error_code sys_mutex_destroy(ppu_thread &ppu, u32 mutex_id);
|
||||
error_code sys_mutex_lock(ppu_thread &ppu, u32 mutex_id, u64 timeout);
|
||||
error_code sys_mutex_trylock(ppu_thread &ppu, u32 mutex_id);
|
||||
error_code sys_mutex_unlock(ppu_thread &ppu, u32 mutex_id);
|
||||
357
kernel/cellos/include/cellos/sys_net.h
Normal file
357
kernel/cellos/include/cellos/sys_net.h
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// Error codes
|
||||
enum sys_net_error : s32 {
|
||||
SYS_NET_ENOENT = 2,
|
||||
SYS_NET_EINTR = 4,
|
||||
SYS_NET_EBADF = 9,
|
||||
SYS_NET_ENOMEM = 12,
|
||||
SYS_NET_EACCES = 13,
|
||||
SYS_NET_EFAULT = 14,
|
||||
SYS_NET_EBUSY = 16,
|
||||
SYS_NET_EINVAL = 22,
|
||||
SYS_NET_EMFILE = 24,
|
||||
SYS_NET_ENOSPC = 28,
|
||||
SYS_NET_EPIPE = 32,
|
||||
SYS_NET_EAGAIN = 35,
|
||||
SYS_NET_EWOULDBLOCK = SYS_NET_EAGAIN,
|
||||
SYS_NET_EINPROGRESS = 36,
|
||||
SYS_NET_EALREADY = 37,
|
||||
SYS_NET_EDESTADDRREQ = 39,
|
||||
SYS_NET_EMSGSIZE = 40,
|
||||
SYS_NET_EPROTOTYPE = 41,
|
||||
SYS_NET_ENOPROTOOPT = 42,
|
||||
SYS_NET_EPROTONOSUPPORT = 43,
|
||||
SYS_NET_EOPNOTSUPP = 45,
|
||||
SYS_NET_EPFNOSUPPORT = 46,
|
||||
SYS_NET_EAFNOSUPPORT = 47,
|
||||
SYS_NET_EADDRINUSE = 48,
|
||||
SYS_NET_EADDRNOTAVAIL = 49,
|
||||
SYS_NET_ENETDOWN = 50,
|
||||
SYS_NET_ENETUNREACH = 51,
|
||||
SYS_NET_ECONNABORTED = 53,
|
||||
SYS_NET_ECONNRESET = 54,
|
||||
SYS_NET_ENOBUFS = 55,
|
||||
SYS_NET_EISCONN = 56,
|
||||
SYS_NET_ENOTCONN = 57,
|
||||
SYS_NET_ESHUTDOWN = 58,
|
||||
SYS_NET_ETOOMANYREFS = 59,
|
||||
SYS_NET_ETIMEDOUT = 60,
|
||||
SYS_NET_ECONNREFUSED = 61,
|
||||
SYS_NET_EHOSTDOWN = 64,
|
||||
SYS_NET_EHOSTUNREACH = 65,
|
||||
};
|
||||
|
||||
static constexpr sys_net_error operator-(sys_net_error v) {
|
||||
return sys_net_error{-static_cast<s32>(v)};
|
||||
}
|
||||
|
||||
// Socket types (prefixed with SYS_NET_)
|
||||
enum lv2_socket_type : s32 {
|
||||
SYS_NET_SOCK_STREAM = 1,
|
||||
SYS_NET_SOCK_DGRAM = 2,
|
||||
SYS_NET_SOCK_RAW = 3,
|
||||
SYS_NET_SOCK_DGRAM_P2P = 6,
|
||||
SYS_NET_SOCK_STREAM_P2P = 10,
|
||||
};
|
||||
|
||||
// Socket options (prefixed with SYS_NET_)
|
||||
enum lv2_socket_option : s32 {
|
||||
SYS_NET_SO_SNDBUF = 0x1001,
|
||||
SYS_NET_SO_RCVBUF = 0x1002,
|
||||
SYS_NET_SO_SNDLOWAT = 0x1003,
|
||||
SYS_NET_SO_RCVLOWAT = 0x1004,
|
||||
SYS_NET_SO_SNDTIMEO = 0x1005,
|
||||
SYS_NET_SO_RCVTIMEO = 0x1006,
|
||||
SYS_NET_SO_ERROR = 0x1007,
|
||||
SYS_NET_SO_TYPE = 0x1008,
|
||||
SYS_NET_SO_NBIO = 0x1100, // Non-blocking IO
|
||||
SYS_NET_SO_TPPOLICY = 0x1101,
|
||||
|
||||
SYS_NET_SO_REUSEADDR = 0x0004,
|
||||
SYS_NET_SO_KEEPALIVE = 0x0008,
|
||||
SYS_NET_SO_BROADCAST = 0x0020,
|
||||
SYS_NET_SO_LINGER = 0x0080,
|
||||
SYS_NET_SO_OOBINLINE = 0x0100,
|
||||
SYS_NET_SO_REUSEPORT = 0x0200,
|
||||
SYS_NET_SO_ONESBCAST = 0x0800,
|
||||
SYS_NET_SO_USECRYPTO = 0x1000,
|
||||
SYS_NET_SO_USESIGNATURE = 0x2000,
|
||||
|
||||
SYS_NET_SOL_SOCKET = 0xffff,
|
||||
};
|
||||
|
||||
// IP options (prefixed with SYS_NET_)
|
||||
enum lv2_ip_option : s32 {
|
||||
SYS_NET_IP_HDRINCL = 2,
|
||||
SYS_NET_IP_TOS = 3,
|
||||
SYS_NET_IP_TTL = 4,
|
||||
SYS_NET_IP_MULTICAST_IF = 9,
|
||||
SYS_NET_IP_MULTICAST_TTL = 10,
|
||||
SYS_NET_IP_MULTICAST_LOOP = 11,
|
||||
SYS_NET_IP_ADD_MEMBERSHIP = 12,
|
||||
SYS_NET_IP_DROP_MEMBERSHIP = 13,
|
||||
SYS_NET_IP_TTLCHK = 23,
|
||||
SYS_NET_IP_MAXTTL = 24,
|
||||
SYS_NET_IP_DONTFRAG = 26
|
||||
};
|
||||
|
||||
// Family (prefixed with SYS_NET_)
|
||||
enum lv2_socket_family : s32 {
|
||||
SYS_NET_AF_UNSPEC = 0,
|
||||
SYS_NET_AF_LOCAL = 1,
|
||||
SYS_NET_AF_UNIX = SYS_NET_AF_LOCAL,
|
||||
SYS_NET_AF_INET = 2,
|
||||
SYS_NET_AF_INET6 = 24,
|
||||
};
|
||||
|
||||
// Flags (prefixed with SYS_NET_)
|
||||
enum {
|
||||
SYS_NET_MSG_OOB = 0x1,
|
||||
SYS_NET_MSG_PEEK = 0x2,
|
||||
SYS_NET_MSG_DONTROUTE = 0x4,
|
||||
SYS_NET_MSG_EOR = 0x8,
|
||||
SYS_NET_MSG_TRUNC = 0x10,
|
||||
SYS_NET_MSG_CTRUNC = 0x20,
|
||||
SYS_NET_MSG_WAITALL = 0x40,
|
||||
SYS_NET_MSG_DONTWAIT = 0x80,
|
||||
SYS_NET_MSG_BCAST = 0x100,
|
||||
SYS_NET_MSG_MCAST = 0x200,
|
||||
SYS_NET_MSG_USECRYPTO = 0x400,
|
||||
SYS_NET_MSG_USESIGNATURE = 0x800,
|
||||
};
|
||||
|
||||
// Shutdown types (prefixed with SYS_NET_)
|
||||
enum {
|
||||
SYS_NET_SHUT_RD = 0,
|
||||
SYS_NET_SHUT_WR = 1,
|
||||
SYS_NET_SHUT_RDWR = 2,
|
||||
};
|
||||
|
||||
// TCP options (prefixed with SYS_NET_)
|
||||
enum lv2_tcp_option : s32 {
|
||||
SYS_NET_TCP_NODELAY = 1,
|
||||
SYS_NET_TCP_MAXSEG = 2,
|
||||
SYS_NET_TCP_MSS_TO_ADVERTISE = 3,
|
||||
};
|
||||
|
||||
// IP protocols (prefixed with SYS_NET_)
|
||||
enum lv2_ip_protocol : s32 {
|
||||
SYS_NET_IPPROTO_IP = 0,
|
||||
SYS_NET_IPPROTO_ICMP = 1,
|
||||
SYS_NET_IPPROTO_IGMP = 2,
|
||||
SYS_NET_IPPROTO_TCP = 6,
|
||||
SYS_NET_IPPROTO_UDP = 17,
|
||||
SYS_NET_IPPROTO_ICMPV6 = 58,
|
||||
};
|
||||
|
||||
// Poll events (prefixed with SYS_NET_)
|
||||
enum {
|
||||
SYS_NET_POLLIN = 0x0001,
|
||||
SYS_NET_POLLPRI = 0x0002,
|
||||
SYS_NET_POLLOUT = 0x0004,
|
||||
SYS_NET_POLLERR = 0x0008, /* revent only */
|
||||
SYS_NET_POLLHUP = 0x0010, /* revent only */
|
||||
SYS_NET_POLLNVAL = 0x0020, /* revent only */
|
||||
SYS_NET_POLLRDNORM = 0x0040,
|
||||
SYS_NET_POLLWRNORM = SYS_NET_POLLOUT,
|
||||
SYS_NET_POLLRDBAND = 0x0080,
|
||||
SYS_NET_POLLWRBAND = 0x0100,
|
||||
};
|
||||
|
||||
enum lv2_socket_abort_flags : s32 {
|
||||
SYS_NET_ABORT_STRICT_CHECK = 1,
|
||||
};
|
||||
|
||||
// in_addr_t type prefixed with sys_net_
|
||||
using sys_net_in_addr_t = u32;
|
||||
|
||||
// in_port_t type prefixed with sys_net_
|
||||
using sys_net_in_port_t = u16;
|
||||
|
||||
// sa_family_t type prefixed with sys_net_
|
||||
using sys_net_sa_family_t = u8;
|
||||
|
||||
// socklen_t type prefixed with sys_net_
|
||||
using sys_net_socklen_t = u32;
|
||||
|
||||
// fd_set prefixed with sys_net_
|
||||
struct sys_net_fd_set {
|
||||
be_t<u32> fds_bits[32];
|
||||
|
||||
u32 bit(s32 s) const { return (fds_bits[(s >> 5) & 31] >> (s & 31)) & 1u; }
|
||||
|
||||
void set(s32 s) { fds_bits[(s >> 5) & 31] |= (1u << (s & 31)); }
|
||||
};
|
||||
|
||||
// hostent prefixed with sys_net_
|
||||
struct sys_net_hostent {
|
||||
vm::bptr<char> h_name;
|
||||
vm::bpptr<char> h_aliases;
|
||||
be_t<s32> h_addrtype;
|
||||
be_t<s32> h_length;
|
||||
vm::bpptr<char> h_addr_list;
|
||||
};
|
||||
|
||||
// in_addr prefixed with sys_net_
|
||||
struct sys_net_in_addr {
|
||||
be_t<u32> _s_addr;
|
||||
};
|
||||
|
||||
// iovec prefixed with sys_net_
|
||||
struct sys_net_iovec {
|
||||
be_t<s32> zero1;
|
||||
vm::bptr<void> iov_base;
|
||||
be_t<s32> zero2;
|
||||
be_t<u32> iov_len;
|
||||
};
|
||||
|
||||
// ip_mreq prefixed with sys_net_
|
||||
struct sys_net_ip_mreq {
|
||||
be_t<u32> imr_multiaddr;
|
||||
be_t<u32> imr_interface;
|
||||
};
|
||||
|
||||
// msghdr prefixed with sys_net_
|
||||
struct sys_net_msghdr {
|
||||
be_t<s32> zero1;
|
||||
vm::bptr<void> msg_name;
|
||||
be_t<u32> msg_namelen;
|
||||
be_t<s32> pad1;
|
||||
be_t<s32> zero2;
|
||||
vm::bptr<sys_net_iovec> msg_iov;
|
||||
be_t<s32> msg_iovlen;
|
||||
be_t<s32> pad2;
|
||||
be_t<s32> zero3;
|
||||
vm::bptr<void> msg_control;
|
||||
be_t<u32> msg_controllen;
|
||||
be_t<s32> msg_flags;
|
||||
};
|
||||
|
||||
// pollfd prefixed with sys_net_
|
||||
struct sys_net_pollfd {
|
||||
be_t<s32> fd;
|
||||
be_t<s16> events;
|
||||
be_t<s16> revents;
|
||||
};
|
||||
|
||||
// sockaddr prefixed with sys_net_
|
||||
struct sys_net_sockaddr {
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
u8 sa_len;
|
||||
u8 sa_family;
|
||||
char sa_data[14];
|
||||
};
|
||||
|
||||
// sockaddr_dl prefixed with sys_net_
|
||||
struct sys_net_sockaddr_dl {
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
u8 sdl_len;
|
||||
u8 sdl_family;
|
||||
be_t<u16> sdl_index;
|
||||
u8 sdl_type;
|
||||
u8 sdl_nlen;
|
||||
u8 sdl_alen;
|
||||
u8 sdl_slen;
|
||||
char sdl_data[12];
|
||||
};
|
||||
|
||||
// sockaddr_in prefixed with sys_net_
|
||||
struct sys_net_sockaddr_in {
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
u8 sin_len;
|
||||
u8 sin_family;
|
||||
be_t<u16> sin_port;
|
||||
be_t<u32> sin_addr;
|
||||
be_t<u64> sin_zero;
|
||||
};
|
||||
|
||||
// sockaddr_in_p2p prefixed with sys_net_
|
||||
struct sys_net_sockaddr_in_p2p {
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
u8 sin_len;
|
||||
u8 sin_family;
|
||||
be_t<u16> sin_port;
|
||||
be_t<u32> sin_addr;
|
||||
be_t<u16> sin_vport;
|
||||
char sin_zero[6];
|
||||
};
|
||||
|
||||
// timeval prefixed with sys_net_
|
||||
struct sys_net_timeval {
|
||||
be_t<s64> tv_sec;
|
||||
be_t<s64> tv_usec;
|
||||
};
|
||||
|
||||
// linger prefixed with sys_net_
|
||||
struct sys_net_linger {
|
||||
be_t<s32> l_onoff;
|
||||
be_t<s32> l_linger;
|
||||
};
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_net_bnet_accept(ppu_thread &, s32 s,
|
||||
vm::ptr<sys_net_sockaddr> addr,
|
||||
vm::ptr<u32> paddrlen);
|
||||
error_code sys_net_bnet_bind(ppu_thread &, s32 s,
|
||||
vm::cptr<sys_net_sockaddr> addr, u32 addrlen);
|
||||
error_code sys_net_bnet_connect(ppu_thread &, s32 s,
|
||||
vm::ptr<sys_net_sockaddr> addr, u32 addrlen);
|
||||
error_code sys_net_bnet_getpeername(ppu_thread &, s32 s,
|
||||
vm::ptr<sys_net_sockaddr> addr,
|
||||
vm::ptr<u32> paddrlen);
|
||||
error_code sys_net_bnet_getsockname(ppu_thread &, s32 s,
|
||||
vm::ptr<sys_net_sockaddr> addr,
|
||||
vm::ptr<u32> paddrlen);
|
||||
error_code sys_net_bnet_getsockopt(ppu_thread &, s32 s, s32 level, s32 optname,
|
||||
vm::ptr<void> optval, vm::ptr<u32> optlen);
|
||||
error_code sys_net_bnet_listen(ppu_thread &, s32 s, s32 backlog);
|
||||
error_code sys_net_bnet_recvfrom(ppu_thread &, s32 s, vm::ptr<void> buf,
|
||||
u32 len, s32 flags,
|
||||
vm::ptr<sys_net_sockaddr> addr,
|
||||
vm::ptr<u32> paddrlen);
|
||||
error_code sys_net_bnet_recvmsg(ppu_thread &, s32 s,
|
||||
vm::ptr<sys_net_msghdr> msg, s32 flags);
|
||||
error_code sys_net_bnet_sendmsg(ppu_thread &, s32 s,
|
||||
vm::cptr<sys_net_msghdr> msg, s32 flags);
|
||||
error_code sys_net_bnet_sendto(ppu_thread &, s32 s, vm::cptr<void> buf, u32 len,
|
||||
s32 flags, vm::cptr<sys_net_sockaddr> addr,
|
||||
u32 addrlen);
|
||||
error_code sys_net_bnet_setsockopt(ppu_thread &, s32 s, s32 level, s32 optname,
|
||||
vm::cptr<void> optval, u32 optlen);
|
||||
error_code sys_net_bnet_shutdown(ppu_thread &, s32 s, s32 how);
|
||||
error_code sys_net_bnet_socket(ppu_thread &, lv2_socket_family family,
|
||||
lv2_socket_type type, lv2_ip_protocol protocol);
|
||||
error_code sys_net_bnet_close(ppu_thread &, s32 s);
|
||||
error_code sys_net_bnet_poll(ppu_thread &, vm::ptr<sys_net_pollfd> fds,
|
||||
s32 nfds, s32 ms);
|
||||
error_code sys_net_bnet_select(ppu_thread &, s32 nfds,
|
||||
vm::ptr<sys_net_fd_set> readfds,
|
||||
vm::ptr<sys_net_fd_set> writefds,
|
||||
vm::ptr<sys_net_fd_set> exceptfds,
|
||||
vm::ptr<sys_net_timeval> timeout);
|
||||
error_code _sys_net_open_dump(ppu_thread &, s32 len, s32 flags);
|
||||
error_code _sys_net_read_dump(ppu_thread &, s32 id, vm::ptr<void> buf, s32 len,
|
||||
vm::ptr<s32> pflags);
|
||||
error_code _sys_net_close_dump(ppu_thread &, s32 id, vm::ptr<s32> pflags);
|
||||
error_code _sys_net_write_dump(ppu_thread &, s32 id, vm::cptr<void> buf,
|
||||
s32 len, u32 unknown);
|
||||
error_code sys_net_abort(ppu_thread &, s32 type, u64 arg, s32 flags);
|
||||
error_code sys_net_infoctl(ppu_thread &, s32 cmd, vm::ptr<void> arg);
|
||||
error_code sys_net_control(ppu_thread &, u32 arg1, s32 arg2, vm::ptr<void> arg3,
|
||||
s32 arg4);
|
||||
error_code sys_net_bnet_ioctl(ppu_thread &, s32 arg1, u32 arg2, u32 arg3);
|
||||
error_code sys_net_bnet_sysctl(ppu_thread &, u32 arg1, u32 arg2, u32 arg3,
|
||||
vm::ptr<void> arg4, u32 arg5, u32 arg6);
|
||||
error_code sys_net_eurus_post_command(ppu_thread &, s32 arg1, u32 arg2,
|
||||
u32 arg3);
|
||||
166
kernel/cellos/include/cellos/sys_net/lv2_socket.h
Normal file
166
kernel/cellos/include/cellos/sys_net/lv2_socket.h
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/NP/ip_address.h"
|
||||
#include "cellos/sys_net.h"
|
||||
#include "rx/EnumBitSet.hpp"
|
||||
#include "util/atomic_bit_set.h"
|
||||
#include "util/mutex.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
#include <poll.h>
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
enum class thread_state : u32;
|
||||
|
||||
class lv2_socket {
|
||||
public:
|
||||
// Poll events
|
||||
enum class poll_t {
|
||||
read,
|
||||
write,
|
||||
error,
|
||||
|
||||
bitset_last = error,
|
||||
};
|
||||
|
||||
union sockopt_data {
|
||||
char ch[128];
|
||||
be_t<s32> _int = 0;
|
||||
sys_net_timeval timeo;
|
||||
sys_net_linger linger;
|
||||
};
|
||||
|
||||
struct sockopt_cache {
|
||||
sockopt_data data{};
|
||||
s32 len = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
SAVESTATE_INIT_POS(7); // Dependency on RPCN
|
||||
|
||||
lv2_socket(lv2_socket_family family, lv2_socket_type type,
|
||||
lv2_ip_protocol protocol);
|
||||
lv2_socket(utils::serial &) {}
|
||||
lv2_socket(utils::serial &, lv2_socket_type type);
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &, bool save_only_this_class = false);
|
||||
virtual ~lv2_socket() noexcept;
|
||||
lv2_socket &operator=(thread_state s) noexcept;
|
||||
|
||||
std::unique_lock<shared_mutex> lock();
|
||||
|
||||
void set_lv2_id(u32 id);
|
||||
rx::EnumBitSet<poll_t> get_events() const;
|
||||
void set_poll_event(rx::EnumBitSet<poll_t> event);
|
||||
void poll_queue(shared_ptr<ppu_thread> ppu, rx::EnumBitSet<poll_t> event,
|
||||
std::function<bool(rx::EnumBitSet<poll_t>)> poll_cb);
|
||||
u32 clear_queue(ppu_thread *);
|
||||
void handle_events(const pollfd &native_fd, bool unset_connecting = false);
|
||||
void queue_wake(ppu_thread *ppu);
|
||||
|
||||
lv2_socket_family get_family() const;
|
||||
lv2_socket_type get_type() const;
|
||||
lv2_ip_protocol get_protocol() const;
|
||||
std::size_t get_queue_size() const;
|
||||
socket_type get_socket() const;
|
||||
#ifdef _WIN32
|
||||
bool is_connecting() const;
|
||||
void set_connecting(bool is_connecting);
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr>
|
||||
accept(bool is_lock = true) = 0;
|
||||
virtual s32 bind(const sys_net_sockaddr &addr) = 0;
|
||||
|
||||
virtual std::optional<s32> connect(const sys_net_sockaddr &addr) = 0;
|
||||
virtual s32 connect_followup() = 0;
|
||||
|
||||
virtual std::pair<s32, sys_net_sockaddr> getpeername() = 0;
|
||||
virtual std::pair<s32, sys_net_sockaddr> getsockname() = 0;
|
||||
|
||||
virtual std::tuple<s32, sockopt_data, u32> getsockopt(s32 level, s32 optname,
|
||||
u32 len) = 0;
|
||||
virtual s32 setsockopt(s32 level, s32 optname,
|
||||
const std::vector<u8> &optval) = 0;
|
||||
|
||||
virtual s32 listen(s32 backlog) = 0;
|
||||
|
||||
virtual std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>>
|
||||
recvfrom(s32 flags, u32 len, bool is_lock = true) = 0;
|
||||
virtual std::optional<s32> sendto(s32 flags, const std::vector<u8> &buf,
|
||||
std::optional<sys_net_sockaddr> opt_sn_addr,
|
||||
bool is_lock = true) = 0;
|
||||
virtual std::optional<s32> sendmsg(s32 flags, const sys_net_msghdr &msg,
|
||||
bool is_lock = true) = 0;
|
||||
|
||||
virtual void close() = 0;
|
||||
virtual s32 shutdown(s32 how) = 0;
|
||||
|
||||
virtual s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) = 0;
|
||||
virtual std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
|
||||
pollfd &native_pfd) = 0;
|
||||
|
||||
error_code abort_socket(s32 flags);
|
||||
|
||||
public:
|
||||
// IDM data
|
||||
static const u32 id_base = 24;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 1000;
|
||||
|
||||
protected:
|
||||
lv2_socket(utils::serial &, bool);
|
||||
|
||||
shared_mutex mutex;
|
||||
s32 lv2_id = 0;
|
||||
|
||||
socket_type native_socket = 0;
|
||||
|
||||
lv2_socket_family family{};
|
||||
lv2_socket_type type{};
|
||||
lv2_ip_protocol protocol{};
|
||||
|
||||
// Events selected for polling
|
||||
atomic_bs_t<poll_t> events{};
|
||||
|
||||
// Event processing workload (pair of thread id and the processing function)
|
||||
std::vector<std::pair<shared_ptr<ppu_thread>,
|
||||
std::function<bool(rx::EnumBitSet<poll_t>)>>>
|
||||
queue;
|
||||
|
||||
// Socket options value keepers
|
||||
// Non-blocking IO option
|
||||
s32 so_nbio = 0;
|
||||
// Error, only used for connection result for non blocking stream sockets
|
||||
s32 so_error = 0;
|
||||
// Unsupported option
|
||||
s32 so_tcp_maxseg = 1500;
|
||||
#ifdef _WIN32
|
||||
s32 so_reuseaddr = 0;
|
||||
s32 so_reuseport = 0;
|
||||
|
||||
// Tracks connect for WSAPoll workaround
|
||||
bool connecting = false;
|
||||
#endif
|
||||
|
||||
sys_net_sockaddr last_bound_addr{};
|
||||
|
||||
public:
|
||||
u64 so_rcvtimeo = 0;
|
||||
u64 so_sendtimeo = 0;
|
||||
};
|
||||
84
kernel/cellos/include/cellos/sys_net/lv2_socket_native.h
Normal file
84
kernel/cellos/include/cellos/sys_net/lv2_socket_native.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "lv2_socket.h"
|
||||
|
||||
class lv2_socket_native final : public lv2_socket {
|
||||
public:
|
||||
static constexpr u32 id_type = 1;
|
||||
|
||||
lv2_socket_native(lv2_socket_family family, lv2_socket_type type,
|
||||
lv2_ip_protocol protocol);
|
||||
lv2_socket_native(utils::serial &ar, lv2_socket_type type);
|
||||
~lv2_socket_native() noexcept override;
|
||||
void save(utils::serial &ar);
|
||||
s32 create_socket();
|
||||
|
||||
std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr>
|
||||
accept(bool is_lock = true) override;
|
||||
s32 bind(const sys_net_sockaddr &addr) override;
|
||||
|
||||
std::optional<s32> connect(const sys_net_sockaddr &addr) override;
|
||||
s32 connect_followup() override;
|
||||
|
||||
std::pair<s32, sys_net_sockaddr> getpeername() override;
|
||||
std::pair<s32, sys_net_sockaddr> getsockname() override;
|
||||
std::tuple<s32, sockopt_data, u32> getsockopt(s32 level, s32 optname,
|
||||
u32 len) override;
|
||||
s32 setsockopt(s32 level, s32 optname,
|
||||
const std::vector<u8> &optval) override;
|
||||
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>>
|
||||
recvfrom(s32 flags, u32 len, bool is_lock = true) override;
|
||||
std::optional<s32> sendto(s32 flags, const std::vector<u8> &buf,
|
||||
std::optional<sys_net_sockaddr> opt_sn_addr,
|
||||
bool is_lock = true) override;
|
||||
std::optional<s32> sendmsg(s32 flags, const sys_net_msghdr &msg,
|
||||
bool is_lock = true) override;
|
||||
|
||||
s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) override;
|
||||
std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
|
||||
pollfd &native_pfd) override;
|
||||
|
||||
bool is_socket_connected();
|
||||
|
||||
s32 listen(s32 backlog) override;
|
||||
void close() override;
|
||||
s32 shutdown(s32 how) override;
|
||||
|
||||
private:
|
||||
void set_socket(socket_type native_socket, lv2_socket_family family,
|
||||
lv2_socket_type type, lv2_ip_protocol protocol);
|
||||
void set_default_buffers();
|
||||
void set_non_blocking();
|
||||
|
||||
private:
|
||||
// Value keepers
|
||||
#ifdef _WIN32
|
||||
s32 so_reuseaddr = 0;
|
||||
s32 so_reuseport = 0;
|
||||
#endif
|
||||
u16 bound_port = 0;
|
||||
bool feign_tcp_conn_failure = false; // Savestate load related
|
||||
};
|
||||
58
kernel/cellos/include/cellos/sys_net/lv2_socket_p2p.h
Normal file
58
kernel/cellos/include/cellos/sys_net/lv2_socket_p2p.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
#include "lv2_socket.h"
|
||||
#include <queue>
|
||||
|
||||
class lv2_socket_p2p : public lv2_socket {
|
||||
public:
|
||||
lv2_socket_p2p(lv2_socket_family family, lv2_socket_type type,
|
||||
lv2_ip_protocol protocol);
|
||||
lv2_socket_p2p(utils::serial &ar, lv2_socket_type type);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr>
|
||||
accept(bool is_lock = true) override;
|
||||
s32 bind(const sys_net_sockaddr &addr) override;
|
||||
|
||||
std::optional<s32> connect(const sys_net_sockaddr &addr) override;
|
||||
s32 connect_followup() override;
|
||||
|
||||
std::pair<s32, sys_net_sockaddr> getpeername() override;
|
||||
std::pair<s32, sys_net_sockaddr> getsockname() override;
|
||||
|
||||
std::tuple<s32, sockopt_data, u32> getsockopt(s32 level, s32 optname,
|
||||
u32 len) override;
|
||||
s32 setsockopt(s32 level, s32 optname,
|
||||
const std::vector<u8> &optval) override;
|
||||
|
||||
s32 listen(s32 backlog) override;
|
||||
|
||||
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>>
|
||||
recvfrom(s32 flags, u32 len, bool is_lock = true) override;
|
||||
std::optional<s32> sendto(s32 flags, const std::vector<u8> &buf,
|
||||
std::optional<sys_net_sockaddr> opt_sn_addr,
|
||||
bool is_lock = true) override;
|
||||
std::optional<s32> sendmsg(s32 flags, const sys_net_msghdr &msg,
|
||||
bool is_lock = true) override;
|
||||
|
||||
void close() override;
|
||||
s32 shutdown(s32 how) override;
|
||||
|
||||
s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) override;
|
||||
std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
|
||||
pollfd &native_pfd) override;
|
||||
|
||||
void handle_new_data(sys_net_sockaddr_in_p2p p2p_addr,
|
||||
std::vector<u8> p2p_data);
|
||||
|
||||
protected:
|
||||
// Port(actual bound port) and Virtual Port(indicated by u16 at the start of
|
||||
// the packet)
|
||||
u16 port = 3658, vport = 0;
|
||||
u32 bound_addr = 0;
|
||||
// Queue containing received packets from network_thread for
|
||||
// SYS_NET_SOCK_DGRAM_P2P sockets
|
||||
std::queue<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>> data{};
|
||||
// List of sock options
|
||||
std::map<u64, sockopt_cache> sockopts;
|
||||
};
|
||||
129
kernel/cellos/include/cellos/sys_net/lv2_socket_p2ps.h
Normal file
129
kernel/cellos/include/cellos/sys_net/lv2_socket_p2ps.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
#include <netinet/in.h>
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "lv2_socket_p2p.h"
|
||||
|
||||
struct nt_p2p_port;
|
||||
|
||||
constexpr be_t<u32> P2PS_U2S_SIG =
|
||||
(static_cast<u32>('U') << 24 | static_cast<u32>('2') << 16 |
|
||||
static_cast<u32>('S') << 8 | static_cast<u32>('0'));
|
||||
|
||||
struct p2ps_encapsulated_tcp {
|
||||
be_t<u32> signature =
|
||||
P2PS_U2S_SIG; // Signature to verify it's P2P Stream data
|
||||
be_t<u32> length = 0; // Length of data
|
||||
be_t<u64> seq = 0; // This should be u32 but changed to u64 for simplicity
|
||||
be_t<u64> ack = 0;
|
||||
be_t<u16> src_port = 0; // fake source tcp port
|
||||
be_t<u16> dst_port = 0; // fake dest tcp port(should be == vport)
|
||||
be_t<u16> checksum = 0;
|
||||
u8 flags = 0;
|
||||
};
|
||||
|
||||
enum p2ps_stream_status {
|
||||
stream_closed, // Default when port is not listening nor connected
|
||||
stream_listening, // Stream is listening, accepting SYN packets
|
||||
stream_handshaking, // Currently handshaking
|
||||
stream_connected, // This is an established connection(after tcp handshake)
|
||||
};
|
||||
|
||||
enum p2ps_tcp_flags : u8 {
|
||||
FIN = (1 << 0),
|
||||
SYN = (1 << 1),
|
||||
RST = (1 << 2),
|
||||
PSH = (1 << 3),
|
||||
ACK = (1 << 4),
|
||||
URG = (1 << 5),
|
||||
ECE = (1 << 6),
|
||||
CWR = (1 << 7),
|
||||
};
|
||||
|
||||
u16 u2s_tcp_checksum(const le_t<u16> *buffer, usz size);
|
||||
std::vector<u8> generate_u2s_packet(const p2ps_encapsulated_tcp &header,
|
||||
const u8 *data, const u32 datasize);
|
||||
|
||||
class lv2_socket_p2ps final : public lv2_socket_p2p {
|
||||
public:
|
||||
static constexpr u32 id_type = 2;
|
||||
|
||||
lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type,
|
||||
lv2_ip_protocol protocol);
|
||||
lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr,
|
||||
u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq,
|
||||
s32 so_nbio);
|
||||
lv2_socket_p2ps(utils::serial &ar, lv2_socket_type type);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
p2ps_stream_status get_status() const;
|
||||
void set_status(p2ps_stream_status new_status);
|
||||
bool handle_connected(p2ps_encapsulated_tcp *tcp_header, u8 *data,
|
||||
::sockaddr_storage *op_addr, nt_p2p_port *p2p_port);
|
||||
bool handle_listening(p2ps_encapsulated_tcp *tcp_header, u8 *data,
|
||||
::sockaddr_storage *op_addr);
|
||||
void send_u2s_packet(std::vector<u8> data, const ::sockaddr_in *dst, u64 seq,
|
||||
bool require_ack);
|
||||
void close_stream();
|
||||
|
||||
std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr>
|
||||
accept(bool is_lock = true) override;
|
||||
s32 bind(const sys_net_sockaddr &addr) override;
|
||||
|
||||
std::optional<s32> connect(const sys_net_sockaddr &addr) override;
|
||||
|
||||
std::pair<s32, sys_net_sockaddr> getpeername() override;
|
||||
std::pair<s32, sys_net_sockaddr> getsockname() override;
|
||||
|
||||
s32 listen(s32 backlog) override;
|
||||
|
||||
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>>
|
||||
recvfrom(s32 flags, u32 len, bool is_lock = true) override;
|
||||
std::optional<s32> sendto(s32 flags, const std::vector<u8> &buf,
|
||||
std::optional<sys_net_sockaddr> opt_sn_addr,
|
||||
bool is_lock = true) override;
|
||||
std::optional<s32> sendmsg(s32 flags, const sys_net_msghdr &msg,
|
||||
bool is_lock = true) override;
|
||||
|
||||
void close() override;
|
||||
s32 shutdown(s32 how) override;
|
||||
|
||||
s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) override;
|
||||
std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
|
||||
pollfd &native_pfd) override;
|
||||
|
||||
private:
|
||||
void close_stream_nl(nt_p2p_port *p2p_port);
|
||||
|
||||
private:
|
||||
static constexpr usz MAX_RECEIVED_BUFFER = (1024 * 1024 * 10);
|
||||
|
||||
p2ps_stream_status status = p2ps_stream_status::stream_closed;
|
||||
|
||||
usz max_backlog = 0; // set on listen
|
||||
std::deque<s32> backlog;
|
||||
|
||||
u16 op_port = 0, op_vport = 0;
|
||||
u32 op_addr = 0;
|
||||
|
||||
u64 data_beg_seq = 0; // Seq of first byte of received_data
|
||||
u64 data_available =
|
||||
0; // Amount of continuous data available(calculated on ACK send)
|
||||
std::map<u64, std::vector<u8>>
|
||||
received_data; // holds seq/data of data received
|
||||
|
||||
u64 cur_seq = 0; // SEQ of next packet to be sent
|
||||
};
|
||||
45
kernel/cellos/include/cellos/sys_net/lv2_socket_raw.h
Normal file
45
kernel/cellos/include/cellos/sys_net/lv2_socket_raw.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include "lv2_socket.h"
|
||||
|
||||
class lv2_socket_raw final : public lv2_socket {
|
||||
public:
|
||||
static constexpr u32 id_type = 1;
|
||||
|
||||
lv2_socket_raw(lv2_socket_family family, lv2_socket_type type,
|
||||
lv2_ip_protocol protocol);
|
||||
lv2_socket_raw(utils::serial &ar, lv2_socket_type type);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr>
|
||||
accept(bool is_lock = true) override;
|
||||
s32 bind(const sys_net_sockaddr &addr) override;
|
||||
|
||||
std::optional<s32> connect(const sys_net_sockaddr &addr) override;
|
||||
s32 connect_followup() override;
|
||||
|
||||
std::pair<s32, sys_net_sockaddr> getpeername() override;
|
||||
std::pair<s32, sys_net_sockaddr> getsockname() override;
|
||||
|
||||
std::tuple<s32, sockopt_data, u32> getsockopt(s32 level, s32 optname,
|
||||
u32 len) override;
|
||||
s32 setsockopt(s32 level, s32 optname,
|
||||
const std::vector<u8> &optval) override;
|
||||
|
||||
s32 listen(s32 backlog) override;
|
||||
|
||||
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>>
|
||||
recvfrom(s32 flags, u32 len, bool is_lock = true) override;
|
||||
std::optional<s32> sendto(s32 flags, const std::vector<u8> &buf,
|
||||
std::optional<sys_net_sockaddr> opt_sn_addr,
|
||||
bool is_lock = true) override;
|
||||
std::optional<s32> sendmsg(s32 flags, const sys_net_msghdr &msg,
|
||||
bool is_lock = true) override;
|
||||
|
||||
void close() override;
|
||||
s32 shutdown(s32 how) override;
|
||||
|
||||
s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) override;
|
||||
std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
|
||||
pollfd &native_pfd) override;
|
||||
};
|
||||
45
kernel/cellos/include/cellos/sys_net/network_context.h
Normal file
45
kernel/cellos/include/cellos/sys_net/network_context.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "util/mutex.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "nt_p2p_port.h"
|
||||
|
||||
struct base_network_thread {
|
||||
void add_ppu_to_awake(ppu_thread *ppu);
|
||||
void del_ppu_to_awake(ppu_thread *ppu);
|
||||
|
||||
shared_mutex mutex_ppu_to_awake;
|
||||
std::vector<ppu_thread *> ppu_to_awake;
|
||||
|
||||
void wake_threads();
|
||||
};
|
||||
|
||||
struct network_thread : base_network_thread {
|
||||
shared_mutex mutex_thread_loop;
|
||||
atomic_t<u32> num_polls = 0;
|
||||
|
||||
static constexpr auto thread_name = "Network Thread";
|
||||
|
||||
void operator()();
|
||||
};
|
||||
|
||||
struct p2p_thread : base_network_thread {
|
||||
shared_mutex list_p2p_ports_mutex;
|
||||
std::map<u16, nt_p2p_port> list_p2p_ports;
|
||||
atomic_t<u32> num_p2p_ports = 0;
|
||||
|
||||
static constexpr auto thread_name = "Network P2P Thread";
|
||||
|
||||
p2p_thread();
|
||||
|
||||
void create_p2p_port(u16 p2p_port);
|
||||
|
||||
void bind_sce_np_port();
|
||||
void operator()();
|
||||
};
|
||||
|
||||
using network_context = named_thread<network_thread>;
|
||||
using p2p_context = named_thread<p2p_thread>;
|
||||
82
kernel/cellos/include/cellos/sys_net/nt_p2p_port.h
Normal file
82
kernel/cellos/include/cellos/sys_net/nt_p2p_port.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "lv2_socket_p2ps.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// dst_vport src_vport flags
|
||||
constexpr s32 VPORT_P2P_HEADER_SIZE = sizeof(u16) + sizeof(u16) + sizeof(u16);
|
||||
|
||||
enum VPORT_P2P_FLAGS {
|
||||
P2P_FLAG_P2P = 1,
|
||||
P2P_FLAG_P2PS = 1 << 1,
|
||||
};
|
||||
|
||||
struct signaling_message {
|
||||
u32 src_addr = 0;
|
||||
u16 src_port = 0;
|
||||
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
namespace sys_net_helpers {
|
||||
bool all_reusable(const std::set<s32> &sock_ids);
|
||||
}
|
||||
|
||||
struct nt_p2p_port {
|
||||
// Real socket where P2P packets are received/sent
|
||||
socket_type p2p_socket = 0;
|
||||
u16 port = 0;
|
||||
|
||||
bool is_ipv6 = false;
|
||||
|
||||
shared_mutex bound_p2p_vports_mutex;
|
||||
// For DGRAM_P2P sockets (vport, sock_ids)
|
||||
std::map<u16, std::set<s32>> bound_p2p_vports{};
|
||||
// For STREAM_P2P sockets (vport, sock_ids)
|
||||
std::map<u16, std::set<s32>> bound_p2ps_vports{};
|
||||
// List of active(either from a connect or an accept) P2PS sockets (key,
|
||||
// sock_id) key is ( (src_vport) << 48 | (dst_vport) << 32 | addr ) with
|
||||
// src_vport and addr being 0 for listening sockets
|
||||
std::map<u64, s32> bound_p2p_streams{};
|
||||
// Current free port index
|
||||
u16 binding_port = 30000;
|
||||
|
||||
// Queued messages from RPCN
|
||||
shared_mutex s_rpcn_mutex;
|
||||
std::vector<std::vector<u8>> rpcn_msgs{};
|
||||
// Queued signaling messages
|
||||
shared_mutex s_sign_mutex;
|
||||
std::vector<signaling_message> sign_msgs{};
|
||||
|
||||
std::array<u8, 65535> p2p_recv_data{};
|
||||
|
||||
nt_p2p_port(u16 port);
|
||||
~nt_p2p_port();
|
||||
|
||||
static void dump_packet(p2ps_encapsulated_tcp *tcph);
|
||||
|
||||
u16 get_port();
|
||||
|
||||
bool handle_connected(s32 sock_id, p2ps_encapsulated_tcp *tcp_header,
|
||||
u8 *data, ::sockaddr_storage *op_addr);
|
||||
bool handle_listening(s32 sock_id, p2ps_encapsulated_tcp *tcp_header,
|
||||
u8 *data, ::sockaddr_storage *op_addr);
|
||||
bool recv_data();
|
||||
};
|
||||
34
kernel/cellos/include/cellos/sys_net/sys_net_helpers.h
Normal file
34
kernel/cellos/include/cellos/sys_net/sys_net_helpers.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "cellos/sys_net.h"
|
||||
|
||||
int get_native_error();
|
||||
sys_net_error convert_error(bool is_blocking, int native_error,
|
||||
bool is_connecting = false);
|
||||
sys_net_error get_last_error(bool is_blocking, bool is_connecting = false);
|
||||
sys_net_sockaddr
|
||||
native_addr_to_sys_net_addr(const ::sockaddr_storage &native_addr);
|
||||
::sockaddr_in sys_net_addr_to_native_addr(const sys_net_sockaddr &sn_addr);
|
||||
bool is_ip_public_address(const ::sockaddr_in &addr);
|
||||
u32 network_clear_queue(ppu_thread &ppu);
|
||||
void clear_ppu_to_awake(ppu_thread &ppu);
|
||||
|
||||
#ifdef _WIN32
|
||||
void windows_poll(std::vector<pollfd> &fds, unsigned long nfds, int timeout,
|
||||
std::vector<bool> &connecting);
|
||||
#endif
|
||||
|
|
@ -4,22 +4,26 @@
|
|||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "sys_sync.h"
|
||||
|
||||
struct lv2_overlay final : ppu_module<lv2_obj>
|
||||
{
|
||||
static const u32 id_base = 0x25000000;
|
||||
struct lv2_overlay final : ppu_module<lv2_obj> {
|
||||
static const u32 id_base = 0x25000000;
|
||||
|
||||
u32 entry{};
|
||||
u32 seg0_code_end{};
|
||||
u32 entry{};
|
||||
u32 seg0_code_end{};
|
||||
|
||||
lv2_overlay() = default;
|
||||
lv2_overlay(utils::serial&) {}
|
||||
static std::function<void(void*)> load(utils::serial& ar);
|
||||
void save(utils::serial& ar);
|
||||
lv2_overlay() = default;
|
||||
lv2_overlay(utils::serial &) {}
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
error_code sys_overlay_load_module(vm::ptr<u32> ovlmid, vm::cptr<char> path, u64 flags, vm::ptr<u32> entry);
|
||||
error_code sys_overlay_load_module_by_fd(vm::ptr<u32> ovlmid, u32 fd, u64 offset, u64 flags, vm::ptr<u32> entry);
|
||||
error_code sys_overlay_load_module(vm::ptr<u32> ovlmid, vm::cptr<char> path,
|
||||
u64 flags, vm::ptr<u32> entry);
|
||||
error_code sys_overlay_load_module_by_fd(vm::ptr<u32> ovlmid, u32 fd,
|
||||
u64 offset, u64 flags,
|
||||
vm::ptr<u32> entry);
|
||||
error_code sys_overlay_unload_module(u32 ovlmid);
|
||||
|
||||
// clang-format off
|
||||
// error_code sys_overlay_get_module_list(sys_pid_t pid, usz ovlmids_num, sys_overlay_t * ovlmids, usz * num_of_modules);
|
||||
// error_code sys_overlay_get_module_info(sys_pid_t pid, sys_overlay_t ovlmid, sys_overlay_module_info_t * info);
|
||||
// error_code sys_overlay_get_module_info2(sys_pid_t pid, sys_overlay_t ovlmid, sys_overlay_module_info2_t * info);//
|
||||
|
|
@ -27,3 +31,4 @@ error_code sys_overlay_unload_module(u32 ovlmid);
|
|||
// error_code sys_overlay_get_module_dbg_info(); //3 params?
|
||||
|
||||
// error_code _sys_prx_load_module(vm::ps3::cptr<char> path, u64 flags, vm::ps3::ptr<sys_prx_load_module_option_t> pOpt);
|
||||
// clang-format on
|
||||
69
kernel/cellos/include/cellos/sys_ppu_thread.h
Normal file
69
kernel/cellos/include/cellos/sys_ppu_thread.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
enum : s32 {
|
||||
SYS_PPU_THREAD_ONCE_INIT = 0,
|
||||
SYS_PPU_THREAD_DONE_INIT = 1,
|
||||
};
|
||||
|
||||
// PPU Thread Flags
|
||||
enum : u64 {
|
||||
SYS_PPU_THREAD_CREATE_JOINABLE = 0x1,
|
||||
SYS_PPU_THREAD_CREATE_INTERRUPT = 0x2,
|
||||
};
|
||||
|
||||
struct sys_ppu_thread_stack_t {
|
||||
be_t<u32> pst_addr;
|
||||
be_t<u32> pst_size;
|
||||
};
|
||||
|
||||
struct ppu_thread_param_t {
|
||||
vm::bptr<void(u64)> entry;
|
||||
be_t<u32> tls; // vm::bptr<void>
|
||||
};
|
||||
|
||||
struct sys_ppu_thread_icontext_t {
|
||||
be_t<u64> gpr[32];
|
||||
be_t<u32> cr;
|
||||
be_t<u32> rsv1;
|
||||
be_t<u64> xer;
|
||||
be_t<u64> lr;
|
||||
be_t<u64> ctr;
|
||||
be_t<u64> pc;
|
||||
};
|
||||
|
||||
// Syscalls
|
||||
|
||||
void _sys_ppu_thread_exit(ppu_thread &ppu, u64 errorcode);
|
||||
s32 sys_ppu_thread_yield(
|
||||
ppu_thread &ppu); // Return value is ignored by the library
|
||||
error_code sys_ppu_thread_join(ppu_thread &ppu, u32 thread_id,
|
||||
vm::ptr<u64> vptr);
|
||||
error_code sys_ppu_thread_detach(ppu_thread &ppu, u32 thread_id);
|
||||
error_code sys_ppu_thread_get_join_state(
|
||||
ppu_thread &ppu,
|
||||
vm::ptr<s32> isjoinable); // Error code is ignored by the library
|
||||
error_code sys_ppu_thread_set_priority(ppu_thread &ppu, u32 thread_id,
|
||||
s32 prio);
|
||||
error_code sys_ppu_thread_get_priority(ppu_thread &ppu, u32 thread_id,
|
||||
vm::ptr<s32> priop);
|
||||
error_code
|
||||
sys_ppu_thread_get_stack_information(ppu_thread &ppu,
|
||||
vm::ptr<sys_ppu_thread_stack_t> sp);
|
||||
error_code sys_ppu_thread_stop(ppu_thread &ppu, u32 thread_id);
|
||||
error_code sys_ppu_thread_restart(ppu_thread &ppu);
|
||||
error_code _sys_ppu_thread_create(ppu_thread &ppu, vm::ptr<u64> thread_id,
|
||||
vm::ptr<ppu_thread_param_t> param, u64 arg,
|
||||
u64 arg4, s32 prio, u32 stacksize, u64 flags,
|
||||
vm::cptr<char> threadname);
|
||||
error_code sys_ppu_thread_start(ppu_thread &ppu, u32 thread_id);
|
||||
error_code sys_ppu_thread_rename(ppu_thread &ppu, u32 thread_id,
|
||||
vm::cptr<char> name);
|
||||
error_code sys_ppu_thread_recover_page_fault(ppu_thread &ppu, u32 thread_id);
|
||||
error_code
|
||||
sys_ppu_thread_get_page_fault_context(ppu_thread &ppu, u32 thread_id,
|
||||
vm::ptr<sys_ppu_thread_icontext_t> ctxt);
|
||||
130
kernel/cellos/include/cellos/sys_process.h
Normal file
130
kernel/cellos/include/cellos/sys_process.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include "Crypto/unself.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// Process Local Object Type
|
||||
enum : u32 {
|
||||
SYS_MEM_OBJECT = 0x08,
|
||||
SYS_MUTEX_OBJECT = 0x85,
|
||||
SYS_COND_OBJECT = 0x86,
|
||||
SYS_RWLOCK_OBJECT = 0x88,
|
||||
SYS_INTR_TAG_OBJECT = 0x0A,
|
||||
SYS_INTR_SERVICE_HANDLE_OBJECT = 0x0B,
|
||||
SYS_EVENT_QUEUE_OBJECT = 0x8D,
|
||||
SYS_EVENT_PORT_OBJECT = 0x0E,
|
||||
SYS_TRACE_OBJECT = 0x21,
|
||||
SYS_SPUIMAGE_OBJECT = 0x22,
|
||||
SYS_PRX_OBJECT = 0x23,
|
||||
SYS_SPUPORT_OBJECT = 0x24,
|
||||
SYS_OVERLAY_OBJECT = 0x25,
|
||||
SYS_LWMUTEX_OBJECT = 0x95,
|
||||
SYS_TIMER_OBJECT = 0x11,
|
||||
SYS_SEMAPHORE_OBJECT = 0x96,
|
||||
SYS_FS_FD_OBJECT = 0x73,
|
||||
SYS_LWCOND_OBJECT = 0x97,
|
||||
SYS_EVENT_FLAG_OBJECT = 0x98,
|
||||
SYS_RSXAUDIO_OBJECT = 0x60,
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_PROCESS_PRIMARY_STACK_SIZE_32K = 0x0000000000000010,
|
||||
SYS_PROCESS_PRIMARY_STACK_SIZE_64K = 0x0000000000000020,
|
||||
SYS_PROCESS_PRIMARY_STACK_SIZE_96K = 0x0000000000000030,
|
||||
SYS_PROCESS_PRIMARY_STACK_SIZE_128K = 0x0000000000000040,
|
||||
SYS_PROCESS_PRIMARY_STACK_SIZE_256K = 0x0000000000000050,
|
||||
SYS_PROCESS_PRIMARY_STACK_SIZE_512K = 0x0000000000000060,
|
||||
SYS_PROCESS_PRIMARY_STACK_SIZE_1M = 0x0000000000000070,
|
||||
};
|
||||
|
||||
constexpr auto SYS_PROCESS_PARAM_SECTION_NAME = ".sys_proc_param";
|
||||
|
||||
enum {
|
||||
SYS_PROCESS_PARAM_INVALID_PRIO = -32768,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
SYS_PROCESS_PARAM_INVALID_STACK_SIZE = 0xffffffff,
|
||||
|
||||
SYS_PROCESS_PARAM_STACK_SIZE_MIN = 0x1000, // 4KB
|
||||
SYS_PROCESS_PARAM_STACK_SIZE_MAX = 0x100000, // 1MB
|
||||
|
||||
SYS_PROCESS_PARAM_VERSION_INVALID = 0xffffffff,
|
||||
SYS_PROCESS_PARAM_VERSION_1 = 0x00000001, // for SDK 08X
|
||||
SYS_PROCESS_PARAM_VERSION_084_0 = 0x00008400,
|
||||
SYS_PROCESS_PARAM_VERSION_090_0 = 0x00009000,
|
||||
SYS_PROCESS_PARAM_VERSION_330_0 = 0x00330000,
|
||||
|
||||
SYS_PROCESS_PARAM_MAGIC = 0x13bcc5f6,
|
||||
|
||||
SYS_PROCESS_PARAM_MALLOC_PAGE_SIZE_NONE = 0x00000000,
|
||||
SYS_PROCESS_PARAM_MALLOC_PAGE_SIZE_64K = 0x00010000,
|
||||
SYS_PROCESS_PARAM_MALLOC_PAGE_SIZE_1M = 0x00100000,
|
||||
|
||||
SYS_PROCESS_PARAM_PPC_SEG_DEFAULT = 0x00000000,
|
||||
SYS_PROCESS_PARAM_PPC_SEG_OVLM = 0x00000001,
|
||||
SYS_PROCESS_PARAM_PPC_SEG_FIXEDADDR_PRX = 0x00000002,
|
||||
|
||||
SYS_PROCESS_PARAM_SDK_VERSION_UNKNOWN = 0xffffffff,
|
||||
};
|
||||
|
||||
struct sys_exit2_param {
|
||||
be_t<u64> x0; // 0x85
|
||||
be_t<u64> this_size; // 0x30
|
||||
be_t<u64> next_size;
|
||||
be_t<s64> prio;
|
||||
be_t<u64> flags;
|
||||
vm::bpptr<char, u64, u64> args;
|
||||
};
|
||||
|
||||
struct ps3_process_info_t {
|
||||
u32 sdk_ver;
|
||||
u32 ppc_seg;
|
||||
SelfAdditionalInfo self_info;
|
||||
u32 ctrl_flags1 = 0;
|
||||
|
||||
bool has_root_perm() const;
|
||||
bool has_debug_perm() const;
|
||||
bool debug_or_root() const;
|
||||
std::string_view get_cellos_appname() const;
|
||||
};
|
||||
|
||||
extern ps3_process_info_t g_ps3_process_info;
|
||||
|
||||
// Auxiliary functions
|
||||
s32 process_getpid();
|
||||
s32 process_get_sdk_version(u32 pid, s32 &ver);
|
||||
void lv2_exitspawn(ppu_thread &ppu, std::vector<std::string> &argv,
|
||||
std::vector<std::string> &envp, std::vector<u8> &data);
|
||||
|
||||
enum CellError : u32;
|
||||
CellError process_is_spu_lock_line_reservation_address(u32 addr, u64 flags);
|
||||
|
||||
// SysCalls
|
||||
s32 sys_process_getpid();
|
||||
s32 sys_process_getppid();
|
||||
error_code sys_process_get_number_of_object(u32 object, vm::ptr<u32> nump);
|
||||
error_code sys_process_get_id(u32 object, vm::ptr<u32> buffer, u32 size,
|
||||
vm::ptr<u32> set_size);
|
||||
error_code sys_process_get_id2(u32 object, vm::ptr<u32> buffer, u32 size,
|
||||
vm::ptr<u32> set_size);
|
||||
error_code _sys_process_get_paramsfo(vm::ptr<char> buffer);
|
||||
error_code sys_process_get_sdk_version(u32 pid, vm::ptr<s32> version);
|
||||
error_code sys_process_get_status(u64 unk);
|
||||
error_code sys_process_is_spu_lock_line_reservation_address(u32 addr,
|
||||
u64 flags);
|
||||
error_code sys_process_kill(u32 pid);
|
||||
error_code sys_process_wait_for_child(u32 pid, vm::ptr<u32> status, u64 unk);
|
||||
error_code sys_process_wait_for_child2(u64 unk1, u64 unk2, u64 unk3, u64 unk4,
|
||||
u64 unk5, u64 unk6);
|
||||
error_code sys_process_detach_child(u64 unk);
|
||||
void _sys_process_exit(ppu_thread &ppu, s32 status, u32 arg2, u32 arg3);
|
||||
void _sys_process_exit2(ppu_thread &ppu, s32 status,
|
||||
vm::ptr<sys_exit2_param> arg, u32 arg_size, u32 arg4);
|
||||
void sys_process_exit3(ppu_thread &ppu, s32 status);
|
||||
error_code sys_process_spawns_a_self2(vm::ptr<u32> pid, u32 primary_prio,
|
||||
u64 flags, vm::ptr<void> stack,
|
||||
u32 stack_size, u32 mem_id,
|
||||
vm::ptr<void> param_sfo,
|
||||
vm::ptr<void> dbg_data);
|
||||
293
kernel/cellos/include/cellos/sys_prx.h
Normal file
293
kernel/cellos/include/cellos/sys_prx.h
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/PPUAnalyser.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// Return codes
|
||||
enum CellPrxError : u32 {
|
||||
CELL_PRX_ERROR_ERROR = 0x80011001, // Error state
|
||||
CELL_PRX_ERROR_ILLEGAL_PERM = 0x800110d1, // No permission to execute API
|
||||
CELL_PRX_ERROR_UNKNOWN_MODULE =
|
||||
0x8001112e, // Specified PRX could not be found
|
||||
CELL_PRX_ERROR_ALREADY_STARTED =
|
||||
0x80011133, // Specified PRX is already started
|
||||
CELL_PRX_ERROR_NOT_STARTED = 0x80011134, // Specified PRX is not started
|
||||
CELL_PRX_ERROR_ALREADY_STOPPED =
|
||||
0x80011135, // Specified PRX is already stopped
|
||||
CELL_PRX_ERROR_CAN_NOT_STOP = 0x80011136, // Specified PRX must not be stopped
|
||||
CELL_PRX_ERROR_NOT_REMOVABLE =
|
||||
0x80011138, // Specified PRX must not be deleted
|
||||
CELL_PRX_ERROR_LIBRARY_NOT_YET_LINKED =
|
||||
0x8001113a, // Called unlinked function
|
||||
CELL_PRX_ERROR_LIBRARY_FOUND =
|
||||
0x8001113b, // Specified library is already registered
|
||||
CELL_PRX_ERROR_LIBRARY_NOTFOUND =
|
||||
0x8001113c, // Specified library is not registered
|
||||
CELL_PRX_ERROR_ILLEGAL_LIBRARY = 0x8001113d, // Library structure is invalid
|
||||
CELL_PRX_ERROR_LIBRARY_INUSE =
|
||||
0x8001113e, // Library cannot be deleted because it is linked
|
||||
CELL_PRX_ERROR_ALREADY_STOPPING =
|
||||
0x8001113f, // Specified PRX is in the process of stopping
|
||||
CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE =
|
||||
0x80011148, // Specified PRX format is invalid and cannot be loaded
|
||||
CELL_PRX_ERROR_INVAL = 0x80011324, // Argument value is invalid
|
||||
CELL_PRX_ERROR_ILLEGAL_PROCESS =
|
||||
0x80011801, // Specified process does not exist
|
||||
CELL_PRX_ERROR_NO_LIBLV2 = 0x80011881, // liblv2.sprx does not exist
|
||||
CELL_PRX_ERROR_UNSUPPORTED_ELF_TYPE =
|
||||
0x80011901, // ELF type of specified file is not supported
|
||||
CELL_PRX_ERROR_UNSUPPORTED_ELF_CLASS =
|
||||
0x80011902, // ELF class of specified file is not supported
|
||||
CELL_PRX_ERROR_UNDEFINED_SYMBOL = 0x80011904, // References undefined symbols
|
||||
CELL_PRX_ERROR_UNSUPPORTED_RELOCATION_TYPE =
|
||||
0x80011905, // Uses unsupported relocation type
|
||||
CELL_PRX_ERROR_ELF_IS_REGISTERED =
|
||||
0x80011910, // Fixed ELF is already registered
|
||||
CELL_PRX_ERROR_NO_EXIT_ENTRY = 0x80011911,
|
||||
};
|
||||
|
||||
enum { SYS_PRX_MODULE_FILENAME_SIZE = 512 };
|
||||
|
||||
struct sys_prx_get_module_id_by_name_option_t {
|
||||
be_t<u64> size;
|
||||
vm::ptr<void> base;
|
||||
};
|
||||
|
||||
struct sys_prx_load_module_option_t {
|
||||
be_t<u64> size;
|
||||
vm::bptr<void> base_addr;
|
||||
};
|
||||
|
||||
struct sys_prx_segment_info_t {
|
||||
be_t<u64> base;
|
||||
be_t<u64> filesz;
|
||||
be_t<u64> memsz;
|
||||
be_t<u64> index;
|
||||
be_t<u64> type;
|
||||
};
|
||||
|
||||
struct sys_prx_module_info_t {
|
||||
be_t<u64> size; // 0
|
||||
char name[30]; // 8
|
||||
char version[2]; // 0x26
|
||||
be_t<u32> modattribute; // 0x28
|
||||
be_t<u32> start_entry; // 0x2c
|
||||
be_t<u32> stop_entry; // 0x30
|
||||
be_t<u32> all_segments_num; // 0x34
|
||||
vm::bptr<char> filename; // 0x38
|
||||
be_t<u32> filename_size; // 0x3c
|
||||
vm::bptr<sys_prx_segment_info_t> segments; // 0x40
|
||||
be_t<u32> segments_num; // 0x44
|
||||
};
|
||||
|
||||
struct sys_prx_module_info_v2_t : sys_prx_module_info_t {
|
||||
be_t<u32> exports_addr; // 0x48
|
||||
be_t<u32> exports_size; // 0x4C
|
||||
be_t<u32> imports_addr; // 0x50
|
||||
be_t<u32> imports_size; // 0x54
|
||||
};
|
||||
|
||||
struct sys_prx_module_info_option_t {
|
||||
be_t<u64> size; // 0x10
|
||||
union {
|
||||
vm::bptr<sys_prx_module_info_t> info;
|
||||
vm::bptr<sys_prx_module_info_v2_t> info_v2;
|
||||
};
|
||||
};
|
||||
|
||||
struct sys_prx_start_module_option_t {
|
||||
be_t<u64> size;
|
||||
};
|
||||
|
||||
struct sys_prx_stop_module_option_t {
|
||||
be_t<u64> size;
|
||||
};
|
||||
|
||||
struct sys_prx_start_stop_module_option_t {
|
||||
be_t<u64> size;
|
||||
be_t<u64> cmd;
|
||||
vm::bptr<s32(u32 argc, vm::ptr<void> argv), u64> entry;
|
||||
be_t<u64> res;
|
||||
vm::bptr<s32(vm::ptr<s32(u32, vm::ptr<void>), u64>, u32 argc,
|
||||
vm::ptr<void> argv),
|
||||
u64>
|
||||
entry2;
|
||||
};
|
||||
|
||||
struct sys_prx_unload_module_option_t {
|
||||
be_t<u64> size;
|
||||
};
|
||||
|
||||
struct sys_prx_get_module_list_t {
|
||||
be_t<u64> size;
|
||||
be_t<u32> max;
|
||||
be_t<u32> count;
|
||||
vm::bptr<u32> idlist;
|
||||
};
|
||||
|
||||
struct sys_prx_get_module_list_option_t {
|
||||
be_t<u64> size; // 0x20
|
||||
be_t<u32> pad;
|
||||
be_t<u32> max;
|
||||
be_t<u32> count;
|
||||
vm::bptr<u32> idlist;
|
||||
be_t<u32> unk; // 0
|
||||
};
|
||||
|
||||
struct sys_prx_register_module_0x20_t {
|
||||
be_t<u64> size; // 0x0
|
||||
be_t<u32> toc; // 0x8
|
||||
be_t<u32> toc_size; // 0xC
|
||||
vm::bptr<void> stubs_ea; // 0x10
|
||||
be_t<u32> stubs_size; // 0x14
|
||||
vm::bptr<void> error_handler; // 0x18
|
||||
char pad[4]; // 0x1C
|
||||
};
|
||||
|
||||
struct sys_prx_register_module_0x30_type_1_t {
|
||||
be_t<u64> size; // 0x0
|
||||
be_t<u64> type; // 0x8
|
||||
be_t<u32> unk3; // 0x10
|
||||
be_t<u32> unk4; // 0x14
|
||||
vm::bptr<void> lib_entries_ea; // 0x18
|
||||
be_t<u32> lib_entries_size; // 0x1C
|
||||
vm::bptr<void> lib_stub_ea; // 0x20
|
||||
be_t<u32> lib_stub_size; // 0x24
|
||||
vm::bptr<void> error_handler; // 0x28
|
||||
char pad[4]; // 0x2C
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
SYS_PRX_RESIDENT = 0,
|
||||
SYS_PRX_NO_RESIDENT = 1,
|
||||
|
||||
SYS_PRX_START_OK = 0,
|
||||
|
||||
SYS_PRX_STOP_SUCCESS = 0,
|
||||
SYS_PRX_STOP_OK = 0,
|
||||
SYS_PRX_STOP_FAILED = 1
|
||||
};
|
||||
|
||||
// Unofficial names for PRX state
|
||||
enum : u32 {
|
||||
PRX_STATE_INITIALIZED,
|
||||
PRX_STATE_STARTING, // In-between state between initialized and started
|
||||
// (internal)
|
||||
PRX_STATE_STARTED,
|
||||
PRX_STATE_STOPPING, // In-between state between started and stopped (internal)
|
||||
PRX_STATE_STOPPED, // Last state, the module cannot be restarted
|
||||
PRX_STATE_DESTROYED, // Last state, the module cannot be restarted
|
||||
};
|
||||
|
||||
struct lv2_prx final : ppu_module<lv2_obj> {
|
||||
static const u32 id_base = 0x23000000;
|
||||
|
||||
atomic_t<u32> state = PRX_STATE_INITIALIZED;
|
||||
shared_mutex mutex;
|
||||
|
||||
std::unordered_map<u32, u32> specials;
|
||||
|
||||
vm::ptr<s32(u32 argc, vm::ptr<void> argv)> start = vm::null;
|
||||
vm::ptr<s32(u32 argc, vm::ptr<void> argv)> stop = vm::null;
|
||||
vm::ptr<s32(u64 callback, u64 argc, vm::ptr<void, u64> argv)> prologue =
|
||||
vm::null;
|
||||
vm::ptr<s32(u64 callback, u64 argc, vm::ptr<void, u64> argv)> epilogue =
|
||||
vm::null;
|
||||
vm::ptr<s32()> exit = vm::null;
|
||||
|
||||
char module_info_name[28]{};
|
||||
u8 module_info_version[2]{};
|
||||
be_t<u16> module_info_attributes{};
|
||||
|
||||
u32 imports_start = umax;
|
||||
u32 imports_end = 0;
|
||||
|
||||
u32 exports_start = umax;
|
||||
u32 exports_end = 0;
|
||||
|
||||
std::basic_string<char> m_loaded_flags;
|
||||
std::basic_string<char> m_external_loaded_flags;
|
||||
|
||||
void load_exports(); // (Re)load exports
|
||||
void restore_exports(); // For savestates
|
||||
void unload_exports();
|
||||
|
||||
lv2_prx() noexcept = default;
|
||||
lv2_prx(utils::serial &) {}
|
||||
static std::function<void(void *)> load(utils::serial &);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_PRX_LOAD_MODULE_FLAGS_FIXEDADDR = 0x1ull,
|
||||
SYS_PRX_LOAD_MODULE_FLAGS_INVALIDMASK = ~SYS_PRX_LOAD_MODULE_FLAGS_FIXEDADDR,
|
||||
};
|
||||
|
||||
// PPC
|
||||
enum {
|
||||
SYS_PRX_R_PPC_ADDR32 = 1,
|
||||
SYS_PRX_R_PPC_ADDR16_LO = 4,
|
||||
SYS_PRX_R_PPC_ADDR16_HI = 5,
|
||||
SYS_PRX_R_PPC_ADDR16_HA = 6,
|
||||
|
||||
SYS_PRX_R_PPC64_ADDR32 = SYS_PRX_R_PPC_ADDR32,
|
||||
SYS_PRX_R_PPC64_ADDR16_LO = SYS_PRX_R_PPC_ADDR16_LO,
|
||||
SYS_PRX_R_PPC64_ADDR16_HI = SYS_PRX_R_PPC_ADDR16_HI,
|
||||
SYS_PRX_R_PPC64_ADDR16_HA = SYS_PRX_R_PPC_ADDR16_HA,
|
||||
SYS_PRX_R_PPC64_ADDR64 = 38,
|
||||
SYS_PRX_VARLINK_TERMINATE32 = 0x00000000
|
||||
};
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_prx_get_ppu_guid(ppu_thread &ppu);
|
||||
error_code
|
||||
_sys_prx_load_module_by_fd(ppu_thread &ppu, s32 fd, u64 offset, u64 flags,
|
||||
vm::ptr<sys_prx_load_module_option_t> pOpt);
|
||||
error_code _sys_prx_load_module_on_memcontainer_by_fd(
|
||||
ppu_thread &ppu, s32 fd, u64 offset, u32 mem_ct, u64 flags,
|
||||
vm::ptr<sys_prx_load_module_option_t> pOpt);
|
||||
error_code _sys_prx_load_module_list(ppu_thread &ppu, s32 count,
|
||||
vm::cpptr<char, u32, u64> path_list,
|
||||
u64 flags,
|
||||
vm::ptr<sys_prx_load_module_option_t> pOpt,
|
||||
vm::ptr<u32> id_list);
|
||||
error_code _sys_prx_load_module_list_on_memcontainer(
|
||||
ppu_thread &ppu, s32 count, vm::cpptr<char, u32, u64> path_list, u32 mem_ct,
|
||||
u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt,
|
||||
vm::ptr<u32> id_list);
|
||||
error_code _sys_prx_load_module_on_memcontainer(
|
||||
ppu_thread &ppu, vm::cptr<char> path, u32 mem_ct, u64 flags,
|
||||
vm::ptr<sys_prx_load_module_option_t> pOpt);
|
||||
error_code _sys_prx_load_module(ppu_thread &ppu, vm::cptr<char> path, u64 flags,
|
||||
vm::ptr<sys_prx_load_module_option_t> pOpt);
|
||||
error_code
|
||||
_sys_prx_start_module(ppu_thread &ppu, u32 id, u64 flags,
|
||||
vm::ptr<sys_prx_start_stop_module_option_t> pOpt);
|
||||
error_code
|
||||
_sys_prx_stop_module(ppu_thread &ppu, u32 id, u64 flags,
|
||||
vm::ptr<sys_prx_start_stop_module_option_t> pOpt);
|
||||
error_code _sys_prx_unload_module(ppu_thread &ppu, u32 id, u64 flags,
|
||||
vm::ptr<sys_prx_unload_module_option_t> pOpt);
|
||||
error_code _sys_prx_register_module(ppu_thread &ppu, vm::cptr<char> name,
|
||||
vm::ptr<void> opt);
|
||||
error_code _sys_prx_query_module(ppu_thread &ppu);
|
||||
error_code _sys_prx_register_library(ppu_thread &ppu, vm::ptr<void> library);
|
||||
error_code _sys_prx_unregister_library(ppu_thread &ppu, vm::ptr<void> library);
|
||||
error_code _sys_prx_link_library(ppu_thread &ppu);
|
||||
error_code _sys_prx_unlink_library(ppu_thread &ppu);
|
||||
error_code _sys_prx_query_library(ppu_thread &ppu);
|
||||
error_code
|
||||
_sys_prx_get_module_list(ppu_thread &ppu, u64 flags,
|
||||
vm::ptr<sys_prx_get_module_list_option_t> pInfo);
|
||||
error_code _sys_prx_get_module_info(ppu_thread &ppu, u32 id, u64 flags,
|
||||
vm::ptr<sys_prx_module_info_option_t> pOpt);
|
||||
error_code _sys_prx_get_module_id_by_name(
|
||||
ppu_thread &ppu, vm::cptr<char> name, u64 flags,
|
||||
vm::ptr<sys_prx_get_module_id_by_name_option_t> pOpt);
|
||||
error_code _sys_prx_get_module_id_by_address(ppu_thread &ppu, u32 addr);
|
||||
error_code _sys_prx_start(ppu_thread &ppu);
|
||||
error_code _sys_prx_stop(ppu_thread &ppu);
|
||||
144
kernel/cellos/include/cellos/sys_rsx.h
Normal file
144
kernel/cellos/include/cellos/sys_rsx.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
class cpu_thread;
|
||||
|
||||
struct RsxDriverInfo {
|
||||
be_t<u32> version_driver; // 0x0
|
||||
be_t<u32> version_gpu; // 0x4
|
||||
be_t<u32> memory_size; // 0x8
|
||||
be_t<u32> hardware_channel; // 0xC
|
||||
be_t<u32> nvcore_frequency; // 0x10
|
||||
be_t<u32> memory_frequency; // 0x14
|
||||
be_t<u32> unk1[4]; // 0x18 - 0x24
|
||||
be_t<u32> unk2; // 0x28 -- pgraph stuff
|
||||
be_t<u32> reportsNotifyOffset; // 0x2C offset to notify memory
|
||||
be_t<u32> reportsOffset; // 0x30 offset to reports memory
|
||||
be_t<u32> reportsReportOffset; // 0x34 offset to reports in reports memory
|
||||
be_t<u32> unk3[6]; // 0x38-0x54
|
||||
be_t<u32> systemModeFlags; // 0x54
|
||||
u8 unk4[0x1064]; // 0x10B8
|
||||
|
||||
struct Head {
|
||||
be_t<u64> lastFlipTime; // 0x0 last flip time
|
||||
atomic_be_t<u32> flipFlags; // 0x8 flags to handle flip/queue
|
||||
be_t<u32> offset; // 0xC
|
||||
be_t<u32> flipBufferId; // 0x10
|
||||
be_t<u32> lastQueuedBufferId; // 0x14 todo: this is definately not this
|
||||
// variable but its 'unused' so im using it
|
||||
// for queueId to pass to flip handler
|
||||
be_t<u32> unk3; // 0x18
|
||||
be_t<u32>
|
||||
lastVTimeLow; // 0x1C last time for first vhandler freq (low 32-bits)
|
||||
atomic_be_t<u64> lastSecondVTime; // 0x20 last time for second vhandler freq
|
||||
be_t<u64> unk4; // 0x28
|
||||
atomic_be_t<u64> vBlankCount; // 0x30
|
||||
be_t<u32> unk; // 0x38 possible u32, 'flip field', top/bottom for interlaced
|
||||
be_t<u32>
|
||||
lastVTimeHigh; // 0x3C last time for first vhandler freq (high 32-bits)
|
||||
} head[8]; // size = 0x40, 0x200
|
||||
|
||||
be_t<u32> unk7; // 0x12B8
|
||||
be_t<u32> unk8; // 0x12BC
|
||||
atomic_be_t<u32> handlers; // 0x12C0 -- flags showing which handlers are set
|
||||
be_t<u32> unk9; // 0x12C4
|
||||
be_t<u32> unk10; // 0x12C8
|
||||
be_t<u32> userCmdParam; // 0x12CC
|
||||
be_t<u32> handler_queue; // 0x12D0
|
||||
be_t<u32> unk11; // 0x12D4
|
||||
be_t<u32> unk12; // 0x12D8
|
||||
be_t<u32> unk13; // 0x12DC
|
||||
be_t<u32> unk14; // 0x12E0
|
||||
be_t<u32> unk15; // 0x12E4
|
||||
be_t<u32> unk16; // 0x12E8
|
||||
be_t<u32> unk17; // 0x12F0
|
||||
be_t<u32> lastError; // 0x12F4 error param for cellGcmSetGraphicsHandler
|
||||
// todo: theres more to this
|
||||
};
|
||||
|
||||
static_assert(sizeof(RsxDriverInfo) == 0x12F8, "rsxSizeTest");
|
||||
static_assert(sizeof(RsxDriverInfo::Head) == 0x40, "rsxHeadSizeTest");
|
||||
|
||||
enum : u64 {
|
||||
// Unused
|
||||
SYS_RSX_IO_MAP_IS_STRICT = 1ull << 60
|
||||
};
|
||||
|
||||
// Unofficial event names
|
||||
enum : u64 {
|
||||
// SYS_RSX_EVENT_GRAPHICS_ERROR = 1 << 0,
|
||||
SYS_RSX_EVENT_VBLANK = 1 << 1,
|
||||
SYS_RSX_EVENT_FLIP_BASE = 1 << 3,
|
||||
SYS_RSX_EVENT_QUEUE_BASE = 1 << 5,
|
||||
SYS_RSX_EVENT_USER_CMD = 1 << 7,
|
||||
SYS_RSX_EVENT_SECOND_VBLANK_BASE = 1 << 10,
|
||||
SYS_RSX_EVENT_UNMAPPED_BASE = 1ull << 32,
|
||||
};
|
||||
|
||||
struct RsxDmaControl {
|
||||
u8 resv[0x40];
|
||||
atomic_be_t<u32> put;
|
||||
atomic_be_t<u32> get;
|
||||
atomic_be_t<u32> ref;
|
||||
be_t<u32> unk[2];
|
||||
be_t<u32> unk1;
|
||||
};
|
||||
|
||||
struct RsxSemaphore {
|
||||
atomic_be_t<u32> val;
|
||||
};
|
||||
|
||||
struct alignas(16) RsxNotify {
|
||||
be_t<u64> timestamp;
|
||||
be_t<u64> zero;
|
||||
};
|
||||
|
||||
struct alignas(16) RsxReport {
|
||||
be_t<u64> timestamp;
|
||||
be_t<u32> val;
|
||||
be_t<u32> pad;
|
||||
};
|
||||
|
||||
struct RsxReports {
|
||||
RsxSemaphore semaphore[1024];
|
||||
RsxNotify notify[64];
|
||||
RsxReport report[2048];
|
||||
};
|
||||
|
||||
struct RsxDisplayInfo {
|
||||
be_t<u32> offset{0};
|
||||
be_t<u32> pitch{0};
|
||||
be_t<u32> width{0};
|
||||
be_t<u32> height{0};
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
bool valid() const { return height != 0u && width != 0u; }
|
||||
};
|
||||
|
||||
// SysCalls
|
||||
error_code sys_rsx_device_open(cpu_thread &cpu);
|
||||
error_code sys_rsx_device_close(cpu_thread &cpu);
|
||||
error_code sys_rsx_memory_allocate(cpu_thread &cpu, vm::ptr<u32> mem_handle,
|
||||
vm::ptr<u64> mem_addr, u32 size, u64 flags,
|
||||
u64 a5, u64 a6, u64 a7);
|
||||
error_code sys_rsx_memory_free(cpu_thread &cpu, u32 mem_handle);
|
||||
error_code sys_rsx_context_allocate(cpu_thread &cpu, vm::ptr<u32> context_id,
|
||||
vm::ptr<u64> lpar_dma_control,
|
||||
vm::ptr<u64> lpar_driver_info,
|
||||
vm::ptr<u64> lpar_reports, u64 mem_ctx,
|
||||
u64 system_mode);
|
||||
error_code sys_rsx_context_free(ppu_thread &ppu, u32 context_id);
|
||||
error_code sys_rsx_context_iomap(cpu_thread &cpu, u32 context_id, u32 io,
|
||||
u32 ea, u32 size, u64 flags);
|
||||
error_code sys_rsx_context_iounmap(cpu_thread &cpu, u32 context_id, u32 io,
|
||||
u32 size);
|
||||
error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3,
|
||||
u64 a4, u64 a5, u64 a6);
|
||||
error_code sys_rsx_device_map(cpu_thread &cpu, vm::ptr<u64> dev_addr,
|
||||
vm::ptr<u64> a2, u32 dev_id);
|
||||
error_code sys_rsx_device_unmap(cpu_thread &cpu, u32 dev_id);
|
||||
error_code sys_rsx_attribute(cpu_thread &cpu, u32 packageId, u32 a2, u32 a3,
|
||||
u32 a4, u32 a5);
|
||||
629
kernel/cellos/include/cellos/sys_rsxaudio.h
Normal file
629
kernel/cellos/include/cellos/sys_rsxaudio.h
Normal file
|
|
@ -0,0 +1,629 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
#include "Emu/Audio/AudioDumper.h"
|
||||
#include "Emu/Audio/audio_resampler.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/system_config_types.h"
|
||||
#include "sys_event.h"
|
||||
#include "sys_sync.h"
|
||||
#include "util/cond.h"
|
||||
#include "util/simple_ringbuf.h"
|
||||
#include "util/transactional_storage.h"
|
||||
|
||||
#if defined(unix) || defined(__unix) || defined(__unix__)
|
||||
// For BSD detection
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#elif defined(BSD) || defined(__APPLE__)
|
||||
#include <sys/event.h>
|
||||
#endif
|
||||
|
||||
enum : u32 {
|
||||
SYS_RSXAUDIO_SERIAL_STREAM_CNT = 4,
|
||||
SYS_RSXAUDIO_STREAM_DATA_BLK_CNT = 4,
|
||||
SYS_RSXAUDIO_DATA_BLK_SIZE = 256,
|
||||
SYS_RSXAUDIO_STREAM_SIZE =
|
||||
SYS_RSXAUDIO_DATA_BLK_SIZE * SYS_RSXAUDIO_STREAM_DATA_BLK_CNT,
|
||||
SYS_RSXAUDIO_CH_PER_STREAM = 2,
|
||||
SYS_RSXAUDIO_SERIAL_MAX_CH = 8,
|
||||
SYS_RSXAUDIO_SPDIF_MAX_CH = 2,
|
||||
SYS_RSXAUDIO_STREAM_SAMPLE_CNT =
|
||||
SYS_RSXAUDIO_STREAM_SIZE / SYS_RSXAUDIO_CH_PER_STREAM / sizeof(f32),
|
||||
|
||||
SYS_RSXAUDIO_RINGBUF_BLK_SZ_SERIAL =
|
||||
SYS_RSXAUDIO_STREAM_SIZE * SYS_RSXAUDIO_SERIAL_STREAM_CNT,
|
||||
SYS_RSXAUDIO_RINGBUF_BLK_SZ_SPDIF = SYS_RSXAUDIO_STREAM_SIZE,
|
||||
|
||||
SYS_RSXAUDIO_RINGBUF_SZ = 16,
|
||||
|
||||
SYS_RSXAUDIO_AVPORT_CNT = 5,
|
||||
|
||||
SYS_RSXAUDIO_FREQ_BASE_384K = 384000,
|
||||
SYS_RSXAUDIO_FREQ_BASE_352K = 352800,
|
||||
|
||||
SYS_RSXAUDIO_PORT_CNT = 3,
|
||||
|
||||
SYS_RSXAUDIO_SPDIF_CNT = 2,
|
||||
};
|
||||
|
||||
enum class RsxaudioAvportIdx : u8 {
|
||||
HDMI_0 = 0,
|
||||
HDMI_1 = 1,
|
||||
AVMULTI = 2,
|
||||
SPDIF_0 = 3,
|
||||
SPDIF_1 = 4,
|
||||
};
|
||||
|
||||
enum class RsxaudioPort : u8 {
|
||||
SERIAL = 0,
|
||||
SPDIF_0 = 1,
|
||||
SPDIF_1 = 2,
|
||||
INVALID = 0xFF,
|
||||
};
|
||||
|
||||
enum class RsxaudioSampleSize : u8 {
|
||||
_16BIT = 2,
|
||||
_32BIT = 4,
|
||||
};
|
||||
|
||||
struct rsxaudio_shmem {
|
||||
struct ringbuf_t {
|
||||
struct entry_t {
|
||||
be_t<u32> valid{};
|
||||
be_t<u32> unk1{};
|
||||
be_t<u64> audio_blk_idx{};
|
||||
be_t<u64> timestamp{};
|
||||
be_t<u32> buf_addr{};
|
||||
be_t<u32> dma_addr{};
|
||||
};
|
||||
|
||||
be_t<u32> active{};
|
||||
be_t<u32> unk2{};
|
||||
be_t<s32> read_idx{};
|
||||
be_t<u32> write_idx{};
|
||||
be_t<s32> rw_max_idx{};
|
||||
be_t<s32> queue_notify_idx{};
|
||||
be_t<s32> queue_notify_step{};
|
||||
be_t<u32> unk6{};
|
||||
be_t<u32> dma_silence_addr{};
|
||||
be_t<u32> unk7{};
|
||||
be_t<u64> next_blk_idx{};
|
||||
|
||||
entry_t entries[16]{};
|
||||
};
|
||||
|
||||
struct uf_event_t {
|
||||
be_t<u64> unk1{};
|
||||
be_t<u32> uf_event_cnt{};
|
||||
u8 unk2[244]{};
|
||||
};
|
||||
|
||||
struct ctrl_t {
|
||||
ringbuf_t ringbuf[SYS_RSXAUDIO_PORT_CNT]{};
|
||||
|
||||
be_t<u32> unk1{};
|
||||
be_t<u32> event_queue_1_id{};
|
||||
u8 unk2[16]{};
|
||||
be_t<u32> event_queue_2_id{};
|
||||
be_t<u32> spdif_ch0_channel_data_lo{};
|
||||
be_t<u32> spdif_ch0_channel_data_hi{};
|
||||
be_t<u32> spdif_ch0_channel_data_tx_cycles{};
|
||||
be_t<u32> unk3{};
|
||||
be_t<u32> event_queue_3_id{};
|
||||
be_t<u32> spdif_ch1_channel_data_lo{};
|
||||
be_t<u32> spdif_ch1_channel_data_hi{};
|
||||
be_t<u32> spdif_ch1_channel_data_tx_cycles{};
|
||||
be_t<u32> unk4{};
|
||||
be_t<u32> intr_thread_prio{};
|
||||
be_t<u32> unk5{};
|
||||
u8 unk6[248]{};
|
||||
uf_event_t channel_uf[SYS_RSXAUDIO_PORT_CNT]{};
|
||||
u8 pad[0x3530]{};
|
||||
};
|
||||
|
||||
u8 dma_serial_region[0x10000]{};
|
||||
u8 dma_spdif_0_region[0x4000]{};
|
||||
u8 dma_spdif_1_region[0x4000]{};
|
||||
u8 dma_silence_region[0x4000]{};
|
||||
ctrl_t ctrl{};
|
||||
};
|
||||
|
||||
static_assert(sizeof(rsxaudio_shmem::ringbuf_t) == 0x230U,
|
||||
"rsxAudioRingBufSizeTest");
|
||||
static_assert(sizeof(rsxaudio_shmem::uf_event_t) == 0x100U,
|
||||
"rsxAudioUfEventTest");
|
||||
static_assert(sizeof(rsxaudio_shmem::ctrl_t) == 0x4000U,
|
||||
"rsxAudioCtrlSizeTest");
|
||||
static_assert(sizeof(rsxaudio_shmem) == 0x20000U, "rsxAudioShmemSizeTest");
|
||||
|
||||
enum rsxaudio_dma_flag : u32 { IO_BASE = 0, IO_ID = 1 };
|
||||
|
||||
struct lv2_rsxaudio final : lv2_obj {
|
||||
static constexpr u32 id_base = 0x60000000;
|
||||
static constexpr u64 dma_io_id = 1;
|
||||
static constexpr u32 dma_io_base = 0x30000000;
|
||||
|
||||
shared_mutex mutex{};
|
||||
bool init = false;
|
||||
|
||||
vm::addr_t shmem{};
|
||||
|
||||
std::array<shared_ptr<lv2_event_queue>, SYS_RSXAUDIO_PORT_CNT> event_queue{};
|
||||
|
||||
// lv2 uses port memory addresses for their names
|
||||
static constexpr std::array<u64, SYS_RSXAUDIO_PORT_CNT> event_port_name{
|
||||
0x8000000000400100, 0x8000000000400200, 0x8000000000400300};
|
||||
|
||||
lv2_rsxaudio() noexcept = default;
|
||||
lv2_rsxaudio(utils::serial &ar) noexcept;
|
||||
void save(utils::serial &ar);
|
||||
|
||||
void page_lock() {
|
||||
ensure(shmem && vm::page_protect(shmem, sizeof(rsxaudio_shmem), 0, 0,
|
||||
vm::page_readable | vm::page_writable |
|
||||
vm::page_executable));
|
||||
}
|
||||
|
||||
void page_unlock() {
|
||||
ensure(shmem && vm::page_protect(shmem, sizeof(rsxaudio_shmem), 0,
|
||||
vm::page_readable | vm::page_writable));
|
||||
}
|
||||
|
||||
rsxaudio_shmem *get_rw_shared_page() const {
|
||||
return reinterpret_cast<rsxaudio_shmem *>(vm::g_sudo_addr + u32{shmem});
|
||||
}
|
||||
};
|
||||
|
||||
class rsxaudio_periodic_tmr {
|
||||
public:
|
||||
enum class wait_result {
|
||||
SUCCESS,
|
||||
INVALID_PARAM,
|
||||
TIMEOUT,
|
||||
TIMER_ERROR,
|
||||
TIMER_CANCELED,
|
||||
};
|
||||
|
||||
rsxaudio_periodic_tmr();
|
||||
~rsxaudio_periodic_tmr();
|
||||
|
||||
rsxaudio_periodic_tmr(const rsxaudio_periodic_tmr &) = delete;
|
||||
rsxaudio_periodic_tmr &operator=(const rsxaudio_periodic_tmr &) = delete;
|
||||
|
||||
// Wait until timer fires and calls callback.
|
||||
wait_result wait(const std::function<void()> &callback);
|
||||
|
||||
// Cancel wait() call
|
||||
void cancel_wait();
|
||||
|
||||
// VTimer funtions
|
||||
|
||||
void vtimer_access_sec(std::invocable<> auto func) {
|
||||
std::lock_guard lock(mutex);
|
||||
std::invoke(func);
|
||||
|
||||
// Adjust timer expiration
|
||||
cancel_timer_unlocked();
|
||||
sched_timer();
|
||||
}
|
||||
|
||||
void enable_vtimer(u32 vtimer_id, u32 rate, u64 crnt_time);
|
||||
|
||||
void disable_vtimer(u32 vtimer_id);
|
||||
|
||||
bool is_vtimer_behind(u32 vtimer_id, u64 crnt_time) const;
|
||||
|
||||
void vtimer_skip_periods(u32 vtimer_id, u64 crnt_time);
|
||||
|
||||
void vtimer_incr(u32 vtimer_id, u64 crnt_time);
|
||||
|
||||
bool is_vtimer_active(u32 vtimer_id) const;
|
||||
|
||||
u64 vtimer_get_sched_time(u32 vtimer_id) const;
|
||||
|
||||
private:
|
||||
static constexpr u64 MAX_BURST_PERIODS = SYS_RSXAUDIO_RINGBUF_SZ;
|
||||
static constexpr u32 VTIMER_MAX = 4;
|
||||
|
||||
struct vtimer {
|
||||
u64 blk_cnt = 0;
|
||||
f64 blk_time = 0.0;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
std::array<vtimer, VTIMER_MAX> vtmr_pool{};
|
||||
|
||||
shared_mutex mutex{};
|
||||
bool in_wait = false;
|
||||
bool zero_period = false;
|
||||
|
||||
#if defined(_WIN32)
|
||||
HANDLE cancel_event{};
|
||||
HANDLE timer_handle{};
|
||||
#elif defined(__linux__)
|
||||
int cancel_event{};
|
||||
int timer_handle{};
|
||||
int epoll_fd{};
|
||||
#elif defined(BSD) || defined(__APPLE__)
|
||||
static constexpr u64 TIMER_ID = 0;
|
||||
static constexpr u64 CANCEL_ID = 1;
|
||||
|
||||
int kq{};
|
||||
struct kevent handle[2]{};
|
||||
#else
|
||||
#error "Implement"
|
||||
#endif
|
||||
|
||||
void sched_timer();
|
||||
void cancel_timer_unlocked();
|
||||
void reset_cancel_flag();
|
||||
|
||||
bool is_vtimer_behind(const vtimer &vtimer, u64 crnt_time) const;
|
||||
|
||||
u64 get_crnt_blk(u64 crnt_time, f64 blk_time) const;
|
||||
f64 get_blk_time(u32 data_rate) const;
|
||||
|
||||
u64 get_rel_next_time();
|
||||
};
|
||||
|
||||
struct rsxaudio_hw_param_t {
|
||||
struct serial_param_t {
|
||||
bool dma_en = false;
|
||||
bool buf_empty_en = false;
|
||||
bool muted = true;
|
||||
bool en = false;
|
||||
u8 freq_div = 8;
|
||||
RsxaudioSampleSize depth = RsxaudioSampleSize::_16BIT;
|
||||
};
|
||||
|
||||
struct spdif_param_t {
|
||||
bool dma_en = false;
|
||||
bool buf_empty_en = false;
|
||||
bool muted = true;
|
||||
bool en = false;
|
||||
bool use_serial_buf = true;
|
||||
u8 freq_div = 8;
|
||||
RsxaudioSampleSize depth = RsxaudioSampleSize::_16BIT;
|
||||
std::array<u8, 6> cs_data = {
|
||||
0x00, 0x90, 0x00,
|
||||
0x40, 0x80, 0x00}; // HW supports only 6 bytes (uart pkt has 8)
|
||||
};
|
||||
|
||||
struct hdmi_param_t {
|
||||
struct hdmi_ch_cfg_t {
|
||||
std::array<u8, SYS_RSXAUDIO_SERIAL_MAX_CH> map{};
|
||||
AudioChannelCnt total_ch_cnt = AudioChannelCnt::STEREO;
|
||||
};
|
||||
|
||||
static constexpr u8 MAP_SILENT_CH = umax;
|
||||
|
||||
bool init = false;
|
||||
hdmi_ch_cfg_t ch_cfg{};
|
||||
std::array<u8, 5> info_frame{}; // TODO: check chstat and info_frame for
|
||||
// info on audio layout, add default values
|
||||
std::array<u8, 5> chstat{};
|
||||
|
||||
bool muted = true;
|
||||
bool force_mute = true;
|
||||
bool use_spdif_1 = false; // TODO: unused for now
|
||||
};
|
||||
|
||||
u32 serial_freq_base = SYS_RSXAUDIO_FREQ_BASE_384K;
|
||||
u32 spdif_freq_base = SYS_RSXAUDIO_FREQ_BASE_352K;
|
||||
|
||||
bool avmulti_av_muted = true;
|
||||
|
||||
serial_param_t serial{};
|
||||
spdif_param_t spdif[2]{};
|
||||
hdmi_param_t hdmi[2]{};
|
||||
|
||||
std::array<RsxaudioPort, SYS_RSXAUDIO_AVPORT_CNT> avport_src = {
|
||||
RsxaudioPort::INVALID, RsxaudioPort::INVALID, RsxaudioPort::INVALID,
|
||||
RsxaudioPort::INVALID, RsxaudioPort::INVALID};
|
||||
};
|
||||
|
||||
// 16-bit PCM converted into float, so buffer must be twice as big
|
||||
using ra_stream_blk_t = std::array<f32, SYS_RSXAUDIO_STREAM_SAMPLE_CNT * 2>;
|
||||
|
||||
class rsxaudio_data_container {
|
||||
public:
|
||||
struct buf_t {
|
||||
std::array<ra_stream_blk_t, SYS_RSXAUDIO_SERIAL_MAX_CH> serial{};
|
||||
std::array<ra_stream_blk_t, SYS_RSXAUDIO_SPDIF_MAX_CH>
|
||||
spdif[SYS_RSXAUDIO_SPDIF_CNT]{};
|
||||
};
|
||||
|
||||
using data_blk_t = std::array<f32, SYS_RSXAUDIO_STREAM_SAMPLE_CNT *
|
||||
SYS_RSXAUDIO_SERIAL_MAX_CH * 2>;
|
||||
|
||||
rsxaudio_data_container(const rsxaudio_hw_param_t &hw_param, const buf_t &buf,
|
||||
bool serial_rdy, bool spdif_0_rdy, bool spdif_1_rdy);
|
||||
u32 get_data_size(RsxaudioAvportIdx avport);
|
||||
void get_data(RsxaudioAvportIdx avport, data_blk_t &data_out);
|
||||
bool data_was_used();
|
||||
|
||||
private:
|
||||
const rsxaudio_hw_param_t &hwp;
|
||||
const buf_t &out_buf;
|
||||
|
||||
std::array<bool, 5> avport_data_avail{};
|
||||
u8 hdmi_stream_cnt[2]{};
|
||||
bool data_was_written = false;
|
||||
|
||||
rsxaudio_data_container(const rsxaudio_data_container &) = delete;
|
||||
rsxaudio_data_container &operator=(const rsxaudio_data_container &) = delete;
|
||||
|
||||
rsxaudio_data_container(rsxaudio_data_container &&) = delete;
|
||||
rsxaudio_data_container &operator=(rsxaudio_data_container &&) = delete;
|
||||
|
||||
// Mix individual channels into final PCM stream. Channels in channel map that
|
||||
// are > input_ch_cnt treated as silent.
|
||||
template <usz output_ch_cnt, usz input_ch_cnt>
|
||||
requires(output_ch_cnt > 0 && output_ch_cnt <= 8 && input_ch_cnt > 0)
|
||||
constexpr void
|
||||
mix(const std::array<u8, 8> &ch_map, RsxaudioSampleSize sample_size,
|
||||
const std::array<ra_stream_blk_t, input_ch_cnt> &input_channels,
|
||||
data_blk_t &data_out) {
|
||||
const ra_stream_blk_t silent_channel{};
|
||||
|
||||
// Build final map
|
||||
std::array<const ra_stream_blk_t *, output_ch_cnt> real_input_ch = {};
|
||||
for (u64 ch_idx = 0; ch_idx < output_ch_cnt; ch_idx++) {
|
||||
if (ch_map[ch_idx] >= input_ch_cnt) {
|
||||
real_input_ch[ch_idx] = &silent_channel;
|
||||
} else {
|
||||
real_input_ch[ch_idx] = &input_channels[ch_map[ch_idx]];
|
||||
}
|
||||
}
|
||||
|
||||
const u32 samples_in_buf = sample_size == RsxaudioSampleSize::_16BIT
|
||||
? SYS_RSXAUDIO_STREAM_SAMPLE_CNT * 2
|
||||
: SYS_RSXAUDIO_STREAM_SAMPLE_CNT;
|
||||
|
||||
for (u32 sample_idx = 0; sample_idx < samples_in_buf * output_ch_cnt;
|
||||
sample_idx += output_ch_cnt) {
|
||||
const u32 src_sample_idx = sample_idx / output_ch_cnt;
|
||||
|
||||
if constexpr (output_ch_cnt >= 1)
|
||||
data_out[sample_idx + 0] = (*real_input_ch[0])[src_sample_idx];
|
||||
if constexpr (output_ch_cnt >= 2)
|
||||
data_out[sample_idx + 1] = (*real_input_ch[1])[src_sample_idx];
|
||||
if constexpr (output_ch_cnt >= 3)
|
||||
data_out[sample_idx + 2] = (*real_input_ch[2])[src_sample_idx];
|
||||
if constexpr (output_ch_cnt >= 4)
|
||||
data_out[sample_idx + 3] = (*real_input_ch[3])[src_sample_idx];
|
||||
if constexpr (output_ch_cnt >= 5)
|
||||
data_out[sample_idx + 4] = (*real_input_ch[4])[src_sample_idx];
|
||||
if constexpr (output_ch_cnt >= 6)
|
||||
data_out[sample_idx + 5] = (*real_input_ch[5])[src_sample_idx];
|
||||
if constexpr (output_ch_cnt >= 7)
|
||||
data_out[sample_idx + 6] = (*real_input_ch[6])[src_sample_idx];
|
||||
if constexpr (output_ch_cnt >= 8)
|
||||
data_out[sample_idx + 7] = (*real_input_ch[7])[src_sample_idx];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace audio {
|
||||
void configure_rsxaudio();
|
||||
}
|
||||
|
||||
class rsxaudio_backend_thread {
|
||||
public:
|
||||
struct port_config {
|
||||
AudioFreq freq = AudioFreq::FREQ_48K;
|
||||
AudioChannelCnt ch_cnt = AudioChannelCnt::STEREO;
|
||||
|
||||
auto operator<=>(const port_config &) const = default;
|
||||
};
|
||||
|
||||
struct avport_bit {
|
||||
bool hdmi_0 : 1;
|
||||
bool hdmi_1 : 1;
|
||||
bool avmulti : 1;
|
||||
bool spdif_0 : 1;
|
||||
bool spdif_1 : 1;
|
||||
};
|
||||
|
||||
rsxaudio_backend_thread();
|
||||
~rsxaudio_backend_thread();
|
||||
|
||||
void operator()();
|
||||
rsxaudio_backend_thread &operator=(thread_state state);
|
||||
|
||||
void set_new_stream_param(
|
||||
const std::array<port_config, SYS_RSXAUDIO_AVPORT_CNT> &cfg,
|
||||
avport_bit muted_avports);
|
||||
void set_mute_state(avport_bit muted_avports);
|
||||
void add_data(rsxaudio_data_container &cont);
|
||||
|
||||
void update_emu_cfg();
|
||||
|
||||
u32 get_sample_rate() const;
|
||||
u8 get_channel_count() const;
|
||||
|
||||
static constexpr auto thread_name = "RsxAudio Backend Thread"sv;
|
||||
|
||||
SAVESTATE_INIT_POS(8.91); // Depends on audio_out_configuration
|
||||
|
||||
private:
|
||||
struct emu_audio_cfg {
|
||||
std::string audio_device{};
|
||||
s64 desired_buffer_duration = 0;
|
||||
f64 time_stretching_threshold = 0;
|
||||
bool buffering_enabled = false;
|
||||
bool convert_to_s16 = false;
|
||||
bool enable_time_stretching = false;
|
||||
bool dump_to_file = false;
|
||||
AudioChannelCnt channels = AudioChannelCnt::STEREO;
|
||||
audio_channel_layout channel_layout = audio_channel_layout::automatic;
|
||||
audio_renderer renderer = audio_renderer::null;
|
||||
audio_provider provider = audio_provider::none;
|
||||
RsxaudioAvportIdx avport = RsxaudioAvportIdx::HDMI_0;
|
||||
|
||||
auto operator<=>(const emu_audio_cfg &) const = default;
|
||||
};
|
||||
|
||||
struct rsxaudio_state {
|
||||
std::array<port_config, SYS_RSXAUDIO_AVPORT_CNT> port{};
|
||||
};
|
||||
|
||||
struct alignas(16) callback_config {
|
||||
static constexpr u16 VOL_NOMINAL = 10000;
|
||||
static constexpr f32 VOL_NOMINAL_INV = 1.0f / VOL_NOMINAL;
|
||||
|
||||
u32 freq : 20 = 48000;
|
||||
|
||||
u16 target_volume = 10000;
|
||||
u16 initial_volume = 10000;
|
||||
u16 current_volume = 10000;
|
||||
|
||||
RsxaudioAvportIdx avport_idx = RsxaudioAvportIdx::HDMI_0;
|
||||
u8 mute_state : SYS_RSXAUDIO_AVPORT_CNT = 0b11111;
|
||||
|
||||
u8 input_ch_cnt : 4 = 2;
|
||||
u8 output_channel_layout : 4 =
|
||||
static_cast<u8>(audio_channel_layout::stereo);
|
||||
|
||||
bool ready : 1 = false;
|
||||
bool convert_to_s16 : 1 = false;
|
||||
bool cfg_changed : 1 = false;
|
||||
bool callback_active : 1 = false;
|
||||
};
|
||||
|
||||
static_assert(sizeof(callback_config) <= 16);
|
||||
|
||||
struct backend_config {
|
||||
port_config cfg{};
|
||||
RsxaudioAvportIdx avport = RsxaudioAvportIdx::HDMI_0;
|
||||
};
|
||||
|
||||
static constexpr u64 ERROR_SERVICE_PERIOD = 500'000;
|
||||
static constexpr u64 SERVICE_PERIOD = 10'000;
|
||||
static constexpr f64 SERVICE_PERIOD_SEC = SERVICE_PERIOD / 1'000'000.0;
|
||||
static constexpr u64 SERVICE_THRESHOLD = 1'500;
|
||||
|
||||
static constexpr f64 TIME_STRETCHING_STEP = 0.1f;
|
||||
|
||||
u64 start_time = get_system_time();
|
||||
u64 time_period_idx = 1;
|
||||
|
||||
emu_audio_cfg new_emu_cfg{};
|
||||
bool emu_cfg_changed = true;
|
||||
|
||||
rsxaudio_state new_ra_state{};
|
||||
bool ra_state_changed = true;
|
||||
|
||||
shared_mutex state_update_m{};
|
||||
cond_variable state_update_c{};
|
||||
|
||||
simple_ringbuf ringbuf{};
|
||||
simple_ringbuf aux_ringbuf{};
|
||||
std::vector<u8> thread_tmp_buf{};
|
||||
std::vector<f32> callback_tmp_buf{};
|
||||
bool use_aux_ringbuf = false;
|
||||
shared_mutex ringbuf_mutex{};
|
||||
|
||||
std::shared_ptr<AudioBackend> backend{};
|
||||
backend_config backend_current_cfg{{}, new_emu_cfg.avport};
|
||||
atomic_t<callback_config> callback_cfg{};
|
||||
bool backend_error_occured = false;
|
||||
bool backend_device_changed = false;
|
||||
|
||||
AudioDumper dumper{};
|
||||
audio_resampler resampler{};
|
||||
|
||||
// Backend
|
||||
void backend_init(const rsxaudio_state &ra_state,
|
||||
const emu_audio_cfg &emu_cfg, bool reset_backend = true);
|
||||
void backend_start();
|
||||
void backend_stop();
|
||||
bool backend_playing();
|
||||
u32 write_data_callback(u32 bytes, void *buf);
|
||||
void state_changed_callback(AudioStateEvent event);
|
||||
|
||||
// Time management
|
||||
u64 get_time_until_service();
|
||||
void update_service_time();
|
||||
void reset_service_time();
|
||||
|
||||
// Helpers
|
||||
static emu_audio_cfg get_emu_cfg();
|
||||
static u8 gen_mute_state(avport_bit avports);
|
||||
static RsxaudioAvportIdx convert_avport(audio_avport avport);
|
||||
};
|
||||
|
||||
class rsxaudio_data_thread {
|
||||
public:
|
||||
// Prevent creation of multiple rsxaudio contexts
|
||||
atomic_t<bool> rsxaudio_ctx_allocated = false;
|
||||
|
||||
shared_mutex rsxaudio_obj_upd_m{};
|
||||
shared_ptr<lv2_rsxaudio> rsxaudio_obj_ptr{};
|
||||
|
||||
void operator()();
|
||||
rsxaudio_data_thread &operator=(thread_state state);
|
||||
|
||||
rsxaudio_data_thread();
|
||||
|
||||
void
|
||||
update_hw_param(std::function<void(rsxaudio_hw_param_t &)> update_callback);
|
||||
void update_mute_state(RsxaudioPort port, bool muted);
|
||||
void update_av_mute_state(RsxaudioAvportIdx avport, bool muted,
|
||||
bool force_mute, bool set = true);
|
||||
void reset_hw();
|
||||
|
||||
static constexpr auto thread_name = "RsxAudioData Thread"sv;
|
||||
|
||||
private:
|
||||
rsxaudio_data_container::buf_t output_buf{};
|
||||
|
||||
transactional_storage<rsxaudio_hw_param_t> hw_param_ts{
|
||||
std::make_shared<universal_pool>(),
|
||||
std::make_shared<rsxaudio_hw_param_t>()};
|
||||
rsxaudio_periodic_tmr timer{};
|
||||
|
||||
void advance_all_timers();
|
||||
void extract_audio_data();
|
||||
static std::pair<bool /*data_present*/, void * /*addr*/>
|
||||
get_ringbuf_addr(RsxaudioPort dst, const lv2_rsxaudio &rsxaudio_obj);
|
||||
|
||||
static f32 pcm_to_float(s32 sample);
|
||||
static f32 pcm_to_float(s16 sample);
|
||||
static void pcm_serial_process_channel(RsxaudioSampleSize word_bits,
|
||||
ra_stream_blk_t &buf_out_l,
|
||||
ra_stream_blk_t &buf_out_r,
|
||||
const void *buf_in, u8 src_stream);
|
||||
static void pcm_spdif_process_channel(RsxaudioSampleSize word_bits,
|
||||
ra_stream_blk_t &buf_out_l,
|
||||
ra_stream_blk_t &buf_out_r,
|
||||
const void *buf_in);
|
||||
bool enqueue_data(RsxaudioPort dst, bool silence, const void *src_addr,
|
||||
const rsxaudio_hw_param_t &hwp);
|
||||
|
||||
static rsxaudio_backend_thread::avport_bit
|
||||
calc_avport_mute_state(const rsxaudio_hw_param_t &hwp);
|
||||
static bool calc_port_active_state(RsxaudioPort port,
|
||||
const rsxaudio_hw_param_t &hwp);
|
||||
};
|
||||
|
||||
using rsx_audio_backend = named_thread<rsxaudio_backend_thread>;
|
||||
using rsx_audio_data = named_thread<rsxaudio_data_thread>;
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_rsxaudio_initialize(vm::ptr<u32> handle);
|
||||
error_code sys_rsxaudio_finalize(u32 handle);
|
||||
error_code sys_rsxaudio_import_shared_memory(u32 handle, vm::ptr<u64> addr);
|
||||
error_code sys_rsxaudio_unimport_shared_memory(u32 handle, vm::ptr<u64> addr);
|
||||
error_code sys_rsxaudio_create_connection(u32 handle);
|
||||
error_code sys_rsxaudio_close_connection(u32 handle);
|
||||
error_code sys_rsxaudio_prepare_process(u32 handle);
|
||||
error_code sys_rsxaudio_start_process(u32 handle);
|
||||
error_code sys_rsxaudio_stop_process(u32 handle);
|
||||
error_code sys_rsxaudio_get_dma_param(u32 handle, u32 flag, vm::ptr<u64> out);
|
||||
55
kernel/cellos/include/cellos/sys_rwlock.h
Normal file
55
kernel/cellos/include/cellos/sys_rwlock.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
struct sys_rwlock_attribute_t {
|
||||
be_t<u32> protocol;
|
||||
be_t<u32> pshared;
|
||||
be_t<u64> ipc_key;
|
||||
be_t<s32> flags;
|
||||
be_t<u32> pad;
|
||||
|
||||
union {
|
||||
nse_t<u64, 1> name_u64;
|
||||
char name[sizeof(u64)];
|
||||
};
|
||||
};
|
||||
|
||||
struct lv2_rwlock final : lv2_obj {
|
||||
static const u32 id_base = 0x88000000;
|
||||
|
||||
const lv2_protocol protocol;
|
||||
const u64 key;
|
||||
const u64 name;
|
||||
|
||||
shared_mutex mutex;
|
||||
atomic_t<s64> owner{0};
|
||||
ppu_thread *rq{};
|
||||
ppu_thread *wq{};
|
||||
|
||||
lv2_rwlock(u32 protocol, u64 key, u64 name) noexcept
|
||||
: protocol{static_cast<u8>(protocol)}, key(key), name(name) {}
|
||||
|
||||
lv2_rwlock(utils::serial &ar);
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
// Aux
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_rwlock_create(ppu_thread &ppu, vm::ptr<u32> rw_lock_id,
|
||||
vm::ptr<sys_rwlock_attribute_t> attr);
|
||||
error_code sys_rwlock_destroy(ppu_thread &ppu, u32 rw_lock_id);
|
||||
error_code sys_rwlock_rlock(ppu_thread &ppu, u32 rw_lock_id, u64 timeout);
|
||||
error_code sys_rwlock_tryrlock(ppu_thread &ppu, u32 rw_lock_id);
|
||||
error_code sys_rwlock_runlock(ppu_thread &ppu, u32 rw_lock_id);
|
||||
error_code sys_rwlock_wlock(ppu_thread &ppu, u32 rw_lock_id, u64 timeout);
|
||||
error_code sys_rwlock_trywlock(ppu_thread &ppu, u32 rw_lock_id);
|
||||
error_code sys_rwlock_wunlock(ppu_thread &ppu, u32 rw_lock_id);
|
||||
|
||||
constexpr auto _sys_rwlock_trywlock = sys_rwlock_trywlock;
|
||||
54
kernel/cellos/include/cellos/sys_semaphore.h
Normal file
54
kernel/cellos/include/cellos/sys_semaphore.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
struct sys_semaphore_attribute_t {
|
||||
be_t<u32> protocol;
|
||||
be_t<u32> pshared;
|
||||
be_t<u64> ipc_key;
|
||||
be_t<s32> flags;
|
||||
be_t<u32> pad;
|
||||
|
||||
union {
|
||||
nse_t<u64, 1> name_u64;
|
||||
char name[sizeof(u64)];
|
||||
};
|
||||
};
|
||||
|
||||
struct lv2_sema final : lv2_obj {
|
||||
static const u32 id_base = 0x96000000;
|
||||
|
||||
const lv2_protocol protocol;
|
||||
const u64 key;
|
||||
const u64 name;
|
||||
const s32 max;
|
||||
|
||||
shared_mutex mutex;
|
||||
atomic_t<s32> val;
|
||||
ppu_thread *sq{};
|
||||
|
||||
lv2_sema(u32 protocol, u64 key, u64 name, s32 max, s32 value) noexcept
|
||||
: protocol{static_cast<u8>(protocol)}, key(key), name(name), max(max),
|
||||
val(value) {}
|
||||
|
||||
lv2_sema(utils::serial &ar);
|
||||
static std::function<void(void *)> load(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
// Aux
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_semaphore_create(ppu_thread &ppu, vm::ptr<u32> sem_id,
|
||||
vm::ptr<sys_semaphore_attribute_t> attr,
|
||||
s32 initial_val, s32 max_val);
|
||||
error_code sys_semaphore_destroy(ppu_thread &ppu, u32 sem_id);
|
||||
error_code sys_semaphore_wait(ppu_thread &ppu, u32 sem_id, u64 timeout);
|
||||
error_code sys_semaphore_trywait(ppu_thread &ppu, u32 sem_id);
|
||||
error_code sys_semaphore_post(ppu_thread &ppu, u32 sem_id, s32 count);
|
||||
error_code sys_semaphore_get_value(ppu_thread &ppu, u32 sem_id,
|
||||
vm::ptr<s32> count);
|
||||
|
|
@ -1,13 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_sm_get_ext_event2(vm::ptr<u64> a1, vm::ptr<u64> a2, vm::ptr<u64> a3, u64 a4);
|
||||
error_code sys_sm_shutdown(ppu_thread& ppu, u16 op, vm::ptr<void> param, u64 size);
|
||||
error_code sys_sm_get_params(vm::ptr<u8> a, vm::ptr<u8> b, vm::ptr<u32> c, vm::ptr<u64> d);
|
||||
error_code sys_sm_get_ext_event2(vm::ptr<u64> a1, vm::ptr<u64> a2,
|
||||
vm::ptr<u64> a3, u64 a4);
|
||||
error_code sys_sm_shutdown(ppu_thread &ppu, u16 op, vm::ptr<void> param,
|
||||
u64 size);
|
||||
error_code sys_sm_get_params(vm::ptr<u8> a, vm::ptr<u8> b, vm::ptr<u32> c,
|
||||
vm::ptr<u64> d);
|
||||
error_code sys_sm_set_shop_mode(s32 mode);
|
||||
error_code sys_sm_control_led(u8 led, u8 action);
|
||||
error_code sys_sm_ring_buzzer(u64 packet, u64 a1, u64 a2);
|
||||
426
kernel/cellos/include/cellos/sys_spu.h
Normal file
426
kernel/cellos/include/cellos/sys_spu.h
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "sys_event.h"
|
||||
#include "sys_sync.h"
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "util/File.h"
|
||||
|
||||
#include <span>
|
||||
|
||||
struct lv2_memory_container;
|
||||
|
||||
enum : s32 {
|
||||
SYS_SPU_THREAD_GROUP_TYPE_NORMAL = 0x00,
|
||||
// SYS_SPU_THREAD_GROUP_TYPE_SEQUENTIAL = 0x01, doesn't exist
|
||||
SYS_SPU_THREAD_GROUP_TYPE_SYSTEM = 0x02,
|
||||
SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER = 0x04,
|
||||
SYS_SPU_THREAD_GROUP_TYPE_NON_CONTEXT = 0x08,
|
||||
SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT = 0x18,
|
||||
SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM = 0x20,
|
||||
};
|
||||
|
||||
enum {
|
||||
SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT = 0x0001,
|
||||
SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT = 0x0002,
|
||||
SYS_SPU_THREAD_GROUP_JOIN_TERMINATED = 0x0004
|
||||
};
|
||||
|
||||
enum {
|
||||
SYS_SPU_THREAD_GROUP_EVENT_RUN = 1,
|
||||
SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION = 2,
|
||||
SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE = 4,
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
SYS_SPU_THREAD_GROUP_EVENT_RUN_KEY = 0xFFFFFFFF53505500ull,
|
||||
SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION_KEY = 0xFFFFFFFF53505503ull,
|
||||
SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE_KEY = 0xFFFFFFFF53505504ull,
|
||||
};
|
||||
|
||||
enum {
|
||||
SYS_SPU_THREAD_GROUP_LOG_ON = 0x0,
|
||||
SYS_SPU_THREAD_GROUP_LOG_OFF = 0x1,
|
||||
SYS_SPU_THREAD_GROUP_LOG_GET_STATUS = 0x2,
|
||||
};
|
||||
|
||||
enum spu_group_status : u32 {
|
||||
SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED,
|
||||
SPU_THREAD_GROUP_STATUS_INITIALIZED,
|
||||
SPU_THREAD_GROUP_STATUS_READY,
|
||||
SPU_THREAD_GROUP_STATUS_WAITING,
|
||||
SPU_THREAD_GROUP_STATUS_SUSPENDED,
|
||||
SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED,
|
||||
SPU_THREAD_GROUP_STATUS_RUNNING,
|
||||
SPU_THREAD_GROUP_STATUS_STOPPED,
|
||||
SPU_THREAD_GROUP_STATUS_DESTROYED, // Internal state
|
||||
SPU_THREAD_GROUP_STATUS_UNKNOWN,
|
||||
};
|
||||
|
||||
enum : s32 {
|
||||
SYS_SPU_SEGMENT_TYPE_COPY = 1,
|
||||
SYS_SPU_SEGMENT_TYPE_FILL = 2,
|
||||
SYS_SPU_SEGMENT_TYPE_INFO = 4,
|
||||
};
|
||||
|
||||
enum spu_stop_syscall : u32 {
|
||||
SYS_SPU_THREAD_STOP_YIELD = 0x0100,
|
||||
SYS_SPU_THREAD_STOP_GROUP_EXIT = 0x0101,
|
||||
SYS_SPU_THREAD_STOP_THREAD_EXIT = 0x0102,
|
||||
SYS_SPU_THREAD_STOP_RECEIVE_EVENT = 0x0110,
|
||||
SYS_SPU_THREAD_STOP_TRY_RECEIVE_EVENT = 0x0111,
|
||||
SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE = 0x0120,
|
||||
};
|
||||
|
||||
struct sys_spu_thread_group_attribute {
|
||||
be_t<u32> nsize; // name length including NULL terminator
|
||||
vm::bcptr<char> name;
|
||||
be_t<s32> type;
|
||||
be_t<u32> ct; // memory container id
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
SYS_SPU_THREAD_OPTION_NONE = 0,
|
||||
SYS_SPU_THREAD_OPTION_ASYNC_INTR_ENABLE = 1,
|
||||
SYS_SPU_THREAD_OPTION_DEC_SYNC_TB_ENABLE = 2,
|
||||
};
|
||||
|
||||
struct sys_spu_thread_attribute {
|
||||
vm::bcptr<char> name;
|
||||
be_t<u32> name_len;
|
||||
be_t<u32> option;
|
||||
};
|
||||
|
||||
struct sys_spu_thread_argument {
|
||||
be_t<u64> arg1;
|
||||
be_t<u64> arg2;
|
||||
be_t<u64> arg3;
|
||||
be_t<u64> arg4;
|
||||
};
|
||||
|
||||
struct sys_spu_segment {
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
be_t<s32> type; // copy, fill, info
|
||||
be_t<u32> ls; // local storage address
|
||||
be_t<u32> size;
|
||||
|
||||
union {
|
||||
be_t<u32> addr; // address or fill value
|
||||
u64 pad;
|
||||
};
|
||||
};
|
||||
|
||||
CHECK_SIZE(sys_spu_segment, 0x18);
|
||||
|
||||
enum : u32 {
|
||||
SYS_SPU_IMAGE_TYPE_USER = 0,
|
||||
SYS_SPU_IMAGE_TYPE_KERNEL = 1,
|
||||
};
|
||||
|
||||
struct sys_spu_image {
|
||||
be_t<u32> type; // user, kernel
|
||||
be_t<u32> entry_point; // Note: in kernel mode it's used to store id
|
||||
vm::bptr<sys_spu_segment> segs;
|
||||
be_t<s32> nsegs;
|
||||
|
||||
template <bool CountInfo = true, typename Phdrs>
|
||||
static s32 get_nsegs(const Phdrs &phdrs) {
|
||||
s32 num_segs = 0;
|
||||
|
||||
for (const auto &phdr : phdrs) {
|
||||
if (phdr.p_type != 1u && phdr.p_type != 4u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (phdr.p_type == 1u && phdr.p_filesz != phdr.p_memsz && phdr.p_filesz) {
|
||||
num_segs += 2;
|
||||
} else if (phdr.p_type == 1u || CountInfo) {
|
||||
num_segs += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return num_segs;
|
||||
}
|
||||
|
||||
template <bool WriteInfo = true, typename Phdrs>
|
||||
static s32 fill(vm::ptr<sys_spu_segment> segs, s32 nsegs, const Phdrs &phdrs,
|
||||
u32 src) {
|
||||
s32 num_segs = 0;
|
||||
|
||||
for (const auto &phdr : phdrs) {
|
||||
if (phdr.p_type == 1u) {
|
||||
if (phdr.p_filesz) {
|
||||
if (num_segs >= nsegs) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
auto *seg = &segs[num_segs++];
|
||||
seg->type = SYS_SPU_SEGMENT_TYPE_COPY;
|
||||
seg->ls = static_cast<u32>(phdr.p_vaddr);
|
||||
seg->size = static_cast<u32>(phdr.p_filesz);
|
||||
seg->addr = static_cast<u32>(phdr.p_offset + src);
|
||||
}
|
||||
|
||||
if (phdr.p_memsz > phdr.p_filesz) {
|
||||
if (num_segs >= nsegs) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
auto *seg = &segs[num_segs++];
|
||||
seg->type = SYS_SPU_SEGMENT_TYPE_FILL;
|
||||
seg->ls = static_cast<u32>(phdr.p_vaddr + phdr.p_filesz);
|
||||
seg->size = static_cast<u32>(phdr.p_memsz - phdr.p_filesz);
|
||||
seg->addr = 0;
|
||||
}
|
||||
} else if (WriteInfo && phdr.p_type == 4u) {
|
||||
if (num_segs >= nsegs) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
auto *seg = &segs[num_segs++];
|
||||
seg->type = SYS_SPU_SEGMENT_TYPE_INFO;
|
||||
seg->size = 0x20;
|
||||
seg->addr = static_cast<u32>(phdr.p_offset + 0x14 + src);
|
||||
} else if (phdr.p_type != 4u) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return num_segs;
|
||||
}
|
||||
|
||||
void load(const fs::file &stream);
|
||||
void free() const;
|
||||
static void deploy(u8 *loc, std::span<const sys_spu_segment> segs,
|
||||
bool is_verbose = true);
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
SYS_SPU_IMAGE_PROTECT = 0,
|
||||
SYS_SPU_IMAGE_DIRECT = 1,
|
||||
};
|
||||
|
||||
struct lv2_spu_image : lv2_obj {
|
||||
static const u32 id_base = 0x22000000;
|
||||
|
||||
const u32 e_entry;
|
||||
const vm::ptr<sys_spu_segment> segs;
|
||||
const s32 nsegs;
|
||||
|
||||
lv2_spu_image(u32 entry, vm::ptr<sys_spu_segment> segs, s32 nsegs)
|
||||
: e_entry(entry), segs(segs), nsegs(nsegs) {}
|
||||
|
||||
lv2_spu_image(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
struct sys_spu_thread_group_syscall_253_info {
|
||||
be_t<u32> deadlineMeetCounter; // From cellSpursGetInfo
|
||||
be_t<u32> deadlineMissCounter; // Same
|
||||
be_t<u64> timestamp;
|
||||
be_t<u64> _x10[6];
|
||||
};
|
||||
|
||||
struct lv2_spu_group {
|
||||
static const u32 id_base = 0x04000100;
|
||||
static const u32 id_step = 0x100;
|
||||
static const u32 id_count = 255;
|
||||
static constexpr std::pair<u32, u32> id_invl_range = {0, 8};
|
||||
|
||||
static_assert(spu_thread::id_count == id_count * 6 + 5);
|
||||
|
||||
const std::string name;
|
||||
const u32 id;
|
||||
const u32 max_num;
|
||||
const u32 mem_size;
|
||||
const s32 type; // SPU Thread Group Type
|
||||
lv2_memory_container *const ct; // Memory Container
|
||||
const bool has_scheduler_context;
|
||||
u32 max_run;
|
||||
|
||||
shared_mutex mutex;
|
||||
|
||||
atomic_t<u32> init; // Initialization Counter
|
||||
atomic_t<typename spu_thread::spu_prio_t> prio{}; // SPU Thread Group Priority
|
||||
atomic_t<spu_group_status> run_state; // SPU Thread Group State
|
||||
atomic_t<s32> exit_status; // SPU Thread Group Exit Status
|
||||
atomic_t<u32> join_state; // flags used to detect exit cause and signal
|
||||
atomic_t<u32> running = 0; // Number of running threads
|
||||
atomic_t<u32> spurs_running = 0;
|
||||
atomic_t<u32> stop_count = 0;
|
||||
atomic_t<u32> wait_term_count = 0;
|
||||
u32 waiter_spu_index = -1; // Index of SPU executing a waiting syscall
|
||||
class ppu_thread *waiter = nullptr;
|
||||
bool set_terminate = false;
|
||||
|
||||
std::array<shared_ptr<named_thread<spu_thread>>, 8> threads; // SPU Threads
|
||||
std::array<s8, 256> threads_map; // SPU Threads map based number
|
||||
std::array<std::pair<u32, std::vector<sys_spu_segment>>, 8>
|
||||
imgs; // Entry points, SPU image segments
|
||||
std::array<std::array<u64, 4>, 8> args; // SPU Thread Arguments
|
||||
|
||||
shared_ptr<lv2_event_queue>
|
||||
ep_run; // port for SYS_SPU_THREAD_GROUP_EVENT_RUN events
|
||||
shared_ptr<lv2_event_queue>
|
||||
ep_exception; // TODO: SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION
|
||||
shared_ptr<lv2_event_queue>
|
||||
ep_sysmodule; // TODO: SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE
|
||||
|
||||
lv2_spu_group(std::string name, u32 num, s32 _prio, s32 type,
|
||||
lv2_memory_container *ct, bool uses_scheduler,
|
||||
u32 mem_size) noexcept
|
||||
: name(std::move(name)), id(idm::last_id()), max_num(num),
|
||||
mem_size(mem_size), type(type), ct(ct),
|
||||
has_scheduler_context(uses_scheduler), max_run(num), init(0),
|
||||
run_state(SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED), exit_status(0),
|
||||
join_state(0), args({}) {
|
||||
threads_map.fill(-1);
|
||||
prio.raw().prio = _prio;
|
||||
}
|
||||
|
||||
SAVESTATE_INIT_POS(8); // Dependency on SPUs
|
||||
|
||||
lv2_spu_group(utils::serial &ar) noexcept;
|
||||
void save(utils::serial &ar);
|
||||
|
||||
CellError send_run_event(u64 data1, u64 data2, u64 data3) const {
|
||||
return ep_run ? ep_run->send(SYS_SPU_THREAD_GROUP_EVENT_RUN_KEY, data1,
|
||||
data2, data3)
|
||||
: CELL_ENOTCONN;
|
||||
}
|
||||
|
||||
CellError send_exception_event(u64 data1, u64 data2, u64 data3) const {
|
||||
return ep_exception
|
||||
? ep_exception->send(SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION_KEY,
|
||||
data1, data2, data3)
|
||||
: CELL_ENOTCONN;
|
||||
}
|
||||
|
||||
CellError send_sysmodule_event(u64 data1, u64 data2, u64 data3) const {
|
||||
return ep_sysmodule ? ep_sysmodule->send(
|
||||
SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE_KEY,
|
||||
data1, data2, data3)
|
||||
: CELL_ENOTCONN;
|
||||
}
|
||||
|
||||
static std::pair<named_thread<spu_thread> *, shared_ptr<lv2_spu_group>>
|
||||
get_thread(u32 id);
|
||||
};
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_spu_initialize(ppu_thread &, u32 max_usable_spu,
|
||||
u32 max_raw_spu);
|
||||
error_code _sys_spu_image_get_information(ppu_thread &,
|
||||
vm::ptr<sys_spu_image> img,
|
||||
vm::ptr<u32> entry_point,
|
||||
vm::ptr<s32> nsegs);
|
||||
error_code sys_spu_image_open(ppu_thread &, vm::ptr<sys_spu_image> img,
|
||||
vm::cptr<char> path);
|
||||
error_code _sys_spu_image_import(ppu_thread &, vm::ptr<sys_spu_image> img,
|
||||
u32 src, u32 size, u32 arg4);
|
||||
error_code _sys_spu_image_close(ppu_thread &, vm::ptr<sys_spu_image> img);
|
||||
error_code _sys_spu_image_get_segments(ppu_thread &, vm::ptr<sys_spu_image> img,
|
||||
vm::ptr<sys_spu_segment> segments,
|
||||
s32 nseg);
|
||||
error_code sys_spu_thread_initialize(ppu_thread &, vm::ptr<u32> thread,
|
||||
u32 group, u32 spu_num,
|
||||
vm::ptr<sys_spu_image>,
|
||||
vm::ptr<sys_spu_thread_attribute>,
|
||||
vm::ptr<sys_spu_thread_argument>);
|
||||
error_code sys_spu_thread_set_argument(ppu_thread &, u32 id,
|
||||
vm::ptr<sys_spu_thread_argument> arg);
|
||||
error_code
|
||||
sys_spu_thread_group_create(ppu_thread &, vm::ptr<u32> id, u32 num, s32 prio,
|
||||
vm::ptr<sys_spu_thread_group_attribute> attr);
|
||||
error_code sys_spu_thread_group_destroy(ppu_thread &, u32 id);
|
||||
error_code sys_spu_thread_group_start(ppu_thread &, u32 id);
|
||||
error_code sys_spu_thread_group_suspend(ppu_thread &, u32 id);
|
||||
error_code sys_spu_thread_group_resume(ppu_thread &, u32 id);
|
||||
error_code sys_spu_thread_group_yield(ppu_thread &, u32 id);
|
||||
error_code sys_spu_thread_group_terminate(ppu_thread &, u32 id, s32 value);
|
||||
error_code sys_spu_thread_group_join(ppu_thread &, u32 id, vm::ptr<u32> cause,
|
||||
vm::ptr<u32> status);
|
||||
error_code sys_spu_thread_group_set_priority(ppu_thread &, u32 id,
|
||||
s32 priority);
|
||||
error_code sys_spu_thread_group_get_priority(ppu_thread &, u32 id,
|
||||
vm::ptr<s32> priority);
|
||||
error_code sys_spu_thread_group_connect_event(ppu_thread &, u32 id, u32 eq,
|
||||
u32 et);
|
||||
error_code sys_spu_thread_group_disconnect_event(ppu_thread &, u32 id, u32 et);
|
||||
error_code sys_spu_thread_group_connect_event_all_threads(ppu_thread &, u32 id,
|
||||
u32 eq_id, u64 req,
|
||||
vm::ptr<u8> spup);
|
||||
error_code sys_spu_thread_group_disconnect_event_all_threads(ppu_thread &,
|
||||
u32 id, u32 spup);
|
||||
error_code sys_spu_thread_group_set_cooperative_victims(ppu_thread &, u32 id,
|
||||
u32 threads_mask);
|
||||
error_code sys_spu_thread_group_syscall_253(
|
||||
ppu_thread &ppu, u32 id,
|
||||
vm::ptr<sys_spu_thread_group_syscall_253_info> info);
|
||||
error_code sys_spu_thread_group_log(ppu_thread &, s32 command,
|
||||
vm::ptr<s32> stat);
|
||||
error_code sys_spu_thread_write_ls(ppu_thread &, u32 id, u32 lsa, u64 value,
|
||||
u32 type);
|
||||
error_code sys_spu_thread_read_ls(ppu_thread &, u32 id, u32 lsa,
|
||||
vm::ptr<u64> value, u32 type);
|
||||
error_code sys_spu_thread_write_spu_mb(ppu_thread &, u32 id, u32 value);
|
||||
error_code sys_spu_thread_set_spu_cfg(ppu_thread &, u32 id, u64 value);
|
||||
error_code sys_spu_thread_get_spu_cfg(ppu_thread &, u32 id, vm::ptr<u64> value);
|
||||
error_code sys_spu_thread_write_snr(ppu_thread &, u32 id, u32 number,
|
||||
u32 value);
|
||||
error_code sys_spu_thread_connect_event(ppu_thread &, u32 id, u32 eq, u32 et,
|
||||
u32 spup);
|
||||
error_code sys_spu_thread_disconnect_event(ppu_thread &, u32 id, u32 et,
|
||||
u32 spup);
|
||||
error_code sys_spu_thread_bind_queue(ppu_thread &, u32 id, u32 spuq,
|
||||
u32 spuq_num);
|
||||
error_code sys_spu_thread_unbind_queue(ppu_thread &, u32 id, u32 spuq_num);
|
||||
error_code sys_spu_thread_get_exit_status(ppu_thread &, u32 id,
|
||||
vm::ptr<s32> status);
|
||||
error_code sys_spu_thread_recover_page_fault(ppu_thread &, u32 id);
|
||||
|
||||
error_code sys_raw_spu_create(ppu_thread &, vm::ptr<u32> id,
|
||||
vm::ptr<void> attr);
|
||||
error_code sys_raw_spu_destroy(ppu_thread &ppu, u32 id);
|
||||
error_code sys_raw_spu_create_interrupt_tag(ppu_thread &, u32 id, u32 class_id,
|
||||
u32 hwthread, vm::ptr<u32> intrtag);
|
||||
error_code sys_raw_spu_set_int_mask(ppu_thread &, u32 id, u32 class_id,
|
||||
u64 mask);
|
||||
error_code sys_raw_spu_get_int_mask(ppu_thread &, u32 id, u32 class_id,
|
||||
vm::ptr<u64> mask);
|
||||
error_code sys_raw_spu_set_int_stat(ppu_thread &, u32 id, u32 class_id,
|
||||
u64 stat);
|
||||
error_code sys_raw_spu_get_int_stat(ppu_thread &, u32 id, u32 class_id,
|
||||
vm::ptr<u64> stat);
|
||||
error_code sys_raw_spu_read_puint_mb(ppu_thread &, u32 id, vm::ptr<u32> value);
|
||||
error_code sys_raw_spu_set_spu_cfg(ppu_thread &, u32 id, u32 value);
|
||||
error_code sys_raw_spu_get_spu_cfg(ppu_thread &, u32 id, vm::ptr<u32> value);
|
||||
error_code sys_raw_spu_recover_page_fault(ppu_thread &, u32 id);
|
||||
|
||||
error_code sys_isolated_spu_create(ppu_thread &, vm::ptr<u32> id,
|
||||
vm::ptr<void> image, u64 arg1, u64 arg2,
|
||||
u64 arg3, u64 arg4);
|
||||
error_code sys_isolated_spu_start(ppu_thread &, u32 id);
|
||||
error_code sys_isolated_spu_destroy(ppu_thread &ppu, u32 id);
|
||||
error_code sys_isolated_spu_create_interrupt_tag(ppu_thread &, u32 id,
|
||||
u32 class_id, u32 hwthread,
|
||||
vm::ptr<u32> intrtag);
|
||||
error_code sys_isolated_spu_set_int_mask(ppu_thread &, u32 id, u32 class_id,
|
||||
u64 mask);
|
||||
error_code sys_isolated_spu_get_int_mask(ppu_thread &, u32 id, u32 class_id,
|
||||
vm::ptr<u64> mask);
|
||||
error_code sys_isolated_spu_set_int_stat(ppu_thread &, u32 id, u32 class_id,
|
||||
u64 stat);
|
||||
error_code sys_isolated_spu_get_int_stat(ppu_thread &, u32 id, u32 class_id,
|
||||
vm::ptr<u64> stat);
|
||||
error_code sys_isolated_spu_read_puint_mb(ppu_thread &, u32 id,
|
||||
vm::ptr<u32> value);
|
||||
error_code sys_isolated_spu_set_spu_cfg(ppu_thread &, u32 id, u32 value);
|
||||
error_code sys_isolated_spu_get_spu_cfg(ppu_thread &, u32 id,
|
||||
vm::ptr<u32> value);
|
||||
|
|
@ -1,25 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// Unofficial error code names
|
||||
enum sys_ss_rng_error : u32
|
||||
{
|
||||
SYS_SS_RNG_ERROR_INVALID_PKG = 0x80010500,
|
||||
SYS_SS_RNG_ERROR_ENOMEM = 0x80010501,
|
||||
SYS_SS_RNG_ERROR_EAGAIN = 0x80010503,
|
||||
SYS_SS_RNG_ERROR_EFAULT = 0x80010509,
|
||||
SYS_SS_RTC_ERROR_UNK = 0x8001050f,
|
||||
enum sys_ss_rng_error : u32 {
|
||||
SYS_SS_RNG_ERROR_INVALID_PKG = 0x80010500,
|
||||
SYS_SS_RNG_ERROR_ENOMEM = 0x80010501,
|
||||
SYS_SS_RNG_ERROR_EAGAIN = 0x80010503,
|
||||
SYS_SS_RNG_ERROR_EFAULT = 0x80010509,
|
||||
SYS_SS_RTC_ERROR_UNK = 0x8001050f,
|
||||
};
|
||||
|
||||
struct CellSsOpenPSID
|
||||
{
|
||||
be_t<u64> high;
|
||||
be_t<u64> low;
|
||||
struct CellSsOpenPSID {
|
||||
be_t<u64> high;
|
||||
be_t<u64> low;
|
||||
};
|
||||
|
||||
error_code sys_ss_random_number_generator(u64 pkg_id, vm::ptr<void> buf, u64 size);
|
||||
error_code sys_ss_random_number_generator(u64 pkg_id, vm::ptr<void> buf,
|
||||
u64 size);
|
||||
error_code sys_ss_access_control_engine(u64 pkg_id, u64 a2, u64 a3);
|
||||
error_code sys_ss_get_console_id(vm::ptr<u8> buf);
|
||||
error_code sys_ss_get_open_psid(vm::ptr<CellSsOpenPSID> psid);
|
||||
|
|
@ -28,6 +27,10 @@ error_code sys_ss_get_cache_of_product_mode(vm::ptr<u8> ptr);
|
|||
error_code sys_ss_secure_rtc(u64 cmd, u64 a2, u64 a3, u64 a4);
|
||||
error_code sys_ss_get_cache_of_flash_ext_flag(vm::ptr<u64> flag);
|
||||
error_code sys_ss_get_boot_device(vm::ptr<u64> dev);
|
||||
error_code sys_ss_update_manager(u64 pkg_id, u64 a1, u64 a2, u64 a3, u64 a4, u64 a5, u64 a6);
|
||||
error_code sys_ss_virtual_trm_manager(u64 pkg_id, u64 a1, u64 a2, u64 a3, u64 a4);
|
||||
error_code sys_ss_individual_info_manager(u64 pkg_id, u64 a2, vm::ptr<u64> out_size, u64 a4, u64 a5, u64 a6);
|
||||
error_code sys_ss_update_manager(u64 pkg_id, u64 a1, u64 a2, u64 a3, u64 a4,
|
||||
u64 a5, u64 a6);
|
||||
error_code sys_ss_virtual_trm_manager(u64 pkg_id, u64 a1, u64 a2, u64 a3,
|
||||
u64 a4);
|
||||
error_code sys_ss_individual_info_manager(u64 pkg_id, u64 a2,
|
||||
vm::ptr<u64> out_size, u64 a4, u64 a5,
|
||||
u64 a6);
|
||||
91
kernel/cellos/include/cellos/sys_storage.h
Normal file
91
kernel/cellos/include/cellos/sys_storage.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "util/File.h"
|
||||
|
||||
enum Devices : u64 {
|
||||
ATA_HDD = 0x101000000000007,
|
||||
BDVD_DRIVE = 0x101000000000006,
|
||||
PATA0_HDD_DRIVE = 0x101000000000008,
|
||||
PATA0_BDVD_DRIVE = BDVD_DRIVE,
|
||||
PATA1_HDD_DRIVE = ATA_HDD,
|
||||
BUILTIN_FLASH = 0x100000000000001,
|
||||
NAND_FLASH = BUILTIN_FLASH,
|
||||
NAND_UNK = 0x100000000000003,
|
||||
NOR_FLASH = 0x100000000000004,
|
||||
MEMORY_STICK = 0x103000000000010,
|
||||
SD_CARD = 0x103000100000010,
|
||||
COMPACT_FLASH = 0x103000200000010,
|
||||
USB_MASS_STORAGE_1_BASE = 0x10300000000000A,
|
||||
USB_MASS_STORAGE_2_BASE = 0x10300000000001F,
|
||||
};
|
||||
|
||||
struct lv2_storage {
|
||||
static const u32 id_base = 0x45000000;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 2048;
|
||||
SAVESTATE_INIT_POS(45);
|
||||
|
||||
const u64 device_id;
|
||||
const fs::file file;
|
||||
const u64 mode;
|
||||
const u64 flags;
|
||||
|
||||
lv2_storage(u64 device_id, fs::file &&file, u64 mode, u64 flags)
|
||||
: device_id(device_id), file(std::move(file)), mode(mode), flags(flags) {}
|
||||
};
|
||||
|
||||
struct StorageDeviceInfo {
|
||||
u8 name[0x20]; // 0x0
|
||||
be_t<u32> zero; // 0x20
|
||||
be_t<u32> zero2; // 0x24
|
||||
be_t<u64> sector_count; // 0x28
|
||||
be_t<u32> sector_size; // 0x30
|
||||
be_t<u32> one; // 0x34
|
||||
u8 flags[8]; // 0x38
|
||||
};
|
||||
|
||||
#define USB_MASS_STORAGE_1(n) (USB_MASS_STORAGE_1_BASE + n) /* For 0-5 */
|
||||
#define USB_MASS_STORAGE_2(n) \
|
||||
(USB_MASS_STORAGE_2_BASE + (n - 6)) /* For 6-127 */
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_storage_open(u64 device, u64 mode, vm::ptr<u32> fd, u64 flags);
|
||||
error_code sys_storage_close(u32 fd);
|
||||
error_code sys_storage_read(u32 fd, u32 mode, u32 start_sector, u32 num_sectors,
|
||||
vm::ptr<void> bounce_buf, vm::ptr<u32> sectors_read,
|
||||
u64 flags);
|
||||
error_code sys_storage_write(u32 fd, u32 mode, u32 start_sector,
|
||||
u32 num_sectors, vm::ptr<void> data,
|
||||
vm::ptr<u32> sectors_wrote, u64 flags);
|
||||
error_code sys_storage_send_device_command(u32 dev_handle, u64 cmd,
|
||||
vm::ptr<void> in, u64 inlen,
|
||||
vm::ptr<void> out, u64 outlen);
|
||||
error_code sys_storage_async_configure(u32 fd, u32 io_buf, u32 equeue_id,
|
||||
u32 unk);
|
||||
error_code sys_storage_async_read();
|
||||
error_code sys_storage_async_write();
|
||||
error_code sys_storage_async_cancel();
|
||||
error_code sys_storage_get_device_info(u64 device,
|
||||
vm::ptr<StorageDeviceInfo> buffer);
|
||||
error_code sys_storage_get_device_config(vm::ptr<u32> storages,
|
||||
vm::ptr<u32> devices);
|
||||
error_code sys_storage_report_devices(u32 storages, u32 start, u32 devices,
|
||||
vm::ptr<u64> device_ids);
|
||||
error_code sys_storage_configure_medium_event(u32 fd, u32 equeue_id, u32 c);
|
||||
error_code sys_storage_set_medium_polling_interval();
|
||||
error_code sys_storage_create_region();
|
||||
error_code sys_storage_delete_region();
|
||||
error_code sys_storage_execute_device_command(
|
||||
u32 fd, u64 cmd, vm::ptr<char> cmdbuf, u64 cmdbuf_size,
|
||||
vm::ptr<char> databuf, u64 databuf_size, vm::ptr<u32> driver_status);
|
||||
error_code sys_storage_check_region_acl();
|
||||
error_code sys_storage_set_region_acl();
|
||||
error_code sys_storage_async_send_device_command(u32 dev_handle, u64 cmd,
|
||||
vm::ptr<void> in, u64 inlen,
|
||||
vm::ptr<void> out, u64 outlen,
|
||||
u64 unk);
|
||||
error_code sys_storage_get_region_offset();
|
||||
error_code sys_storage_set_emulated_speed();
|
||||
469
kernel/cellos/include/cellos/sys_sync.h
Normal file
469
kernel/cellos/include/cellos/sys_sync.h
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/mutex.h"
|
||||
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/IPC.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "util/shared_ptr.hpp"
|
||||
|
||||
// attr_protocol (waiting scheduling policy)
|
||||
enum lv2_protocol : u8 {
|
||||
SYS_SYNC_FIFO = 0x1, // First In, First Out Order
|
||||
SYS_SYNC_PRIORITY = 0x2, // Priority Order
|
||||
SYS_SYNC_PRIORITY_INHERIT = 0x3, // Basic Priority Inheritance Protocol
|
||||
SYS_SYNC_RETRY = 0x4, // Not selected while unlocking
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
SYS_SYNC_ATTR_PROTOCOL_MASK = 0xf,
|
||||
};
|
||||
|
||||
// attr_recursive (recursive locks policy)
|
||||
enum {
|
||||
SYS_SYNC_RECURSIVE = 0x10,
|
||||
SYS_SYNC_NOT_RECURSIVE = 0x20,
|
||||
SYS_SYNC_ATTR_RECURSIVE_MASK = 0xf0,
|
||||
};
|
||||
|
||||
// attr_pshared (sharing among processes policy)
|
||||
enum {
|
||||
SYS_SYNC_PROCESS_SHARED = 0x100,
|
||||
SYS_SYNC_NOT_PROCESS_SHARED = 0x200,
|
||||
SYS_SYNC_ATTR_PSHARED_MASK = 0xf00,
|
||||
};
|
||||
|
||||
// attr_flags (creation policy)
|
||||
enum {
|
||||
SYS_SYNC_NEWLY_CREATED =
|
||||
0x1, // Create new object, fails if specified IPC key exists
|
||||
SYS_SYNC_NOT_CREATE =
|
||||
0x2, // Reference existing object, fails if IPC key not found
|
||||
SYS_SYNC_NOT_CARE =
|
||||
0x3, // Reference existing object, create new one if IPC key not found
|
||||
SYS_SYNC_ATTR_FLAGS_MASK = 0xf,
|
||||
};
|
||||
|
||||
// attr_adaptive
|
||||
enum {
|
||||
SYS_SYNC_ADAPTIVE = 0x1000,
|
||||
SYS_SYNC_NOT_ADAPTIVE = 0x2000,
|
||||
SYS_SYNC_ATTR_ADAPTIVE_MASK = 0xf000,
|
||||
};
|
||||
|
||||
enum ppu_thread_status : u32;
|
||||
|
||||
struct ppu_non_sleeping_count_t {
|
||||
bool has_running; // no actual count for optimization sake
|
||||
u32 onproc_count;
|
||||
};
|
||||
|
||||
// Base class for some kernel objects (shared set of 8192 objects).
|
||||
struct lv2_obj {
|
||||
static const u32 id_step = 0x100;
|
||||
static const u32 id_count = 8192;
|
||||
static constexpr std::pair<u32, u32> id_invl_range = {0, 8};
|
||||
|
||||
private:
|
||||
enum thread_cmd : s32 {
|
||||
yield_cmd = smin,
|
||||
enqueue_cmd,
|
||||
};
|
||||
|
||||
// Function executed under IDM mutex, error will make the object creation fail
|
||||
// and the error will be returned
|
||||
CellError on_id_create() {
|
||||
exists++;
|
||||
return {};
|
||||
}
|
||||
|
||||
public:
|
||||
SAVESTATE_INIT_POS(4); // Dependency on PPUs
|
||||
|
||||
lv2_obj() noexcept = default;
|
||||
lv2_obj(u32 i) noexcept : exists{i} {}
|
||||
lv2_obj(lv2_obj &&rhs) noexcept : exists{+rhs.exists} {}
|
||||
lv2_obj(utils::serial &) noexcept {}
|
||||
lv2_obj &operator=(lv2_obj &&rhs) noexcept {
|
||||
exists = +rhs.exists;
|
||||
return *this;
|
||||
}
|
||||
void save(utils::serial &) {}
|
||||
|
||||
// Existence validation (workaround for shared-ptr ref-counting)
|
||||
atomic_t<u32> exists = 0;
|
||||
|
||||
template <typename Ptr> static bool check(Ptr &&ptr) {
|
||||
return ptr && ptr->exists;
|
||||
}
|
||||
|
||||
// wrapper for name64 string formatting
|
||||
struct name_64 {
|
||||
u64 data;
|
||||
};
|
||||
|
||||
static std::string name64(u64 name_u64);
|
||||
|
||||
// Find and remove the object from the linked list
|
||||
template <bool ModifyNode = true, typename T>
|
||||
static T *unqueue(T *&first, T *object, T *T::*mem_ptr = &T::next_cpu) {
|
||||
auto it = +first;
|
||||
|
||||
if (it == object) {
|
||||
atomic_storage<T *>::release(first, it->*mem_ptr);
|
||||
|
||||
if constexpr (ModifyNode) {
|
||||
atomic_storage<T *>::release(it->*mem_ptr, nullptr);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
for (; it;) {
|
||||
const auto next = it->*mem_ptr + 0;
|
||||
|
||||
if (next == object) {
|
||||
atomic_storage<T *>::release(it->*mem_ptr, next->*mem_ptr);
|
||||
|
||||
if constexpr (ModifyNode) {
|
||||
atomic_storage<T *>::release(next->*mem_ptr, nullptr);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
it = next;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Remove an object from the linked set according to the protocol
|
||||
template <typename E, typename T>
|
||||
static E *schedule(T &first, u32 protocol, bool modify_node = true) {
|
||||
auto it = static_cast<E *>(first);
|
||||
|
||||
if (!it) {
|
||||
return it;
|
||||
}
|
||||
|
||||
auto parent_found = &first;
|
||||
|
||||
if (protocol == SYS_SYNC_FIFO) {
|
||||
while (true) {
|
||||
const auto next = +it->next_cpu;
|
||||
|
||||
if (next) {
|
||||
parent_found = &it->next_cpu;
|
||||
it = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cpu_flag::again - it->state) {
|
||||
atomic_storage<T>::release(*parent_found, nullptr);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
auto prio = it->prio.load();
|
||||
auto found = it;
|
||||
|
||||
while (true) {
|
||||
auto &node = it->next_cpu;
|
||||
const auto next = static_cast<E *>(node);
|
||||
|
||||
if (!next) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto _prio = static_cast<E *>(next)->prio.load();
|
||||
|
||||
// This condition tests for equality as well so the earliest element to be
|
||||
// pushed is popped
|
||||
if (_prio.prio < prio.prio ||
|
||||
(_prio.prio == prio.prio && _prio.order < prio.order)) {
|
||||
found = next;
|
||||
parent_found = &node;
|
||||
prio = _prio;
|
||||
}
|
||||
|
||||
it = next;
|
||||
}
|
||||
|
||||
if (cpu_flag::again - found->state) {
|
||||
atomic_storage<T>::release(*parent_found, found->next_cpu);
|
||||
|
||||
if (modify_node) {
|
||||
atomic_storage<T>::release(found->next_cpu, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
template <typename T> static void emplace(T &first, T object) {
|
||||
atomic_storage<T>::release(object->next_cpu, first);
|
||||
atomic_storage<T>::release(first, object);
|
||||
|
||||
object->prio.atomic_op(
|
||||
[order = ++g_priority_order_tag](
|
||||
std::common_type_t<decltype(std::declval<T>()->prio.load())>
|
||||
&prio) {
|
||||
if constexpr (requires {
|
||||
+std::declval<decltype(prio)>().preserve_bit;
|
||||
}) {
|
||||
if (prio.preserve_bit) {
|
||||
// Restoring state on load
|
||||
prio.preserve_bit = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
prio.order = order;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
// Remove the current thread from the scheduling queue, register timeout
|
||||
static bool sleep_unlocked(cpu_thread &, u64 timeout, u64 current_time);
|
||||
|
||||
// Schedule the thread
|
||||
static bool awake_unlocked(cpu_thread *, s32 prio = enqueue_cmd);
|
||||
|
||||
public:
|
||||
static constexpr u64 max_timeout = u64{umax} / 1000;
|
||||
|
||||
static bool sleep(cpu_thread &cpu, const u64 timeout = 0);
|
||||
|
||||
static bool awake(cpu_thread *thread, s32 prio = enqueue_cmd);
|
||||
|
||||
// Returns true on successful context switch, false otherwise
|
||||
static bool yield(cpu_thread &thread);
|
||||
|
||||
static void set_priority(cpu_thread &thread, s32 prio) {
|
||||
ensure(prio + 512u < 3712);
|
||||
awake(&thread, prio);
|
||||
}
|
||||
|
||||
static inline void awake_all() {
|
||||
awake({});
|
||||
g_to_awake.clear();
|
||||
}
|
||||
|
||||
static void make_scheduler_ready();
|
||||
|
||||
static std::pair<ppu_thread_status, u32>
|
||||
ppu_state(ppu_thread *ppu, bool lock_idm = true, bool lock_lv2 = true);
|
||||
|
||||
static inline void append(cpu_thread *const thread) {
|
||||
g_to_awake.emplace_back(thread);
|
||||
}
|
||||
|
||||
// Serialization related
|
||||
static void set_future_sleep(cpu_thread *cpu);
|
||||
static bool is_scheduler_ready();
|
||||
|
||||
// Must be called under IDM lock
|
||||
static ppu_non_sleeping_count_t count_non_sleeping_threads();
|
||||
|
||||
static inline bool has_ppus_in_running_state() noexcept {
|
||||
return count_non_sleeping_threads().has_running != 0;
|
||||
}
|
||||
|
||||
static void set_yield_frequency(u64 freq, u64 max_allowed_tsx);
|
||||
|
||||
static void cleanup();
|
||||
|
||||
template <typename T> static inline u64 get_key(const T &attr) {
|
||||
return (attr.pshared == SYS_SYNC_PROCESS_SHARED ? +attr.ipc_key : 0);
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
static error_code create(u32 pshared, u64 ipc_key, s32 flags, F &&make,
|
||||
bool key_not_zero = true) {
|
||||
switch (pshared) {
|
||||
case SYS_SYNC_PROCESS_SHARED: {
|
||||
if (key_not_zero && ipc_key == 0) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
switch (flags) {
|
||||
case SYS_SYNC_NEWLY_CREATED:
|
||||
case SYS_SYNC_NOT_CARE:
|
||||
case SYS_SYNC_NOT_CREATE: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SYS_SYNC_NOT_PROCESS_SHARED: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
// EAGAIN for IDM IDs shortage
|
||||
CellError error = CELL_EAGAIN;
|
||||
|
||||
if (!idm::import <lv2_obj, T>([&]() -> shared_ptr<T> {
|
||||
shared_ptr<T> result = make();
|
||||
|
||||
auto finalize_construct = [&]() -> shared_ptr<T> {
|
||||
if ((error = result->on_id_create())) {
|
||||
result.reset();
|
||||
}
|
||||
|
||||
return std::move(result);
|
||||
};
|
||||
|
||||
if (pshared != SYS_SYNC_PROCESS_SHARED) {
|
||||
// Creation of unique (non-shared) object handle
|
||||
return finalize_construct();
|
||||
}
|
||||
|
||||
auto &ipc_container = g_fxo->get<ipc_manager<T, u64>>();
|
||||
|
||||
if (flags == SYS_SYNC_NOT_CREATE) {
|
||||
result = ipc_container.get(ipc_key);
|
||||
|
||||
if (!result) {
|
||||
error = CELL_ESRCH;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Run on_id_create() on existing object
|
||||
return finalize_construct();
|
||||
}
|
||||
|
||||
bool added = false;
|
||||
std::tie(added, result) = ipc_container.add(
|
||||
ipc_key, finalize_construct, flags != SYS_SYNC_NEWLY_CREATED);
|
||||
|
||||
if (!added) {
|
||||
if (flags == SYS_SYNC_NEWLY_CREATED) {
|
||||
// Object already exists but flags does not allow it
|
||||
error = CELL_EEXIST;
|
||||
|
||||
// We specified we do not want to peek pointer's value, result
|
||||
// must be empty
|
||||
AUDIT(!result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Run on_id_create() on existing object
|
||||
return finalize_construct();
|
||||
}
|
||||
|
||||
return result;
|
||||
})) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void on_id_destroy(T &obj, u64 ipc_key, u64 pshared = umax) {
|
||||
if (pshared == umax) {
|
||||
// Default is to check key
|
||||
pshared = ipc_key != 0;
|
||||
}
|
||||
|
||||
if (obj.exists-- == 1u && pshared) {
|
||||
g_fxo->get<ipc_manager<T, u64>>().remove(ipc_key);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static shared_ptr<T> load(u64 ipc_key, shared_ptr<T> make,
|
||||
u64 pshared = umax) {
|
||||
if (pshared == umax ? ipc_key != 0 : pshared != 0) {
|
||||
g_fxo->need<ipc_manager<T, u64>>();
|
||||
|
||||
g_fxo->get<ipc_manager<T, u64>>().add(ipc_key, [&]() { return make; });
|
||||
}
|
||||
|
||||
// Ensure no error
|
||||
ensure(!make->on_id_create());
|
||||
return make;
|
||||
}
|
||||
|
||||
template <typename T, typename Storage = lv2_obj>
|
||||
static std::function<void(void *)> load_func(shared_ptr<T> make,
|
||||
u64 pshared = umax) {
|
||||
const u64 key = make->key;
|
||||
return [ptr = load<T>(key, make, pshared)](void *storage) {
|
||||
*static_cast<atomic_ptr<Storage> *>(storage) = ptr;
|
||||
};
|
||||
}
|
||||
|
||||
static bool wait_timeout(u64 usec, ppu_thread *cpu = {}, bool scale = true,
|
||||
bool is_usleep = false);
|
||||
|
||||
static void notify_all() noexcept;
|
||||
|
||||
// Can be called before the actual sleep call in order to move it out of mutex
|
||||
// scope
|
||||
static void prepare_for_sleep(cpu_thread &cpu);
|
||||
|
||||
struct notify_all_t {
|
||||
notify_all_t() noexcept { g_postpone_notify_barrier = true; }
|
||||
|
||||
notify_all_t(const notify_all_t &) = delete;
|
||||
|
||||
static void cleanup() {
|
||||
for (auto &cpu : g_to_notify) {
|
||||
if (!cpu) {
|
||||
return;
|
||||
}
|
||||
|
||||
// While IDM mutex is still locked (this function assumes so) check if
|
||||
// the notification is still needed Pending flag is meant for forced
|
||||
// notification (if the CPU really has pending work it can restore the
|
||||
// flag in theory) Disabled to allow reservation notifications from here
|
||||
if (false && cpu != &g_to_notify &&
|
||||
static_cast<const decltype(cpu_thread::state) *>(cpu)->none_of(
|
||||
cpu_flag::signal + cpu_flag::pending)) {
|
||||
// Omit it (this is a void pointer, it can hold anything)
|
||||
cpu = &g_to_notify;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~notify_all_t() noexcept { lv2_obj::notify_all(); }
|
||||
};
|
||||
|
||||
// Scheduler mutex
|
||||
static shared_mutex g_mutex;
|
||||
|
||||
// Proirity tags
|
||||
static atomic_t<u64> g_priority_order_tag;
|
||||
|
||||
private:
|
||||
// Pending list of threads to run
|
||||
static thread_local std::vector<class cpu_thread *> g_to_awake;
|
||||
|
||||
// Scheduler queue for active PPU threads
|
||||
static class ppu_thread *g_ppu;
|
||||
|
||||
// Waiting for the response from
|
||||
static u32 g_pending;
|
||||
|
||||
// Pending list of threads to notify (cpu_thread::state ptr)
|
||||
static thread_local std::add_pointer_t<const void> g_to_notify[4];
|
||||
|
||||
// If a notify_all_t object exists locally, postpone notifications to the
|
||||
// destructor of it (not recursive, notifies on the first destructor for
|
||||
// safety)
|
||||
static thread_local bool g_postpone_notify_barrier;
|
||||
|
||||
static void schedule_all(u64 current_time = 0);
|
||||
};
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_time_set_timezone(s32 timezone, s32 summertime);
|
||||
error_code sys_time_get_timezone(vm::ptr<s32> timezone, vm::ptr<s32> summertime);
|
||||
error_code sys_time_get_timezone(vm::ptr<s32> timezone,
|
||||
vm::ptr<s32> summertime);
|
||||
error_code sys_time_get_current_time(vm::ptr<s64> sec, vm::ptr<s64> nsec);
|
||||
error_code sys_time_set_current_time(s64 sec, s64 nsec);
|
||||
u64 sys_time_get_timebase_frequency();
|
||||
70
kernel/cellos/include/cellos/sys_timer.h
Normal file
70
kernel/cellos/include/cellos/sys_timer.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "sys_event.h"
|
||||
|
||||
// Timer State
|
||||
enum : u32 {
|
||||
SYS_TIMER_STATE_STOP = 0,
|
||||
SYS_TIMER_STATE_RUN = 1,
|
||||
};
|
||||
|
||||
struct sys_timer_information_t {
|
||||
be_t<u64> next_expire; // system_time_t
|
||||
be_t<u64> period;
|
||||
be_t<u32> timer_state;
|
||||
be_t<u32> pad;
|
||||
};
|
||||
|
||||
struct lv2_timer : lv2_obj {
|
||||
static const u32 id_base = 0x11000000;
|
||||
|
||||
shared_mutex mutex;
|
||||
atomic_t<u32> state{SYS_TIMER_STATE_STOP};
|
||||
|
||||
shared_ptr<lv2_event_queue> port;
|
||||
u64 source;
|
||||
u64 data1;
|
||||
u64 data2;
|
||||
|
||||
atomic_t<u64> expire{0}; // Next expiration time
|
||||
atomic_t<u64> period{0}; // Period (oneshot if 0)
|
||||
|
||||
u64 check(u64 _now) noexcept;
|
||||
u64 check_unlocked(u64 _now) noexcept;
|
||||
|
||||
lv2_timer() noexcept : lv2_obj(1) {}
|
||||
|
||||
void get_information(sys_timer_information_t &info) const {
|
||||
if (state == SYS_TIMER_STATE_RUN) {
|
||||
info.timer_state = state;
|
||||
info.next_expire = expire;
|
||||
info.period = period;
|
||||
} else {
|
||||
info.timer_state = SYS_TIMER_STATE_STOP;
|
||||
info.next_expire = 0;
|
||||
info.period = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lv2_timer(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
};
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
// Syscalls
|
||||
|
||||
error_code sys_timer_create(ppu_thread &, vm::ptr<u32> timer_id);
|
||||
error_code sys_timer_destroy(ppu_thread &, u32 timer_id);
|
||||
error_code sys_timer_get_information(ppu_thread &, u32 timer_id,
|
||||
vm::ptr<sys_timer_information_t> info);
|
||||
error_code _sys_timer_start(ppu_thread &, u32 timer_id, u64 basetime,
|
||||
u64 period); // basetime type changed from s64
|
||||
error_code sys_timer_stop(ppu_thread &, u32 timer_id);
|
||||
error_code sys_timer_connect_event_queue(ppu_thread &, u32 timer_id,
|
||||
u32 queue_id, u64 name, u64 data1,
|
||||
u64 data2);
|
||||
error_code sys_timer_disconnect_event_queue(ppu_thread &, u32 timer_id);
|
||||
error_code sys_timer_sleep(ppu_thread &, u32 sleep_time);
|
||||
error_code sys_timer_usleep(ppu_thread &, u64 sleep_time);
|
||||
33
kernel/cellos/include/cellos/sys_tty.h
Normal file
33
kernel/cellos/include/cellos/sys_tty.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
|
||||
// TTY channels
|
||||
enum {
|
||||
SYS_TTYP_PPU_STDIN = 0,
|
||||
SYS_TTYP_PPU_STDOUT = 0,
|
||||
SYS_TTYP_PPU_STDERR = 1,
|
||||
SYS_TTYP_SPU_STDOUT = 2,
|
||||
SYS_TTYP_USER1 = 3,
|
||||
SYS_TTYP_USER2 = 4,
|
||||
SYS_TTYP_USER3 = 5,
|
||||
SYS_TTYP_USER4 = 6,
|
||||
SYS_TTYP_USER5 = 7,
|
||||
SYS_TTYP_USER6 = 8,
|
||||
SYS_TTYP_USER7 = 9,
|
||||
SYS_TTYP_USER8 = 10,
|
||||
SYS_TTYP_USER9 = 11,
|
||||
SYS_TTYP_USER10 = 12,
|
||||
SYS_TTYP_USER11 = 13,
|
||||
SYS_TTYP_USER12 = 14,
|
||||
SYS_TTYP_USER13 = 15,
|
||||
};
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
// SysCalls
|
||||
error_code sys_tty_read(s32 ch, vm::ptr<char> buf, u32 len,
|
||||
vm::ptr<u32> preadlen);
|
||||
error_code sys_tty_write(ppu_thread &ppu, s32 ch, vm::cptr<char> buf, u32 len,
|
||||
vm::ptr<u32> pwritelen);
|
||||
713
kernel/cellos/include/cellos/sys_uart.h
Normal file
713
kernel/cellos/include/cellos/sys_uart.h
Normal file
|
|
@ -0,0 +1,713 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "sys_rsxaudio.h"
|
||||
#include "util/Thread.h"
|
||||
#include "util/cond.h"
|
||||
#include "util/mutex.h"
|
||||
#include "util/simple_ringbuf.h"
|
||||
|
||||
enum : u32 {
|
||||
PS3AV_RX_BUF_SIZE = 0x800,
|
||||
PS3AV_TX_BUF_SIZE = 0x800,
|
||||
|
||||
PS3AV_VERSION = 0x205,
|
||||
|
||||
PS3AV_CID_AV_INIT = 0x00000001,
|
||||
PS3AV_CID_AV_FIN = 0x00000002,
|
||||
PS3AV_CID_AV_GET_HW_CONF = 0x00000003,
|
||||
PS3AV_CID_AV_GET_MONITOR_INFO = 0x00000004,
|
||||
PS3AV_CID_AV_GET_BKSV_LIST = 0x00000005,
|
||||
PS3AV_CID_AV_ENABLE_EVENT = 0x00000006,
|
||||
PS3AV_CID_AV_DISABLE_EVENT = 0x00000007,
|
||||
PS3AV_CID_AV_GET_PORT_STATE = 0x00000009,
|
||||
PS3AV_CID_AV_TV_MUTE = 0x0000000A,
|
||||
PS3AV_CID_AV_NULL_CMD = 0x0000000B,
|
||||
PS3AV_CID_AV_GET_AKSV = 0x0000000C,
|
||||
PS3AV_CID_AV_UNK4 = 0x0000000D,
|
||||
PS3AV_CID_AV_UNK5 = 0x0000000E,
|
||||
|
||||
PS3AV_CID_AV_VIDEO_MUTE = 0x00010002,
|
||||
PS3AV_CID_AV_VIDEO_DISABLE_SIG = 0x00010003,
|
||||
PS3AV_CID_AV_VIDEO_YTRAPCONTROL = 0x00010004,
|
||||
PS3AV_CID_AV_VIDEO_UNK5 = 0x00010005,
|
||||
PS3AV_CID_AV_VIDEO_UNK6 = 0x00010006,
|
||||
PS3AV_CID_AV_AUDIO_MUTE = 0x00020002,
|
||||
PS3AV_CID_AV_ACP_CTRL = 0x00020003,
|
||||
PS3AV_CID_AV_SET_ACP_PACKET = 0x00020004,
|
||||
PS3AV_CID_AV_ADD_SIGNAL_CTL = 0x00030001,
|
||||
PS3AV_CID_AV_SET_CC_CODE = 0x00030002,
|
||||
PS3AV_CID_AV_SET_CGMS_WSS = 0x00030003,
|
||||
PS3AV_CID_AV_SET_MACROVISION = 0x00030004,
|
||||
PS3AV_CID_AV_UNK7 = 0x00030005,
|
||||
PS3AV_CID_AV_UNK8 = 0x00030006,
|
||||
PS3AV_CID_AV_UNK9 = 0x00030007,
|
||||
PS3AV_CID_AV_HDMI_MODE = 0x00040001,
|
||||
PS3AV_CID_AV_UNK15 = 0x00050001,
|
||||
|
||||
PS3AV_CID_AV_CEC_MESSAGE = 0x000A0001,
|
||||
PS3AV_CID_AV_GET_CEC_CONFIG = 0x000A0002,
|
||||
PS3AV_CID_AV_UNK11 = 0x000A0003,
|
||||
PS3AV_CID_AV_UNK12 = 0x000A0004,
|
||||
PS3AV_CID_AV_UNK13 = 0x000A0005,
|
||||
PS3AV_CID_AV_UNK14 = 0x000A0006,
|
||||
|
||||
PS3AV_CID_VIDEO_INIT = 0x01000001,
|
||||
PS3AV_CID_VIDEO_MODE = 0x01000002,
|
||||
PS3AV_CID_VIDEO_ROUTE = 0x01000003,
|
||||
PS3AV_CID_VIDEO_FORMAT = 0x01000004,
|
||||
PS3AV_CID_VIDEO_PITCH = 0x01000005,
|
||||
PS3AV_CID_VIDEO_GET_HW_CONF = 0x01000006,
|
||||
PS3AV_CID_VIDEO_GET_REG = 0x01000008,
|
||||
PS3AV_CID_VIDEO_UNK = 0x01000009,
|
||||
PS3AV_CID_VIDEO_UNK1 = 0x0100000A,
|
||||
PS3AV_CID_VIDEO_UNK2 = 0x0100000B,
|
||||
PS3AV_CID_VIDEO_UNK3 = 0x0100000C,
|
||||
|
||||
PS3AV_CID_AUDIO_INIT = 0x02000001,
|
||||
PS3AV_CID_AUDIO_MODE = 0x02000002,
|
||||
PS3AV_CID_AUDIO_MUTE = 0x02000003,
|
||||
PS3AV_CID_AUDIO_ACTIVE = 0x02000004,
|
||||
PS3AV_CID_AUDIO_INACTIVE = 0x02000005,
|
||||
PS3AV_CID_AUDIO_SPDIF_BIT = 0x02000006,
|
||||
PS3AV_CID_AUDIO_CTRL = 0x02000007,
|
||||
|
||||
PS3AV_CID_AVB_PARAM = 0x04000001,
|
||||
|
||||
PS3AV_CID_EVENT_UNPLUGGED = 0x10000001,
|
||||
PS3AV_CID_EVENT_PLUGGED = 0x10000002,
|
||||
PS3AV_CID_EVENT_HDCP_DONE = 0x10000003,
|
||||
PS3AV_CID_EVENT_HDCP_FAIL = 0x10000004,
|
||||
PS3AV_CID_EVENT_HDCP_REAUTH = 0x10000005,
|
||||
PS3AV_CID_EVENT_HDCP_ERROR = 0x10000006,
|
||||
|
||||
PS3AV_REPLY_BIT = 0x80000000,
|
||||
|
||||
PS3AV_RESBIT_720x480P = 0x0003, /* 0x0001 | 0x0002 */
|
||||
PS3AV_RESBIT_720x576P = 0x0003, /* 0x0001 | 0x0002 */
|
||||
PS3AV_RESBIT_1280x720P = 0x0004,
|
||||
PS3AV_RESBIT_1920x1080I = 0x0008,
|
||||
PS3AV_RESBIT_1920x1080P = 0x4000,
|
||||
|
||||
PS3AV_MONITOR_TYPE_NONE = 0,
|
||||
PS3AV_MONITOR_TYPE_HDMI = 1,
|
||||
PS3AV_MONITOR_TYPE_DVI = 2,
|
||||
PS3AV_MONITOR_TYPE_AVMULTI = 3,
|
||||
|
||||
PS3AV_COLORIMETRY_xvYCC_601 = 1,
|
||||
PS3AV_COLORIMETRY_xvYCC_709 = 2,
|
||||
PS3AV_COLORIMETRY_MD0 = 1 << 4,
|
||||
PS3AV_COLORIMETRY_MD1 = 1 << 5,
|
||||
PS3AV_COLORIMETRY_MD2 = 1 << 6,
|
||||
|
||||
PS3AV_CS_SUPPORTED = 1,
|
||||
PS3AV_RGB_SELECTABLE_QAUNTIZATION_RANGE = 8,
|
||||
PS3AV_12BIT_COLOR = 16,
|
||||
|
||||
PS3AV_MON_INFO_AUDIO_BLK_MAX = 16,
|
||||
|
||||
PS3AV_MON_INFO_AUDIO_TYPE_LPCM = 1,
|
||||
PS3AV_MON_INFO_AUDIO_TYPE_AC3 = 2,
|
||||
PS3AV_MON_INFO_AUDIO_TYPE_AAC = 6,
|
||||
PS3AV_MON_INFO_AUDIO_TYPE_DTS = 7,
|
||||
PS3AV_MON_INFO_AUDIO_TYPE_DDP = 10,
|
||||
PS3AV_MON_INFO_AUDIO_TYPE_DTS_HD = 11,
|
||||
PS3AV_MON_INFO_AUDIO_TYPE_DOLBY_THD = 12,
|
||||
|
||||
PS3AV_HDMI_BEHAVIOR_HDCP_OFF = 0x01,
|
||||
PS3AV_HDMI_BEHAVIOR_DVI = 0x40,
|
||||
PS3AV_HDMI_BEHAVIOR_EDID_PASS = 0x80,
|
||||
PS3AV_HDMI_BEHAVIOR_NORMAL = 0xFF,
|
||||
|
||||
PS3AV_EVENT_BIT_UNPLUGGED = 0x01,
|
||||
PS3AV_EVENT_BIT_PLUGGED = 0x02,
|
||||
PS3AV_EVENT_BIT_HDCP_DONE = 0x04,
|
||||
PS3AV_EVENT_BIT_HDCP_FAIL = 0x08,
|
||||
PS3AV_EVENT_BIT_HDCP_REAUTH = 0x10,
|
||||
PS3AV_EVENT_BIT_HDCP_TOPOLOGY = 0x20,
|
||||
PS3AV_EVENT_BIT_UNK = 0x80000000,
|
||||
|
||||
PS3AV_HEAD_A_HDMI = 0,
|
||||
PS3AV_HEAD_B_ANALOG = 1,
|
||||
|
||||
PS3AV_AUDIO_PORT_HDMI_0 = 1 << 0,
|
||||
PS3AV_AUDIO_PORT_HDMI_1 = 1 << 1,
|
||||
PS3AV_AUDIO_PORT_AVMULTI = 1 << 10,
|
||||
PS3AV_AUDIO_PORT_SPDIF_0 = 1 << 20,
|
||||
PS3AV_AUDIO_PORT_SPDIF_1 = 1 << 21,
|
||||
|
||||
PS3AV_STATUS_SUCCESS = 0x00,
|
||||
PS3AV_STATUS_RECEIVE_VUART_ERROR = 0x01,
|
||||
PS3AV_STATUS_SYSCON_COMMUNICATE_FAIL = 0x02,
|
||||
PS3AV_STATUS_INVALID_COMMAND = 0x03,
|
||||
PS3AV_STATUS_INVALID_PORT = 0x04,
|
||||
PS3AV_STATUS_INVALID_VID = 0x05,
|
||||
PS3AV_STATUS_INVALID_COLOR_SPACE = 0x06,
|
||||
PS3AV_STATUS_INVALID_FS = 0x07,
|
||||
PS3AV_STATUS_INVALID_AUDIO_CH = 0x08,
|
||||
PS3AV_STATUS_UNSUPPORTED_VERSION = 0x09,
|
||||
PS3AV_STATUS_INVALID_SAMPLE_SIZE = 0x0A,
|
||||
PS3AV_STATUS_FAILURE = 0x0B,
|
||||
PS3AV_STATUS_UNSUPPORTED_COMMAND = 0x0C,
|
||||
PS3AV_STATUS_BUFFER_OVERFLOW = 0x0D,
|
||||
PS3AV_STATUS_INVALID_VIDEO_PARAM = 0x0E,
|
||||
PS3AV_STATUS_NO_SEL = 0x0F,
|
||||
PS3AV_STATUS_INVALID_AV_PARAM = 0x10,
|
||||
PS3AV_STATUS_INVALID_AUDIO_PARAM = 0x11,
|
||||
PS3AV_STATUS_UNSUPPORTED_HDMI_MODE = 0x12,
|
||||
PS3AV_STATUS_NO_SYNC_HEAD = 0x13,
|
||||
PS3AV_STATUS_UNK_0x14 = 0x14,
|
||||
};
|
||||
|
||||
const u8 PS3AV_AKSV_VALUE[5] = {0x00, 0x00, 0x0F, 0xFF, 0xFF};
|
||||
const u8 PS3AV_BKSV_VALUE[5] = {0xFF, 0xFF, 0xF0, 0x00, 0x00};
|
||||
|
||||
enum PS3_AV_OP_MODE : u32 {
|
||||
// BIG operation modes could send more then 4096 bytes
|
||||
|
||||
NOT_BLOCKING_BIG_OP = 0,
|
||||
BLOCKING_BIG_OP = 1,
|
||||
NOT_BLOCKING_OP = 2,
|
||||
};
|
||||
|
||||
enum class UartHdmiEvent : u8 {
|
||||
NONE = 0,
|
||||
UNPLUGGED = 1,
|
||||
PLUGGED = 2,
|
||||
HDCP_DONE = 3,
|
||||
};
|
||||
|
||||
enum class UartAudioCtrlID : u32 {
|
||||
DAC_RESET = 0,
|
||||
DAC_DE_EMPHASIS = 1,
|
||||
AVCLK = 2,
|
||||
};
|
||||
|
||||
enum class UartAudioAvport : u8 {
|
||||
HDMI_0 = 0x0,
|
||||
HDMI_1 = 0x1,
|
||||
AVMULTI_0 = 0x10,
|
||||
AVMULTI_1 = 0x11,
|
||||
SPDIF_0 = 0x20,
|
||||
SPDIF_1 = 0x21,
|
||||
};
|
||||
|
||||
enum class UartAudioSource : u32 {
|
||||
SERIAL = 0,
|
||||
SPDIF = 1,
|
||||
};
|
||||
|
||||
enum class UartAudioFreq : u32 {
|
||||
_32K = 1,
|
||||
_44K = 2,
|
||||
_48K = 3,
|
||||
_88K = 4,
|
||||
_96K = 5,
|
||||
_176K = 6,
|
||||
_192K = 7,
|
||||
};
|
||||
|
||||
enum class UartAudioFormat : u32 {
|
||||
PCM = 1,
|
||||
BITSTREAM = 0xFF,
|
||||
};
|
||||
|
||||
enum class UartAudioSampleSize : u32 {
|
||||
_16BIT = 1,
|
||||
_20BIT = 2,
|
||||
_24BIT = 3,
|
||||
};
|
||||
|
||||
class vuart_hdmi_event_handler {
|
||||
public:
|
||||
vuart_hdmi_event_handler(u64 time_offset = 0);
|
||||
|
||||
void set_target_state(UartHdmiEvent start_state, UartHdmiEvent end_state);
|
||||
bool events_available();
|
||||
|
||||
u64 time_until_next();
|
||||
UartHdmiEvent get_occured_event();
|
||||
|
||||
private:
|
||||
static constexpr u64 EVENT_TIME_DURATION = 20000;
|
||||
static constexpr u64 EVENT_TIME_THRESHOLD = 1000;
|
||||
|
||||
u64 time_of_next_event = 0;
|
||||
const u64 time_offset = 0;
|
||||
|
||||
// Assume that syscon initialized hdmi to plugged state
|
||||
UartHdmiEvent current_state = UartHdmiEvent::PLUGGED;
|
||||
UartHdmiEvent current_to_state = UartHdmiEvent::PLUGGED;
|
||||
|
||||
UartHdmiEvent base_state = UartHdmiEvent::NONE;
|
||||
UartHdmiEvent target_state = UartHdmiEvent::NONE;
|
||||
|
||||
void schedule_next();
|
||||
void advance_state();
|
||||
};
|
||||
|
||||
class vuart_av_thread;
|
||||
|
||||
struct ps3av_cmd {
|
||||
virtual u16 get_size(vuart_av_thread &vuart, const void *pkt_buf) = 0;
|
||||
virtual void execute(vuart_av_thread &vuart, const void *pkt_buf) = 0;
|
||||
virtual ~ps3av_cmd() {};
|
||||
};
|
||||
|
||||
class vuart_av_thread {
|
||||
public:
|
||||
atomic_t<bool> initialized{};
|
||||
|
||||
shared_mutex rx_mutex{};
|
||||
shared_mutex tx_mutex{};
|
||||
|
||||
shared_mutex tx_wake_m{};
|
||||
cond_variable tx_wake_c{};
|
||||
shared_mutex tx_rdy_m{};
|
||||
cond_variable tx_rdy_c{};
|
||||
shared_mutex rx_wake_m{};
|
||||
cond_variable rx_wake_c{};
|
||||
|
||||
bool head_b_initialized = false;
|
||||
u8 hdmi_behavior_mode = PS3AV_HDMI_BEHAVIOR_NORMAL;
|
||||
u16 av_cmd_ver = 0;
|
||||
u32 hdmi_events_bitmask = 0;
|
||||
bool hdmi_res_set[2]{false, false};
|
||||
|
||||
void operator()();
|
||||
void parse_tx_buffer(u32 buf_size);
|
||||
vuart_av_thread &operator=(thread_state);
|
||||
|
||||
u32 enque_tx_data(const void *data, u32 data_sz);
|
||||
u32 get_tx_bytes();
|
||||
u32 read_rx_data(void *data, u32 data_sz);
|
||||
|
||||
u32 get_reply_buf_free_size();
|
||||
|
||||
template <bool UseScBuffer = false>
|
||||
void write_resp(u32 cid, u32 status, const void *data = nullptr,
|
||||
u16 data_size = 0);
|
||||
|
||||
void add_hdmi_events(UartHdmiEvent first_event, UartHdmiEvent last_event,
|
||||
bool hdmi_0, bool hdmi_1);
|
||||
void add_hdmi_events(UartHdmiEvent last_event, bool hdmi_0, bool hdmi_1);
|
||||
|
||||
static RsxaudioAvportIdx avport_to_idx(UartAudioAvport avport);
|
||||
|
||||
static constexpr auto thread_name = "VUART AV Thread"sv;
|
||||
|
||||
private:
|
||||
struct temp_buf {
|
||||
u32 crnt_size = 0;
|
||||
u8 buf[PS3AV_RX_BUF_SIZE]{};
|
||||
};
|
||||
|
||||
simple_ringbuf tx_buf{PS3AV_TX_BUF_SIZE};
|
||||
simple_ringbuf rx_buf{PS3AV_RX_BUF_SIZE};
|
||||
|
||||
// uart_mngr could sometimes read past the tx_buffer due to weird size checks
|
||||
// in FW, but no further than size of largest packet
|
||||
u8 temp_tx_buf[PS3AV_TX_BUF_SIZE * 2]{};
|
||||
temp_buf temp_rx_buf{};
|
||||
temp_buf temp_rx_sc_buf{};
|
||||
|
||||
vuart_hdmi_event_handler hdmi_event_handler[2]{0, 5000};
|
||||
bool hdcp_first_auth[2]{true, true};
|
||||
|
||||
u32 read_tx_data(void *data, u32 data_sz);
|
||||
std::shared_ptr<ps3av_cmd> get_cmd(u32 cid);
|
||||
void commit_rx_buf(bool syscon_buf);
|
||||
|
||||
void add_unplug_event(bool hdmi_0, bool hdmi_1);
|
||||
void add_plug_event(bool hdmi_0, bool hdmi_1);
|
||||
void add_hdcp_done_event(bool hdmi_0, bool hdmi_1);
|
||||
void commit_event_data(const void *data, u16 data_size);
|
||||
void dispatch_hdmi_event(UartHdmiEvent event, UartAudioAvport hdmi);
|
||||
};
|
||||
|
||||
using vuart_av = named_thread<vuart_av_thread>;
|
||||
|
||||
struct vuart_params {
|
||||
be_t<u64, 1> rx_buf_size;
|
||||
be_t<u64, 1> tx_buf_size;
|
||||
};
|
||||
|
||||
static_assert(sizeof(vuart_params) == 16);
|
||||
|
||||
struct ps3av_pkt_reply_hdr {
|
||||
be_t<u16, 1> version;
|
||||
be_t<u16, 1> length;
|
||||
be_t<u32, 1> cid;
|
||||
be_t<u32, 1> status;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_reply_hdr) == 12);
|
||||
|
||||
struct ps3av_header {
|
||||
be_t<u16, 1> version;
|
||||
be_t<u16, 1> length;
|
||||
be_t<u32, 1> cid;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_header) == 8);
|
||||
|
||||
struct ps3av_info_resolution {
|
||||
be_t<u32, 1> res_bits;
|
||||
be_t<u32, 1> native;
|
||||
};
|
||||
|
||||
struct ps3av_info_cs {
|
||||
u8 rgb;
|
||||
u8 yuv444;
|
||||
u8 yuv422;
|
||||
u8 colorimetry_data;
|
||||
};
|
||||
|
||||
struct ps3av_info_color {
|
||||
be_t<u16, 1> red_x;
|
||||
be_t<u16, 1> red_y;
|
||||
be_t<u16, 1> green_x;
|
||||
be_t<u16, 1> green_y;
|
||||
be_t<u16, 1> blue_x;
|
||||
be_t<u16, 1> blue_y;
|
||||
be_t<u16, 1> white_x;
|
||||
be_t<u16, 1> white_y;
|
||||
be_t<u32, 1> gamma;
|
||||
};
|
||||
|
||||
struct ps3av_info_audio {
|
||||
u8 type;
|
||||
u8 max_num_of_ch;
|
||||
u8 fs;
|
||||
u8 sbit;
|
||||
};
|
||||
|
||||
struct ps3av_get_monitor_info_reply {
|
||||
u8 avport;
|
||||
u8 monitor_id[10];
|
||||
u8 monitor_type;
|
||||
u8 monitor_name[16];
|
||||
ps3av_info_resolution res_60;
|
||||
ps3av_info_resolution res_50;
|
||||
ps3av_info_resolution res_other;
|
||||
ps3av_info_resolution res_vesa;
|
||||
ps3av_info_cs cs;
|
||||
ps3av_info_color color;
|
||||
u8 supported_ai;
|
||||
u8 speaker_info;
|
||||
be_t<u16, 1> num_of_audio_block;
|
||||
ps3av_info_audio audio_info[PS3AV_MON_INFO_AUDIO_BLK_MAX];
|
||||
be_t<u16, 1> hor_screen_size;
|
||||
be_t<u16, 1> ver_screen_size;
|
||||
u8 supported_content_types;
|
||||
u8 reserved_1[3];
|
||||
ps3av_info_resolution res_60_packed_3D;
|
||||
ps3av_info_resolution res_50_packed_3D;
|
||||
ps3av_info_resolution res_other_3D;
|
||||
ps3av_info_resolution res_60_sbs_3D;
|
||||
ps3av_info_resolution res_50_sbs_3D;
|
||||
u8 vendor_specific_flags;
|
||||
u8 reserved_2[7];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_get_monitor_info_reply) == 208);
|
||||
|
||||
struct ps3av_get_monitor_info {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
be_t<u16, 1> reserved;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_get_monitor_info) == 12);
|
||||
|
||||
struct ps3av_get_hw_info_reply {
|
||||
be_t<u16, 1> num_of_hdmi;
|
||||
be_t<u16, 1> num_of_avmulti;
|
||||
be_t<u16, 1> num_of_spdif;
|
||||
be_t<u16, 1> extra_bistream_support;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_get_hw_info_reply) == 8);
|
||||
|
||||
struct ps3av_pkt_set_hdmi_mode {
|
||||
ps3av_header hdr;
|
||||
u8 mode;
|
||||
u8 resv[3];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_set_hdmi_mode) == 12);
|
||||
|
||||
struct ps3av_pkt_audio_mode {
|
||||
ps3av_header hdr;
|
||||
UartAudioAvport avport;
|
||||
u8 reserved0[3];
|
||||
be_t<u32, 1> mask;
|
||||
be_t<u32, 1> audio_num_of_ch;
|
||||
be_t<UartAudioFreq, 1> audio_fs;
|
||||
be_t<UartAudioSampleSize, 1> audio_word_bits;
|
||||
be_t<UartAudioFormat, 1> audio_format;
|
||||
be_t<UartAudioSource, 1> audio_source;
|
||||
u8 audio_enable[4];
|
||||
u8 audio_swap[4];
|
||||
u8 audio_map[4];
|
||||
be_t<u32, 1> audio_layout;
|
||||
be_t<u32, 1> audio_downmix;
|
||||
be_t<u32, 1> audio_downmix_level;
|
||||
u8 audio_cs_info[8];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_audio_mode) == 68);
|
||||
|
||||
struct ps3av_pkt_audio_mute {
|
||||
ps3av_header hdr;
|
||||
UartAudioAvport avport;
|
||||
u8 reserved0[3];
|
||||
u8 mute;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_audio_mute) == 13);
|
||||
|
||||
struct ps3av_pkt_audio_set_active {
|
||||
ps3av_header hdr;
|
||||
be_t<u32, 1> audio_port;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_audio_set_active) == 12);
|
||||
|
||||
struct ps3av_pkt_audio_spdif_bit {
|
||||
ps3av_header hdr;
|
||||
UartAudioAvport avport;
|
||||
u8 reserved0[3];
|
||||
be_t<u32, 1> audio_port;
|
||||
be_t<u32, 1> spdif_bit_data[12];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_audio_spdif_bit) == 64);
|
||||
|
||||
struct ps3av_pkt_audio_ctrl {
|
||||
ps3av_header hdr;
|
||||
be_t<UartAudioCtrlID, 1> audio_ctrl_id;
|
||||
be_t<u32, 1> audio_ctrl_data[4];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_audio_ctrl) == 28);
|
||||
|
||||
struct ps3av_pkt_hdmi_plugged_event {
|
||||
ps3av_header hdr;
|
||||
ps3av_get_monitor_info_reply minfo;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_hdmi_plugged_event) == 216);
|
||||
|
||||
struct ps3av_pkt_hdmi_hdcp_done_event {
|
||||
ps3av_header hdr;
|
||||
be_t<u32, 1> ksv_cnt;
|
||||
u8 ksv_arr[20][5];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_hdmi_hdcp_done_event) == 112);
|
||||
|
||||
struct ps3av_pkt_av_init {
|
||||
ps3av_header hdr;
|
||||
be_t<u32, 1> event_bit;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_av_init) == 12);
|
||||
|
||||
struct ps3av_pkt_av_init_reply {
|
||||
be_t<u32, 1> unk;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_av_init_reply) == 4);
|
||||
|
||||
struct ps3av_pkt_enable_event {
|
||||
ps3av_header hdr;
|
||||
be_t<u32, 1> event_bit;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_enable_event) == 12);
|
||||
|
||||
struct ps3av_pkt_get_bksv {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
u8 resv[2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_get_bksv) == 12);
|
||||
|
||||
struct ps3av_pkt_get_bksv_reply {
|
||||
be_t<u16, 1> avport;
|
||||
u8 resv[2];
|
||||
be_t<u32, 1> ksv_cnt;
|
||||
u8 ksv_arr[20][5];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_get_bksv_reply) == 108);
|
||||
|
||||
struct ps3av_pkt_video_get_hw_cfg_reply {
|
||||
be_t<u32, 1> gx_available;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_video_get_hw_cfg_reply) == 4);
|
||||
|
||||
struct ps3av_pkt_video_set_pitch {
|
||||
ps3av_header hdr;
|
||||
be_t<u32, 1> video_head;
|
||||
be_t<u32, 1> pitch;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_video_set_pitch) == 16);
|
||||
|
||||
struct ps3av_pkt_get_aksv_reply {
|
||||
be_t<u32, 1> ksv_size;
|
||||
u8 ksv_arr[2][5];
|
||||
u8 resv[2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_get_aksv_reply) == 16);
|
||||
|
||||
struct ps3av_pkt_inc_avset {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> num_of_video_pkt;
|
||||
be_t<u16, 1> num_of_audio_pkt;
|
||||
be_t<u16, 1> num_of_av_video_pkt;
|
||||
be_t<u16, 1> num_of_av_audio_pkt;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_inc_avset) == 16);
|
||||
|
||||
struct ps3av_pkt_av_audio_param {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
be_t<u16, 1> resv;
|
||||
u8 mclk;
|
||||
u8 ns[3];
|
||||
u8 enable;
|
||||
u8 swaplr;
|
||||
u8 fifomap;
|
||||
u8 inputctrl;
|
||||
u8 inputlen;
|
||||
u8 layout;
|
||||
u8 info[5];
|
||||
u8 chstat[5];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_av_audio_param) == 32);
|
||||
|
||||
struct ps3av_pkt_av_video_cs {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
be_t<u16, 1> av_vid;
|
||||
be_t<u16, 1> av_cs_out;
|
||||
be_t<u16, 1> av_cs_in;
|
||||
u8 dither;
|
||||
u8 bitlen_out;
|
||||
u8 super_white;
|
||||
u8 aspect;
|
||||
u8 unk1;
|
||||
u8 unk2;
|
||||
u8 resv[2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_av_video_cs) == 24);
|
||||
|
||||
struct ps3av_pkt_video_mode {
|
||||
ps3av_header hdr;
|
||||
be_t<u32, 1> video_head;
|
||||
be_t<u16, 1> unk1;
|
||||
be_t<u16, 1> unk2;
|
||||
be_t<u32, 1> video_vid;
|
||||
be_t<u32, 1> width;
|
||||
be_t<u32, 1> height;
|
||||
be_t<u32, 1> pitch;
|
||||
be_t<u32, 1> video_out_format;
|
||||
be_t<u32, 1> video_format;
|
||||
be_t<u16, 1> unk3;
|
||||
be_t<u16, 1> video_order;
|
||||
be_t<u32, 1> unk4;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_video_mode) == 48);
|
||||
|
||||
struct ps3av_pkt_av_video_ytrapcontrol {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> unk1;
|
||||
be_t<u16, 1> unk2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_av_video_ytrapcontrol) == 12);
|
||||
|
||||
struct ps3av_pkt_av_get_cec_config_reply {
|
||||
be_t<u32, 1> cec_present;
|
||||
};
|
||||
|
||||
struct ps3av_pkt_video_format {
|
||||
ps3av_header hdr;
|
||||
be_t<u32, 1> video_head;
|
||||
be_t<u32, 1> video_format;
|
||||
be_t<u16, 1> unk;
|
||||
be_t<u16, 1> video_order;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_video_format) == 20);
|
||||
|
||||
struct ps3av_pkt_av_set_cgms_wss {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
u8 resv[2];
|
||||
be_t<u32, 1> cgms_wss;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_av_set_cgms_wss) == 16);
|
||||
|
||||
struct ps3av_pkt_set_acp_packet {
|
||||
ps3av_header hdr;
|
||||
u8 avport;
|
||||
u8 pkt_type;
|
||||
u8 resv[2];
|
||||
u8 pkt_data[32];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_set_acp_packet) == 44);
|
||||
|
||||
struct ps3av_pkt_acp_ctrl {
|
||||
ps3av_header hdr;
|
||||
u8 avport;
|
||||
u8 packetctl;
|
||||
u8 resv[2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_acp_ctrl) == 12);
|
||||
|
||||
struct ps3av_pkt_add_signal_ctl {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
be_t<u16, 1> signal_ctl;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_add_signal_ctl) == 12);
|
||||
|
||||
struct ps3av_pkt_av_audio_mute {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
be_t<u16, 1> mute;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_av_audio_mute) == 12);
|
||||
|
||||
struct ps3av_pkt_video_disable_sig {
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
be_t<u16, 1> resv;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ps3av_pkt_video_disable_sig) == 12);
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_uart_initialize(ppu_thread &ppu);
|
||||
error_code sys_uart_receive(ppu_thread &ppu, vm::ptr<void> buffer, u64 size,
|
||||
u32 mode);
|
||||
error_code sys_uart_send(ppu_thread &ppu, vm::cptr<void> buffer, u64 size,
|
||||
u32 mode);
|
||||
error_code sys_uart_get_params(vm::ptr<vuart_params> buffer);
|
||||
118
kernel/cellos/include/cellos/sys_usbd.h
Normal file
118
kernel/cellos/include/cellos/sys_usbd.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Input/product_info.h"
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
#define MAX_SYS_USBD_TRANSFERS 0x44
|
||||
|
||||
// PS3 internal codes
|
||||
enum PS3StandardUsbErrors : u32 {
|
||||
HC_CC_NOERR = 0x00,
|
||||
EHCI_CC_MISSMF = 0x10,
|
||||
EHCI_CC_XACT = 0x20,
|
||||
EHCI_CC_BABBLE = 0x30,
|
||||
EHCI_CC_DATABUF = 0x40,
|
||||
EHCI_CC_HALTED = 0x50,
|
||||
};
|
||||
|
||||
enum PS3IsochronousUsbErrors : u8 {
|
||||
USBD_HC_CC_NOERR = 0x00,
|
||||
USBD_HC_CC_MISSMF = 0x01,
|
||||
USBD_HC_CC_XACT = 0x02,
|
||||
USBD_HC_CC_BABBLE = 0x04,
|
||||
USBD_HC_CC_DATABUF = 0x08,
|
||||
};
|
||||
|
||||
enum SysUsbdEvents : u32 {
|
||||
SYS_USBD_ATTACH = 0x01,
|
||||
SYS_USBD_DETACH = 0x02,
|
||||
SYS_USBD_TRANSFER_COMPLETE = 0x03,
|
||||
SYS_USBD_TERMINATE = 0x04,
|
||||
};
|
||||
|
||||
// PS3 internal structures
|
||||
struct UsbInternalDevice {
|
||||
u8 device_high; // System flag maybe (used in generating actual device number)
|
||||
u8 device_low; // Just a number identifying the device (used in generating
|
||||
// actual device number)
|
||||
u8 unk3; // ? Seems to always be 2?
|
||||
u8 unk4; // ?
|
||||
};
|
||||
|
||||
struct UsbDeviceRequest {
|
||||
u8 bmRequestType;
|
||||
u8 bRequest;
|
||||
be_t<u16> wValue;
|
||||
be_t<u16> wIndex;
|
||||
be_t<u16> wLength;
|
||||
};
|
||||
|
||||
struct UsbDeviceIsoRequest {
|
||||
vm::ptr<void> buf;
|
||||
be_t<u32> start_frame;
|
||||
be_t<u32> num_packets;
|
||||
be_t<u16> packets[8];
|
||||
};
|
||||
|
||||
error_code sys_usbd_initialize(ppu_thread &ppu, vm::ptr<u32> handle);
|
||||
error_code sys_usbd_finalize(ppu_thread &ppu, u32 handle);
|
||||
error_code sys_usbd_get_device_list(ppu_thread &ppu, u32 handle,
|
||||
vm::ptr<UsbInternalDevice> device_list,
|
||||
u32 max_devices);
|
||||
error_code sys_usbd_get_descriptor_size(ppu_thread &ppu, u32 handle,
|
||||
u32 device_handle);
|
||||
error_code sys_usbd_get_descriptor(ppu_thread &ppu, u32 handle,
|
||||
u32 device_handle, vm::ptr<void> descriptor,
|
||||
u32 desc_size);
|
||||
error_code sys_usbd_register_ldd(ppu_thread &ppu, u32 handle,
|
||||
vm::cptr<char> s_product, u16 slen_product);
|
||||
error_code sys_usbd_unregister_ldd(ppu_thread &ppu, u32 handle,
|
||||
vm::cptr<char> s_product, u16 slen_product);
|
||||
error_code sys_usbd_open_pipe(ppu_thread &ppu, u32 handle, u32 device_handle,
|
||||
u32 unk1, u64 unk2, u64 unk3, u32 endpoint,
|
||||
u64 unk4);
|
||||
error_code sys_usbd_open_default_pipe(ppu_thread &ppu, u32 handle,
|
||||
u32 device_handle);
|
||||
error_code sys_usbd_close_pipe(ppu_thread &ppu, u32 handle, u32 pipe_handle);
|
||||
error_code sys_usbd_receive_event(ppu_thread &ppu, u32 handle,
|
||||
vm::ptr<u64> arg1, vm::ptr<u64> arg2,
|
||||
vm::ptr<u64> arg3);
|
||||
error_code sys_usbd_detect_event(ppu_thread &ppu);
|
||||
error_code sys_usbd_attach(ppu_thread &ppu, u32 handle, u32 unk1, u32 unk2,
|
||||
u32 device_handle);
|
||||
error_code sys_usbd_transfer_data(ppu_thread &ppu, u32 handle, u32 id_pipe,
|
||||
vm::ptr<u8> buf, u32 buf_size,
|
||||
vm::ptr<UsbDeviceRequest> request,
|
||||
u32 type_transfer);
|
||||
error_code
|
||||
sys_usbd_isochronous_transfer_data(ppu_thread &ppu, u32 handle, u32 id_pipe,
|
||||
vm::ptr<UsbDeviceIsoRequest> iso_request);
|
||||
error_code sys_usbd_get_transfer_status(ppu_thread &ppu, u32 handle,
|
||||
u32 id_transfer, u32 unk1,
|
||||
vm::ptr<u32> result,
|
||||
vm::ptr<u32> count);
|
||||
error_code sys_usbd_get_isochronous_transfer_status(
|
||||
ppu_thread &ppu, u32 handle, u32 id_transfer, u32 unk1,
|
||||
vm::ptr<UsbDeviceIsoRequest> request, vm::ptr<u32> result);
|
||||
error_code sys_usbd_get_device_location(ppu_thread &ppu, u32 handle,
|
||||
u32 device_handle,
|
||||
vm::ptr<u8> location);
|
||||
error_code sys_usbd_send_event(ppu_thread &ppu);
|
||||
error_code sys_usbd_event_port_send(ppu_thread &ppu, u32 handle, u64 arg1,
|
||||
u64 arg2, u64 arg3);
|
||||
error_code sys_usbd_allocate_memory(ppu_thread &ppu);
|
||||
error_code sys_usbd_free_memory(ppu_thread &ppu);
|
||||
error_code sys_usbd_get_device_speed(ppu_thread &ppu);
|
||||
error_code sys_usbd_register_extra_ldd(ppu_thread &ppu, u32 handle,
|
||||
vm::cptr<char> s_product,
|
||||
u16 slen_product, u16 id_vendor,
|
||||
u16 id_product_min, u16 id_product_max);
|
||||
error_code sys_usbd_unregister_extra_ldd(ppu_thread &ppu, u32 handle,
|
||||
vm::cptr<char> s_product,
|
||||
u16 slen_product);
|
||||
|
||||
void connect_usb_controller(u8 index, input::product_type);
|
||||
void handle_hotplug_event(bool connected);
|
||||
76
kernel/cellos/include/cellos/sys_vm.h
Normal file
76
kernel/cellos/include/cellos/sys_vm.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "sys_memory.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
enum : u64 {
|
||||
SYS_VM_STATE_INVALID = 0ull,
|
||||
SYS_VM_STATE_UNUSED = 1ull,
|
||||
SYS_VM_STATE_ON_MEMORY = 2ull,
|
||||
SYS_VM_STATE_STORED = 4ull,
|
||||
|
||||
SYS_VM_POLICY_AUTO_RECOMMENDED = 1ull,
|
||||
};
|
||||
|
||||
struct sys_vm_statistics_t {
|
||||
be_t<u64> page_fault_ppu; // Number of bad virtual memory accesses from a PPU
|
||||
// thread.
|
||||
be_t<u64> page_fault_spu; // Number of bad virtual memory accesses from a SPU
|
||||
// thread.
|
||||
be_t<u64> page_in; // Number of virtual memory backup reading operations.
|
||||
be_t<u64> page_out; // Number of virtual memory backup writing operations.
|
||||
be_t<u32> pmem_total; // Total physical memory allocated for the virtual
|
||||
// memory area.
|
||||
be_t<u32> pmem_used; // Physical memory in use by the virtual memory area.
|
||||
be_t<u64> timestamp;
|
||||
};
|
||||
|
||||
// Block info
|
||||
struct sys_vm_t {
|
||||
static const u32 id_base = 0x1;
|
||||
static const u32 id_step = 0x1;
|
||||
static const u32 id_count = 16;
|
||||
|
||||
lv2_memory_container *const ct;
|
||||
const u32 addr;
|
||||
const u32 size;
|
||||
atomic_t<u32> psize;
|
||||
|
||||
sys_vm_t(u32 addr, u32 vsize, lv2_memory_container *ct, u32 psize);
|
||||
~sys_vm_t();
|
||||
|
||||
SAVESTATE_INIT_POS(10);
|
||||
|
||||
sys_vm_t(utils::serial &ar);
|
||||
void save(utils::serial &ar);
|
||||
|
||||
static std::array<atomic_t<u32>, id_count> g_ids;
|
||||
|
||||
static u32 find_id(u32 addr) { return g_ids[addr >> 28].load(); }
|
||||
};
|
||||
|
||||
// Aux
|
||||
class ppu_thread;
|
||||
|
||||
// SysCalls
|
||||
error_code sys_vm_memory_map(ppu_thread &ppu, u64 vsize, u64 psize, u32 cid,
|
||||
u64 flag, u64 policy, vm::ptr<u32> addr);
|
||||
error_code sys_vm_memory_map_different(ppu_thread &ppu, u64 vsize, u64 psize,
|
||||
u32 cid, u64 flag, u64 policy,
|
||||
vm::ptr<u32> addr);
|
||||
error_code sys_vm_unmap(ppu_thread &ppu, u32 addr);
|
||||
error_code sys_vm_append_memory(ppu_thread &ppu, u32 addr, u64 size);
|
||||
error_code sys_vm_return_memory(ppu_thread &ppu, u32 addr, u64 size);
|
||||
error_code sys_vm_lock(ppu_thread &ppu, u32 addr, u32 size);
|
||||
error_code sys_vm_unlock(ppu_thread &ppu, u32 addr, u32 size);
|
||||
error_code sys_vm_touch(ppu_thread &ppu, u32 addr, u32 size);
|
||||
error_code sys_vm_flush(ppu_thread &ppu, u32 addr, u32 size);
|
||||
error_code sys_vm_invalidate(ppu_thread &ppu, u32 addr, u32 size);
|
||||
error_code sys_vm_store(ppu_thread &ppu, u32 addr, u32 size);
|
||||
error_code sys_vm_sync(ppu_thread &ppu, u32 addr, u32 size);
|
||||
error_code sys_vm_test(ppu_thread &ppu, u32 addr, u32 size,
|
||||
vm::ptr<u64> result);
|
||||
error_code sys_vm_get_statistics(ppu_thread &ppu, u32 addr,
|
||||
vm::ptr<sys_vm_statistics_t> stat);
|
||||
2529
kernel/cellos/src/lv2.cpp
Normal file
2529
kernel/cellos/src/lv2.cpp
Normal file
File diff suppressed because it is too large
Load diff
16
kernel/cellos/src/sys_bdemu.cpp
Normal file
16
kernel/cellos/src/sys_bdemu.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
|
||||
#include "sys_bdemu.h"
|
||||
|
||||
LOG_CHANNEL(sys_bdemu);
|
||||
|
||||
error_code sys_bdemu_send_command(u64 cmd, u64 a2, u64 a3, vm::ptr<void> buf,
|
||||
u64 buf_len) {
|
||||
sys_bdemu.todo("sys_bdemu_send_command(cmd=0%llx, a2=0x%x, a3=0x%x, "
|
||||
"buf=0x%x, buf_len=0x%x)",
|
||||
cmd, a2, a3, buf, buf_len);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
12
kernel/cellos/src/sys_btsetting.cpp
Normal file
12
kernel/cellos/src/sys_btsetting.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "sys_btsetting.h"
|
||||
|
||||
LOG_CHANNEL(sys_btsetting);
|
||||
|
||||
error_code sys_btsetting_if(u64 cmd, vm::ptr<void> msg) {
|
||||
sys_btsetting.todo("sys_btsetting_if(cmd=0x%llx, msg=*0x%x)", cmd, msg);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
509
kernel/cellos/src/sys_cond.cpp
Normal file
509
kernel/cellos/src/sys_cond.cpp
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/System.h"
|
||||
#include "util/serialization.hpp"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
|
||||
#include "sys_cond.h"
|
||||
|
||||
#include "rx/asm.hpp"
|
||||
|
||||
LOG_CHANNEL(sys_cond);
|
||||
|
||||
lv2_cond::lv2_cond(utils::serial &ar) noexcept
|
||||
: key(ar), name(ar), mtx_id(ar),
|
||||
mutex(idm::check_unlocked<lv2_obj, lv2_mutex>(mtx_id)),
|
||||
_mutex(idm::get_unlocked<lv2_obj, lv2_mutex>(mtx_id)) // May be nullptr
|
||||
{}
|
||||
|
||||
lv2_cond::lv2_cond(u64 key, u64 name, u32 mtx_id,
|
||||
shared_ptr<lv2_obj> mutex0) noexcept
|
||||
: key(key), name(name), mtx_id(mtx_id),
|
||||
mutex(static_cast<lv2_mutex *>(mutex0.get())), _mutex(mutex0) {}
|
||||
|
||||
CellError lv2_cond::on_id_create() {
|
||||
exists++;
|
||||
|
||||
static auto do_it = [](lv2_cond *_this) -> CellError {
|
||||
if (lv2_obj::check(_this->mutex)) {
|
||||
_this->mutex->cond_count++;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Mutex has been destroyed, cannot create conditional variable
|
||||
return CELL_ESRCH;
|
||||
};
|
||||
|
||||
if (mutex) {
|
||||
return do_it(this);
|
||||
}
|
||||
|
||||
ensure(!!Emu.DeserialManager());
|
||||
|
||||
Emu.PostponeInitCode([this]() {
|
||||
if (!mutex) {
|
||||
_mutex = static_cast<shared_ptr<lv2_obj>>(
|
||||
ensure(idm::get_unlocked<lv2_obj, lv2_mutex>(mtx_id)));
|
||||
}
|
||||
|
||||
// Defer function
|
||||
ensure(CellError{} == do_it(this));
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::function<void(void *)> lv2_cond::load(utils::serial &ar) {
|
||||
return load_func(make_shared<lv2_cond>(exact_t<utils::serial &>(ar)));
|
||||
}
|
||||
|
||||
void lv2_cond::save(utils::serial &ar) { ar(key, name, mtx_id); }
|
||||
|
||||
error_code sys_cond_create(ppu_thread &ppu, vm::ptr<u32> cond_id, u32 mutex_id,
|
||||
vm::ptr<sys_cond_attribute_t> attr) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_cond.trace("sys_cond_create(cond_id=*0x%x, mutex_id=0x%x, attr=*0x%x)",
|
||||
cond_id, mutex_id, attr);
|
||||
|
||||
auto mutex = idm::get_unlocked<lv2_obj, lv2_mutex>(mutex_id);
|
||||
|
||||
if (!mutex) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
const auto _attr = *attr;
|
||||
|
||||
const u64 ipc_key = lv2_obj::get_key(_attr);
|
||||
|
||||
if (ipc_key) {
|
||||
sys_cond.warning("sys_cond_create(cond_id=*0x%x, attr=*0x%x): IPC=0x%016x",
|
||||
cond_id, attr, ipc_key);
|
||||
}
|
||||
|
||||
if (const auto error =
|
||||
lv2_obj::create<lv2_cond>(_attr.pshared, ipc_key, _attr.flags, [&] {
|
||||
return make_single<lv2_cond>(ipc_key, _attr.name_u64, mutex_id,
|
||||
std::move(mutex));
|
||||
})) {
|
||||
return error;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*cond_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_cond_destroy(ppu_thread &ppu, u32 cond_id) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_cond.trace("sys_cond_destroy(cond_id=0x%x)", cond_id);
|
||||
|
||||
const auto cond = idm::withdraw<lv2_obj, lv2_cond>(
|
||||
cond_id, [&](lv2_cond &cond) -> CellError {
|
||||
std::lock_guard lock(cond.mutex->mutex);
|
||||
|
||||
if (atomic_storage<ppu_thread *>::load(cond.sq)) {
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
cond.mutex->cond_count--;
|
||||
lv2_obj::on_id_destroy(cond, cond.key);
|
||||
return {};
|
||||
});
|
||||
|
||||
if (!cond) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (cond->key) {
|
||||
sys_cond.warning("sys_cond_destroy(cond_id=0x%x): IPC=0x%016x", cond_id,
|
||||
cond->key);
|
||||
}
|
||||
|
||||
if (cond.ret) {
|
||||
return cond.ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_cond_signal(ppu_thread &ppu, u32 cond_id) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_cond.trace("sys_cond_signal(cond_id=0x%x)", cond_id);
|
||||
|
||||
while (true) {
|
||||
if (ppu.test_stopped()) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool finished = true;
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
const auto cond = idm::check<lv2_obj, lv2_cond>(
|
||||
cond_id, [&, notify = lv2_obj::notify_all_t()](lv2_cond &cond) {
|
||||
if (atomic_storage<ppu_thread *>::load(cond.sq)) {
|
||||
std::lock_guard lock(cond.mutex->mutex);
|
||||
|
||||
if (ppu.state & cpu_flag::suspend) {
|
||||
// Test if another signal caused the current thread to be
|
||||
// suspended, in which case it needs to wait until the thread
|
||||
// wakes up (otherwise the signal may cause unexpected results)
|
||||
finished = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto cpu =
|
||||
cond.schedule<ppu_thread>(cond.sq, cond.mutex->protocol)) {
|
||||
if (static_cast<ppu_thread *>(cpu)->state & cpu_flag::again) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Is EBUSY returned after reqeueing, on sys_cond_destroy?
|
||||
|
||||
if (cond.mutex->try_own(*cpu)) {
|
||||
cond.awake(cpu);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cond.mutex->mutex.lock_unlock();
|
||||
|
||||
if (ppu.state & cpu_flag::suspend) {
|
||||
finished = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!finished) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cond) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
error_code sys_cond_signal_all(ppu_thread &ppu, u32 cond_id) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_cond.trace("sys_cond_signal_all(cond_id=0x%x)", cond_id);
|
||||
|
||||
while (true) {
|
||||
if (ppu.test_stopped()) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool finished = true;
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
const auto cond = idm::check<lv2_obj, lv2_cond>(
|
||||
cond_id, [&, notify = lv2_obj::notify_all_t()](lv2_cond &cond) {
|
||||
if (atomic_storage<ppu_thread *>::load(cond.sq)) {
|
||||
std::lock_guard lock(cond.mutex->mutex);
|
||||
|
||||
if (ppu.state & cpu_flag::suspend) {
|
||||
// Test if another signal caused the current thread to be
|
||||
// suspended, in which case it needs to wait until the thread
|
||||
// wakes up (otherwise the signal may cause unexpected results)
|
||||
finished = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto cpu = +cond.sq; cpu; cpu = cpu->next_cpu) {
|
||||
if (cpu->state & cpu_flag::again) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cpu_thread *result = nullptr;
|
||||
auto sq = cond.sq;
|
||||
atomic_storage<ppu_thread *>::release(cond.sq, nullptr);
|
||||
|
||||
while (const auto cpu =
|
||||
cond.schedule<ppu_thread>(sq, SYS_SYNC_PRIORITY)) {
|
||||
if (cond.mutex->try_own(*cpu)) {
|
||||
ensure(!std::exchange(result, cpu));
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
cond.awake(result);
|
||||
}
|
||||
} else {
|
||||
cond.mutex->mutex.lock_unlock();
|
||||
|
||||
if (ppu.state & cpu_flag::suspend) {
|
||||
finished = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!finished) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cond) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
error_code sys_cond_signal_to(ppu_thread &ppu, u32 cond_id, u32 thread_id) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_cond.trace("sys_cond_signal_to(cond_id=0x%x, thread_id=0x%x)", cond_id,
|
||||
thread_id);
|
||||
|
||||
while (true) {
|
||||
if (ppu.test_stopped()) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool finished = true;
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
const auto cond = idm::check<lv2_obj, lv2_cond>(
|
||||
cond_id, [&, notify = lv2_obj::notify_all_t()](lv2_cond &cond) {
|
||||
if (!idm::check_unlocked<named_thread<ppu_thread>>(thread_id)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (atomic_storage<ppu_thread *>::load(cond.sq)) {
|
||||
std::lock_guard lock(cond.mutex->mutex);
|
||||
|
||||
if (ppu.state & cpu_flag::suspend) {
|
||||
// Test if another signal caused the current thread to be
|
||||
// suspended, in which case it needs to wait until the thread
|
||||
// wakes up (otherwise the signal may cause unexpected results)
|
||||
finished = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (auto cpu = +cond.sq; cpu; cpu = cpu->next_cpu) {
|
||||
if (cpu->id == thread_id) {
|
||||
if (static_cast<ppu_thread *>(cpu)->state & cpu_flag::again) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ensure(cond.unqueue(cond.sq, cpu));
|
||||
|
||||
if (cond.mutex->try_own(*cpu)) {
|
||||
cond.awake(cpu);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cond.mutex->mutex.lock_unlock();
|
||||
|
||||
if (ppu.state & cpu_flag::suspend) {
|
||||
finished = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (!finished) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cond || cond.ret == -1) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (!cond.ret) {
|
||||
return not_an_error(CELL_EPERM);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
error_code sys_cond_wait(ppu_thread &ppu, u32 cond_id, u64 timeout) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_cond.trace("sys_cond_wait(cond_id=0x%x, timeout=%lld)", cond_id, timeout);
|
||||
|
||||
// Further function result
|
||||
ppu.gpr[3] = CELL_OK;
|
||||
|
||||
auto &sstate = *ppu.optional_savestate_state;
|
||||
|
||||
const auto cond = idm::get<lv2_obj, lv2_cond>(
|
||||
cond_id, [&, notify = lv2_obj::notify_all_t()](lv2_cond &cond) -> s64 {
|
||||
if (!ppu.loaded_from_savestate &&
|
||||
atomic_storage<u32>::load(cond.mutex->control.raw().owner) !=
|
||||
ppu.id) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
lv2_obj::prepare_for_sleep(ppu);
|
||||
|
||||
std::lock_guard lock(cond.mutex->mutex);
|
||||
|
||||
const u64 syscall_state = sstate.try_read<u64>().second;
|
||||
sstate.clear();
|
||||
|
||||
if (ppu.loaded_from_savestate) {
|
||||
if (syscall_state & 1) {
|
||||
// Mutex sleep
|
||||
ensure(!cond.mutex->try_own(ppu));
|
||||
} else {
|
||||
lv2_obj::emplace(cond.sq, &ppu);
|
||||
}
|
||||
|
||||
cond.sleep(ppu, timeout);
|
||||
return static_cast<u32>(syscall_state >> 32);
|
||||
}
|
||||
|
||||
// Register waiter
|
||||
lv2_obj::emplace(cond.sq, &ppu);
|
||||
|
||||
// Unlock the mutex
|
||||
const u32 count = cond.mutex->lock_count.exchange(0);
|
||||
|
||||
if (const auto cpu = cond.mutex->reown<ppu_thread>()) {
|
||||
if (cpu->state & cpu_flag::again) {
|
||||
ensure(cond.unqueue(cond.sq, &ppu));
|
||||
ppu.state += cpu_flag::again;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cond.mutex->append(cpu);
|
||||
}
|
||||
|
||||
// Sleep current thread and schedule mutex waiter
|
||||
cond.sleep(ppu, timeout);
|
||||
|
||||
// Save the recursive value
|
||||
return count;
|
||||
});
|
||||
|
||||
if (!cond) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (ppu.state & cpu_flag::again) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (cond.ret < 0) {
|
||||
return CELL_EPERM;
|
||||
}
|
||||
|
||||
while (auto state = +ppu.state) {
|
||||
if (state & cpu_flag::signal &&
|
||||
ppu.state.test_and_reset(cpu_flag::signal)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_stopped(state)) {
|
||||
std::lock_guard lock(cond->mutex->mutex);
|
||||
|
||||
bool mutex_sleep = false;
|
||||
bool cond_sleep = false;
|
||||
|
||||
for (auto cpu = atomic_storage<ppu_thread *>::load(cond->sq); cpu;
|
||||
cpu = cpu->next_cpu) {
|
||||
if (cpu == &ppu) {
|
||||
cond_sleep = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cpu = atomic_storage<ppu_thread *>::load(
|
||||
cond->mutex->control.raw().sq);
|
||||
cpu; cpu = cpu->next_cpu) {
|
||||
if (cpu == &ppu) {
|
||||
mutex_sleep = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cond_sleep && !mutex_sleep) {
|
||||
break;
|
||||
}
|
||||
|
||||
const u64 optional_syscall_state =
|
||||
u32{mutex_sleep} | (u64{static_cast<u32>(cond.ret)} << 32);
|
||||
sstate(optional_syscall_state);
|
||||
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
for (usz i = 0; cpu_flag::signal - ppu.state && i < 50; i++) {
|
||||
rx::busy_wait(500);
|
||||
}
|
||||
|
||||
if (ppu.state & cpu_flag::signal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (lv2_obj::wait_timeout(timeout, &ppu)) {
|
||||
const u64 start_time = ppu.start_time;
|
||||
|
||||
// Wait for rescheduling
|
||||
if (ppu.check_state()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
std::lock_guard lock(cond->mutex->mutex);
|
||||
|
||||
// Try to cancel the waiting
|
||||
if (cond->unqueue(cond->sq, &ppu)) {
|
||||
// TODO: Is EBUSY returned after reqeueing, on sys_cond_destroy?
|
||||
ppu.gpr[3] = CELL_ETIMEDOUT;
|
||||
|
||||
// Own or requeue
|
||||
if (cond->mutex->try_own(ppu)) {
|
||||
break;
|
||||
}
|
||||
} else if (atomic_storage<u32>::load(
|
||||
cond->mutex->control.raw().owner) == ppu.id) {
|
||||
break;
|
||||
}
|
||||
|
||||
cond->mutex->sleep(ppu);
|
||||
ppu.start_time =
|
||||
start_time; // Restore start time because awake has been called
|
||||
timeout = 0;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify ownership
|
||||
ensure(atomic_storage<u32>::load(cond->mutex->control.raw().owner) == ppu.id);
|
||||
|
||||
// Restore the recursive value
|
||||
cond->mutex->lock_count.release(static_cast<u32>(cond.ret));
|
||||
|
||||
return not_an_error(ppu.gpr[3]);
|
||||
}
|
||||
464
kernel/cellos/src/sys_config.cpp
Normal file
464
kernel/cellos/src/sys_config.cpp
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "cellos/sys_event.h"
|
||||
|
||||
#include "sys_config.h"
|
||||
|
||||
LOG_CHANNEL(sys_config);
|
||||
|
||||
// Enums
|
||||
template <>
|
||||
void fmt_class_string<sys_config_service_id>::format(std::string &out, u64 id) {
|
||||
const s64 s_id = static_cast<s64>(id);
|
||||
|
||||
switch (s_id) {
|
||||
case SYS_CONFIG_SERVICE_PADMANAGER:
|
||||
out += "SYS_CONFIG_SERVICE_PADMANAGER";
|
||||
return;
|
||||
case SYS_CONFIG_SERVICE_PADMANAGER2:
|
||||
out += "SYS_CONFIG_SERVICE_PADMANAGER2";
|
||||
return;
|
||||
case SYS_CONFIG_SERVICE_USER_LIBPAD:
|
||||
out += "SYS_CONFIG_SERVICE_USER_LIBPAD";
|
||||
return;
|
||||
case SYS_CONFIG_SERVICE_USER_LIBKB:
|
||||
out += "SYS_CONFIG_SERVICE_USER_LIBKB";
|
||||
return;
|
||||
case SYS_CONFIG_SERVICE_USER_LIBMOUSE:
|
||||
out += "SYS_CONFIG_SERVICE_USER_LIBMOUSE";
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_id < 0) {
|
||||
fmt::append(out, "SYS_CONFIG_SERVICE_USER_%llx", id & ~(1ull << 63));
|
||||
} else {
|
||||
fmt::append(out, "SYS_CONFIG_SERVICE_%llx", id);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<sys_config_service_listener_type>::format(
|
||||
std::string &out, u64 arg) {
|
||||
format_enum(out, arg, [](auto value) {
|
||||
switch (value) {
|
||||
STR_CASE(SYS_CONFIG_SERVICE_LISTENER_ONCE);
|
||||
STR_CASE(SYS_CONFIG_SERVICE_LISTENER_REPEATING);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
// Utilities
|
||||
void dump_buffer(std::string &out, const std::vector<u8> &buffer) {
|
||||
if (!buffer.empty()) {
|
||||
out.reserve(out.size() + buffer.size() * 2 + 1);
|
||||
fmt::append(out, "0x");
|
||||
|
||||
for (u8 x : buffer) {
|
||||
fmt::append(out, "%02x", x);
|
||||
}
|
||||
} else {
|
||||
fmt::append(out, "EMPTY");
|
||||
}
|
||||
}
|
||||
|
||||
// LV2 Config
|
||||
void lv2_config::initialize() {
|
||||
if (m_state || !m_state.compare_and_swap_test(0, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register padmanager service, notifying vsh that a controller is connected
|
||||
static const u8 hid_info[0x1a] = {
|
||||
0x01, 0x01, // 2 unk
|
||||
0x02, 0x02, // 4
|
||||
0x00, 0x00, // 6
|
||||
0x00, 0x00, // 8
|
||||
0x00, 0x00, // 10
|
||||
0x05, 0x4c, // 12 vid
|
||||
0x02, 0x68, // 14 pid
|
||||
0x00, 0x10, // 16 unk2
|
||||
0x91, 0x88, // 18
|
||||
0x04, 0x00, // 20
|
||||
0x00, 0x07, // 22
|
||||
0x00, 0x00, // 24
|
||||
0x00, 0x00 // 26
|
||||
};
|
||||
|
||||
// user_id for the padmanager seems to signify the controller port number, and
|
||||
// the buffer contains some sort of HID descriptor
|
||||
lv2_config_service::create(SYS_CONFIG_SERVICE_PADMANAGER, 0, 1, 0, hid_info,
|
||||
0x1a)
|
||||
->notify();
|
||||
lv2_config_service::create(SYS_CONFIG_SERVICE_PADMANAGER2, 0, 1, 0, hid_info,
|
||||
0x1a)
|
||||
->notify();
|
||||
}
|
||||
|
||||
void lv2_config::add_service_event(shared_ptr<lv2_config_service_event> event) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
events.emplace(event->id, std::move(event));
|
||||
}
|
||||
|
||||
void lv2_config::remove_service_event(u32 id) {
|
||||
shared_ptr<lv2_config_service_event> ptr;
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (auto it = events.find(id); it != events.end()) {
|
||||
ptr = std::move(it->second);
|
||||
events.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
lv2_config_service_event &
|
||||
lv2_config_service_event::operator=(thread_state s) noexcept {
|
||||
if (s == thread_state::destroying_context && !m_destroyed.exchange(true)) {
|
||||
if (auto global = g_fxo->try_get<lv2_config>()) {
|
||||
global->remove_service_event(id);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
lv2_config_service_event::~lv2_config_service_event() noexcept {
|
||||
operator=(thread_state::destroying_context);
|
||||
}
|
||||
|
||||
lv2_config::~lv2_config() noexcept {
|
||||
for (auto &[key, event] : events) {
|
||||
if (event) {
|
||||
// Avoid collision with lv2_config_service_event destructor
|
||||
event->m_destroyed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LV2 Config Service Listener
|
||||
bool lv2_config_service_listener::check_service(
|
||||
const lv2_config_service &service) const {
|
||||
// Filter by type
|
||||
if (type == SYS_CONFIG_SERVICE_LISTENER_ONCE && !service_events.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter by service ID or verbosity
|
||||
if (service_id != service.id || min_verbosity > service.verbosity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// realhw only seems to send the pad connected events to the listeners that
|
||||
// provided 0x01 as the first byte of their data buffer
|
||||
// TODO: Figure out how this filter works more properly
|
||||
if (service_id == SYS_CONFIG_SERVICE_PADMANAGER &&
|
||||
(data.empty() || data[0] != 0x01)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Event applies to this listener!
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lv2_config_service_listener::notify(
|
||||
const shared_ptr<lv2_config_service_event> &event) {
|
||||
service_events.emplace_back(event);
|
||||
return event->notify();
|
||||
}
|
||||
|
||||
bool lv2_config_service_listener::notify(
|
||||
const shared_ptr<lv2_config_service> &service) {
|
||||
if (!check_service(*service))
|
||||
return false;
|
||||
|
||||
// Create service event and notify queue!
|
||||
const auto event = lv2_config_service_event::create(handle, service, *this);
|
||||
return notify(event);
|
||||
}
|
||||
|
||||
void lv2_config_service_listener::notify_all() {
|
||||
std::vector<shared_ptr<lv2_config_service>> services;
|
||||
|
||||
// Grab all events
|
||||
idm::select<lv2_config_service>([&](u32 /*id*/, lv2_config_service &service) {
|
||||
if (check_service(service)) {
|
||||
services.push_back(service.get_shared_ptr());
|
||||
}
|
||||
});
|
||||
|
||||
// Sort services by timestamp
|
||||
sort(services.begin(), services.end(),
|
||||
[](const shared_ptr<lv2_config_service> &s1,
|
||||
const shared_ptr<lv2_config_service> &s2) {
|
||||
return s1->timestamp < s2->timestamp;
|
||||
});
|
||||
|
||||
// Notify listener (now with services in sorted order)
|
||||
for (auto &service : services) {
|
||||
this->notify(service);
|
||||
}
|
||||
}
|
||||
|
||||
// LV2 Config Service
|
||||
void lv2_config_service::unregister() {
|
||||
registered = false;
|
||||
|
||||
// Notify listeners
|
||||
notify();
|
||||
|
||||
// Allow this object to be destroyed by withdrawing it from the IDM
|
||||
// Note that it won't be destroyed while there are service events that hold a
|
||||
// reference to it
|
||||
idm::remove<lv2_config_service>(idm_id);
|
||||
}
|
||||
|
||||
void lv2_config_service::notify() const {
|
||||
std::vector<shared_ptr<lv2_config_service_listener>> listeners;
|
||||
|
||||
const shared_ptr<lv2_config_service> sptr = get_shared_ptr();
|
||||
|
||||
idm::select<lv2_config_service_listener>(
|
||||
[&](u32 /*id*/, lv2_config_service_listener &listener) {
|
||||
if (listener.check_service(*sptr))
|
||||
listeners.push_back(listener.get_shared_ptr());
|
||||
});
|
||||
|
||||
for (auto &listener : listeners) {
|
||||
listener->notify(sptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool lv2_config_service_event::notify() const {
|
||||
const auto _handle = handle;
|
||||
|
||||
if (!_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send event
|
||||
return _handle->notify(SYS_CONFIG_EVENT_SOURCE_SERVICE,
|
||||
(static_cast<u64>(service->is_registered()) << 32) |
|
||||
id,
|
||||
service->get_size());
|
||||
}
|
||||
|
||||
// LV2 Config Service Event
|
||||
void lv2_config_service_event::write(sys_config_service_event_t *dst) const {
|
||||
const auto registered = service->is_registered();
|
||||
|
||||
dst->service_listener_handle = listener.get_id();
|
||||
dst->registered = registered;
|
||||
dst->service_id = service->id;
|
||||
dst->user_id = service->user_id;
|
||||
|
||||
if (registered) {
|
||||
dst->verbosity = service->verbosity;
|
||||
dst->padding = service->padding;
|
||||
|
||||
const auto size = service->data.size();
|
||||
dst->data_size = static_cast<u32>(size);
|
||||
memcpy(dst->data, service->data.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Syscalls
|
||||
*/
|
||||
error_code sys_config_open(u32 equeue_hdl, vm::ptr<u32> out_config_hdl) {
|
||||
sys_config.trace("sys_config_open(equeue_hdl=0x%x, out_config_hdl=*0x%x)",
|
||||
equeue_hdl, out_config_hdl);
|
||||
|
||||
// Find queue with the given ID
|
||||
const auto queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(equeue_hdl);
|
||||
if (!queue) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Initialize lv2_config global state
|
||||
auto &global = g_fxo->get<lv2_config>();
|
||||
if (true) {
|
||||
global.initialize();
|
||||
}
|
||||
|
||||
// Create a lv2_config_handle object
|
||||
const auto config = lv2_config_handle::create(std::move(queue));
|
||||
if (config) {
|
||||
*out_config_hdl = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// Failed to allocate sys_config object
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
error_code sys_config_close(u32 config_hdl) {
|
||||
sys_config.trace("sys_config_close(config_hdl=0x%x)", config_hdl);
|
||||
|
||||
if (!idm::remove<lv2_config_handle>(config_hdl)) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_get_service_event(u32 config_hdl, u32 event_id,
|
||||
vm::ptr<sys_config_service_event_t> dst,
|
||||
u64 size) {
|
||||
sys_config.trace("sys_config_get_service_event(config_hdl=0x%x, "
|
||||
"event_id=0x%llx, dst=*0x%llx, size=0x%llx)",
|
||||
config_hdl, event_id, dst, size);
|
||||
|
||||
// Find sys_config handle object with the given ID
|
||||
const auto cfg = idm::get_unlocked<lv2_config_handle>(config_hdl);
|
||||
if (!cfg) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Find service_event object
|
||||
const auto event = g_fxo->get<lv2_config>().find_event(event_id);
|
||||
if (!event) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Check buffer fits
|
||||
if (!event->check_buffer_size(size)) {
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
// Write event to buffer
|
||||
event->write(dst.get_ptr());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_add_service_listener(
|
||||
u32 config_hdl, sys_config_service_id service_id, u64 min_verbosity,
|
||||
vm::ptr<void> in, u64 size, sys_config_service_listener_type type,
|
||||
vm::ptr<u32> out_listener_hdl) {
|
||||
sys_config.trace("sys_config_add_service_listener(config_hdl=0x%x, "
|
||||
"service_id=0x%llx, min_verbosity=0x%llx, in=*0x%x, "
|
||||
"size=%lld, type=0x%llx, out_listener_hdl=*0x%x)",
|
||||
config_hdl, service_id, min_verbosity, in, size, type,
|
||||
out_listener_hdl);
|
||||
|
||||
// Find sys_config handle object with the given ID
|
||||
auto cfg = idm::get_unlocked<lv2_config_handle>(config_hdl);
|
||||
if (!cfg) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Create service listener
|
||||
const auto listener = lv2_config_service_listener::create(
|
||||
cfg, service_id, min_verbosity, type, static_cast<u8 *>(in.get_ptr()),
|
||||
size);
|
||||
if (!listener) {
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
std::string buf_str;
|
||||
dump_buffer(buf_str, listener->data);
|
||||
sys_config.todo(
|
||||
"Registered service listener for service %llx with non-zero buffer: %s",
|
||||
service_id, buf_str.c_str());
|
||||
}
|
||||
|
||||
// Notify listener with all past events
|
||||
listener->notify_all();
|
||||
|
||||
// Done!
|
||||
*out_listener_hdl = listener->get_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_remove_service_listener(u32 config_hdl,
|
||||
u32 listener_hdl) {
|
||||
sys_config.trace(
|
||||
"sys_config_remove_service_listener(config_hdl=0x%x, listener_hdl=0x%x)",
|
||||
config_hdl, listener_hdl);
|
||||
|
||||
// Remove listener from IDM
|
||||
if (!idm::remove<lv2_config_service_listener>(listener_hdl)) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_register_service(u32 config_hdl,
|
||||
sys_config_service_id service_id,
|
||||
u64 user_id, u64 verbosity,
|
||||
vm::ptr<u8> data_buf, u64 size,
|
||||
vm::ptr<u32> out_service_hdl) {
|
||||
sys_config.trace("sys_config_register_service(config_hdl=0x%x, "
|
||||
"service_id=0x%llx, user_id=0x%llx, verbosity=0x%llx, "
|
||||
"data_but=*0x%llx, size=%lld, out_service_hdl=*0x%llx)",
|
||||
config_hdl, service_id, user_id, verbosity, data_buf, size,
|
||||
out_service_hdl);
|
||||
|
||||
// Find sys_config handle object with the given ID
|
||||
const auto cfg = idm::get_unlocked<lv2_config_handle>(config_hdl);
|
||||
if (!cfg) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Create service
|
||||
const auto service = lv2_config_service::create(
|
||||
service_id, user_id, verbosity, 0, data_buf.get_ptr(), size);
|
||||
if (!service) {
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
// Notify all listeners
|
||||
service->notify();
|
||||
|
||||
// Done!
|
||||
*out_service_hdl = service->get_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_unregister_service(u32 config_hdl, u32 service_hdl) {
|
||||
sys_config.trace(
|
||||
"sys_config_unregister_service(config_hdl=0x%x, service_hdl=0x%x)",
|
||||
config_hdl, service_hdl);
|
||||
|
||||
// Remove listener from IDM
|
||||
auto service = idm::withdraw<lv2_config_service>(service_hdl);
|
||||
if (!service) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Unregister service
|
||||
service->unregister();
|
||||
|
||||
// Done!
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* IO Events - TODO
|
||||
*/
|
||||
error_code sys_config_get_io_event(u32 config_hdl, u32 event_id /*?*/,
|
||||
vm::ptr<void> out_buf /*?*/,
|
||||
u64 size /*?*/) {
|
||||
sys_config.todo("sys_config_get_io_event(config_hdl=0x%x, event_id=0x%x, "
|
||||
"out_buf=*0x%x, size=%lld)",
|
||||
config_hdl, event_id, out_buf, size);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_register_io_error_listener(u32 config_hdl) {
|
||||
sys_config.todo("sys_config_register_io_error_listener(config_hdl=0x%x)",
|
||||
config_hdl);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_unregister_io_error_listener(u32 config_hdl) {
|
||||
sys_config.todo("sys_config_unregister_io_error_listener(config_hdl=0x%x)",
|
||||
config_hdl);
|
||||
return CELL_OK;
|
||||
}
|
||||
13
kernel/cellos/src/sys_console.cpp
Normal file
13
kernel/cellos/src/sys_console.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
|
||||
#include "sys_console.h"
|
||||
|
||||
LOG_CHANNEL(sys_console);
|
||||
|
||||
error_code sys_console_write(vm::cptr<char> buf, u32 len) {
|
||||
sys_console.todo("sys_console_write(buf=*0x%x, len=0x%x)", buf, len);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
28
kernel/cellos/src/sys_crypto_engine.cpp
Normal file
28
kernel/cellos/src/sys_crypto_engine.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
|
||||
#include "sys_crypto_engine.h"
|
||||
|
||||
LOG_CHANNEL(sys_crypto_engine);
|
||||
|
||||
error_code sys_crypto_engine_create(vm::ptr<u32> id) {
|
||||
sys_crypto_engine.todo("sys_crypto_engine_create(id=*0x%x)", id);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_crypto_engine_destroy(u32 id) {
|
||||
sys_crypto_engine.todo("sys_crypto_engine_destroy(id=0x%x)", id);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_crypto_engine_random_generate(vm::ptr<void> buffer,
|
||||
u64 buffer_size) {
|
||||
sys_crypto_engine.todo(
|
||||
"sys_crypto_engine_random_generate(buffer=*0x%x, buffer_size=0x%x",
|
||||
buffer, buffer_size);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
129
kernel/cellos/src/sys_dbg.cpp
Normal file
129
kernel/cellos/src/sys_dbg.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "sys_dbg.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
|
||||
#include "Emu/Cell/PPUInterpreter.h"
|
||||
#include "Emu/Memory/vm_locking.h"
|
||||
#include "rpcsx/fw/ps3/sys_lv2dbg.h"
|
||||
|
||||
#include "rx/align.hpp"
|
||||
#include "rx/asm.hpp"
|
||||
|
||||
void ppu_register_function_at(u32 addr, u32 size,
|
||||
ppu_intrp_func_t ptr = nullptr);
|
||||
|
||||
LOG_CHANNEL(sys_dbg);
|
||||
|
||||
error_code sys_dbg_read_process_memory(s32 pid, u32 address, u32 size,
|
||||
vm::ptr<void> data) {
|
||||
sys_dbg.warning("sys_dbg_read_process_memory(pid=0x%x, address=0x%llx, "
|
||||
"size=0x%x, data=*0x%x)",
|
||||
pid, address, size, data);
|
||||
|
||||
// Todo(TGEnigma): Process lookup (only 1 process exists right now)
|
||||
if (pid != 1) {
|
||||
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
|
||||
}
|
||||
|
||||
if (!size || !data) {
|
||||
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
|
||||
}
|
||||
|
||||
vm::writer_lock lock;
|
||||
|
||||
// Check if data destination is writable
|
||||
if (!vm::check_addr(data.addr(), vm::page_writable, size)) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
// Check if the source is readable
|
||||
if (!vm::check_addr(address, vm::page_readable, size)) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
std::memmove(data.get_ptr(), vm::base(address), size);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_dbg_write_process_memory(s32 pid, u32 address, u32 size,
|
||||
vm::cptr<void> data) {
|
||||
sys_dbg.warning("sys_dbg_write_process_memory(pid=0x%x, address=0x%llx, "
|
||||
"size=0x%x, data=*0x%x)",
|
||||
pid, address, size, data);
|
||||
|
||||
// Todo(TGEnigma): Process lookup (only 1 process exists right now)
|
||||
if (pid != 1) {
|
||||
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
|
||||
}
|
||||
|
||||
if (!size || !data) {
|
||||
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
|
||||
}
|
||||
|
||||
// Check if data source is readable
|
||||
if (!vm::check_addr(data.addr(), vm::page_readable, size)) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
// Check destination (can be read-only actually)
|
||||
if (!vm::check_addr(address, vm::page_readable, size)) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
vm::writer_lock lock;
|
||||
|
||||
// Again
|
||||
if (!vm::check_addr(data.addr(), vm::page_readable, size) ||
|
||||
!vm::check_addr(address, vm::page_readable, size)) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
const u8 *data_ptr = static_cast<const u8 *>(data.get_ptr());
|
||||
|
||||
if ((address >> 28) == 0xDu) {
|
||||
// Stack pages (4k pages is the exception here)
|
||||
std::memmove(vm::base(address), data_ptr, size);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
const u32 end = address + size;
|
||||
|
||||
for (u32 i = address, exec_update_size = 0; i < end;) {
|
||||
const u32 op_size =
|
||||
std::min<u32>(rx::alignUp<u32>(i + 1, 0x10000), end) - i;
|
||||
|
||||
const bool is_exec =
|
||||
vm::check_addr(i, vm::page_executable | vm::page_readable);
|
||||
|
||||
if (is_exec) {
|
||||
exec_update_size += op_size;
|
||||
i += op_size;
|
||||
}
|
||||
|
||||
if (!is_exec || i >= end) {
|
||||
// Commit executable data update
|
||||
// The read memory is also super ptr so memmove can work correctly on all
|
||||
// implementations
|
||||
const u32 before_addr = i - exec_update_size;
|
||||
std::memmove(vm::get_super_ptr(before_addr),
|
||||
vm::get_super_ptr(data.addr() + (before_addr - address)),
|
||||
exec_update_size);
|
||||
ppu_register_function_at(before_addr, exec_update_size);
|
||||
exec_update_size = 0;
|
||||
|
||||
if (i >= end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_exec) {
|
||||
std::memmove(vm::base(i), data_ptr + (i - address), op_size);
|
||||
i += op_size;
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
732
kernel/cellos/src/sys_event.cpp
Normal file
732
kernel/cellos/src/sys_event.cpp
Normal file
|
|
@ -0,0 +1,732 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "sys_event.h"
|
||||
|
||||
#include "Emu/IPC.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "sys_process.h"
|
||||
|
||||
#include "rx/asm.hpp"
|
||||
|
||||
LOG_CHANNEL(sys_event);
|
||||
|
||||
lv2_event_queue::lv2_event_queue(u32 protocol, s32 type, s32 size, u64 name,
|
||||
u64 ipc_key) noexcept
|
||||
: id(idm::last_id()), protocol{static_cast<u8>(protocol)},
|
||||
type(static_cast<u8>(type)), size(static_cast<u8>(size)), name(name),
|
||||
key(ipc_key) {}
|
||||
|
||||
lv2_event_queue::lv2_event_queue(utils::serial &ar) noexcept
|
||||
: id(idm::last_id()), protocol(ar), type(ar), size(ar), name(ar), key(ar) {
|
||||
ar(events);
|
||||
}
|
||||
|
||||
std::function<void(void *)> lv2_event_queue::load(utils::serial &ar) {
|
||||
auto queue = make_shared<lv2_event_queue>(exact_t<utils::serial &>(ar));
|
||||
return [ptr = lv2_obj::load(queue->key, queue)](void *storage) {
|
||||
*static_cast<atomic_ptr<lv2_obj> *>(storage) = ptr;
|
||||
};
|
||||
}
|
||||
|
||||
void lv2_event_queue::save(utils::serial &ar) {
|
||||
ar(protocol, type, size, name, key, events);
|
||||
}
|
||||
|
||||
void lv2_event_queue::save_ptr(utils::serial &ar, lv2_event_queue *q) {
|
||||
if (!lv2_obj::check(q)) {
|
||||
ar(u32{0});
|
||||
return;
|
||||
}
|
||||
|
||||
ar(q->id);
|
||||
}
|
||||
|
||||
shared_ptr<lv2_event_queue>
|
||||
lv2_event_queue::load_ptr(utils::serial &ar, shared_ptr<lv2_event_queue> &queue,
|
||||
std::string_view msg) {
|
||||
const u32 id = ar.pop<u32>();
|
||||
|
||||
if (!id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (auto q = idm::get_unlocked<lv2_obj, lv2_event_queue>(id)) {
|
||||
// Already initialized
|
||||
return q;
|
||||
}
|
||||
|
||||
if (id >> 24 != id_base >> 24) {
|
||||
fmt::throw_exception("Failed in event queue pointer deserialization "
|
||||
"(invalid ID): location: %s, id=0x%x",
|
||||
msg, id);
|
||||
}
|
||||
|
||||
Emu.PostponeInitCode([id, &queue, msg_str = std::string{msg}]() {
|
||||
// Defer resolving
|
||||
queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(id);
|
||||
|
||||
if (!queue) {
|
||||
fmt::throw_exception("Failed in event queue pointer deserialization (not "
|
||||
"found): location: %s, id=0x%x",
|
||||
msg_str, id);
|
||||
}
|
||||
});
|
||||
|
||||
// Null until resolved
|
||||
return {};
|
||||
}
|
||||
|
||||
lv2_event_port::lv2_event_port(utils::serial &ar)
|
||||
: type(ar), name(ar),
|
||||
queue(lv2_event_queue::load_ptr(ar, queue, "eventport")) {}
|
||||
|
||||
void lv2_event_port::save(utils::serial &ar) {
|
||||
ar(type, name);
|
||||
|
||||
lv2_event_queue::save_ptr(ar, queue.get());
|
||||
}
|
||||
|
||||
shared_ptr<lv2_event_queue> lv2_event_queue::find(u64 ipc_key) {
|
||||
if (ipc_key == SYS_EVENT_QUEUE_LOCAL) {
|
||||
// Invalid IPC key
|
||||
return {};
|
||||
}
|
||||
|
||||
return g_fxo->get<ipc_manager<lv2_event_queue, u64>>().get(ipc_key);
|
||||
}
|
||||
|
||||
extern void resume_spu_thread_group_from_waiting(spu_thread &spu);
|
||||
|
||||
CellError lv2_event_queue::send(lv2_event event, bool *notified_thread,
|
||||
lv2_event_port *port) {
|
||||
if (notified_thread) {
|
||||
*notified_thread = false;
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
if (!exists) {
|
||||
return CELL_ENOTCONN;
|
||||
}
|
||||
|
||||
if (!pq && !sq) {
|
||||
if (events.size() < this->size + 0u) {
|
||||
// Save event
|
||||
events.emplace_back(event);
|
||||
return {};
|
||||
}
|
||||
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
if (type == SYS_PPU_QUEUE) {
|
||||
// Store event in registers
|
||||
auto &ppu = static_cast<ppu_thread &>(*schedule<ppu_thread>(pq, protocol));
|
||||
|
||||
if (ppu.state & cpu_flag::again) {
|
||||
if (auto cpu = get_current_cpu_thread()) {
|
||||
cpu->state += cpu_flag::again;
|
||||
cpu->state += cpu_flag::exit;
|
||||
}
|
||||
|
||||
sys_event.warning("Ignored event!");
|
||||
|
||||
// Fake error for abort
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = event;
|
||||
|
||||
awake(&ppu);
|
||||
|
||||
if (port &&
|
||||
ppu.prio.load().prio <
|
||||
ensure(cpu_thread::get_current<ppu_thread>())->prio.load().prio) {
|
||||
// Block event port disconnection for the time being of sending events
|
||||
// PPU -> lower prio PPU is the only case that can cause thread blocking
|
||||
port->is_busy++;
|
||||
ensure(notified_thread);
|
||||
*notified_thread = true;
|
||||
}
|
||||
} else {
|
||||
// Store event in In_MBox
|
||||
auto &spu = static_cast<spu_thread &>(*schedule<spu_thread>(sq, protocol));
|
||||
|
||||
if (spu.state & cpu_flag::again) {
|
||||
if (auto cpu = get_current_cpu_thread()) {
|
||||
cpu->state += cpu_flag::exit + cpu_flag::again;
|
||||
}
|
||||
|
||||
sys_event.warning("Ignored event!");
|
||||
|
||||
// Fake error for abort
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
const u32 data1 = static_cast<u32>(std::get<1>(event));
|
||||
const u32 data2 = static_cast<u32>(std::get<2>(event));
|
||||
const u32 data3 = static_cast<u32>(std::get<3>(event));
|
||||
spu.ch_in_mbox.set_values(4, CELL_OK, data1, data2, data3);
|
||||
resume_spu_thread_group_from_waiting(spu);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
error_code sys_event_queue_create(cpu_thread &cpu, vm::ptr<u32> equeue_id,
|
||||
vm::ptr<sys_event_queue_attribute_t> attr,
|
||||
u64 ipc_key, s32 size) {
|
||||
cpu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.warning("sys_event_queue_create(equeue_id=*0x%x, attr=*0x%x, "
|
||||
"ipc_key=0x%llx, size=%d)",
|
||||
equeue_id, attr, ipc_key, size);
|
||||
|
||||
if (size <= 0 || size > 127) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const u32 protocol = attr->protocol;
|
||||
|
||||
if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_PRIORITY) {
|
||||
sys_event.error("sys_event_queue_create(): unknown protocol (0x%x)",
|
||||
protocol);
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const u32 type = attr->type;
|
||||
|
||||
if (type != SYS_PPU_QUEUE && type != SYS_SPU_QUEUE) {
|
||||
sys_event.error("sys_event_queue_create(): unknown type (0x%x)", type);
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const u32 pshared = ipc_key == SYS_EVENT_QUEUE_LOCAL
|
||||
? SYS_SYNC_NOT_PROCESS_SHARED
|
||||
: SYS_SYNC_PROCESS_SHARED;
|
||||
constexpr u32 flags = SYS_SYNC_NEWLY_CREATED;
|
||||
const u64 name = attr->name_u64;
|
||||
|
||||
if (const auto error =
|
||||
lv2_obj::create<lv2_event_queue>(pshared, ipc_key, flags, [&]() {
|
||||
return make_shared<lv2_event_queue>(protocol, type, size, name,
|
||||
ipc_key);
|
||||
})) {
|
||||
return error;
|
||||
}
|
||||
|
||||
cpu.check_state();
|
||||
*equeue_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_queue_destroy(ppu_thread &ppu, u32 equeue_id, s32 mode) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.warning("sys_event_queue_destroy(equeue_id=0x%x, mode=%d)",
|
||||
equeue_id, mode);
|
||||
|
||||
if (mode && mode != SYS_EVENT_QUEUE_DESTROY_FORCE) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
std::vector<lv2_event> events;
|
||||
|
||||
std::unique_lock<shared_mutex> qlock;
|
||||
|
||||
cpu_thread *head{};
|
||||
|
||||
const auto queue = idm::withdraw<lv2_obj, lv2_event_queue>(
|
||||
equeue_id, [&](lv2_event_queue &queue) -> CellError {
|
||||
qlock = std::unique_lock{queue.mutex};
|
||||
|
||||
head = queue.type == SYS_PPU_QUEUE
|
||||
? static_cast<cpu_thread *>(+queue.pq)
|
||||
: +queue.sq;
|
||||
|
||||
if (!mode && head) {
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
if (!queue.events.empty()) {
|
||||
// Copy events for logging, does not empty
|
||||
events.insert(events.begin(), queue.events.begin(),
|
||||
queue.events.end());
|
||||
}
|
||||
|
||||
lv2_obj::on_id_destroy(queue, queue.key);
|
||||
|
||||
if (!head) {
|
||||
qlock.unlock();
|
||||
} else {
|
||||
for (auto cpu = head; cpu; cpu = cpu->get_next_cpu()) {
|
||||
if (cpu->state & cpu_flag::again) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
if (!queue) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (ppu.state & cpu_flag::again) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (queue.ret) {
|
||||
return queue.ret;
|
||||
}
|
||||
|
||||
std::string lost_data;
|
||||
|
||||
if (qlock.owns_lock()) {
|
||||
if (sys_event.warning) {
|
||||
u32 size = 0;
|
||||
|
||||
for (auto cpu = head; cpu; cpu = cpu->get_next_cpu()) {
|
||||
size++;
|
||||
}
|
||||
|
||||
fmt::append(lost_data, "Forcefully awaken waiters (%u):\n", size);
|
||||
|
||||
for (auto cpu = head; cpu; cpu = cpu->get_next_cpu()) {
|
||||
lost_data += cpu->get_name();
|
||||
lost_data += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (queue->type == SYS_PPU_QUEUE) {
|
||||
for (auto cpu = +queue->pq; cpu; cpu = cpu->next_cpu) {
|
||||
cpu->gpr[3] = CELL_ECANCELED;
|
||||
queue->append(cpu);
|
||||
}
|
||||
|
||||
atomic_storage<ppu_thread *>::release(queue->pq, nullptr);
|
||||
lv2_obj::awake_all();
|
||||
} else {
|
||||
for (auto cpu = +queue->sq; cpu; cpu = cpu->next_cpu) {
|
||||
cpu->ch_in_mbox.set_values(1, CELL_ECANCELED);
|
||||
resume_spu_thread_group_from_waiting(*cpu);
|
||||
}
|
||||
|
||||
atomic_storage<spu_thread *>::release(queue->sq, nullptr);
|
||||
}
|
||||
|
||||
qlock.unlock();
|
||||
}
|
||||
|
||||
if (sys_event.warning) {
|
||||
if (!events.empty()) {
|
||||
fmt::append(lost_data, "Unread queue events (%u):\n", events.size());
|
||||
}
|
||||
|
||||
for (const lv2_event &evt : events) {
|
||||
fmt::append(lost_data, "data0=0x%x, data1=0x%x, data2=0x%x, data3=0x%x\n",
|
||||
std::get<0>(evt), std::get<1>(evt), std::get<2>(evt),
|
||||
std::get<3>(evt));
|
||||
}
|
||||
|
||||
if (!lost_data.empty()) {
|
||||
sys_event.warning("sys_event_queue_destroy(): %s", lost_data);
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_queue_tryreceive(ppu_thread &ppu, u32 equeue_id,
|
||||
vm::ptr<sys_event_t> event_array,
|
||||
s32 size, vm::ptr<u32> number) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.trace("sys_event_queue_tryreceive(equeue_id=0x%x, "
|
||||
"event_array=*0x%x, size=%d, number=*0x%x)",
|
||||
equeue_id, event_array, size, number);
|
||||
|
||||
const auto queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(equeue_id);
|
||||
|
||||
if (!queue) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (queue->type != SYS_PPU_QUEUE) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
std::array<sys_event_t, 127> events;
|
||||
|
||||
std::unique_lock lock(queue->mutex);
|
||||
|
||||
if (!queue->exists) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
s32 count = 0;
|
||||
|
||||
while (count < size && !queue->events.empty()) {
|
||||
auto &dest = events[count++];
|
||||
std::tie(dest.source, dest.data1, dest.data2, dest.data3) =
|
||||
queue->events.front();
|
||||
queue->events.pop_front();
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
ppu.check_state();
|
||||
|
||||
std::copy_n(events.begin(), count, event_array.get_ptr());
|
||||
*number = count;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_queue_receive(ppu_thread &ppu, u32 equeue_id,
|
||||
vm::ptr<sys_event_t> dummy_event,
|
||||
u64 timeout) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.trace(
|
||||
"sys_event_queue_receive(equeue_id=0x%x, *0x%x, timeout=0x%llx)",
|
||||
equeue_id, dummy_event, timeout);
|
||||
|
||||
ppu.gpr[3] = CELL_OK;
|
||||
|
||||
const auto queue = idm::get<lv2_obj, lv2_event_queue>(
|
||||
equeue_id,
|
||||
[&,
|
||||
notify = lv2_obj::notify_all_t()](lv2_event_queue &queue) -> CellError {
|
||||
if (queue.type != SYS_PPU_QUEUE) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
lv2_obj::prepare_for_sleep(ppu);
|
||||
|
||||
std::lock_guard lock(queue.mutex);
|
||||
|
||||
// "/dev_flash/vsh/module/msmw2.sprx" seems to rely on some cryptic
|
||||
// shared memory behaviour that we don't emulate correctly
|
||||
// This is a hack to avoid waiting for 1m40s every time we boot vsh
|
||||
if (queue.key == 0x8005911000000012 && Emu.IsVsh()) {
|
||||
sys_event.todo("sys_event_queue_receive(equeue_id=0x%x, *0x%x, "
|
||||
"timeout=0x%llx) Bypassing timeout for msmw2.sprx",
|
||||
equeue_id, dummy_event, timeout);
|
||||
timeout = 1;
|
||||
}
|
||||
|
||||
if (queue.events.empty()) {
|
||||
queue.sleep(ppu, timeout);
|
||||
lv2_obj::emplace(queue.pq, &ppu);
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) =
|
||||
queue.events.front();
|
||||
queue.events.pop_front();
|
||||
return {};
|
||||
});
|
||||
|
||||
if (!queue) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (queue.ret) {
|
||||
if (queue.ret != CELL_EBUSY) {
|
||||
return queue.ret;
|
||||
}
|
||||
} else {
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// If cancelled, gpr[3] will be non-zero. Other registers must contain event
|
||||
// data.
|
||||
while (auto state = +ppu.state) {
|
||||
if (state & cpu_flag::signal &&
|
||||
ppu.state.test_and_reset(cpu_flag::signal)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_stopped(state)) {
|
||||
std::lock_guard lock_rsx(queue->mutex);
|
||||
|
||||
for (auto cpu = +queue->pq; cpu; cpu = cpu->next_cpu) {
|
||||
if (cpu == &ppu) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (usz i = 0; cpu_flag::signal - ppu.state && i < 50; i++) {
|
||||
rx::busy_wait(500);
|
||||
}
|
||||
|
||||
if (ppu.state & cpu_flag::signal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (lv2_obj::wait_timeout(timeout, &ppu)) {
|
||||
// Wait for rescheduling
|
||||
if (ppu.check_state()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
if (!atomic_storage<ppu_thread *>::load(queue->pq)) {
|
||||
// Waiters queue is empty, so the thread must have been signaled
|
||||
queue->mutex.lock_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
std::lock_guard lock(queue->mutex);
|
||||
|
||||
if (!queue->unqueue(queue->pq, &ppu)) {
|
||||
break;
|
||||
}
|
||||
|
||||
ppu.gpr[3] = CELL_ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
}
|
||||
|
||||
return not_an_error(ppu.gpr[3]);
|
||||
}
|
||||
|
||||
error_code sys_event_queue_drain(ppu_thread &ppu, u32 equeue_id) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.trace("sys_event_queue_drain(equeue_id=0x%x)", equeue_id);
|
||||
|
||||
const auto queue = idm::check<lv2_obj, lv2_event_queue>(
|
||||
equeue_id, [&](lv2_event_queue &queue) {
|
||||
std::lock_guard lock(queue.mutex);
|
||||
|
||||
queue.events.clear();
|
||||
});
|
||||
|
||||
if (!queue) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_port_create(cpu_thread &cpu, vm::ptr<u32> eport_id,
|
||||
s32 port_type, u64 name) {
|
||||
cpu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.warning(
|
||||
"sys_event_port_create(eport_id=*0x%x, port_type=%d, name=0x%llx)",
|
||||
eport_id, port_type, name);
|
||||
|
||||
if (port_type != SYS_EVENT_PORT_LOCAL && port_type != 3) {
|
||||
sys_event.error("sys_event_port_create(): unknown port type (%d)",
|
||||
port_type);
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
if (const u32 id = idm::make<lv2_obj, lv2_event_port>(port_type, name)) {
|
||||
cpu.check_state();
|
||||
*eport_id = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
error_code sys_event_port_destroy(ppu_thread &ppu, u32 eport_id) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.warning("sys_event_port_destroy(eport_id=0x%x)", eport_id);
|
||||
|
||||
const auto port = idm::withdraw<lv2_obj, lv2_event_port>(
|
||||
eport_id, [](lv2_event_port &port) -> CellError {
|
||||
if (lv2_obj::check(port.queue)) {
|
||||
return CELL_EISCONN;
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
if (!port) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (port.ret) {
|
||||
return port.ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_port_connect_local(cpu_thread &cpu, u32 eport_id,
|
||||
u32 equeue_id) {
|
||||
cpu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.warning(
|
||||
"sys_event_port_connect_local(eport_id=0x%x, equeue_id=0x%x)", eport_id,
|
||||
equeue_id);
|
||||
|
||||
std::lock_guard lock(id_manager::g_mutex);
|
||||
|
||||
const auto port = idm::check_unlocked<lv2_obj, lv2_event_port>(eport_id);
|
||||
|
||||
if (!port || !idm::check_unlocked<lv2_obj, lv2_event_queue>(equeue_id)) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (port->type != SYS_EVENT_PORT_LOCAL) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
if (lv2_obj::check(port->queue)) {
|
||||
return CELL_EISCONN;
|
||||
}
|
||||
|
||||
port->queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(equeue_id);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_port_connect_ipc(ppu_thread &ppu, u32 eport_id,
|
||||
u64 ipc_key) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.warning("sys_event_port_connect_ipc(eport_id=0x%x, ipc_key=0x%x)",
|
||||
eport_id, ipc_key);
|
||||
|
||||
if (ipc_key == 0) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
auto queue = lv2_event_queue::find(ipc_key);
|
||||
|
||||
std::lock_guard lock(id_manager::g_mutex);
|
||||
|
||||
const auto port = idm::check_unlocked<lv2_obj, lv2_event_port>(eport_id);
|
||||
|
||||
if (!port || !queue) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (port->type != SYS_EVENT_PORT_IPC) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
if (lv2_obj::check(port->queue)) {
|
||||
return CELL_EISCONN;
|
||||
}
|
||||
|
||||
port->queue = std::move(queue);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_port_disconnect(ppu_thread &ppu, u32 eport_id) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event.warning("sys_event_port_disconnect(eport_id=0x%x)", eport_id);
|
||||
|
||||
std::lock_guard lock(id_manager::g_mutex);
|
||||
|
||||
const auto port = idm::check_unlocked<lv2_obj, lv2_event_port>(eport_id);
|
||||
|
||||
if (!port) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (!lv2_obj::check(port->queue)) {
|
||||
return CELL_ENOTCONN;
|
||||
}
|
||||
|
||||
if (port->is_busy) {
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
port->queue.reset();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) {
|
||||
const auto cpu = cpu_thread::get_current();
|
||||
const auto ppu = cpu ? cpu->try_get<ppu_thread>() : nullptr;
|
||||
|
||||
if (cpu) {
|
||||
cpu->state += cpu_flag::wait;
|
||||
}
|
||||
|
||||
sys_event.trace("sys_event_port_send(eport_id=0x%x, data1=0x%llx, "
|
||||
"data2=0x%llx, data3=0x%llx)",
|
||||
eport_id, data1, data2, data3);
|
||||
|
||||
bool notified_thread = false;
|
||||
|
||||
const auto port = idm::check<lv2_obj, lv2_event_port>(
|
||||
eport_id,
|
||||
[&, notify = lv2_obj::notify_all_t()](lv2_event_port &port) -> CellError {
|
||||
if (ppu && ppu->loaded_from_savestate) {
|
||||
port.is_busy++;
|
||||
notified_thread = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (lv2_obj::check(port.queue)) {
|
||||
const u64 source =
|
||||
port.name ? port.name
|
||||
: (u64{process_getpid() + 0u} << 32) | u64{eport_id};
|
||||
|
||||
return port.queue->send(
|
||||
source, data1, data2, data3, ¬ified_thread,
|
||||
ppu && port.queue->type == SYS_PPU_QUEUE ? &port : nullptr);
|
||||
}
|
||||
|
||||
return CELL_ENOTCONN;
|
||||
});
|
||||
|
||||
if (!port) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (ppu && notified_thread) {
|
||||
// Wait to be requeued
|
||||
if (ppu->test_stopped()) {
|
||||
// Wait again on savestate load
|
||||
ppu->state += cpu_flag::again;
|
||||
}
|
||||
|
||||
port->is_busy--;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (port.ret) {
|
||||
if (port.ret == CELL_EAGAIN) {
|
||||
// Not really an error code exposed to games (thread has raised
|
||||
// cpu_flag::again)
|
||||
return not_an_error(CELL_EAGAIN);
|
||||
}
|
||||
|
||||
if (port.ret == CELL_EBUSY) {
|
||||
return not_an_error(CELL_EBUSY);
|
||||
}
|
||||
|
||||
return port.ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
514
kernel/cellos/src/sys_event_flag.cpp
Normal file
514
kernel/cellos/src/sys_event_flag.cpp
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "sys_event_flag.h"
|
||||
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
|
||||
#include "rx/asm.hpp"
|
||||
|
||||
LOG_CHANNEL(sys_event_flag);
|
||||
|
||||
lv2_event_flag::lv2_event_flag(utils::serial &ar)
|
||||
: protocol(ar), key(ar), type(ar), name(ar) {
|
||||
ar(pattern);
|
||||
}
|
||||
|
||||
std::function<void(void *)> lv2_event_flag::load(utils::serial &ar) {
|
||||
return load_func(make_shared<lv2_event_flag>(exact_t<utils::serial &>(ar)));
|
||||
}
|
||||
|
||||
void lv2_event_flag::save(utils::serial &ar) {
|
||||
ar(protocol, key, type, name, pattern);
|
||||
}
|
||||
|
||||
error_code sys_event_flag_create(ppu_thread &ppu, vm::ptr<u32> id,
|
||||
vm::ptr<sys_event_flag_attribute_t> attr,
|
||||
u64 init) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event_flag.warning(
|
||||
"sys_event_flag_create(id=*0x%x, attr=*0x%x, init=0x%llx)", id, attr,
|
||||
init);
|
||||
|
||||
if (!id || !attr) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
const auto _attr = *attr;
|
||||
|
||||
const u32 protocol = _attr.protocol;
|
||||
|
||||
if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_PRIORITY) {
|
||||
sys_event_flag.error("sys_event_flag_create(): unknown protocol (0x%x)",
|
||||
protocol);
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const u32 type = _attr.type;
|
||||
|
||||
if (type != SYS_SYNC_WAITER_SINGLE && type != SYS_SYNC_WAITER_MULTIPLE) {
|
||||
sys_event_flag.error("sys_event_flag_create(): unknown type (0x%x)", type);
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const u64 ipc_key = lv2_obj::get_key(_attr);
|
||||
|
||||
if (const auto error = lv2_obj::create<lv2_event_flag>(
|
||||
_attr.pshared, ipc_key, _attr.flags, [&] {
|
||||
return make_shared<lv2_event_flag>(
|
||||
_attr.protocol, ipc_key, _attr.type, _attr.name_u64, init);
|
||||
})) {
|
||||
return error;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_flag_destroy(ppu_thread &ppu, u32 id) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event_flag.warning("sys_event_flag_destroy(id=0x%x)", id);
|
||||
|
||||
const auto flag = idm::withdraw<lv2_obj, lv2_event_flag>(
|
||||
id, [&](lv2_event_flag &flag) -> CellError {
|
||||
if (flag.sq) {
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
lv2_obj::on_id_destroy(flag, flag.key);
|
||||
return {};
|
||||
});
|
||||
|
||||
if (!flag) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (flag.ret) {
|
||||
return flag.ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_flag_wait(ppu_thread &ppu, u32 id, u64 bitptn, u32 mode,
|
||||
vm::ptr<u64> result, u64 timeout) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event_flag.trace("sys_event_flag_wait(id=0x%x, bitptn=0x%llx, mode=0x%x, "
|
||||
"result=*0x%x, timeout=0x%llx)",
|
||||
id, bitptn, mode, result, timeout);
|
||||
|
||||
// Fix function arguments for external access
|
||||
ppu.gpr[3] = -1;
|
||||
ppu.gpr[4] = bitptn;
|
||||
ppu.gpr[5] = mode;
|
||||
ppu.gpr[6] = 0;
|
||||
|
||||
// Always set result
|
||||
struct store_result {
|
||||
vm::ptr<u64> ptr;
|
||||
u64 val = 0;
|
||||
|
||||
~store_result() noexcept {
|
||||
if (ptr) {
|
||||
cpu_thread::get_current()->check_state();
|
||||
*ptr = val;
|
||||
}
|
||||
}
|
||||
} store{result};
|
||||
|
||||
if (!lv2_event_flag::check_mode(mode)) {
|
||||
sys_event_flag.error("sys_event_flag_wait(): unknown mode (0x%x)", mode);
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const auto flag = idm::get<lv2_obj, lv2_event_flag>(
|
||||
id,
|
||||
[&, notify = lv2_obj::notify_all_t()](lv2_event_flag &flag) -> CellError {
|
||||
if (flag.pattern
|
||||
.fetch_op([&](u64 &pat) {
|
||||
return lv2_event_flag::check_pattern(pat, bitptn, mode,
|
||||
&ppu.gpr[6]);
|
||||
})
|
||||
.second) {
|
||||
// TODO: is it possible to return EPERM in this case?
|
||||
return {};
|
||||
}
|
||||
|
||||
lv2_obj::prepare_for_sleep(ppu);
|
||||
|
||||
std::lock_guard lock(flag.mutex);
|
||||
|
||||
if (flag.pattern
|
||||
.fetch_op([&](u64 &pat) {
|
||||
return lv2_event_flag::check_pattern(pat, bitptn, mode,
|
||||
&ppu.gpr[6]);
|
||||
})
|
||||
.second) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (flag.type == SYS_SYNC_WAITER_SINGLE && flag.sq) {
|
||||
return CELL_EPERM;
|
||||
}
|
||||
|
||||
flag.sleep(ppu, timeout);
|
||||
lv2_obj::emplace(flag.sq, &ppu);
|
||||
return CELL_EBUSY;
|
||||
});
|
||||
|
||||
if (!flag) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (flag.ret) {
|
||||
if (flag.ret != CELL_EBUSY) {
|
||||
return flag.ret;
|
||||
}
|
||||
} else {
|
||||
store.val = ppu.gpr[6];
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
while (auto state = +ppu.state) {
|
||||
if (state & cpu_flag::signal &&
|
||||
ppu.state.test_and_reset(cpu_flag::signal)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_stopped(state)) {
|
||||
std::lock_guard lock(flag->mutex);
|
||||
|
||||
for (auto cpu = +flag->sq; cpu; cpu = cpu->next_cpu) {
|
||||
if (cpu == &ppu) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (usz i = 0; cpu_flag::signal - ppu.state && i < 50; i++) {
|
||||
rx::busy_wait(500);
|
||||
}
|
||||
|
||||
if (ppu.state & cpu_flag::signal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (lv2_obj::wait_timeout(timeout, &ppu)) {
|
||||
// Wait for rescheduling
|
||||
if (ppu.check_state()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
if (!atomic_storage<ppu_thread *>::load(flag->sq)) {
|
||||
// Waiters queue is empty, so the thread must have been signaled
|
||||
flag->mutex.lock_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
std::lock_guard lock(flag->mutex);
|
||||
|
||||
if (!flag->unqueue(flag->sq, &ppu)) {
|
||||
break;
|
||||
}
|
||||
|
||||
ppu.gpr[3] = CELL_ETIMEDOUT;
|
||||
ppu.gpr[6] = flag->pattern;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
}
|
||||
|
||||
store.val = ppu.gpr[6];
|
||||
return not_an_error(ppu.gpr[3]);
|
||||
}
|
||||
|
||||
error_code sys_event_flag_trywait(ppu_thread &ppu, u32 id, u64 bitptn, u32 mode,
|
||||
vm::ptr<u64> result) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event_flag.trace(
|
||||
"sys_event_flag_trywait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x)",
|
||||
id, bitptn, mode, result);
|
||||
|
||||
// Always set result
|
||||
struct store_result {
|
||||
vm::ptr<u64> ptr;
|
||||
u64 val = 0;
|
||||
|
||||
~store_result() noexcept {
|
||||
if (ptr) {
|
||||
cpu_thread::get_current()->check_state();
|
||||
*ptr = val;
|
||||
}
|
||||
}
|
||||
} store{result};
|
||||
|
||||
if (!lv2_event_flag::check_mode(mode)) {
|
||||
sys_event_flag.error("sys_event_flag_trywait(): unknown mode (0x%x)", mode);
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
u64 pattern{};
|
||||
|
||||
const auto flag =
|
||||
idm::check<lv2_obj, lv2_event_flag>(id, [&](lv2_event_flag &flag) {
|
||||
return flag.pattern
|
||||
.fetch_op([&](u64 &pat) {
|
||||
return lv2_event_flag::check_pattern(pat, bitptn, mode, &pattern);
|
||||
})
|
||||
.second;
|
||||
});
|
||||
|
||||
if (!flag) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (!flag.ret) {
|
||||
return not_an_error(CELL_EBUSY);
|
||||
}
|
||||
|
||||
store.val = pattern;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_flag_set(cpu_thread &cpu, u32 id, u64 bitptn) {
|
||||
cpu.state += cpu_flag::wait;
|
||||
|
||||
// Warning: may be called from SPU thread.
|
||||
sys_event_flag.trace("sys_event_flag_set(id=0x%x, bitptn=0x%llx)", id,
|
||||
bitptn);
|
||||
|
||||
const auto flag = idm::get_unlocked<lv2_obj, lv2_event_flag>(id);
|
||||
|
||||
if (!flag) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if ((flag->pattern & bitptn) == bitptn) {
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (lv2_obj::notify_all_t notify; true) {
|
||||
std::lock_guard lock(flag->mutex);
|
||||
|
||||
for (auto ppu = +flag->sq; ppu; ppu = ppu->next_cpu) {
|
||||
if (ppu->state & cpu_flag::again) {
|
||||
cpu.state += cpu_flag::again;
|
||||
|
||||
// Fake error for abort
|
||||
return not_an_error(CELL_EAGAIN);
|
||||
}
|
||||
}
|
||||
|
||||
u32 count = 0;
|
||||
|
||||
// Process all waiters in single atomic op
|
||||
for (u64 pattern = flag->pattern, to_write = pattern, dependant_mask = 0;;
|
||||
to_write = pattern, dependant_mask = 0) {
|
||||
count = 0;
|
||||
to_write |= bitptn;
|
||||
dependant_mask = 0;
|
||||
|
||||
for (auto ppu = +flag->sq; ppu; ppu = ppu->next_cpu) {
|
||||
ppu->gpr[7] = 0;
|
||||
}
|
||||
|
||||
auto first = +flag->sq;
|
||||
|
||||
auto get_next = [&]() -> ppu_thread * {
|
||||
s32 prio = smax;
|
||||
ppu_thread *it{};
|
||||
|
||||
for (auto ppu = first; ppu; ppu = ppu->next_cpu) {
|
||||
if (!ppu->gpr[7] && (flag->protocol != SYS_SYNC_PRIORITY ||
|
||||
ppu->prio.load().prio <= prio)) {
|
||||
it = ppu;
|
||||
prio = ppu->prio.load().prio;
|
||||
}
|
||||
}
|
||||
|
||||
if (it) {
|
||||
// Mark it so it won't reappear
|
||||
it->gpr[7] = 1;
|
||||
}
|
||||
|
||||
return it;
|
||||
};
|
||||
|
||||
while (auto it = get_next()) {
|
||||
auto &ppu = *it;
|
||||
|
||||
const u64 pattern = ppu.gpr[4];
|
||||
const u64 mode = ppu.gpr[5];
|
||||
|
||||
// If it's OR mode, set bits must have waken up the thread therefore no
|
||||
// dependency on old value
|
||||
const u64 dependant_mask_or =
|
||||
((mode & 0xf) == SYS_EVENT_FLAG_WAIT_OR ||
|
||||
(bitptn & pattern & to_write) == pattern
|
||||
? 0
|
||||
: pattern);
|
||||
|
||||
if (lv2_event_flag::check_pattern(to_write, pattern, mode,
|
||||
&ppu.gpr[6])) {
|
||||
dependant_mask |= dependant_mask_or;
|
||||
ppu.gpr[3] = CELL_OK;
|
||||
count++;
|
||||
|
||||
if (!to_write) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ppu.gpr[3] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
dependant_mask &= ~bitptn;
|
||||
|
||||
auto [new_val, ok] = flag->pattern.fetch_op([&](u64 &x) {
|
||||
if ((x ^ pattern) & dependant_mask) {
|
||||
return false;
|
||||
}
|
||||
|
||||
x |= bitptn;
|
||||
|
||||
// Clear the bit-wise difference
|
||||
x &= ~((pattern | bitptn) & ~to_write);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (ok) {
|
||||
break;
|
||||
}
|
||||
|
||||
pattern = new_val;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// Remove waiters
|
||||
for (auto next_cpu = &flag->sq; *next_cpu;) {
|
||||
auto &ppu = **next_cpu;
|
||||
|
||||
if (ppu.gpr[3] == CELL_OK) {
|
||||
atomic_storage<ppu_thread *>::release(*next_cpu, ppu.next_cpu);
|
||||
ppu.next_cpu = nullptr;
|
||||
flag->append(&ppu);
|
||||
continue;
|
||||
}
|
||||
|
||||
next_cpu = &ppu.next_cpu;
|
||||
};
|
||||
|
||||
lv2_obj::awake_all();
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_flag_clear(ppu_thread &ppu, u32 id, u64 bitptn) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event_flag.trace("sys_event_flag_clear(id=0x%x, bitptn=0x%llx)", id,
|
||||
bitptn);
|
||||
|
||||
const auto flag = idm::check<lv2_obj, lv2_event_flag>(
|
||||
id, [&](lv2_event_flag &flag) { flag.pattern &= bitptn; });
|
||||
|
||||
if (!flag) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_flag_cancel(ppu_thread &ppu, u32 id, vm::ptr<u32> num) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event_flag.trace("sys_event_flag_cancel(id=0x%x, num=*0x%x)", id, num);
|
||||
|
||||
if (num)
|
||||
*num = 0;
|
||||
|
||||
const auto flag = idm::get_unlocked<lv2_obj, lv2_event_flag>(id);
|
||||
|
||||
if (!flag) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
u32 value = 0;
|
||||
{
|
||||
lv2_obj::notify_all_t notify;
|
||||
|
||||
std::lock_guard lock(flag->mutex);
|
||||
|
||||
for (auto cpu = +flag->sq; cpu; cpu = cpu->next_cpu) {
|
||||
if (cpu->state & cpu_flag::again) {
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Get current pattern
|
||||
const u64 pattern = flag->pattern;
|
||||
|
||||
// Signal all threads to return CELL_ECANCELED (protocol does not matter)
|
||||
while (auto ppu = flag->schedule<ppu_thread>(flag->sq, SYS_SYNC_FIFO)) {
|
||||
ppu->gpr[3] = CELL_ECANCELED;
|
||||
ppu->gpr[6] = pattern;
|
||||
|
||||
value++;
|
||||
flag->append(ppu);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
lv2_obj::awake_all();
|
||||
}
|
||||
}
|
||||
|
||||
static_cast<void>(ppu.test_stopped());
|
||||
|
||||
if (num)
|
||||
*num = value;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_event_flag_get(ppu_thread &ppu, u32 id, vm::ptr<u64> flags) {
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_event_flag.trace("sys_event_flag_get(id=0x%x, flags=*0x%x)", id, flags);
|
||||
|
||||
const auto flag = idm::check<lv2_obj, lv2_event_flag>(
|
||||
id, [](lv2_event_flag &flag) { return +flag.pattern; });
|
||||
|
||||
ppu.check_state();
|
||||
|
||||
if (!flag) {
|
||||
if (flags)
|
||||
*flags = 0;
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (!flags) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
*flags = flag.ret;
|
||||
return CELL_OK;
|
||||
}
|
||||
3248
kernel/cellos/src/sys_fs.cpp
Normal file
3248
kernel/cellos/src/sys_fs.cpp
Normal file
File diff suppressed because it is too large
Load diff
267
kernel/cellos/src/sys_game.cpp
Normal file
267
kernel/cellos/src/sys_game.cpp
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "cellos/sys_process.h"
|
||||
#include "util/StrUtil.h"
|
||||
#include "util/Thread.h"
|
||||
#include "util/sysinfo.hpp"
|
||||
#include "util/v128.hpp"
|
||||
|
||||
#include "Emu/Cell/timers.hpp"
|
||||
#include "sys_game.h"
|
||||
|
||||
LOG_CHANNEL(sys_game);
|
||||
|
||||
struct system_sw_version {
|
||||
system_sw_version() {
|
||||
f64 version_f = 0;
|
||||
if (!try_to_float(&version_f, utils::get_firmware_version(), 0.0f,
|
||||
99.9999f))
|
||||
sys_game.error("Error parsing firmware version");
|
||||
version = static_cast<usz>(version_f * 10000);
|
||||
}
|
||||
|
||||
system_sw_version(const system_sw_version &) = delete;
|
||||
|
||||
system_sw_version &operator=(const system_sw_version &) = delete;
|
||||
|
||||
~system_sw_version() = default;
|
||||
|
||||
atomic_t<u64> version;
|
||||
};
|
||||
|
||||
struct board_storage {
|
||||
public:
|
||||
bool read(u8 *buffer) {
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
const auto data = storage.load();
|
||||
memcpy(buffer, &data, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write(u8 *buffer) {
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
storage.store(read_from_ptr<be_t<v128>>(buffer));
|
||||
written = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
board_storage() {
|
||||
memset(&storage.raw(), -1, size);
|
||||
if (fs::file file; file.open(file_path, fs::read))
|
||||
file.read(&storage.raw(), std::min(file.size(), size));
|
||||
}
|
||||
|
||||
board_storage(const board_storage &) = delete;
|
||||
|
||||
board_storage &operator=(const board_storage &) = delete;
|
||||
|
||||
~board_storage() {
|
||||
if (written) {
|
||||
if (fs::file file;
|
||||
file.open(file_path, fs::create + fs::write + fs::lock)) {
|
||||
file.write(&storage.raw(), size);
|
||||
file.trunc(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
atomic_be_t<v128> storage;
|
||||
bool written = false;
|
||||
const std::string file_path =
|
||||
rpcs3::utils::get_hdd1_dir() + "/caches/board_storage.bin";
|
||||
static constexpr u64 size = sizeof(v128);
|
||||
};
|
||||
|
||||
struct watchdog_t {
|
||||
struct alignas(8) control_t {
|
||||
bool needs_restart = false;
|
||||
bool active = false;
|
||||
char pad[sizeof(u32) - sizeof(bool) * 2]{};
|
||||
u32 timeout = 0;
|
||||
};
|
||||
|
||||
atomic_t<control_t> control;
|
||||
|
||||
void operator()() {
|
||||
u64 start_time = get_system_time();
|
||||
u64 old_time = start_time;
|
||||
u64 current_time = old_time;
|
||||
|
||||
constexpr u64 sleep_time = 50'000;
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting) {
|
||||
if (Emu.GetStatus(false) == system_state::paused) {
|
||||
start_time += current_time - old_time;
|
||||
old_time = current_time;
|
||||
thread_ctrl::wait_for(sleep_time);
|
||||
current_time = get_system_time();
|
||||
continue;
|
||||
}
|
||||
|
||||
old_time = std::exchange(current_time, get_system_time());
|
||||
|
||||
const auto old = control
|
||||
.fetch_op([&](control_t &data) {
|
||||
if (data.needs_restart) {
|
||||
data.needs_restart = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
.first;
|
||||
|
||||
if (old.active && old.needs_restart) {
|
||||
start_time = current_time;
|
||||
old_time = current_time;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (old.active && current_time - start_time >= old.timeout) {
|
||||
sys_game.success("Watchdog timeout! Restarting the game...");
|
||||
|
||||
Emu.CallFromMainThread([]() { Emu.Restart(false); });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr auto thread_name = "LV2 Watchdog Thread"sv;
|
||||
};
|
||||
|
||||
void abort_lv2_watchdog() {
|
||||
if (auto thr = g_fxo->try_get<named_thread<watchdog_t>>()) {
|
||||
sys_game.notice("Aborting %s...", thr->thread_name);
|
||||
*thr = thread_state::aborting;
|
||||
}
|
||||
}
|
||||
|
||||
error_code _sys_game_watchdog_start(u32 timeout) {
|
||||
sys_game.trace("sys_game_watchdog_start(timeout=%d)", timeout);
|
||||
|
||||
// According to disassembly
|
||||
timeout *= 1'000'000;
|
||||
timeout &= -64;
|
||||
|
||||
if (!g_fxo->get<named_thread<watchdog_t>>()
|
||||
.control
|
||||
.fetch_op([&](watchdog_t::control_t &data) {
|
||||
if (data.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data.needs_restart = true;
|
||||
data.active = true;
|
||||
data.timeout = timeout;
|
||||
return true;
|
||||
})
|
||||
.second) {
|
||||
return CELL_EABORT;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code _sys_game_watchdog_stop() {
|
||||
sys_game.trace("sys_game_watchdog_stop()");
|
||||
|
||||
g_fxo->get<named_thread<watchdog_t>>().control.fetch_op(
|
||||
[](watchdog_t::control_t &data) {
|
||||
if (!data.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data.active = false;
|
||||
return true;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code _sys_game_watchdog_clear() {
|
||||
sys_game.trace("sys_game_watchdog_clear()");
|
||||
|
||||
g_fxo->get<named_thread<watchdog_t>>().control.fetch_op(
|
||||
[](watchdog_t::control_t &data) {
|
||||
if (!data.active || data.needs_restart) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data.needs_restart = true;
|
||||
return true;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code _sys_game_set_system_sw_version(u64 version) {
|
||||
sys_game.trace("sys_game_set_system_sw_version(version=%d)", version);
|
||||
|
||||
if (!g_ps3_process_info.has_root_perm())
|
||||
return CELL_ENOSYS;
|
||||
|
||||
g_fxo->get<system_sw_version>().version = version;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u64 _sys_game_get_system_sw_version() {
|
||||
sys_game.trace("sys_game_get_system_sw_version()");
|
||||
|
||||
return g_fxo->get<system_sw_version>().version;
|
||||
}
|
||||
|
||||
error_code _sys_game_board_storage_read(vm::ptr<u8> buffer,
|
||||
vm::ptr<u8> status) {
|
||||
sys_game.trace("sys_game_board_storage_read(buffer=*0x%x, status=*0x%x)",
|
||||
buffer, status);
|
||||
|
||||
if (!buffer || !status) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
*status = g_fxo->get<board_storage>().read(buffer.get_ptr()) ? 0x00 : 0xFF;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code _sys_game_board_storage_write(vm::ptr<u8> buffer,
|
||||
vm::ptr<u8> status) {
|
||||
sys_game.trace("sys_game_board_storage_write(buffer=*0x%x, status=*0x%x)",
|
||||
buffer, status);
|
||||
|
||||
if (!buffer || !status) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
*status = g_fxo->get<board_storage>().write(buffer.get_ptr()) ? 0x00 : 0xFF;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code _sys_game_get_rtc_status(vm::ptr<s32> status) {
|
||||
sys_game.trace("sys_game_get_rtc_status(status=*0x%x)", status);
|
||||
|
||||
if (!status) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
*status = 0;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
102
kernel/cellos/src/sys_gamepad.cpp
Normal file
102
kernel/cellos/src/sys_gamepad.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "sys_gamepad.h"
|
||||
|
||||
LOG_CHANNEL(sys_gamepad);
|
||||
|
||||
u32 sys_gamepad_ycon_initalize(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo("sys_gamepad_ycon_initalize(in=%d, out=%d) -> CELL_OK", in,
|
||||
out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_finalize(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo("sys_gamepad_ycon_finalize(in=%d, out=%d) -> CELL_OK", in,
|
||||
out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_has_input_ownership(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo(
|
||||
"sys_gamepad_ycon_has_input_ownership(in=%d, out=%d) -> CELL_OK", in,
|
||||
out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_enumerate_device(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo(
|
||||
"sys_gamepad_ycon_enumerate_device(in=%d, out=%d) -> CELL_OK", in, out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_get_device_info(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo("sys_gamepad_ycon_get_device_info(in=%d, out=%d) -> CELL_OK",
|
||||
in, out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_read_raw_report(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo("sys_gamepad_ycon_read_raw_report(in=%d, out=%d) -> CELL_OK",
|
||||
in, out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_write_raw_report(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo(
|
||||
"sys_gamepad_ycon_write_raw_report(in=%d, out=%d) -> CELL_OK", in, out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_get_feature(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo("sys_gamepad_ycon_get_feature(in=%d, out=%d) -> CELL_OK", in,
|
||||
out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_set_feature(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo("sys_gamepad_ycon_set_feature(in=%d, out=%d) -> CELL_OK", in,
|
||||
out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u32 sys_gamepad_ycon_is_gem(vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
sys_gamepad.todo("sys_gamepad_ycon_is_gem(in=%d, out=%d) -> CELL_OK", in,
|
||||
out);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// syscall(621,packet_id,u8 *in,u8 *out)
|
||||
// Talk:LV2_Functions_and_Syscalls#Syscall_621_.280x26D.29 gamepad_if usage
|
||||
u32 sys_gamepad_ycon_if(u8 packet_id, vm::ptr<u8> in, vm::ptr<u8> out) {
|
||||
|
||||
switch (packet_id) {
|
||||
case 0:
|
||||
return sys_gamepad_ycon_initalize(in, out);
|
||||
case 1:
|
||||
return sys_gamepad_ycon_finalize(in, out);
|
||||
case 2:
|
||||
return sys_gamepad_ycon_has_input_ownership(in, out);
|
||||
case 3:
|
||||
return sys_gamepad_ycon_enumerate_device(in, out);
|
||||
case 4:
|
||||
return sys_gamepad_ycon_get_device_info(in, out);
|
||||
case 5:
|
||||
return sys_gamepad_ycon_read_raw_report(in, out);
|
||||
case 6:
|
||||
return sys_gamepad_ycon_write_raw_report(in, out);
|
||||
case 7:
|
||||
return sys_gamepad_ycon_get_feature(in, out);
|
||||
case 8:
|
||||
return sys_gamepad_ycon_set_feature(in, out);
|
||||
case 9:
|
||||
return sys_gamepad_ycon_is_gem(in, out);
|
||||
default:
|
||||
sys_gamepad.error(
|
||||
"sys_gamepad_ycon_if(packet_id=*%d, in=%d, out=%d), unknown packet id",
|
||||
packet_id, in, out);
|
||||
break;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
41
kernel/cellos/src/sys_gpio.cpp
Normal file
41
kernel/cellos/src/sys_gpio.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "sys_gpio.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
|
||||
LOG_CHANNEL(sys_gpio);
|
||||
|
||||
error_code sys_gpio_get(u64 device_id, vm::ptr<u64> value) {
|
||||
sys_gpio.trace("sys_gpio_get(device_id=0x%llx, value=*0x%x)", device_id,
|
||||
value);
|
||||
|
||||
if (device_id != SYS_GPIO_LED_DEVICE_ID &&
|
||||
device_id != SYS_GPIO_DIP_SWITCH_DEVICE_ID) {
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Retail consoles dont have LEDs or DIPs switches, hence always sets 0 in
|
||||
// paramenter
|
||||
if (!value.try_write(0)) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_gpio_set(u64 device_id, u64 mask, u64 value) {
|
||||
sys_gpio.trace("sys_gpio_set(device_id=0x%llx, mask=0x%llx, value=0x%llx)",
|
||||
device_id, mask, value);
|
||||
|
||||
// Retail consoles dont have LEDs or DIPs switches, hence the syscall can't
|
||||
// modify devices's value
|
||||
switch (device_id) {
|
||||
case SYS_GPIO_LED_DEVICE_ID:
|
||||
return CELL_OK;
|
||||
case SYS_GPIO_DIP_SWITCH_DEVICE_ID:
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
193
kernel/cellos/src/sys_hid.cpp
Normal file
193
kernel/cellos/src/sys_hid.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "sys_hid.h"
|
||||
|
||||
#include "Emu/Memory/vm_var.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "rpcsx/fw/ps3/cellPad.h"
|
||||
|
||||
#include "sys_process.h"
|
||||
|
||||
LOG_CHANNEL(sys_hid);
|
||||
|
||||
error_code sys_hid_manager_open(ppu_thread &ppu, u64 device_type, u64 port_no,
|
||||
vm::ptr<u32> handle) {
|
||||
sys_hid.todo("sys_hid_manager_open(device_type=0x%llx, port_no=0x%llx, "
|
||||
"handle=*0x%llx)",
|
||||
device_type, port_no, handle);
|
||||
|
||||
// device type == 1 = pad, 2 = kb, 3 = mouse
|
||||
if (device_type > 3) {
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
// 'handle' starts at 0x100 in realhw, and increments every time
|
||||
// sys_hid_manager_open is called however, sometimes the handle is reused when
|
||||
// opening sys_hid_manager again (even when the previous one hasn't been
|
||||
// closed yet) - maybe when processes/threads get killed/finish they also
|
||||
// release their handles?
|
||||
static u32 ctr = 0x100;
|
||||
*handle = ctr++;
|
||||
|
||||
if (device_type == 1) {
|
||||
cellPadInit(ppu, 7);
|
||||
cellPadSetPortSetting(::narrow<u32>(port_no) /* 0 */,
|
||||
CELL_PAD_SETTING_LDD | CELL_PAD_SETTING_PRESS_ON |
|
||||
CELL_PAD_SETTING_SENSOR_ON);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_hid_manager_ioctl(u32 hid_handle, u32 pkg_id, vm::ptr<void> buf,
|
||||
u64 buf_size) {
|
||||
sys_hid.todo("sys_hid_manager_ioctl(hid_handle=0x%x, pkg_id=0x%llx, "
|
||||
"buf=*0x%x, buf_size=0x%llx)",
|
||||
hid_handle, pkg_id, buf, buf_size);
|
||||
|
||||
// clang-format off
|
||||
// From realhw syscall dump when vsh boots
|
||||
// SC count | handle | pkg_id | *buf (in) | *buf (out) | size -> ret
|
||||
// ---------|--------|--------|---------------------------------------------------------------------------|---------------------------------------------------------------------------|------------
|
||||
// 28893 | 0x101 | 0x2 | 000000000000000000000000000000000000000000 | 054c02680102020000000000000008035000001c1f | 21 -> 0
|
||||
// 28894 | 0x101 | 0x3 | 00000000 | 00000000 | 4 -> 0
|
||||
// 28895 | 0x101 | 0x5 | 00000000 | 00000000 | 4 -> 0
|
||||
// 28896 | 0x101 | 0x68 | 01000000d0031cb020169e502006b7f80000000000606098000000000000000000000000d | 01000000d0031cb020169e502006b7f80000000000606098000000000000000000000000d | 64 -> 0
|
||||
// | | | 0031c90000000002006bac400000000d0031cb0000000002006b4d0 | 0031c90000000002006bac400000000d0031cb0000000002006b4d0 |
|
||||
// 28898 | 0x102 | 0x2 | 000000000000000000000000000000000000000000 | 054c02680102020000000000000008035000001c1f | 21 -> 0
|
||||
// 28901 | 0x100 | 0x64 | 00000001 | 00000001 | 4 -> 0xffffffff80010002 # x3::hidportassign
|
||||
// 2890 | 0x100 | 0x65 | 6b49d200 | 6b49d200 | 4 -> 0xffffffff80010002 # x3::hidportassign
|
||||
// 28903 | 0x100 | 0x66 | 00000001 | 00000001 | 4 -> 0 # x3::hidportassign
|
||||
// 28904 | 0x100 | 0x0 | 00000001000000ff000000ff000000ff000000ff000000010000000100000001000000010 | 00000001000000ff000000ff000000ff000000ff000000010000000100000001000000010 | 68 -> 0 # x3::hidportassign
|
||||
// | | | 000000000000000000000000000000000000001000000010000000100000001 | 000000000000000000000000000000000000001000000010000000100000001 |
|
||||
// 28907 | 0x101 | 0x3 | 00000001 | 00000001 | 4 -> 0
|
||||
// 28908 | 0x101 | 0x5 | 00000001 | 00000001 | 4 -> 0
|
||||
// 29404 | 0x100 | 0x4 | 00 | ee | 1 -> 0
|
||||
// *** repeats 30600, 31838, 33034, 34233, 35075 (35075 is x3::hidportassign) ***
|
||||
// 35076 | 0x100 | 0x0 | 00000001000000ff000000ff000000ff000000ff000000320000003200000032000000320 | 00000001000000ff000000ff000000ff000000ff000000320000003200000032000000320 | 68 -> 0
|
||||
// | | | 000003200000032000000320000003200002710000027100000271000002710 | 000003200000032000000320000003200002710000027100000271000002710 |
|
||||
// *** more 0x4 that have buf(in)=00 and buf(out)=ee ***
|
||||
// clang-format on
|
||||
|
||||
if (pkg_id == 2) {
|
||||
// Return what realhw seems to return
|
||||
// TODO: Figure out what this corresponds to
|
||||
auto info = vm::static_ptr_cast<sys_hid_info_2>(buf);
|
||||
info->vid = 0x054C;
|
||||
info->pid = 0x0268;
|
||||
|
||||
u8 realhw[17] = {0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x08, 0x03, 0x50, 0x00, 0x00, 0x1c, 0x1f};
|
||||
memcpy(info->unk, &realhw, 17);
|
||||
} else if (pkg_id == 5) {
|
||||
auto info = vm::static_ptr_cast<sys_hid_info_5>(buf);
|
||||
info->vid = 0x054C;
|
||||
info->pid = 0x0268;
|
||||
}
|
||||
// pkg_id == 6 == setpressmode?
|
||||
else if (pkg_id == 0x68) {
|
||||
[[maybe_unused]] auto info = vm::static_ptr_cast<sys_hid_ioctl_68>(buf);
|
||||
// info->unk2 = 0;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_hid_manager_check_focus() {
|
||||
// spammy sys_hid.todo("sys_hid_manager_check_focus()");
|
||||
|
||||
return not_an_error(1);
|
||||
}
|
||||
|
||||
error_code sys_hid_manager_513(u64 a1, u64 a2, vm::ptr<void> buf,
|
||||
u64 buf_size) {
|
||||
sys_hid.todo("sys_hid_manager_513(%llx, %llx, buf=%llx, buf_size=%llx)", a1,
|
||||
a2, buf, buf_size);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_hid_manager_514(u32 pkg_id, vm::ptr<void> buf, u64 buf_size) {
|
||||
if (pkg_id == 0xE) {
|
||||
sys_hid.trace(
|
||||
"sys_hid_manager_514(pkg_id=0x%x, buf=*0x%x, buf_size=0x%llx)", pkg_id,
|
||||
buf, buf_size);
|
||||
} else {
|
||||
sys_hid.todo("sys_hid_manager_514(pkg_id=0x%x, buf=*0x%x, buf_size=0x%llx)",
|
||||
pkg_id, buf, buf_size);
|
||||
}
|
||||
|
||||
if (pkg_id == 0xE) {
|
||||
// buf holds device_type
|
||||
// auto device_type = vm::static_ptr_cast<u8>(buf);
|
||||
// spammy sys_hid.todo("device_type: 0x%x", device_type[0]);
|
||||
|
||||
// return 1 or 0? look like almost like another check_focus type check,
|
||||
// returning 0 looks to keep system focus
|
||||
} else if (pkg_id == 0xD) {
|
||||
auto inf = vm::static_ptr_cast<sys_hid_manager_514_pkg_d>(buf);
|
||||
// unk1 = (pad# << 24) | pad# | 0x100
|
||||
// return value doesn't seem to be used again
|
||||
sys_hid.todo("unk1: 0x%x, unk2:0x%x", inf->unk1, inf->unk2);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_hid_manager_is_process_permission_root(u32 pid) {
|
||||
sys_hid.todo("sys_hid_manager_is_process_permission_root(pid=0x%x)", pid);
|
||||
|
||||
return not_an_error(g_ps3_process_info.has_root_perm());
|
||||
}
|
||||
|
||||
error_code sys_hid_manager_add_hot_key_observer(u32 event_queue,
|
||||
vm::ptr<u32> unk) {
|
||||
sys_hid.todo(
|
||||
"sys_hid_manager_add_hot_key_observer(event_queue=0x%x, unk=*0x%x)",
|
||||
event_queue, unk);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_hid_manager_read(u32 handle, u32 pkg_id, vm::ptr<void> buf,
|
||||
u64 buf_size) {
|
||||
if (!buf) {
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
(pkg_id == 2 || pkg_id == 0x81 ? sys_hid.trace : sys_hid.todo)(
|
||||
"sys_hid_manager_read(handle=0x%x, pkg_id=0x%x, buf=*0x%x, "
|
||||
"buf_size=0x%llx)",
|
||||
handle, pkg_id, buf, buf_size);
|
||||
|
||||
if (pkg_id == 2) {
|
||||
// cellPadGetData
|
||||
// it returns just button array from 'CellPadData'
|
||||
// auto data = vm::static_ptr_cast<u16[64]>(buf);
|
||||
// todo: use handle and dont call cellpad here
|
||||
vm::var<CellPadData> tmpData;
|
||||
if ((cellPadGetData(0, +tmpData) == CELL_OK) && tmpData->len > 0) {
|
||||
u64 cpySize = std::min(static_cast<u64>(tmpData->len) * sizeof(u16),
|
||||
buf_size * sizeof(u16));
|
||||
memcpy(buf.get_ptr(), &tmpData->button, cpySize);
|
||||
return not_an_error(cpySize);
|
||||
}
|
||||
} else if (pkg_id == 0x81) {
|
||||
// cellPadGetDataExtra?
|
||||
vm::var<CellPadData> tmpData;
|
||||
if ((cellPadGetData(0, +tmpData) == CELL_OK) && tmpData->len > 0) {
|
||||
u64 cpySize = std::min(static_cast<u64>(tmpData->len) * sizeof(u16),
|
||||
buf_size * sizeof(u16));
|
||||
memcpy(buf.get_ptr(), &tmpData->button, cpySize);
|
||||
return not_an_error(cpySize / 2);
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue