fusee: Add support for firmware version 6.0.0.

fusee: Implement splash screen rendering.
fusee: Add minor notes and update lz library.
This commit is contained in:
hexkyz 2018-09-17 21:03:03 +01:00
parent 39d812f434
commit f864b0835d
10 changed files with 337 additions and 44 deletions

View file

@ -194,7 +194,10 @@ void nx_hwinit()
AHB_AHB_SPARE_REG_0 &= 0xFFFFFF9F;
pmc->scratch49 = (((pmc->scratch49 >> 1) << 1) & 0xFFFFFFFD);
/* Apply the memory built-in self test workaround. */
mbist_workaround();
/* Reboot SE. */
clkrst_reboot(CARDEVICE_SE);
/* Initialize the fuse driver. */
@ -203,9 +206,15 @@ void nx_hwinit()
/* Initialize the memory controller. */
mc_enable();
/* Configure oscillators, pinmux and GPIOs. */
/* Configure oscillators. */
config_oscillators();
/* Disable pinmux tristate input clamping. */
APB_MISC_PP_PINMUX_GLOBAL_0 = 0;
/* Configure GPIOs. */
/* NOTE: In 3.x+ part of the GPIO configuration is skipped if the unit is SDEV. */
/* NOTE: In 6.x+ the GPIO configuration's order was changed a bit. */
config_gpios();
/* Uncomment for UART debugging. */
@ -214,21 +223,34 @@ void nx_hwinit()
uart_init(UART_C, 115200);
*/
/* Reboot CL-DVFS. */
clkrst_reboot(CARDEVICE_CL_DVFS);
/* Reboot I2C1. */
clkrst_reboot(CARDEVICE_I2C1);
/* Reboot I2C5. */
clkrst_reboot(CARDEVICE_I2C5);
/* Reboot SE. */
/* NOTE: In 4.x+ this was removed. */
clkrst_reboot(CARDEVICE_SE);
/* Reboot unknown device. */
clkrst_reboot(CARDEVICE_UNK);
/* Initialize I2C1 and I2C5. */
/* Initialize I2C1. */
/* NOTE: In 6.x+ this was moved to after the PMIC is configured. */
i2c_init(I2C_1);
/* Initialize I2C5. */
i2c_init(I2C_5);
/* Configure the PMIC. */
uint8_t val = 0x40;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGBBC, &val, 1);
val = 0x78;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, &val, 1);
val = 0x38;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_CFG0, &val, 1);
val = 0x3A;
@ -245,19 +267,38 @@ void nx_hwinit()
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_SD1, &val, 1);
val = 0x1B;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_SD3, &val, 1);
/* TODO: In 3.x+ this was added. */
/*
val = 0x22;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_GPIO3, &val, 1);
*/
/* TODO: In 3.x+, if the unit is SDEV, the MBLPD bit is set. */
/*
i2c_query(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGGLBL1, &val, 1);
val |= 0x40;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGGLBL1, &val, 1);
*/
/* Configure SD0 voltage. */
val = 42; /* 42 = (1125000 - 600000) / 12500 -> 1.125V */
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_SD0, &val, 1);
/* Configure and lock PMC scratch registers. */
/* NOTE: In 4.x+ this was removed. */
config_pmc_scratch();
/* Set super clock burst policy. */
car->sclk_brst_pol = ((car->sclk_brst_pol & 0xFFFF8888) | 0x3333);
/* Configure memory controller carveouts. */
/* NOTE: In 4.x+ this was removed. */
mc_config_carveout();
/* Initialize and save SDRAM. */
/* Initialize SDRAM. */
sdram_init();
/* Save SDRAM LP0 parameters. */
sdram_lp0_save_params(sdram_get_params());
}

View file

@ -78,11 +78,73 @@
*************************************************************************/
/*************************************************************************
* Constants used for LZ77 coding
*************************************************************************/
/* Maximum offset (can be any size < 2^31). Lower values give faster
compression, while higher values gives better compression. The default
value of 100000 is quite high. Experiment to see what works best for
you. */
#define LZ_MAX_OFFSET 100000
/*************************************************************************
* INTERNAL FUNCTIONS *
*************************************************************************/
/*************************************************************************
* _LZ_StringCompare() - Return maximum length string match.
*************************************************************************/
static unsigned int _LZ_StringCompare( unsigned char * str1,
unsigned char * str2, unsigned int minlen, unsigned int maxlen )
{
unsigned int len;
for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len );
return len;
}
/*************************************************************************
* _LZ_WriteVarSize() - Write unsigned integer with variable number of
* bytes depending on value.
*************************************************************************/
static int _LZ_WriteVarSize( unsigned int x, unsigned char * buf )
{
unsigned int y;
int num_bytes, i, b;
/* Determine number of bytes needed to store the number x */
y = x >> 3;
for( num_bytes = 5; num_bytes >= 2; -- num_bytes )
{
if( y & 0xfe000000 ) break;
y <<= 7;
}
/* Write all bytes, seven bits in each, with 8:th bit set for all */
/* but the last byte. */
for( i = num_bytes-1; i >= 0; -- i )
{
b = (x >> (i*7)) & 0x0000007f;
if( i > 0 )
{
b |= 0x00000080;
}
*buf ++ = (unsigned char) b;
}
/* Return number of bytes written */
return num_bytes;
}
/*************************************************************************
* _LZ_ReadVarSize() - Read unsigned integer with variable number of
* bytes depending on value.
@ -117,6 +179,141 @@ static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf )
*************************************************************************/
/*************************************************************************
* LZ_Compress() - Compress a block of data using an LZ77 coder.
* in - Input (uncompressed) buffer.
* out - Output (compressed) buffer. This buffer must be 0.4% larger
* than the input buffer, plus one byte.
* insize - Number of input bytes.
* The function returns the size of the compressed data.
*************************************************************************/
int LZ_Compress( const unsigned char *in, unsigned char *out, unsigned int insize )
{
unsigned char marker, symbol;
unsigned int inpos, outpos, bytesleft, i;
unsigned int maxoffset, offset, bestoffset;
unsigned int maxlength, length, bestlength;
unsigned int histogram[ 256 ];
unsigned char *ptr1, *ptr2;
/* Do we have anything to compress? */
if( insize < 1 )
{
return 0;
}
/* Create histogram */
for( i = 0; i < 256; ++ i )
{
histogram[ i ] = 0;
}
for( i = 0; i < insize; ++ i )
{
++ histogram[ in[ i ] ];
}
/* Find the least common byte, and use it as the marker symbol */
marker = 0;
for( i = 1; i < 256; ++ i )
{
if( histogram[ i ] < histogram[ marker ] )
{
marker = (unsigned char) i;
}
}
/* Remember the marker symbol for the decoder */
out[ 0 ] = marker;
/* Start of compression */
inpos = 0;
outpos = 1;
/* Main compression loop */
bytesleft = insize;
do
{
/* Determine most distant position */
if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET;
else maxoffset = inpos;
/* Get pointer to current position */
ptr1 = &in[ inpos ];
/* Search history window for maximum length string match */
bestlength = 3;
bestoffset = 0;
for( offset = 3; offset <= maxoffset; ++ offset )
{
/* Get pointer to candidate string */
ptr2 = &ptr1[ -(int)offset ];
/* Quickly determine if this is a candidate (for speed) */
if( (ptr1[ 0 ] == ptr2[ 0 ]) &&
(ptr1[ bestlength ] == ptr2[ bestlength ]) )
{
/* Determine maximum length for this offset */
maxlength = (bytesleft < offset ? bytesleft : offset);
/* Count maximum length match at this offset */
length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength );
/* Better match than any previous match? */
if( length > bestlength )
{
bestlength = length;
bestoffset = offset;
}
}
}
/* Was there a good enough match? */
if( (bestlength >= 8) ||
((bestlength == 4) && (bestoffset <= 0x0000007f)) ||
((bestlength == 5) && (bestoffset <= 0x00003fff)) ||
((bestlength == 6) && (bestoffset <= 0x001fffff)) ||
((bestlength == 7) && (bestoffset <= 0x0fffffff)) )
{
out[ outpos ++ ] = (unsigned char) marker;
outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] );
outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] );
inpos += bestlength;
bytesleft -= bestlength;
}
else
{
/* Output single byte (or two bytes if marker byte) */
symbol = in[ inpos ++ ];
out[ outpos ++ ] = symbol;
if( symbol == marker )
{
out[ outpos ++ ] = 0;
}
-- bytesleft;
}
}
while( bytesleft > 3 );
/* Dump remaining bytes, if any */
while( inpos < insize )
{
if( in[ inpos ] == marker )
{
out[ outpos ++ ] = marker;
out[ outpos ++ ] = 0;
}
else
{
out[ outpos ++ ] = in[ inpos ];
}
++ inpos;
}
return outpos;
}
/*************************************************************************
* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder.
* in - Input (compressed) buffer.
@ -125,8 +322,7 @@ static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf )
* insize - Number of input bytes.
*************************************************************************/
void LZ_Uncompress( const unsigned char *in, unsigned char *out,
unsigned int insize )
int LZ_Uncompress( const unsigned char *in, unsigned char *out, unsigned int insize )
{
unsigned char marker, symbol;
unsigned int i, inpos, outpos, length, offset;
@ -134,7 +330,7 @@ void LZ_Uncompress( const unsigned char *in, unsigned char *out,
/* Do we have anything to uncompress? */
if( insize < 1 )
{
return;
return 0;
}
/* Get marker symbol from input stream */
@ -176,4 +372,6 @@ void LZ_Uncompress( const unsigned char *in, unsigned char *out,
}
}
while( inpos < insize );
return outpos;
}

View file

@ -41,9 +41,8 @@ extern "C" {
* Function prototypes
*************************************************************************/
void LZ_Uncompress( const unsigned char *in, unsigned char *out,
unsigned int insize );
int LZ_Compress(const unsigned char *in, unsigned char *out, unsigned int insize);
int LZ_Uncompress(const unsigned char *in, unsigned char *out, unsigned int insize);
#ifdef __cplusplus
}

View file

@ -80,7 +80,7 @@ CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
KIPFILES := loader.kip pm.kip sm.kip boot_100.kip boot_200.kip
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) exosphere.bin thermosphere.bin $(KIPFILES)
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) exosphere.bin thermosphere.bin splash_screen.bmp $(KIPFILES)
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
@ -166,6 +166,11 @@ $(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
%.bmp.o %_bmp.h: %.bmp
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
%.kip.o %_kip.h: %.kip
#---------------------------------------------------------------------------------

View file

@ -27,9 +27,10 @@
#define EXOSPHERE_TARGET_FIRMWARE_300 3
#define EXOSPHERE_TARGET_FIRMWARE_400 4
#define EXOSPHERE_TARGET_FIRMWARE_500 5
#define EXOSPHERE_TARGET_FIRMWARE_600 6
#define EXOSPHERE_TARGET_FIRMWARE_MIN EXOSPHERE_TARGET_FIRMWARE_100
#define EXOSPHERE_TARGET_FIRMWARE_MAX EXOSPHERE_TARGET_FIRMWARE_500
#define EXOSPHERE_TARGET_FIRMWARE_MAX EXOSPHERE_TARGET_FIRMWARE_600
typedef struct {
unsigned int magic;

View file

@ -75,6 +75,8 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
return EXOSPHERE_TARGET_FIRMWARE_400;
case 0x0B: /* 5.0.0 - 5.1.0 */
return EXOSPHERE_TARGET_FIRMWARE_500;
case 0x0E: /* 6.0.0 */
return EXOSPHERE_TARGET_FIRMWARE_600;
default:
return 0;
}
@ -158,6 +160,8 @@ static void nxboot_set_bootreason() {
static void nxboot_move_bootconfig() {
FILE *bcfile;
void *bootconfig;
uint32_t bootconfig_addr;
uint32_t bootconfig_size;
/* Allocate memory for reading BootConfig. */
bootconfig = memalign(0x1000, 0x4000);
@ -175,9 +179,13 @@ static void nxboot_move_bootconfig() {
}
fclose(bcfile);
/* Copy the first 0x3000 bytes into IRAM. */
memset((void *)0x4003D000, 0, 0x3000);
memcpy((void *)0x4003D000, bootconfig, 0x3000);
/* Select the actual BootConfig size and destination address. */
bootconfig_addr = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_600) ? 0x4003D000 : 0x4003F800;
bootconfig_size = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_400) ? 0x3000 : 0x1000;
/* Copy the BootConfig into IRAM. */
memset((void *)bootconfig_addr, 0, bootconfig_size);
memcpy((void *)bootconfig_addr, bootconfig, bootconfig_size);
/* Clean up. */
free(bootconfig);
@ -329,8 +337,10 @@ uint32_t nxboot_main(void) {
/* Select the right address for the warmboot firmware. */
if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_400) {
warmboot_memaddr = (void *)0x8000D000;
} else {
} else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_600) {
warmboot_memaddr = (void *)0x4003B000;
} else {
warmboot_memaddr = (void *)0x4003D800;
}
printf("[NXBOOT]: Copying warmboot firmware...\n");
@ -399,7 +409,7 @@ uint32_t nxboot_main(void) {
printf("[NXBOOT]: Powering on the CCPLEX...\n");
/* Display splash screen. */
display_splash_screen_bmp(loader_ctx->custom_splash_path);
display_splash_screen_bmp(loader_ctx->custom_splash_path, (void *)0xC0000000);
/* Unmount everything. */
nxfs_unmount_all();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

View file

@ -15,20 +15,75 @@
*/
#include <stdio.h>
#include "di.h"
#include "timers.h"
#include "splash_screen.h"
#include "fs_utils.h"
#include "display/video_fb.h"
void display_splash_screen_bmp(const char *custom_splash_path) {
uint8_t *splash_screen = g_default_splash_screen;
#define u8 uint8_t
#define u32 uint32_t
#include "splash_screen_bmp.h"
#undef u8
#undef u32
static void render_bmp(const uint32_t *bmp_data, uint32_t *framebuffer, uint32_t bmp_width, uint32_t bmp_height, uint32_t bmp_pos_x, uint32_t bmp_pos_y) {
/* Render the BMP. */
for (uint32_t y = bmp_pos_y; y < (bmp_pos_y + bmp_height); y++) {
for (uint32_t x = bmp_pos_x; x < (bmp_pos_x + bmp_width); x++) {
framebuffer[x + (y * SPLASH_SCREEN_STRIDE)] = bmp_data[(bmp_height + bmp_pos_y - 1 - y) * bmp_width + x - bmp_pos_x];
}
}
/* Re-initialize the frame buffer. */
display_init_framebuffer(framebuffer);
}
void display_splash_screen_bmp(const char *custom_splash_path, void *fb_address) {
uint8_t *splash_screen = (uint8_t *)splash_screen_bmp;
/* Try to load an external custom splash screen. */
if ((custom_splash_path != NULL) && (custom_splash_path[0] != '\x00')) {
if (!read_from_file(splash_screen, sizeof(g_default_splash_screen), custom_splash_path)) {
if (!read_from_file(splash_screen, sizeof(&splash_screen_bmp), custom_splash_path)) {
fatal_error("Failed to read custom splash screen from %s!\n", custom_splash_path);
}
}
/* TODO: Display the splash screen. It should be a pointer to a BMP, at this point. */
/* Check for 'BM' magic. */
if ((splash_screen[0] == 'B') && (splash_screen[1] == 'M')) {
/* Extract BMP parameters. */
uint32_t bmp_size = (splash_screen[0x02] | (splash_screen[0x03] << 8) | (splash_screen[0x04] << 16) | (splash_screen[0x05] << 24));
uint32_t bmp_offset = (splash_screen[0x0A] | (splash_screen[0x0B] << 8) | (splash_screen[0x0C] << 16) | (splash_screen[0x0D] << 24));
uint32_t bmp_width = (splash_screen[0x12] | (splash_screen[0x13] << 8) | (splash_screen[0x14] << 16) | (splash_screen[0x15] << 24));
uint32_t bmp_height = (splash_screen[0x16] | (splash_screen[0x17] << 8) | (splash_screen[0x18] << 16) | (splash_screen[0x19] << 24));
uint16_t bmp_bpp = (splash_screen[0x1C] | (splash_screen[0x1D] << 8));
uint32_t bmp_data_size = (splash_screen[0x22] | (splash_screen[0x23] << 8) | (splash_screen[0x24] << 16) | (splash_screen[0x25] << 24));
/* Data size can be wrong or set to 0. In that case, we calculate it instead. */
if (!bmp_data_size || (bmp_data_size >= bmp_size))
bmp_data_size = (bmp_size - bmp_offset);
/* Only accept images up to 720x1280 resolution and with 32 BPP. */
if ((bmp_width > SPLASH_SCREEN_WIDTH_MAX) || (bmp_height > SPLASH_SCREEN_HEIGHT_MAX)) {
fatal_error("Invalid splash screen dimensions!\n");
} else if (bmp_bpp != SPLASH_SCREEN_BPP) {
fatal_error("Invalid splash screen color depth!\n");
} else if (bmp_data_size > SPLASH_SCREEN_SIZE_MAX) {
fatal_error("Splash screen data size is too big!\n");
}
/* Calculate screen positions. */
uint32_t bmp_pos_x = ((SPLASH_SCREEN_WIDTH_MAX - bmp_width) / 2);
uint32_t bmp_pos_y = ((SPLASH_SCREEN_HEIGHT_MAX - bmp_height) / 2);
/* Advance to data. */
splash_screen += bmp_offset;
/* Render the BMP. */
render_bmp((uint32_t *)splash_screen, (uint32_t *)fb_address, bmp_width, bmp_height, bmp_pos_x, bmp_pos_y);
} else {
fatal_error("Invalid splash screen format!\n");
}
/* Display the splash screen for three seconds. */
udelay(3000000);

View file

@ -19,9 +19,12 @@
#include <stdint.h>
/* TODO: Actually make this a real thing. */
extern unsigned char g_default_splash_screen[1];
#define SPLASH_SCREEN_WIDTH_MAX 720
#define SPLASH_SCREEN_HEIGHT_MAX 1280
#define SPLASH_SCREEN_BPP 32
#define SPLASH_SCREEN_STRIDE 768
#define SPLASH_SCREEN_SIZE_MAX (SPLASH_SCREEN_HEIGHT_MAX * SPLASH_SCREEN_STRIDE * 4)
void display_splash_screen_bmp(const char *custom_splash_path);
void display_splash_screen_bmp(const char *custom_splash_path, void *fb_address);
#endif

View file

@ -1,19 +0,0 @@
/*
* Copyright (c) 2018 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 "splash_screen.h"
uint8_t g_default_splash_screen[1] = {0};