Initial commit

This commit is contained in:
DH 2023-06-23 03:28:14 +03:00
commit 1fdadaaee9
38 changed files with 5512 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
build/
compile_commands.json

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "orbis-kernel"]
path = orbis-kernel
url = git@github.com:RPCSX/orbis-kernel.git

17
3rdparty/crypto/CMakeLists.txt vendored Normal file
View file

@ -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)

164
3rdparty/crypto/include/crypto/sha1.h vendored Normal file
View file

@ -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 <polarssl_maintainer at polarssl.org>
*
* 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 <string.h>
#ifdef _MSC_VER
#include <basetsd.h>
typedef UINT32 uint32_t;
#else
#include <inttypes.h>
#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

377
3rdparty/crypto/src/sha1.c vendored Normal file
View file

@ -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 <polarssl_maintainer at polarssl.org>
*
* 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));
}

9
CMakeLists.txt Normal file
View file

@ -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)

339
LICENSE Normal file
View file

@ -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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
<signature of Ty Coon>, 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.

1
orbis-kernel Submodule

@ -0,0 +1 @@
Subproject commit 05d35b71483880246bc4c1a28f857e9046af7c36

35
rpcsx-os/CMakeLists.txt Normal file
View file

@ -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)

14
rpcsx-os/align.hpp Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
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

131
rpcsx-os/io-device.cpp Normal file
View file

@ -0,0 +1,131 @@
#include "io-device.hpp"
#include <fcntl.h>
#include <string>
#include <unistd.h>
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<HostIoDeviceInstance *>(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<HostIoDeviceInstance *>(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<HostIoDeviceInstance *>(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<HostIoDeviceInstance *>(instance);
::close(hostIoInstance->hostFd);
return io_device_instance_close(instance);
}
static std::int32_t host_io_open(IoDevice *device, orbis::Ref<IoDeviceInstance> *instance,
const char *path, std::uint32_t flags,
std::uint32_t mode) {
auto hostDevice = static_cast<HostIoDevice *>(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;
}

56
rpcsx-os/io-device.hpp Normal file
View file

@ -0,0 +1,56 @@
#pragma once
#include "orbis/utils/Rc.hpp"
#include <cstdint>
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<IoDevice> 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<IoDeviceInstance> *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);

19
rpcsx-os/io-devices.hpp Normal file
View file

@ -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();

291
rpcsx-os/iodev/dce.cpp Normal file
View file

@ -0,0 +1,291 @@
#include "io-device.hpp"
#include <cinttypes>
#include <cstddef>
#include <cstdio>
#include <cstring>
#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 <typename T>
// 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<DceInstance *>(instance);
static std::uint64_t *bufferInUsePtr = nullptr;
if (request == 0xc0308203) {
// flip control
auto args = reinterpret_cast<FlipControlArgs *>(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<RegisterBuffer *>(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<RegisterBufferAttributeArgs *>(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<FlipRequestArgs *>(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<std::uint64_t *>(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<IoDeviceInstance> *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;
}

64
rpcsx-os/iodev/dipsw.cpp Normal file
View file

@ -0,0 +1,64 @@
#include "io-device.hpp"
#include <cstdio>
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<std::uint32_t *>(argp) = 0;
return 0;
}
// 0x40088808
// 0x40088809
if (request == 0x40088808) {
std::fprintf(stderr, "dipsw ioctl 0x40088808(%p)\n", argp);
*reinterpret_cast<std::uint32_t *>(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<Args *>(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<IoDeviceInstance> *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;
}

124
rpcsx-os/iodev/dmem.cpp Normal file
View file

@ -0,0 +1,124 @@
#include "io-device.hpp"
#include <cinttypes>
#include <cstdio>
#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<DmemDevice *>(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<AllocateDirectMemoryArgs *>(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<Args *>(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<DmemDevice *>(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<DmemDevice *>(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<void *>(device->memBeginAddress + offset), size,
prot, flags);
return addr;
}
static std::int32_t dmem_device_open(IoDevice *device,
orbis::Ref<IoDeviceInstance> *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;
}

265
rpcsx-os/iodev/gc.cpp Normal file
View file

@ -0,0 +1,265 @@
#include "io-device.hpp"
#include <atomic>
#include <cinttypes>
#include <cstdio>
#include <cstring>
// #include <rpcs4/bridge.hpp>
#include "vm.hpp"
#include <string>
#include <sys/mman.h>
#include <thread>
#include <type_traits>
#include <utility>
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<void **>(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<Args *>(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<Args *>(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<Args *>(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<Args *>(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<Args *>(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<Args *>(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<Args *>(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<Args *>(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<IoDeviceInstance> *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;
}

41
rpcsx-os/iodev/hid.cpp Normal file
View file

@ -0,0 +1,41 @@
#include "io-device.hpp"
#include "vm.hpp"
#include <cinttypes>
#include <cstdio>
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<IoDeviceInstance> *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;
}

View file

@ -0,0 +1,34 @@
#include "io-device.hpp"
#include <cstdio>
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<IoDeviceInstance> *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;
}

View file

@ -0,0 +1,35 @@
#include "io-device.hpp"
#include <cstdio>
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<IoDeviceInstance> *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;
}

View file

@ -0,0 +1,47 @@
#include "io-device.hpp"
#include "vm.hpp"
#include <cinttypes>
#include <cstdio>
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<IoDeviceInstance> *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;
}

View file

@ -0,0 +1,33 @@
#include "io-device.hpp"
#include <cstdio>
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<IoDeviceInstance> *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;
}

28
rpcsx-os/iodev/null.cpp Normal file
View file

@ -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<IoDeviceInstance> *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;
}

39
rpcsx-os/iodev/rng.cpp Normal file
View file

@ -0,0 +1,39 @@
#include "io-device.hpp"
#include "vm.hpp"
#include <cinttypes>
#include <cstdio>
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<IoDeviceInstance> *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;
}

48
rpcsx-os/iodev/stderr.cpp Normal file
View file

@ -0,0 +1,48 @@
#include "io-device.hpp"
#include <fstream>
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<IoDeviceInstance> *instance,
const char *path, std::uint32_t flags,
std::uint32_t mode) {
auto stderrDevice = static_cast<StderrDevice *>(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;
}

28
rpcsx-os/iodev/stdin.cpp Normal file
View file

@ -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<IoDeviceInstance> *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;
}

49
rpcsx-os/iodev/stdout.cpp Normal file
View file

@ -0,0 +1,49 @@
#include "io-device.hpp"
#include <cstdio>
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<IoDeviceInstance> *instance,
const char *path, std::uint32_t flags,
std::uint32_t mode) {
auto stdoutDevice = static_cast<StdoutDevice *>(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;
}

32
rpcsx-os/iodev/zero.cpp Normal file
View file

@ -0,0 +1,32 @@
#include "io-device.hpp"
#include <cstring>
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<IoDeviceInstance> *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;
}

755
rpcsx-os/linker.cpp Normal file
View file

@ -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 <elf.h>
#include <memory>
#include <orbis/thread/Process.hpp>
#include <sys/mman.h>
#include <unordered_map>
#include <crypto/sha1.h>
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<const unsigned char *>(fid.data()),
fid.length());
sha1_update(&ctx, reinterpret_cast<const unsigned char *>(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 "<unknown>";
}
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<std::uint64_t, std::vector<Symbol>> g_symTable;
static std::vector<std::string> g_libraryPathList;
Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image, orbis::Process *process) {
Ref<orbis::Module> 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<std::uint64_t>(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<std::byte *>(
rx::vm::map(reinterpret_cast<void *>(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<std::uintptr_t>(imageBase + header.e_entry) : 0;
if (sceProcParamIndex >= 0) {
result->processParam =
phdrs[sceProcParamIndex].p_vaddr ? reinterpret_cast<void *>(imageBase + phdrs[sceProcParamIndex].p_vaddr) : nullptr;
result->processParamSize = phdrs[sceProcParamIndex].p_memsz;
}
if (sceModuleParamIndex >= 0) {
result->moduleParam =
phdrs[sceProcParamIndex].p_vaddr ? reinterpret_cast<void *>(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<GnuExceptionInfo *>(
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<std::uint32_t *>(&exinfo->first);
dataBuffer = imageBase + offset;
} else if (exinfo->encoding == 0x1B) {
auto offset = *reinterpret_cast<std::int32_t *>(&exinfo->first);
dataBuffer = &exinfo->first + sizeof(std::int32_t) + offset;
} else {
std::abort();
}
auto *dataBufferIt = dataBuffer;
while (true) {
auto size = *reinterpret_cast<std::int32_t *>(dataBufferIt);
dataBufferIt += sizeof(std::uint32_t);
if (size == 0) {
break;
}
if (size == -1) {
size = *reinterpret_cast<std::uint64_t *>(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<Elf64_Dyn> 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<const char *>(
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<const Elf64_Sym *>(
sceDynlibData + dyns[sceSymtabIndex].d_un.d_val)
: nullptr;
std::unordered_map<std::uint64_t, std::size_t> idToModuleIndex;
std::unordered_map<std::uint64_t, std::size_t> 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<std::uint32_t>(dyn.d_un.d_val)
// : "<no strtab>");
// }
// std::printf("\n");
if (dyn.d_tag == kElfDynamicTypeSceModuleInfo) {
std::strncpy(result->name,
sceStrtab + static_cast<std::uint32_t>(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<std::uint32_t>(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<std::uint32_t>(dyn.d_un.d_val);
}
switch (dyn.d_tag) {
case kElfDynamicTypeScePltGot:
result->pltGot = dyn.d_un.d_ptr ?
reinterpret_cast<std::uint64_t *>(imageBase + dyn.d_un.d_ptr) : nullptr;
break;
case kElfDynamicTypeSceJmpRel:
if (sceDynlibData != nullptr) {
pltRelocations = reinterpret_cast<orbis::Relocation *>(
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<orbis::Relocation *>(
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<orbis::SymbolVisibility>(visibility),
.bind = static_cast<orbis::SymbolBind>(bind),
.type = static_cast<orbis::SymbolType>(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<std::uint32_t>(sym.st_name)
// : "<no strtab>",
// 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<orbis::Module> rx::linker::loadModuleFile(const char *path,
orbis::Process *process) {
orbis::Ref<IoDeviceInstance> 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<std::byte> 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);
}

63
rpcsx-os/linker.hpp Normal file
View file

@ -0,0 +1,63 @@
#pragma once
#include "orbis/module/Module.hpp"
#include "orbis/utils/Rc.hpp"
#include <cstddef>
#include <span>
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<uint32_t>(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<orbis::Module> loadModule(std::span<std::byte> image, orbis::Process *process);
orbis::utils::Ref<orbis::Module> loadModuleFile(const char *path, orbis::Process *process);
} // namespace re::loader

637
rpcsx-os/main.cpp Normal file
View file

@ -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 <orbis/KernelContext.hpp>
#include <orbis/module.hpp>
#include <orbis/module/Module.hpp>
#include <orbis/sys/sysentry.hpp>
#include <orbis/sys/sysproto.hpp>
#include <orbis/thread/Process.hpp>
#include <orbis/thread/ProcessOps.hpp>
#include <orbis/thread/Thread.hpp>
#include <asm/prctl.h>
#include <fcntl.h>
#include <libunwind.h>
#include <link.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <csignal>
#include <cstddef>
#include <cstdint>
struct LibcInfo {
std::uint64_t textBegin = ~static_cast<std::uint64_t>(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<std::uint64_t>(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<void *>(ip - baseAddress),
(proc_res == 0 ? functionName : "??"),
reinterpret_cast<unsigned long>(
proc_res == 0 ? ip - baseAddress - off : 0),
static_cast<unsigned long>(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<std::uint64_t>(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<std::uint64_t>(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<void *>(ip - baseAddress),
(proc_res == 0 ? functionName : "??"),
reinterpret_cast<unsigned long>(
proc_res == 0 ? ip - baseAddress - off : 0),
static_cast<unsigned long>(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_t *>(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_t *>(ucontext), g_currentThread,
2);
} else {
printStackTrace(reinterpret_cast<ucontext_t *>(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<ThreadParam *>(paramsVoid);
delete reinterpret_cast<ThreadParam *>(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 <typename T> std::uint64_t push(T value) {
address -= sizeof(value);
address &= ~(alignof(T) - 1);
*reinterpret_cast<T *>(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<void *>(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<orbis::Module> executableModule,
std::span<const char *> argv, std::span<const char *> 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<void *>(stackStartAddress), stackSize,
rx::vm::kMapProtCpuWrite | rx::vm::kMapProtCpuRead,
rx::vm::kMapFlagAnonymous | rx::vm::kMapFlagFixed |
rx::vm::kMapFlagPrivate | rx::vm::kMapFlagStack);
mainThread.stackEnd =
reinterpret_cast<std::byte *>(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<std::uint64_t> argvOffsets;
std::vector<std::uint64_t> envpOffsets;
auto libkernel = rx::linker::loadModuleFile(
"/system/common/lib/libkernel_sys.sprx", mainProcess);
// *reinterpret_cast<std::uint32_t *>(
// reinterpret_cast<std::byte *>(libkernel->base) + 0x6c2e4) = ~0;
StackWriter stack{reinterpret_cast<std::uint64_t>(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<std::uint64_t>(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<std::uint64_t *>(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(&currentContext);
createEmuThread(
mainThread, libkernel->entryPoint,
utils::alignDown(
stack.address -
reinterpret_cast<std::uint64_t>(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 [<options>...] <virtual path to elf> [args...]\n", argv0);
std::printf(" options:\n");
std::printf(" -m, --mount <host path> <virtual path>\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<LibcInfo *>(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<const char *>());
} else {
std::fprintf(stderr, "Unexpected executable type\n");
status = 1;
}
// entryPoint();
// rx::vm::printHostStats();
rx::vm::uninitialize();
return status;
}

533
rpcsx-os/ops.cpp Normal file
View file

@ -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 <cstdio>
#include <filesystem>
#include <map>
#include <optional>
#include <set>
#include <unistd.h>
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<IoDeviceInstance> handle =
static_cast<IoDeviceInstance *>(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<std::uint64_t>(result);
return{};
}
orbis::SysResult munmap(orbis::Thread *thread, orbis::ptr<void> addr,
orbis::size_t len) {
return ErrorCode::INVAL;
}
orbis::SysResult msync(orbis::Thread *thread, orbis::ptr<void> addr,
orbis::size_t len, orbis::sint flags) {
return {};
}
orbis::SysResult mprotect(orbis::Thread *thread, orbis::ptr<const void> addr,
orbis::size_t len, orbis::sint prot) {
rx::vm::protect((void *)addr, len, prot);
return {};
}
orbis::SysResult minherit(orbis::Thread *thread, orbis::ptr<void> addr,
orbis::size_t len, orbis::sint inherit) {
return ErrorCode::INVAL;
}
orbis::SysResult madvise(orbis::Thread *thread, orbis::ptr<void> addr,
orbis::size_t len, orbis::sint behav) {
return ErrorCode::INVAL;
}
orbis::SysResult mincore(orbis::Thread *thread, orbis::ptr<const void> addr,
orbis::size_t len, orbis::ptr<char> vec) {
return ErrorCode::INVAL;
}
orbis::SysResult mlock(orbis::Thread *thread, orbis::ptr<const void> 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<const void> addr,
orbis::size_t len) {
return {};
}
orbis::SysResult virtual_query(orbis::Thread *thread,
orbis::ptr<const void> addr, orbis::sint flags,
orbis::ptr<void> 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<const char> path,
orbis::sint flags, orbis::sint mode) {
std::printf("sys_open(%s)\n", path);
orbis::Ref<IoDeviceInstance> 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<IoDeviceInstance> handle =
static_cast<IoDeviceInstance *>(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<const void> data, orbis::ulong size) {
Ref<IoDeviceInstance> handle =
static_cast<IoDeviceInstance *>(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<void> data, orbis::ulong size) {
return ErrorCode::NOTSUP;
}
orbis::SysResult pread(orbis::Thread *thread, orbis::sint fd,
orbis::ptr<void> data, orbis::ulong size,
orbis::ulong offset) {
return ErrorCode::NOTSUP;
}
orbis::SysResult pwrite(orbis::Thread *thread, orbis::sint fd,
orbis::ptr<const void> 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<const char> 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<orbis::ptr<void>> 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<char> findSymbolById(orbis::Module *module, std::uint64_t id) {
for (auto sym : module->symbols) {
if (sym.id == id && sym.bind != orbis::SymbolBind::Local) {
return (ptr<char>)module->base + sym.address;
}
}
return nullptr;
}
orbis::SysResult dynlib_dlsym(orbis::Thread *thread, orbis::ModuleHandle handle,
orbis::ptr<const char> symbol,
orbis::ptr<orbis::ptr<void>> 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<const char> name,
orbis::uint64_t arg1,
orbis::ptr<ModuleHandle> 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<struct ucontext> ctxt,
ptr<orbis::slong> arg, orbis::sint flags) {
return ErrorCode::NOTSUP;
}
SysResult thr_new(orbis::Thread *thread, orbis::ptr<struct thr_param> param,
orbis::sint param_size) {
return {};
}
SysResult thr_exit(orbis::Thread *thread, orbis::ptr<orbis::slong> 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<const timespec> 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<const char> 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<std::string> allNeededModules;
auto proc = thread->tproc;
std::map<std::string, Module *> 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,
};

7
rpcsx-os/ops.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include "orbis/thread/ProcessOps.hpp"
namespace rx {
extern orbis::ProcessOps procOpsTable;
}

View file

@ -0,0 +1,115 @@
#pragma once
#include "orbis/error.hpp"
#include "orbis/thread/RegisterId.hpp"
#include <cstdint>
#include <cstring>
#include <sys/ucontext.h>
#include <immintrin.h>
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 <typename T> using ptr = T *;
template <typename T> using cptr = T * const;
using caddr_t = ptr<char>;
inline ErrorCode uread(void *kernelAddress, ptr<const void> userAddress,
size_t size) {
std::memcpy(kernelAddress, userAddress, size);
return {};
}
inline ErrorCode uwrite(ptr<void> userAddress, const void *kernelAddress,
size_t size) {
std::memcpy(userAddress, kernelAddress, size);
return {};
}
inline ErrorCode ureadString(char *kernelAddress, size_t kernelSize, ptr<const char> userAddress) {
std::strncpy(kernelAddress, userAddress, kernelSize);
if (kernelAddress[kernelSize - 1] != '\0') {
kernelAddress[kernelSize - 1] = '\0';
return ErrorCode::NAMETOOLONG;
}
return {};
}
template <typename T> T uread(ptr<T> pointer) {
T result;
uread(&result, pointer, sizeof(T));
return result;
}
template <typename T> void uwrite(ptr<T> pointer, T data) {
uwrite(pointer, &data, sizeof(T));
}
inline uint64_t readRegister(void *context, RegisterId id) {
auto c = &reinterpret_cast<ucontext_t *>(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<ucontext_t *>(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

64
rpcsx-os/vfs.cpp Normal file
View file

@ -0,0 +1,64 @@
#include "vfs.hpp"
#include "io-device.hpp"
#include "orbis/error/ErrorCode.hpp"
#include "orbis/error/SysResult.hpp"
#include <filesystem>
#include <map>
#include <string_view>
static std::map<std::string, orbis::Ref<IoDevice>> 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<IoDeviceInstance> *instance) {
orbis::Ref<IoDevice> 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;
}

16
rpcsx-os/vfs.hpp Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include "orbis/error/SysResult.hpp"
#include "orbis/utils/Rc.hpp"
#include <filesystem>
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<IoDeviceInstance> *instance);
} // namespace vfs

923
rpcsx-os/vm.cpp Normal file
View file

@ -0,0 +1,923 @@
#include "vm.hpp"
#include "align.hpp"
#include <bit>
#include <cassert>
#include <cinttypes>
#include <cstring>
#include <fcntl.h>
#include <map>
#include <sys/mman.h>
#include <unistd.h>
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<std::uint64_t>(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<std::uint32_t>(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<std::uint64_t>(0) : 0;
std::uint64_t addReadableFlags =
(addFlags & kReadable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t addWritableFlags =
(addFlags & kWritable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t addExecutableFlags =
(addFlags & kExecutable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t addGpuReadableFlags =
(addFlags & kGpuReadable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t addGpuWritableFlags =
(addFlags & kGpuWritable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t removeAllocatedFlags =
(removeFlags & kAllocated) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t removeReadableFlags =
(removeFlags & kReadable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t removeWritableFlags =
(removeFlags & kWritable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t removeExecutableFlags =
(removeFlags & kExecutable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t removeGpuReadableFlags =
(removeFlags & kGpuReadable) ? ~static_cast<std::uint64_t>(0) : 0;
std::uint64_t removeGpuWritableFlags =
(removeFlags & kGpuWritable) ? ~static_cast<std::uint64_t>(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<std::uint64_t>(0);
}
};
static Block gBlocks[kBlockCount];
static std::map<std::uint64_t, rx::vm::VirtualQueryInfo, std::greater<>>
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<void *>(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<std::uint64_t>(addr);
std::uint64_t alignment = (flags & kMapFlagsAlignMask) >> kMapFlagsAlignShift;
if (alignment == 0) {
alignment = kPageSize;
} else {
alignment = static_cast<std::uint64_t>(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<std::uint64_t>(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<void *>(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<std::uint64_t>(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<std::uint64_t>(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<std::uint64_t>(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<std::uint64_t>(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);
}

74
rpcsx-os/vm.hpp Normal file
View file

@ -0,0 +1,74 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <string>
namespace rx::vm {
static constexpr std::uint64_t kPageShift = 14;
static constexpr std::uint64_t kPageSize = static_cast<std::uint64_t>(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);
}