mirror of
https://github.com/RPCSX/rpcsx.git
synced 2024-11-26 21:00:35 +00:00
Initial commit
This commit is contained in:
commit
1fdadaaee9
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build/
|
||||
compile_commands.json
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal 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
17
3rdparty/crypto/CMakeLists.txt
vendored
Normal 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
164
3rdparty/crypto/include/crypto/sha1.h
vendored
Normal 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
377
3rdparty/crypto/src/sha1.c
vendored
Normal 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
9
CMakeLists.txt
Normal 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
339
LICENSE
Normal 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
1
orbis-kernel
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 05d35b71483880246bc4c1a28f857e9046af7c36
|
35
rpcsx-os/CMakeLists.txt
Normal file
35
rpcsx-os/CMakeLists.txt
Normal 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
14
rpcsx-os/align.hpp
Normal 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
131
rpcsx-os/io-device.cpp
Normal 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
56
rpcsx-os/io-device.hpp
Normal 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
19
rpcsx-os/io-devices.hpp
Normal 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
291
rpcsx-os/iodev/dce.cpp
Normal 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
64
rpcsx-os/iodev/dipsw.cpp
Normal 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
124
rpcsx-os/iodev/dmem.cpp
Normal 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
265
rpcsx-os/iodev/gc.cpp
Normal 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
41
rpcsx-os/iodev/hid.cpp
Normal 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;
|
||||
}
|
34
rpcsx-os/iodev/hmd_3da.cpp
Normal file
34
rpcsx-os/iodev/hmd_3da.cpp
Normal 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;
|
||||
}
|
35
rpcsx-os/iodev/hmd_cmd.cpp
Normal file
35
rpcsx-os/iodev/hmd_cmd.cpp
Normal 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;
|
||||
}
|
47
rpcsx-os/iodev/hmd_mmap.cpp
Normal file
47
rpcsx-os/iodev/hmd_mmap.cpp
Normal 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;
|
||||
}
|
33
rpcsx-os/iodev/hmd_snsr.cpp
Normal file
33
rpcsx-os/iodev/hmd_snsr.cpp
Normal 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
28
rpcsx-os/iodev/null.cpp
Normal 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
39
rpcsx-os/iodev/rng.cpp
Normal 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
48
rpcsx-os/iodev/stderr.cpp
Normal 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
28
rpcsx-os/iodev/stdin.cpp
Normal 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
49
rpcsx-os/iodev/stdout.cpp
Normal 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
32
rpcsx-os/iodev/zero.cpp
Normal 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
755
rpcsx-os/linker.cpp
Normal 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
63
rpcsx-os/linker.hpp
Normal 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
637
rpcsx-os/main.cpp
Normal 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(¤tContext);
|
||||
|
||||
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
533
rpcsx-os/ops.cpp
Normal 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
7
rpcsx-os/ops.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "orbis/thread/ProcessOps.hpp"
|
||||
|
||||
namespace rx {
|
||||
extern orbis::ProcessOps procOpsTable;
|
||||
}
|
115
rpcsx-os/orbis-kernel-config/orbis-config.hpp
Normal file
115
rpcsx-os/orbis-kernel-config/orbis-config.hpp
Normal 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
64
rpcsx-os/vfs.cpp
Normal 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
16
rpcsx-os/vfs.hpp
Normal 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
923
rpcsx-os/vm.cpp
Normal 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
74
rpcsx-os/vm.hpp
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user