RetroArch/wiiu/hbl.c
2018-01-06 09:44:03 -03:00

258 lines
6.7 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"
#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 %" PRIi32 " of %" PRIi32 " 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;
}