Skip to content

Commit 4cac89c

Browse files
committed
Reorder post-build and managed bootloader mode merging
Priously, post-bulid was run before the merge from managed bootloader mode. This renders many post-build scripts less than useful, as most of them compute a digest of the ROM image generated in a build. This reorders the post-build scripts to come after the managed bootloader mode so that post-build script digests are useful again.
1 parent aeeb43f commit 4cac89c

File tree

3 files changed

+237
-157
lines changed

3 files changed

+237
-157
lines changed

tools/build_api.py

Lines changed: 2 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,14 @@
2727
from os.path import relpath
2828
from os import linesep, remove, makedirs
2929
from time import time
30-
from intelhex import IntelHex
3130
from json import load, dump
3231
from jinja2 import FileSystemLoader
3332
from jinja2.environment import Environment
3433

3534
from .arm_pack_manager import Cache
3635
from .utils import (mkdir, run_cmd, run_cmd_ext, NotSupportedException,
3736
ToolException, InvalidReleaseTargetException,
38-
intelhex_offset, integer, generate_update_filename, copy_when_different)
37+
copy_when_different)
3938
from .paths import (MBED_CMSIS_PATH, MBED_TARGETS_PATH, MBED_LIBRARIES,
4039
MBED_HEADER, MBED_DRIVERS, MBED_PLATFORM, MBED_HAL,
4140
MBED_CONFIG_FILE, MBED_LIBRARIES_DRIVERS,
@@ -393,124 +392,6 @@ def prepare_toolchain(src_paths, build_dir, target, toolchain_name,
393392

394393
return toolchain
395394

396-
def _printihex(ihex):
397-
import pprint
398-
pprint.PrettyPrinter().pprint(ihex.todict())
399-
400-
def _real_region_size(region):
401-
try:
402-
part = intelhex_offset(region.filename, offset=region.start)
403-
return (part.maxaddr() - part.minaddr()) + 1
404-
except AttributeError:
405-
return region.size
406-
407-
408-
def _fill_header(region_list, current_region):
409-
"""Fill an application header region
410-
411-
This is done it three steps:
412-
* Fill the whole region with zeros
413-
* Fill const, timestamp and size entries with their data
414-
* Fill the digests using this header as the header region
415-
"""
416-
region_dict = {r.name: r for r in region_list}
417-
header = IntelHex()
418-
header.puts(current_region.start, b'\x00' * current_region.size)
419-
start = current_region.start
420-
for member in current_region.filename:
421-
_, type, subtype, data = member
422-
member_size = Config.header_member_size(member)
423-
if type == "const":
424-
fmt = {
425-
"8le": ">B", "16le": "<H", "32le": "<L", "64le": "<Q",
426-
"8be": "<B", "16be": ">H", "32be": ">L", "64be": ">Q"
427-
}[subtype]
428-
header.puts(start, struct.pack(fmt, integer(data, 0)))
429-
elif type == "timestamp":
430-
fmt = {"32le": "<L", "64le": "<Q",
431-
"32be": ">L", "64be": ">Q"}[subtype]
432-
header.puts(start, struct.pack(fmt, int(time())))
433-
elif type == "size":
434-
fmt = {"32le": "<L", "64le": "<Q",
435-
"32be": ">L", "64be": ">Q"}[subtype]
436-
size = sum(_real_region_size(region_dict[r]) for r in data)
437-
header.puts(start, struct.pack(fmt, size))
438-
elif type == "digest":
439-
if data == "header":
440-
ih = header[:start]
441-
else:
442-
ih = intelhex_offset(region_dict[data].filename, offset=region_dict[data].start)
443-
if subtype.startswith("CRCITT32"):
444-
fmt = {"CRCITT32be": ">L", "CRCITT32le": "<L"}[subtype]
445-
crc_val = zlib.crc32(ih.tobinarray()) & 0xffffffff
446-
header.puts(start, struct.pack(fmt, crc_val))
447-
elif subtype.startswith("SHA"):
448-
if subtype == "SHA256":
449-
hash = hashlib.sha256()
450-
elif subtype == "SHA512":
451-
hash = hashlib.sha512()
452-
hash.update(ih.tobinarray())
453-
header.puts(start, hash.digest())
454-
start += Config.header_member_size(member)
455-
return header
456-
457-
458-
def merge_region_list(region_list, destination, notify, config, padding=b'\xFF'):
459-
"""Merge the region_list into a single image
460-
461-
Positional Arguments:
462-
region_list - list of regions, which should contain filenames
463-
destination - file name to write all regions to
464-
padding - bytes to fill gaps with
465-
"""
466-
merged = IntelHex()
467-
_, format = splitext(destination)
468-
notify.info("Merging Regions")
469-
# Merged file list: Keep track of binary/hex files that we have already
470-
# merged. e.g In some cases, bootloader may be split into multiple parts, but
471-
# all internally referring to the same bootloader file.
472-
merged_list = []
473-
474-
for region in region_list:
475-
if region.active and not region.filename:
476-
raise ToolException("Active region has no contents: No file found.")
477-
if isinstance(region.filename, list):
478-
header_basename, _ = splitext(destination)
479-
header_filename = header_basename + "_header.hex"
480-
_fill_header(region_list, region).tofile(header_filename, format='hex')
481-
region = region._replace(filename=header_filename)
482-
if region.filename and (region.filename not in merged_list):
483-
notify.info(" Filling region %s with %s" % (region.name, region.filename))
484-
part = intelhex_offset(region.filename, offset=region.start)
485-
part.start_addr = None
486-
# Normally, we assume that part.maxddr() can be beyond
487-
# end of rom. However, if the size is restricted with config, do check.
488-
if config.target.restrict_size is not None:
489-
part_size = (part.maxaddr() - part.minaddr()) + 1
490-
if part_size > region.size:
491-
raise ToolException("Contents of region %s does not fit"
492-
% region.name)
493-
merged_list.append(region.filename)
494-
merged.merge(part)
495-
elif region.filename in merged_list:
496-
notify.info(" Skipping %s as it is merged previously" % (region.name))
497-
498-
# Hex file can have gaps, so no padding needed. While other formats may
499-
# need padding. Iterate through segments and pad the gaps.
500-
if format != ".hex":
501-
# begin patching from the end of the first segment
502-
_, begin = merged.segments()[0]
503-
for start, stop in merged.segments()[1:]:
504-
pad_size = start - begin
505-
merged.puts(begin, padding * pad_size)
506-
begin = stop + 1
507-
508-
if not exists(dirname(destination)):
509-
makedirs(dirname(destination))
510-
notify.info("Space used after regions merged: 0x%x" %
511-
(merged.maxaddr() - merged.minaddr() + 1))
512-
merged.tofile(destination, format=format.strip("."))
513-
514395

515396
UPDATE_WHITELIST = (
516397
"application",
@@ -605,27 +486,7 @@ def build_project(src_paths, build_path, target, toolchain_name,
605486
objects = toolchain.compile_sources(resources, sorted(resources.get_file_paths(FileType.INC_DIR)))
606487
resources.add_files_to_type(FileType.OBJECT, objects)
607488

608-
# Link Program
609-
if toolchain.config.has_regions:
610-
binary, _ = toolchain.link_program(resources, build_path, name + "_application")
611-
region_list = list(toolchain.config.regions)
612-
region_list = [r._replace(filename=binary) if r.active else r
613-
for r in region_list]
614-
res = "%s.%s" % (join(build_path, name),
615-
getattr(toolchain.target, "OUTPUT_EXT", "bin"))
616-
merge_region_list(region_list, res, notify, toolchain.config)
617-
update_regions = [
618-
r for r in region_list if r.name in UPDATE_WHITELIST
619-
]
620-
if update_regions:
621-
update_res = join(build_path, generate_update_filename(name, toolchain.target))
622-
merge_region_list(update_regions, update_res, notify, toolchain.config)
623-
res = (res, update_res)
624-
else:
625-
res = (res, None)
626-
else:
627-
res, _ = toolchain.link_program(resources, build_path, name)
628-
res = (res, None)
489+
res = toolchain.link_program(resources, build_path, name)
629490

630491
into_dir, extra_artifacts = toolchain.config.deliver_into()
631492
if into_dir:

tools/regions.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# mbed SDK
2+
# Copyright (c) 2011-2013 ARM Limited
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""
17+
Utilities for working with region lists.
18+
"""
19+
20+
import hashlib
21+
import struct
22+
import zlib
23+
from time import time
24+
from os.path import splitext, exists, dirname
25+
from os import makedirs
26+
from .config import Config
27+
from .utils import (
28+
ToolException,
29+
intelhex_offset,
30+
integer
31+
)
32+
from intelhex import IntelHex
33+
34+
UPDATE_WHITELIST = (
35+
"application"
36+
)
37+
38+
39+
def _printihex(ihex):
40+
import pprint
41+
pprint.PrettyPrinter().pprint(ihex.todict())
42+
43+
44+
def _real_region_size(region):
45+
try:
46+
part = intelhex_offset(region.filename, offset=region.start)
47+
return (part.maxaddr() - part.minaddr()) + 1
48+
except AttributeError:
49+
return region.size
50+
51+
52+
def _fill_header(region_list, current_region):
53+
"""Fill an application header region
54+
55+
This is done it three steps:
56+
* Fill the whole region with zeros
57+
* Fill const, timestamp and size entries with their data
58+
* Fill the digests using this header as the header region
59+
"""
60+
region_dict = {r.name: r for r in region_list}
61+
header = IntelHex()
62+
header.puts(current_region.start, b'\x00' * current_region.size)
63+
start = current_region.start
64+
for member in current_region.filename:
65+
_, type, subtype, data = member
66+
if type == "const":
67+
fmt = {
68+
"8le": ">B", "16le": "<H", "32le": "<L", "64le": "<Q",
69+
"8be": "<B", "16be": ">H", "32be": ">L", "64be": ">Q"
70+
}[subtype]
71+
header.puts(start, struct.pack(fmt, integer(data, 0)))
72+
elif type == "timestamp":
73+
fmt = {"32le": "<L", "64le": "<Q",
74+
"32be": ">L", "64be": ">Q"}[subtype]
75+
header.puts(start, struct.pack(fmt, int(time())))
76+
elif type == "size":
77+
fmt = {"32le": "<L", "64le": "<Q",
78+
"32be": ">L", "64be": ">Q"}[subtype]
79+
size = sum(_real_region_size(region_dict[r]) for r in data)
80+
header.puts(start, struct.pack(fmt, size))
81+
elif type == "digest":
82+
if data == "header":
83+
ih = header[:start]
84+
else:
85+
ih = intelhex_offset(
86+
region_dict[data].filename,
87+
offset=region_dict[data].start
88+
)
89+
if subtype.startswith("CRCITT32"):
90+
fmt = {"CRCITT32be": ">L", "CRCITT32le": "<L"}[subtype]
91+
crc_val = zlib.crc32(ih.tobinarray()) & 0xffffffff
92+
header.puts(start, struct.pack(fmt, crc_val))
93+
elif subtype.startswith("SHA"):
94+
if subtype == "SHA256":
95+
hash = hashlib.sha256()
96+
elif subtype == "SHA512":
97+
hash = hashlib.sha512()
98+
hash.update(ih.tobinarray())
99+
header.puts(start, hash.digest())
100+
start += Config.header_member_size(member)
101+
return header
102+
103+
104+
def merge_region_list(
105+
region_list,
106+
destination,
107+
notify,
108+
config,
109+
padding=b'\xFF'
110+
):
111+
"""Merge the region_list into a single image
112+
113+
Positional Arguments:
114+
region_list - list of regions, which should contain filenames
115+
destination - file name to write all regions to
116+
padding - bytes to fill gaps with
117+
"""
118+
merged = IntelHex()
119+
_, format = splitext(destination)
120+
notify.info("Merging Regions")
121+
# Merged file list: Keep track of binary/hex files that we have already
122+
# merged. e.g In some cases, bootloader may be split into multiple parts,
123+
# but all internally referring to the same bootloader file.
124+
merged_list = []
125+
126+
for region in region_list:
127+
if region.active and not region.filename:
128+
raise ToolException(
129+
"Active region has no contents: No file found."
130+
)
131+
if isinstance(region.filename, list):
132+
header_basename, _ = splitext(destination)
133+
header_filename = header_basename + "_header.hex"
134+
_fill_header(region_list, region).tofile(
135+
header_filename, format='hex'
136+
)
137+
region = region._replace(filename=header_filename)
138+
if region.filename and (region.filename not in merged_list):
139+
notify.info(" Filling region %s with %s" % (
140+
region.name, region.filename
141+
))
142+
part = intelhex_offset(region.filename, offset=region.start)
143+
part.start_addr = None
144+
# Normally, we assume that part.maxddr() can be beyond
145+
# end of rom. If the size is restricted with config, don't
146+
# allow this.
147+
if config.target.restrict_size is not None:
148+
part_size = (part.maxaddr() - part.minaddr()) + 1
149+
if part_size > region.size:
150+
raise ToolException(
151+
"Contents of region %s does not fit" % region.name
152+
)
153+
merged_list.append(region.filename)
154+
merged.merge(part)
155+
elif region.filename in merged_list:
156+
notify.info(
157+
" Skipping %s as it is merged previously" % (region.name)
158+
)
159+
160+
# Hex file can have gaps, so no padding needed. While other formats may
161+
# need padding. Iterate through segments and pad the gaps.
162+
if format != ".hex":
163+
# begin patching from the end of the first segment
164+
_, begin = merged.segments()[0]
165+
for start, stop in merged.segments()[1:]:
166+
pad_size = start - begin
167+
merged.puts(begin, padding * pad_size)
168+
begin = stop + 1
169+
170+
if not exists(dirname(destination)):
171+
makedirs(dirname(destination))
172+
notify.info("Space used after regions merged: 0x%x" %
173+
(merged.maxaddr() - merged.minaddr() + 1))
174+
merged.tofile(destination, format=format.strip("."))

0 commit comments

Comments
 (0)