diff options
Diffstat (limited to 'firmware/common/rb-loader.c')
-rw-r--r-- | firmware/common/rb-loader.c | 192 |
1 files changed, 133 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 | */ |
81 | int load_firmware(unsigned char* buf, const char* firmware, int buffer_size) | 86 | int 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 | */ | ||
127 | static 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 | |||
163 | end: | 179 | end: |
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 | */ | ||
189 | int 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 | } | ||