diff --git a/arm11u/Makefile b/arm11u/Makefile new file mode 100644 index 0000000..02d2192 --- /dev/null +++ b/arm11u/Makefile @@ -0,0 +1,38 @@ +CC = arm-none-eabi-gcc +LINK = arm-none-eabi-ld +OBJCOPY = arm-none-eabi-objcopy +CTRULIB = "../libctru" +CFLAGS += -Wall -std=c99 -march=armv6 -O0 -I"$(CTRULIB)/include" +LDFLAGS += --script=ccd00.ld -L"$(DEVKITARM)/arm-none-eabi/lib" -L"$(CTRULIB)/lib" + +CFILES = $(wildcard source/*.c) +OFILES = $(CFILES:source/%.c=build/%.o) +DFILES = $(CFILES:source/%.c=build/%.d) +SFILES = $(wildcard source/*.s) +OFILES += $(SFILES:source/%.s=build/%.o) +PROJECTNAME = ${shell basename "$(CURDIR)"} + +.PHONY:=all + +all: $(PROJECTNAME).bin + +$(PROJECTNAME).bin: $(PROJECTNAME).elf + $(OBJCOPY) -O binary $< $@ + +$(PROJECTNAME).elf: $(OFILES) + $(LINK) $(LDFLAGS) -o $(PROJECTNAME).elf $(filter-out build/crt0.o, $(OFILES)) -lctru -lc + +clean: + @rm -f build/*.o build/*.d + @rm -f $(PROJECTNAME).elf $(PROJECTNAME).bin + @echo "all cleaned up !" + +-include $(DFILES) + +build/%.o: source/%.c + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.o: source/%.s + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d diff --git a/arm11u/source/crt0.s b/arm11u/source/crt0.s new file mode 100644 index 0000000..6463bc2 --- /dev/null +++ b/arm11u/source/crt0.s @@ -0,0 +1,12 @@ +.section ".text" +.arm +.align 4 +.global _init +.global _start + +_start: + blx __libc_init_array + blx main + +_init: + bx lr diff --git a/arm11u/source/main.c b/arm11u/source/main.c new file mode 100644 index 0000000..8842b97 --- /dev/null +++ b/arm11u/source/main.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + Handle srvHandle, aptuHandle; + + getSrvHandle(&srvHandle); + + //initialize APT stuff, escape load screen + srv_getServiceHandle(srvHandle, &aptuHandle, "APT:U"); + APT_GetLockHandle(aptuHandle, 0x0, NULL); + svc_closeHandle(aptuHandle); + svc_sleepThread(0x50000); + + Handle hmEvent; + srv_getServiceHandle(srvHandle, &aptuHandle, "APT:U"); + APT_Initialize(aptuHandle, 0x300, &hmEvent, NULL); + svc_closeHandle(aptuHandle); + svc_sleepThread(0x50000); + + srv_getServiceHandle(srvHandle, &aptuHandle, "APT:U"); + APT_Enable(aptuHandle, 0x0); + svc_closeHandle(aptuHandle); + svc_sleepThread(0x50000); + + //do stuff with GPU... + Handle gspGpuHandle; + srv_getServiceHandle(srvHandle, &gspGpuHandle, "gsp::Gpu"); + + GSPGPU_AcquireRight(gspGpuHandle, 0x0); + GSPGPU_SetLcdForceBlack(gspGpuHandle, 0x0); + + //set subscreen to blue + u32 regData=0x01FF0000; + GSPGPU_WriteHWRegs(gspGpuHandle, 0x202A04, (u8*)®Data, 4); + + //grab main left screen framebuffer addresses + u8* topLeftFramebuffers[2]; + GSPGPU_ReadHWRegs(gspGpuHandle, 0x400468, (u8*)&topLeftFramebuffers, 8); + + //convert PA to VA (assuming FB in VRAM) + topLeftFramebuffers[0]+=0x7000000; + topLeftFramebuffers[1]+=0x7000000; + + //setup our gsp shared mem section + u8 threadID; + Handle gspEvent, gspSharedMemHandle; + svc_createEvent(&gspEvent, 0x0); + GSPGPU_RegisterInterruptRelayQueue(gspGpuHandle, gspEvent, 0x1, &gspSharedMemHandle, &threadID); + svc_mapMemoryBlock(gspSharedMemHandle, 0x10002000, 0x3, 0x10000000); + + //map GSP heap + u8* gspHeap; + svc_controlMemory((u32*)&gspHeap, 0x0, 0x0, 0x2000000, 0x10003, 0x3); + + int i; + for(i=1;i<0x600000;i++) + { + gspHeap[i]=0xFF^gspHeap[i-1]; + } + + //wait until we can write stuff to it + svc_waitSynchronization1(gspEvent, 0x55bcb0); + + // //GSP shared mem : 0x2779F000 + // //write GX command ! (to GSP shared mem, at 0x10002000) + u32* gxCmdBuf=(u32*)(0x10002000+0x800+threadID*0x200); + u32 gxCommand[0x8]; + + //GX RequestDma + gxCommand[0]=0x00; //CommandID + gxCommand[1]=(u32)gspHeap; //source address + // gxCommand[2]=(u32)topLeftFramebuffers[0]; //destination address + gxCommand[2]=0x1F000000; //destination address + // gxCommand[3]=0x5DC00*2; //size + gxCommand[3]=0x600000; //size + gxCommand[4]=gxCommand[5]=gxCommand[6]=gxCommand[7]=0x0; + + GSPGPU_submitGxCommand(gxCmdBuf, gxCommand, gspGpuHandle); + + // debug + regData=0x010000FF; + GSPGPU_WriteHWRegs(gspGpuHandle, 0x202A04, (u8*)®Data, 4); + + svc_waitSynchronization1(hmEvent, 0xffffffffffffffff); + + // debug + regData=0x0100FFFF; + GSPGPU_WriteHWRegs(gspGpuHandle, 0x202A04, (u8*)®Data, 4); + + while(1); + + return 0; +} diff --git a/libctru/Makefile b/libctru/Makefile new file mode 100644 index 0000000..92c78e4 --- /dev/null +++ b/libctru/Makefile @@ -0,0 +1,36 @@ +CC = arm-none-eabi-gcc +AR = arm-none-eabi-ar +CFLAGS += -Wall -std=c99 -march=armv6 -O0 -I"$(CURDIR)/include/" + +CFILES = $(wildcard source/*.c) +OFILES = $(CFILES:source/%.c=build/%.o) +DFILES = $(CFILES:source/%.c=build/%.d) +SFILES = $(wildcard source/*.s) +OFILES += $(SFILES:source/%.s=build/%.o) +PROJECTNAME = "libctru" + +.PHONY:=all dir + +all: dir lib/$(PROJECTNAME).a + +dir: + mkdir -p build + mkdir -p lib + +lib/$(PROJECTNAME).a: $(OFILES) + $(AR) rvs $@ $^ + +clean: + @rm -f build/*.o build/*.d + @rm -f $(PROJECTNAME).a + @echo "all cleaned up !" + +-include $(DFILES) + +build/%.o: source/%.c + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.o: source/%.s + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d diff --git a/libctru/include/ctr/APT.h b/libctru/include/ctr/APT.h new file mode 100644 index 0000000..c184400 --- /dev/null +++ b/libctru/include/ctr/APT.h @@ -0,0 +1,10 @@ +#ifndef APT_H +#define APT_H + +void APT_GetLockHandle(Handle handle, u16 flags, Handle* lockHandle); +void APT_Initialize(Handle handle, u32 a, Handle* eventHandle1, Handle* eventHandle2); +Result APT_Enable(Handle handle, u32 a); +Result APT_PrepareToJumpToHomeMenu(Handle handle); +Result APT_JumpToHomeMenu(Handle handle, u32 a, u32 b, u32 c); + +#endif diff --git a/libctru/include/ctr/GSP.h b/libctru/include/ctr/GSP.h new file mode 100644 index 0000000..b3bae75 --- /dev/null +++ b/libctru/include/ctr/GSP.h @@ -0,0 +1,12 @@ +#ifndef GSP_H +#define GSP_H + +void GSPGPU_AcquireRight(Handle handle, u8 flags); +void GSPGPU_SetLcdForceBlack(Handle handle, u8 flags); +void GSPGPU_WriteHWRegs(Handle handle, u32 regAddr, u8* data, u8 size); +void GSPGPU_ReadHWRegs(Handle handle, u32 regAddr, u8* data, u8 size); +void GSPGPU_RegisterInterruptRelayQueue(Handle handle, Handle eventHandle, u32 flags, Handle* outMemHandle, u8* threadID); +Result GSPGPU_TriggerCmdReqQueue(Handle handle); +Result GSPGPU_submitGxCommand(u32* sharedGspCmdBuf, u32 gxCommand[0x20], Handle handle); + +#endif diff --git a/libctru/include/ctr/srv.h b/libctru/include/ctr/srv.h new file mode 100644 index 0000000..7c3f302 --- /dev/null +++ b/libctru/include/ctr/srv.h @@ -0,0 +1,9 @@ +#ifndef SRV_H +#define SRV_H + + +Result srv_10002(Handle handle); +void getSrvHandle(Handle* out); +void srv_getServiceHandle(Handle handle, Handle* out, char* server); + +#endif diff --git a/libctru/include/ctr/svc.h b/libctru/include/ctr/svc.h new file mode 100644 index 0000000..a692bef --- /dev/null +++ b/libctru/include/ctr/svc.h @@ -0,0 +1,14 @@ +#ifndef SVC_H +#define SVC_H + + u32* svc_getData(void); + void svc_sleepThread(s64 ns); + Result svc_controlMemory(u32* outaddr, u32 addr0, u32 addr1, u32 size, u32 operation, u32 permissions); //(outaddr is usually the same as the input addr0) + Result svc_createEvent(Handle* event, u8 resettype); + Result svc_mapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherpermission); + Result svc_waitSynchronization1(Handle handle, s64 nanoseconds); + Result svc_closeHandle(Handle handle); + Result svc_connectToPort(volatile Handle* out, const char* portName); + Result svc_sendSyncRequest(Handle session); + +#endif diff --git a/libctru/include/ctr/types.h b/libctru/include/ctr/types.h new file mode 100644 index 0000000..5e59346 --- /dev/null +++ b/libctru/include/ctr/types.h @@ -0,0 +1,19 @@ +#ifndef TYPES_H +#define TYPES_H + + #include + + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + typedef unsigned long long u64; + + typedef signed char s8; + typedef signed short s16; + typedef signed int s32; + typedef signed long long s64; + + typedef u32 Handle; + typedef u32 Result; + +#endif diff --git a/libctru/source/APT.c b/libctru/source/APT.c new file mode 100644 index 0000000..853f1b2 --- /dev/null +++ b/libctru/source/APT.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include + +void APT_GetLockHandle(Handle handle, u16 flags, Handle* lockHandle) +{ + u32* svcData=svc_getData(); + svcData[0]=0x10040; //request header code + svcData[1]=flags; + svc_sendSyncRequest(handle); //check return value... + if(lockHandle)*lockHandle=svcData[1]; +} + +void APT_Initialize(Handle handle, u32 a, Handle* eventHandle1, Handle* eventHandle2) +{ + u32* svcData=svc_getData(); + svcData[0]=0x20080; //request header code + svcData[1]=a; + svcData[2]=0x0; + svc_sendSyncRequest(handle); //check return value... + if(eventHandle1)*eventHandle1=svcData[3]; //return to menu event ? + if(eventHandle2)*eventHandle2=svcData[4]; +} + +Result APT_Enable(Handle handle, u32 a) +{ + u32* svcData=svc_getData(); + svcData[0]=0x30040; //request header code + svcData[1]=a; + svc_sendSyncRequest(handle); //check return value... + return svcData[1]; +} + +Result APT_PrepareToJumpToHomeMenu(Handle handle) +{ + u32* svcData=svc_getData(); + svcData[0]=0x2b0000; //request header code + svc_sendSyncRequest(handle); //check return value... + return svcData[1]; +} + +Result APT_JumpToHomeMenu(Handle handle, u32 a, u32 b, u32 c) +{ + u32* svcData=svc_getData(); + svcData[0]=0x2C0044; //request header code + svcData[1]=a; + svcData[2]=b; + svcData[3]=c; + svcData[4]=(b<<14)|2; + svc_sendSyncRequest(handle); //check return value... + return svcData[1]; +} diff --git a/libctru/source/GSP.c b/libctru/source/GSP.c new file mode 100644 index 0000000..9413f2d --- /dev/null +++ b/libctru/source/GSP.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include + + +void GSPGPU_AcquireRight(Handle handle, u8 flags) +{ + u32* svcData=svc_getData(); + svcData[0]=0x160042; //request header code + svcData[1]=flags; + svcData[2]=0x0; + svcData[3]=0xffff8001; + svc_sendSyncRequest(handle); //check return value... +} + +void GSPGPU_SetLcdForceBlack(Handle handle, u8 flags) +{ + u32* svcData=svc_getData(); + svcData[0]=0xB0040; //request header code + svcData[1]=flags; + svc_sendSyncRequest(handle); //check return value... +} + +void GSPGPU_WriteHWRegs(Handle handle, u32 regAddr, u8* data, u8 size) +{ + if(size>0x80 || !data)return; + + u32* svcData=svc_getData(); + svcData[0]=0x10082; //request header code + svcData[1]=regAddr; + svcData[2]=size; + svcData[3]=(size<<14)|2; + svcData[4]=(u32)data; + svc_sendSyncRequest(handle); //check return value... +} + +void GSPGPU_ReadHWRegs(Handle handle, u32 regAddr, u8* data, u8 size) +{ + if(size>0x80 || !data)return; + + u32* svcData=svc_getData(); + svcData[0]=0x40080; //request header code + svcData[1]=regAddr; + svcData[2]=size; + svcData[0x40]=(size<<14)|2; + svcData[0x40+1]=(u32)data; + svc_sendSyncRequest(handle); //check return value... +} + +void GSPGPU_RegisterInterruptRelayQueue(Handle handle, Handle eventHandle, u32 flags, Handle* outMemHandle, u8* threadID) +{ + u32* svcData=svc_getData(); + svcData[0]=0x130042; //request header code + svcData[1]=flags; + svcData[2]=0x0; + svcData[3]=eventHandle; + svc_sendSyncRequest(handle); //check return value... + if(threadID)*threadID=svcData[2]; + if(outMemHandle)*outMemHandle=svcData[4]; +} + +Result GSPGPU_TriggerCmdReqQueue(Handle handle) +{ + u32* svcData=svc_getData(); + svcData[0]=0xC0000; //request header code + svc_sendSyncRequest(handle); //check return value... + return svcData[0]; +} + +//essentially : get commandIndex and totalCommands, calculate offset of new command, copy command and update totalCommands +//use LDREX/STREX because this data may also be accessed by the GSP module and we don't want to break stuff +//(mostly, we could overwrite the buffer header with wrong data and make the GSP module reexecute old commands) +Result GSPGPU_submitGxCommand(u32* sharedGspCmdBuf, u32 gxCommand[0x8], Handle handle) +{ + if(!sharedGspCmdBuf || !gxCommand)return -1; + + u32 cmdBufHeader; + __asm__ ("ldrex %[result], [%[adr]]" : [result] "=r" (cmdBufHeader) : [adr] "r" (sharedGspCmdBuf)); + + u8 commandIndex=cmdBufHeader&0xFF; + u8 totalCommands=(cmdBufHeader>>8)&0xFF; + + if(totalCommands>15)return -2; + + u8 nextCmd=(commandIndex+totalCommands)%15; //there are 15 command slots + u32* dst=&sharedGspCmdBuf[8*(1+nextCmd)]; + memcpy(dst, gxCommand, 0x20); + + u32 mcrVal=0x0; + __asm__ ("mcr p15, 0, %[val], c7, c10, 4" :: [val] "r" (mcrVal)); //Data Synchronization Barrier Register + totalCommands++; + cmdBufHeader=((cmdBufHeader)&0xFFFF00FF)|(((u32)totalCommands)<<8); + + while(1) + { + u32 strexResult; + __asm__ ("strex %[result], %[val], [%[adr]]" : [result] "=r" (strexResult) : [adr] "r" (sharedGspCmdBuf), [val] "r" (cmdBufHeader)); + if(!strexResult)break; + + __asm__ ("ldrex %[result], [%[adr]]" : [result] "=r" (cmdBufHeader) : [adr] "r" (sharedGspCmdBuf)); + totalCommands=((cmdBufHeader&0xFF00)>>8)+1; + cmdBufHeader=((cmdBufHeader)&0xFFFF00FF)|((totalCommands<<8)|0xFF00); + } + + if(totalCommands==1) + { + GSPGPU_TriggerCmdReqQueue(handle); + } + + return 0; +} diff --git a/libctru/source/srv.c b/libctru/source/srv.c new file mode 100644 index 0000000..cf4e4a2 --- /dev/null +++ b/libctru/source/srv.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include + +Result srv_10002(Handle handle) +{ + u32* svcData=svc_getData(); + svcData[0]=0x10002; //request header code + svcData[1]=0x20; + svc_sendSyncRequest(handle); //check return value... + return svcData[1]; +} + +void getSrvHandle(Handle* out) +{ + if(!out)return; + + svc_connectToPort(out, "srv:"); + srv_10002(*out); +} + +void srv_getServiceHandle(Handle handle, Handle* out, char* server) +{ + u8 l=strlen(server); + if(!out || !server || l>8)return; + u32* svcData=svc_getData(); + svcData[0]=0x50100; //request header code + strcpy((char*)&svcData[1], server); + svcData[3]=l; + svcData[4]=0x0; + svc_sendSyncRequest(handle); //check return value... + *out=svcData[3]; +} diff --git a/libctru/source/svc.s b/libctru/source/svc.s new file mode 100644 index 0000000..a7cca5d --- /dev/null +++ b/libctru/source/svc.s @@ -0,0 +1,72 @@ +.arm + +.align 4 + +.global svc_getData +.type svc_getData, %function +svc_getData: + mrc p15, 0, r0, c13, c0, 3 + add r0, #0x80 + bx lr + + +.global svc_controlMemory +.type svc_controlMemory, %function +svc_controlMemory: + stmfd sp!, {r0, r4} + ldr R0, [sp, #0x8] + ldr r4, [sp, #0x8+0x4] + svc 0x01 + ldr r2, [sp] + str r1, [r2] + ldr r4, [sp, #4]! + add sp, sp, #4 + bx lr + +.global svc_sleepThread +.type svc_sleepThread, %function +svc_sleepThread: + svc 0x0A + bx lr + +.global svc_createEvent +.type svc_createEvent, %function +svc_createEvent: + str r0, [sp,#-4]! + svc 0x17 + ldr r2, [sp], #4 + str r1, [r2] + bx lr + +.global svc_mapMemoryBlock +.type svc_mapMemoryBlock, %function +svc_mapMemoryBlock: + svc 0x1F + bx lr + +.global svc_closeHandle +.type svc_closeHandle, %function +svc_closeHandle: + svc 0x23 + bx lr + +.global svc_waitSynchronization1 +.type svc_waitSynchronization1, %function +svc_waitSynchronization1: + svc 0x24 + bx lr + +.global svc_connectToPort +.type svc_connectToPort, %function +svc_connectToPort: + str r0, [sp,#-0x4]! + svc 0x2D + ldr r3, [sp], #4 + str r1, [r3] + bx lr + +.global svc_sendSyncRequest +.type svc_sendSyncRequest, %function +svc_sendSyncRequest: + svc 0x32 + bx lr