blob: 1a10c546a8b2876ee532af3a637625482cd1c6df [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include "os/mynewt.h"
#include "hal/hal_bsp.h"
#include "hal/hal_flash.h"
#include "hal/hal_flash_int.h"
#include "mfg/mfg.h"
#include "flash_map/flash_map.h"
const struct flash_area *flash_map;
int flash_map_entries;
int
flash_area_open(uint8_t id, const struct flash_area **fap)
{
const struct flash_area *area;
int i;
if (flash_map == NULL) {
return SYS_EACCES;
}
for (i = 0; i < flash_map_entries; i++) {
area = flash_map + i;
if (area->fa_id == id) {
*fap = area;
return 0;
}
}
return SYS_ENOENT;
}
void
flash_area_close(const struct flash_area *fa)
{
/* nothing to do for now */
}
int
flash_area_to_sectors(int id, int *cnt, struct flash_area *ret)
{
const struct flash_area *fa;
const struct hal_flash *hf;
uint32_t start;
uint32_t size;
int rc;
int i;
rc = flash_area_open(id, &fa);
if (rc != 0) {
return rc;
}
*cnt = 0;
hf = hal_bsp_flash_dev(fa->fa_device_id);
for (i = 0; i < hf->hf_sector_cnt; i++) {
hf->hf_itf->hff_sector_info(hf, i, &start, &size);
if (start >= fa->fa_off && start < fa->fa_off + fa->fa_size) {
if (ret) {
ret->fa_id = id;
ret->fa_device_id = fa->fa_device_id;
ret->fa_off = start;
ret->fa_size = size;
ret++;
}
(*cnt)++;
}
}
flash_area_close(fa);
return 0;
}
int
flash_area_getnext_sector(int id, int *sec_id, struct flash_area *ret)
{
const struct flash_area *fa;
const struct hal_flash *hf;
uint32_t start;
uint32_t size;
int rc;
int i;
rc = flash_area_open(id, &fa);
if (rc) {
return rc;
}
if (!ret || *sec_id < -1) {
rc = SYS_EINVAL;
goto end;
}
hf = hal_bsp_flash_dev(fa->fa_device_id);
i = *sec_id + 1;
for (; i < hf->hf_sector_cnt; i++) {
hf->hf_itf->hff_sector_info(hf, i, &start, &size);
if (start >= fa->fa_off && start < fa->fa_off + fa->fa_size) {
ret->fa_id = id;
ret->fa_device_id = fa->fa_device_id;
ret->fa_off = start;
ret->fa_size = size;
*sec_id = i;
rc = 0;
goto end;
}
}
rc = SYS_ENOENT;
end:
flash_area_close(fa);
return rc;
}
int
flash_area_read(const struct flash_area *fa, uint32_t off, void *dst,
uint32_t len)
{
if (off > fa->fa_size || off + len > fa->fa_size) {
return -1;
}
return hal_flash_read(fa->fa_device_id, fa->fa_off + off, dst, len);
}
int
flash_area_write(const struct flash_area *fa, uint32_t off, const void *src,
uint32_t len)
{
if (off > fa->fa_size || off + len > fa->fa_size) {
return -1;
}
return hal_flash_write(fa->fa_device_id, fa->fa_off + off,
(void *)src, len);
}
int
flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len)
{
if (off > fa->fa_size || off + len > fa->fa_size) {
return -1;
}
return hal_flash_erase(fa->fa_device_id, fa->fa_off + off, len);
}
uint8_t
flash_area_align(const struct flash_area *fa)
{
return hal_flash_align(fa->fa_device_id);
}
uint32_t
flash_area_erased_val(const struct flash_area *fa)
{
return hal_flash_erased_val(fa->fa_device_id);
}
/**
* Checks if a flash area has been erased. Returns false if there are any
* non non-erased bytes.
*
* @param fa An opened flash area to iterate.
* the count of flash area TLVs in the meta
* region is greater than this number, this
* function fails.
* @param empty (out) On success, the is_empty result gets written
* here.
* @return 0 on success; nonzero on failure.
*/
int
flash_area_is_empty(const struct flash_area *fa, bool *empty)
{
int rc;
*empty = false;
rc = hal_flash_isempty_no_buf(fa->fa_device_id, fa->fa_off, fa->fa_size);
if (rc < 0) {
return rc;
} else if (rc == 1) {
*empty = true;
}
return 0;
}
int
flash_area_read_is_empty(const struct flash_area *fa, uint32_t off, void *dst,
uint32_t len)
{
return hal_flash_isempty(fa->fa_device_id, fa->fa_off + off, dst, len);
}
/**
* Converts the specified image slot index to a flash area ID. If the
* specified value is not a valid image slot index (0 or 1), a crash is
* triggered.
*/
int
flash_area_id_from_image_slot(int slot)
{
switch (slot) {
case 0:
return FLASH_AREA_IMAGE_0;
case 1:
return FLASH_AREA_IMAGE_1;
default:
assert(0);
return FLASH_AREA_IMAGE_0;
}
}
/**
* Converts the specified flash area ID to an image slot index (0 or 1). If
* the area ID does not correspond to an image slot, -1 is returned.
*/
int
flash_area_id_to_image_slot(int area_id)
{
switch (area_id) {
case FLASH_AREA_IMAGE_0:
return 0;
case FLASH_AREA_IMAGE_1:
return 1;
default:
return -1;
}
}
/**
* Reads the flash map layout from the manufacturing meta region. This
* function requires that the flash map be populated with a
* FLASH_AREA_BOOTLOADER entry, as the meta region is stored at the end of the
* boot loader area.
*
* @param max_areas The maximum number of flash areas to read. If
* the count of flash area TLVs in the meta
* region is greater than this number, this
* function fails.
* @param out_areas (out) An array of flash areas. On success, the flash
* map stored in the meta region gets written
* here.
* @param out_num_areas (out) On success, the number of flash areas read gets
* written here.
*
* @return 0 on success; nonzero on failure.
*/
static int
flash_map_read_mfg(int max_areas,
struct flash_area *out_areas, int *out_num_areas)
{
struct mfg_meta_flash_area meta_flash_area;
struct mfg_meta_tlv tlv;
struct flash_area *fap;
uint32_t off;
int rc;
*out_num_areas = 0;
off = 0;
/* Ensure manufacturing meta region has been located in flash. */
rc = mfg_init();
if (rc != 0) {
return rc;
}
while (1) {
if (*out_num_areas >= max_areas) {
return -1;
}
rc = mfg_next_tlv_with_type(&tlv, &off, MFG_META_TLV_TYPE_FLASH_AREA);
switch (rc) {
case 0:
break;
case MFG_EDONE:
return 0;
default:
return rc;
}
rc = mfg_read_tlv_flash_area(&tlv, off, &meta_flash_area);
if (rc != 0) {
return rc;
}
fap = out_areas + *out_num_areas;
fap->fa_id = meta_flash_area.area_id;
fap->fa_device_id = meta_flash_area.device_id;
fap->fa_off = meta_flash_area.offset;
fap->fa_size = meta_flash_area.size;
(*out_num_areas)++;
}
}
void
flash_map_init(void)
{
static struct flash_area mfg_areas[MYNEWT_VAL(FLASH_MAP_MAX_AREAS)];
int num_areas;
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
rc = hal_flash_init();
SYSINIT_PANIC_ASSERT(rc == 0);
/* Use the hardcoded default flash map. This is done for two reasons:
* 1. A minimal flash map configuration is required to boot strap the
* process of reading the flash map from the manufacturing meta region.
* In particular, a FLASH_AREA_BOOTLOADER entry is required, as the meta
* region is located at the end of the boot loader area.
* 2. If we fail to read the flash map from the meta region, the system
* continues to use the default flash map.
*/
flash_map = sysflash_map_dflt;
flash_map_entries = sizeof sysflash_map_dflt / sizeof sysflash_map_dflt[0];
/* Attempt to read the flash map from the manufacturing meta region. On
* success, use the new flash map instead of the default hardcoded one.
*/
rc = flash_map_read_mfg(sizeof mfg_areas / sizeof mfg_areas[0],
mfg_areas, &num_areas);
if (rc == 0) {
flash_map = mfg_areas;
flash_map_entries = num_areas;
}
}