/* adapted from https://github.com/dimok789/homebrew_launcher */ /**************************************************************************** * * Copyright (C) 2015 Dimok * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ #include #include #include #include #include "hbl.h" #define MEM_AREA_TABLE ((s_mem_area*)(MEM_BASE + 0x1600)) #define ELF_DATA_ADDR (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x00)) #define ELF_DATA_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x04)) #define RPX_MAX_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x0C)) #define RPX_MAX_CODE_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x10)) #define APP_BASE_MEM ((unsigned char*)(MEM_BASE + 0x2000)) typedef struct _s_mem_area { unsigned int address; unsigned int size; struct _s_mem_area *next; } s_mem_area; void SC0x25_KernelCopyData(unsigned int addr, unsigned int src, unsigned int len); typedef struct _memory_values_t { unsigned int start_address; unsigned int end_address; } memory_values_t; static const memory_values_t mem_vals_540[] = { { 0x2E609EFC, 0x2FF82000 }, // 26083 kB { 0x29030800, 0x293F6000 }, // 3864 kB { 0x288EEC30, 0x28B06800 }, // 2144 kB { 0x2D3B966C, 0x2D894000 }, // 4971 kB { 0x2CB56370, 0x2D1EF000 }, // 6756 kB { 0x2D8AD3D8, 0x2E000000 }, // 7499 kB { 0x2970200C, 0x298B9800 }, // 1759 kB { 0x2A057B68, 0x2A1B9000 }, // 1414 kB { 0x2ABBCC4C, 0x2ACB9000 }, // 1010 kB {0, 0} }; static inline void memoryAddArea(int start, int end, int cur_index) { // Create and copy new memory area s_mem_area * mem_area = MEM_AREA_TABLE; mem_area[cur_index].address = start; mem_area[cur_index].size = end - start; mem_area[cur_index].next = 0; // Fill pointer to this area in the previous area if (cur_index > 0) { mem_area[cur_index - 1].next = &mem_area[cur_index]; } } void *getApplicationEndAddr(void) { extern u32 _end[]; if((u32)_end >= 0x01000000) return APP_BASE_MEM; return _end; } /* Create memory areas arrays */ static void memoryInitAreaTable(u32 args_size) { u32 ApplicationMemoryEnd = (u32)getApplicationEndAddr() + args_size; // This one seems to be available on every firmware and therefore its our code area but also our main RPX area behind our code // 22876 kB - our application // ok memoryAddArea(ApplicationMemoryEnd + 0x30000000, 0x30000000 + 0x01E20000, 0); const memory_values_t * mem_vals = mem_vals_540; // Fill entries int i = 0; while (mem_vals[i].start_address) { memoryAddArea(mem_vals[i].start_address, mem_vals[i].end_address, i + 1); i++; } } static int HomebrewCopyMemory(u8 *address, u32 bytes, u32 args_size) { args_size += 0x7; args_size &= ~0x7; if (args_size > 0x10000) args_size = 0x10000; memoryInitAreaTable(args_size); RPX_MAX_SIZE = 0x40000000; RPX_MAX_CODE_SIZE = 0x03000000; // check if we load an RPX or an ELF if (*(u16 *)&address[7] != 0xCAFE) { // assume ELF printf("loading ELF file \n"); ELF_DATA_ADDR = (u32)getApplicationEndAddr() + args_size; if (ELF_DATA_ADDR >= 0x01000000) return -1; } else { // RPX printf("loading RPX file \n"); ELF_DATA_ADDR = MEM_AREA_TABLE->address; } //! if we load an ELF file if (ELF_DATA_ADDR < 0x01000000) { if ((ELF_DATA_ADDR + bytes) > 0x01000000) return -1; memcpy((void *)ELF_DATA_ADDR, address, bytes); ELF_DATA_SIZE = bytes; } else { DCFlushRange(address, bytes); u32 done = 0; u32 addressPhysical = (u32)OSEffectiveToPhysical(address); s_mem_area *mem_map = MEM_AREA_TABLE; u32 mapPosition = 0; while ((done < bytes) && mem_map) { if (mapPosition >= mem_map->size) { mem_map = mem_map->next; if (!mem_map) return -1; mapPosition = 0; } u32 blockSize = bytes - done; if ((mapPosition + blockSize) > mem_map->size) blockSize = mem_map->size - mapPosition; SC0x25_KernelCopyData(mem_map->address + mapPosition, (addressPhysical + done), blockSize); mapPosition += blockSize; done += blockSize; } ELF_DATA_SIZE = done; } return bytes; } int HBL_loadToMemory(const char *filepath, u32 args_size) { if (!filepath || !*filepath) return -1; printf("Loading file %s\n", filepath); FILE *fp = fopen(filepath, "rb"); if (!fp) { printf("failed to open file %s\n", filepath); return -1; } u32 bytesRead = 0; fseek(fp, 0, SEEK_END); u32 fileSize = ftell(fp); fseek(fp, 0, SEEK_SET); unsigned char *buffer = (unsigned char *) memalign(0x40, (fileSize + 0x3F) & ~0x3F); if (!buffer) { printf("Not enough memory\n"); return -1; } // Copy rpl in memory while (bytesRead < fileSize) { printf("progress: %f \r", 100.0f * (f32)bytesRead / (f32)fileSize); u32 blockSize = 0x8000; if (blockSize > (fileSize - bytesRead)) blockSize = fileSize - bytesRead; int ret = fread(buffer + bytesRead, 1, blockSize, fp); if (ret <= 0) { printf("Failure on reading file %s\n", filepath); break; } bytesRead += ret; } printf("progress: %f \n", 100.0f * (f32)bytesRead / (f32)fileSize); if (bytesRead != fileSize) { free(buffer); printf("File loading not finished for file %s, finished %i of %i bytes\n", filepath, bytesRead, fileSize); printf("File read failure"); return -1; } int ret = HomebrewCopyMemory(buffer, bytesRead, args_size); free(buffer); if (ret < 0) { printf("Not enough memory"); return -1; } return fileSize; }