tcpgecko/src/pygecko.c
2017-03-29 16:50:33 +02:00

1394 lines
43 KiB
C

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include "common/common.h"
#include <zlib.h> // Actually must be included before os_functions
#include "dynamic_libs/os_functions.h"
#include <string.h>
#include <malloc.h>
#include "main.h"
#include "dynamic_libs/socket_functions.h"
#include "dynamic_libs/gx2_functions.h"
#include "kernel/syscalls.h"
#include "dynamic_libs/fs_functions.h"
#include "common/fs_defs.h"
#include "system/exception_handler.h"
void *client;
void *commandBlock;
bool kernelCopyServiceStarted;
struct pygecko_bss_t {
int error, line;
void *thread;
unsigned char stack[0x6F00];
};
/* TCP Gecko Commands */
#define COMMAND_WRITE_8 0x01
#define COMMAND_WRITE_16 0x02
#define COMMAND_WRITE_32 0x03
#define COMMAND_READ_MEMORY 0x04
#define COMMAND_READ_MEMORY_KERNEL 0x05
#define COMMAND_VALIDATE_ADDRESS_RANGE 0x06
#define COMMAND_MEMORY_DISASSEMBLE 0x08
#define COMMAND_READ_MEMORY_COMPRESSED 0x09 // TODO Remove command when done and integrate in read memory
#define COMMAND_KERNEL_WRITE 0x0B
#define COMMAND_KERNEL_READ 0x0C
#define COMMAND_TAKE_SCREEN_SHOT 0x0D // TODO Finish this
#define COMMAND_UPLOAD_MEMORY 0x41
#define COMMAND_SERVER_STATUS 0x50
#define COMMAND_GET_DATA_BUFFER_SIZE 0x51
#define COMMAND_READ_FILE 0x52
#define COMMAND_READ_DIRECTORY 0x53
#define COMMAND_REPLACE_FILE 0x54 // TODO Test this
#define COMMAND_GET_CODE_HANDLER_ADDRESS 0x55
#define COMMAND_READ_THREADS 0x56
#define COMMAND_ACCOUNT_IDENTIFIER 0x57
#define COMMAND_WRITE_SCREEN 0x58 // TODO Exception DSI
#define COMMAND_FOLLOW_POINTER 0x60
#define COMMAND_RPC 0x70
#define COMMAND_GET_SYMBOL 0x71
#define COMMAND_MEMORY_SEARCH 0x73
// #define COMMAND_SYSTEM_CALL 0x80
#define COMMAND_EXECUTE_ASSEMBLY 0x81
#define COMMAND_SERVER_VERSION 0x99
#define COMMAND_OS_VERSION 0x9A
#define COMMAND_RUN_KERNEL_COPY_SERVICE 0xCD
#define CHECK_ERROR(cond) if (cond) { bss->line = __LINE__; goto error; }
#define errno (*__gh_errno_ptr())
#define MSG_DONTWAIT 32
#define EWOULDBLOCK 6
#define FS_BUFFER_SIZE 0x1000
#define DATA_BUFFER_SIZE 0x5000
#define DISASSEMBLER_BUFFER_SIZE 0x1024
#define WRITE_SCREEN_MESSAGE_BUFFER_SIZE 100
#define SERVER_VERSION "02/25/2017"
#define ONLY_ZEROS_READ 0xB0
#define NON_ZEROS_READ 0xBD
#define ASSERT_MINIMUM_HOLDS(actual, minimum, variableName) \
if(actual < minimum) { \
char buffer[100] = {0}; \
__os_snprintf(buffer, 100, "%s: Limit exceeded (minimum: %i, actual: %i)", variableName, minimum, actual); \
OSFatal(buffer); \
} \
#define ASSERT_MAXIMUM_HOLDS(maximum, actual, variableName) \
if(actual > maximum) { \
char buffer[100] = {0}; \
__os_snprintf(buffer, 100, "%s: Limit exceeded (maximum: %i, actual: %i)", variableName, maximum, actual); \
OSFatal(buffer); \
} \
#define ASSERT_FUNCTION_SUCCEEDED(returnValue, functionName) \
if (returnValue < 0) { \
char buffer[100] = {0}; \
__os_snprintf(buffer, 100, "%s failed with return value: %i", functionName, returnValue); \
OSFatal(buffer); \
} \
#define ASSERT_VALID_EFFECTIVE_ADDRESS(effectiveAddress, message) \
if(!OSIsAddressValid((void *) effectiveAddress)) { \
char buffer[100] = {0}; \
__os_snprintf(buffer, 100, "Address %04x invalid: %s", effectiveAddress, message); \
OSFatal(buffer); \
} \
#define ASSERT_INTEGER(actual, expected, name) \
if(actual != expected) { \
char buffer[50] = {0}; \
__os_snprintf(buffer, 50, "%s assertion failed: %i == %i", name, actual, expected); \
OSFatal(buffer); \
} \
#define ASSERT_STRING(actual, expected) \
if(strcmp(actual, expected) != 0) { \
char buffer[50] = {0}; \
__os_snprintf(buffer, 50, "String assertion failed: \"%s\" == \"%s\"", actual, expected); \
OSFatal(buffer); \
} \
#define ASSERT_ALLOCATED(variable, name) \
if(variable == 0) { \
char buffer[50] = {0}; \
__os_snprintf(buffer, 50, "%s allocation failed", name); \
OSFatal(buffer); \
} \
ZEXTERN int ZEXPORT
deflateEnd OF((z_streamp
strm));
ZEXTERN int ZEXPORT
deflateInit OF((z_streamp
strm,
int level
));
ZEXTERN int ZEXPORT
deflate OF((z_streamp
strm,
int flush
));
/*struct breakpoint {
u32 address;
u32 instruction;
};
// 10 general breakpoints + 2 step breakpoints
breakpoint breakpoints[12];
breakpoint *getBreakpoint(u32 address, int size) {
breakpoint *breakpointsList = breakpoints;
for (int breakpointIndex = 0; breakpointIndex < size; breakpointIndex++) {
if (breakpointsList[breakpointIndex].address == address) {
return &breakpointsList[breakpointIndex];
}
}
return 0;
}*/
unsigned char *memcpy_buffer[DATA_BUFFER_SIZE];
void pygecko_memcpy(unsigned char *destinationBuffer, unsigned char *sourceBuffer, unsigned int length) {
memcpy(memcpy_buffer, sourceBuffer, length);
SC0x25_KernelCopyData((unsigned int) OSEffectiveToPhysical(destinationBuffer), (unsigned int) &memcpy_buffer,
length);
DCFlushRange(destinationBuffer, (u32) length);
}
unsigned char *kernelCopyBuffer[4];
void kernelCopy(unsigned char *destinationBuffer, unsigned char *sourceBuffer, unsigned int length) {
memcpy(kernelCopyBuffer, sourceBuffer, length);
unsigned int destinationAddress = (unsigned int) OSEffectiveToPhysical(destinationBuffer);
SC0x25_KernelCopyData(destinationAddress, (unsigned int) &kernelCopyBuffer, length);
DCFlushRange(destinationBuffer, (u32) length);
}
#define KERNEL_COPY_SOURCE_ADDRESS 0x10100000
int kernelCopyService(int argc, void *argv) {
while (true) {
// Read the destination address from the source address
int destinationAddress = *(int *) KERNEL_COPY_SOURCE_ADDRESS;
// Avoid crashing
if (OSIsAddressValid((const void *) destinationAddress)) {
// Perform memory copy
unsigned char *valueBuffer = (unsigned char *) (KERNEL_COPY_SOURCE_ADDRESS + 4);
kernelCopy((unsigned char *) destinationAddress, valueBuffer, 4);
// "Consume" address and value for synchronization with the code handler for instance
*(int *) KERNEL_COPY_SOURCE_ADDRESS = 0;
*(((int *) KERNEL_COPY_SOURCE_ADDRESS) + 1) = 0;
}
}
}
void startKernelCopyService() {
unsigned int stack = (unsigned int) memalign(0x40, 0x100);
ASSERT_ALLOCATED(stack, "Kernel copy thread stack")
stack += 0x100;
void *thread = memalign(0x40, 0x1000);
ASSERT_ALLOCATED(thread, "Kernel copy thread")
int status = OSCreateThread(thread, kernelCopyService, 1, NULL, (u32) stack + sizeof(stack), sizeof(stack), 31,
OS_THREAD_ATTR_AFFINITY_CORE1 | OS_THREAD_ATTR_PINNED_AFFINITY | OS_THREAD_ATTR_DETACH);
ASSERT_INTEGER(status, 1, "Creating kernel copy thread")
// OSSetThreadName(thread, "Kernel Copier");
OSResumeThread(thread);
}
/*Validates the address range (last address inclusive) but is SLOW on bigger ranges */
static int validateAddressRange(int starting_address, int ending_address) {
return __OSValidateAddressSpaceRange(1, (void *) starting_address, ending_address - starting_address + 1);
}
static int recvwait(struct pygecko_bss_t *bss, int sock, void *buffer, int len) {
int ret;
while (len > 0) {
ret = recv(sock, buffer, len, 0);
CHECK_ERROR(ret < 0);
len -= ret;
buffer += ret;
}
return 0;
error:
bss->error = ret;
return ret;
}
static int recvbyte(struct pygecko_bss_t *bss, int sock) {
unsigned char buffer[1];
int ret;
ret = recvwait(bss, sock, buffer, 1);
if (ret < 0) return ret;
return buffer[0];
}
static int checkbyte(int sock) {
unsigned char buffer[1];
int ret;
ret = recv(sock, buffer, 1, MSG_DONTWAIT);
if (ret < 0) return ret;
if (ret == 0) return -1;
return buffer[0];
}
static int sendwait(struct pygecko_bss_t *bss, int sock, const void *buffer, int len) {
int ret;
while (len > 0) {
ret = send(sock, buffer, len, 0);
CHECK_ERROR(ret < 0);
len -= ret;
buffer += ret;
}
return 0;
error:
bss->error = ret;
return ret;
}
static int sendbyte(struct pygecko_bss_t *bss, int sock, unsigned char byte) {
unsigned char buffer[1];
buffer[0] = byte;
return sendwait(bss, sock, buffer, 1);
}
/*void performSystemCall(int value) {
// TODO Exception DSI?
asm(
"li 0, %0\n"
"sc\n"
"blr\n"
: // No output
:"r"(value) // Input
:"0" // Overwritten register
);
}*/
void writeScreen(char message[100], int secondsDelay) {
// TODO Does nothing then crashes (in games)?
OSScreenClearBufferEx(0, 0);
OSScreenClearBufferEx(1, 0);
OSScreenPutFontEx(0, 14, 1, message);
OSScreenPutFontEx(1, 14, 1, message);
sleep(secondsDelay);
OSScreenFlipBuffersEx(0);
OSScreenFlipBuffersEx(1);
}
void receiveString(struct pygecko_bss_t *bss,
int clientfd,
char *stringBuffer,
int bufferSize) {
// Receive the string length
char lengthBuffer[4] = {0};
int ret = recvwait(bss, clientfd, lengthBuffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (string length)")
int stringLength = ((int *) lengthBuffer)[0];
if (stringLength >= 0 && stringLength <= bufferSize) {
// Receive the actual string
ret = recvwait(bss, clientfd, stringBuffer, stringLength);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (string)")
} else {
OSFatal("String buffer size exceeded");
}
}
void considerInitializingFileSystem() {
if (!client) {
// Initialize the file system
int status = FSInit();
ASSERT_FUNCTION_SUCCEEDED(status, "FSInit")
// Allocate the client
client = malloc(FS_CLIENT_SIZE);
ASSERT_ALLOCATED(client, "Client")
// Register the client
status = FSAddClientEx(client, 0, -1);
ASSERT_FUNCTION_SUCCEEDED(status, "FSAddClientEx")
// Allocate the command block
commandBlock = malloc(FS_CMD_BLOCK_SIZE);
ASSERT_ALLOCATED(commandBlock, "Command block")
FSInitCmdBlock(commandBlock);
}
}
char *disassemblerBuffer;
void *disassemblerBufferPointer;
void formatDisassembled(char *format, ...) {
if (!disassemblerBuffer) {
disassemblerBuffer = malloc(DISASSEMBLER_BUFFER_SIZE);
ASSERT_ALLOCATED(disassemblerBuffer, "Disassembler buffer")
disassemblerBufferPointer = disassemblerBuffer;
}
va_list variableArguments;
va_start(variableArguments, format);
char *temporaryBuffer;
int printedBytesCount = vasprintf(&temporaryBuffer, format, variableArguments);
ASSERT_ALLOCATED(temporaryBuffer, "Temporary buffer")
ASSERT_MINIMUM_HOLDS(printedBytesCount, 1, "Printed bytes count")
va_end(variableArguments);
// Do not smash the buffer
long projectedSize = (void *) disassemblerBuffer - disassemblerBufferPointer + printedBytesCount;
if (projectedSize < DISASSEMBLER_BUFFER_SIZE) {
memcpy(disassemblerBuffer, temporaryBuffer, printedBytesCount);
disassemblerBuffer += printedBytesCount;
}
free(temporaryBuffer);
}
bool isValidDataAddress(int address) {
return OSIsAddressValid((const void *) address)
&& address >= 0x10000000
&& address < 0x50000000;
}
int roundUpToAligned(int number) {
return (number + 3) & ~0x03;
}
#define ERROR_BUFFER_SIZE 100
void reportIllegalCommandByte(int commandByte) {
char errorBuffer[ERROR_BUFFER_SIZE];
__os_snprintf(errorBuffer, ERROR_BUFFER_SIZE, "Illegal command byte received: 0x%02x\nServer Version: %s",
commandByte, SERVER_VERSION);
OSFatal(errorBuffer);
}
void writeInt(unsigned int address, unsigned int value) {
pygecko_memcpy((unsigned char *) address, (unsigned char *) &value, 4);
}
#define MINIMUM_KERNEL_COMPARE_LENGTH 4
#define KERNEL_MEMORY_COMPARE_STEP_SIZE 1
int kernelMemoryCompare(const void *sourceBuffer,
const void *destinationBuffer,
int length) {
if (length < MINIMUM_KERNEL_COMPARE_LENGTH) {
ASSERT_MINIMUM_HOLDS(length, MINIMUM_KERNEL_COMPARE_LENGTH, "length");
}
bool loopEntered = false;
while (kern_read(sourceBuffer) == kern_read(destinationBuffer)) {
loopEntered = true;
sourceBuffer = (char *) sourceBuffer + KERNEL_MEMORY_COMPARE_STEP_SIZE;
destinationBuffer = (char *) destinationBuffer + KERNEL_MEMORY_COMPARE_STEP_SIZE;
length -= KERNEL_MEMORY_COMPARE_STEP_SIZE;
if (length <= MINIMUM_KERNEL_COMPARE_LENGTH - 1) {
break;
}
}
if (loopEntered) {
sourceBuffer -= KERNEL_MEMORY_COMPARE_STEP_SIZE;
destinationBuffer -= KERNEL_MEMORY_COMPARE_STEP_SIZE;
}
return kern_read(sourceBuffer) - kern_read(destinationBuffer);
}
static int rungecko(struct pygecko_bss_t *bss, int clientfd) {
int ret;
// Hold the command and the data
unsigned char buffer[1 + DATA_BUFFER_SIZE];
// Run the RPC server
while (true) {
ret = checkbyte(clientfd);
if (ret < 0) {
CHECK_ERROR(errno != EWOULDBLOCK);
GX2WaitForVsync();
continue;
}
switch (ret) {
case COMMAND_WRITE_8: {
char *destinationAddress;
ret = recvwait(bss, clientfd, buffer, 8);
CHECK_ERROR(ret < 0);
destinationAddress = ((char **) buffer)[0];
*destinationAddress = buffer[7];
DCFlushRange(destinationAddress, 1);
break;
}
case COMMAND_WRITE_16: {
short *destinationAddress;
ret = recvwait(bss, clientfd, buffer, 8);
CHECK_ERROR(ret < 0)
destinationAddress = ((short **) buffer)[0];
*destinationAddress = ((short *) buffer)[3];
DCFlushRange(destinationAddress, 2);
break;
}
case COMMAND_WRITE_32: {
int destinationAddress, value;
ret = recvwait(bss, clientfd, buffer, 8);
CHECK_ERROR(ret < 0)
destinationAddress = ((int *) buffer)[0];
value = ((int *) buffer)[1];
pygecko_memcpy((unsigned char *) destinationAddress, (unsigned char *) &value, 4);
break;
}
case COMMAND_READ_MEMORY: {
const unsigned char *startingAddress, *endingAddress;
ret = recvwait(bss, clientfd, buffer, 2 * 4);
CHECK_ERROR(ret < 0)
startingAddress = ((const unsigned char **) buffer)[0];
endingAddress = ((const unsigned char **) buffer)[1];
while (startingAddress != endingAddress) {
int length = (int) (endingAddress - startingAddress);
// Do not smash the buffer
if (length > DATA_BUFFER_SIZE) {
length = DATA_BUFFER_SIZE;
}
// Figure out if all bytes are zero to possibly avoid sending them
int rangeIterationIndex = 0;
for (; rangeIterationIndex < length; rangeIterationIndex++) {
int character = startingAddress[rangeIterationIndex];
if (character != 0) {
break;
}
}
if (rangeIterationIndex == length) {
// No need to send all zero bytes for performance
ret = sendbyte(bss, clientfd, ONLY_ZEROS_READ);
CHECK_ERROR(ret < 0)
} else {
// TODO Compression of ptr, sending of status, compressed size and data, length: 1 + 4 + len(data)
buffer[0] = NON_ZEROS_READ;
memcpy(buffer + 1, startingAddress, length);
ret = sendwait(bss, clientfd, buffer, length + 1);
CHECK_ERROR(ret < 0)
}
/* No exit condition.
We reconnect client-sided instead as a hacky work-around
to gain a little more performance by avoiding the very rare search canceling
*/
startingAddress += length;
}
break;
}
case COMMAND_READ_MEMORY_KERNEL: {
const unsigned char *startingAddress, *endingAddress, *useKernRead;
ret = recvwait(bss, clientfd, buffer, 3 * 4);
CHECK_ERROR(ret < 0)
startingAddress = ((const unsigned char **) buffer)[0];
endingAddress = ((const unsigned char **) buffer)[1];
useKernRead = ((const unsigned char **) buffer)[2];
while (startingAddress != endingAddress) {
int length = (int) (endingAddress - startingAddress);
// Do not smash the buffer
if (length > DATA_BUFFER_SIZE) {
length = DATA_BUFFER_SIZE;
}
// Figure out if all bytes are zero to possibly avoid sending them
int rangeIterationIndex = 0;
for (; rangeIterationIndex < length; rangeIterationIndex++) {
int character = useKernRead ? kern_read(startingAddress + rangeIterationIndex)
: startingAddress[rangeIterationIndex];
if (character != 0) {
break;
}
}
if (rangeIterationIndex == length) {
// No need to send all zero bytes for performance
ret = sendbyte(bss, clientfd, ONLY_ZEROS_READ);
CHECK_ERROR(ret < 0)
} else {
// TODO Compression of ptr, sending of status, compressed size and data, length: 1 + 4 + len(data)
buffer[0] = NON_ZEROS_READ;
if (useKernRead) {
for (int offset = 0; offset < length; offset += 4) {
*((int *) (buffer + 1) + offset / 4) = kern_read(startingAddress + offset);
}
} else {
memcpy(buffer + 1, startingAddress, length);
}
ret = sendwait(bss, clientfd, buffer, length + 1);
CHECK_ERROR(ret < 0)
}
/* No exit condition.
We reconnect client-sided instead as a hacky work-around
to gain a little more performance by avoiding the very rare search canceling
*/
startingAddress += length;
}
break;
}
case COMMAND_VALIDATE_ADDRESS_RANGE: {
ret = recvwait(bss, clientfd, buffer, 8);
CHECK_ERROR(ret < 0)
// Retrieve the data
int startingAddress = ((int *) buffer)[0];
int endingAddress = ((int *) buffer)[1];
int isAddressRangeValid = validateAddressRange(startingAddress, endingAddress);
sendbyte(bss, clientfd, (unsigned char) isAddressRangeValid);
break;
}
/*case COMMAND_DISASSEMBLE_RANGE: {
// Receive the starting, ending address and the disassembler options
ret = recvwait(bss, clientfd, buffer, 4 + 4 + 4);
CHECK_ERROR(ret < 0)
void *startingAddress = ((void **) buffer)[0];
void *endingAddress = ((void **) buffer)[1];
int disassemblerOptions = ((int *) buffer)[2];
// Disassemble
DisassemblePPCRange(startingAddress, endingAddress, formatDisassembled, OSGetSymbolName,
(u32) disassemblerOptions);
// Send the disassembler buffer size
int length = DISASSEMBLER_BUFFER_SIZE;
ret = sendwait(bss, clientfd, &length, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (disassembler buffer size)")
// Send the data
ret = sendwait(bss, clientfd, disassemblerBufferPointer, length);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (disassembler buffer)")
// Place the pointer back to the beginning
disassemblerBuffer = (char *) disassemblerBufferPointer;
break;
}*/
case COMMAND_MEMORY_DISASSEMBLE: {
// Receive the starting address, ending address and disassembler options
ret = recvwait(bss, clientfd, buffer, 4 + 4 + 4);
CHECK_ERROR(ret < 0)
int startingAddress = ((int *) buffer)[0];
int endingAddress = ((int *) buffer)[1];
int disassemblerOptions = ((int *) buffer)[2];
int currentAddress = startingAddress;
int bufferSize = PPC_DISASM_MAX_BUFFER;
int integerSize = 4;
// Disassemble everything
while (currentAddress < endingAddress) {
int currentIntegerIndex = 0;
while ((currentIntegerIndex < (DATA_BUFFER_SIZE / integerSize))
&& (currentAddress < endingAddress)) {
int value = *(int *) currentAddress;
((int *) buffer)[currentIntegerIndex++] = value;
char *opCodeBuffer = malloc(bufferSize);
bool status = DisassemblePPCOpcode((u32 *) currentAddress, opCodeBuffer, (u32) bufferSize,
OSGetSymbolName,
(u32) disassemblerOptions);
((int *) buffer)[currentIntegerIndex++] = status;
if (status == 1) {
// Send the length of the opCode buffer string
int length = strlen(opCodeBuffer);
((int *) buffer)[currentIntegerIndex++] = length;
// Send the opCode buffer itself
memcpy(buffer + (currentIntegerIndex * integerSize), opCodeBuffer, length);
currentIntegerIndex += (roundUpToAligned(length) / integerSize);
}
free(opCodeBuffer);
currentAddress += integerSize;
}
int bytesToSend = currentIntegerIndex * integerSize;
ret = sendwait(bss, clientfd, &bytesToSend, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (Buffer size)")
// VALUE(4)|STATUS(4)|LENGTH(4)|DISASSEMBLED(LENGTH)
ret = sendwait(bss, clientfd, buffer, bytesToSend);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (Buffer)")
}
int bytesToSend = 0;
ret = sendwait(bss, clientfd, &bytesToSend, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (No more bytes)")
break;
}
case COMMAND_READ_MEMORY_COMPRESSED: {
// Receive the starting address and length
ret = recvwait(bss, clientfd, buffer, 4 + 4);
CHECK_ERROR(ret < 0)
int startingAddress = ((int *) buffer)[0];
unsigned int inputLength = ((unsigned int *) buffer)[1];
z_stream stream;
memset(&stream, 0, sizeof(stream));
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
// Initialize the stream struct
ret = deflateInit(&stream, Z_BEST_COMPRESSION);
ASSERT_INTEGER(ret, Z_OK, "deflateInit")
// Supply the data
stream.avail_in = inputLength;
stream.next_in = (Bytef *) startingAddress;
stream.avail_out = DATA_BUFFER_SIZE;
void *outputBuffer = (void *) (&buffer + 4);
stream.next_out = (Bytef *) outputBuffer;
// Deflate
ret = deflate(&stream, Z_FINISH);
ASSERT_INTEGER(ret, Z_OK, "deflate");
// Finish
ret = deflateEnd(&stream);
ASSERT_INTEGER(ret, Z_OK, "deflateEnd");
// Send the compressed buffer size and content
int deflatedSize = stream.total_out;
((int *) buffer)[0] = deflatedSize;
ret = sendwait(bss, clientfd, buffer, 4 + deflatedSize);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (Compressed data)")
break;
// https://www.gamedev.net/resources/_/technical/game-programming/in-memory-data-compression-and-decompression-r2279
/*
// Setup compressed buffer
unsigned int compressedBufferSize = length * 2;
void *compressedBuffer = (void *) OSAllocFromSystem(compressedBufferSize, 0x4);
ASSERT_ALLOCATED(compressedBuffer, "Compressed buffer")
unsigned int zlib_handle;
OSDynLoad_Acquire("zlib125.rpl", (u32 *) &zlib_handle);
int (*compress2)(char *, int *, const char *, int, int);
OSDynLoad_FindExport((u32) zlib_handle, 0, "compress2", &compress2);
int destinationBufferSize;
int status = compress2((char *) compressedBuffer, &destinationBufferSize,
(const char *) rawBuffer, length, Z_DEFAULT_COMPRESSION);
ret = sendwait(bss, clientfd, &status, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (status)")
if (status == Z_OK) {
// Send the compressed buffer size and content
((int *) buffer)[0] = destinationBufferSize;
memcpy(buffer + 4, compressedBuffer, destinationBufferSize);
ret = sendwait(bss, clientfd, buffer, 4 + destinationBufferSize);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (Compressed data)")
}
free(rawBuffer);
OSFreeToSystem(compressedBuffer);
break;*/
}
case COMMAND_KERNEL_WRITE: {
void *ptr, *value;
ret = recvwait(bss, clientfd, buffer, 8);
CHECK_ERROR(ret < 0)
ptr = ((void **) buffer)[0];
value = ((void **) buffer)[1];
kern_write(ptr, (uint32_t) value);
break;
}
case COMMAND_KERNEL_READ: {
void *ptr, *value;
ret = recvwait(bss, clientfd, buffer, 4);
CHECK_ERROR(ret < 0);
ptr = ((void **) buffer)[0];
value = (void *) kern_read(ptr);
*(void **) buffer = value;
sendwait(bss, clientfd, buffer, 4);
break;
}
case COMMAND_TAKE_SCREEN_SHOT: {
GX2ColorBuffer colorBuffer;
// TODO Initialize colorBuffer!
GX2Surface surface = colorBuffer.surface;
void *image_data = surface.image_data;
u32 image_size = surface.image_size;
// Send the image size so that the client knows how much to read
ret = sendwait(bss, clientfd, &image_size, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (image size)")
unsigned int imageBytesSent = 0;
while (imageBytesSent < image_size) {
int length = image_size - imageBytesSent;
// Do not smash the buffer
if (length > DATA_BUFFER_SIZE) {
length = DATA_BUFFER_SIZE;
}
// Send the image bytes
memcpy(buffer, image_data, length);
ret = sendwait(bss, clientfd, buffer, length);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (image bytes)")
imageBytesSent += length;
}
break;
}
case COMMAND_UPLOAD_MEMORY: {
// Receive the starting and ending addresses
ret = recvwait(bss, clientfd, buffer, 8);
CHECK_ERROR(ret < 0)
unsigned char *current_address = ((unsigned char **) buffer)[0];
unsigned char *end_address = ((unsigned char **) buffer)[1];
while (current_address != end_address) {
int length;
length = (int) (end_address - current_address);
if (length > DATA_BUFFER_SIZE) {
length = DATA_BUFFER_SIZE;
}
ret = recvwait(bss, clientfd, buffer, length);
CHECK_ERROR(ret < 0)
pygecko_memcpy(current_address, buffer, (unsigned int) length);
current_address += length;
}
break;
}
case COMMAND_GET_DATA_BUFFER_SIZE: {
((int *) buffer)[0] = DATA_BUFFER_SIZE;
ret = sendwait(bss, clientfd, buffer, 4);
CHECK_ERROR(ret < 0)
break;
}
case COMMAND_READ_FILE: {
char file_path[FS_MAX_FULLPATH_SIZE] = {0};
receiveString(bss, clientfd, file_path, FS_MAX_FULLPATH_SIZE);
considerInitializingFileSystem();
int handle;
int status = FSOpenFile(client, commandBlock, file_path, "r", &handle, FS_RET_ALL_ERROR);
if (status == FS_STATUS_OK) {
// Send the OK status
((int *) buffer)[0] = status;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (OK status)")
// Retrieve the file statistics
FSStat stat;
ret = FSGetStatFile(client, commandBlock, handle, &stat, FS_RET_ALL_ERROR);
ASSERT_FUNCTION_SUCCEEDED(ret, "FSGetStatFile")
// Send the total bytes count
int totalBytes = (int) stat.size;
((int *) buffer)[0] = totalBytes;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (total bytes)")
// Allocate the file bytes buffer
unsigned int file_buffer_size = 0x2000;
char *fileBuffer = (char *) OSAllocFromSystem(file_buffer_size, FS_IO_BUFFER_ALIGN);
ASSERT_ALLOCATED(fileBuffer, "File buffer")
int totalBytesRead = 0;
while (totalBytesRead < totalBytes) {
int bytesRead = FSReadFile(client, commandBlock, fileBuffer, 1, file_buffer_size,
handle, 0, FS_RET_ALL_ERROR);
ASSERT_FUNCTION_SUCCEEDED(bytesRead, "FSReadFile")
// Send file bytes
ret = sendwait(bss, clientfd, fileBuffer, bytesRead);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (file buffer)")
totalBytesRead += bytesRead;
}
ret = FSCloseFile(client, commandBlock, handle, FS_RET_ALL_ERROR);
ASSERT_FUNCTION_SUCCEEDED(ret, "FSCloseFile")
OSFreeToSystem(fileBuffer);
} else {
// Send the error status
((int *) buffer)[0] = status;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (error status)")
}
break;
}
case COMMAND_READ_DIRECTORY: {
char directory_path[FS_MAX_FULLPATH_SIZE] = {0};
receiveString(bss, clientfd, directory_path, FS_MAX_FULLPATH_SIZE);
considerInitializingFileSystem();
int handle;
FSDirEntry entry;
ret = FSOpenDir(client, commandBlock, directory_path, &handle, FS_RET_ALL_ERROR);
if (ret == FS_STATUS_OK) {
// Send the success status
((int *) buffer)[0] = ret;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (success status)")
int entrySize = sizeof(FSDirEntry);
// Read every entry in the given directory
while (FSReadDir(client, commandBlock, handle, &entry, -1) == FS_STATUS_OK) {
// Let the client know how much data is going to be sent (even though this is constant)
((int *) buffer)[0] = entrySize;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (data coming)")
// Send the struct
ret = sendwait(bss, clientfd, &entry, entrySize);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (directory entry)")
}
// No more data will be sent, hence a 0 byte
((int *) buffer)[0] = 0;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (no more data)")
// Done, close the directory also
ret = FSCloseDir(client, commandBlock, handle, FS_RET_ALL_ERROR);
ASSERT_FUNCTION_SUCCEEDED(ret, "FSCloseDir")
} else {
// Send the status
((int *) buffer)[0] = ret;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (error status)")
}
break;
}
case COMMAND_REPLACE_FILE: {
// TODO Write file
// Receive the file path
char file_path[FS_MAX_FULLPATH_SIZE] = {0};
receiveString(bss, clientfd, file_path, FS_MAX_FULLPATH_SIZE);
considerInitializingFileSystem();
// Create an empty file for writing. Its contents will be erased
int handle;
int status = FSOpenFile(client, commandBlock, file_path, "w", &handle, FS_RET_ALL_ERROR);
if (status == FS_STATUS_OK) {
// Send the OK status
((int *) buffer)[0] = status;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (OK status)")
// Set the file handle position to the beginning
ret = FSSetPosFile(client, commandBlock, handle, 0, FS_RET_ALL_ERROR);
ASSERT_FUNCTION_SUCCEEDED(ret, "FSSetPosFile")
// Allocate the file bytes buffer
unsigned int file_buffer_size = 0x2000;
char *fileBuffer = (char *) OSAllocFromSystem(file_buffer_size, FS_IO_BUFFER_ALIGN);
ASSERT_ALLOCATED(fileBuffer, "File buffer")
// Send the maximum file buffer size
ret = sendwait(bss, clientfd, &file_buffer_size, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (maximum file buffer size)")
while (true) {
// Receive the data bytes length
unsigned int dataLength;
ret = recvwait(bss, clientfd, &dataLength, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (File bytes length)")
ASSERT_MAXIMUM_HOLDS(file_buffer_size, dataLength, "File buffer overrun attempted")
if (dataLength > 0) {
// Receive the data
ret = recvwait(bss, clientfd, fileBuffer, dataLength);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (File buffer)")
// Write the data and advance file handle position
ret = FSWriteFile(client, commandBlock, fileBuffer, 1,
dataLength, handle, 0, FS_RET_ALL_ERROR);
ASSERT_FUNCTION_SUCCEEDED(ret, "FSWriteFile")
} else {
// Done
break;
}
}
/*// Flush the file back
ret = FSFlushFile(client, commandBlock, handle, FS_RET_ALL_ERROR);
CHECK_FUNCTION_FAILED(ret, "FSFlushFile")*/
// Close the file
ret = FSCloseFile(client, commandBlock, handle, FS_RET_ALL_ERROR);
ASSERT_FUNCTION_SUCCEEDED(ret, "FSCloseFile")
// Free the file buffer
OSFreeToSystem(fileBuffer);
} else {
// Send the status
((int *) buffer)[0] = status;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (status)")
}
break;
}
case COMMAND_GET_CODE_HANDLER_ADDRESS: {
((int *) buffer)[0] = CODE_HANDLER_INSTALL_ADDRESS;
ret = sendwait(bss, clientfd, buffer, 4);
CHECK_ERROR(ret < 0)
break;
}
case COMMAND_READ_THREADS: {
int OS_THREAD_SIZE = 0x6A0;
int currentThreadAddress = OSGetCurrentThread();
ASSERT_VALID_EFFECTIVE_ADDRESS(currentThreadAddress, "OSGetCurrentThread")
int iterationThreadAddress = currentThreadAddress;
int temporaryThreadAddress;
// Follow "previous thread" pointers back to the beginning
while ((temporaryThreadAddress = *(int *) (iterationThreadAddress + 0x390)) != 0) {
iterationThreadAddress = temporaryThreadAddress;
ASSERT_VALID_EFFECTIVE_ADDRESS(iterationThreadAddress, "iterationThreadAddress going backwards")
}
// Send all threads by following the "next thread" pointers
while ((temporaryThreadAddress = *(int *) (iterationThreadAddress + 0x38C)) != 0) {
// Send the starting thread's address
((int *) buffer)[0] = iterationThreadAddress;
// Send the thread struct itself
memcpy(buffer + 4, (void *) iterationThreadAddress, OS_THREAD_SIZE);
ret = sendwait(bss, clientfd, buffer, 4 + OS_THREAD_SIZE);
CHECK_ERROR(ret < 0)
iterationThreadAddress = temporaryThreadAddress;
ASSERT_VALID_EFFECTIVE_ADDRESS(iterationThreadAddress, "iterationThreadAddress going forwards")
}
// The previous while would skip the last thread so send it also
((int *) buffer)[0] = iterationThreadAddress;
memcpy(buffer + 4, (void *) iterationThreadAddress, OS_THREAD_SIZE);
ret = sendwait(bss, clientfd, buffer, 4 + OS_THREAD_SIZE);
CHECK_ERROR(ret < 0)
// Let the client know that no more threads are coming
((int *) buffer)[0] = 0;
ret = sendwait(bss, clientfd, buffer, 4);
CHECK_ERROR(ret < 0)
break;
}
case COMMAND_ACCOUNT_IDENTIFIER: {
// Acquire the RPL
unsigned int nn_act_handle;
OSDynLoad_Acquire("nn_act.rpl", &nn_act_handle);
// Acquire the functions via their mangled file names
int (*nn_act_Initialize)(void);
OSDynLoad_FindExport(nn_act_handle, 0, "Initialize__Q2_2nn3actFv", &nn_act_Initialize);
ASSERT_ALLOCATED(nn_act_Initialize, "nn_act_Initialize")
unsigned char (*nn_act_GetSlotNo)(void);
OSDynLoad_FindExport(nn_act_handle, 0, "GetSlotNo__Q2_2nn3actFv", &nn_act_GetSlotNo);
ASSERT_ALLOCATED(nn_act_GetSlotNo, "nn_act_GetSlotNo")
unsigned int (*nn_act_GetPersistentIdEx)(unsigned char);
OSDynLoad_FindExport(nn_act_handle, 0, "GetPersistentIdEx__Q2_2nn3actFUc", &nn_act_GetPersistentIdEx);
ASSERT_ALLOCATED(nn_act_GetPersistentIdEx, "nn_act_GetPersistentIdEx")
int (*nn_act_Finalize)(void);
OSDynLoad_FindExport(nn_act_handle, 0, "Finalize__Q2_2nn3actFv", &nn_act_Finalize);
ASSERT_ALLOCATED(nn_act_Finalize, "nn_act_Finalize")
// Get the identifier
ret = nn_act_Initialize();
// ASSERT_INTEGER(ret, 1, "Initializing account library");
unsigned char slotNumber = nn_act_GetSlotNo();
unsigned int persistentIdentifier = nn_act_GetPersistentIdEx(slotNumber);
ret = nn_act_Finalize();
ASSERT_FUNCTION_SUCCEEDED(ret, "nn_act_Finalize");
// Send it
ret = sendwait(bss, clientfd, &persistentIdentifier, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (persistent identifier)")
break;
}
case COMMAND_WRITE_SCREEN: {
char message[WRITE_SCREEN_MESSAGE_BUFFER_SIZE];
ret = recvwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (write screen seconds)")
int seconds = ((int *) buffer)[0];
receiveString(bss, clientfd, message, WRITE_SCREEN_MESSAGE_BUFFER_SIZE);
writeScreen(message, seconds);
break;
}
case COMMAND_FOLLOW_POINTER: {
ret = recvwait(bss, clientfd, buffer, 8);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (Pointer address and offsets count)")
// Retrieve the pointer address and amount of offsets
int baseAddress = ((int *) buffer)[0];
int offsetsCount = ((int *) buffer)[1];
// Receive the offsets
ret = recvwait(bss, clientfd, buffer, offsetsCount * 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (offsets)")
int offsets[offsetsCount];
int offsetIndex = 0;
for (; offsetIndex < offsetsCount; offsetIndex++) {
offsets[offsetIndex] = ((int *) buffer)[offsetIndex];
}
int destinationAddress = baseAddress;
if (isValidDataAddress(destinationAddress)) {
// Apply pointer offsets
for (offsetIndex = 0; offsetIndex < offsetsCount; offsetIndex++) {
int pointerValue = *(int *) destinationAddress;
int offset = offsets[offsetIndex];
destinationAddress = pointerValue + offset;
// Validate the pointer address
bool isValidDestinationAddress = isValidDataAddress(destinationAddress);
// Bail out if invalid
if (!isValidDestinationAddress) {
destinationAddress = -1;
break;
}
}
} else {
destinationAddress = -1;
}
// Return the destination address
((int *) buffer)[0] = destinationAddress;
ret = sendwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (destination address)")
break;
}
case COMMAND_SERVER_STATUS: {
ret = sendbyte(bss, clientfd, 1);
CHECK_ERROR(ret < 0)
break;
}
case COMMAND_RPC: {
long long (*fun)(int, int, int, int, int, int, int, int);
int r3, r4, r5, r6, r7, r8, r9, r10;
long long result;
ret = recvwait(bss, clientfd, buffer, 4 + 8 * 4);
CHECK_ERROR(ret < 0);
fun = ((void **) buffer)[0];
r3 = ((int *) buffer)[1];
r4 = ((int *) buffer)[2];
r5 = ((int *) buffer)[3];
r6 = ((int *) buffer)[4];
r7 = ((int *) buffer)[5];
r8 = ((int *) buffer)[6];
r9 = ((int *) buffer)[7];
r10 = ((int *) buffer)[8];
result = fun(r3, r4, r5, r6, r7, r8, r9, r10);
((long long *) buffer)[0] = result;
ret = sendwait(bss, clientfd, buffer, 8);
CHECK_ERROR(ret < 0)
break;
}
case COMMAND_GET_SYMBOL: {
int size = recvbyte(bss, clientfd);
CHECK_ERROR(size < 0)
ret = recvwait(bss, clientfd, buffer, size);
CHECK_ERROR(ret < 0)
/* Identify the RPL name and symbol name */
char *rplname = (char *) &((int *) buffer)[2];
char *symname = (char *) (&buffer[0] + ((int *) buffer)[1]);
/* Get the symbol and store it in the buffer */
unsigned int module_handle, function_address;
OSDynLoad_Acquire(rplname, &module_handle);
char data = (char) recvbyte(bss, clientfd);
OSDynLoad_FindExport(module_handle, data, symname, &function_address);
((int *) buffer)[0] = (int) function_address;
ret = sendwait(bss, clientfd, buffer, 4);
CHECK_ERROR(ret < 0)
break;
}
case COMMAND_MEMORY_SEARCH: {
// Receive the initial data
ret = recvwait(bss, clientfd, buffer, 4 * 6);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (memory search information)")
int bufferIndex = 0;
int startingAddress = ((int *) buffer)[bufferIndex++];
int length = ((int *) buffer)[bufferIndex++];
int kernelRead = ((int *) buffer)[bufferIndex++];
int resultsLimit = ((int *) buffer)[bufferIndex++];
int aligned = ((int *) buffer)[bufferIndex++];
int searchBytesCount = ((int *) buffer)[bufferIndex];
// Receive the search bytes
char searchBytes[searchBytesCount];
ret = recvwait(bss, clientfd, searchBytes, searchBytesCount);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (memory search bytes)")
int iterationIncrement = aligned ? searchBytesCount : 1;
int searchBytesOccurrences = 0;
// Perform the bytes search and collect the results
for (int currentAddress = startingAddress;
currentAddress < startingAddress + length;
currentAddress += iterationIncrement) {
int comparisonResult;
if (kernelRead) {
comparisonResult = kernelMemoryCompare((void *) currentAddress, searchBytes, searchBytesCount);
} else {
comparisonResult = memcmp((void *) currentAddress, searchBytes, searchBytesCount);
}
if (comparisonResult == 0) {
// Search bytes have been found
((int *) buffer)[1 + searchBytesOccurrences] = currentAddress;
searchBytesOccurrences++;
if ((resultsLimit == searchBytesOccurrences)
|| (searchBytesOccurrences == ((DATA_BUFFER_SIZE / 4) - 1))) {
// We bail out
break;
}
}
}
((int *) buffer)[0] = searchBytesOccurrences * 4;
ret = sendwait(bss, clientfd, buffer, 4 + (searchBytesOccurrences * 4));
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (Sending search bytes occurrences)")
break;
}
/*case COMMAND_SYSTEM_CALL: {
ret = recvwait(bss, clientfd, buffer, 4);
ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (system call)")
int value = ((int *) buffer)[0];
performSystemCall(value);
break;
}*/
case COMMAND_EXECUTE_ASSEMBLY: {
// Receive the assembly
receiveString(bss, clientfd, (char *) buffer, DATA_BUFFER_SIZE);
// Write the assembly to an executable code region
int destinationAddress = 0x10000000 - DATA_BUFFER_SIZE;
pygecko_memcpy((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE);
// Execute the assembly from there
void (*function)() = (void (*)()) destinationAddress;
function();
// Clear the memory contents again
memset((void *) buffer, 0, DATA_BUFFER_SIZE);
pygecko_memcpy((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE);
break;
}
case COMMAND_SERVER_VERSION: {
char versionBuffer[50];
strcpy(versionBuffer, SERVER_VERSION);
int versionLength = strlen(versionBuffer);
((int *) buffer)[0] = versionLength;
memcpy(buffer + 4, versionBuffer, versionLength);
// Send the length and the version string
ret = sendwait(bss, clientfd, buffer, 4 + versionLength);
ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (server version)");
break;
}
case COMMAND_OS_VERSION: {
((int *) buffer)[0] = (int) OS_FIRMWARE;
ret = sendwait(bss, clientfd, buffer, 4);
CHECK_ERROR(ret < 0)
break;
}
case COMMAND_RUN_KERNEL_COPY_SERVICE: {
if (!kernelCopyServiceStarted) {
kernelCopyServiceStarted = true;
startKernelCopyService();
}
break;
}
default:
reportIllegalCommandByte(ret);
break;
}
}
error:
bss->error = ret;
return 0;
}
static int start(int argc, void *argv) {
int sockfd = -1, clientfd = -1, ret = 0, len;
struct sockaddr_in addr;
struct pygecko_bss_t *bss = argv;
setup_os_exceptions();
socket_lib_init();
while (1) {
addr.sin_family = AF_INET;
addr.sin_port = 7331;
addr.sin_addr.s_addr = 0;
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
CHECK_ERROR(sockfd == -1)
ret = bind(sockfd, (void *) &addr, 16);
CHECK_ERROR(ret < 0)
ret = listen(sockfd, 20);
CHECK_ERROR(ret < 0)
while (1) {
len = 16;
clientfd = accept(sockfd, (void *) &addr, &len);
CHECK_ERROR(clientfd == -1)
ret = rungecko(bss, clientfd);
CHECK_ERROR(ret < 0)
socketclose(clientfd);
clientfd = -1;
}
error:
if (clientfd != -1)
socketclose(clientfd);
if (sockfd != -1)
socketclose(sockfd);
bss->error = ret;
// Fix the console freezing when e.g. going to the friend list
GX2WaitForVsync();
}
return 0;
}
static int CCThread(int argc, void *argv) {
struct pygecko_bss_t *bss;
bss = memalign(0x40, sizeof(struct pygecko_bss_t));
if (bss == 0)
return 0;
memset(bss, 0, sizeof(struct pygecko_bss_t));
if (OSCreateThread(&bss->thread, start, 1, bss, (u32) bss->stack + sizeof(bss->stack), sizeof(bss->stack), 0,
0xc) == 1) {
OSResumeThread(&bss->thread);
} else {
free(bss);
}
if (CCHandler == 1) {
void (*entrypoint)() = (void *) CODE_HANDLER_INSTALL_ADDRESS;
while (1) {
usleep(9000);
entrypoint();
}
}
return 0;
}
void start_pygecko() {
// Force the debugger to be initialized by default
// writeInt((unsigned int) (OSIsDebuggerInitialized + 0x1C), 0x38000001); // li r3, 1
unsigned int stack = (unsigned int) memalign(0x40, 0x100);
ASSERT_ALLOCATED(stack, "TCP Gecko stack")
stack += 0x100;
void *thread = memalign(0x40, 0x1000);
ASSERT_ALLOCATED(thread, "TCP Gecko thread")
int status = OSCreateThread(thread, CCThread, 1,
NULL, (u32) stack + sizeof(stack),
sizeof(stack), 0,
OS_THREAD_ATTR_AFFINITY_CORE1 | OS_THREAD_ATTR_PINNED_AFFINITY | OS_THREAD_ATTR_DETACH);
ASSERT_INTEGER(status, 1, "Creating TCP Gecko thread")
// OSSetThreadName(thread, "TCP Gecko");
OSResumeThread(thread);
}