/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * * Copyright (C) 2012 Marcin Bukat * * 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 1 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include "config.h" #include "loader_strerror.h" #include "rkw-loader.h" #include "crc32-rkw.h" #include "file.h" #include "panic.h" /* loosely based on load_firmware() * on success we return size of loaded image * on error we return negative value which can be deciphered by means * of rkw_strerror() function */ int load_rkw(unsigned char* buf, const char* firmware, int buffer_size) { char filename[MAX_PATH]; int fd; int rc; int len; int ret; uint32_t crc, fw_crc; struct rkw_header_t rkw_info; /* only filename passed */ if (firmware[0] != '/') { /* First check in BOOTDIR */ snprintf(filename, sizeof(filename), BOOTDIR "/%s",firmware); fd = open(filename, O_RDONLY); if(fd < 0) { /* Check in root dir */ snprintf(filename, sizeof(filename),"/%s",firmware); fd = open(filename, O_RDONLY); if (fd < 0) return EFILE_NOT_FOUND; } } else { /* full path passed */ fd = open(firmware, O_RDONLY); if (fd < 0) return EFILE_NOT_FOUND; } rc = read(fd, &rkw_info, sizeof(rkw_info)); if (rc < (int)sizeof(rkw_info)) { ret = EREAD_HEADER_FAILED; goto end; } /* check if RKW is valid */ if (rkw_info.magic_number != RKLD_MAGIC) { ret = EINVALID_FORMAT; goto end; } /* check header crc if present */ if (rkw_info.load_options & RKW_HEADER_CRC) { crc = crc32_rkw((uint8_t *)&rkw_info, sizeof(rkw_info)-sizeof(uint32_t)); if (rkw_info.crc != crc) { ret = EBAD_HEADER_CHKSUM; goto end; } } /* check image size */ len = rkw_info.load_limit - rkw_info.load_address; if (len > buffer_size) { ret = EFILE_TOO_BIG; goto end; } /* check load address - we support loading only at 0x60000000 */ if (rkw_info.load_address != 0x60000000) { ret = EINVALID_LOAD_ADDR; goto end; } /* rockbox extension - we use one of reserved fields to store * model number information. This prevents from loading * rockbox RKW for different player. */ if (rkw_info.reserved0 != 0 && rkw_info.reserved0 != MODEL_NUMBER) { ret = EBAD_MODEL; goto end; } /* skip header */ lseek(fd, sizeof(rkw_info), SEEK_SET); /* load image into buffer */ rc = read(fd, buf, len); if(rc < len) { ret = EREAD_IMAGE_FAILED; goto end; } if (rkw_info.load_options & RKW_IMAGE_CRC) { rc = read(fd, &fw_crc, sizeof(uint32_t)); crc = crc32_rkw((uint8_t *)buf, len); if (fw_crc != crc) { ret = EBAD_CHKSUM; goto end; } } ret = len; end: close(fd); return ret; }