summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <me.theuser@yahoo.com>2017-02-10 13:53:46 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2017-10-29 17:53:04 +0100
commite9de9c1452499d852c8b4ec75cde06520fe7c961 (patch)
treed1312cb9bfe439412806c8bdf09e19c5bb36a2a2
parentefb71ed0ce6afee0643025f71e48eabca297d872 (diff)
downloadrockbox-e9de9c1452499d852c8b4ec75cde06520fe7c961.tar.gz
rockbox-e9de9c1452499d852c8b4ec75cde06520fe7c961.zip
Add boot from other volumes in bootloader on targets with HAVE_MULTIVOLUME
Adds the ability to load firmware from other drives on MULTIVOLUME targets Mihail Zenkov <mihail.zenkov@gmail.com> had posted a hard coded patch to allow this on several Sansa players, I made it more universal Redirect file rockbox_main.<name> should placed in root of drive you would like to be main, if this file empty or there a single slash '/' firmware will be loaded from /.rockbox in root of this drive If instead a /<*DIRECTORY*> is supplied in rockbox_main.<name> then firmware will be loaded from /<dir>/.rockbox/ NOTES* The directory can have multiple levels however.. leading slash MUST be included trailing slash can be omitted (eg. /test/.rockbox would be simply '/test' in the redirect file) Redirect file will not work on internal drive (whatever is default boot drive) Volume with the highest index containing redirect file will be loaded first. Firmware file is checked for boot data region, if missing, firmware image will not be loaded. On failure or if no redirect file is found load will fallback to internal drive Currently only Sansa Fuze+, Sansa Clip+, Sansa Clip Zip, Sansa Fuzev2, and Sansa Fuzev1 are implemented. Players (with HAVE_MULTIVOLUME) will need #define HAVE_BOOTDATA and #define BOOT_REDIR "rockbox_main.<name>" added to their config file boot_data is implemented in crt0.s file (See g#1552) ARM and IMX233 have aleady been implemented Once these conditions are met <HAVE_MULTIBOOT> will be defined by config.h Partitions on the drives are able to have a redirect as well. Change-Id: Iada3263919f6bcad7d0d7d8279b4239aafa07ee9
-rw-r--r--firmware/common/rb-loader.c192
-rw-r--r--firmware/export/config.h5
-rw-r--r--firmware/export/config/sansaclipplus.h2
-rw-r--r--firmware/export/config/sansaclipzip.h2
-rw-r--r--firmware/export/config/sansafuze.h3
-rw-r--r--firmware/export/config/sansafuzeplus.h2
-rw-r--r--firmware/export/config/sansafuzev2.h3
-rw-r--r--firmware/include/rb-loader.h17
8 files changed, 167 insertions, 59 deletions
diff --git a/firmware/common/rb-loader.c b/firmware/common/rb-loader.c
index 82b636d451..8bf553a3a8 100644
--- a/firmware/common/rb-loader.c
+++ b/firmware/common/rb-loader.c
@@ -7,6 +7,7 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * 8 *
9 * Copyright (C) 2005 by Linus Nielsen Feltzing 9 * Copyright (C) 2005 by Linus Nielsen Feltzing
10 * Copyright (C) 2017 by William Wilgus
10 * 11 *
11 * This program is free software; you can redistribute it and/or 12 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License 13 * modify it under the terms of the GNU General Public License
@@ -22,18 +23,16 @@
22#include "config.h" 23#include "config.h"
23#include "system.h" 24#include "system.h"
24#include "file.h" 25#include "file.h"
25#include "rb-loader.h"
26#include "loader_strerror.h" 26#include "loader_strerror.h"
27 27
28#if defined(HAVE_BOOTDATA) 28#if defined(HAVE_BOOTDATA)
29#include "bootdata.h" 29#include "bootdata.h"
30#include "crc32.h" 30#include "crc32.h"
31 31
32/* Write boot data into location marked by magic header 32/* Write bootdata into location in FIRMWARE marked by magic header
33 * buffer is already loaded with the firmware image 33 * Assumes buffer is already loaded with the firmware image
34 * we just need to find the location and write 34 * We just need to find the location and write data into the
35 * data into the payload along with the crc 35 * payload region along with the crc for later verification and use.
36 * for later verification and use.
37 * Returns payload len on success, 36 * Returns payload len on success,
38 * On error returns EKEY_NOT_FOUND 37 * On error returns EKEY_NOT_FOUND
39 */ 38 */
@@ -51,68 +50,92 @@ static int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
51 if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 || 50 if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
52 fw_boot_data->magic[1] != BOOT_DATA_MAGIC1) 51 fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
53 continue; 52 continue;
54 /* 0 fill bootloader struct then add our data */ 53
55 memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE); 54 memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
56 bl_boot_data.boot_volume = boot_volume; 55 bl_boot_data.boot_volume = boot_volume;
57 /* 0 fill payload region in firmware */ 56
58 memset(fw_boot_data->payload, 0, fw_boot_data->length); 57 memset(fw_boot_data->payload, 0, fw_boot_data->length);
59 /* determine maximum bytes we can write to firmware 58 /* determine maximum bytes we can write to firmware
60 BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */ 59 BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
61 payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length); 60 payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
62 /* write payload size back to firmware struct */
63 fw_boot_data->length = payload_len; 61 fw_boot_data->length = payload_len;
64 /* copy data to firmware bootdata struct */ 62 /* copy data to FIRMWARE bootdata struct */
65 memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len); 63 memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
66 /* calculate and write the crc for the payload */ 64 /* crc will be used within the firmware to check validity of bootdata */
67 fw_boot_data->crc = crc_32(fw_boot_data->payload, 65 fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
68 payload_len,
69 0xffffffff);
70 break; 66 break;
71 67
72 } 68 }
73 return payload_len; 69 return payload_len;
74} 70}
75#endif /* HAVE_BOOTDATA */ 71#endif /* HAVE_BOOTDATA */
76/* Load firmware image in a format created by add method of tools/scramble 72
77 * on success we return size loaded image 73#ifdef HAVE_MULTIBOOT /* defined by config.h */
78 * on error we return negative value which can be deciphered by means 74/* Check in root of this <volume> for rockbox_main.<playername>
79 * of strerror() function 75 * if this file empty or there is a single slash '/'
76 * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
77 * If instead '/<*DIRECTORY*>' is supplied
78 * addpath will be set to this DIRECTORY buf =
79 * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
80 * On error returns Negative number or 0
81 * On success returns bytes from snprintf
82 * and generated path will be placed in buf
83 * note: if supplied buffer is too small return will be
84 * the number of bytes that would have been written
80 */ 85 */
81int load_firmware(unsigned char* buf, const char* firmware, int buffer_size) 86int get_redirect_dir(char* buf, int buffer_size, int volume,
87 const char* rootdir, const char* firmware)
82{ 88{
83 char filename[MAX_PATH];
84 int fd; 89 int fd;
85 int rc; 90 int f_offset;
86 int len; 91 char add_path[MAX_PATH];
87 int ret = 0; 92 /* Check in root of volume for rockbox_main.<playername> redirect */
88 unsigned long chksum; 93 snprintf(add_path, sizeof(add_path), "/<%d>/"BOOT_REDIR, volume);
89 unsigned long sum; 94 fd = open(add_path, O_RDONLY);
90 int i; 95 if (fd < 0)
91 96 return EFILE_NOT_FOUND;
92 /* only filename passed */ 97
93 if (firmware[0] != '/') 98 /*clear add_path for re-use*/
94 { 99 memset(add_path, 0, sizeof(add_path));
95 /* First check in BOOTDIR */ 100 f_offset = read(fd, add_path,sizeof(add_path));
96 snprintf(filename, sizeof(filename), BOOTDIR "/%s",firmware); 101 close(fd);
97
98 fd = open(filename, O_RDONLY);
99 if(fd < 0)
100 {
101 /* Check in root dir */
102 snprintf(filename, sizeof(filename),"/%s",firmware);
103 fd = open(filename, O_RDONLY);
104 102
105 if (fd < 0) 103 for(int i = f_offset - 1;i > 0; i--)
106 return EFILE_NOT_FOUND;
107 }
108 }
109 else
110 { 104 {
111 /* full path passed */ 105 /* strip control chars < SPACE or all if path doesn't start with '/' */
112 fd = open(firmware, O_RDONLY); 106 if (add_path[i] < 0x20 || add_path[0] != '/')
113 if (fd < 0) 107 add_path[i] = '\0';
114 return EFILE_NOT_FOUND;
115 } 108 }
109 /* if '/add_path' is specified in rockbox_main.<playername>
110 path is /<vol#>/add_path/rootdir/firmwarename
111 if add_path is empty or '/' is missing from beginning
112 path is /<vol#>/rootdir/firmwarename
113 */
114 return snprintf(buf, buffer_size, "/<%d>%s/%s/%s", volume, add_path,
115 rootdir, firmware);
116}
117#endif /* HAVE_MULTIBOOT */
118
119/* loads a firmware file from supplied filename
120 * file opened, checks firmware size and checksum
121 * if no error, firmware loaded to supplied buffer
122 * file closed
123 * Returns size of loaded image on success
124 * On error returns Negative value deciphered by means
125 * of strerror() function
126 */
127static int load_firmware_filename(unsigned char* buf,
128 const char* filename,
129 int buffer_size)
130{
131 int len;
132 unsigned long chksum, sum;
133 int i;
134 int ret;
135 int fd = open(filename, O_RDONLY);
136
137 if (fd < 0)
138 return EFILE_NOT_FOUND;
116 139
117 len = filesize(fd) - 8; 140 len = filesize(fd) - 8;
118 141
@@ -124,18 +147,16 @@ int load_firmware(unsigned char* buf, const char* firmware, int buffer_size)
124 147
125 lseek(fd, FIRMWARE_OFFSET_FILE_CRC, SEEK_SET); 148 lseek(fd, FIRMWARE_OFFSET_FILE_CRC, SEEK_SET);
126 149
127 rc = read(fd, &chksum, 4); 150 if (read(fd, &chksum, 4) < 4)
128 chksum = betoh32(chksum); /* Rockbox checksums are big-endian */
129 if(rc < 4)
130 { 151 {
131 ret = EREAD_CHKSUM_FAILED; 152 ret = EREAD_CHKSUM_FAILED;
132 goto end; 153 goto end;
133 } 154 }
155 chksum = betoh32(chksum); /* Rockbox checksums are big-endian */
134 156
135 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET); 157 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
136 158
137 rc = read(fd, buf, len); 159 if (read(fd, buf, len) < len)
138 if(rc < len)
139 { 160 {
140 ret = EREAD_IMAGE_FAILED; 161 ret = EREAD_IMAGE_FAILED;
141 goto end; 162 goto end;
@@ -148,19 +169,72 @@ int load_firmware(unsigned char* buf, const char* firmware, int buffer_size)
148 sum += buf[i]; 169 sum += buf[i];
149 } 170 }
150 171
151 if(sum != chksum) 172 if (sum != chksum)
152 { 173 {
153 ret = EBAD_CHKSUM; 174 ret = EBAD_CHKSUM;
154 goto end; 175 goto end;
155 } 176 }
156#ifdef HAVE_BOOTDATA
157 /* 0 is the default boot volume */
158 write_bootdata(buf, ret, 0);
159#endif
160 ret = len; 177 ret = len;
161 178
162
163end: 179end:
164 close(fd); 180 close(fd);
165 return ret; 181 return ret;
166} 182}
183
184/* Load firmware image in a format created by add method of tools/scramble
185 * on success we return size loaded image
186 * on error we return negative value which can be deciphered by means
187 * of strerror() function
188 */
189int load_firmware(unsigned char* buf, const char* firmware, int buffer_size)
190{
191
192 int ret = EFILE_NOT_FOUND;
193 char filename[MAX_PATH+2];
194 /* only filename passed */
195 if (firmware[0] != '/')
196 {
197
198#ifdef HAVE_MULTIBOOT /* defined by config.h */
199 /* checks <volumes> highest index to lowest for redirect file
200 * 0 is the default boot volume, it is not checked here
201 * if found <volume>/rockbox_main.<playername> and firmware
202 * has a bootdata region this firmware will be loaded */
203 for (unsigned int i = NUM_VOLUMES - 1; i > 0 && ret < 0; i--)
204 {
205 if (get_redirect_dir(filename, sizeof(filename), i,
206 BOOTDIR, firmware) > 0)
207 {
208 ret = load_firmware_filename(buf, filename, buffer_size);
209 /* if firmware has no boot_data don't load from external drive */
210 if (write_bootdata(buf, ret, i) <= 0)
211 ret = EKEY_NOT_FOUND;
212 }
213 /* if ret is valid breaks from loop to continue loading */
214 }
215#endif
216
217 if (ret < 0) /* Check default volume, no valid firmware file loaded yet */
218 {
219 /* First check in BOOTDIR */
220 snprintf(filename, sizeof(filename), BOOTDIR "/%s",firmware);
221
222 ret = load_firmware_filename(buf, filename, buffer_size);
223
224 if (ret < 0)
225 {
226 /* Check in root dir */
227 snprintf(filename, sizeof(filename),"/%s",firmware);
228 ret = load_firmware_filename(buf, filename, buffer_size);
229 }
230#ifdef HAVE_BOOTDATA
231 /* 0 is the default boot volume */
232 write_bootdata(buf, ret, 0);
233#endif
234 }
235 }
236 else /* full path passed ROLO etc.*/
237 ret = load_firmware_filename(buf, firmware, buffer_size);
238
239 return ret;
240}
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 51e0f7a17b..475bd573d4 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -859,6 +859,11 @@ Lyre prototype 1 */
859#error HAVE_MULTIDRIVE needs to have an explicit NUM_DRIVES 859#error HAVE_MULTIDRIVE needs to have an explicit NUM_DRIVES
860#endif 860#endif
861 861
862/* note to remove multi-partition booting this could be changed to MULTIDRIVE */
863#if defined(HAVE_BOOTDATA) && defined(BOOT_REDIR) && defined(HAVE_MULTIVOLUME)
864#define HAVE_MULTIBOOT
865#endif
866
862#ifndef NUM_DRIVES 867#ifndef NUM_DRIVES
863#define NUM_DRIVES 1 868#define NUM_DRIVES 1
864#endif 869#endif
diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h
index cf2b1136a3..addf7d86c0 100644
--- a/firmware/export/config/sansaclipplus.h
+++ b/firmware/export/config/sansaclipplus.h
@@ -9,6 +9,8 @@
9#define FIRMWARE_OFFSET_FILE_CRC 0 9#define FIRMWARE_OFFSET_FILE_CRC 0
10/* Define if boot data from bootloader has been enabled for the target */ 10/* Define if boot data from bootloader has been enabled for the target */
11#define HAVE_BOOTDATA 11#define HAVE_BOOTDATA
12/* define boot redirect file name allows booting from external drives */
13#define BOOT_REDIR "rockbox_main.clip+"
12 14
13#define HAVE_MULTIDRIVE 15#define HAVE_MULTIDRIVE
14#define NUM_DRIVES 2 16#define NUM_DRIVES 2
diff --git a/firmware/export/config/sansaclipzip.h b/firmware/export/config/sansaclipzip.h
index 9f1ff1f206..fc9f558eef 100644
--- a/firmware/export/config/sansaclipzip.h
+++ b/firmware/export/config/sansaclipzip.h
@@ -9,6 +9,8 @@
9#define FIRMWARE_OFFSET_FILE_CRC 0 9#define FIRMWARE_OFFSET_FILE_CRC 0
10/* Define if boot data from bootloader has been enabled for the target */ 10/* Define if boot data from bootloader has been enabled for the target */
11#define HAVE_BOOTDATA 11#define HAVE_BOOTDATA
12/* define boot redirect file name allows booting from external drives */
13#define BOOT_REDIR "rockbox_main.clipzip"
12 14
13#define HAVE_MULTIDRIVE 15#define HAVE_MULTIDRIVE
14#define NUM_DRIVES 2 16#define NUM_DRIVES 2
diff --git a/firmware/export/config/sansafuze.h b/firmware/export/config/sansafuze.h
index d674d1f88a..fae3463d75 100644
--- a/firmware/export/config/sansafuze.h
+++ b/firmware/export/config/sansafuze.h
@@ -8,6 +8,9 @@
8/* Define if boot data from bootloader has been enabled for the target */ 8/* Define if boot data from bootloader has been enabled for the target */
9#define HAVE_BOOTDATA 9#define HAVE_BOOTDATA
10 10
11/* define boot redirect file name allows booting from external drives */
12#define BOOT_REDIR "rockbox_main.fuze"
13
11#define HW_SAMPR_CAPS SAMPR_CAP_ALL 14#define HW_SAMPR_CAPS SAMPR_CAP_ALL
12 15
13/* define this if you have recording possibility */ 16/* define this if you have recording possibility */
diff --git a/firmware/export/config/sansafuzeplus.h b/firmware/export/config/sansafuzeplus.h
index af5235a6c3..2a57619b06 100644
--- a/firmware/export/config/sansafuzeplus.h
+++ b/firmware/export/config/sansafuzeplus.h
@@ -10,6 +10,8 @@
10#define MODEL_NAME "Sandisk Sansa Fuze+" 10#define MODEL_NAME "Sandisk Sansa Fuze+"
11/* Define if boot data from bootloader has been enabled for the target */ 11/* Define if boot data from bootloader has been enabled for the target */
12#define HAVE_BOOTDATA 12#define HAVE_BOOTDATA
13/* define boot redirect file name allows booting from external drives */
14#define BOOT_REDIR "rockbox_main.fuze+"
13 15
14#define HW_SAMPR_CAPS SAMPR_CAP_ALL 16#define HW_SAMPR_CAPS SAMPR_CAP_ALL
15 17
diff --git a/firmware/export/config/sansafuzev2.h b/firmware/export/config/sansafuzev2.h
index a4d90160bd..b85e0747a3 100644
--- a/firmware/export/config/sansafuzev2.h
+++ b/firmware/export/config/sansafuzev2.h
@@ -8,6 +8,9 @@
8/* Define if boot data from bootloader has been enabled for the target */ 8/* Define if boot data from bootloader has been enabled for the target */
9#define HAVE_BOOTDATA 9#define HAVE_BOOTDATA
10 10
11/* define boot redirect file name allows booting from external drives */
12#define BOOT_REDIR "rockbox_main.fuze2"
13
11#define HW_SAMPR_CAPS SAMPR_CAP_ALL 14#define HW_SAMPR_CAPS SAMPR_CAP_ALL
12 15
13/* define this if you have recording possibility */ 16/* define this if you have recording possibility */
diff --git a/firmware/include/rb-loader.h b/firmware/include/rb-loader.h
index 86c5026af9..71b6e038aa 100644
--- a/firmware/include/rb-loader.h
+++ b/firmware/include/rb-loader.h
@@ -19,3 +19,20 @@
19 ****************************************************************************/ 19 ****************************************************************************/
20 20
21int load_firmware(unsigned char* buf, const char* firmware, int buffer_size); 21int load_firmware(unsigned char* buf, const char* firmware, int buffer_size);
22
23#ifdef HAVE_MULTIBOOT /* defined by config.h */
24/* Check in root of this <volume> for rockbox_main.<playername>
25 * if this file empty or there is a single slash '/'
26 * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
27 * If instead '/<*DIRECTORY*>' is supplied
28 * addpath will be set to this DIRECTORY buf =
29 * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
30 * On error returns Negative number or 0
31 * On success returns bytes from snprintf
32 * and generated path will be placed in buf
33 * note: if supplied buffer is too small return will be
34 * the number of bytes that would have been written
35 */
36int get_redirect_dir(char* buf, int buffer_size, int volume,
37 const char* rootdir, const char* firmware);
38#endif