From e34d537040f6beec575cbfe5eb7fe37aaa64fea5 Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 31 Oct 2021 16:35:33 +0100 Subject: [PATCH] Use WUMS 0.2.1 --- .github/workflows/ci.yml | 58 ++++++++++++++++ .github/workflows/pr.yml | 17 +++++ .gitignore | 10 +++ Dockerfile | 6 ++ Makefile | 141 +++++++++++++++++++++++++++++++++++++++ README.md | 14 ++++ source/kernel.cpp | 92 +++++++++++++++++++++++++ source/kernel.h | 1 + source/logger.h | 27 ++++++++ source/main.cpp | 56 ++++++++++++++++ source/syscalls.s | 5 ++ 11 files changed, 427 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pr.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 source/kernel.cpp create mode 100644 source/kernel.h create mode 100644 source/logger.h create mode 100644 source/main.cpp create mode 100644 source/syscalls.s diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ea0fb3c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,58 @@ +name: CI-Release + +on: + push: + branches: + - main + +jobs: + build-binary: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.wms" + deploy-binary: + needs: build-binary + runs-on: ubuntu-18.04 + steps: + - name: Get environment variables + id: get_repository_name + run: | + echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//") >> $GITHUB_ENV + echo DATETIME=$(echo $(date '+%Y%m%d-%H%M%S')) >> $GITHUB_ENV + - uses: actions/download-artifact@master + with: + name: binary + path: wiiu/modules + - name: zip artifact + run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip wiiu + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + draft: false + prerelease: true + body: | + Not a stable release: + ${{ github.event.head_commit.message }} + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_content_type: application/unknown \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..0413d45 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,17 @@ +name: CI-PR + +on: [pull_request] + +jobs: + build-binary: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.wms" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba57c28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.cbp +*.elf +*.layout +*.rpx +build/ +*.save-failed +.idea/ +cmake-build-debug/ +CMakeLists.txt +*.wms diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..69712ba --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM wiiuenv/devkitppc:20210920 + +COPY --from=wiiuenv/libkernel:20211031 /artifacts $DEVKITPRO +COPY --from=wiiuenv/wiiumodulesystem:20211031 /artifacts $DEVKITPRO + +WORKDIR project \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..abb3247 --- /dev/null +++ b/Makefile @@ -0,0 +1,141 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wums/share/wums_rules + +WUMS_ROOT := $(DEVKITPRO)/wums +WUT_ROOT := $(DEVKITPRO)/wut +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := USBSerialLoggingModule +BUILD := build +SOURCES := source +DATA := data +INCLUDES := source + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -g -Wall -Wextra -O0 -ffunction-sections\ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ + +CXXFLAGS := $(CFLAGS) -std=c++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libkernel.ld $(WUMSSPECS) + +LIBS := -lwums -lwut -lkernel + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#------------------------------------------------------------------------------- + export LD := $(CC) +#------------------------------------------------------------------------------- +else +#------------------------------------------------------------------------------- + export LD := $(CXX) +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).rpx $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).wms + +$(OUTPUT).wms : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o: %.s + @echo $(notdir $<) + @$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..93db6ff --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +## Building using the Dockerfile + +It's possible to use a docker image for building. This way you don't need anything installed on your host system. + +``` +# Build docker image (only needed once) +docker build . -t dynloadpatchmodule-builder + +# make +docker run -it --rm -v ${PWD}:/project dynloadpatchmodule-builder make + +# make clean +docker run -it --rm -v ${PWD}:/project dynloadpatchmodule-builder make clean +``` diff --git a/source/kernel.cpp b/source/kernel.cpp new file mode 100644 index 0000000..6977fc9 --- /dev/null +++ b/source/kernel.cpp @@ -0,0 +1,92 @@ +#include + +#define k_memset ((void (*)( uint32_t , uint32_t, uint32_t))0xfff09d60) +#define KiReport ((void (*)( const char*, ... ))0xfff0ad0c) +#define IopShell_AsyncCallback (0xfff1b7d8) +#define IopShell_ReadCallback (0xfff1b9a0) +#define k_memcpy ((void (*)(void*,void*, uint32_t))0xfff09e44) +#define IPCKDriver_OpenAsync ((int32_t (*)(uint32_t client_ram_pid, const char *device, uint32_t mode, uint32_t asyncCallback, void * context))0xfff0b9c8) +#define WaitAsyncReply ((uint32_t (*)(void* , uint32_t, uint32_t))0xfff180dc) + +#define IPCKDriver_IoctlAsync ((int32_t (*)( \ + uint32_t clientProcessId,\ + uint32_t loaderProcessId,\ + uint32_t handle,\ + uint32_t request,\ + void* inBuf,\ + uint32_t inLen,\ + void* outBuf,\ + uint32_t outLen,\ + uint32_t asyncCallback,\ + void * context))0xfff0bc5c) + + +uint32_t IPCKDriver_IOS_Open_Sync(uint32_t clientProcessId, const char *device_name, uint32_t mode) { + register int r13 asm ("r13"); + k_memcpy((void *) (r13 - 0x1180), (void *) device_name, 0x10); + void *context = (void *) (r13 + 0x450); + auto error = IPCKDriver_OpenAsync(clientProcessId, (const char *) (r13 - 0x1180), mode, IopShell_AsyncCallback, context); + if (error < 0) { + return error; + } + return WaitAsyncReply(context, 20000000, 6); +} + +uint32_t IPCKDriver_IOS_Ioctl_Sync(uint32_t clientProcessId, uint32_t handle, uint32_t request, void *inBuf, uint32_t inLen, void *outBuf, uint32_t ouLen) { + register int r13 asm ("r13"); + void *context = (void *) (r13 + 0x450); + + auto error = IPCKDriver_IoctlAsync(clientProcessId, 0xFFFFFFFF, handle, request, inBuf, inLen, outBuf, ouLen, IopShell_AsyncCallback, context); + + if (error < 0) { + return error; + } + uint32_t res = WaitAsyncReply(context, 20000000, 6); + return res; +} + + +void IopShellInitInternal() { + register int r13 asm ("r13"); + *((uint32_t *) (r13 + 0x450)) = 0; + *((uint32_t *) (r13 + 0x454)) = 0; + *((uint32_t *) (r13 + 0x458)) = 0xffffffe3; + auto handle = IPCKDriver_IOS_Open_Sync(0, "/dev/iopsh", (uint32_t) 5); + if (static_cast(handle) < 0) { + KiReport("####################################################################################\n"); + KiReport("IopShellInit failed\n"); + KiReport("####################################################################################\n"); + return; + } + *(uint32_t *) (r13 + -0x6d84) = static_cast(handle); + *(uint32_t *) (r13 + -0x1178) = 5; + *(uint32_t *) (r13 + -0x117c) = 0x1040; + + auto result = IPCKDriver_IOS_Ioctl_Sync(0, static_cast(handle), 3, (void *) (r13 - 0x1180), 0x14, (void *) (r13 - 0x1140), 0x4); + if (static_cast(result) < 0) { + KiReport("####################################################################################\n"); + KiReport("IopShellInit failed\n"); + KiReport("####################################################################################\n"); + return; + } + *(uint32_t *) (r13 + -0x117c) = 0x636F7300; // "cos" + result = IPCKDriver_IOS_Ioctl_Sync(0, static_cast(handle), 1, (void *) (r13 - 0x1180), 0x14, (void *) (r13 - 0x1140), 0x4); + if (static_cast(result) < 0) { + KiReport("####################################################################################\n"); + KiReport("IopShellInit failed\n"); + KiReport("####################################################################################\n"); + return; + } + *((uint32_t *) (r13 + 0x450)) = 0; + *((uint32_t *) (r13 + 0x454)) = 0; + *((uint32_t *) (r13 + 0x458)) = 0xffffffe3; + k_memset((r13 - 0x1180), 0, 0x1040); + result = IPCKDriver_IoctlAsync(0, 0xFFFFFFFF, handle, 5, nullptr, 0, (void *) (r13 - 0x1180), 0x1040, IopShell_ReadCallback, (void*) (r13 + 0x450)); + if (static_cast(result) < 0) { + KiReport("####################################################################################\n"); + KiReport("IopShellInit failed\n"); + KiReport("####################################################################################\n"); + return; + } + KiReport("IopShellInit was successful\n"); +} \ No newline at end of file diff --git a/source/kernel.h b/source/kernel.h new file mode 100644 index 0000000..a53195c --- /dev/null +++ b/source/kernel.h @@ -0,0 +1 @@ +void IopShellInitInternal(); diff --git a/source/logger.h b/source/logger.h new file mode 100644 index 0000000..90e32b9 --- /dev/null +++ b/source/logger.h @@ -0,0 +1,27 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \ + OSFatal_printf("[%s]%s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \ + WHBLogWritef("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#ifdef __cplusplus +} +#endif diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..4912598 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include "kernel.h" + +WUMS_MODULE_EXPORT_NAME("homebrew_usbseriallogging"); +WUMS_MODULE_SKIP_ENTRYPOINT(); +WUMS_MODULE_INIT_BEFORE_RELOCATION_DONE_HOOK(); + +extern "C" void SC_0x51(); + +WUMS_INITIALIZE(args) { + WHBLogUdpInit(); + auto gModuleData = args.module_information; + if (gModuleData == nullptr) { + OSFatal("USBSerialLogging: Failed to get gModuleData pointer."); + } + if (gModuleData->version != MODULE_INFORMATION_VERSION) { + OSFatal("USBSerialLogging: The module information struct version does not match."); + } + + // Start syslogging on iosu side + int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0); + if (mcpFd >= 0) { + int in = 0xFA; // IPC_CUSTOM_START_USB_LOGGING + int out = 0; + IOS_Ioctl(mcpFd, 100, &in, sizeof(in), &out, sizeof(out)); + IOS_Close(mcpFd); + } + + // Patch loader.elf to spit out less warnings when loading .rpx built with wut + KernelNOPAtPhysicalAddress(0x0100b770 - 0x01000000 + 0x32000000); + KernelNOPAtPhysicalAddress(0x0100b800 - 0x01000000 + 0x32000000); + KernelNOPAtPhysicalAddress(0x0100b7b8 - 0x01000000 + 0x32000000); + ICInvalidateRange(reinterpret_cast(0x0100b770), 0x04); + ICInvalidateRange(reinterpret_cast(0x0100b800), 0x04); + ICInvalidateRange(reinterpret_cast(0x0100b7b8), 0x04); + + KernelPatchSyscall(0x51, (uint32_t) &IopShellInitInternal); + + // Start iopshell on kernel + SC_0x51(); +} + +#define IopShell_UserCallback (0x101C400 + 0x1926c) +#define IopShell_RegisterCallback ((void (*)( uint32_t,uint32_t,uint32_t,uint32_t))(0x101C400 + 0x19638)) +#define IopShell_CreateThread ((void (*)( void))(0x101C400 + 0x19504)) + +WUMS_APPLICATION_STARTS(){ + IopShell_RegisterCallback(IopShell_UserCallback, 0x100978f8, 0x10097900, 0x10097c40); + IopShell_CreateThread(); +} + diff --git a/source/syscalls.s b/source/syscalls.s new file mode 100644 index 0000000..962d42e --- /dev/null +++ b/source/syscalls.s @@ -0,0 +1,5 @@ +.global SC_0x51 +SC_0x51: + li %r0, 0x5100 + sc +blr \ No newline at end of file