/* 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
#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;
}