mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 07:59:42 +00:00
308 lines
8.8 KiB
C
308 lines
8.8 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <wiiu/os.h>
|
|
|
|
#include "hbl.h"
|
|
|
|
#ifdef WIIU_LOG_RPX
|
|
#include "../verbosity.h"
|
|
#endif
|
|
|
|
#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)
|
|
{
|
|
int i = 0;
|
|
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 */
|
|
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 */
|
|
ELF_DATA_ADDR = (u32)getApplicationEndAddr() + args_size;
|
|
if (ELF_DATA_ADDR >= 0x01000000)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
/* RPX */
|
|
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
|
|
{
|
|
u32 done = 0;
|
|
u32 mapPosition = 0;
|
|
|
|
DCFlushRange(address, bytes);
|
|
|
|
u32 addressPhysical = (u32)OSEffectiveToPhysical(address);
|
|
|
|
s_mem_area *mem_map = MEM_AREA_TABLE;
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef WIIU_LOG_RPX
|
|
|
|
#define LINE_LEN 32
|
|
/**
|
|
* This is called between when the RPX is read off the storage medium and
|
|
* when it is sent to the loader. It prints a hexdump to the logger, which
|
|
* can then be parsed out by a script.
|
|
*
|
|
* If we can at least semi-reliably generate the "System Memory Error", this
|
|
* can be useful in identifying if the problem is corrupt file i/o vs in-memory
|
|
* corruption.
|
|
*/
|
|
void log_rpx(const char *filepath, unsigned char *buf, size_t len)
|
|
{
|
|
int i;
|
|
unsigned int line_buffer[LINE_LEN];
|
|
|
|
RARCH_LOG("=== BEGIN file=%s size=%d ===\n", filepath, len);
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
int offset = i % LINE_LEN;
|
|
line_buffer[offset] = buf[i];
|
|
|
|
if (offset == (LINE_LEN-1))
|
|
{
|
|
RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
|
|
line_buffer[0], line_buffer[1], line_buffer[2], line_buffer[3],
|
|
line_buffer[4], line_buffer[5], line_buffer[6], line_buffer[7],
|
|
line_buffer[8], line_buffer[9], line_buffer[10], line_buffer[11],
|
|
line_buffer[12], line_buffer[13], line_buffer[14], line_buffer[15],
|
|
line_buffer[16], line_buffer[17], line_buffer[18], line_buffer[19],
|
|
line_buffer[20], line_buffer[21], line_buffer[22], line_buffer[23],
|
|
line_buffer[24], line_buffer[25], line_buffer[26], line_buffer[27],
|
|
line_buffer[28], line_buffer[29], line_buffer[30], line_buffer[31]);
|
|
}
|
|
}
|
|
if ((len % LINE_LEN) != 0)
|
|
{
|
|
for (i = (LINE_LEN - (len % LINE_LEN)); i < LINE_LEN; i++)
|
|
line_buffer[i] = 0;
|
|
|
|
RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
|
|
line_buffer[0], line_buffer[1], line_buffer[2], line_buffer[3],
|
|
line_buffer[4], line_buffer[5], line_buffer[6], line_buffer[7],
|
|
line_buffer[8], line_buffer[9], line_buffer[10], line_buffer[11],
|
|
line_buffer[16], line_buffer[17], line_buffer[18], line_buffer[19],
|
|
line_buffer[20], line_buffer[21], line_buffer[22], line_buffer[23],
|
|
line_buffer[24], line_buffer[25], line_buffer[26], line_buffer[27],
|
|
line_buffer[28], line_buffer[29], line_buffer[30], line_buffer[31]);
|
|
|
|
}
|
|
RARCH_LOG("=== END %s ===\n", filepath);
|
|
}
|
|
|
|
#endif
|
|
|
|
int HBL_loadToMemory(const char *file_path, u32 args_size)
|
|
{
|
|
int ret;
|
|
FILE *fp;
|
|
size_t buffer_size;
|
|
unsigned char *buffer = NULL;
|
|
u32 bytes_read = 0;
|
|
u32 file_size = 0;
|
|
if (!file_path || !*file_path)
|
|
return -1;
|
|
|
|
if (!(fp = fopen(file_path, "rb")))
|
|
return -1;
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
file_size = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
buffer_size = (file_size + 0x3f) & ~0x3f;
|
|
buffer = (unsigned char *)memalign(0x40, buffer_size);
|
|
|
|
if (!buffer)
|
|
{
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
|
|
memset(buffer, 0, buffer_size);
|
|
|
|
/* Copy rpl in memory */
|
|
while (bytes_read < file_size)
|
|
{
|
|
int ret;
|
|
u32 block_size = 0x8000;
|
|
|
|
if (block_size > (file_size - bytes_read))
|
|
block_size = file_size - bytes_read;
|
|
|
|
if ((ret = fread(buffer + bytes_read, 1, block_size, fp)) <= 0)
|
|
break;
|
|
|
|
bytes_read += ret;
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
if (bytes_read != file_size)
|
|
{
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
#ifdef WIIU_LOG_RPX
|
|
log_rpx(file_path, buffer, bytes_read);
|
|
#endif
|
|
|
|
ret = HomebrewCopyMemory(buffer, bytes_read, args_size);
|
|
|
|
free(buffer);
|
|
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
return file_size;
|
|
}
|