Skip to content

Reserve and Render header in managed BL mode #5950

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 70 additions & 2 deletions tools/build_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import tempfile
import datetime
import uuid
import struct
import zlib
import hashlib
from shutil import rmtree
from os.path import join, exists, dirname, basename, abspath, normpath, splitext
from os.path import relpath
Expand All @@ -33,7 +36,7 @@
from .arm_pack_manager import Cache
from .utils import (mkdir, run_cmd, run_cmd_ext, NotSupportedException,
ToolException, InvalidReleaseTargetException,
intelhex_offset)
intelhex_offset, integer)
from .paths import (MBED_CMSIS_PATH, MBED_TARGETS_PATH, MBED_LIBRARIES,
MBED_HEADER, MBED_DRIVERS, MBED_PLATFORM, MBED_HAL,
MBED_CONFIG_FILE, MBED_LIBRARIES_DRIVERS,
Expand Down Expand Up @@ -339,8 +342,68 @@ def prepare_toolchain(src_paths, build_dir, target, toolchain_name,

return toolchain

def _printihex(ihex):
import pprint
pprint.PrettyPrinter().pprint(ihex.todict())

def _real_region_size(region):
try:
part = intelhex_offset(region.filename, offset=region.start)
return (part.maxaddr() - part.minaddr()) + 1
except AttributeError:
return region.size


def _fill_header(region_list, current_region):
"""Fill an application header region

This is done it three steps:
* Fill the whole region with zeros
* Fill const, timestamp and size entries with their data
* Fill the digests using this header as the header region
"""
region_dict = {r.name: r for r in region_list}
header = IntelHex()
header.puts(current_region.start, b'\x00' * current_region.size)
start = current_region.start
for member in current_region.filename:
_, type, subtype, data = member
member_size = Config.header_member_size(member)
if type == "const":
fmt = {
"8le": ">B", "16le": "<H", "32le": "<L", "64le": "<Q",
"8be": "<B", "16be": ">H", "32be": ">L", "64be": ">Q"
}[subtype]
header.puts(start, struct.pack(fmt, integer(data, 0)))
elif type == "timestamp":
fmt = {"32le": "<L", "64le": "<Q",
"32be": ">L", "64be": ">Q"}[subtype]
header.puts(start, struct.pack(fmt, time()))
elif type == "size":
fmt = {"32le": "<L", "64le": "<Q",
"32be": ">L", "64be": ">Q"}[subtype]
size = sum(_real_region_size(region_dict[r]) for r in data)
header.puts(start, struct.pack(fmt, size))
elif type == "digest":
if data == "header":
ih = header[:start]
else:
ih = intelhex_offset(region_dict[data].filename, offset=region_dict[data].start)
if subtype.startswith("CRCITT32"):
fmt = {"CRCITT32be": ">l", "CRCITT32le": "<l"}[subtype]
header.puts(start, struct.pack(fmt, zlib.crc32(ih.tobinarray())))
elif subtype.startswith("SHA"):
if subtype == "SHA256":
hash = hashlib.sha256()
elif subtype == "SHA512":
hash = hashlib.sha512()
hash.update(ih.tobinarray())
header.puts(start, hash.digest())
start += Config.header_member_size(member)
return header

def merge_region_list(region_list, destination, padding=b'\xFF'):
"""Merege the region_list into a single image
"""Merge the region_list into a single image

Positional Arguments:
region_list - list of regions, which should contain filenames
Expand All @@ -355,6 +418,11 @@ def merge_region_list(region_list, destination, padding=b'\xFF'):
for region in region_list:
if region.active and not region.filename:
raise ToolException("Active region has no contents: No file found.")
if isinstance(region.filename, list):
header_basename, _ = splitext(destination)
header_filename = header_basename + "_header.hex"
_fill_header(region_list, region).tofile(header_filename, format='hex')
region = region._replace(filename=header_filename)
if region.filename:
print(" Filling region %s with %s" % (region.name, region.filename))
part = intelhex_offset(region.filename, offset=region.start)
Expand Down
60 changes: 59 additions & 1 deletion tools/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from jinja2.environment import Environment
from jsonschema import Draft4Validator, RefResolver

from ..utils import json_file_to_dict, intelhex_offset
from ..utils import json_file_to_dict, intelhex_offset, integer
from ..arm_pack_manager import Cache
from ..targets import (CUMULATIVE_ATTRIBUTES, TARGET_MAP, generate_py_target,
get_resolution_order, Target)
Expand All @@ -41,8 +41,11 @@
unicode = str
PATH_OVERRIDES = set(["target.bootloader_img"])
BOOTLOADER_OVERRIDES = set(["target.bootloader_img", "target.restrict_size",
"target.header_format", "target.header_offset",
"target.app_offset",
"target.mbed_app_start", "target.mbed_app_size"])


# Base class for all configuration exceptions
class ConfigException(Exception):
"""Config system only exception. Makes it easier to distinguish config
Expand Down Expand Up @@ -568,6 +571,41 @@ def regions(self):
raise ConfigException(
"Bootloader build requested but no bootlader configuration")

@staticmethod
def header_member_size(member):
_, _, subtype, _ = member
try:
return int(subtype[:-2]) // 8
except:
if subtype.startswith("CRCITT32"):
return 32 // 8
elif subtype == "SHA256":
return 256 // 8
elif subtype == "SHA512":
return 512 // 8
else:
raise ValueError("target.header_format: subtype %s is not "
"understood" % subtype)

@staticmethod
def _header_size(format):
return sum(Config.header_member_size(m) for m in format)

def _make_header_region(self, start, header_format, offset=None):
size = self._header_size(header_format)
region = Region("header", start, size, False, None)
start += size
start = ((start + 7) // 8) * 8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain this logic in a comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's pretty standard "round up to a multiple of 8" logic. Do you want an explanation of why 8 or something else?

Copy link
Contributor

@sarahmarshy sarahmarshy Feb 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I got that it is a round up to a multiple of 8, but I haven't seen it that often so had to logic it out myself. I think just a comment saying "round up to multiple of 8" would help people who haven't seen it before. Or you could add a comment for the entire function about the region it returns/its alignment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a strong preference for commenting the entire function. I'll do that, if you don't mind.

return (start, region)

@staticmethod
def _assign_new_offset(rom_start, start, new_offset, region_name):
newstart = rom_start + integer(new_offset, 0)
if newstart < start:
raise ConfigException(
"Can not place % region inside previous region" % region_name)
return newstart

def _generate_bootloader_build(self, rom_start, rom_size):
start = rom_start
rom_end = rom_start + rom_size
Expand All @@ -588,14 +626,34 @@ def _generate_bootloader_build(self, rom_start, rom_size):
yield Region("bootloader", rom_start, part_size, False,
filename)
start = rom_start + part_size
if self.target.header_format:
if self.target.header_offset:
start = self._assign_new_offset(
rom_start, start, self.target.header_offset, "header")
start, region = self._make_header_region(
start, self.target.header_format)
yield region._replace(filename=self.target.header_format)
if self.target.restrict_size is not None:
new_size = int(self.target.restrict_size, 0)
new_size = Config._align_floor(start + new_size, self.sectors) - start
yield Region("application", start, new_size, True, None)
start += new_size
if self.target.header_format:
if self.target.header_offset:
start = self._assign_new_offset(
rom_start, start, self.target.header_offset, "header")
start, region = self._make_header_region(
start, self.target.header_format)
yield region
if self.target.app_offset:
start = self._assign_new_offset(
rom_start, start, self.target.app_offset, "application")
yield Region("post_application", start, rom_end - start,
False, None)
else:
if self.target.app_offset:
start = self._assign_new_offset(
rom_start, start, self.target.app_offset, "application")
yield Region("application", start, rom_end - start,
True, None)
if start > rom_start + rom_size:
Expand Down
8 changes: 8 additions & 0 deletions tools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,3 +539,11 @@ def intelhex_offset(filename, offset):
raise ToolException("File %s does not have a known binary file type"
% filename)
return ih


def integer(maybe_string, base):
"""Make an integer of a number or a string"""
if isinstance(maybe_string, int):
return maybe_string
else:
return int(maybe_string, base)