From 0e018f8392f3e3ef6e7d40a8ebdb24e57076664c Mon Sep 17 00:00:00 2001 From: BullyWiiPlaza Date: Wed, 24 May 2017 18:48:20 +0200 Subject: [PATCH] Release development version --- src/breakpoints/bit.h | 13 -- src/breakpoints/breakpoints.h | 87 -------- src/breakpoints/stringify.h | 8 - src/dynamic_libs/os_functions.c | 27 ++- src/dynamic_libs/os_functions.h | 18 +- src/entry.c | 19 +- src/entry.h | 6 - src/kernel/syscalls.h | 11 +- src/pygecko.c | 369 +++++++------------------------- src/pygecko.h | 4 - src/system/exception_handler.c | 231 +++++++++----------- src/system/exception_handler.h | 46 ++++ src/utils/utils.c | 27 --- tcpgecko.elf | Bin 142780 -> 141988 bytes 14 files changed, 275 insertions(+), 591 deletions(-) delete mode 100644 src/breakpoints/bit.h delete mode 100644 src/breakpoints/breakpoints.h delete mode 100644 src/breakpoints/stringify.h delete mode 100644 src/entry.h delete mode 100644 src/utils/utils.c diff --git a/src/breakpoints/bit.h b/src/breakpoints/bit.h deleted file mode 100644 index b1facd6..0000000 --- a/src/breakpoints/bit.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef TCPGECKO_BIT_UTILITIES_H -#define TCPGECKO_BIT_UTILITIES_H - -// http://stackoverflow.com/a/47990/3764804 -inline unsigned int setBit(unsigned int value, bool bit, int bitIndex) { - return value | (bit << bitIndex); -} - -inline bool getBit(unsigned int value, int bitIndex) { - return ((value >> bitIndex) & 1) == 1; -} - -#endif \ No newline at end of file diff --git a/src/breakpoints/breakpoints.h b/src/breakpoints/breakpoints.h deleted file mode 100644 index 73f2e2b..0000000 --- a/src/breakpoints/breakpoints.h +++ /dev/null @@ -1,87 +0,0 @@ -#include "bit.h" -#include "stringify.h" - -#ifndef TCPGECKO_BREAKPOINTS_H -#define TCPGECKO_BREAKPOINTS_H - -/* Breakpoint types -#define BREAKPOINT_READ 0x01 -#define BREAKPOINT_WRITE 0x02 */ - -struct DataBreakpoint { - unsigned int value; - bool translate; - bool write; - bool read; -} __attribute__((packed)); - -// Special purpose registers -#define INSTRUCTION_ADDRESS_BREAKPOINT_REGISTER 0x3F2 -#define DATA_ADDRESS_BREAKPOINT_REGISTER 0x3F5 - -// Data address breakpoint bit indices -#define TRANSLATE_BIT_INDEX 29 -#define WRITE_BIT_INDEX 30 -#define READ_BIT_INDEX 31 - -// http://www.ds.ewi.tudelft.nl/vakken/in1006/instruction-set/mtspr.html -#define moveToSpecialPurposeRegister(specialPurposeRegister, value) \ - __asm__ __volatile__ ("mtspr %0, %1" : : "K" (specialPurposeRegister), "r" (value)) \ - -// http://elixir.free-electrons.com/linux/v2.6.24/source/include/asm-powerpc/reg.h#L713 -#define moveFromSpecialPurposeRegister(specialPurposeRegister) \ -({unsigned int value; \ -asm volatile("mfspr %0, " __stringify(specialPurposeRegister) : "=r" (value)); \ -value;}) \ - - -// https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.alangref/idalangref_isync_ics_instrs.htm -static inline void synchronizeInstructions() { - __asm__ __volatile__ ("isync" : : : "memory"); -} - -// https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.alangref/idalangref_eieio_instrs.htm -static inline void enforeInOrderExecutionOfIO() { - __asm__ __volatile__ ("eieio" : : : "memory"); -} - -static inline void setDataAddressBreakpointRegister(struct DataBreakpoint dataBreakpoint) { - unsigned int value = dataBreakpoint.value; - - // Breakpoint translation - value = setBit(value, dataBreakpoint.translate, TRANSLATE_BIT_INDEX); - - // Write breakpoint - value = setBit(value, dataBreakpoint.write, WRITE_BIT_INDEX); - - // Read breakpoint - value = setBit(value, dataBreakpoint.read, READ_BIT_INDEX); - - moveToSpecialPurposeRegister(DATA_ADDRESS_BREAKPOINT_REGISTER, value); - synchronizeInstructions(); -} - -static inline struct DataBreakpoint getDataAddressBreakpointRegisterContents(struct DataBreakpoint dataBreakpoint) { - unsigned int value = moveFromSpecialPurposeRegister(DATA_ADDRESS_BREAKPOINT_REGISTER); - dataBreakpoint.translate = getBit(value, TRANSLATE_BIT_INDEX); - dataBreakpoint.write = getBit(value, WRITE_BIT_INDEX); - dataBreakpoint.read = getBit(value, READ_BIT_INDEX); - value = setBit(value, 0, TRANSLATE_BIT_INDEX); - value = setBit(value, 0, WRITE_BIT_INDEX); - value = setBit(value, 0, READ_BIT_INDEX); - dataBreakpoint.value = value; - - return dataBreakpoint; -} - -// https://www.manualslib.com/manual/606065/Ibm-Powerpc-750gx.html?page=64 -static inline void setInstructionAddressBreakpointRegister(unsigned int address) { - moveToSpecialPurposeRegister(INSTRUCTION_ADDRESS_BREAKPOINT_REGISTER, address); - synchronizeInstructions(); -} - -static inline int getInstructionAddressBreakpointRegister() { - return moveFromSpecialPurposeRegister(INSTRUCTION_ADDRESS_BREAKPOINT_REGISTER); -} - -#endif \ No newline at end of file diff --git a/src/breakpoints/stringify.h b/src/breakpoints/stringify.h deleted file mode 100644 index b4dd0a7..0000000 --- a/src/breakpoints/stringify.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef TCPGECKO_STRING_H -#define TCPGECKO_STRING_H - -// http://elixir.free-electrons.com/linux/v2.6.24/source/include/linux/stringify.h#L9 -#define __stringify_1(x) #x -#define __stringify(x) __stringify_1(x) - -#endif \ No newline at end of file diff --git a/src/dynamic_libs/os_functions.c b/src/dynamic_libs/os_functions.c index a7cf516..ea07ea6 100644 --- a/src/dynamic_libs/os_functions.c +++ b/src/dynamic_libs/os_functions.c @@ -52,6 +52,20 @@ EXPORT_DECL(int, OSCreateThread, void *thread, s32(*callback)(s32, void * ), s32 priority, u32 attr); +EXPORT_DECL(void, OSEnableInterrupts, void); + +EXPORT_DECL(void, __OSClearAndEnableInterrupt, void); + +EXPORT_DECL(int, OSIsInterruptEnabled, void); + +EXPORT_DECL(int, OSIsDebuggerPresent, void); + +EXPORT_DECL(void, OSRestoreInterrupts, void); + +EXPORT_DECL(void, OSSetDABR, int, int, int, int); + +EXPORT_DECL(void, OSSetIABR, int, int); + EXPORT_DECL(int, OSGetCurrentThread, void); EXPORT_DECL(int, OSResumeThread, void *thread); @@ -103,8 +117,9 @@ EXPORT_DECL(void, __Exit, void); EXPORT_DECL(void, OSFatal, const char *msg); -EXPORT_DECL(void, OSSetExceptionCallback, u8 - exceptionType, exception_callback newCallback); +EXPORT_DECL(void, OSSetExceptionCallback, u8 exceptionType, exception_callback callback); + +EXPORT_DECL(void, __OSSetInterruptHandler, u8 exceptionType, exception_callback callback); EXPORT_DECL(void, DCFlushRange, const void *addr, u32 length); @@ -250,6 +265,7 @@ void InitOSFunctionPointers(void) { OS_FIND_EXPORT(coreinit_handle, OSFatal); OS_FIND_EXPORT(coreinit_handle, OSGetTitleID); OS_FIND_EXPORT(coreinit_handle, OSSetExceptionCallback); + OS_FIND_EXPORT(coreinit_handle, __OSSetInterruptHandler); OS_FIND_EXPORT(coreinit_handle, DCFlushRange); OS_FIND_EXPORT(coreinit_handle, ICInvalidateRange); OS_FIND_EXPORT(coreinit_handle, OSEffectiveToPhysical); @@ -279,6 +295,13 @@ void InitOSFunctionPointers(void) { //!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- //! Thread functions //!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + OS_FIND_EXPORT(coreinit_handle, OSEnableInterrupts); + OS_FIND_EXPORT(coreinit_handle, __OSClearAndEnableInterrupt); + OS_FIND_EXPORT(coreinit_handle, OSIsInterruptEnabled); + OS_FIND_EXPORT(coreinit_handle, OSIsDebuggerPresent); + OS_FIND_EXPORT(coreinit_handle, OSRestoreInterrupts); + OS_FIND_EXPORT(coreinit_handle, OSSetDABR); + OS_FIND_EXPORT(coreinit_handle, OSSetIABR); OS_FIND_EXPORT(coreinit_handle, OSGetCurrentThread); OS_FIND_EXPORT(coreinit_handle, OSCreateThread); OS_FIND_EXPORT(coreinit_handle, OSResumeThread); diff --git a/src/dynamic_libs/os_functions.h b/src/dynamic_libs/os_functions.h index 2839bc8..6383b80 100644 --- a/src/dynamic_libs/os_functions.h +++ b/src/dynamic_libs/os_functions.h @@ -140,6 +140,20 @@ extern int (*OSCreateThread)(void *thread, s32 (*callback)(s32, void *), s32 argc, void *args, u32 stack, u32 stack_size, s32 priority, u32 attr); +extern void (*OSEnableInterrupts)(void); + +extern void (*__OSClearAndEnableInterrupt)(void); + +extern int (*OSIsInterruptEnabled)(void); + +extern int (*OSIsDebuggerPresent)(void); + +extern void (*OSRestoreInterrupts)(void); + +extern void (*OSSetDABR)(int, int, int, int); + +extern void (*OSSetIABR)(int, int); + extern int (*OSGetCurrentThread)(void); extern int (*OSResumeThread)(void *thread); @@ -216,7 +230,9 @@ extern int (*OSScreenEnableEx)(unsigned int bufferNum, int enable); typedef unsigned char (*exception_callback)(void *interruptedContext); -extern void (*OSSetExceptionCallback)(u8 exceptionType, exception_callback newCallback); +extern void (*OSSetExceptionCallback)(u8 exceptionType, exception_callback callback); + +extern void (*__OSSetInterruptHandler)(u8 interruptType, exception_callback callback); extern int (*OSAllocFromSystem)(unsigned int size, unsigned int align); diff --git a/src/entry.c b/src/entry.c index 28f12c8..683742c 100644 --- a/src/entry.c +++ b/src/entry.c @@ -5,25 +5,10 @@ #include "pygecko.h" #include "main.h" #include "utils/logger.h" - -#define TITLE_ID_MII_VERSE 0x000500301001600A -#define TITLE_ID_MII_MAKER 0x000500101004A000 -#define TITLE_ID_BAYONETTA_2 0x0005000010172500 -#define TITLE_ID_INTERNET_BROWSER 0x000500301001200A - -bool isRunningTitleID(unsigned long long int japaneseTitleID) { - unsigned long long int currentTitleID = (unsigned long long int) OSGetTitleID(); - return currentTitleID == japaneseTitleID // JAP - || currentTitleID == japaneseTitleID + 0x100 // USA - || currentTitleID == japaneseTitleID + 0x200; // EUR -} +#include "title.h" int __entry_menu(int argc, char **argv) { - if (OSGetTitleID != 0 - && !isRunningTitleID(TITLE_ID_MII_VERSE) - && !isRunningTitleID(TITLE_ID_MII_MAKER) - && !isRunningTitleID(TITLE_ID_BAYONETTA_2) - && !isRunningTitleID(TITLE_ID_INTERNET_BROWSER)) { + if (isRunningAllowedTitleID()) { InitOSFunctionPointers(); InitSocketFunctionPointers(); InitGX2FunctionPointers(); diff --git a/src/entry.h b/src/entry.h deleted file mode 100644 index fbeec53..0000000 --- a/src/entry.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef TCPGECKO_ENTRY_H -#define TCPGECKO_ENTRY_H - -bool isRunningTitleID(unsigned long long int japaneseTitleID); - -#endif \ No newline at end of file diff --git a/src/kernel/syscalls.h b/src/kernel/syscalls.h index 2eaefa7..283aa0d 100644 --- a/src/kernel/syscalls.h +++ b/src/kernel/syscalls.h @@ -7,19 +7,24 @@ extern "C" { #include #include "common/kernel_defs.h" +#include "../common/kernel_defs.h" void KernelSetupSyscalls(void); + void KernelRestoreInstructions(void); void SC0x25_KernelCopyData(unsigned int addr, unsigned int src, unsigned int len); -void SC0x36_KernelReadDBATs(bat_table_t * table); -void SC0x37_KernelWriteDBATs(bat_table_t * table); + +void SC0x36_KernelReadDBATs(bat_table_t *table); + +void SC0x37_KernelWriteDBATs(bat_table_t *table); uint32_t __attribute__ ((noinline)) kern_read(const void *addr); + void __attribute__ ((noinline)) kern_write(void *addr, uint32_t value); #ifdef __cplusplus } #endif -#endif // __KERNEL_FUNCTIONS_H_ +#endif \ No newline at end of file diff --git a/src/pygecko.c b/src/pygecko.c index aea241e..dc4df3f 100644 --- a/src/pygecko.c +++ b/src/pygecko.c @@ -15,7 +15,13 @@ #include "system/exception_handler.h" #include "utils/logger.h" #include "system/memory.h" -#include "breakpoints/breakpoints.h" +#include "system/breakpoints.h" +#include "system/threads.h" +#include "utils/linked_list.h" +#include "assertions.h" +#include "kernel.h" +#include "address.h" +#include "disassembler.h" void *client; void *commandBlock; @@ -48,12 +54,11 @@ struct pygecko_bss_t { #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_WRITE_SCREEN 0x58 // TODO Exception DSI #define COMMAND_FOLLOW_POINTER 0x60 #define COMMAND_REMOTE_PROCEDURE_CALL 0x70 #define COMMAND_GET_SYMBOL 0x71 #define COMMAND_MEMORY_SEARCH 0x73 -// #define COMMAND_SYSTEM_CALL 0x80 #define COMMAND_EXECUTE_ASSEMBLY 0x81 #define COMMAND_PAUSE_CONSOLE 0x82 #define COMMAND_RESUME_CONSOLE 0x83 @@ -61,74 +66,23 @@ struct pygecko_bss_t { #define COMMAND_SERVER_VERSION 0x99 #define COMMAND_GET_OS_VERSION 0x9A #define COMMAND_SET_DATA_BREAKPOINT 0xA0 -#define COMMAND_GET_DATA_BREAKPOINT 0xA1 #define COMMAND_SET_INSTRUCTION_BREAKPOINT 0xA2 -#define COMMAND_GET_INSTRUCTION_BREAKPOINT 0xA3 #define COMMAND_RUN_KERNEL_COPY_SERVICE 0xCD -#define COMMAND_IOSUHAX_READ_FILE 0xD0 +#define COMMAND_IOSU_HAX_READ_FILE 0xD0 #define COMMAND_GET_VERSION_HASH 0xE0 #define CHECK_ERROR(cond) if (cond) { bss->line = __LINE__; goto error; } #define errno (*__gh_errno_ptr()) -#define MSG_DONTWAIT 32 +#define MSG_DONT_WAIT 32 #define EWOULDBLOCK 6 #define DATA_BUFFER_SIZE 0x5000 -#define WRITE_SCREEN_MESSAGE_BUFFER_SIZE 100 +// #define WRITE_SCREEN_MESSAGE_BUFFER_SIZE 100 #define SERVER_VERSION "05/24/2017" #define ONLY_ZEROS_READ 0xB0 #define NON_ZEROS_READ 0xBD #define VERSION_HASH 0x39C9444B -#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)); @@ -143,75 +97,20 @@ 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; -}*/ +// ########## Being kernel_copy.h ############ +// TODO Variable size, not hard-coded 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); + SC0x25_KernelCopyData((unsigned int) OSEffectiveToPhysical(destinationBuffer), (unsigned int) &memcpy_buffer, length); DCFlushRange(destinationBuffer, (u32) length); } -unsigned char *kernelCopyBuffer[4]; +// ########## End kernel_copy.h ############ -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); -} +// ########## Being pause.h ############ int (*AVMGetDRCScanMode)(int); @@ -255,10 +154,9 @@ bool isConsolePaused() { return value == PAUSED; } -/*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); -} +// ########## End pause.h ############ + +// ########## Being socket_functions.h ############ static int recvwait(struct pygecko_bss_t *bss, int sock, void *buffer, int len) { int ret; @@ -288,7 +186,7 @@ static int checkbyte(int sock) { unsigned char buffer[1]; int ret; - ret = recv(sock, buffer, 1, MSG_DONTWAIT); + ret = recv(sock, buffer, 1, MSG_DONT_WAIT); if (ret < 0) return ret; if (ret == 0) return -1; return buffer[0]; @@ -315,32 +213,6 @@ static int sendByte(struct pygecko_bss_t *bss, int sock, unsigned char 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, @@ -360,6 +232,22 @@ void receiveString(struct pygecko_bss_t *bss, } } +// ########## End socket_functions.h ############ + +/*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 considerInitializingFileSystem() { if (!client) { // Initialize the file system @@ -382,45 +270,6 @@ void considerInitializingFileSystem() { } } -char *disassemblerBuffer; -void *disassemblerBufferPointer; -#define DISASSEMBLER_BUFFER_SIZE 0x1024 - -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 150 void reportIllegalCommandByte(int commandByte) { @@ -431,37 +280,6 @@ void reportIllegalCommandByte(int commandByte) { OSFatal(errorBuffer); } -#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 processCommands(struct pygecko_bss_t *bss, int clientfd) { int ret; @@ -1043,7 +861,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { break; } - case COMMAND_IOSUHAX_READ_FILE: { + case COMMAND_IOSU_HAX_READ_FILE: { log_print("COMMAND_IOSUHAX_READ_FILE"); // TODO Crashes console on this call @@ -1139,43 +957,31 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { break; } case COMMAND_READ_THREADS: { - int OS_THREAD_SIZE = 0x6A0; + struct node *threads = getAllThreads(); + int threadCount = length(threads); + log_printf("Thread Count: %i\n", threadCount); - int currentThreadAddress = OSGetCurrentThread(); - ASSERT_VALID_EFFECTIVE_ADDRESS(currentThreadAddress, "OSGetCurrentThread") - int iterationThreadAddress = currentThreadAddress; - int temporaryThreadAddress; + // Send the thread count + log_print("Sending thread count...\n"); + ((int *) buffer)[0] = threadCount; + ret = sendwait(bss, clientfd, buffer, sizeof(int)); + ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (thread count)"); - // 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 the thread addresses and data + struct node* currentThread = threads; + while (currentThread != NULL) { + int data = (int) currentThread->data; + log_printf("Thread data: %08x\n", data); + ((int *) buffer)[0] = (int) currentThread->data; + memcpy(buffer + sizeof(int), currentThread->data, THREAD_SIZE); + log_print("Sending node...\n"); + ret = sendwait(bss, clientfd, buffer, sizeof(int) + THREAD_SIZE); + ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (thread address and data)") + + currentThread = currentThread->next; } - // 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) + destroy(threads); break; } @@ -1212,7 +1018,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { break; } - case COMMAND_WRITE_SCREEN: { + /*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)") @@ -1221,7 +1027,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { writeScreen(message, seconds); break; - } + }*/ case COMMAND_FOLLOW_POINTER: { ret = recvwait(bss, clientfd, buffer, 8); ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (Pointer address and offsets count)") @@ -1374,16 +1180,6 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { 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); @@ -1441,47 +1237,30 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { break; } case COMMAND_SET_DATA_BREAKPOINT: { - ret = recvwait(bss, clientfd, buffer, 4 + 3 * 1); + // Read the data from the client + ret = recvwait(bss, clientfd, buffer, sizeof(int) + sizeof(bool) * 2); ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (data breakpoint)"); + + // Parse the data and set the breakpoint int bufferIndex = 0; unsigned int address = ((unsigned int *) buffer)[bufferIndex]; - bufferIndex += 4; - bool translate = buffer[bufferIndex++]; - bool write = buffer[bufferIndex++]; + bufferIndex += sizeof(int); bool read = buffer[bufferIndex]; - struct DataBreakpoint dataBreakpoint; - dataBreakpoint.value = address; - dataBreakpoint.translate = translate; - dataBreakpoint.write = write; - dataBreakpoint.read = read; - setDataAddressBreakpointRegister(dataBreakpoint); - - break; - } - case COMMAND_GET_DATA_BREAKPOINT: { - struct DataBreakpoint dataBreakpoint; - getDataAddressBreakpointRegisterContents(dataBreakpoint); - int structureSize = sizeof(dataBreakpoint); - memcpy(buffer, (const void *) &dataBreakpoint, structureSize); - ret = sendwait(bss, clientfd, buffer, structureSize); - ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (data breakpoint)"); + bufferIndex += sizeof(bool); + bool write = buffer[bufferIndex]; + bufferIndex += sizeof(bool); + setDataAddressBreakPointRegister(address, read, write); break; } case COMMAND_SET_INSTRUCTION_BREAKPOINT: { - // Read the address and set the breakpoint execute + // Read the address ret = recvwait(bss, clientfd, buffer, sizeof(int)); ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (instruction breakpoint)"); - unsigned int address = ((unsigned int *) buffer)[0]; - setInstructionAddressBreakpointRegister(address); - break; - } - case COMMAND_GET_INSTRUCTION_BREAKPOINT: { - int address = getInstructionAddressBreakpointRegister(); - ((int *) buffer)[0] = address; - ret = sendwait(bss, clientfd, buffer, sizeof(int)); - ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (instruction breakpoint)"); + // Parse the address and set the breakpoint + unsigned int address = ((unsigned int *) buffer)[0]; + setInstructionAddressBreakPointRegister(address); break; } @@ -1513,7 +1292,7 @@ struct pygecko_bss_t *bss; static int runTCPGeckoServer(int argc, void *argv) { bss = argv; - setup_os_exceptions(); + // setup_os_exceptions(); socket_lib_init(); log_init(COMPUTER_IP_ADDRESS); diff --git a/src/pygecko.h b/src/pygecko.h index 1945fdd..d483633 100644 --- a/src/pygecko.h +++ b/src/pygecko.h @@ -1,15 +1,11 @@ #ifndef _PYGECKO_H_ #define _PYGECKO_H_ -#include "common/types.h" -#include "dynamic_libs/os_functions.h" - /* Main */ #ifdef __cplusplus extern "C" { #endif -//! C wrapper for our C++ functions void startTCPGecko(void); #ifdef __cplusplus diff --git a/src/system/exception_handler.c b/src/system/exception_handler.c index d5b63b2..9c7bee0 100644 --- a/src/system/exception_handler.c +++ b/src/system/exception_handler.c @@ -3,167 +3,142 @@ #include "../utils/logger.h" #include "exception_handler.h" -#define OS_EXCEPTION_MODE_GLOBAL_ALL_CORES 4 - -#define OS_EXCEPTION_DSI 2 -#define OS_EXCEPTION_ISI 3 -#define OS_EXCEPTION_PROGRAM 6 - -/* Exceptions */ -typedef struct OSContext -{ - /* OSContext identifier */ - uint32_t tag1; - uint32_t tag2; - - /* GPRs */ - uint32_t gpr[32]; - - /* Special registers */ - uint32_t cr; - uint32_t lr; - uint32_t ctr; - uint32_t xer; - - /* Initial PC and MSR */ - uint32_t srr0; - uint32_t srr1; - - /* Only valid during DSI exception */ - uint32_t exception_specific0; - uint32_t exception_specific1; - - /* There is actually a lot more here but we don't need the rest*/ -} OSContext; - -#define CPU_STACK_TRACE_DEPTH 10 -#define __stringify(rn) #rn - -#define mfspr(_rn) \ -({ register uint32_t _rval = 0; \ - asm volatile("mfspr %0," __stringify(_rn) \ - : "=r" (_rval));\ - _rval; \ -}) - -typedef struct _framerec { - struct _framerec *up; - void *lr; -} frame_rec, *frame_rec_t; - static const char *exception_names[] = { - "DSI", - "ISI", - "PROGRAM" + "DSI", + "ISI", + "PROGRAM" }; static const char exception_print_formats[18][45] = { - "Exception type %s occurred!\n", // 0 - "GPR00 %08X GPR08 %08X GPR16 %08X GPR24 %08X\n", // 1 - "GPR01 %08X GPR09 %08X GPR17 %08X GPR25 %08X\n", // 2 - "GPR02 %08X GPR10 %08X GPR18 %08X GPR26 %08X\n", // 3 - "GPR03 %08X GPR11 %08X GPR19 %08X GPR27 %08X\n", // 4 - "GPR04 %08X GPR12 %08X GPR20 %08X GPR28 %08X\n", // 5 - "GPR05 %08X GPR13 %08X GPR21 %08X GPR29 %08X\n", // 6 - "GPR06 %08X GPR14 %08X GPR22 %08X GPR30 %08X\n", // 7 - "GPR07 %08X GPR15 %08X GPR23 %08X GPR31 %08X\n", // 8 - "LR %08X SRR0 %08x SRR1 %08x\n", // 9 - "DAR %08X DSISR %08X\n", // 10 - "\nSTACK DUMP:", // 11 - " --> ", // 12 - " -->\n", // 13 - "\n", // 14 - "%p", // 15 - "\nCODE DUMP:\n", // 16 - "%p: %08X %08X %08X %08X\n", // 17 + "Exception type %s occurred!\n", // 0 + "GPR00 %08X GPR08 %08X GPR16 %08X GPR24 %08X\n", // 1 + "GPR01 %08X GPR09 %08X GPR17 %08X GPR25 %08X\n", // 2 + "GPR02 %08X GPR10 %08X GPR18 %08X GPR26 %08X\n", // 3 + "GPR03 %08X GPR11 %08X GPR19 %08X GPR27 %08X\n", // 4 + "GPR04 %08X GPR12 %08X GPR20 %08X GPR28 %08X\n", // 5 + "GPR05 %08X GPR13 %08X GPR21 %08X GPR29 %08X\n", // 6 + "GPR06 %08X GPR14 %08X GPR22 %08X GPR30 %08X\n", // 7 + "GPR07 %08X GPR15 %08X GPR23 %08X GPR31 %08X\n", // 8 + "LR %08X SRR0 %08x SRR1 %08x\n", // 9 + "DAR %08X DSISR %08X\n", // 10 + "\nSTACK DUMP:", // 11 + " --> ", // 12 + " -->\n", // 13 + "\n", // 14 + "%p", // 15 + "\nCODE DUMP:\n", // 16 + "%p: %08X %08X %08X %08X\n", // 17 }; -static unsigned char exception_cb(void * c, unsigned char exception_type) { - char buf[850]; - int pos = 0; +static unsigned char exceptionCallback(void *c, unsigned char exception_type) { + char stackTraceBuffer[850]; + int pos = 0; - OSContext *context = (OSContext *) c; - /* - * This part is mostly from libogc. Thanks to the devs over there. - */ - pos += sprintf(buf + pos, exception_print_formats[0], exception_names[exception_type]); - pos += sprintf(buf + pos, exception_print_formats[1], context->gpr[0], context->gpr[8], context->gpr[16], context->gpr[24]); - pos += sprintf(buf + pos, exception_print_formats[2], context->gpr[1], context->gpr[9], context->gpr[17], context->gpr[25]); - pos += sprintf(buf + pos, exception_print_formats[3], context->gpr[2], context->gpr[10], context->gpr[18], context->gpr[26]); - pos += sprintf(buf + pos, exception_print_formats[4], context->gpr[3], context->gpr[11], context->gpr[19], context->gpr[27]); - pos += sprintf(buf + pos, exception_print_formats[5], context->gpr[4], context->gpr[12], context->gpr[20], context->gpr[28]); - pos += sprintf(buf + pos, exception_print_formats[6], context->gpr[5], context->gpr[13], context->gpr[21], context->gpr[29]); - pos += sprintf(buf + pos, exception_print_formats[7], context->gpr[6], context->gpr[14], context->gpr[22], context->gpr[30]); - pos += sprintf(buf + pos, exception_print_formats[8], context->gpr[7], context->gpr[15], context->gpr[23], context->gpr[31]); - pos += sprintf(buf + pos, exception_print_formats[9], context->lr, context->srr0, context->srr1); + OSContext *context = (OSContext *) c; + /* + * This part is mostly from libogc. Thanks to the devs over there. + */ + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[0], exception_names[exception_type]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[1], context->gpr[0], context->gpr[8], + context->gpr[16], + context->gpr[24]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[2], context->gpr[1], context->gpr[9], + context->gpr[17], + context->gpr[25]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[3], context->gpr[2], context->gpr[10], + context->gpr[18], + context->gpr[26]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[4], context->gpr[3], context->gpr[11], + context->gpr[19], + context->gpr[27]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[5], context->gpr[4], context->gpr[12], + context->gpr[20], + context->gpr[28]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[6], context->gpr[5], context->gpr[13], + context->gpr[21], + context->gpr[29]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[7], context->gpr[6], context->gpr[14], + context->gpr[22], + context->gpr[30]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[8], context->gpr[7], context->gpr[15], + context->gpr[23], + context->gpr[31]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[9], context->lr, context->srr0, context->srr1); //if(exception_type == OS_EXCEPTION_DSI) { - pos += sprintf(buf + pos, exception_print_formats[10], context->exception_specific1, context->exception_specific0); // this freezes + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[10], context->exception_specific1, + context->exception_specific0); // this freezes //} - void *pc = (void*)context->srr0; - void *lr = (void*)context->lr; - void *r1 = (void*)context->gpr[1]; - register uint32_t i = 0; - register frame_rec_t l,p = (frame_rec_t)lr; + void *pc = (void *) context->srr0; + void *lr = (void *) context->lr; + void *r1 = (void *) context->gpr[1]; + register uint32_t currentStackTraceDepth = 0; + register frame_rec_t l, p = (frame_rec_t) lr; l = p; p = r1; - if(!p) - asm volatile("mr %0,%%r1" : "=r"(p)); + if (!p) + asm volatile("mr %0,%%r1" : "=r"(p)); - pos += sprintf(buf + pos, exception_print_formats[11]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[11]); - for(i = 0; i < CPU_STACK_TRACE_DEPTH-1 && p->up; p = p->up, i++) { - if(i % 4) - pos += sprintf(buf + pos, exception_print_formats[12]); + for (currentStackTraceDepth = 0; + currentStackTraceDepth < CPU_STACK_TRACE_DEPTH - 1 && p->up; p = p->up, currentStackTraceDepth++) { + if (currentStackTraceDepth % 4) + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[12]); else { - if(i > 0) - pos += sprintf(buf + pos, exception_print_formats[13]); + if (currentStackTraceDepth > 0) + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[13]); else - pos += sprintf(buf + pos, exception_print_formats[14]); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[14]); } - switch(i) { + switch (currentStackTraceDepth) { case 0: - if(pc) - pos += sprintf(buf + pos, exception_print_formats[15],pc); + if (pc) + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[15], pc); break; case 1: - if(!l) - l = (frame_rec_t)mfspr(8); - pos += sprintf(buf + pos, exception_print_formats[15],(void*)l); + if (!l) + l = (frame_rec_t) mfspr(8); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[15], (void *) l); break; default: - pos += sprintf(buf + pos, exception_print_formats[15],(void*)(p->up->lr)); + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[15], (void *) (p->up->lr)); break; } } //if(exception_type == OS_EXCEPTION_DSI) { - uint32_t *pAdd = (uint32_t*)context->srr0; - pos += sprintf(buf + pos, exception_print_formats[16]); - // TODO by Dimok: this was actually be 3 instead of 2 lines in libogc .... but there is just no more space anymore on the screen - for (i = 0; i < 8; i += 4) - pos += sprintf(buf + pos, exception_print_formats[17], &(pAdd[i]),pAdd[i], pAdd[i+1], pAdd[i+2], pAdd[i+3]); + uint32_t *pAdd = (uint32_t *) context->srr0; + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[16]); + // TODO by Dimok: this was actually be 3 instead of 2 lines in libogc .... but there is just no more space anymore on the screen + for (currentStackTraceDepth = 0; currentStackTraceDepth < 8; currentStackTraceDepth += 4) + pos += sprintf(stackTraceBuffer + pos, exception_print_formats[17], &(pAdd[currentStackTraceDepth]), + pAdd[currentStackTraceDepth], pAdd[currentStackTraceDepth + 1], pAdd[currentStackTraceDepth + 2], + pAdd[currentStackTraceDepth + 3]); //} - log_print(buf); - OSFatal(buf); - return 1; + log_print(stackTraceBuffer); + OSFatal(stackTraceBuffer); + + return 1; } -static unsigned char dsi_exception_cb(void * context) { - return exception_cb(context, 0); -} -static unsigned char isi_exception_cb(void * context) { - return exception_cb(context, 1); -} -static unsigned char program_exception_cb(void * context) { - return exception_cb(context, 2); +static unsigned char dsi_exception_cb(void *context) { + return exceptionCallback(context, 0); } -void setup_os_exceptions(void) { - OSSetExceptionCallback(OS_EXCEPTION_DSI, &dsi_exception_cb); - OSSetExceptionCallback(OS_EXCEPTION_ISI, &isi_exception_cb); - OSSetExceptionCallback(OS_EXCEPTION_PROGRAM, &program_exception_cb); +static unsigned char isi_exception_cb(void *context) { + return exceptionCallback(context, 1); +} + +static unsigned char program_exception_cb(void *context) { + return exceptionCallback(context, 2); +} + +void setup_os_exceptions() { + OSSetExceptionCallback(OS_EXCEPTION_DSI, &dsi_exception_cb); + OSSetExceptionCallback(OS_EXCEPTION_ISI, &isi_exception_cb); + OSSetExceptionCallback(OS_EXCEPTION_PROGRAM, &program_exception_cb); } \ No newline at end of file diff --git a/src/system/exception_handler.h b/src/system/exception_handler.h index 7626f92..a464522 100644 --- a/src/system/exception_handler.h +++ b/src/system/exception_handler.h @@ -5,6 +5,52 @@ extern "C" { #endif +#include "../utils/stringify.h" + +#define OS_EXCEPTION_DSI 2 +#define OS_EXCEPTION_ISI 3 +#define OS_EXCEPTION_PROGRAM 6 + +/* Exceptions */ +typedef struct OSContext { + /* OSContext identifier */ + uint32_t tag1; + uint32_t tag2; + + /* GPRs */ + uint32_t gpr[32]; + + /* Special registers */ + uint32_t cr; + uint32_t lr; + uint32_t ctr; + uint32_t xer; + + /* Initial PC and MSR */ + uint32_t srr0; + uint32_t srr1; + + /* Only valid during DSI exception */ + uint32_t exception_specific0; + uint32_t exception_specific1; + + /* There is actually a lot more here but we don't need the rest*/ +} OSContext; + +#define CPU_STACK_TRACE_DEPTH 10 + +#define mfspr(_rn) \ +({ register uint32_t _rval = 0; \ + asm volatile("mfspr %0," __stringify(_rn) \ + : "=r" (_rval));\ + _rval; \ +}) + +typedef struct _framerec { + struct _framerec *up; + void *lr; +} frame_rec, *frame_rec_t; + void setup_os_exceptions(void); #ifdef __cplusplus diff --git a/src/utils/utils.c b/src/utils/utils.c deleted file mode 100644 index 09123fe..0000000 --- a/src/utils/utils.c +++ /dev/null @@ -1,27 +0,0 @@ - - -void m_DCFlushRange(unsigned int startAddr, unsigned int size) -{ - register unsigned int addr = startAddr & ~0x1F; - register unsigned int end_addr = startAddr + size; - - while(addr < end_addr) - { - asm volatile("dcbf 0, %0" : : "r"(addr)); - addr += 0x20; - } - asm volatile("sync; eieio"); -} - - -void m_DCInvalidateRange(unsigned int startAddr, unsigned int size) -{ - register unsigned int addr = startAddr & ~0x1F; - register unsigned int end_addr = startAddr + size; - - while(addr < end_addr) - { - asm volatile("dcbi 0, %0" : : "r"(addr)); - addr += 0x20; - } -} diff --git a/tcpgecko.elf b/tcpgecko.elf index 26780d1305ca1dda743dadd92687d4cd4fb14789..71a64bf5b217a7a950bd6412dcf07252faa8083c 100644 GIT binary patch delta 29548 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ȨB* z5GJ+FtDxGKA^a+*=9jBfVJ-B)%kkMypG*5GtWmcsd#7%3cDs%cl3|hG!~u(*y}Sij zg=1NG2C+U4&v$D!iRWuF#B=b17and{HNDMmoyCC#U=KyZ(WAW*b_>>Re+vSVyRzFU z|H@qvVh^6d;XxH_iNZ61vLijGkruu-H6o{pw}VXJ4!9yCc=L2KZF+5bMotUJftgHP zj5rAA(%jkD5-1HtKN&I!xEth4V%_%X5tpUFQ^WQ-s+{fAx_!pn+}ZL%N1CMO{v2%+NdaNqP zN$s!0D{t#*6BgD}4}8^@(%#pn$LBa5)4XF;UdGO@K-*rv5$^i|_Ut$2MmVLYRIY9y+0DD+eV-dD>>YRDQAI-DKL?<^Qyk4sEC1Rr*^W^kBO=JaHy z2aoMXZ-w*9;59;Kz7=1FjfyWLzvajTWYBszPdr)EJ*=L+tzi?{k!gn~pWV>7dGrC@ zE)A?zAdms=@fDauojn46ESSABo{&%Q+ljy72`8EUd`HHxdZGKQ75fmN6AxAxAI|8Qqp(F3nt*bMF8z;c4|53qV zKG+++ZJQ=TF9WHFNcTuD8mB-AxCsVsys!I**JiImC5AKoNKh)UMFft2nRw43xA<{P zmY$^qO-e{5>}6QNYw{La9dNe*?-Q&^%(o^;?*$S28UPs^p|YSNz*%|^tO^w>))IelG6_%6TGZ1S5No9IV&hv4-NX(wMIqc>Ve-FkRIa& zNy5AM^)#h5N|mRl^IE3{<>__1;DzGW))}#RdLyZsiT4rUfvh^NZf@{mU;SR2m2?{`Qb+%+9w>?=Dv1_x4?n zsjxV~vlxHcJ@o zyK_*@zW?>N;ZRKrk30)MY2T5L0O{@au0MrM)54T#lkGU?7tJ7F2S0LVu5%JefETOg zqP$V*>?5Ni->&~UM>W$2eNgX@K9~eA*B=CBZuM>Umv0YW4-CKECB~~&xrYCv=F� zqTFrzQ5SGOuU;620a)vDStd!t+&T#a1i$xB;^wkpYq_-ev42E=iRKW(7>5ns4Wuh3 zVZf3WAG*pHpWfoC&~j08#?EKqPca#VkCwq*bhu{H`py>!D&9AY9gP{d(t zTwOD{f>5NxpAbdI$h?%c(BE)x3{(9w#nnECyFWHP1^i-H)t%G|-|cXrEfMas^yn6+ zYjwxcZEgH5+!#)!Yj@4#LF3P_Y%Axv_tdK8kGPW3xo1_&j<>a^bFkdgjjp6j?lDz* zp=)a<_biYV!AE2@N9fmbs=-TcY*u4ZC)1lG_);r9m?iT ztI|{2JUQGPE=p=_*fh-Q;0SxMRq9-LwqRenwM}y?7coM0@~>@)^SG(dG`G!kJNGT8 zI=;naxRZNNRXAn638t#&16PcRJEOuD^SNIDF~t?SklUw9=UoRDf_fChxzuJ*&2&Rs zqM0#K(Y9j|_X=XR72XZ&E+{=EH5YzZ@8SNzp+s}<4qGXsWzRE>JQ5@c3!kw5)sZckm~)x8Lh0GBih}6%V@kY!FFB;1dKU@ZM8;iI(Dg$ZjDB`AbGYs%QbC}=Ast(zYPHNG#5g8x;+0YBlZ`j3G z%MzGdfd5wq>r&?$_PeahxCEE6m>Z+YXm%AAbMYY=jURG^-~DRFR##mymlkCpYqpx- zzy`Y?%GiFmfmkDQcQKTglyDnV8T;3l zf+RAWE<-6uGTrK`C$5r_V80s{z9*@A_(k8ZPSD>Q3`$(JdQEs1FW$16ySh)>B5jU-} zJ%+2FOR;Z)C(7XI(R}gEpIx3RE=|??x@(|{yI<9MyQ@&(mPI6egSdujkuY3%z&u|} z+U4pIxNlLTI_+G+NHu{!ZHZ5F_sLxxivbnF!ev95Za zIQT@Q1V8D4?_hDzLZshV|G)eQ|62n8%l3jafet-VIqs8VQIeeg89DJ-q99-E8-#h<1 zB3B%t%=`+F(4!TX&=r_?9#4@fZ0(mITv>cqk<8T@ljr~Hh?g=}sTnG(ln3x%)4}=2 zMoXF@CgCi&Fdav`;B$^Rj=^_J8F8Fa!zTtliNw)$2)>=fak7OtPA9>Khfghhw&oU9 z7HNOCqO|Du70Z_{tJD^il&mOy^wBi!6l)3}LP%i!14T>AOG`$?K@}l+uJ`wGTnt0gJg~C7e3iBW zTQVB1r*?6>+vdH`%~lKMGU%{Go3ni7vPzJ_QXJ;O(q$#sMWs|%jCFE1mL($So zZK7}OG$PX}0qFvn6yA#c%XF44FDd<%_G3#Fp0ywaEjFL_r@=iO*B1K$7sQ36+?+gX zcJi#0nYXy&_i+mz%qcG|TES3Dmlgf4+)H*r>7$j)S4cl^^{H7{T3Lxh$<4}Mps;|j z_R-Qx9BSS&n2!}JD=Ht=N(4)o5M0mgb!E75!v|LCVNYArAh%vC zBv?gpN}c=(^?{W#`Fqk1s3TJSq&`uc94x_;3q>)(dG(LC4}ZGCQ0H<*bfYN7S4hN_ zZ{No815zE9MH_}-U!17(L*;SqA=np3f=m1>6Ltd8CW!&;oH0--l_`xAVx%?+v!Of} z;7E{0nN&6za1k%chspTJHm6nggRqJ-Twlr{mXxp5qqQj;h^ss5hslLL;Rgk1g z852)%&T%k9qVrPI+=juF!G&Pc;Kfy9M0ltB4GK^Gb n!77U@a24F7g1az0{KuJ2c!SgdS1tUG02tnQB)7pYY(xGZ+$*x+ delta 29804 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