diff --git a/src/breakpoints/bit.h b/src/breakpoints/bit.h new file mode 100644 index 0000000..b1facd6 --- /dev/null +++ b/src/breakpoints/bit.h @@ -0,0 +1,13 @@ +#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 new file mode 100644 index 0000000..73f2e2b --- /dev/null +++ b/src/breakpoints/breakpoints.h @@ -0,0 +1,87 @@ +#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 new file mode 100644 index 0000000..b4dd0a7 --- /dev/null +++ b/src/breakpoints/stringify.h @@ -0,0 +1,8 @@ +#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/entry.c b/src/entry.c index e960fb1..28f12c8 100644 --- a/src/entry.c +++ b/src/entry.c @@ -1,4 +1,3 @@ -#include #include "dynamic_libs/os_functions.h" #include "dynamic_libs/gx2_functions.h" #include "dynamic_libs/socket_functions.h" diff --git a/src/main.c b/src/main.c index 90231c1..158ed0d 100644 --- a/src/main.c +++ b/src/main.c @@ -19,8 +19,6 @@ bool isCodeHandlerInstalled; -// TODO Make sure accessing the browser does not freeze the console - #define PRINT_TEXT2(x, y, ...) { snprintf(messageBuffer, 80, __VA_ARGS__); OSScreenPutFontEx(0, x, y, messageBuffer); OSScreenPutFontEx(1, x, y, messageBuffer); } typedef enum { diff --git a/src/pygecko.c b/src/pygecko.c index b5531ae..eddfefe 100644 --- a/src/pygecko.c +++ b/src/pygecko.c @@ -15,6 +15,7 @@ #include "system/exception_handler.h" #include "utils/logger.h" #include "system/memory.h" +#include "breakpoints/breakpoints.h" void *client; void *commandBlock; @@ -58,7 +59,11 @@ struct pygecko_bss_t { #define COMMAND_RESUME_CONSOLE 0x83 #define COMMAND_IS_CONSOLE_PAUSED 0x84 #define COMMAND_SERVER_VERSION 0x99 -#define COMMAND_OS_VERSION 0x9A +#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_GET_VERSION_HASH 0xE0 @@ -1420,18 +1425,63 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { strcpy(versionBuffer, SERVER_VERSION); int versionLength = strlen(versionBuffer); ((int *) buffer)[0] = versionLength; - memcpy(buffer + 4, versionBuffer, versionLength); + memcpy(buffer + sizeof(int), versionBuffer, versionLength); // Send the length and the version string - ret = sendwait(bss, clientfd, buffer, 4 + versionLength); + ret = sendwait(bss, clientfd, buffer, sizeof(int) + versionLength); ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (server version)"); break; } - case COMMAND_OS_VERSION: { + case COMMAND_GET_OS_VERSION: { ((int *) buffer)[0] = (int) OS_FIRMWARE; - ret = sendwait(bss, clientfd, buffer, 4); - CHECK_ERROR(ret < 0) + ret = sendwait(bss, clientfd, buffer, sizeof(int)); + ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (OS version)"); + + break; + } + case COMMAND_SET_DATA_BREAKPOINT: { + ret = recvwait(bss, clientfd, buffer, 4 + 3 * 1); + ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (data breakpoint)"); + int bufferIndex = 0; + unsigned int address = ((unsigned int *) buffer)[bufferIndex]; + bufferIndex += 4; + bool translate = buffer[bufferIndex++]; + bool write = buffer[bufferIndex++]; + 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)"); + + break; + } + case COMMAND_SET_INSTRUCTION_BREAKPOINT: { + // Read the address and set the breakpoint execute + 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)"); break; } @@ -1443,10 +1493,11 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { break; } - default: + default: { reportIllegalCommandByte(ret); break; + } } } diff --git a/tcpgecko.elf b/tcpgecko.elf index e4b7bad..89308fe 100644 Binary files a/tcpgecko.elf and b/tcpgecko.elf differ