commit 1fdadaaee929886c60de3bcfa5e93d88934de86d Author: DH Date: Fri Jun 23 03:28:14 2023 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6654b8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +compile_commands.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8f82a6e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "orbis-kernel"] + path = orbis-kernel + url = git@github.com:RPCSX/orbis-kernel.git diff --git a/3rdparty/crypto/CMakeLists.txt b/3rdparty/crypto/CMakeLists.txt new file mode 100644 index 0000000..af96e4d --- /dev/null +++ b/3rdparty/crypto/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.0) +project(libcrypto) +set(PROJECT_PATH crypto) + +set(INCLUDE + include/${PROJECT_PATH}/sha1.h +) + +set(SRC + src/sha1.c +) + +add_library(${PROJECT_NAME} STATIC ${INCLUDE} ${SRC}) +target_include_directories(${PROJECT_NAME} PUBLIC include) +target_include_directories(${PROJECT_NAME} PRIVATE include/${PROJECT_PATH}) +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") +set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/3rdparty/crypto/include/crypto/sha1.h b/3rdparty/crypto/include/crypto/sha1.h new file mode 100644 index 0000000..e313d10 --- /dev/null +++ b/3rdparty/crypto/include/crypto/sha1.h @@ -0,0 +1,164 @@ +#pragma once + +/** + * \file sha1.h + * + * \brief SHA-1 cryptographic hash function + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include + +#ifdef _MSC_VER +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define POLARSSL_ERR_SHA1_FILE_IO_ERROR \ + -0x0076 /**< Read/write error in file. */ + +// Regular implementation +// + +/** + * \brief SHA-1 context structure + */ +typedef struct { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +} sha1_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-1 context setup + * + * \param ctx context to be initialized + */ +void sha1_starts(sha1_context *ctx); + +/** + * \brief SHA-1 process buffer + * + * \param ctx SHA-1 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_update(sha1_context *ctx, const unsigned char *input, size_t ilen); + +/** + * \brief SHA-1 final digest + * + * \param ctx SHA-1 context + * \param output SHA-1 checksum result + */ +void sha1_finish(sha1_context *ctx, unsigned char output[20]); + +/* Internal use */ +void sha1_process(sha1_context *ctx, const unsigned char data[64]); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = SHA-1( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-1 checksum result + */ +void sha1(const unsigned char *input, size_t ilen, unsigned char output[20]); + +/** + * \brief Output = SHA-1( file contents ) + * + * \param path input file name + * \param output SHA-1 checksum result + * + * \return 0 if successful, or POLARSSL_ERR_SHA1_FILE_IO_ERROR + */ +int sha1_file(const char *path, unsigned char output[20]); + +/** + * \brief SHA-1 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void sha1_hmac_starts(sha1_context *ctx, const unsigned char *key, + size_t keylen); + +/** + * \brief SHA-1 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_hmac_update(sha1_context *ctx, const unsigned char *input, + size_t ilen); + +/** + * \brief SHA-1 HMAC final digest + * + * \param ctx HMAC context + * \param output SHA-1 HMAC checksum result + */ +void sha1_hmac_finish(sha1_context *ctx, unsigned char output[20]); + +/** + * \brief SHA-1 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void sha1_hmac_reset(sha1_context *ctx); + +/** + * \brief Output = HMAC-SHA-1( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-SHA-1 result + */ +void sha1_hmac(const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[20]); + +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/crypto/src/sha1.c b/3rdparty/crypto/src/sha1.c new file mode 100644 index 0000000..cf5796a --- /dev/null +++ b/3rdparty/crypto/src/sha1.c @@ -0,0 +1,377 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#include "crypto/sha1.h" + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n, b, i) \ + { \ + (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \ + ((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); \ + } +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n, b, i) \ + { \ + (b)[(i)] = (unsigned char)((n) >> 24); \ + (b)[(i) + 1] = (unsigned char)((n) >> 16); \ + (b)[(i) + 2] = (unsigned char)((n) >> 8); \ + (b)[(i) + 3] = (unsigned char)((n)); \ + } +#endif + +/* + * SHA-1 context setup + */ +void sha1_starts(sha1_context *ctx) { + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +void sha1_process(sha1_context *ctx, const unsigned char data[64]) { + uint32_t temp, W[16], A, B, C, D, E; + + GET_UINT32_BE(W[0], data, 0); + GET_UINT32_BE(W[1], data, 4); + GET_UINT32_BE(W[2], data, 8); + GET_UINT32_BE(W[3], data, 12); + GET_UINT32_BE(W[4], data, 16); + GET_UINT32_BE(W[5], data, 20); + GET_UINT32_BE(W[6], data, 24); + GET_UINT32_BE(W[7], data, 28); + GET_UINT32_BE(W[8], data, 32); + GET_UINT32_BE(W[9], data, 36); + GET_UINT32_BE(W[10], data, 40); + GET_UINT32_BE(W[11], data, 44); + GET_UINT32_BE(W[12], data, 48); + GET_UINT32_BE(W[13], data, 52); + GET_UINT32_BE(W[14], data, 56); + GET_UINT32_BE(W[15], data, 60); + +#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ + (temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ \ + W[t & 0x0F], \ + (W[t & 0x0F] = S(temp, 1))) + +#define P(a, b, c, d, e, x) \ + { \ + e += S(a, 5) + F(b, c, d) + K + x; \ + b = S(b, 30); \ + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x, y, z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P(A, B, C, D, E, W[0]); + P(E, A, B, C, D, W[1]); + P(D, E, A, B, C, W[2]); + P(C, D, E, A, B, W[3]); + P(B, C, D, E, A, W[4]); + P(A, B, C, D, E, W[5]); + P(E, A, B, C, D, W[6]); + P(D, E, A, B, C, W[7]); + P(C, D, E, A, B, W[8]); + P(B, C, D, E, A, W[9]); + P(A, B, C, D, E, W[10]); + P(E, A, B, C, D, W[11]); + P(D, E, A, B, C, W[12]); + P(C, D, E, A, B, W[13]); + P(B, C, D, E, A, W[14]); + P(A, B, C, D, E, W[15]); + P(E, A, B, C, D, R(16)); + P(D, E, A, B, C, R(17)); + P(C, D, E, A, B, R(18)); + P(B, C, D, E, A, R(19)); + +#undef K +#undef F + +#define F(x, y, z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P(A, B, C, D, E, R(20)); + P(E, A, B, C, D, R(21)); + P(D, E, A, B, C, R(22)); + P(C, D, E, A, B, R(23)); + P(B, C, D, E, A, R(24)); + P(A, B, C, D, E, R(25)); + P(E, A, B, C, D, R(26)); + P(D, E, A, B, C, R(27)); + P(C, D, E, A, B, R(28)); + P(B, C, D, E, A, R(29)); + P(A, B, C, D, E, R(30)); + P(E, A, B, C, D, R(31)); + P(D, E, A, B, C, R(32)); + P(C, D, E, A, B, R(33)); + P(B, C, D, E, A, R(34)); + P(A, B, C, D, E, R(35)); + P(E, A, B, C, D, R(36)); + P(D, E, A, B, C, R(37)); + P(C, D, E, A, B, R(38)); + P(B, C, D, E, A, R(39)); + +#undef K +#undef F + +#define F(x, y, z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P(A, B, C, D, E, R(40)); + P(E, A, B, C, D, R(41)); + P(D, E, A, B, C, R(42)); + P(C, D, E, A, B, R(43)); + P(B, C, D, E, A, R(44)); + P(A, B, C, D, E, R(45)); + P(E, A, B, C, D, R(46)); + P(D, E, A, B, C, R(47)); + P(C, D, E, A, B, R(48)); + P(B, C, D, E, A, R(49)); + P(A, B, C, D, E, R(50)); + P(E, A, B, C, D, R(51)); + P(D, E, A, B, C, R(52)); + P(C, D, E, A, B, R(53)); + P(B, C, D, E, A, R(54)); + P(A, B, C, D, E, R(55)); + P(E, A, B, C, D, R(56)); + P(D, E, A, B, C, R(57)); + P(C, D, E, A, B, R(58)); + P(B, C, D, E, A, R(59)); + +#undef K +#undef F + +#define F(x, y, z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P(A, B, C, D, E, R(60)); + P(E, A, B, C, D, R(61)); + P(D, E, A, B, C, R(62)); + P(C, D, E, A, B, R(63)); + P(B, C, D, E, A, R(64)); + P(A, B, C, D, E, R(65)); + P(E, A, B, C, D, R(66)); + P(D, E, A, B, C, R(67)); + P(C, D, E, A, B, R(68)); + P(B, C, D, E, A, R(69)); + P(A, B, C, D, E, R(70)); + P(E, A, B, C, D, R(71)); + P(D, E, A, B, C, R(72)); + P(C, D, E, A, B, R(73)); + P(B, C, D, E, A, R(74)); + P(A, B, C, D, E, R(75)); + P(E, A, B, C, D, R(76)); + P(D, E, A, B, C, R(77)); + P(C, D, E, A, B, R(78)); + P(B, C, D, E, A, R(79)); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* + * SHA-1 process buffer + */ +void sha1_update(sha1_context *ctx, const unsigned char *input, size_t ilen) { + size_t fill; + uint32_t left; + + // TODO:: Syphurith: Orz. It is said that size_t is an unsigned type.. + if (ilen <= 0) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t)ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if (ctx->total[0] < (uint32_t)ilen) + ctx->total[1]++; + + if (left && ilen >= fill) { + memcpy((void *)(ctx->buffer + left), input, fill); + sha1_process(ctx, ctx->buffer); + input += fill; + ilen -= fill; + left = 0; + } + + while (ilen >= 64) { + sha1_process(ctx, input); + input += 64; + ilen -= 64; + } + + if (ilen > 0) + memcpy((void *)(ctx->buffer + left), input, ilen); +} + +static const unsigned char sha1_padding[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* + * SHA-1 final digest + */ +void sha1_finish(sha1_context *ctx, unsigned char output[20]) { + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = (ctx->total[0] >> 29) | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + PUT_UINT32_BE(high, msglen, 0); + PUT_UINT32_BE(low, msglen, 4); + + last = ctx->total[0] & 0x3F; + padn = (last < 56) ? (56 - last) : (120 - last); + + sha1_update(ctx, sha1_padding, padn); + sha1_update(ctx, msglen, 8); + + PUT_UINT32_BE(ctx->state[0], output, 0); + PUT_UINT32_BE(ctx->state[1], output, 4); + PUT_UINT32_BE(ctx->state[2], output, 8); + PUT_UINT32_BE(ctx->state[3], output, 12); + PUT_UINT32_BE(ctx->state[4], output, 16); +} + +/* + * output = SHA-1( input buffer ) + */ +void sha1(const unsigned char *input, size_t ilen, unsigned char output[20]) { + sha1_context ctx; + + sha1_starts(&ctx); + sha1_update(&ctx, input, ilen); + sha1_finish(&ctx, output); + + memset(&ctx, 0, sizeof(sha1_context)); +} + +/* + * SHA-1 HMAC context setup + */ +void sha1_hmac_starts(sha1_context *ctx, const unsigned char *key, + size_t keylen) { + size_t i; + unsigned char sum[20]; + + if (keylen > 64) { + sha1(key, keylen, sum); + keylen = 20; + key = sum; + } + + memset(ctx->ipad, 0x36, 64); + memset(ctx->opad, 0x5C, 64); + + for (i = 0; i < keylen; i++) { + ctx->ipad[i] = (unsigned char)(ctx->ipad[i] ^ key[i]); + ctx->opad[i] = (unsigned char)(ctx->opad[i] ^ key[i]); + } + + sha1_starts(ctx); + sha1_update(ctx, ctx->ipad, 64); + + memset(sum, 0, sizeof(sum)); +} + +/* + * SHA-1 HMAC process buffer + */ +void sha1_hmac_update(sha1_context *ctx, const unsigned char *input, + size_t ilen) { + sha1_update(ctx, input, ilen); +} + +/* + * SHA-1 HMAC final digest + */ +void sha1_hmac_finish(sha1_context *ctx, unsigned char output[20]) { + unsigned char tmpbuf[20]; + + sha1_finish(ctx, tmpbuf); + sha1_starts(ctx); + sha1_update(ctx, ctx->opad, 64); + sha1_update(ctx, tmpbuf, 20); + sha1_finish(ctx, output); + + memset(tmpbuf, 0, sizeof(tmpbuf)); +} + +/* + * SHA1 HMAC context reset + */ +void sha1_hmac_reset(sha1_context *ctx) { + sha1_starts(ctx); + sha1_update(ctx, ctx->ipad, 64); +} + +/* + * output = HMAC-SHA-1( hmac key, input buffer ) + */ +void sha1_hmac(const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[20]) { + sha1_context ctx; + + sha1_hmac_starts(&ctx, key, keylen); + sha1_hmac_update(&ctx, input, ilen); + sha1_hmac_finish(&ctx, output); + + memset(&ctx, 0, sizeof(sha1_context)); +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0ef2285 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) +project(rpcsx) + +set(CMAKE_CXX_EXTENSIONS off) +set(CMAKE_CXX_STANDARD 23) + +add_subdirectory(3rdparty/crypto) +add_subdirectory(orbis-kernel) +add_subdirectory(rpcsx-os) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/orbis-kernel b/orbis-kernel new file mode 160000 index 0000000..05d35b7 --- /dev/null +++ b/orbis-kernel @@ -0,0 +1 @@ +Subproject commit 05d35b71483880246bc4c1a28f857e9046af7c36 diff --git a/rpcsx-os/CMakeLists.txt b/rpcsx-os/CMakeLists.txt new file mode 100644 index 0000000..7ee390d --- /dev/null +++ b/rpcsx-os/CMakeLists.txt @@ -0,0 +1,35 @@ +add_library(standalone-config INTERFACE) +target_include_directories(standalone-config INTERFACE orbis-kernel-config) +add_library(orbis::kernel::config ALIAS standalone-config) + +add_executable(rpcsx-os + iodev/dce.cpp + iodev/dipsw.cpp + iodev/dmem.cpp + iodev/gc.cpp + iodev/hid.cpp + iodev/hmd_3da.cpp + iodev/hmd_cmd.cpp + iodev/hmd_mmap.cpp + iodev/hmd_snsr.cpp + iodev/null.cpp + iodev/rng.cpp + iodev/stderr.cpp + iodev/stdin.cpp + iodev/stdout.cpp + iodev/zero.cpp + + main.cpp + vm.cpp + ops.cpp + linker.cpp + io-device.cpp + vfs.cpp +) +target_include_directories(rpcsx-os PUBLIC .) +target_link_libraries(rpcsx-os PUBLIC orbis::kernel libcrypto unwind unwind-x86_64) +target_link_options(rpcsx-os PUBLIC "LINKER:-Ttext-segment,0x0000010000000000") +target_compile_options(rpcsx-os PRIVATE "-march=native") + +set_target_properties(rpcsx-os PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +install(TARGETS rpcsx-os RUNTIME DESTINATION bin) diff --git a/rpcsx-os/align.hpp b/rpcsx-os/align.hpp new file mode 100644 index 0000000..9ec863a --- /dev/null +++ b/rpcsx-os/align.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace utils { +inline constexpr std::uint64_t alignUp(std::uint64_t value, + std::uint64_t alignment) { + return (value + (alignment - 1)) & ~(alignment - 1); +} +inline constexpr std::uint64_t alignDown(std::uint64_t value, + std::uint64_t alignment) { + return value & ~(alignment - 1); +} +} // namespace utils diff --git a/rpcsx-os/io-device.cpp b/rpcsx-os/io-device.cpp new file mode 100644 index 0000000..b0cdd73 --- /dev/null +++ b/rpcsx-os/io-device.cpp @@ -0,0 +1,131 @@ +#include "io-device.hpp" +#include +#include +#include + +std::int64_t io_device_instance_close(IoDeviceInstance *instance) { + return 0; +} + +void io_device_instance_init(IoDevice *device, IoDeviceInstance *instance) { + if (instance->device == nullptr) { + instance->device = device; + } + + if (instance->close == nullptr) { + instance->close = io_device_instance_close; + } +} + +struct HostIoDevice : IoDevice { + std::string hostPath; +}; + +struct HostIoDeviceInstance : IoDeviceInstance { + int hostFd; +}; + +static std::int64_t host_io_device_instance_read(IoDeviceInstance *instance, + void *data, + std::uint64_t size) { + auto hostIoInstance = static_cast(instance); + return ::read(hostIoInstance->hostFd, data, size); // TODO: convert errno +} + +static std::int64_t host_io_device_instance_write(IoDeviceInstance *instance, + const void *data, + std::uint64_t size) { + auto hostIoInstance = static_cast(instance); + return ::write(hostIoInstance->hostFd, data, size); // TODO: convert errno +} + +static std::int64_t host_io_device_instance_lseek(IoDeviceInstance *instance, + std::uint64_t offset, + std::uint32_t whence) { + auto hostIoInstance = static_cast(instance); + + return ::lseek(hostIoInstance->hostFd, offset, whence); // TODO: convert errno +} + +static std::int64_t host_io_device_instance_close(IoDeviceInstance *instance) { + auto hostIoInstance = static_cast(instance); + ::close(hostIoInstance->hostFd); + return io_device_instance_close(instance); +} + +static std::int32_t host_io_open(IoDevice *device, orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto hostDevice = static_cast(device); + auto realPath = hostDevice->hostPath + "/" + path; + + int realFlags = flags & O_ACCMODE; + flags &= ~O_ACCMODE; + + if ((flags & kOpenFlagAppend) != 0) { + realFlags |= O_APPEND; + flags &= ~kOpenFlagAppend; + } + + if ((flags & kOpenFlagNonBlock) != 0) { + realFlags |= O_NONBLOCK; + flags &= ~kOpenFlagNonBlock; + } + + if ((flags & kOpenFlagFsync) != 0) { + realFlags |= O_FSYNC; + flags &= ~kOpenFlagFsync; + } + + if ((flags & kOpenFlagAsync) != 0) { + realFlags |= O_ASYNC; + flags &= ~kOpenFlagAsync; + } + + if ((flags & kOpenFlagTrunc) != 0) { + realFlags |= O_TRUNC; + flags &= ~kOpenFlagTrunc; + } + + if ((flags & kOpenFlagCreat) != 0) { + realFlags |= O_CREAT; + flags &= ~kOpenFlagCreat; + } + + if ((flags & kOpenFlagExcl) != 0) { + realFlags |= O_EXCL; + flags &= ~kOpenFlagExcl; + } + + if (flags != 0) { + std::fprintf(stderr, "host_io_open: ***ERROR*** Unhandled open flags %x\n", flags); + } + + int hostFd = ::open(realPath.c_str(), realFlags, 0777); + + if (hostFd < 0) { + std::fprintf(stderr, "host_io_open: '%s' not found.\n", realPath.c_str()); + return 1; // TODO: convert errno + } + + auto newInstance = new HostIoDeviceInstance(); + + newInstance->hostFd = hostFd; + + newInstance->read = host_io_device_instance_read; + newInstance->write = host_io_device_instance_write; + newInstance->lseek = host_io_device_instance_lseek; + newInstance->close = host_io_device_instance_close; + + io_device_instance_init(device, newInstance); + + *instance = newInstance; + return 0; +} + +IoDevice *createHostIoDevice(const char *hostPath) { + auto result = new HostIoDevice(); + result->open = host_io_open; + result->hostPath = hostPath; + return result; +} diff --git a/rpcsx-os/io-device.hpp b/rpcsx-os/io-device.hpp new file mode 100644 index 0000000..ef1ec2d --- /dev/null +++ b/rpcsx-os/io-device.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "orbis/utils/Rc.hpp" +#include + +struct IoDevice; + +enum OpenFlags { + kOpenFlagReadOnly = 0x0, + kOpenFlagWriteOnly = 0x1, + kOpenFlagReadWrite = 0x2, + kOpenFlagNonBlock = 0x4, + kOpenFlagAppend = 0x8, + kOpenFlagShLock = 0x10, + kOpenFlagExLock = 0x20, + kOpenFlagAsync = 0x40, + kOpenFlagFsync = 0x80, + kOpenFlagCreat = 0x200, + kOpenFlagTrunc = 0x400, + kOpenFlagExcl = 0x800, + kOpenFlagDSync = 0x1000, + kOpenFlagDirect = 0x10000, +}; + +struct IoDeviceInstance : orbis::RcBase { + orbis::Ref device; + + std::int64_t (*ioctl)(IoDeviceInstance *instance, std::uint64_t request, + void *argp) = nullptr; + + std::int64_t (*write)(IoDeviceInstance *instance, const void *data, + std::uint64_t size) = nullptr; + std::int64_t (*read)(IoDeviceInstance *instance, void *data, + std::uint64_t size) = nullptr; + std::int64_t (*close)(IoDeviceInstance *instance) = nullptr; + std::int64_t (*lseek)(IoDeviceInstance *instance, std::uint64_t offset, + std::uint32_t whence) = nullptr; + + void *(*mmap)(IoDeviceInstance *instance, void *address, std::uint64_t size, + std::int32_t prot, std::int32_t flags, + std::int64_t offset) = nullptr; + void *(*munmap)(IoDeviceInstance *instance, void *address, + std::uint64_t size) = nullptr; +}; + +struct IoDevice : orbis::RcBase { + std::int32_t (*open)(IoDevice *device, orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) = nullptr; +}; + + +std::int64_t io_device_instance_close(IoDeviceInstance *instance); +void io_device_instance_init(IoDevice *device, IoDeviceInstance *instance); + +IoDevice *createHostIoDevice(const char *hostPath); diff --git a/rpcsx-os/io-devices.hpp b/rpcsx-os/io-devices.hpp new file mode 100644 index 0000000..3109c2b --- /dev/null +++ b/rpcsx-os/io-devices.hpp @@ -0,0 +1,19 @@ +#pragma once + +struct IoDevice; + +IoDevice *createDceCharacterDevice(); +IoDevice *createDipswCharacterDevice(); +IoDevice *createDmemCharacterDevice(int index); +IoDevice *createGcCharacterDevice(); +IoDevice *createHidCharacterDevice(); +IoDevice *createHmd3daCharacterDevice(); +IoDevice *createHmdCmdCharacterDevice(); +IoDevice *createHmdMmapCharacterDevice(); +IoDevice *createHmdSnsrCharacterDevice(); +IoDevice *createNullCharacterDevice(); +IoDevice *createStderrCharacterDevice(); +IoDevice *createStdinCharacterDevice(); +IoDevice *createStdoutCharacterDevice(); +IoDevice *createZeroCharacterDevice(); +IoDevice *createRngCharacterDevice(); diff --git a/rpcsx-os/iodev/dce.cpp b/rpcsx-os/iodev/dce.cpp new file mode 100644 index 0000000..4726330 --- /dev/null +++ b/rpcsx-os/iodev/dce.cpp @@ -0,0 +1,291 @@ +#include "io-device.hpp" +#include +#include +#include +#include +#include "vm.hpp" + +struct VideoOutBuffer { + std::uint32_t pixelFormat; + std::uint32_t tilingMode; + std::uint32_t pitch; + std::uint32_t width; + std::uint32_t height; +}; + +struct DceDevice : public IoDevice {}; + +// template +// inline bool +// atomic_compare_exchange_weak(volatile T *ptr, T *expected, T desired, +// int successMemOrder = __ATOMIC_SEQ_CST, +// int failureMemOrder = __ATOMIC_SEQ_CST) { +// return __atomic_compare_exchange_n(ptr, expected, desired, true, +// __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +// } + +struct DceInstance : public IoDeviceInstance { + VideoOutBuffer bufferAttributes{}; + + // std::uint64_t flipStatusOffset = + // mem::allocateInternal(sizeof(liverpool::bridge::FlipStatus), + // alignof(liverpool::bridge::FlipStatus)); + // liverpool::bridge::FlipStatus *flipStatus = new ( + // mem::mapInternal(flipStatusOffset, sizeof(liverpool::bridge::FlipStatus))) + // liverpool::bridge::FlipStatus(); + + DceInstance() { + // *flipStatus = {}; + // orbis::bridge.sendSetFlipStatus(flipStatusOffset); + } + void registerBuffer(int index, std::uint64_t address) { + // orbis::bridge.sendSetBuffer(index, address, bufferAttributes.width, + // bufferAttributes.height, bufferAttributes.pitch, + // bufferAttributes.pixelFormat, + // bufferAttributes.tilingMode); + } + + void flip(std::uint32_t bufferIndex, std::uint64_t flipMode, + std::uint64_t flipArg) { + + // orbis::bridge.sendFlip(bufferIndex, flipArg); + // orbis::bridge.wait(); + } + + // liverpool::bridge::FlipStatus getFlipStatus() { + // int expected = 0; + // while (!atomic_compare_exchange_weak(&flipStatus->locked, &expected, 1)) { + // expected = 0; + // } + + // liverpool::bridge::FlipStatus result = *flipStatus; + // flipStatus->locked = 0; + + // return result; + // } +}; + +struct RegisterBuffer { + std::uint64_t attributeIndex; + std::uint64_t index; + std::uint64_t address; + std::uint64_t unk; +}; + +struct RegisterBufferAttributeArgs { + std::uint64_t unk0; + std::uint8_t unk1; + std::uint8_t unk2_flag; + std::uint16_t unk3; // 0 + std::uint32_t pixelFormat; + std::uint32_t tilingMode; // 1 // tilingMode? + std::uint32_t pitch; + std::uint32_t width; + std::uint32_t height; + std::uint8_t unk4_zero; // 0 + std::uint8_t unk5_zero; // 0 + std::uint16_t unk6; + std::uint64_t unk7; // -1 + std::uint32_t unk8; +}; + +struct FlipRequestArgs { + std::uint64_t arg1; + std::int32_t displayBufferIndex; + std::uint64_t flipMode; // flip mode? + std::uint64_t flipArg; + std::uint32_t arg5; + std::uint32_t arg6; + std::uint32_t arg7; + std::uint32_t arg8; +}; + +struct FlipControlStatus { + std::uint64_t flipArg; + std::uint64_t unk0; + std::uint64_t count; + std::uint64_t processTime; + std::uint64_t tsc; + std::uint32_t currentBuffer; + std::uint32_t unkQueueNum; + std::uint32_t gcQueueNum; + std::uint32_t unk2QueueNum; + std::uint32_t submitTsc; + std::uint64_t unk1; +}; + +struct FlipControlArgs { + std::uint32_t id; + // std::uint32_t padding; + std::uint64_t arg2; + void *ptr; + std::uint64_t size; // 0x48 // size? +}; + +struct ResolutionStatus { + std::uint32_t width; + std::uint32_t heigth; + std::uint32_t x; + std::uint32_t y; +}; + +static std::int64_t dce_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + auto dceInstance = static_cast(instance); + static std::uint64_t *bufferInUsePtr = nullptr; + + if (request == 0xc0308203) { + // flip control + auto args = reinterpret_cast(argp); + + std::printf("dce: FlipControl(%d, %lx, %p, %lx)\n", args->id, args->arg2, + args->ptr, args->size); + + if (args->id == 6) { // set flip rate? + std::printf("dce: FlipControl(set flip rate, %lx, %p, %lx)\n", args->arg2, + args->ptr, args->size); + } else if (args->id == 10) { + if (args->size != sizeof(FlipControlStatus)) { + return 0; + } + + // auto currentStatus = dceInstance->getFlipStatus(); + + FlipControlStatus flipStatus{}; + // flipStatus.flipArg = currentStatus.arg; + // flipStatus.count = currentStatus.count; + flipStatus.processTime = 0; // TODO + flipStatus.tsc = 0; // TODO + // flipStatus.currentBuffer = currentStatus.currentBuffer; + flipStatus.unkQueueNum = 0; // TODO + flipStatus.gcQueueNum = 0; // TODO + flipStatus.unk2QueueNum = 0; // TODO + flipStatus.submitTsc = 0; // TODO + + std::memcpy(args->ptr, &flipStatus, sizeof(FlipControlStatus)); + } else if (args->id == 12) { + *(std::uint64_t *)args->ptr = 0; + } else if (args->id == 19) { + // get resolution status + auto status = (ResolutionStatus *)args->ptr; + status->width = 1920; + status->heigth = 1080; + status->x = 0; + status->y = 0; + } else if (args->id == 9) { + std::printf("dce: FlipControl allocate(%u, %lx, %p, %lx)\n", args->id, + args->arg2, args->ptr, args->size); + *(std::uint64_t *)args->ptr = 0; // dev offset + *(std::uint64_t *)args->size = 0x100000; // size + } else if (args->id == 31) { + bufferInUsePtr = (std::uint64_t *)args->size; + std::printf("flipStatusPtr = %p\n", bufferInUsePtr); + return 0; + } else if (args->id != 0 && args->id != 1) { // used during open/close + std::printf("dce: UNIMPLEMENTED FlipControl(%u, %lx, %p, %lx)\n", + args->id, args->arg2, args->ptr, args->size); + + std::fflush(stdout); + __builtin_trap(); + } + return 0; + } + + if (request == 0xc0308206) { + auto args = reinterpret_cast(argp); + std::fprintf(stderr, "dce: RegisterBuffer(%lx, %lx, %lx, %lx)\n", + args->attributeIndex, args->index, args->address, args->unk); + + dceInstance->registerBuffer(args->index, args->address); + return 0; + } + + if (request == 0xc0308207) { // SCE_SYS_DCE_IOCTL_REGISTER_BUFFER_ATTRIBUTE + auto args = reinterpret_cast(argp); + + std::fprintf( + stderr, + "dce: RegisterBufferAttributes(unk0=%lx, unk1=%x, unk2_flag=%x, " + "unk3=%x, " + "pixelFormat=%x, tilingMode=%x, pitch=%u, width=%u, " + "height=%u, " + "unk4_zero=%x, unk5_zero=%x, unk6=%x, unk7_-1=%lx, unk8=%x)\n", + args->unk0, args->unk1, args->unk2_flag, args->unk3, args->pixelFormat, + args->tilingMode, args->pitch, args->width, args->height, + args->unk4_zero, args->unk5_zero, args->unk6, args->unk7, args->unk8); + + dceInstance->bufferAttributes.pixelFormat = args->pixelFormat; + dceInstance->bufferAttributes.tilingMode = args->tilingMode; + dceInstance->bufferAttributes.pitch = args->pitch; + dceInstance->bufferAttributes.width = args->width; + dceInstance->bufferAttributes.height = args->height; + return 0; + } + + if (request == 0xc0488204) { + // flip request + auto args = reinterpret_cast(argp); + + std::fprintf( + stderr, + "dce: FlipRequestArgs(%lx, displayBufferIndex = %x, flipMode = %lx, " + "flipArg = %lx, " + "%x, %x, %x, " + "%x)\n", + args->arg1, args->displayBufferIndex, args->flipMode, args->flipArg, + args->arg5, args->arg6, args->arg7, args->arg8); + + dceInstance->flip(args->displayBufferIndex, args->flipMode, args->flipArg); + + if (args->flipMode == 1 || args->arg7 == 0) { + // orbis::bridge.sendDoFlip(); + } + + + if (args->displayBufferIndex != -1) { + if (bufferInUsePtr) { + auto ptr = bufferInUsePtr + args->displayBufferIndex; + std::printf(" ========== fill status to %p\n", ptr); + *ptr = 0; + } + } + return 0; + } + + if (request == 0x80088209) { // deallocate? + auto arg = *reinterpret_cast(argp); + std::fprintf(stderr, "dce: 0x80088209(%lx)\n", arg); + return 0; + } + + std::fprintf(stderr, "***ERROR*** Unhandled dce ioctl %lx\n", request); + std::fflush(stdout); + __builtin_trap(); + return 0; +} + +static void *dce_instance_mmap(IoDeviceInstance *instance, void *address, + std::uint64_t size, std::int32_t prot, + std::int32_t flags, std::int64_t offset) { + std::fprintf(stderr, "dce mmap: address=%p, size=%lx, offset=%lx\n", address, + size, offset); + return rx::vm::map(address, size, prot, flags); +} + +static std::int32_t dce_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new DceInstance(); + newInstance->ioctl = dce_instance_ioctl; + newInstance->mmap = dce_instance_mmap; + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createDceCharacterDevice() { + auto *newDevice = new DceDevice(); + newDevice->open = dce_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/dipsw.cpp b/rpcsx-os/iodev/dipsw.cpp new file mode 100644 index 0000000..8cf05ff --- /dev/null +++ b/rpcsx-os/iodev/dipsw.cpp @@ -0,0 +1,64 @@ +#include "io-device.hpp" +#include + +struct DmemDevice : public IoDevice { +}; + +struct DmemInstance : public IoDeviceInstance { +}; + +static std::int64_t dipsw_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + if (request == 0x40048806) { // is connected? + std::fprintf(stderr, "dipsw ioctl 0x40048806(%p)\n", argp); + + *reinterpret_cast(argp) = 0; + return 0; + } + + // 0x40088808 + // 0x40088809 + + if (request == 0x40088808) { + std::fprintf(stderr, "dipsw ioctl 0x40088808(%p)\n", argp); + *reinterpret_cast(argp) = 1; + return 0; + } + + + // 0x8010880a + if (request == 0x8010880a) { // write data? used on initilization + struct Args { + std::uint64_t address; + std::uint64_t size; + }; + + auto args = reinterpret_cast(argp); + + std::fprintf(stderr, "dipsw ioctl 0x8010880a(0x%lx, 0x%lx)\n", args->address, args->size); + + return 0; + } + + std::fprintf(stderr, "***ERROR*** Unhandled dipsw ioctl %lx\n", request); + std::fflush(stdout); + //__builtin_trap(); + return 0; +} + +static std::int32_t dipsw_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new DmemInstance(); + newInstance->ioctl = dipsw_instance_ioctl; + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createDipswCharacterDevice() { + auto *newDevice = new DmemDevice(); + newDevice->open = dipsw_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/dmem.cpp b/rpcsx-os/iodev/dmem.cpp new file mode 100644 index 0000000..b16a98d --- /dev/null +++ b/rpcsx-os/iodev/dmem.cpp @@ -0,0 +1,124 @@ +#include "io-device.hpp" +#include +#include +#include "vm.hpp" + +struct DmemDevice : public IoDevice { + int index; + std::uint64_t nextOffset; + std::uint64_t memBeginAddress; +}; + +struct DmemInstance : public IoDeviceInstance {}; + +struct AllocateDirectMemoryArgs { + std::uint64_t searchStart; + std::uint64_t searchEnd; + std::uint64_t len; + std::uint64_t alignment; + std::uint32_t memoryType; +}; + +static constexpr auto dmemSize = 4ul * 1024 * 1024 * 1024; +// static const std::uint64_t nextOffset = 0; +// static const std::uint64_t memBeginAddress = 0xfe0000000; + +static std::int64_t dmem_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + + auto device = static_cast(instance->device.get()); + switch (request) { + case 0x4008800a: // get size + std::fprintf(stderr, "dmem%u getTotalSize(%p)\n", device->index, argp); + *(std::uint64_t *)argp = dmemSize; + return 0; + + case 0xc0208016: // get avaiable size + std::fprintf(stderr, "dmem%u getAvaiableSize(%p)\n", device->index, argp); + *(std::uint64_t *)argp = dmemSize - device->nextOffset; + return 0; + + case 0xc0288001: { // sceKernelAllocateDirectMemory + auto args = reinterpret_cast(argp); + auto alignedOffset = + (device->nextOffset + args->alignment - 1) & ~(args->alignment - 1); + + std::fprintf( + stderr, + "dmem%u allocateDirectMemory(searchStart = %lx, searchEnd = %lx, len " + "= %lx, alignment = %lx, memoryType = %x) -> 0x%lx\n", + device->index, args->searchStart, args->searchEnd, args->len, + args->alignment, args->memoryType, alignedOffset); + + if (alignedOffset + args->len > dmemSize) { + return -1; + } + + args->searchStart = alignedOffset; + device->nextOffset = alignedOffset + args->len; + return 0; + } + + case 0x80108002: { // sceKernelReleaseDirectMemory + struct Args { + std::uint64_t address; + std::uint64_t size; + }; + + auto args = reinterpret_cast(argp); + + std::fprintf( + stderr, "TODO: dmem%u releaseDirectMemory(address=0x%lx, size=0x%lx)\n", + device->index, args->address, args->size); + //std::fflush(stdout); + //__builtin_trap(); + return 0; + } + + default: + std::fprintf(stderr, "***ERROR*** Unhandled dmem%u ioctl %lx\n", + static_cast(instance->device.get())->index, request); + + return 0; + + std::fflush(stdout); + __builtin_trap(); + } + + return -1; +} + +static void *dmem_instance_mmap(IoDeviceInstance *instance, void *address, + std::uint64_t size, std::int32_t prot, + std::int32_t flags, std::int64_t offset) { + auto device = static_cast(instance->device.get()); + std::fprintf(stderr, "WARNING: dmem%u mmap %lx -> %lx\n", device->index, + offset, device->memBeginAddress + offset); + + auto addr = + rx::vm::map(reinterpret_cast(device->memBeginAddress + offset), size, + prot, flags); + return addr; +} + +static std::int32_t dmem_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new DmemInstance{}; + newInstance->ioctl = dmem_instance_ioctl; + newInstance->mmap = dmem_instance_mmap; + + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createDmemCharacterDevice(int index) { + auto *newDevice = new DmemDevice(); + newDevice->open = dmem_device_open; + newDevice->index = index; + newDevice->nextOffset = 0; + newDevice->memBeginAddress = 0xf'e000'0000 + dmemSize * index; + return newDevice; +} diff --git a/rpcsx-os/iodev/gc.cpp b/rpcsx-os/iodev/gc.cpp new file mode 100644 index 0000000..4867a95 --- /dev/null +++ b/rpcsx-os/iodev/gc.cpp @@ -0,0 +1,265 @@ +#include "io-device.hpp" +#include +#include +#include +#include +// #include +#include "vm.hpp" +#include +#include +#include +#include +#include + +struct GcDevice : public IoDevice {}; + +struct GcInstance : public IoDeviceInstance {}; + +static std::uint64_t g_submitDoneFlag; + +static std::int64_t gc_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + // 0xc00c8110 + // 0xc0848119 + + switch (request) { + case 0xc008811b: // get submit done flag ptr? + // TODO + std::fprintf(stderr, "gc ioctl 0xc008811b(%lx)\n", *(std::uint64_t *)argp); + *reinterpret_cast(argp) = &g_submitDoneFlag; + return 0; + + case 0xc0108102: { // submit? + struct Args { + std::uint32_t arg0; + std::uint32_t count; + std::uint64_t *cmds; + }; + + auto args = reinterpret_cast(argp); + + std::fprintf(stderr, "gc ioctl 0xc0108102(%x, %x, %p)\n", args->arg0, + args->count, args->cmds); + + for (int i = 0; i < args->count; ++i) { + auto cmd = args->cmds + (i * 2); + auto cmdId = cmd[0] & 0xffff'ffff; + auto addressLoPart = cmd[0] >> 32; + auto addressHiPart = cmd[1] & 0xff; + auto address = addressLoPart | (addressHiPart << 32); + auto unkPreservedVal = cmd[1] & 0xfff00000ffffff00; + auto size = ((cmd[1] >> 32) & 0xfffff) << 2; + + // std::fprintf(stderr, " %lx\n", cmd[0]); + // std::fprintf(stderr, " %lx\n", cmd[1]); + std::fprintf(stderr, " %u:\n", i); + std::fprintf(stderr, " cmdId = %lx\n", cmdId); + std::fprintf(stderr, " address = %lx\n", address); + std::fprintf(stderr, " unkPreservedVal = %lx\n", unkPreservedVal); + std::fprintf(stderr, " size = %lu\n", size); + + // orbis::bridge.sendCommandBuffer(address, size); + } + + break; + } + + case 0xc0088101: { // switch buffer? + struct Args { + std::uint32_t arg0; + std::uint32_t arg1; + }; + + auto args = reinterpret_cast(argp); + + std::fprintf(stderr, "gc ioctl 0xc0088101(%x, %x)\n", args->arg0, + args->arg1); + break; + } + + case 0xc020810c: { // submit and flip? + struct Args { + std::uint32_t arg0; + std::uint32_t count; + std::uint64_t *cmds; + std::uint64_t arg3; // flipArg? + std::uint32_t arg4; // bufferIndex? + }; + + auto args = reinterpret_cast(argp); + + std::fprintf(stderr, "gc ioctl 0xc020810c(%x, %x, %p, %lx, %x)\n", + args->arg0, args->count, args->cmds, args->arg3, args->arg4); + + for (int i = 0; i < args->count; ++i) { + auto cmd = args->cmds + (i * 2); + auto cmdId = cmd[0] & 0xffff'ffff; + auto addressLoPart = cmd[0] >> 32; + auto addressHiPart = cmd[1] & 0xff; + auto address = addressLoPart | (addressHiPart << 32); + auto unkPreservedVal = cmd[1] & 0xfff00000ffffff00; + auto size = ((cmd[1] >> 32) & 0xfffff) << 2; + + // std::fprintf(stderr, " %lx\n", cmd[0]); + // std::fprintf(stderr, " %lx\n", cmd[1]); + std::fprintf(stderr, " %u:\n", i); + std::fprintf(stderr, " cmdId = %lx\n", cmdId); + std::fprintf(stderr, " address = %lx\n", address); + std::fprintf(stderr, " unkPreservedVal = %lx\n", unkPreservedVal); + std::fprintf(stderr, " size = %lu\n", size); + + // orbis::bridge.sendCommandBuffer(address, size); + } + + //orbis::bridge.sendDoFlip(); + break; + } + + case 0xc0048116: { + std::fprintf(stderr, "gc ioctl 0xc0048116(%x)\n", *(std::uint32_t *)argp); + break; + } + + case 0xc00c8110: { + // set gs ring sizes + struct Args { + std::uint32_t arg1; + std::uint32_t arg2; + std::uint32_t unk; // 0 + }; + auto args = reinterpret_cast(argp); + + std::fprintf(stderr, + "gc ioctl set gs ring sizes: arg1=0x%x, arg2=0x%x, unk=0x%x\n", + args->arg1, args->arg2, args->unk); + break; + } + + case 0xc0848119: { // stats report control? + struct Args { + std::uint32_t unk; // 0x10001 + std::uint32_t arg1; + std::uint32_t arg2; + std::uint32_t arg3; + }; + auto args = reinterpret_cast(argp); + std::fprintf( + stderr, + "gc ioctl stats report control(unk=%x,arg1=%x,arg2=%x,arg3=%x)\n", + args->unk, args->arg1, args->arg2, args->arg3); + break; + } + + case 0xc010810b: { // something like stats masks? + struct Args { + std::uint64_t arg1; + std::uint64_t arg2; + }; + + auto args = reinterpret_cast(argp); + std::fprintf(stderr, "gc ioctl stats mask(arg1=%lx,arg2=%lx)\n", args->arg1, + args->arg2); + break; + } + + case 0xc030810d: { // map compute queue + struct Args { + std::uint32_t pipeHi; + std::uint32_t pipeLo; + std::uint32_t queueId; + std::uint32_t queuePipe; + std::uint64_t ringBaseAddress; + std::uint64_t readPtrAddress; + std::uint64_t dingDongPtr; + std::uint32_t count; + }; + + auto args = reinterpret_cast(argp); + + std::fprintf(stderr, + "gc ioctl map compute queue(pipeHi=%x, pipeLo=%x, queueId=%x, " + "queuePipe=%x, ringBaseAddress=%lx, readPtrAddress=%lx, " + "unkPtr=%lx, count=%u)\n", + args->pipeHi, args->pipeLo, args->queueId, args->queuePipe, + args->ringBaseAddress, args->readPtrAddress, args->dingDongPtr, + args->count); + + args->pipeHi = 0x769c766; + args->pipeLo = 0x72e8e3c1; + args->queueId = -0x248d50d8; + args->queuePipe = 0xd245ed58; + + ((std::uint64_t *)args->dingDongPtr)[0xf0 / sizeof(std::uint64_t)] = 1; + + // TODO: implement + // std::fflush(stdout); + //__builtin_trap(); + break; + } + + case 0xc010811c: { + // ding dong for workload + struct Args { + std::uint32_t pipeHi; + std::uint32_t pipeLo; + std::uint32_t queueId; + std::uint32_t nextStartOffsetInDw; + }; + + auto args = reinterpret_cast(argp); + std::fprintf(stderr, + "gc ioctl ding dong for workload(pipeHi=%x, pipeLo=%x, queueId=%x, " + "nextStartOffsetInDw=%x)\n", + args->pipeHi, args->pipeLo, args->queueId, args->nextStartOffsetInDw); + + + // TODO: implement + + break; + } + + case 0xc0048114: { + // SetWaveLimitMultipliers + std::fprintf(stderr, "***WARNING*** Unknown gc ioctl_%lx(0x%lx)\n", request, (unsigned long)*(std::uint32_t *)argp); + break; + } + + case 0xc004811f: { + std::fprintf(stderr, "***WARNING*** Unknown gc ioctl_%lx(0x%lx)\n", request, (unsigned long)*(std::uint32_t *)argp); + break; + } + + default: + std::fprintf(stderr, "***ERROR*** Unhandled gc ioctl %lx\n", request); + std::fflush(stdout); + __builtin_trap(); + break; + } + return 0; +} + +static void *gc_instance_mmap(IoDeviceInstance *instance, void *address, + std::uint64_t size, std::int32_t prot, + std::int32_t flags, std::int64_t offset) { + std::fprintf(stderr, "***ERROR*** Unhandled gc mmap %lx\n", offset); + + return rx::vm::map(address, size, prot, flags); +} + +static std::int32_t gc_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new GcInstance{}; + newInstance->ioctl = gc_instance_ioctl; + newInstance->mmap = gc_instance_mmap; + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createGcCharacterDevice() { + auto *newDevice = new GcDevice; + newDevice->open = gc_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/hid.cpp b/rpcsx-os/iodev/hid.cpp new file mode 100644 index 0000000..67597f6 --- /dev/null +++ b/rpcsx-os/iodev/hid.cpp @@ -0,0 +1,41 @@ +#include "io-device.hpp" +#include "vm.hpp" +#include +#include + +struct HidDevice : public IoDevice {}; +struct HidInstance : public IoDeviceInstance {}; + +static std::int64_t hid_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + std::fprintf(stderr, "***ERROR*** Unhandled hid ioctl %" PRIx64 "\n", request); + + // 0x800c4802 + return 0; +} + +static void *hid_instance_mmap(IoDeviceInstance *instance, void *address, + std::uint64_t size, std::int32_t prot, + std::int32_t flags, std::int64_t offset) { + std::fprintf(stderr, "***ERROR*** Unhandled hid mmap %" PRIx64 "\n", offset); + return rx::vm::map(address, size, prot, flags); +} + +static std::int32_t hid_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new HidInstance{}; + newInstance->ioctl = hid_instance_ioctl; + newInstance->mmap = hid_instance_mmap; + + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createHidCharacterDevice() { + auto *newDevice = new HidDevice; + newDevice->open = hid_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/hmd_3da.cpp b/rpcsx-os/iodev/hmd_3da.cpp new file mode 100644 index 0000000..074af74 --- /dev/null +++ b/rpcsx-os/iodev/hmd_3da.cpp @@ -0,0 +1,34 @@ +#include "io-device.hpp" +#include + +struct Hmd3daDevice : public IoDevice { +}; + +struct Hmd3daInstance : public IoDeviceInstance { +}; + +static std::int64_t hmd_3da_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + + std::fprintf(stderr, "***ERROR*** Unhandled hmd_3da ioctl %lx\n", + request); + return -1; +} + +static std::int32_t hmd_3da_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new Hmd3daInstance{}; + newInstance->ioctl = hmd_3da_instance_ioctl; + + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createHmd3daCharacterDevice() { + auto *newDevice = new Hmd3daDevice(); + newDevice->open = hmd_3da_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/hmd_cmd.cpp b/rpcsx-os/iodev/hmd_cmd.cpp new file mode 100644 index 0000000..e22f176 --- /dev/null +++ b/rpcsx-os/iodev/hmd_cmd.cpp @@ -0,0 +1,35 @@ +#include "io-device.hpp" +#include + +struct HmdCmdDevice : public IoDevice { +}; + +struct HmdCmdInstance : public IoDeviceInstance { +}; + +static std::int64_t hmd_cmd_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + + std::fprintf(stderr, "***ERROR*** Unhandled hmd_cmd ioctl %lx\n", + request); + return -1; +} + +static std::int32_t hmd_cmd_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new HmdCmdInstance{}; + newInstance->ioctl = hmd_cmd_instance_ioctl; + + io_device_instance_init(device, newInstance); + + *instance = newInstance; + return 0; +} + +IoDevice *createHmdCmdCharacterDevice() { + auto *newDevice = new HmdCmdDevice(); + newDevice->open = hmd_cmd_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/hmd_mmap.cpp b/rpcsx-os/iodev/hmd_mmap.cpp new file mode 100644 index 0000000..0a89531 --- /dev/null +++ b/rpcsx-os/iodev/hmd_mmap.cpp @@ -0,0 +1,47 @@ +#include "io-device.hpp" +#include "vm.hpp" +#include +#include + +struct HmdMmapDevice : public IoDevice {}; + +struct HmdMmapInstance : public IoDeviceInstance { +}; + +static std::int64_t hmd_mmap_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + + std::fprintf(stderr, "***ERROR*** Unhandled hmd_mmap ioctl %lx\n", + request); + std::fflush(stdout); + __builtin_trap(); + return -1; +} + +static void * hmd_mmap_instance_mmap(IoDeviceInstance *instance, + void *address, std::uint64_t size, + std::int32_t prot, + std::int32_t flags, + std::int64_t offset) { + std::fprintf(stderr, "***ERROR*** Unhandled hmd_mmap mmap %lx\n", offset); + return rx::vm::map(address, size, prot, flags); +} + +static std::int32_t hmd_mmap_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new HmdMmapInstance{}; + newInstance->ioctl = hmd_mmap_instance_ioctl; + newInstance->mmap = hmd_mmap_instance_mmap; + + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createHmdMmapCharacterDevice() { + auto *newDevice = new HmdMmapDevice(); + newDevice->open = hmd_mmap_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/hmd_snsr.cpp b/rpcsx-os/iodev/hmd_snsr.cpp new file mode 100644 index 0000000..1d1e275 --- /dev/null +++ b/rpcsx-os/iodev/hmd_snsr.cpp @@ -0,0 +1,33 @@ +#include "io-device.hpp" + +#include + +struct HmdSnsrDevice : public IoDevice {}; + +struct HmdSnsrInstance : public IoDeviceInstance { +}; + +static std::int64_t smd_snr_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + + std::fprintf(stderr, "***ERROR*** Unhandled hmd_snsr ioctl %lx\n", + request); + return -1; +} + +static std::int32_t smd_snr_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new HmdSnsrInstance{}; + newInstance->ioctl = smd_snr_instance_ioctl; + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createHmdSnsrCharacterDevice() { + auto *newDevice = new HmdSnsrDevice(); + newDevice->open = smd_snr_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/null.cpp b/rpcsx-os/iodev/null.cpp new file mode 100644 index 0000000..3bab3ae --- /dev/null +++ b/rpcsx-os/iodev/null.cpp @@ -0,0 +1,28 @@ +#include "io-device.hpp" + +struct NullDevice : public IoDevice {}; + +struct NullInstance : public IoDeviceInstance { +}; + +static std::int64_t null_instance_write(IoDeviceInstance *instance, + const void *data, std::uint64_t size) { + return size; +} + +static std::int32_t null_device_open(IoDevice *device, orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new NullInstance{}; + newInstance->write = null_instance_write; + + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createNullCharacterDevice() { + auto *newDevice = new NullDevice(); + newDevice->open = null_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/rng.cpp b/rpcsx-os/iodev/rng.cpp new file mode 100644 index 0000000..62053a8 --- /dev/null +++ b/rpcsx-os/iodev/rng.cpp @@ -0,0 +1,39 @@ +#include "io-device.hpp" +#include "vm.hpp" +#include +#include + +struct RngDevice : public IoDevice {}; +struct RngInstance : public IoDeviceInstance {}; + +static std::int64_t rng_instance_ioctl(IoDeviceInstance *instance, + std::uint64_t request, void *argp) { + std::fprintf(stderr, "***ERROR*** Unhandled rng ioctl %" PRIx64 "\n", request); + return 0; +} + +static void *rng_instance_mmap(IoDeviceInstance *instance, void *address, + std::uint64_t size, std::int32_t prot, + std::int32_t flags, std::int64_t offset) { + std::fprintf(stderr, "***ERROR*** Unhandled rng mmap %" PRIx64 "\n", offset); + return rx::vm::map(address, size, prot, flags); +} + +static std::int32_t rng_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new RngInstance{}; + newInstance->ioctl = rng_instance_ioctl; + newInstance->mmap = rng_instance_mmap; + + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createRngCharacterDevice() { + auto *newDevice = new RngDevice{}; + newDevice->open = rng_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/stderr.cpp b/rpcsx-os/iodev/stderr.cpp new file mode 100644 index 0000000..be49c06 --- /dev/null +++ b/rpcsx-os/iodev/stderr.cpp @@ -0,0 +1,48 @@ +#include "io-device.hpp" +#include + +struct StderrInstance : public IoDeviceInstance {}; + +struct StderrDevice : public IoDevice { + StderrInstance *instance = nullptr; +}; + +static std::int64_t stderr_instance_write(IoDeviceInstance *instance, const void *data, std::uint64_t size) { + auto result = fwrite(data, 1, size, stderr); + fflush(stderr); + + return result; +} + +static std::int64_t stderr_instance_close(IoDeviceInstance *instance) { + instance->device->decRef(); + return 0; +} + +static std::int32_t stderr_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto stderrDevice = static_cast(device); + if (stderrDevice->instance == nullptr) { + auto *newInstance = new StderrInstance{}; + newInstance->write = stderr_instance_write; + newInstance->close = stderr_instance_close; + + io_device_instance_init(device, newInstance); + + *instance = newInstance; + } else { + device->incRef(); + + *instance = stderrDevice->instance; + } + + return 0; +} + +IoDevice *createStderrCharacterDevice() { + auto *newDevice = new StderrDevice(); + newDevice->open = stderr_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/stdin.cpp b/rpcsx-os/iodev/stdin.cpp new file mode 100644 index 0000000..c7a86d5 --- /dev/null +++ b/rpcsx-os/iodev/stdin.cpp @@ -0,0 +1,28 @@ +#include "io-device.hpp" + +struct StdinDevice : public IoDevice { +}; + +struct StdinInstance : public IoDeviceInstance { +}; + +static std::int64_t stdin_instance_read(IoDeviceInstance *instance, void *data, + std::uint64_t size) { + return -1; +} + +static std::int32_t open(IoDevice *device, orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new StdinInstance{}; + newInstance->read = stdin_instance_read; + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createStdinCharacterDevice() { + auto *newDevice = new StdinDevice(); + newDevice->open = open; + return newDevice; +} diff --git a/rpcsx-os/iodev/stdout.cpp b/rpcsx-os/iodev/stdout.cpp new file mode 100644 index 0000000..5cf92c2 --- /dev/null +++ b/rpcsx-os/iodev/stdout.cpp @@ -0,0 +1,49 @@ +#include "io-device.hpp" +#include + + +struct StdoutInstance : public IoDeviceInstance {}; + +struct StdoutDevice : public IoDevice { + StdoutInstance *instance = nullptr; +}; + +static std::int64_t stdout_instance_write(IoDeviceInstance *instance, + const void *data, + std::uint64_t size) { + auto result = std::fwrite(data, 1, size, stdout); + std::fflush(stdout); + + return result; +} + +static std::int64_t stdout_instance_close(IoDeviceInstance *instance) { + instance->device->decRef(); + return 0; +} + +static std::int32_t stdout_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto stdoutDevice = static_cast(device); + if (stdoutDevice->instance == nullptr) { + auto *newInstance = new StdoutInstance{}; + newInstance->write = stdout_instance_write; + newInstance->close = stdout_instance_close; + io_device_instance_init(device, newInstance); + + *instance = newInstance; + } else { + device->incRef(); + *instance = stdoutDevice->instance; + } + + return 0; +} + +IoDevice *createStdoutCharacterDevice() { + auto *newDevice = new StdoutDevice(); + newDevice->open = stdout_device_open; + return newDevice; +} diff --git a/rpcsx-os/iodev/zero.cpp b/rpcsx-os/iodev/zero.cpp new file mode 100644 index 0000000..6bc9075 --- /dev/null +++ b/rpcsx-os/iodev/zero.cpp @@ -0,0 +1,32 @@ +#include "io-device.hpp" +#include + +struct ZeroDevice : public IoDevice { +}; + +struct ZeroInstance : public IoDeviceInstance { +}; + +static std::int64_t zero_device_read(IoDeviceInstance *instance, void *data, + std::uint64_t size) { + std::memset(data, 0, size); + return size; +} + +static std::int32_t zero_device_open(IoDevice *device, + orbis::Ref *instance, + const char *path, std::uint32_t flags, + std::uint32_t mode) { + auto *newInstance = new ZeroInstance{}; + newInstance->read = zero_device_read; + + io_device_instance_init(device, newInstance); + *instance = newInstance; + return 0; +} + +IoDevice *createZeroCharacterDevice() { + auto *newDevice = new ZeroDevice(); + newDevice->open = zero_device_open; + return newDevice; +} diff --git a/rpcsx-os/linker.cpp b/rpcsx-os/linker.cpp new file mode 100644 index 0000000..85775f2 --- /dev/null +++ b/rpcsx-os/linker.cpp @@ -0,0 +1,755 @@ +#include "linker.hpp" +#include "align.hpp" +#include "io-device.hpp" +#include "orbis/module/Module.hpp" +#include "vfs.hpp" +#include "vm.hpp" +#include +#include +#include +#include +#include +#include + +using orbis::utils::Ref; + +std::uint64_t rx::linker::encodeFid(std::string_view fid) { + static const char suffix[] = + "\x51\x8D\x64\xA6\x35\xDE\xD8\xC1\xE6\xB0\x39\xB1\xC3\xE5\x52\x30"; + + sha1_context ctx; + unsigned char output[20]; + + sha1_starts(&ctx); + sha1_update(&ctx, reinterpret_cast(fid.data()), + fid.length()); + sha1_update(&ctx, reinterpret_cast(suffix), + sizeof(suffix) - 1); + sha1_finish(&ctx, output); + + std::uint64_t hash; + std::memcpy(&hash, output, sizeof(hash)); + return hash; +} + +enum OrbisElfProgramType { + kElfProgramTypeNull = 0, + kElfProgramTypeLoad = 1, + kElfProgramTypeDynamic = 2, + kElfProgramTypeInterp = 3, + kElfProgramTypeNote = 4, + kElfProgramTypeShlib = 5, + kElfProgramTypePhdr = 6, + kElfProgramTypeTls = 7, + kElfProgramTypeSceDynlibData = 0x61000000, + kElfProgramTypeSceProcParam = 0x61000001, + kElfProgramTypeSceModuleParam = 0x61000002, + kElfProgramTypeSceRelRo = 0x61000010, + kElfProgramTypeGnuEhFrame = 0x6474e550, + kElfProgramTypeGnuRelRo = 0x6474e552, + kElfProgramTypeSceComment = 0x6fffff00, + kElfProgramTypeSceVersion = 0x6fffff01, +}; + +enum OrbisElfDynamicType { + kElfDynamicTypeNull = 0, + kElfDynamicTypeNeeded = 1, + kElfDynamicTypePltRelSize = 2, + kElfDynamicTypePltGot = 3, + kElfDynamicTypeHash = 4, + kElfDynamicTypeStrTab = 5, + kElfDynamicTypeSymTab = 6, + kElfDynamicTypeRela = 7, + kElfDynamicTypeRelaSize = 8, + kElfDynamicTypeRelaEnt = 9, + kElfDynamicTypeStrSize = 10, + kElfDynamicTypeSymEnt = 11, + kElfDynamicTypeInit = 12, + kElfDynamicTypeFini = 13, + kElfDynamicTypeSoName = 14, + kElfDynamicTypeRpath = 15, + kElfDynamicTypeSymbolic = 16, + kElfDynamicTypeRel = 17, + kElfDynamicTypeRelSize = 18, + kElfDynamicTypeRelEent = 19, + kElfDynamicTypePltRel = 20, + kElfDynamicTypeDebug = 21, + kElfDynamicTypeTextRel = 22, + kElfDynamicTypeJmpRel = 23, + kElfDynamicTypeBindNow = 24, + kElfDynamicTypeInitArray = 25, + kElfDynamicTypeFiniArray = 26, + kElfDynamicTypeInitArraySize = 27, + kElfDynamicTypeFiniArraySize = 28, + kElfDynamicTypeRunPath = 29, + kElfDynamicTypeFlags = 30, + kElfDynamicTypePreinitArray = 32, + kElfDynamicTypePreinitArraySize = 33, + kElfDynamicTypeSceFingerprint = 0x61000007, + kElfDynamicTypeSceOriginalFilename = 0x61000009, + kElfDynamicTypeSceModuleInfo = 0x6100000d, + kElfDynamicTypeSceNeededModule = 0x6100000f, + kElfDynamicTypeSceModuleAttr = 0x61000011, + kElfDynamicTypeSceExportLib = 0x61000013, + kElfDynamicTypeSceImportLib = 0x61000015, + kElfDynamicTypeSceExportLibAttr = 0x61000017, + kElfDynamicTypeSceImportLibAttr = 0x61000019, + kElfDynamicTypeSceHash = 0x61000025, + kElfDynamicTypeScePltGot = 0x61000027, + kElfDynamicTypeSceJmpRel = 0x61000029, + kElfDynamicTypeScePltRel = 0x6100002b, + kElfDynamicTypeScePltRelSize = 0x6100002d, + kElfDynamicTypeSceRela = 0x6100002f, + kElfDynamicTypeSceRelaSize = 0x61000031, + kElfDynamicTypeSceRelaEnt = 0x61000033, + kElfDynamicTypeSceStrTab = 0x61000035, + kElfDynamicTypeSceStrSize = 0x61000037, + kElfDynamicTypeSceSymTab = 0x61000039, + kElfDynamicTypeSceSymEnt = 0x6100003b, + kElfDynamicTypeSceHashSize = 0x6100003d, + kElfDynamicTypeSceOriginalFilename1 = 0x61000041, + kElfDynamicTypeSceModuleInfo1 = 0x61000043, + kElfDynamicTypeSceNeededModule1 = 0x61000045, + kElfDynamicTypeSceImportLib1 = 0x61000049, + kElfDynamicTypeSceSymTabSize = 0x6100003f, + kElfDynamicTypeRelaCount = 0x6ffffff9 +}; + +inline const char *toString(OrbisElfDynamicType dynType) { + switch (dynType) { + case kElfDynamicTypeNull: + return "Null"; + case kElfDynamicTypeNeeded: + return "Needed"; + case kElfDynamicTypePltRelSize: + return "PltRelSize"; + case kElfDynamicTypePltGot: + return "PltGot"; + case kElfDynamicTypeHash: + return "Hash"; + case kElfDynamicTypeStrTab: + return "StrTab"; + case kElfDynamicTypeSymTab: + return "SymTab"; + case kElfDynamicTypeRela: + return "Rela"; + case kElfDynamicTypeRelaSize: + return "RelaSize"; + case kElfDynamicTypeRelaEnt: + return "RelaEnt"; + case kElfDynamicTypeStrSize: + return "StrSize"; + case kElfDynamicTypeSymEnt: + return "SymEnt"; + case kElfDynamicTypeInit: + return "Init"; + case kElfDynamicTypeFini: + return "Fini"; + case kElfDynamicTypeSoName: + return "SoName"; + case kElfDynamicTypeRpath: + return "Rpath"; + case kElfDynamicTypeSymbolic: + return "Symbolic"; + case kElfDynamicTypeRel: + return "Rel"; + case kElfDynamicTypeRelSize: + return "RelSize"; + case kElfDynamicTypeRelEent: + return "RelEent"; + case kElfDynamicTypePltRel: + return "PltRel"; + case kElfDynamicTypeDebug: + return "Debug"; + case kElfDynamicTypeTextRel: + return "TextRel"; + case kElfDynamicTypeJmpRel: + return "JmpRel"; + case kElfDynamicTypeBindNow: + return "BindNow"; + case kElfDynamicTypeInitArray: + return "InitArray"; + case kElfDynamicTypeFiniArray: + return "FiniArray"; + case kElfDynamicTypeInitArraySize: + return "InitArraySize"; + case kElfDynamicTypeFiniArraySize: + return "FiniArraySize"; + case kElfDynamicTypeRunPath: + return "RunPath"; + case kElfDynamicTypeFlags: + return "Flags"; + case kElfDynamicTypePreinitArray: + return "PreinitArray"; + case kElfDynamicTypePreinitArraySize: + return "PreinitArraySize"; + case kElfDynamicTypeSceFingerprint: + return "SceFingerprint"; + case kElfDynamicTypeSceOriginalFilename: + return "SceOriginalFilename"; + case kElfDynamicTypeSceModuleInfo: + return "SceModuleInfo"; + case kElfDynamicTypeSceNeededModule: + return "SceNeededModule"; + case kElfDynamicTypeSceModuleAttr: + return "SceModuleAttr"; + case kElfDynamicTypeSceExportLib: + return "SceExportLib"; + case kElfDynamicTypeSceImportLib: + return "SceImportLib"; + case kElfDynamicTypeSceExportLibAttr: + return "SceExportLibAttr"; + case kElfDynamicTypeSceImportLibAttr: + return "SceImportLibAttr"; + case kElfDynamicTypeSceHash: + return "SceHash"; + case kElfDynamicTypeScePltGot: + return "ScePltGot"; + case kElfDynamicTypeSceJmpRel: + return "SceJmpRel"; + case kElfDynamicTypeScePltRel: + return "ScePltRel"; + case kElfDynamicTypeScePltRelSize: + return "ScePltRelSize"; + case kElfDynamicTypeSceRela: + return "SceRela"; + case kElfDynamicTypeSceRelaSize: + return "SceRelaSize"; + case kElfDynamicTypeSceRelaEnt: + return "SceRelaEnt"; + case kElfDynamicTypeSceStrTab: + return "SceStrTab"; + case kElfDynamicTypeSceStrSize: + return "SceStrSize"; + case kElfDynamicTypeSceSymTab: + return "SceSymTab"; + case kElfDynamicTypeSceSymEnt: + return "SceSymEnt"; + case kElfDynamicTypeSceHashSize: + return "SceHashSize"; + case kElfDynamicTypeSceOriginalFilename1: + return "SceOriginalFilename1"; + case kElfDynamicTypeSceModuleInfo1: + return "SceModuleInfo1"; + case kElfDynamicTypeSceNeededModule1: + return "SceNeededModule1"; + case kElfDynamicTypeSceImportLib1: + return "SceImportLib1"; + case kElfDynamicTypeSceSymTabSize: + return "SceSymTabSize"; + case kElfDynamicTypeRelaCount: + return "RelaCount"; + } + + return ""; +} + +struct SceProcessParam { + std::uint64_t size = 0x40; + std::uint32_t magic = 0x4942524F; + std::uint32_t entryCount = 3; + std::uint64_t sdkVersion = 0x4508101; + + std::uint64_t unk0 = 0; + std::uint64_t unk1 = 0; + std::uint64_t unk2 = 0; + std::uint64_t unk3 = 0; + + std::uint64_t sceLibcParam_ptr = 0; + + // ext, size == 0x50 + std::uint64_t sceLibcKernelMemParam_ptr = 0; + std::uint64_t sceLibcKernelFsParam_ptr = 0; +}; + +struct Symbol { + orbis::Module *module; + std::uint64_t address; + std::uint64_t bindMode; +}; + +static std::unordered_map> g_symTable; +static std::vector g_libraryPathList; + +Ref rx::linker::loadModule(std::span image, orbis::Process *process) { + Ref result{new orbis::Module{}}; + + Elf64_Ehdr header; + std::memcpy(&header, image.data(), sizeof(Elf64_Ehdr)); + result->type = header.e_type; + + Elf64_Phdr phdrsStorage[16]; + if (header.e_phnum > std::size(phdrsStorage)) { + std::abort(); + } + + std::memcpy(phdrsStorage, image.data() + header.e_phoff, + header.e_phnum * sizeof(Elf64_Phdr)); + auto phdrs = std::span(phdrsStorage, header.e_phnum); + + std::uint64_t imageSize = 0; + std::uint64_t baseAddress = ~static_cast(0); + int dynamicPhdrIndex = -1; + int interpPhdrIndex = -1; + int notePhdrIndex = -1; + int shlibPhdrIndex = -1; + int phdrPhdrIndex = -1; + int tlsPhdrIndex = -1; + int sceDynlibDataPhdrIndex = -1; + int sceProcParamIndex = -1; + int sceModuleParamIndex = -1; + int sceRelRoPhdrIndex = -1; + int gnuEhFramePhdrIndex = -1; + int gnuRelRoPhdrIndex = -1; + int sceCommentPhdrIndex = -1; + int sceVersionPhdrIndex = -1; + + for (auto &phdr : phdrs) { + std::size_t index = &phdr - phdrs.data(); + + switch (phdr.p_type) { + case kElfProgramTypeNull: + break; + case kElfProgramTypeLoad: + baseAddress = std::min( + baseAddress, utils::alignDown(phdr.p_vaddr, phdr.p_align)); + imageSize = std::max( + imageSize, utils::alignUp(phdr.p_vaddr + phdr.p_memsz, phdr.p_align)); + break; + case kElfProgramTypeDynamic: + dynamicPhdrIndex = index; + break; + case kElfProgramTypeInterp: + interpPhdrIndex = index; + break; + case kElfProgramTypeNote: + notePhdrIndex = index; + break; + case kElfProgramTypeShlib: + shlibPhdrIndex = index; + break; + case kElfProgramTypePhdr: + phdrPhdrIndex = index; + break; + case kElfProgramTypeTls: + tlsPhdrIndex = index; + break; + case kElfProgramTypeSceDynlibData: + sceDynlibDataPhdrIndex = index; + break; + case kElfProgramTypeSceProcParam: + sceProcParamIndex = index; + break; + case kElfProgramTypeSceModuleParam: + sceModuleParamIndex = index; + break; + case kElfProgramTypeSceRelRo: + sceRelRoPhdrIndex = index; + baseAddress = std::min( + baseAddress, utils::alignDown(phdr.p_vaddr, phdr.p_align)); + imageSize = std::max( + imageSize, utils::alignUp(phdr.p_vaddr + phdr.p_memsz, phdr.p_align)); + break; + case kElfProgramTypeGnuEhFrame: + gnuEhFramePhdrIndex = index; + break; + case kElfProgramTypeGnuRelRo: + gnuRelRoPhdrIndex = index; + break; + case kElfProgramTypeSceComment: + sceCommentPhdrIndex = index; + break; + case kElfProgramTypeSceVersion: + sceVersionPhdrIndex = index; + break; + } + } + + auto imageBase = reinterpret_cast( + rx::vm::map(reinterpret_cast(baseAddress), utils::alignUp(imageSize, rx::vm::kPageSize), + rx::vm::kMapProtCpuRead | rx::vm::kMapProtCpuWrite, + rx::vm::kMapFlagPrivate | rx::vm::kMapFlagAnonymous)); + + if (imageBase == MAP_FAILED) { + std::abort(); + } + + result->entryPoint = + header.e_entry ? reinterpret_cast(imageBase + header.e_entry) : 0; + + if (sceProcParamIndex >= 0) { + result->processParam = + phdrs[sceProcParamIndex].p_vaddr ? reinterpret_cast(imageBase + phdrs[sceProcParamIndex].p_vaddr) : nullptr; + result->processParamSize = phdrs[sceProcParamIndex].p_memsz; + } + + if (sceModuleParamIndex >= 0) { + result->moduleParam = + phdrs[sceProcParamIndex].p_vaddr ? reinterpret_cast(imageBase + phdrs[sceModuleParamIndex].p_vaddr) : nullptr; + result->moduleParamSize = phdrs[sceModuleParamIndex].p_memsz; + + // std::printf("sce_module_param: "); + // for (auto elem : image.subspan(phdrs[sceModuleParamIndex].p_offset, phdrs[sceModuleParamIndex].p_filesz)) { + // std::printf(" %02x", (unsigned)elem); + // } + // std::printf("\n"); + } + + if (tlsPhdrIndex >= 0) { + result->tlsAlign = phdrs[tlsPhdrIndex].p_align; + result->tlsSize = phdrs[tlsPhdrIndex].p_memsz; + result->tlsInitSize = phdrs[tlsPhdrIndex].p_filesz; + result->tlsInit = phdrs[tlsPhdrIndex].p_vaddr ? imageBase + phdrs[tlsPhdrIndex].p_vaddr : nullptr; + } + + if (gnuEhFramePhdrIndex >= 0 && phdrs[gnuEhFramePhdrIndex].p_vaddr > 0) { + result->ehFrame = imageBase + phdrs[gnuEhFramePhdrIndex].p_vaddr; + result->ehFrameSize = phdrs[gnuEhFramePhdrIndex].p_memsz; + + struct GnuExceptionInfo { + uint8_t version; + uint8_t encoding; + uint8_t fdeCount; + uint8_t encodingTable; + std::byte first; + }; + + auto *exinfo = reinterpret_cast( + image.data() + phdrs[gnuEhFramePhdrIndex].p_offset); + + if (exinfo->version != 1) { + std::abort(); + } + + if (exinfo->fdeCount != 0x03) { + std::abort(); + } + + if (exinfo->encodingTable != 0x3b) { + std::abort(); + } + + std::byte *dataBuffer = nullptr; + + if (exinfo->encoding == 0x03) { + auto offset = *reinterpret_cast(&exinfo->first); + dataBuffer = imageBase + offset; + } else if (exinfo->encoding == 0x1B) { + auto offset = *reinterpret_cast(&exinfo->first); + dataBuffer = &exinfo->first + sizeof(std::int32_t) + offset; + } else { + std::abort(); + } + + auto *dataBufferIt = dataBuffer; + while (true) { + auto size = *reinterpret_cast(dataBufferIt); + dataBufferIt += sizeof(std::uint32_t); + + if (size == 0) { + break; + } + + if (size == -1) { + size = *reinterpret_cast(dataBufferIt) + + sizeof(std::uint64_t); + } + + dataBufferIt += size; + } + + result->ehFrameHdr = imageBase + phdrs[gnuEhFramePhdrIndex].p_vaddr + (dataBuffer - image.data() - phdrs[gnuEhFramePhdrIndex].p_offset); + result->ehFrameHdrSize = dataBufferIt - dataBuffer; + } + + for (auto &phdr : phdrs) { + if (phdr.p_type == kElfProgramTypeLoad || + phdr.p_type == kElfProgramTypeSceRelRo) { + std::memcpy(imageBase + phdr.p_vaddr, image.data() + phdr.p_offset, + phdr.p_filesz); + std::memset(imageBase + phdr.p_vaddr + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz); + + if (result->segmentCount >= std::size(result->segments)) { + std::abort(); + } + + auto &segment = result->segments[result->segmentCount++]; + segment.addr = imageBase + phdr.p_vaddr; + segment.size = phdr.p_memsz; + segment.prot = phdr.p_flags; + } + } + + if (dynamicPhdrIndex >= 0 && phdrs[dynamicPhdrIndex].p_filesz > 0) { + auto &dynPhdr = phdrs[dynamicPhdrIndex]; + std::vector dyns(dynPhdr.p_filesz / sizeof(Elf64_Dyn)); + std::memcpy(dyns.data(), image.data() + dynPhdr.p_offset, + dyns.size() * sizeof(Elf64_Dyn)); + + int sceStrtabIndex = -1; + int sceSymtabIndex = -1; + std::size_t sceSymtabSize = 0; + for (auto &dyn : dyns) { + if (dyn.d_tag == kElfDynamicTypeSceStrTab) { + sceStrtabIndex = &dyn - dyns.data(); + } else if (dyn.d_tag == kElfDynamicTypeSceSymTab) { + sceSymtabIndex = &dyn - dyns.data(); + } else if (dyn.d_tag == kElfDynamicTypeSceSymTabSize) { + sceSymtabSize = dyn.d_un.d_val; + } + } + + auto sceStrtab = sceStrtabIndex >= 0 && sceDynlibDataPhdrIndex >= 0 + ? reinterpret_cast( + image.data() + dyns[sceStrtabIndex].d_un.d_val + + phdrs[sceDynlibDataPhdrIndex].p_offset) + : nullptr; + + auto sceDynlibData = + sceDynlibDataPhdrIndex >= 0 + ? image.data() + phdrs[sceDynlibDataPhdrIndex].p_offset + : nullptr; + + auto sceSymtabData = + sceSymtabIndex >= 0 && sceDynlibData != nullptr + ? reinterpret_cast( + sceDynlibData + dyns[sceSymtabIndex].d_un.d_val) + : nullptr; + + std::unordered_map idToModuleIndex; + std::unordered_map idToLibraryIndex; + + orbis::Relocation *pltRelocations = nullptr; + std::size_t pltRelocationCount = 0; + orbis::Relocation *nonPltRelocations = nullptr; + std::size_t nonPltRelocationCount = 0; + + for (auto dyn : dyns) { + // std::printf("%s: %lx", toString((OrbisElfDynamicType)dyn.d_tag), + // dyn.d_un.d_val); + + // if (dyn.d_tag == kElfDynamicTypeSceNeededModule || + // dyn.d_tag == kElfDynamicTypeNeeded || + // dyn.d_tag == kElfDynamicTypeSceOriginalFilename || + // dyn.d_tag == kElfDynamicTypeSceImportLib || + // dyn.d_tag == kElfDynamicTypeSceExportLib || + // dyn.d_tag == kElfDynamicTypeSceModuleInfo) { + // std::printf(" ('%s')", + // sceStrtab + // ? sceStrtab + static_cast(dyn.d_un.d_val) + // : ""); + // } + + // std::printf("\n"); + + if (dyn.d_tag == kElfDynamicTypeSceModuleInfo) { + std::strncpy(result->name, + sceStrtab + static_cast(dyn.d_un.d_val), + sizeof(result->name)); + } + + if (dyn.d_tag == kElfDynamicTypeSceModuleInfo) { + idToModuleIndex[dyn.d_un.d_val >> 48] = -1; + } + + if (dyn.d_tag == kElfDynamicTypeSceNeededModule) { + auto [it, inserted] = idToModuleIndex.try_emplace( + dyn.d_un.d_val >> 48, result->neededModules.size()); + + if (inserted) { + result->neededModules.emplace_back(); + } + + result->neededModules[it->second].name = + sceStrtab + static_cast(dyn.d_un.d_val); + } else if (dyn.d_tag == kElfDynamicTypeSceImportLib || + dyn.d_tag == kElfDynamicTypeSceExportLib) { + auto [it, inserted] = idToLibraryIndex.try_emplace( + dyn.d_un.d_val >> 48, result->neededLibraries.size()); + + if (inserted) { + result->neededLibraries.emplace_back(); + } + + result->neededLibraries[it->second].name = + sceStrtab + static_cast(dyn.d_un.d_val); + } + + switch (dyn.d_tag) { + case kElfDynamicTypeScePltGot: + result->pltGot = dyn.d_un.d_ptr ? + reinterpret_cast(imageBase + dyn.d_un.d_ptr) : nullptr; + break; + + case kElfDynamicTypeSceJmpRel: + if (sceDynlibData != nullptr) { + pltRelocations = reinterpret_cast( + sceDynlibData + dyn.d_un.d_ptr); + } + break; + case kElfDynamicTypeScePltRel: + break; + case kElfDynamicTypeScePltRelSize: + pltRelocationCount = dyn.d_un.d_val / sizeof(orbis::Relocation); + break; + case kElfDynamicTypeSceRela: + if (sceDynlibData != nullptr) { + nonPltRelocations = reinterpret_cast( + sceDynlibData + dyn.d_un.d_ptr); + } + break; + case kElfDynamicTypeSceRelaSize: + nonPltRelocationCount = dyn.d_un.d_val / sizeof(orbis::Relocation); + break; + case kElfDynamicTypeSceRelaEnt: + break; + + case kElfDynamicTypeInit: + result->initProc = imageBase + dyn.d_un.d_ptr; + break; + case kElfDynamicTypeFini: + result->finiProc = imageBase + dyn.d_un.d_ptr; + break; + } + } + + if (sceSymtabData != nullptr && sceSymtabSize / sizeof(Elf64_Sym) > 0) { + auto sceSymtab = + std::span(sceSymtabData, sceSymtabSize / sizeof(Elf64_Sym)); + + result->symbols.reserve(sceSymtab.size()); + + for (auto &sym : sceSymtab) { + auto visibility = ELF64_ST_VISIBILITY(sym.st_other); + auto type = ELF64_ST_TYPE(sym.st_info); + auto bind = ELF64_ST_BIND(sym.st_info); + orbis::Symbol symbol{ + .address = sym.st_value, + .size = sym.st_size, + .visibility = static_cast(visibility), + .bind = static_cast(bind), + .type = static_cast(type), + }; + + if (sceStrtab != nullptr && sym.st_name != 0) { + auto fullName = std::string_view(sceStrtab + sym.st_name); + if (auto hashPos = fullName.find('#'); + hashPos != std::string_view::npos) { + std::string_view module; + std::string_view library; + std::string_view name; + + name = fullName.substr(0, hashPos); + auto moduleLibary = fullName.substr(hashPos + 1); + + hashPos = moduleLibary.find('#'); + + if (hashPos == std::string_view::npos) { + std::abort(); + } + + library = moduleLibary.substr(0, hashPos); + module = moduleLibary.substr(hashPos + 1); + + auto libaryNid = decodeNid(library); + auto moduleNid = decodeNid(module); + + symbol.libraryIndex = idToLibraryIndex.at(libaryNid); + symbol.moduleIndex = idToModuleIndex.at(moduleNid); + symbol.id = decodeNid(name); + } else { + // std::printf("ignored: (%s) - %lx\n", + // sceStrtab ? sceStrtab + + // static_cast(sym.st_name) + // : "", + // sym.st_value); + } + } + + result->symbols.push_back(symbol); + } + } + + if (pltRelocations != nullptr && pltRelocationCount > 0) { + result->pltRelocations.reserve(pltRelocationCount); + for (auto rel : std::span(pltRelocations, pltRelocationCount)) { + result->pltRelocations.push_back(rel); + } + } + + if (nonPltRelocations != nullptr && nonPltRelocationCount > 0) { + result->nonPltRelocations.reserve(nonPltRelocationCount); + for (auto rel : std::span(nonPltRelocations, nonPltRelocationCount)) { + result->nonPltRelocations.push_back(rel); + } + } + } + + result->base = imageBase; + result->size = imageSize; + + // std::printf("Module %s (%p - %p)\n", result->name, result->base, + // (char *)result->base + result->size); + // std::printf("Needed modules: ["); + // for (bool isFirst = true; auto &module : result->neededModules) { + // if (isFirst) { + // isFirst = false; + // } else { + // std::printf(", "); + // } + + // std::printf("'%s'", module.name.c_str()); + // } + // std::printf("]\n"); + // std::printf("Needed libraries: ["); + // for (bool isFirst = true; auto &library : result->neededLibraries) { + // if (isFirst) { + // isFirst = false; + // } else { + // std::printf(", "); + // } + + // std::printf("'%s'", library.name.c_str()); + // } + // std::printf("]\n"); + + for (auto seg : std::span(result->segments, result->segmentCount)) { + rx::vm::protect(seg.addr, seg.size, rx::vm::kMapProtCpuAll); + } + + result->id = process->modulesMap.insert(result); + result->proc = process; + if (tlsPhdrIndex >= 0 /* result->tlsSize != 0 */) { + result->tlsIndex = process->nextTlsSlot++; + } + + return result; +} + +Ref rx::linker::loadModuleFile(const char *path, + orbis::Process *process) { + orbis::Ref instance; + if (vfs::open(path, kOpenFlagReadOnly, 0, &instance).isError()) { + return{}; + } + + auto len = instance->lseek(instance.get(), 0, SEEK_END); + instance->lseek(instance.get(), 0, SEEK_SET); + + std::vector image(len); + auto ptr = image.data(); + auto endPtr = ptr + image.size(); + + while (ptr != endPtr) { + auto result = instance->read(instance.get(), ptr, endPtr - ptr); + if (result < 0) { + std::fprintf(stderr, "Module file reading error\n"); + std::abort(); + } + ptr += result; + } + + instance->close(instance.get()); + + return loadModule(image, process); +} diff --git a/rpcsx-os/linker.hpp b/rpcsx-os/linker.hpp new file mode 100644 index 0000000..61d161e --- /dev/null +++ b/rpcsx-os/linker.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include "orbis/module/Module.hpp" +#include "orbis/utils/Rc.hpp" +#include +#include + +namespace rx::linker { +inline constexpr char nidLookup[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; + +constexpr std::uint64_t decodeNid(std::string_view nid) { + std::uint64_t result = 0; + + if (nid.size() > 11) { + std::abort(); + } + + for (std::size_t i = 0; i < nid.size(); ++i) { + auto it = std::strchr(nidLookup, nid[i]); + + if (it == nullptr) { + std::abort(); + } + + auto value = static_cast(it - nidLookup); + + if (i == 10) { + result <<= 4; + result |= (value >> 2); + break; + } + + result <<= 6; + result |= value; + } + + return result; +} + +std::uint64_t encodeFid(std::string_view fid); + +struct Symbol { + orbis::Module *module; + std::uint64_t address; + std::uint64_t bindMode; +}; + +enum OrbisElfType_t { + kElfTypeNone = 0, + kElfTypeRel = 1, + kElfTypeExec = 2, + kElfTypeDyn = 3, + kElfTypeCore = 4, + kElfTypeNum = 5, + kElfTypeSceExec = 0xfe00, + kElfTypeSceDynExec = 0xfe10, + kElfTypeSceDynamic = 0xfe18 +}; + +orbis::utils::Ref loadModule(std::span image, orbis::Process *process); +orbis::utils::Ref loadModuleFile(const char *path, orbis::Process *process); +} // namespace re::loader diff --git a/rpcsx-os/main.cpp b/rpcsx-os/main.cpp new file mode 100644 index 0000000..c355d12 --- /dev/null +++ b/rpcsx-os/main.cpp @@ -0,0 +1,637 @@ +#include "align.hpp" +#include "io-device.hpp" +#include "io-devices.hpp" +#include "linker.hpp" +#include "ops.hpp" +#include "vfs.hpp" +#include "vm.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct LibcInfo { + std::uint64_t textBegin = ~static_cast(0); + std::uint64_t textSize = 0; +}; + +static LibcInfo libcInfo; + +struct ThreadParam { + void (*startFunc)(void *); + void *arg; + orbis::Thread *thread; +}; + +static thread_local orbis::Thread *g_currentThread = nullptr; + +static void printStackTrace(ucontext_t *context, int fileno) { + unw_cursor_t cursor; + + char buffer[1024]; + + if (int r = unw_init_local2(&cursor, context, UNW_INIT_SIGNAL_FRAME)) { + int len = snprintf(buffer, sizeof(buffer), "unw_init_local: %s\n", + unw_strerror(r)); + write(fileno, buffer, len); + return; + } + + char functionName[256]; + + int count = 0; + do { + unw_word_t ip; + unw_get_reg(&cursor, UNW_REG_IP, &ip); + + unw_word_t off; + int proc_res = + unw_get_proc_name(&cursor, functionName, sizeof(functionName), &off); + + Dl_info dlinfo; + int dladdr_res = ::dladdr((void *)ip, &dlinfo); + + unsigned long baseAddress = + dladdr_res != 0 ? reinterpret_cast(dlinfo.dli_fbase) : 0; + + int len = snprintf(buffer, sizeof(buffer), "%3d: %s+%p: %s(%lx)+%#lx\n", + count, (dladdr_res != 0 ? dlinfo.dli_fname : "??"), + reinterpret_cast(ip - baseAddress), + (proc_res == 0 ? functionName : "??"), + reinterpret_cast( + proc_res == 0 ? ip - baseAddress - off : 0), + static_cast(proc_res == 0 ? off : 0)); + write(fileno, buffer, len); + count++; + } while (unw_step(&cursor) > 0 && count < 32); +} + +static std::size_t printAddressLocation(char *dest, std::size_t destLen, + orbis::Thread *thread, + std::uint64_t address) { + if (thread == nullptr || address == 0) { + return 0; + } + + for (auto [id, module] : thread->tproc->modulesMap) { + auto moduleBase = reinterpret_cast(module->base); + if (moduleBase > address || moduleBase + module->size <= address) { + continue; + } + + return std::snprintf(dest, destLen, "%s+%#" PRIx64, module->name, + address - moduleBase); + } + + return 0; +} + +static void printStackTrace(ucontext_t *context, orbis::Thread *thread, + int fileno) { + unw_cursor_t cursor; + + char buffer[1024]; + + if (int r = unw_init_local2(&cursor, context, UNW_INIT_SIGNAL_FRAME)) { + int len = snprintf(buffer, sizeof(buffer), "unw_init_local: %s\n", + unw_strerror(r)); + write(fileno, buffer, len); + return; + } + + int count = 0; + char functionName[256]; + do { + unw_word_t ip; + unw_get_reg(&cursor, UNW_REG_IP, &ip); + std::size_t offset = 0; + + offset += + std::snprintf(buffer + offset, sizeof(buffer) - offset, "%3d: ", count); + + if (auto loc = printAddressLocation(buffer + offset, + sizeof(buffer) - offset, thread, ip)) { + offset += loc; + offset += std::snprintf(buffer + offset, sizeof(buffer) - offset, "\n"); + } else { + unw_word_t off; + int proc_res = + unw_get_proc_name(&cursor, functionName, sizeof(functionName), &off); + + Dl_info dlinfo; + int dladdr_res = ::dladdr((void *)ip, &dlinfo); + + unsigned long baseAddress = + dladdr_res != 0 ? reinterpret_cast(dlinfo.dli_fbase) + : 0; + + offset = snprintf(buffer, sizeof(buffer), "%3d: %s+%p: %s(%lx)+%#lx\n", + count, (dladdr_res != 0 ? dlinfo.dli_fname : "??"), + reinterpret_cast(ip - baseAddress), + (proc_res == 0 ? functionName : "??"), + reinterpret_cast( + proc_res == 0 ? ip - baseAddress - off : 0), + static_cast(proc_res == 0 ? off : 0)); + } + + write(fileno, buffer, offset); + count++; + } while (unw_step(&cursor) > 0 && count < 32); +} + +__attribute__((no_stack_protector)) static void +handle_signal(int sig, siginfo_t *info, void *ucontext) { + std::uint64_t hostFs = _readgsbase_u64(); + _writefsbase_u64(hostFs); + + // syscall(SYS_arch_prctl, ARCH_GET_GS, &hostFs); + // syscall(SYS_arch_prctl, ARCH_SET_FS, hostFs); + + if (sig == SIGSYS) { + // printf("%x: %x\n", tid, thread->tid); + g_currentThread->context = reinterpret_cast(ucontext); + orbis::syscall_entry(g_currentThread); + _writefsbase_u64(g_currentThread->fsBase); + // syscall(SYS_arch_prctl, ARCH_SET_FS, g_currentThread->regs.fs); + return; + } + + const char message[] = "Signal handler!\n"; + write(2, message, sizeof(message) - 1); + + char buf[128] = ""; + int len = snprintf(buf, sizeof(buf), " [%s] %u: Signal address=%p\n", + g_currentThread ? "guest" : "host", + g_currentThread ? g_currentThread->tid : ::gettid(), + info->si_addr); + write(2, buf, len); + + if (std::size_t printed = printAddressLocation( + buf, sizeof(buf), g_currentThread, (std::uint64_t)info->si_addr)) { + printed += std::snprintf(buf + printed, sizeof(buf) - printed, "\n"); + write(2, buf, printed); + } + + struct sigaction act {}; + sigset_t mask; + sigemptyset(&mask); + + act.sa_handler = SIG_DFL; + act.sa_flags = SA_SIGINFO | SA_ONSTACK; + act.sa_mask = mask; + + if (sigaction(sig, &act, NULL)) { + perror("Error sigaction:"); + std::exit(-1); + } + + if (g_currentThread) { + printStackTrace(reinterpret_cast(ucontext), g_currentThread, + 2); + } else { + printStackTrace(reinterpret_cast(ucontext), 2); + } +} + +static void setupSigHandlers() { + stack_t ss; + + ss.ss_sp = malloc(SIGSTKSZ); + if (ss.ss_sp == NULL) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + ss.ss_size = SIGSTKSZ; + ss.ss_flags = 0; + + if (sigaltstack(&ss, NULL) == -1) { + perror("sigaltstack"); + exit(EXIT_FAILURE); + } + + struct sigaction act; + sigset_t mask; + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + + act.sa_sigaction = handle_signal; + act.sa_flags = SA_SIGINFO | SA_ONSTACK; + act.sa_mask = mask; + + if (sigaction(SIGSYS, &act, NULL)) { + perror("Error sigaction:"); + exit(-1); + } + + if (sigaction(SIGILL, &act, NULL)) { + perror("Error sigaction:"); + exit(-1); + } + + if (sigaction(SIGSEGV, &act, NULL)) { + perror("Error sigaction:"); + exit(-1); + } + + if (sigaction(SIGBUS, &act, NULL)) { + perror("Error sigaction:"); + exit(-1); + } + + if (sigaction(SIGABRT, &act, NULL)) { + perror("Error sigaction:"); + exit(-1); + } +} + +__attribute__((no_stack_protector)) static void * +emuThreadEntryPoint(void *paramsVoid) { + auto params = *reinterpret_cast(paramsVoid); + delete reinterpret_cast(paramsVoid); + + g_currentThread = params.thread; + + std::uint64_t hostFs; + syscall(SYS_arch_prctl, ARCH_GET_FS, &hostFs); + syscall(SYS_arch_prctl, ARCH_SET_GS, hostFs); + + if (prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, + libcInfo.textBegin, libcInfo.textSize, nullptr)) { + perror("prctl failed\n"); + exit(-1); + } + + syscall(SYS_arch_prctl, ARCH_SET_FS, params.thread->fsBase); + params.startFunc(params.arg); + syscall(SYS_arch_prctl, ARCH_SET_FS, hostFs); + + return nullptr; +} + +struct StackWriter { + std::uint64_t address; + + template std::uint64_t push(T value) { + address -= sizeof(value); + address &= ~(alignof(T) - 1); + *reinterpret_cast(address) = value; + return address; + } + + void align(std::uint64_t alignment) { address &= ~(alignment - 1); } + + std::uint64_t pushString(const char *value) { + auto len = std::strlen(value); + address -= len + 1; + std::memcpy(reinterpret_cast(address), value, len + 1); + return address; + } + + std::uint64_t alloc(std::uint64_t size, std::uint64_t alignment) { + address -= size; + address &= ~(alignment - 1); + return address; + } +}; + +static void createEmuThread(orbis::Thread &thread, uint64_t entryPoint, + uint64_t hostStackSize, uint64_t arg) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstack(&attr, thread.stackStart, hostStackSize); + + pthread_t pthread; + auto params = new ThreadParam; + + params->startFunc = (void (*)(void *))entryPoint; + params->arg = (void *)arg; + params->thread = &thread; + + pthread_create(&pthread, &attr, emuThreadEntryPoint, params); + pthread_join(pthread, nullptr); +} + +static bool g_traceSyscalls = false; +static const char *getSyscallName(orbis::Thread *thread, int sysno) { + auto sysvec = thread->tproc->sysent; + + if (sysno >= sysvec->size) { + return nullptr; + } + + return orbis::getSysentName(sysvec->table[sysno].call); +} +static void onSysEnter(orbis::Thread *thread, int id, uint64_t *args, + int argsCount) { + if (true || !g_traceSyscalls) { + return; + } + std::printf(" [%u] ", thread->tid); + + if (auto name = getSyscallName(thread, id)) { + std::printf("%s(", name); + } else { + std::printf("sys_%u(", id); + } + + for (int i = 0; i < argsCount; ++i) { + if (i != 0) { + std::printf(", "); + } + + std::printf("%#lx", args[i]); + } + + std::printf(")\n"); +} + +static void onSysExit(orbis::Thread *thread, int id, uint64_t *args, + int argsCount, orbis::SysResult result) { + if (!result.isError() && !g_traceSyscalls) { + return; + } + + std::printf("%c: [%u] ", result.isError() ? 'E' : 'S', thread->tid); + + if (auto name = getSyscallName(thread, id)) { + std::printf("%s(", name); + } else { + std::printf("sys_%u(", id); + } + + for (int i = 0; i < argsCount; ++i) { + if (i != 0) { + std::printf(", "); + } + + std::printf("%#lx", args[i]); + } + + std::printf(") -> Status %d, Value %lx:%lx\n", result.value(), + thread->retval[0], thread->retval[1]); +} + +static int ps4Exec(orbis::Process *mainProcess, + orbis::utils::Ref executableModule, + std::span argv, std::span envp) { + mainProcess->sysent = &orbis::ps4_sysvec; + mainProcess->ops = &rx::procOpsTable; + + orbis::Thread mainThread; + mainThread.tproc = mainProcess; + mainThread.tid = mainProcess->pid; + mainThread.state = orbis::ThreadState::RUNNING; + + const auto stackEndAddress = 0x7'ffff'c000ull; + const auto stackSize = 0x40000 * 16; + auto stackStartAddress = stackEndAddress - stackSize; + mainThread.stackStart = + rx::vm::map(reinterpret_cast(stackStartAddress), stackSize, + rx::vm::kMapProtCpuWrite | rx::vm::kMapProtCpuRead, + rx::vm::kMapFlagAnonymous | rx::vm::kMapFlagFixed | + rx::vm::kMapFlagPrivate | rx::vm::kMapFlagStack); + + mainThread.stackEnd = + reinterpret_cast(mainThread.stackStart) + stackSize; + + rx::vfs::mount("/dev/dmem0", createDmemCharacterDevice(0)); + rx::vfs::mount("/dev/dmem1", createDmemCharacterDevice(1)); + rx::vfs::mount("/dev/dmem2", createDmemCharacterDevice(2)); + rx::vfs::mount("/dev/stdout", createStdoutCharacterDevice()); + rx::vfs::mount("/dev/stderr", createStderrCharacterDevice()); + rx::vfs::mount("/dev/stdin", createStdinCharacterDevice()); + rx::vfs::mount("/dev/zero", createZeroCharacterDevice()); + rx::vfs::mount("/dev/null", createNullCharacterDevice()); + rx::vfs::mount("/dev/dipsw", createDipswCharacterDevice()); + rx::vfs::mount("/dev/dce", createDceCharacterDevice()); + rx::vfs::mount("/dev/hmd_cmd", createHmdCmdCharacterDevice()); + rx::vfs::mount("/dev/hmd_snsr", createHmdSnsrCharacterDevice()); + rx::vfs::mount("/dev/hmd_3da", createHmd3daCharacterDevice()); + rx::vfs::mount("/dev/hmd_dist", createHmdMmapCharacterDevice()); + rx::vfs::mount("/dev/hid", createHidCharacterDevice()); + rx::vfs::mount("/dev/gc", createGcCharacterDevice()); + rx::vfs::mount("/dev/rng", createRngCharacterDevice()); + + rx::procOpsTable.open(&mainThread, "/dev/stdin", 0, 0); + rx::procOpsTable.open(&mainThread, "/dev/stdout", 0, 0); + rx::procOpsTable.open(&mainThread, "/dev/stderr", 0, 0); + + std::vector argvOffsets; + std::vector envpOffsets; + + auto libkernel = rx::linker::loadModuleFile( + "/system/common/lib/libkernel_sys.sprx", mainProcess); + + // *reinterpret_cast( + // reinterpret_cast(libkernel->base) + 0x6c2e4) = ~0; + + StackWriter stack{reinterpret_cast(mainThread.stackEnd)}; + + for (auto elem : argv) { + argvOffsets.push_back(stack.pushString(elem)); + } + + argvOffsets.push_back(0); + + for (auto elem : envp) { + envpOffsets.push_back(stack.pushString(elem)); + } + + envpOffsets.push_back(0); + + // clang-format off + std::uint64_t auxv[] = { + AT_ENTRY, executableModule->entryPoint, + AT_BASE, reinterpret_cast(libkernel->base), + AT_NULL, 0 + }; + // clang-format on + + std::size_t argSize = + sizeof(std::uint64_t) + sizeof(std::uint64_t) * argvOffsets.size() + + sizeof(std::uint64_t) * envpOffsets.size() + sizeof(auxv); + + auto sp = stack.alloc(argSize, 32); + + auto arg = reinterpret_cast(sp); + *arg++ = argvOffsets.size() - 1; + + for (auto argvOffsets : argvOffsets) { + *arg++ = argvOffsets; + } + + for (auto envpOffset : envpOffsets) { + *arg++ = envpOffset; + } + + executableModule = {}; + + memcpy(arg, auxv, sizeof(auxv)); + + ucontext_t currentContext; + getcontext(¤tContext); + + createEmuThread( + mainThread, libkernel->entryPoint, + utils::alignDown( + stack.address - + reinterpret_cast(mainThread.stackStart) - 0x1000, + rx::vm::kPageSize), + sp); + return 0; +} + +struct KernelEventLogger : public orbis::KernelContext::EventListener { + void onProcessCreated(orbis::Process *process) override { + std::printf("process %u was created\n", (unsigned)process->pid); + } + void onProcessDeleted(orbis::pid_t pid) override { + std::printf("process %u was deleted\n", (unsigned)pid); + } +}; + +static void usage(const char *argv0) { + std::printf("%s [...] [args...]\n", argv0); + std::printf(" options:\n"); + std::printf(" -m, --mount \n"); + std::printf(" --trace\n"); +} + +int main(int argc, const char *argv[]) { + if (argc == 2) { + if (std::strcmp(argv[1], "-h") == 0 || + std::strcmp(argv[1], "--help") == 0) { + usage(argv[0]); + return 1; + } + } + + if (argc < 2) { + usage(argv[0]); + return 1; + } + + auto processPhdr = [](struct dl_phdr_info *info, size_t, void *data) { + auto path = std::string_view(info->dlpi_name); + auto slashPos = path.rfind('/'); + if (slashPos == std::string_view::npos) { + return 0; + } + + auto name = path.substr(slashPos + 1); + if (name.starts_with("libc.so")) { + std::printf("%s\n", std::string(name).c_str()); + auto libcInfo = reinterpret_cast(data); + + for (std::size_t i = 0; i < info->dlpi_phnum; ++i) { + auto &phdr = info->dlpi_phdr[i]; + + if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X) == PF_X) { + libcInfo->textBegin = + std::min(libcInfo->textBegin, phdr.p_vaddr + info->dlpi_addr); + libcInfo->textSize = std::max(libcInfo->textSize, phdr.p_memsz); + } + } + + return 1; + } + + return 0; + }; + + dl_iterate_phdr(processPhdr, &libcInfo); + + std::printf("libc text %zx-%zx\n", libcInfo.textBegin, + libcInfo.textBegin + libcInfo.textSize); + + setupSigHandlers(); + // rx::vm::printHostStats(); + KernelEventLogger eventLogger; + orbis::KernelContext context; + context.addEventListener(&eventLogger); + + rx::vfs::initialize(); + + int argIndex = 1; + while (argIndex < argc) { + if (argv[argIndex] == std::string_view("--mount") || + argv[argIndex] == std::string_view("-m")) { + if (argc <= argIndex + 2) { + usage(argv[0]); + return 1; + } + + std::printf("mounting '%s' to virtual '%s'\n", argv[argIndex + 1], + argv[argIndex + 2]); + if (!std::filesystem::is_directory(argv[argIndex + 1])) { + std::fprintf(stderr, "Directory '%s' not exists\n", argv[argIndex + 1]); + return 1; + } + + rx::vfs::mount(argv[argIndex + 2], createHostIoDevice(argv[argIndex + 1])); + argIndex += 3; + continue; + } + + if (argv[argIndex] == std::string_view("--trace")) { + argIndex++; + g_traceSyscalls = true; + continue; + } + + break; + } + + if (argIndex >= argc) { + usage(argv[0]); + return 1; + } + + rx::vm::initialize(); + // rx::vm::printHostStats(); + auto initProcess = context.createProcess(10); + initProcess->sysent = &orbis::ps4_sysvec; + initProcess->onSysEnter = onSysEnter; + initProcess->onSysExit = onSysExit; + auto executableModule = + rx::linker::loadModuleFile(argv[argIndex], initProcess); + initProcess->processParam = executableModule->processParam; + initProcess->processParamSize = executableModule->processParamSize; + + int status = 0; + + if (executableModule->type == rx::linker::kElfTypeSceDynExec || + executableModule->type == rx::linker::kElfTypeSceExec) { + status = ps4Exec(initProcess, std::move(executableModule), + std::span(argv + argIndex, argc - argIndex), + std::span()); + } else { + std::fprintf(stderr, "Unexpected executable type\n"); + status = 1; + } + + // entryPoint(); + + // rx::vm::printHostStats(); + rx::vm::uninitialize(); + + return status; +} diff --git a/rpcsx-os/ops.cpp b/rpcsx-os/ops.cpp new file mode 100644 index 0000000..c725d98 --- /dev/null +++ b/rpcsx-os/ops.cpp @@ -0,0 +1,533 @@ +#include "ops.hpp" +#include "io-device.hpp" +#include "linker.hpp" +#include "orbis/module/ModuleHandle.hpp" +#include "orbis/thread/Process.hpp" +#include "orbis/thread/Thread.hpp" +#include "orbis/utils/Rc.hpp" +#include "vfs.hpp" +#include "vm.hpp" +#include +#include +#include +#include +#include +#include + +using namespace orbis; + +extern "C" void __register_frame(const void *); + +namespace { +orbis::SysResult mmap(orbis::Thread *thread, orbis::caddr_t addr, + orbis::size_t len, orbis::sint prot, orbis::sint flags, + orbis::sint fd, orbis::off_t pos) { + auto result = (void *)-1; + if (fd == -1) { + result = rx::vm::map(addr, len, prot, flags); + } else { + Ref handle = + static_cast(thread->tproc->fileDescriptors.get(fd)); + if (handle == nullptr) { + return ErrorCode::BADF; + } + + result = handle->mmap(handle.get(), addr, len, prot, flags, pos); + } + + if (result == (void *)-1) { + return ErrorCode::NOMEM; + } + + thread->retval[0] = reinterpret_cast(result); + return{}; +} + +orbis::SysResult munmap(orbis::Thread *thread, orbis::ptr addr, + orbis::size_t len) { + return ErrorCode::INVAL; +} + +orbis::SysResult msync(orbis::Thread *thread, orbis::ptr addr, + orbis::size_t len, orbis::sint flags) { + return {}; +} + +orbis::SysResult mprotect(orbis::Thread *thread, orbis::ptr addr, + orbis::size_t len, orbis::sint prot) { + rx::vm::protect((void *)addr, len, prot); + return {}; +} + +orbis::SysResult minherit(orbis::Thread *thread, orbis::ptr addr, + orbis::size_t len, orbis::sint inherit) { + return ErrorCode::INVAL; +} + +orbis::SysResult madvise(orbis::Thread *thread, orbis::ptr addr, + orbis::size_t len, orbis::sint behav) { + return ErrorCode::INVAL; +} + +orbis::SysResult mincore(orbis::Thread *thread, orbis::ptr addr, + orbis::size_t len, orbis::ptr vec) { + return ErrorCode::INVAL; +} + +orbis::SysResult mlock(orbis::Thread *thread, orbis::ptr addr, + orbis::size_t len) { + return {}; +} +orbis::SysResult mlockall(orbis::Thread *thread, orbis::sint how) { return {}; } +orbis::SysResult munlockall(orbis::Thread *thread) { return {}; } +orbis::SysResult munlock(orbis::Thread *thread, orbis::ptr addr, + orbis::size_t len) { + return {}; +} +orbis::SysResult virtual_query(orbis::Thread *thread, + orbis::ptr addr, orbis::sint flags, + orbis::ptr info, orbis::ulong infoSize) { + if (infoSize != sizeof(rx::vm::VirtualQueryInfo)) { + return ErrorCode::INVAL; + } + + if (!rx::vm::virtualQuery(addr, flags, (rx::vm::VirtualQueryInfo *)info)) { + return ErrorCode::FAULT; + } + return {}; +} + +orbis::SysResult open(orbis::Thread *thread, orbis::ptr path, + orbis::sint flags, orbis::sint mode) { + std::printf("sys_open(%s)\n", path); + orbis::Ref instance; + auto result = rx::vfs::open(path, flags, mode, &instance); + if (result.isError()) { + return result; + } + + thread->retval[0] = thread->tproc->fileDescriptors.insert(instance); + return {}; +} + +orbis::SysResult close(orbis::Thread *thread, orbis::sint fd) { + if (!thread->tproc->fileDescriptors.remove(fd)) { + return ErrorCode::BADF; + } + + return {}; +} + +#define IOCPARM_SHIFT 13 /* number of bits for ioctl size */ +#define IOCPARM_MASK ((1 << IOCPARM_SHIFT) - 1) /* parameter length mask */ +#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK) +#define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << 16)) +#define IOCGROUP(x) (((x) >> 8) & 0xff) + +#define IOCPARM_MAX (1 << IOCPARM_SHIFT) /* max size of ioctl */ +#define IOC_VOID 0x20000000 /* no parameters */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOC_INOUT (IOC_IN | IOC_OUT) +#define IOC_DIRMASK (IOC_VOID | IOC_OUT | IOC_IN) + +#define _IOC(inout, group, num, len) \ + ((unsigned long)((inout) | (((len)&IOCPARM_MASK) << 16) | ((group) << 8) | \ + (num))) +#define _IO(g, n) _IOC(IOC_VOID, (g), (n), 0) +#define _IOWINT(g, n) _IOC(IOC_VOID, (g), (n), sizeof(int)) +#define _IOR(g, n, t) _IOC(IOC_OUT, (g), (n), sizeof(t)) +#define _IOW(g, n, t) _IOC(IOC_IN, (g), (n), sizeof(t)) +/* this should be _IORW, but stdio got there first */ +#define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) + +static std::string iocGroupToString(unsigned iocGroup) { + if (iocGroup >= 128) { + const char *sceGroups[] = { + "DEV", + "DMEM", + "GC", + "DCE", + "UVD", + "VCE", + "DBGGC", + "TWSI", + "MDBG", + "DEVENV", + "AJM", + "TRACE", + "IBS", + "MBUS", + "HDMI", + "CAMERA", + "FAN", + "THERMAL", + "PFS", + "ICC_CONFIG", + "IPC", + "IOSCHED", + "ICC_INDICATOR", + "EXFATFS", + "ICC_NVS", + "DVE", + "ICC_POWER", + "AV_CONTROL", + "ICC_SC_CONFIGURATION", + "ICC_DEVICE_POWER", + "SSHOT", + "DCE_SCANIN", + "FSCTRL", + "HMD", + "SHM", + "PHYSHM", + "HMDDFU", + "BLUETOOTH_HID", + "SBI", + "S3DA", + "SPM", + "BLOCKPOOL", + "SDK_EVENTLOG", + }; + + if (iocGroup - 127 >= std::size(sceGroups)) { + return "'?'"; + } + + return sceGroups[iocGroup - 127]; + } + + if (isprint(iocGroup)) { + return "'" + std::string(1, (char)iocGroup) + "'"; + } + + return "'?'"; +} + +static void printIoctl(unsigned long arg) { + std::printf("0x%lx { IO%s%s %lu(%s), %lu, %lu }\n", arg, + arg & IOC_OUT ? "R" : "", arg & IOC_IN ? "W" : "", IOCGROUP(arg), + iocGroupToString(IOCGROUP(arg)).c_str(), arg & 0xFF, + IOCPARM_LEN(arg)); +} + +static void ioctlToStream(std::ostream &stream, unsigned long arg) { + stream << "0x" << std::hex << arg << " { IO"; + + if ((arg & IOC_OUT) != 0) { + stream << 'R'; + } + + if ((arg & IOC_IN) != 0) { + stream << 'W'; + } + if ((arg & IOC_VOID) != 0) { + stream << 'i'; + } + + stream << " 0x" << IOCGROUP(arg); + stream << "('" << iocGroupToString(IOCGROUP(arg)) << "'), "; + stream << std::dec << (arg & 0xFF) << ", " << IOCPARM_LEN(arg) << " }"; +} + +static std::string ioctlToString(unsigned long arg) { + std::ostringstream stream; + ioctlToStream(stream, arg); + return std::move(stream).str(); +} + +orbis::SysResult ioctl(orbis::Thread *thread, orbis::sint fd, orbis::ulong com, + orbis::caddr_t argp) { + std::printf("ioctl: %s\n", ioctlToString(com).c_str()); + + Ref handle = + static_cast(thread->tproc->fileDescriptors.get(fd)); + if (handle == nullptr) { + return ErrorCode::BADF; + } + + auto result = handle->ioctl(handle.get(), com, argp); + + if (result < 0) { + // TODO + return ErrorCode::IO; + } + + thread->retval[0] = result; + return {}; +} +orbis::SysResult write(orbis::Thread *thread, orbis::sint fd, + orbis::ptr data, orbis::ulong size) { + Ref handle = + static_cast(thread->tproc->fileDescriptors.get(fd)); + if (handle == nullptr) { + return ErrorCode::BADF; + } + + auto result = handle->write(handle.get(), data, size); + + if (result < 0) { + // TODO + return ErrorCode::IO; + } + + thread->retval[0] = result; + return {}; +} +orbis::SysResult read(orbis::Thread *thread, orbis::sint fd, + orbis::ptr data, orbis::ulong size) { + return ErrorCode::NOTSUP; +} +orbis::SysResult pread(orbis::Thread *thread, orbis::sint fd, + orbis::ptr data, orbis::ulong size, + orbis::ulong offset) { + return ErrorCode::NOTSUP; +} +orbis::SysResult pwrite(orbis::Thread *thread, orbis::sint fd, + orbis::ptr data, orbis::ulong size, + orbis::ulong offset) { + return ErrorCode::NOTSUP; +} +orbis::SysResult lseek(orbis::Thread *thread, orbis::sint fd, + orbis::ulong offset, orbis::sint whence) { + return ErrorCode::NOTSUP; +} +orbis::SysResult ftruncate(orbis::Thread *thread, orbis::sint fd, + orbis::off_t length) { + return ErrorCode::NOTSUP; +} +orbis::SysResult truncate(orbis::Thread *thread, orbis::ptr path, + orbis::off_t length) { + return ErrorCode::NOTSUP; +} + +orbis::SysResult dynlib_get_obj_member(orbis::Thread *thread, + orbis::ModuleHandle handle, + orbis::uint64_t index, + orbis::ptr> addrp) { + auto module = thread->tproc->modulesMap.get(handle); + + if (module == nullptr) { + return ErrorCode::INVAL; + } + + switch (index) { + case 1: + *addrp = module->initProc; + return {}; + + case 8: + *addrp = module->moduleParam; + return {}; + } + + return ErrorCode::INVAL; +} + +ptr findSymbolById(orbis::Module *module, std::uint64_t id) { + for (auto sym : module->symbols) { + if (sym.id == id && sym.bind != orbis::SymbolBind::Local) { + return (ptr)module->base + sym.address; + } + } + + return nullptr; +} + +orbis::SysResult dynlib_dlsym(orbis::Thread *thread, orbis::ModuleHandle handle, + orbis::ptr symbol, + orbis::ptr> addrp) { + std::printf("sys_dynlib_dlsym(%u, '%s')\n", (unsigned)handle, symbol); + auto module = thread->tproc->modulesMap.get(handle); + + if (module == nullptr) { + return ErrorCode::INVAL; + } + + std::string_view symView(symbol); + + if (symView.size() == 11) { + if (auto addr = findSymbolById(module, rx::linker::decodeNid(symView))) { + *addrp = addr; + return {}; + } + } + + if (auto addr = findSymbolById(module, rx::linker::encodeFid(symView))) { + *addrp = addr; + return {}; + } + + return ErrorCode::NOENT; +} +orbis::SysResult dynlib_do_copy_relocations(orbis::Thread *thread) { + // TODO + return {}; +} +orbis::SysResult dynlib_load_prx(orbis::Thread *thread, + orbis::ptr name, + orbis::uint64_t arg1, + orbis::ptr pHandle, + orbis::uint64_t arg3) { + std::printf("sys_dynlib_load_prx: %s\n", name); + auto module = rx::linker::loadModuleFile(name, thread->tproc); + thread->tproc->ops->processNeeded(thread); + auto result = module->relocate(thread->tproc); + if (result.isError()) { + thread->tproc->modulesMap.remove(module->id); + return result; + } + + *pHandle = module->id; + return {}; +} +orbis::SysResult dynlib_unload_prx(orbis::Thread *thread, + orbis::ModuleHandle handle) { + return ErrorCode::NOTSUP; +} + +SysResult thr_create(orbis::Thread *thread, orbis::ptr ctxt, + ptr arg, orbis::sint flags) { + return ErrorCode::NOTSUP; +} +SysResult thr_new(orbis::Thread *thread, orbis::ptr param, + orbis::sint param_size) { + return {}; +} +SysResult thr_exit(orbis::Thread *thread, orbis::ptr state) { + return ErrorCode::NOTSUP; +} +SysResult thr_kill(orbis::Thread *thread, orbis::slong id, orbis::sint sig) { + return ErrorCode::NOTSUP; +} +SysResult thr_kill2(orbis::Thread *thread, orbis::pid_t pid, orbis::slong id, + orbis::sint sig) { + return ErrorCode::NOTSUP; +} +SysResult thr_suspend(orbis::Thread *thread, + orbis::ptr timeout) { + return ErrorCode::NOTSUP; +} +SysResult thr_wake(orbis::Thread *thread, orbis::slong id) { + return ErrorCode::NOTSUP; +} +SysResult thr_set_name(orbis::Thread *thread, orbis::slong id, + orbis::ptr name) { + return ErrorCode::NOTSUP; +} +orbis::SysResult exit(orbis::Thread *thread, orbis::sint status) { + std::printf("Requested exit with status %d\n", status); + std::exit(status); +} + +SysResult processNeeded(Thread *thread) { + while (true) { + std::set allNeededModules; + auto proc = thread->tproc; + std::map loadedModules; + + for (auto [id, module] : proc->modulesMap) { + for (auto mod : module->neededModules) { + allNeededModules.insert(mod.name); + } + + loadedModules[module->name] = module; + } + + bool hasLoadedNeeded = false; + + for (auto &needed : allNeededModules) { + if (auto it = loadedModules.find(needed); it != loadedModules.end()) { + continue; + } + + hasLoadedNeeded = true; + + std::printf("loading needed: %s\n", needed.c_str()); + bool isLoaded = false; + for (auto path : {"/system/common/lib/", "/system/priv/lib/"}) { + auto loadedNeeded = rx::linker::loadModuleFile( + (std::string(path) + needed + ".sprx").c_str(), proc); + + if (loadedNeeded == nullptr) { + continue; + } + + isLoaded = true; + break; + } + + if (!isLoaded) { + std::printf("Needed '%s' not found\n", needed.c_str()); + return ErrorCode::NOENT; + } + } + + if (!hasLoadedNeeded) { + thread->tproc->modulesMap.walk( + [&loadedModules](ModuleHandle modId, Module *module) { + // std::printf("Module '%s' has id %u\n", module->name, + // (unsigned)modId); + + module->importedModules.clear(); + module->importedModules.reserve(module->neededModules.size()); + for (auto mod : module->neededModules) { + module->importedModules.push_back(loadedModules.at(mod.name)); + } + }); + + break; + } + } + + return {}; +} + +SysResult registerEhFrames(Thread *thread) { + for (auto [id, module] : thread->tproc->modulesMap) { + __register_frame(module->ehFrameHdr); + } + + return {}; +} +} // namespace + +ProcessOps rx::procOpsTable = { + .mmap = mmap, + .munmap = munmap, + .msync = msync, + .mprotect = mprotect, + .minherit = minherit, + .madvise = madvise, + .mincore = mincore, + .mlock = mlock, + .mlockall = mlockall, + .munlockall = munlockall, + .munlock = munlock, + .virtual_query = virtual_query, + .open = open, + .close = close, + .ioctl = ioctl, + .write = write, + .read = read, + .pread = pread, + .pwrite = pwrite, + .lseek = lseek, + .ftruncate = ftruncate, + .truncate = truncate, + .dynlib_get_obj_member = dynlib_get_obj_member, + .dynlib_dlsym = dynlib_dlsym, + .dynlib_do_copy_relocations = dynlib_do_copy_relocations, + .dynlib_load_prx = dynlib_load_prx, + .dynlib_unload_prx = dynlib_unload_prx, + .thr_create = thr_create, + .thr_new = thr_new, + .thr_exit = thr_exit, + .thr_kill = thr_kill, + .thr_kill2 = thr_kill2, + .thr_suspend = thr_suspend, + .thr_wake = thr_wake, + .thr_set_name = thr_set_name, + .exit = exit, + .processNeeded = processNeeded, + .registerEhFrames = registerEhFrames, +}; diff --git a/rpcsx-os/ops.hpp b/rpcsx-os/ops.hpp new file mode 100644 index 0000000..52f7b96 --- /dev/null +++ b/rpcsx-os/ops.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "orbis/thread/ProcessOps.hpp" + +namespace rx { +extern orbis::ProcessOps procOpsTable; +} diff --git a/rpcsx-os/orbis-kernel-config/orbis-config.hpp b/rpcsx-os/orbis-kernel-config/orbis-config.hpp new file mode 100644 index 0000000..ca50da2 --- /dev/null +++ b/rpcsx-os/orbis-kernel-config/orbis-config.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include "orbis/error.hpp" +#include "orbis/thread/RegisterId.hpp" +#include +#include +#include +#include + +namespace orbis { +using int8_t = std::int8_t; +using int16_t = std::int16_t; +using int32_t = std::int32_t; +using int64_t = std::int64_t; + +using uint8_t = std::uint8_t; +using uint16_t = std::uint16_t; +using uint32_t = std::uint32_t; +using uint64_t = std::uint64_t; + +using size_t = uint64_t; +using ssize_t = int64_t; +using off_t = int64_t; + +using uint = uint32_t; +using sint = int32_t; + +using slong = int64_t; +using ulong = uint64_t; + +template using ptr = T *; +template using cptr = T * const; + +using caddr_t = ptr; + +inline ErrorCode uread(void *kernelAddress, ptr userAddress, + size_t size) { + std::memcpy(kernelAddress, userAddress, size); + return {}; +} + +inline ErrorCode uwrite(ptr userAddress, const void *kernelAddress, + size_t size) { + std::memcpy(userAddress, kernelAddress, size); + return {}; +} + +inline ErrorCode ureadString(char *kernelAddress, size_t kernelSize, ptr userAddress) { + std::strncpy(kernelAddress, userAddress, kernelSize); + if (kernelAddress[kernelSize - 1] != '\0') { + kernelAddress[kernelSize - 1] = '\0'; + return ErrorCode::NAMETOOLONG; + } + + return {}; +} + + +template T uread(ptr pointer) { + T result; + uread(&result, pointer, sizeof(T)); + return result; +} + +template void uwrite(ptr pointer, T data) { + uwrite(pointer, &data, sizeof(T)); +} + +inline uint64_t readRegister(void *context, RegisterId id) { + auto c = &reinterpret_cast(context)->uc_mcontext; + switch (id) { + case RegisterId::r15: return c->gregs[REG_R15]; + case RegisterId::r14: return c->gregs[REG_R14]; + case RegisterId::r13: return c->gregs[REG_R13]; + case RegisterId::r12: return c->gregs[REG_R12]; + case RegisterId::r11: return c->gregs[REG_R11]; + case RegisterId::r10: return c->gregs[REG_R10]; + case RegisterId::r9: return c->gregs[REG_R9]; + case RegisterId::r8: return c->gregs[REG_R8]; + case RegisterId::rdi: return c->gregs[REG_RDI]; + case RegisterId::rsi: return c->gregs[REG_RSI]; + case RegisterId::rbp: return c->gregs[REG_RBP]; + case RegisterId::rbx: return c->gregs[REG_RBX]; + case RegisterId::rdx: return c->gregs[REG_RDX]; + case RegisterId::rcx: return c->gregs[REG_RCX]; + case RegisterId::rax: return c->gregs[REG_RAX]; + case RegisterId::rsp: return c->gregs[REG_RSP]; + case RegisterId::rflags: return c->gregs[REG_EFL]; + } +} + +inline void writeRegister(void *context, RegisterId id, uint64_t value) { + auto c = &reinterpret_cast(context)->uc_mcontext; + switch (id) { + case RegisterId::r15: c->gregs[REG_R15] = value; return; + case RegisterId::r14: c->gregs[REG_R14] = value; return; + case RegisterId::r13: c->gregs[REG_R13] = value; return; + case RegisterId::r12: c->gregs[REG_R12] = value; return; + case RegisterId::r11: c->gregs[REG_R11] = value; return; + case RegisterId::r10: c->gregs[REG_R10] = value; return; + case RegisterId::r9: c->gregs[REG_R9] = value; return; + case RegisterId::r8: c->gregs[REG_R8] = value; return; + case RegisterId::rdi: c->gregs[REG_RDI] = value; return; + case RegisterId::rsi: c->gregs[REG_RSI] = value; return; + case RegisterId::rbp: c->gregs[REG_RBP] = value; return; + case RegisterId::rbx: c->gregs[REG_RBX] = value; return; + case RegisterId::rdx: c->gregs[REG_RDX] = value; return; + case RegisterId::rcx: c->gregs[REG_RCX] = value; return; + case RegisterId::rax: c->gregs[REG_RAX] = value; return; + case RegisterId::rsp: c->gregs[REG_RSP] = value; return; + case RegisterId::rflags: c->gregs[REG_EFL] = value; return; + } +} + +} // namespace orbis diff --git a/rpcsx-os/vfs.cpp b/rpcsx-os/vfs.cpp new file mode 100644 index 0000000..b3bf2cc --- /dev/null +++ b/rpcsx-os/vfs.cpp @@ -0,0 +1,64 @@ + + +#include "vfs.hpp" +#include "io-device.hpp" +#include "orbis/error/ErrorCode.hpp" +#include "orbis/error/SysResult.hpp" +#include +#include +#include + +static std::map> sMountsMap; + +void rx::vfs::initialize() {} +void rx::vfs::deinitialize() { + sMountsMap.clear(); +} + +orbis::SysResult rx::vfs::mount(const std::filesystem::path &guestPath, IoDevice *dev) { + auto [it, inserted] = + sMountsMap.emplace(guestPath.lexically_normal().string(), dev); + + if (!inserted) { + return orbis::ErrorCode::EXIST; + } + + return {}; +} + +orbis::SysResult rx::vfs::open(std::string_view path, int flags, int mode, + orbis::Ref *instance) { + orbis::Ref device; + bool isCharacterDevice = path.starts_with("/dev/"); + + for (auto &mount : sMountsMap) { + if (!path.starts_with(mount.first)) { + continue; + } + + path.remove_prefix(mount.first.length()); + device = mount.second; + break; + } + + if (isCharacterDevice && device != nullptr) { + if (!path.empty()) { + std::fprintf(stderr, + "vfs::open: access to character device subentry '%s' (%s)\n", + path.data(), std::string(path).c_str()); + return orbis::ErrorCode::NOENT; + } + } + + if (device != nullptr) { + return (orbis::ErrorCode)device->open( + device.get(), instance, std::string(path).c_str(), flags, mode); + } + + if (isCharacterDevice) { + std::fprintf(stderr, "vfs::open: character device '%s' not found.\n", + std::string(path).c_str()); + } + + return orbis::ErrorCode::NOENT; +} diff --git a/rpcsx-os/vfs.hpp b/rpcsx-os/vfs.hpp new file mode 100644 index 0000000..735c2d8 --- /dev/null +++ b/rpcsx-os/vfs.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "orbis/error/SysResult.hpp" +#include "orbis/utils/Rc.hpp" +#include + +struct IoDevice; +struct IoDeviceInstance; + +namespace rx::vfs { +void initialize(); +void deinitialize(); +orbis::SysResult mount(const std::filesystem::path &guestPath, IoDevice *dev); +orbis::SysResult open(std::string_view path, int flags, int mode, + orbis::Ref *instance); +} // namespace vfs diff --git a/rpcsx-os/vm.cpp b/rpcsx-os/vm.cpp new file mode 100644 index 0000000..8e8ce96 --- /dev/null +++ b/rpcsx-os/vm.cpp @@ -0,0 +1,923 @@ +#include "vm.hpp" +#include "align.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace utils { +namespace { +void *map(void *address, std::size_t size, int prot, int flags, int fd = -1, + off_t offset = 0) { + return ::mmap(address, size, prot, flags, fd, offset); +} + +void *reserve(std::size_t size) { + return map(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS); +} + +bool reserve(void *address, std::size_t size) { + return map(address, size, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) != MAP_FAILED; +} + +bool protect(void *address, std::size_t size, int prot) { + return ::mprotect(address, size, prot) == 0; +} + +bool unmap(void *address, std::size_t size) { + return ::munmap(address, size) == 0; +} +} // namespace +} // namespace utils + +std::string rx::vm::mapFlagsToString(std::int32_t flags) { + std::string result; + + if ((flags & kMapFlagShared) == kMapFlagShared) { + if (!result.empty()) { + result += " | "; + } + + result += "Shared"; + flags &= ~kMapFlagShared; + } + if ((flags & kMapFlagPrivate) == kMapFlagPrivate) { + if (!result.empty()) { + result += " | "; + } + + result += "Private"; + flags &= ~kMapFlagPrivate; + } + if ((flags & kMapFlagFixed) == kMapFlagFixed) { + if (!result.empty()) { + result += " | "; + } + + result += "Fixed"; + flags &= ~kMapFlagFixed; + } + if ((flags & kMapFlagRename) == kMapFlagRename) { + if (!result.empty()) { + result += " | "; + } + + result += "Rename"; + flags &= ~kMapFlagRename; + } + if ((flags & kMapFlagNoReserve) == kMapFlagNoReserve) { + if (!result.empty()) { + result += " | "; + } + + result += "NoReserve"; + flags &= ~kMapFlagNoReserve; + } + if ((flags & kMapFlagNoOverwrite) == kMapFlagNoOverwrite) { + if (!result.empty()) { + result += " | "; + } + + result += "NoOverwrite"; + flags &= ~kMapFlagNoOverwrite; + } + if ((flags & kMapFlagVoid) == kMapFlagVoid) { + if (!result.empty()) { + result += " | "; + } + + result += "Void"; + flags &= ~kMapFlagVoid; + } + if ((flags & kMapFlagHasSemaphore) == kMapFlagHasSemaphore) { + if (!result.empty()) { + result += " | "; + } + + result += "HasSemaphore"; + flags &= ~kMapFlagHasSemaphore; + } + if ((flags & kMapFlagStack) == kMapFlagStack) { + if (!result.empty()) { + result += " | "; + } + + result += "Stack"; + flags &= ~kMapFlagStack; + } + if ((flags & kMapFlagNoSync) == kMapFlagNoSync) { + if (!result.empty()) { + result += " | "; + } + + result += "NoSync"; + flags &= ~kMapFlagNoSync; + } + if ((flags & kMapFlagAnonymous) == kMapFlagAnonymous) { + if (!result.empty()) { + result += " | "; + } + + result += "Anonymous"; + flags &= ~kMapFlagAnonymous; + } + if ((flags & kMapFlagSystem) == kMapFlagSystem) { + if (!result.empty()) { + result += " | "; + } + + result += "System"; + flags &= ~kMapFlagSystem; + } + if ((flags & kMapFlagAllAvaiable) == kMapFlagAllAvaiable) { + if (!result.empty()) { + result += " | "; + } + + result += "AllAvaiable"; + flags &= ~kMapFlagAllAvaiable; + } + if ((flags & kMapFlagNoCore) == kMapFlagNoCore) { + if (!result.empty()) { + result += " | "; + } + + result += "NoCore"; + flags &= ~kMapFlagNoCore; + } + if ((flags & kMapFlagPrefaultRead) == kMapFlagPrefaultRead) { + if (!result.empty()) { + result += " | "; + } + + result += "PrefaultRead"; + flags &= ~kMapFlagPrefaultRead; + } + if ((flags & kMapFlagSelf) == kMapFlagSelf) { + if (!result.empty()) { + result += " | "; + } + + result += "Self"; + flags &= ~kMapFlagSelf; + } + + auto alignment = (flags & kMapFlagsAlignMask) >> kMapFlagsAlignShift; + flags &= ~kMapFlagsAlignMask; + + if (alignment != 0) { + if (!result.empty()) { + result += " | "; + } + + result += "Alignment(" + std::to_string(alignment) + ")"; + } + + if (flags != 0) { + if (!result.empty()) { + result += " | "; + } + + result += std::to_string(flags); + } + + return result; +} + +std::string rx::vm::mapProtToString(std::int32_t prot) { + std::string result; + + if ((prot & kMapProtCpuRead) == kMapProtCpuRead) { + if (!result.empty()) { + result += " | "; + } + result += "CpuRead"; + prot &= ~kMapProtCpuRead; + } + if ((prot & kMapProtCpuWrite) == kMapProtCpuWrite) { + if (!result.empty()) { + result += " | "; + } + result += "CpuWrite"; + prot &= ~kMapProtCpuWrite; + } + if ((prot & kMapProtCpuExec) == kMapProtCpuExec) { + if (!result.empty()) { + result += " | "; + } + result += "CpuExec"; + prot &= ~kMapProtCpuExec; + } + if ((prot & kMapProtGpuRead) == kMapProtGpuRead) { + if (!result.empty()) { + result += " | "; + } + result += "GpuRead"; + prot &= ~kMapProtGpuRead; + } + if ((prot & kMapProtGpuWrite) == kMapProtGpuWrite) { + if (!result.empty()) { + result += " | "; + } + result += "GpuWrite"; + prot &= ~kMapProtGpuWrite; + } + + if (prot != 0) { + if (!result.empty()) { + result += " | "; + } + + result += std::to_string(prot); + } + + return result; +} + +static constexpr std::uint64_t kPageMask = rx::vm::kPageSize - 1; +static constexpr std::uint64_t kBlockShift = 32; +static constexpr std::uint64_t kBlockSize = static_cast(1) + << kBlockShift; +static constexpr std::uint64_t kBlockMask = kBlockSize - 1; +static constexpr std::uint64_t kPagesInBlock = kBlockSize / rx::vm::kPageSize; +static constexpr std::uint64_t kFirstBlock = 0x00; +static constexpr std::uint64_t kLastBlock = 0xff; +static constexpr std::uint64_t kBlockCount = kLastBlock - kFirstBlock + 1; +static constexpr std::uint64_t kGroupSize = 64; +static constexpr std::uint64_t kGroupMask = kGroupSize - 1; +static constexpr std::uint64_t kGroupsInBlock = kPagesInBlock / kGroupSize; +static constexpr std::uint64_t kMinAddress = + kFirstBlock * kBlockSize + rx::vm::kPageSize * 0x10; +static constexpr std::uint64_t kMaxAddress = (kLastBlock + 1) * kBlockSize - 1; +static constexpr std::uint64_t kMemorySize = kBlockCount * kBlockSize; + +static int gMemoryShm = -1; + +struct Group { + std::uint64_t allocated; + std::uint64_t readable; + std::uint64_t writable; + std::uint64_t executable; + std::uint64_t gpuReadable; + std::uint64_t gpuWritable; +}; + +enum { + kReadable = rx::vm::kMapProtCpuRead, + kWritable = rx::vm::kMapProtCpuWrite, + kExecutable = rx::vm::kMapProtCpuExec, + kGpuReadable = rx::vm::kMapProtGpuRead, + kGpuWritable = rx::vm::kMapProtGpuWrite, + + kAllocated = 1 << 3, +}; + +inline constexpr std::uint64_t makePagesMask(std::uint64_t page, + std::uint64_t count) { + if (count == 64) { + return ~0ull << page; + } + + return ((1ull << count) - 1ull) << page; +} +struct Block { + Group groups[kGroupsInBlock]; + + void setFlags(std::uint64_t firstPage, std::uint64_t pagesCount, + std::uint32_t flags) { + modifyFlags(firstPage, pagesCount, flags, ~static_cast(0)); + } + + void addFlags(std::uint64_t firstPage, std::uint64_t pagesCount, + std::uint32_t flags) { + modifyFlags(firstPage, pagesCount, flags, 0); + } + + void removeFlags(std::uint64_t firstPage, std::uint64_t pagesCount, + std::uint32_t flags) { + modifyFlags(firstPage, pagesCount, 0, flags); + } + + void modifyFlags(std::uint64_t firstPage, std::uint64_t pagesCount, + std::uint32_t addFlags, std::uint32_t removeFlags) { + std::uint64_t groupIndex = firstPage / kGroupSize; + + std::uint64_t addAllocatedFlags = + (addFlags & kAllocated) ? ~static_cast(0) : 0; + std::uint64_t addReadableFlags = + (addFlags & kReadable) ? ~static_cast(0) : 0; + std::uint64_t addWritableFlags = + (addFlags & kWritable) ? ~static_cast(0) : 0; + std::uint64_t addExecutableFlags = + (addFlags & kExecutable) ? ~static_cast(0) : 0; + std::uint64_t addGpuReadableFlags = + (addFlags & kGpuReadable) ? ~static_cast(0) : 0; + std::uint64_t addGpuWritableFlags = + (addFlags & kGpuWritable) ? ~static_cast(0) : 0; + + std::uint64_t removeAllocatedFlags = + (removeFlags & kAllocated) ? ~static_cast(0) : 0; + std::uint64_t removeReadableFlags = + (removeFlags & kReadable) ? ~static_cast(0) : 0; + std::uint64_t removeWritableFlags = + (removeFlags & kWritable) ? ~static_cast(0) : 0; + std::uint64_t removeExecutableFlags = + (removeFlags & kExecutable) ? ~static_cast(0) : 0; + std::uint64_t removeGpuReadableFlags = + (removeFlags & kGpuReadable) ? ~static_cast(0) : 0; + std::uint64_t removeGpuWritableFlags = + (removeFlags & kGpuWritable) ? ~static_cast(0) : 0; + + if ((firstPage & kGroupMask) != 0) { + auto count = kGroupSize - (firstPage & kGroupMask); + + if (count > pagesCount) { + count = pagesCount; + } + + auto mask = makePagesMask(firstPage, count); + pagesCount -= count; + + auto &group = groups[groupIndex++]; + + group.allocated = (group.allocated & ~(removeAllocatedFlags & mask)) | + (addAllocatedFlags & mask); + group.readable = (group.readable & ~(removeReadableFlags & mask)) | + (addReadableFlags & mask); + group.writable = (group.writable & ~(removeWritableFlags & mask)) | + (addWritableFlags & mask); + group.executable = (group.executable & ~(removeExecutableFlags & mask)) | + (addExecutableFlags & mask); + group.gpuReadable = + (group.gpuReadable & ~(removeGpuReadableFlags & mask)) | + (addGpuReadableFlags & mask); + group.gpuWritable = + (group.gpuWritable & ~(removeGpuWritableFlags & mask)) | + (addGpuWritableFlags & mask); + } + + while (pagesCount >= kGroupSize) { + pagesCount -= kGroupSize; + + auto &group = groups[groupIndex++]; + + group.allocated = + (group.allocated & ~removeAllocatedFlags) | addAllocatedFlags; + group.readable = + (group.readable & ~removeReadableFlags) | addReadableFlags; + group.writable = + (group.writable & ~removeWritableFlags) | addWritableFlags; + group.executable = + (group.executable & ~removeExecutableFlags) | addExecutableFlags; + group.gpuReadable = + (group.gpuReadable & ~removeGpuReadableFlags) | addGpuReadableFlags; + group.gpuWritable = + (group.gpuWritable & ~removeGpuWritableFlags) | addGpuWritableFlags; + } + + if (pagesCount > 0) { + auto mask = makePagesMask(0, pagesCount); + auto &group = groups[groupIndex++]; + + group.allocated = (group.allocated & ~(removeAllocatedFlags & mask)) | + (addAllocatedFlags & mask); + group.readable = (group.readable & ~(removeReadableFlags & mask)) | + (addReadableFlags & mask); + group.writable = (group.writable & ~(removeWritableFlags & mask)) | + (addWritableFlags & mask); + group.executable = (group.executable & ~(removeExecutableFlags & mask)) | + (addExecutableFlags & mask); + group.gpuReadable = + (group.gpuReadable & ~(removeGpuReadableFlags & mask)) | + (addGpuReadableFlags & mask); + group.gpuWritable = + (group.gpuWritable & ~(removeGpuWritableFlags & mask)) | + (addGpuWritableFlags & mask); + } + } + + bool isFreePages(std::uint64_t page, std::uint64_t count) { + auto groupIndex = page / kGroupSize; + + std::uint64_t foundCount = 0; + + { + auto pageInGroup = page % kGroupSize; + auto allocatedBits = groups[groupIndex].allocated; + auto freePages = std::countr_zero(allocatedBits >> pageInGroup); + + if (freePages < count && freePages + pageInGroup < kGroupSize) { + return false; + } + + foundCount += freePages; + } + + for (++groupIndex; groupIndex < kGroupsInBlock && foundCount < count; + ++groupIndex) { + auto allocatedBits = groups[groupIndex].allocated; + auto freePages = std::countr_zero(allocatedBits); + foundCount += freePages; + + if (freePages != kGroupSize) { + break; + } + } + + return foundCount >= count; + } + + std::uint64_t findFreePages(std::uint64_t count, std::uint64_t alignment) { + std::uint64_t foundCount = 0; + std::uint64_t foundPage = 0; + + if (alignment < kGroupSize * rx::vm::kPageSize) { + std::uint64_t groupAlignment = alignment >> rx::vm::kPageShift; + + for (std::uint64_t groupIndex = 0; + groupIndex < kGroupsInBlock && foundCount < count; ++groupIndex) { + auto allocatedBits = groups[groupIndex].allocated; + + if (foundCount != 0) { + // we already found block with free pages at the end + if (count - foundCount >= kGroupSize) { + // we need whole group. if it not empty, we need to try next range + if (allocatedBits != 0) { + foundCount = 0; + } else { + foundCount += kGroupSize; + } + } else { + if (allocatedBits == 0) { + // whole group is clear, fast path + foundCount += kGroupSize; + break; + } else { + // add free pages from beginning of the current group + foundCount += std::countr_zero(allocatedBits); + + if (foundCount >= count) { + break; + } + + // not enough free pages, need to try next range + foundCount = 0; + } + } + } + + if (foundCount == 0) { + if (~allocatedBits == 0) { + continue; + } + + if (count < kGroupSize) { + // For small allocations try to find free room from beggining of + // group + auto tmpAllocatedBits = allocatedBits; + std::uint64_t processedPages = 0; + + while (processedPages < kGroupSize) { + auto freeCount = std::countr_zero(tmpAllocatedBits); + if (freeCount + processedPages > kGroupSize) { + freeCount = kGroupSize - processedPages; + } + + processedPages += freeCount; + if (freeCount >= 64) { + tmpAllocatedBits = 0; + } else { + tmpAllocatedBits >>= freeCount; + } + + if (freeCount >= count || + (freeCount > 0 && processedPages >= kGroupSize)) { + foundPage = + groupIndex * kGroupSize + processedPages - freeCount; + foundCount = freeCount; + break; + } + + while (auto usedCount = std::countr_one(tmpAllocatedBits)) { + auto nextProcessedPages = + utils::alignUp(processedPages + usedCount, groupAlignment); + if (nextProcessedPages - processedPages >= 64) { + tmpAllocatedBits = 0; + } else { + tmpAllocatedBits >>= nextProcessedPages - processedPages; + } + processedPages = nextProcessedPages; + } + } + } else { + // this is big allocation, count free last pages in block, continue + // searching on next iterations + auto freeCount = std::countl_zero(allocatedBits); + auto alignedPageIndex = + utils::alignUp(kGroupSize - freeCount, groupAlignment); + freeCount = + kGroupSize - alignedPageIndex; // calc aligned free pages + + foundCount = freeCount; + foundPage = groupIndex * kGroupSize + alignedPageIndex; + } + } + } + } else { + std::uint64_t blockAlignment = + alignment / (kGroupSize * rx::vm::kPageSize); + + for (std::uint64_t groupIndex = 0; + groupIndex < kGroupsInBlock && foundCount < count; ++groupIndex) { + if (foundCount == 0) { + groupIndex = utils::alignUp(groupIndex, blockAlignment); + + if (groupIndex >= kGroupsInBlock) { + break; + } + } + + auto allocatedBits = groups[groupIndex].allocated; + + if (allocatedBits == 0) { + if (foundCount == 0) { + foundPage = groupIndex * kGroupSize; + } + + foundCount += kGroupSize; + } else { + if (foundCount == 0 && count < kGroupSize) { + auto freeCount = std::countr_zero(allocatedBits); + + if (freeCount >= count) { + foundPage = groupIndex * kGroupSize; + foundCount = freeCount; + break; + } + } + + foundCount = 0; + } + } + } + + if (foundCount >= count) { + assert(((foundPage << rx::vm::kPageShift) & (alignment - 1)) == 0); + return foundPage; + } + + return ~static_cast(0); + } +}; + +static Block gBlocks[kBlockCount]; + +static std::map> + gVirtualAllocations; + +static void reserve(std::uint64_t startAddress, std::uint64_t endAddress) { + auto blockIndex = startAddress >> kBlockShift; + + assert(endAddress > startAddress); + assert(blockIndex == (endAddress >> kBlockShift)); + + auto firstPage = (startAddress & kBlockMask) >> rx::vm::kPageShift; + auto pagesCount = + (endAddress - startAddress + (rx::vm::kPageSize - 1)) >> rx::vm::kPageShift; + + gBlocks[blockIndex - kFirstBlock].setFlags(firstPage, pagesCount, kAllocated); +} + +void rx::vm::initialize() { + std::printf("Memory: initialization\n"); + + gMemoryShm = ::shm_open("/orbis-memory", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + + if (gMemoryShm == -1) { + std::printf("Memory: failed to open /orbis-memory\n"); + std::abort(); + } + + if (::ftruncate64(gMemoryShm, kMemorySize) < 0) { + std::printf("Memory: failed to allocate /orbis-memory\n"); + std::abort(); + } + + std::uintptr_t virtualAddressStart = 0x40'0000; + std::uintptr_t virtualAddressEnd = 0xff'ffff'ffff; + + reserve(0, virtualAddressStart); // unmapped area + + utils::reserve(reinterpret_cast(virtualAddressStart), + virtualAddressEnd - virtualAddressStart + 1); + + // orbis::bridge.setUpSharedMemory(kMinAddress, kMemorySize, "/orbis-memory"); +} + +void rx::vm::uninitialize() { + std::printf("Memory: shutdown\n"); + ::close(gMemoryShm); + gMemoryShm = -1; + ::shm_unlink("/orbis-memory"); + + for (auto &block : gBlocks) { + block = {}; + } +} + +constexpr auto kPhysicalMemorySize = 5568ull * 1024 * 1024; +constexpr auto kFlexibleMemorySize = 448ull * 1024 * 1024; +constexpr auto kMainDirectMemorySize = + kPhysicalMemorySize - kFlexibleMemorySize; + +/* +std::uint64_t allocate(std::uint64_t phyAddress, std::uint64_t size, + std::uint64_t align, std::int32_t memType, + std::uint32_t blockFlags) { + // TODO + return 0; +} + +bool setMemoryRangeName(std::uint64_t phyAddress, std::uint64_t size, + const char *name) { + // TODO + return false; +} +*/ + +void *rx::vm::map(void *addr, std::uint64_t len, std::int32_t prot, + std::int32_t flags) { + std::printf("rx::vm::map(addr = %p, len = %" PRIu64 + ", prot = %s, flags = %s)\n", + addr, len, mapProtToString(prot).c_str(), + mapFlagsToString(flags).c_str()); + + auto pagesCount = (len + (kPageSize - 1)) >> kPageShift; + auto hitAddress = reinterpret_cast(addr); + + std::uint64_t alignment = (flags & kMapFlagsAlignMask) >> kMapFlagsAlignShift; + if (alignment == 0) { + alignment = kPageSize; + } else { + alignment = static_cast(1) << alignment; + } + + if (alignment < kPageSize) { + std::printf("Memory error: wrong alignment %" PRId64 "\n", alignment); + alignment = kPageSize; + } + + if (len > kBlockSize) { + std::printf("Memory error: too big allocation %" PRId64 " pages\n", + pagesCount); + return MAP_FAILED; + } + + flags &= ~kMapFlagsAlignMask; + + if (hitAddress & (alignment - 1)) { + if (flags & kMapFlagStack) { + hitAddress = utils::alignDown(hitAddress - 1, alignment); + flags |= kMapFlagFixed; + flags &= ~kMapFlagStack; + } else { + hitAddress = utils::alignUp(hitAddress, alignment); + } + } + + std::uint64_t address = 0; + if ((flags & kMapFlagFixed) == kMapFlagFixed) { + address = hitAddress; + + auto blockIndex = address >> kBlockShift; + + if (blockIndex < kFirstBlock || blockIndex > kLastBlock) { + std::printf("Memory error: fixed mapping with wrong address %" PRIx64 + " pages\n", + address); + return MAP_FAILED; + } + } else if (hitAddress != 0) { + auto blockIndex = hitAddress >> kBlockShift; + auto page = (hitAddress & kBlockMask) >> kPageShift; + + if (blockIndex < kFirstBlock || blockIndex > kLastBlock) { + std::printf("Memory error: wrong hit address %" PRIx64 " pages\n", + hitAddress); + hitAddress = 0; + } else { + blockIndex -= kFirstBlock; + + if (gBlocks[blockIndex].isFreePages(page, pagesCount)) { + address = hitAddress; + } + } + } + + static constexpr auto kBadAddress = ~static_cast(0); + + if (address == 0 && hitAddress != 0) { + auto hitBlockIndex = hitAddress >> kBlockShift; + for (auto blockIndex = hitBlockIndex; blockIndex <= kLastBlock; + ++blockIndex) { + auto pageAddress = gBlocks[blockIndex - kFirstBlock].findFreePages( + pagesCount, alignment); + + if (pageAddress != kBadAddress) { + address = (pageAddress << kPageShift) | (blockIndex * kBlockSize); + break; + } + } + } + + if (address == 0) { + // for (auto blockIndex = kFirstUserBlock; blockIndex <= kLastUserBlock; + // ++blockIndex) { + std::size_t blockIndex = 0; // system managed block + + auto pageAddress = + gBlocks[blockIndex - kFirstBlock].findFreePages(pagesCount, alignment); + + if (pageAddress != kBadAddress) { + address = (pageAddress << kPageShift) | (blockIndex * kBlockSize); + // break; + } + // } + } + + if (address == 0) { + std::printf("Memory error: no free memory left for mapping of %" PRId64 + " pages\n", + pagesCount); + return MAP_FAILED; + } + + if (address & (alignment - 1)) { + std::printf("Memory error: failed to map aligned address\n"); + std::abort(); + } + + if (address >= kMaxAddress || address > kMaxAddress - len) { + std::printf("Memory error: out of memory\n"); + std::abort(); + } + + gBlocks[(address >> kBlockShift) - kFirstBlock].setFlags( + (address & kBlockMask) >> kPageShift, pagesCount, + (flags & (kMapProtCpuAll | kMapProtGpuAll)) | kAllocated); + + int realFlags = MAP_FIXED | MAP_SHARED; + bool isAnon = (flags & kMapFlagAnonymous) == kMapFlagAnonymous; + flags &= ~(kMapFlagFixed | kMapFlagAnonymous); + + /* + if (flags & kMapFlagStack) { + realFlags |= MAP_GROWSDOWN | MAP_STACK | MAP_ANONYMOUS | MAP_PRIVATE; + offset = 0; + fd = -1; + flags &= ~kMapFlagStack; + } else { + realFlags |= MAP_SHARED; + } + */ + if (flags) { + std::printf(" unhandled flags 0x%" PRIx32 "\n", flags); + } + + auto &allocInfo = gVirtualAllocations[address]; + allocInfo.start = address; + allocInfo.end = address + len; + // allocInfo.offset = offset; // TODO + allocInfo.protection = prot; + allocInfo.memoryType = 3; // TODO + allocInfo.flags = kBlockFlagDirectMemory; // TODO + allocInfo.name[0] = '\0'; // TODO + + // orbis::bridge.sendMemoryProtect(address, len, prot); + auto result = + utils::map(reinterpret_cast(address), len, prot & kMapProtCpuAll, + realFlags, gMemoryShm, address - kMinAddress); + + if (result != MAP_FAILED && isAnon) { + bool needReprotect = (prot & PROT_WRITE) == 0; + if (needReprotect) { + ::mprotect(result, len, PROT_WRITE); + } + std::memset(result, 0, len); + if (needReprotect) { + ::mprotect(result, len, prot & kMapProtCpuAll); + } + } + + return result; +} + +bool rx::vm::unmap(void *addr, std::uint64_t size) { + auto pages = (size + (kPageSize - 1)) >> kPageShift; + auto address = reinterpret_cast(addr); + + if (address < kMinAddress || address >= kMaxAddress || size > kMaxAddress || + address > kMaxAddress - size) { + std::printf("Memory error: unmap out of memory\n"); + return false; + } + + if ((address & kPageMask) != 0) { + std::printf("Memory error: unmap unaligned address\n"); + return false; + } + + if ((address >> kBlockShift) != ((address + size - 1) >> kBlockShift)) { + std::printf( + "Memory error: unmap cross block range. address 0x%lx, size=0x%lx\n", + address, size); + __builtin_trap(); + } + + gBlocks[(address >> kBlockShift) - kFirstBlock].removeFlags( + (address & kBlockMask) >> kPageShift, pages, ~0); + // orbis::bridge.sendMemoryProtect(address, size, 0); + return utils::unmap(addr, size); +} + +bool rx::vm::protect(void *addr, std::uint64_t size, std::int32_t prot) { + std::printf("rx::vm::protect(addr = %p, len = %" PRIu64 ", prot = %s)\n", addr, + size, mapProtToString(prot).c_str()); + + auto pages = (size + (kPageSize - 1)) >> kPageShift; + auto address = reinterpret_cast(addr); + if (address < kMinAddress || address >= kMaxAddress || size > kMaxAddress || + address > kMaxAddress - size) { + std::printf("Memory error: protect out of memory\n"); + return false; + } + + if ((address & kPageMask) != 0) { + std::printf("Memory error: protect unaligned address\n"); + return false; + } + + if ((address >> kBlockShift) != ((address + size - 1) >> kBlockShift)) { + std::printf("Memory error: protect cross block range\n"); + std::abort(); + } + + gBlocks[(address >> kBlockShift) - kFirstBlock].setFlags( + (address & kBlockMask) >> kPageShift, pages, + kAllocated | (prot & (kMapProtCpuAll | kMapProtGpuAll))); + + // orbis::bridge.sendMemoryProtect(reinterpret_cast(addr), + // size, prot); + return ::mprotect(addr, size, prot & kMapProtCpuAll) == 0; +} + +bool rx::vm::queryProtection(const void *addr, std::uint64_t *startAddress, + std::uint64_t *endAddress, std::int64_t *prot) { + // TODO + return false; +} + +bool rx::vm::virtualQuery(const void *addr, std::int32_t flags, + VirtualQueryInfo *info) { + auto address = reinterpret_cast(addr); + + auto it = gVirtualAllocations.lower_bound(address); + + if (it == gVirtualAllocations.end()) { + return false; + } + + if ((flags & 1) == 0) { + if (it->second.end <= address) { + return false; + } + } else { + if (it->second.start > address || it->second.end <= address) { + return false; + } + } + + *info = it->second; + return true; +} + +void rx::vm::printHostStats() { + FILE *maps = fopen("/proc/self/maps", "r"); + + if (!maps) { + return; + } + + char *line = nullptr; + std::size_t size = 0; + while (getline(&line, &size, maps) > 0) { + std::printf("%s", line); + } + + free(line); +} diff --git a/rpcsx-os/vm.hpp b/rpcsx-os/vm.hpp new file mode 100644 index 0000000..9afb994 --- /dev/null +++ b/rpcsx-os/vm.hpp @@ -0,0 +1,74 @@ +#pragma once +#include +#include +#include + +namespace rx::vm { +static constexpr std::uint64_t kPageShift = 14; +static constexpr std::uint64_t kPageSize = static_cast(1) + << kPageShift; + +enum BlockFlags { + kBlockFlagFlexibleMemory = 1 << 0, + kBlockFlagDirectMemory = 1 << 1, + kBlockFlagStack = 1 << 2, + kBlockFlagPooledMemory = 1 << 3, + kBlockFlagCommited = 1 << 4, +}; + +enum MapFlags { + kMapFlagShared = 0x1, + kMapFlagPrivate = 0x2, + kMapFlagFixed = 0x10, + kMapFlagRename = 0x20, + kMapFlagNoReserve = 0x40, + kMapFlagNoOverwrite = 0x80, + kMapFlagVoid = 0x100, + kMapFlagHasSemaphore = 0x200, + kMapFlagStack = 0x400, + kMapFlagNoSync = 0x800, + kMapFlagAnonymous = 0x1000, + kMapFlagSystem = 0x2000, + kMapFlagAllAvaiable = 0x4000, + kMapFlagNoCore = 0x20000, + kMapFlagPrefaultRead = 0x40000, + kMapFlagSelf = 0x80000, +}; + +enum MapProt { + kMapProtCpuRead = 1, + kMapProtCpuWrite = 2, + kMapProtCpuExec = 4, + kMapProtCpuAll = 0x7, + kMapProtGpuRead = 0x10, + kMapProtGpuWrite = 0x20, + kMapProtGpuAll = 0x30, +}; + +struct VirtualQueryInfo { + uint64_t start; + uint64_t end; + uint64_t offset; + int32_t protection; + int32_t memoryType; + uint32_t flags; + char name[32]; +}; + +static constexpr std::uint32_t kMapFlagsAlignShift = 24; +static constexpr std::uint32_t kMapFlagsAlignMask = 0x1f << kMapFlagsAlignShift; + +std::string mapFlagsToString(std::int32_t flags); +std::string mapProtToString(std::int32_t prot); + +void printHostStats(); +void initialize(); +void uninitialize(); +void *map(void *addr, std::uint64_t len, std::int32_t prot, std::int32_t flags); +bool unmap(void *addr, std::uint64_t size); +bool protect(void *addr, std::uint64_t size, std::int32_t prot); + +bool virtualQuery(const void *addr, std::int32_t flags, VirtualQueryInfo *info); +bool queryProtection(const void *addr, std::uint64_t *startAddress, + std::uint64_t *endAddress, std::int64_t *prot); +}