blob: 899c5adb42f94901925dc9133341e82ae5db95e2 [file] [log] [blame]
David Brown5153bd62017-01-06 11:16:53 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#include <zephyr.h>
David Brown5153bd62017-01-06 11:16:53 -070021#include <flash.h>
22
Marti Bolivar51181cf2017-03-20 11:03:41 -040023#include "target.h"
Ricardo Salveti3a2c1242017-01-19 10:22:35 -020024
David Brown5153bd62017-01-06 11:16:53 -070025#include <flash_map/flash_map.h>
26#include <hal/hal_flash.h>
27#include <sysflash/sysflash.h>
28
Marti Bolivar4a97b4c2017-01-31 18:20:02 -050029#define BOOT_LOG_LEVEL BOOT_LOG_LEVEL_INFO
30#include "bootutil/bootutil_log.h"
Ricardo Salveti7cf3d9e2017-01-18 16:38:22 -020031
David Brown5153bd62017-01-06 11:16:53 -070032extern struct device *boot_flash_device;
33
34/*
Marti Bolivared2eaf12017-06-14 09:46:53 -040035 * For now, we only support one flash device.
36 *
37 * Pick a random device ID for it that's unlikely to collide with
38 * anything "real".
39 */
40#define FLASH_DEVICE_ID 100
41#define FLASH_DEVICE_BASE CONFIG_FLASH_BASE_ADDRESS
42
Marti Bolivar83a3cef2017-05-05 11:59:49 -040043#define FLASH_MAP_ENTRY_MAGIC 0xd00dbeef
44
45struct flash_map_entry {
46 const uint32_t magic;
47 const struct flash_area area;
48 unsigned int ref_count;
49};
50
Marti Bolivared2eaf12017-06-14 09:46:53 -040051/*
David Brown5153bd62017-01-06 11:16:53 -070052 * The flash area describes essentially the partition table of the
53 * flash. In this case, it starts with FLASH_AREA_IMAGE_0.
54 */
Marti Bolivar83a3cef2017-05-05 11:59:49 -040055static struct flash_map_entry part_map[] = {
David Brown0d0652a2017-04-11 17:33:30 -060056 {
Marti Bolivar83a3cef2017-05-05 11:59:49 -040057 .magic = FLASH_MAP_ENTRY_MAGIC,
58 .area = {
59 .fa_id = FLASH_AREA_IMAGE_0,
60 .fa_device_id = FLASH_DEVICE_ID,
61 .fa_off = FLASH_AREA_IMAGE_0_OFFSET,
62 .fa_size = FLASH_AREA_IMAGE_0_SIZE,
63 },
David Brown0d0652a2017-04-11 17:33:30 -060064 },
65 {
Marti Bolivar83a3cef2017-05-05 11:59:49 -040066 .magic = FLASH_MAP_ENTRY_MAGIC,
67 .area = {
68 .fa_id = FLASH_AREA_IMAGE_1,
69 .fa_device_id = FLASH_DEVICE_ID,
70 .fa_off = FLASH_AREA_IMAGE_1_OFFSET,
71 .fa_size = FLASH_AREA_IMAGE_1_SIZE,
72 },
David Brown0d0652a2017-04-11 17:33:30 -060073 },
74 {
Marti Bolivar83a3cef2017-05-05 11:59:49 -040075 .magic = FLASH_MAP_ENTRY_MAGIC,
76 .area = {
77 .fa_id = FLASH_AREA_IMAGE_SCRATCH,
78 .fa_device_id = FLASH_DEVICE_ID,
79 .fa_off = FLASH_AREA_IMAGE_SCRATCH_OFFSET,
80 .fa_size = FLASH_AREA_IMAGE_SCRATCH_SIZE,
81 },
82 }
David Brown5153bd62017-01-06 11:16:53 -070083};
84
Marti Bolivared2eaf12017-06-14 09:46:53 -040085int flash_device_base(uint8_t fd_id, uintptr_t *ret)
86{
87 if (fd_id != FLASH_DEVICE_ID) {
88 BOOT_LOG_ERR("invalid flash ID %d; expected %d",
89 fd_id, FLASH_DEVICE_ID);
90 return -EINVAL;
91 }
92 *ret = FLASH_DEVICE_BASE;
93 return 0;
94}
95
David Brown5153bd62017-01-06 11:16:53 -070096/*
David Brown5153bd62017-01-06 11:16:53 -070097 * `open` a flash area. The `area` in this case is not the individual
98 * sectors, but describes the particular flash area in question.
99 */
100int flash_area_open(uint8_t id, const struct flash_area **area)
101{
David Brown0d0652a2017-04-11 17:33:30 -0600102 int i;
Ricardo Salveti7cf3d9e2017-01-18 16:38:22 -0200103
David Brown0d0652a2017-04-11 17:33:30 -0600104 BOOT_LOG_DBG("area %d", id);
David Brown5153bd62017-01-06 11:16:53 -0700105
David Brown0d0652a2017-04-11 17:33:30 -0600106 for (i = 0; i < ARRAY_SIZE(part_map); i++) {
Marti Bolivar83a3cef2017-05-05 11:59:49 -0400107 if (id == part_map[i].area.fa_id) {
David Brown0d0652a2017-04-11 17:33:30 -0600108 break;
David Brown74b3c582017-04-11 17:39:25 -0600109 }
David Brown0d0652a2017-04-11 17:33:30 -0600110 }
David Brown74b3c582017-04-11 17:39:25 -0600111 if (i == ARRAY_SIZE(part_map)) {
David Brown0d0652a2017-04-11 17:33:30 -0600112 return -1;
David Brown74b3c582017-04-11 17:39:25 -0600113 }
David Brown5153bd62017-01-06 11:16:53 -0700114
Marti Bolivar83a3cef2017-05-05 11:59:49 -0400115 *area = &part_map[i].area;
116 part_map[i].ref_count++;
David Brown0d0652a2017-04-11 17:33:30 -0600117 return 0;
David Brown5153bd62017-01-06 11:16:53 -0700118}
119
120/*
121 * Nothing to do on close.
122 */
123void flash_area_close(const struct flash_area *area)
124{
Fabio Utzige7686262017-06-28 09:26:54 -0300125 struct flash_map_entry *entry;
126
127 if (!area) {
128 return;
129 }
130
131 entry = CONTAINER_OF(area, struct flash_map_entry, area);
Marti Bolivar83a3cef2017-05-05 11:59:49 -0400132 if (entry->magic != FLASH_MAP_ENTRY_MAGIC) {
133 BOOT_LOG_ERR("invalid area %p (id %u)", area, area->fa_id);
134 return;
135 }
136 if (entry->ref_count == 0) {
137 BOOT_LOG_ERR("area %u use count underflow", area->fa_id);
138 return;
139 }
140 entry->ref_count--;
141}
142
143void zephyr_flash_area_warn_on_open(void)
144{
145 int i;
146
147 for (i = 0; i < ARRAY_SIZE(part_map); i++) {
148 struct flash_map_entry *entry = &part_map[i];
149 if (entry->ref_count) {
150 BOOT_LOG_WRN("area %u has %u users",
151 entry->area.fa_id, entry->ref_count);
152 }
153 }
David Brown5153bd62017-01-06 11:16:53 -0700154}
155
156int flash_area_read(const struct flash_area *area, uint32_t off, void *dst,
David Brown0d0652a2017-04-11 17:33:30 -0600157 uint32_t len)
David Brown5153bd62017-01-06 11:16:53 -0700158{
David Brown0d0652a2017-04-11 17:33:30 -0600159 BOOT_LOG_DBG("area=%d, off=%x, len=%x", area->fa_id, off, len);
160 return flash_read(boot_flash_device, area->fa_off + off, dst, len);
David Brown5153bd62017-01-06 11:16:53 -0700161}
162
163int flash_area_write(const struct flash_area *area, uint32_t off, const void *src,
David Brown0d0652a2017-04-11 17:33:30 -0600164 uint32_t len)
David Brown5153bd62017-01-06 11:16:53 -0700165{
David Brown0d0652a2017-04-11 17:33:30 -0600166 int rc = 0;
Michael Scotte12746c2017-01-31 16:07:08 -0800167
David Brown0d0652a2017-04-11 17:33:30 -0600168 BOOT_LOG_DBG("area=%d, off=%x, len=%x", area->fa_id, off, len);
169 flash_write_protection_set(boot_flash_device, false);
170 rc = flash_write(boot_flash_device, area->fa_off + off, src, len);
171 flash_write_protection_set(boot_flash_device, true);
172 return rc;
David Brown5153bd62017-01-06 11:16:53 -0700173}
174
175int flash_area_erase(const struct flash_area *area, uint32_t off, uint32_t len)
176{
David Brown0d0652a2017-04-11 17:33:30 -0600177 int rc;
Marti Bolivar53cfdb92017-02-10 15:05:22 -0500178
David Brown0d0652a2017-04-11 17:33:30 -0600179 BOOT_LOG_DBG("area=%d, off=%x, len=%x", area->fa_id, off, len);
180 flash_write_protection_set(boot_flash_device, false);
181 rc = flash_erase(boot_flash_device, area->fa_off + off, len);
182 flash_write_protection_set(boot_flash_device, true);
183 return rc;
David Brown5153bd62017-01-06 11:16:53 -0700184}
185
186uint8_t flash_area_align(const struct flash_area *area)
187{
David Brown0d0652a2017-04-11 17:33:30 -0600188 return hal_flash_align(area->fa_id);
David Brown5153bd62017-01-06 11:16:53 -0700189}
190
191/*
192 * This depends on the mappings defined in sysflash.h, and assumes
193 * that slot 0, slot 1, and the scratch area area contiguous.
194 */
195int flash_area_id_from_image_slot(int slot)
196{
David Brown0d0652a2017-04-11 17:33:30 -0600197 return slot + FLASH_AREA_IMAGE_0;
David Brown5153bd62017-01-06 11:16:53 -0700198}
199
Marti Bolivardc4c42b2017-09-21 14:20:40 -0400200/*
201 * This is used by the legacy file as well; don't mark it static until
202 * that file is removed.
203 */
204int flash_area_get_bounds(int idx, uint32_t *off, uint32_t *len)
David Brown5153bd62017-01-06 11:16:53 -0700205{
David Brown0d0652a2017-04-11 17:33:30 -0600206 /*
207 * This simple layout has uniform slots, so just fill in the
208 * right one.
209 */
David Brown74b3c582017-04-11 17:39:25 -0600210 if (idx < FLASH_AREA_IMAGE_0 || idx > FLASH_AREA_IMAGE_SCRATCH) {
David Brown0d0652a2017-04-11 17:33:30 -0600211 return -1;
David Brown74b3c582017-04-11 17:39:25 -0600212 }
David Brown5153bd62017-01-06 11:16:53 -0700213
David Brown0d0652a2017-04-11 17:33:30 -0600214 switch (idx) {
215 case FLASH_AREA_IMAGE_0:
Marti Bolivar1c0ddca2017-06-14 09:51:43 -0400216 *off = FLASH_AREA_IMAGE_0_OFFSET;
217 *len = FLASH_AREA_IMAGE_0_SIZE;
David Brown1d9f1852017-05-23 10:32:22 -0600218 break;
David Brown0d0652a2017-04-11 17:33:30 -0600219 case FLASH_AREA_IMAGE_1:
Marti Bolivar1c0ddca2017-06-14 09:51:43 -0400220 *off = FLASH_AREA_IMAGE_1_OFFSET;
221 *len = FLASH_AREA_IMAGE_1_SIZE;
David Brown1d9f1852017-05-23 10:32:22 -0600222 break;
David Brown0d0652a2017-04-11 17:33:30 -0600223 case FLASH_AREA_IMAGE_SCRATCH:
Marti Bolivar1c0ddca2017-06-14 09:51:43 -0400224 *off = FLASH_AREA_IMAGE_SCRATCH_OFFSET;
225 *len = FLASH_AREA_IMAGE_SCRATCH_SIZE;
David Brown1d9f1852017-05-23 10:32:22 -0600226 break;
David Brown0d0652a2017-04-11 17:33:30 -0600227 default:
228 BOOT_LOG_ERR("unknown flash area %d", idx);
229 return -1;
230 }
David Brown5153bd62017-01-06 11:16:53 -0700231
Marti Bolivardc4c42b2017-09-21 14:20:40 -0400232 BOOT_LOG_DBG("area %d: offset=0x%x, length=0x%x", idx, *off, *len);
David Brown0d0652a2017-04-11 17:33:30 -0600233 return 0;
David Brown5153bd62017-01-06 11:16:53 -0700234}
Marti Bolivar1c0ddca2017-06-14 09:51:43 -0400235
236/*
Marti Bolivardc4c42b2017-09-21 14:20:40 -0400237 * The legacy fallbacks are used instead if the flash driver doesn't
238 * provide page layout support.
Marti Bolivar1c0ddca2017-06-14 09:51:43 -0400239 */
Marti Bolivardc4c42b2017-09-21 14:20:40 -0400240#if defined(CONFIG_FLASH_PAGE_LAYOUT)
241struct layout_data {
242 uint32_t area_idx;
243 uint32_t area_off;
244 uint32_t area_len;
245 void *ret; /* struct flash_area* or struct flash_sector* */
246 uint32_t ret_idx;
247 uint32_t ret_len;
248 int status;
249};
250
251/*
252 * Generic page layout discovery routine. This is kept separate to
253 * support both the deprecated flash_area_to_sectors() and the current
254 * flash_area_get_sectors(). A lot of this can be inlined once
255 * flash_area_to_sectors() is removed.
256 */
257static int flash_area_layout(int idx, int *cnt, void *ret,
258 flash_page_cb cb, struct layout_data *cb_data)
259{
260 cb_data->area_idx = idx;
261 if (flash_area_get_bounds(idx, &cb_data->area_off, &cb_data->area_len)) {
262 return -1;
263 }
264 cb_data->ret = ret;
265 cb_data->ret_idx = 0;
266 cb_data->ret_len = *cnt;
267 cb_data->status = 0;
268
269 flash_page_foreach(boot_flash_device, cb, cb_data);
270
271 if (cb_data->status == 0) {
272 *cnt = cb_data->ret_idx;
273 }
274
275 return cb_data->status;
276}
277
278/*
279 * Check if a flash_page_foreach() callback should exit early, due to
280 * one of the following conditions:
281 *
282 * - The flash page described by "info" is before the area of interest
283 * described in "data"
284 * - The flash page is after the end of the area
285 * - There are too many flash pages on the device to fit in the array
286 * held in data->ret. In this case, data->status is set to -ENOMEM.
287 *
288 * The value to return to flash_page_foreach() is stored in
289 * "bail_value" if the callback should exit early.
290 */
291static bool should_bail(const struct flash_pages_info *info,
292 struct layout_data *data,
293 bool *bail_value)
294{
295 if (info->start_offset < data->area_off) {
296 *bail_value = true;
297 return true;
298 } else if (info->start_offset >= data->area_off + data->area_len) {
299 *bail_value = false;
300 return true;
301 } else if (data->ret_idx >= data->ret_len) {
302 data->status = -ENOMEM;
303 *bail_value = false;
304 return true;
305 }
306
307 return false;
308}
309
310static bool to_sectors_cb(const struct flash_pages_info *info, void *datav)
311{
312 struct layout_data *data = datav;
313 struct flash_area *ret = data->ret;
314 bool bail;
315
316 if (should_bail(info, data, &bail)) {
317 return bail;
318 }
319
320 ret[data->ret_idx].fa_id = data->area_idx;
321 ret[data->ret_idx].fa_device_id = 0;
322 ret[data->ret_idx].pad16 = 0;
323 ret[data->ret_idx].fa_off = info->start_offset;
324 ret[data->ret_idx].fa_size = info->size;
325 data->ret_idx++;
326
327 return true;
328}
329
330int flash_area_to_sectors(int idx, int *cnt, struct flash_area *ret)
331{
332 struct layout_data data;
333
334 return flash_area_layout(idx, cnt, ret, to_sectors_cb, &data);
335}
336
337static bool get_sectors_cb(const struct flash_pages_info *info, void *datav)
338{
339 struct layout_data *data = datav;
340 struct flash_sector *ret = data->ret;
341 bool bail;
342
343 if (should_bail(info, data, &bail)) {
344 return bail;
345 }
346
347 ret[data->ret_idx].fs_off = info->start_offset - data->area_off;
348 ret[data->ret_idx].fs_size = info->size;
349 data->ret_idx++;
350
351 return true;
352}
353
Marti Bolivar1c0ddca2017-06-14 09:51:43 -0400354int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret)
355{
Marti Bolivardc4c42b2017-09-21 14:20:40 -0400356 struct layout_data data;
Marti Bolivar1c0ddca2017-06-14 09:51:43 -0400357
Marti Bolivardc4c42b2017-09-21 14:20:40 -0400358 return flash_area_layout(idx, cnt, ret, get_sectors_cb, &data);
Marti Bolivar1c0ddca2017-06-14 09:51:43 -0400359}
Marti Bolivardc4c42b2017-09-21 14:20:40 -0400360#endif /* defined(CONFIG_FLASH_PAGE_LAYOUT) */