blob: 5684776231603591e49857ad482050b65245622a [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 <string.h>
#include "os/mynewt.h"
#include "mfg/mfg.h"
/**
* The "manufacturing meta region" is located at the end of the boot loader
* flash area. This region has the following structure.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |Version (0x01) | 0xff padding |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | TLV type | TLV size | TLV data ("TLV size" bytes) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~
* ~ ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | TLV type | TLV size | TLV data ("TLV size" bytes) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~
* ~ ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Region size | 0xff padding |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Magic (0x3bb2a269) |
* +-+-+-+-+-+--+-+-+-+-end of boot loader area+-+-+-+-+-+-+-+-+-+-+
*
* The number of TLVs is variable; two are shown above for illustrative
* purposes.
*
* Fields:
* <Header>
* 1. Version: Manufacturing meta version number; always 0x01.
*
* <TLVs>
* 2. TLV type: Indicates the type of data to follow.
* 3. TLV size: The number of bytes of data to follow.
* 4. TLV data: "TLV size" bytes of data.
*
* <Footer>
* 5. Region size: The size, in bytes, of the entire manufacturing meta region;
* includes header, TLVs, and footer.
* 6. Magic: indicates the presence of the manufacturing meta region.
*/
#define MFG_META_MAGIC 0x3bb2a269
#define MFG_META_HEADER_SZ 4
#define MFG_META_FOOTER_SZ 8
#define MFG_META_TLV_SZ 2
#define MFG_META_FLASH_AREA_SZ 12
struct {
uint8_t valid:1;
uint32_t off;
uint32_t size;
} mfg_state;
struct mfg_meta_header {
uint8_t version;
uint8_t pad8;
uint16_t pad16;
};
struct mfg_meta_footer {
uint16_t size;
uint16_t pad16;
uint32_t magic;
};
_Static_assert(sizeof (struct mfg_meta_header) == MFG_META_HEADER_SZ,
"mfg_meta_header must be 4 bytes");
_Static_assert(sizeof (struct mfg_meta_footer) == MFG_META_FOOTER_SZ,
"mfg_meta_footer must be 8 bytes");
_Static_assert(sizeof (struct mfg_meta_flash_area) == MFG_META_FLASH_AREA_SZ,
"mfg_meta_flash_area must be 12 bytes");
/**
* Retrieves a TLV header from the mfg meta region. To request the first TLV
* in the region, specify an offset of 0. To request a subsequent TLV, specify
* the values retrieved by the previous call to this function.
*
* @param tlv (in / out) Input: The previously-read TLV header; not used
* as input when requesting the first TLV.
* Output: On success, the requested TLV header
* gets written here.
* @param off (in / out) Input: The flash-area-offset of the previously
* read TLV header; 0 when requesting the
* first TLV.
* Output: On success, the flash-area-offset of
* the retrieved TLV header.
*
* @return 0 if a TLV header was successfully retrieved;
* MFG_EDONE if there are no additional TLVs to
* read;
* Other MFG error code on failure.
*/
int
mfg_next_tlv(struct mfg_meta_tlv *tlv, uint32_t *off)
{
const struct flash_area *fap;
int rc;
if (!mfg_state.valid) {
return MFG_EUNINIT;
}
rc = flash_area_open(FLASH_AREA_BOOTLOADER, &fap);
if (rc != 0) {
rc = MFG_EFLASH;
goto done;
}
if (*off == 0) {
*off = mfg_state.off + MFG_META_HEADER_SZ;
} else {
/* Advance past current TLV. */
*off += MFG_META_TLV_SZ + tlv->size;
}
if (*off + MFG_META_FOOTER_SZ >= fap->fa_size) {
/* Reached end of boot area; no more TLVs. */
return MFG_EDONE;
}
/* Read next TLV. */
memset(tlv, 0, sizeof *tlv);
rc = flash_area_read(fap, *off, tlv, MFG_META_TLV_SZ);
if (rc != 0) {
rc = MFG_EFLASH;
goto done;
}
rc = 0;
done:
flash_area_close(fap);
return rc;
}
/**
* Retrieves a TLV header of the specified type from the mfg meta region. To
* request the first TLV in the region, specify an offset of 0. To request a
* subsequent TLV, specify the values retrieved by the previous call to this
* function.
*
* @param tlv (in / out) Input: The previously-read TLV header; not used
* as input when requesting the first TLV.
* Output: On success, the requested TLV header
* gets written here.
* @param off (in / out) Input: The flash-area-offset of the previously
* read TLV header; 0 when requesting the
* first TLV.
* Output: On success, the flash-area-offset of
* the retrieved TLV header.
* @param type The type of TLV to retrieve; one of the
* MFG_META_TLV_TYPE_[...] constants.
*
* @return 0 if a TLV header was successfully retrieved;
* MFG_EDONE if there are no additional TLVs of
* the specified type to read;
* Other MFG error code on failure.
*/
int
mfg_next_tlv_with_type(struct mfg_meta_tlv *tlv, uint32_t *off, uint8_t type)
{
int rc;
while (1) {
rc = mfg_next_tlv(tlv, off);
if (rc != 0) {
break;
}
if (tlv->type == type) {
break;
}
/* Proceed to next TLV. */
}
return rc;
}
/**
* Reads a flash-area TLV from the manufacturing meta region. This function
* should only be called after a TLV has been identified as having the
* MFG_META_TLV_TYPE_FLASH_AREA type.
*
* @param tlv The header of the TLV to read. This header
* should have been retrieved via a call to
* mfg_next_tlv() or mfg_next_tlv_with_type().
* @param off The flash-area-offset of the TLV header. Note:
* this is the offset of the TLV header, not
* the TLV data.
* @param out_mfa (out) On success, the retrieved flash area
* information gets written here.
*
* @return 0 on success; MFG error code on failure.
*/
int
mfg_read_tlv_flash_area(const struct mfg_meta_tlv *tlv, uint32_t off,
struct mfg_meta_flash_area *out_mfa)
{
const struct flash_area *fap;
int read_sz;
int rc;
rc = flash_area_open(FLASH_AREA_BOOTLOADER, &fap);
if (rc != 0) {
rc = MFG_EFLASH;
goto done;
}
memset(out_mfa, 0, sizeof *out_mfa);
read_sz = min(MFG_META_FLASH_AREA_SZ, tlv->size);
rc = flash_area_read(fap, off + MFG_META_TLV_SZ, out_mfa, read_sz);
if (rc != 0) {
rc = MFG_EFLASH;
goto done;
}
rc = 0;
done:
flash_area_close(fap);
return rc;
}
/**
* Reads a hash TLV from the manufacturing meta region. This function should
* only be called after a TLV has been identified as having the
* MFG_META_TLV_TYPE_HASH type.
*
* @param tlv The header of the TLV to read. This header
* should have been retrieved via a call to
* mfg_next_tlv() or mfg_next_tlv_with_type().
* @param off The flash-area-offset of the TLV header. Note:
* this is the offset of the TLV header, not
* the TLV data.
* @param out_hash (out) On success, the retrieved SHA256 hash gets
* written here. This buffer must be at least
* 32 bytes wide.
*
* @return 0 on success; MFG error code on failure.
*/
int
mfg_read_tlv_hash(const struct mfg_meta_tlv *tlv, uint32_t off, void *out_hash)
{
const struct flash_area *fap;
int read_sz;
int rc;
rc = flash_area_open(FLASH_AREA_BOOTLOADER, &fap);
if (rc != 0) {
rc = MFG_EFLASH;
goto done;
}
read_sz = min(MFG_HASH_SZ, tlv->size);
rc = flash_area_read(fap, off + MFG_META_TLV_SZ, out_hash, read_sz);
if (rc != 0) {
rc = MFG_EFLASH;
goto done;
}
rc = 0;
done:
flash_area_close(fap);
return rc;
}
/**
* Locates the manufacturing meta region in flash. This function must be
* called before any TLVs can be read. No-op if this function has already
* executed successfully.
*
* @return 0 on success; MFG error code on failure.
*/
int
mfg_init(void)
{
const struct flash_area *fap;
struct mfg_meta_footer ftr;
uint16_t off;
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
if (mfg_state.valid) {
/* Already initialized. */
return 0;
}
mfg_state.valid = 0;
rc = flash_area_open(FLASH_AREA_BOOTLOADER, &fap);
if (rc != 0) {
rc = MFG_EFLASH;
goto done;
}
off = fap->fa_size - sizeof ftr;
rc = flash_area_read(fap, off, &ftr, sizeof ftr);
if (rc != 0) {
rc = MFG_EFLASH;
goto done;
}
if (ftr.magic != MFG_META_MAGIC) {
rc = MFG_EBADDATA;
goto done;
}
if (ftr.size > fap->fa_size) {
rc = MFG_EBADDATA;
goto done;
}
mfg_state.valid = 1;
mfg_state.off = fap->fa_size - ftr.size;
mfg_state.size = ftr.size;
rc = 0;
done:
flash_area_close(fap);
return rc;
}