rockbox/utils/MTP/beastpatcher/beastpatcher.c
James Buren a90ef8195b mknkboot/beastpatcher: implement basic firmware validation
This imports the MD5 code used by other utilities and creates
a function for checking the provided NK.bin against known original
firmware checksums. Integration into mknkboot and beastpatcher is
also added.

For the sake of consistency with beastpatcher, mknkboot had its
printf statements rewrote to print to stderr like beastpatcher
does.

Change-Id: I0e52271d8d627a5b02302ab5cd1da2815b7cec1e
2021-06-12 06:32:08 +00:00

287 lines
8.3 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* $Id$
*
* Copyright (c) 2009, Dave Chapman
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#include <stdio.h>
#if !defined(_MSC_VER)
#include <unistd.h>
#include <fcntl.h>
#endif
#include <string.h>
#include <stdlib.h>
#if !defined(_MSC_VER)
#include <inttypes.h>
#else
#include "pstdint.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include "mtp_common.h"
#include "bootimg.h"
#include "mknkboot.h"
/* Code to create a single-boot bootloader.
Based on tools/gigabeats.c by Will Robertson.
*/
/* Entry point (and load address) for the main Rockbox bootloader */
#define BL_ENTRY_POINT 0x8a000000
static void put_uint32le(uint32_t x, unsigned char* p)
{
p[0] = (unsigned char)(x & 0xff);
p[1] = (unsigned char)((x >> 8) & 0xff);
p[2] = (unsigned char)((x >> 16) & 0xff);
p[3] = (unsigned char)((x >> 24) & 0xff);
}
static uint32_t calc_csum(const unsigned char* pb, int cb)
{
uint32_t l = 0;
while (cb--)
l += *pb++;
return l;
}
static void create_single_boot(unsigned char* boot, int bootlen,
unsigned char** fwbuf, off_t* fwsize)
{
unsigned char* buf;
/* 15 bytes for header, 16 for signature bypass,
* 12 for record header, size of bootloader, 12 for footer */
*fwsize = 15 + 16 + 12 + bootlen + 12;
*fwbuf = malloc(*fwsize);
if(fwbuf == NULL) {
fprintf(stderr, "[ERR] Cannot allocate memory.\n" );
*fwbuf = NULL;
*fwsize = 0;
return;
}
buf = *fwbuf;
/* Copy bootloader image. */
memcpy(buf + 43, boot, bootlen);
/* Step 2: Create the file header */
sprintf((char *)buf, "B000FF\n");
put_uint32le(0x88200000, buf+7);
/* If the value below is too small, the update will attempt to flash.
* Be careful when changing this (leaving it as is won't cause issues) */
put_uint32le(0xCC0CD8, buf+11);
/* Step 3: Add the signature bypass record */
put_uint32le(0x88065A10, buf+15);
put_uint32le(4, buf+19);
put_uint32le(0xE3A00001, buf+27);
put_uint32le(calc_csum(buf+27,4), buf+23);
/* Step 4: Create a record for the actual code */
put_uint32le(BL_ENTRY_POINT, buf+31);
put_uint32le(bootlen, buf+35);
put_uint32le(calc_csum(buf + 43, bootlen), buf+39);
/* Step 5: Write the footer */
put_uint32le(0, buf+*fwsize-12);
put_uint32le(BL_ENTRY_POINT, buf+*fwsize-8);
put_uint32le(0, buf+*fwsize-4);
return;
}
static int readfile(const char* filename, struct filebuf *buf)
{
int res;
FILE* fp;
size_t bread;
#ifdef _LARGEFILE64_SOURCE
struct stat64 sb;
res = stat64(filename, &sb);
#else
struct stat sb;
res = stat(filename, &sb);
#endif
if(res == -1) {
fprintf(stderr, "[ERR] Getting firmware file size failed!\n");
return 1;
}
buf->len = sb.st_size;
buf->buf = (unsigned char*)malloc(buf->len);
/* load firmware binary to memory. */
fp = fopen(filename, "rb");
bread = fread(buf->buf, sizeof(unsigned char), buf->len, fp);
if((off_t)(bread * sizeof(unsigned char)) != buf->len) {
fprintf(stderr, "[ERR] Error reading file %s!\n", filename);
return 1;
}
fclose(fp);
return 0;
}
int beastpatcher(const char* bootfile, const char* firmfile, int interactive)
{
char yesno[4];
struct mtp_info_t mtp_info;
struct filebuf bootloader;
struct filebuf firmware;
struct filebuf fw;
bootloader.buf = bootimg;
bootloader.len = LEN_bootimg;
if (bootfile) {
if(readfile(bootfile, &bootloader) != 0) {
fprintf(stderr,"[ERR] Reading bootloader file failed.\n");
return 1;
}
}
if (firmfile) {
if(readfile(firmfile, &firmware) != 0) {
fprintf(stderr,"[ERR] Reading firmware file failed.\n");
return 1;
}
}
if (mtp_init(&mtp_info) < 0) {
fprintf(stderr,"[ERR] Can not init MTP\n");
return 1;
}
/* Scan for attached MTP devices. */
if (mtp_scan(&mtp_info) < 0)
{
fprintf(stderr,"[ERR] No devices found\n");
return 1;
}
printf("[INFO] Found device \"%s - %s\"\n", mtp_info.manufacturer,
mtp_info.modelname);
printf("[INFO] Device version: \"%s\"\n",mtp_info.version);
if (interactive) {
if(firmfile) {
printf("\nEnter i to install the Rockbox dualboot bootloader or c "
"to cancel and do nothing (i/c): ");
}
else {
printf("\nEnter i to install the Rockbox bootloader or c to cancel "
"and do nothing (i/c): ");
}
fgets(yesno,4,stdin);
}
if (!interactive || yesno[0]=='i')
{
if(firmfile) {
/* if a firmware file is given create a dualboot image. */
if(verifyfirm(&firmware) < 0 || mknkboot(&firmware, &bootloader, &fw))
{
fprintf(stderr,"[ERR] Creating dualboot firmware failed.\n");
return 1;
}
}
else {
/* Create a single-boot bootloader from the embedded bootloader */
create_single_boot(bootloader.buf, bootloader.len, &fw.buf, &fw.len);
}
if (fw.buf == NULL)
{
fprintf(stderr,"[ERR] Creating the bootloader failed.\n");
return 1;
}
if (mtp_send_firmware(&mtp_info, fw.buf, fw.len) == 0)
{
fprintf(stderr,"[INFO] Bootloader installed successfully.\n");
}
else
{
fprintf(stderr,"[ERR] Bootloader install failed.\n");
}
/* We are now done with the firmware image */
free(fw.buf);
}
else
{
fprintf(stderr,"[INFO] Installation cancelled.\n");
}
if(bootfile) {
free(bootloader.buf);
}
if(firmfile) {
free(firmware.buf);
}
mtp_finished(&mtp_info);
return 0;
}
int sendfirm(const char* filename)
{
struct mtp_info_t mtp_info;
if (mtp_init(&mtp_info) < 0) {
fprintf(stderr,"[ERR] Can not init MTP\n");
return 1;
}
/* Scan for attached MTP devices. */
if (mtp_scan(&mtp_info) < 0)
{
fprintf(stderr,"[ERR] No devices found\n");
return 1;
}
printf("[INFO] Found device \"%s - %s\"\n", mtp_info.manufacturer,
mtp_info.modelname);
printf("[INFO] Device version: \"%s\"\n",mtp_info.version);
mtp_send_file(&mtp_info, filename);
mtp_finished(&mtp_info);
return 0;
}