Skip to content

Commit 16aee25

Browse files
flash-regions: Iterate through MemMap without flash size assumptions.
1 parent d0b6f4c commit 16aee25

File tree

3 files changed

+106
-50
lines changed

3 files changed

+106
-50
lines changed

src/__tests__/flash-regions.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('Read MicroPython UICR data.', () => {
2525
'micro:bit v2.0.99+b260810 on 2020-11-17; ' +
2626
'MicroPython b260810 on 2020-11-17';
2727

28-
const result = flashRegions.getIntelHexUicrData(uPyHexFile);
28+
const result = flashRegions.getIntelHexFlashRegionsData(uPyHexFile);
2929

3030
expect(result.flashPageSize).toEqual(expectedPageSize);
3131
expect(result.flashSize).toEqual(expectedFlashSize);

src/flash-regions.ts

Lines changed: 104 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,68 @@
3535
import MemoryMap from 'nrf-intel-hex';
3636

3737
import { DeviceMemInfo, DeviceVersion } from './device-mem-info';
38+
import { areUint8ArraysEqual } from './common';
3839
import * as hexMapUtil from './hex-map-utils';
3940

41+
/** Indicates the data contain in each of the different regions */
42+
enum RegionId {
43+
/** Soft Device is the data blob containing the Nordic Bluetooth stack. */
44+
softDevice = 1,
45+
/** Contains the MicroPython runtime. */
46+
microPython = 2,
47+
/** Contains the MicroPython microbit filesystem reserved flash. */
48+
fs = 3,
49+
}
50+
51+
/**
52+
* The "hash type" field in a region row indicates how to interpret the "hash
53+
* data" field.
54+
*/
55+
enum RegionHashType {
56+
/** The hash data is empty. */
57+
empty = 0,
58+
/** The full hash data field is used as a hash of the region in flash */
59+
data = 1,
60+
/** The 4 LSB bytes of the hash data field are used as a pointer */
61+
pointer = 2,
62+
}
63+
64+
/**
65+
* The data stored in a Region row from the Flash Regions table.
66+
*/
4067
interface RegionRow {
41-
id: number;
68+
/** The Region ID, as described in the RegionId enum. */
69+
id: RegionId;
70+
/** The flash page where this Region starts. */
4271
startPage: number;
72+
/** Length of the region in bytes. */
4373
lengthBytes: number;
44-
hashType: number;
74+
/** Identifies the type of data contained in the Hash Data field. */
75+
hashType: RegionHashType;
76+
/** Hash Data can be one of the types indicated in the RegionHashType enum. */
4577
hashData: number;
78+
/** When Hash Data is a pointer, this variable holds the pointed string. */
4679
hashPointerData: string;
4780
}
4881

82+
/**
83+
* The Flash Regions Table ends with a Header containing information about the
84+
* table itsel.
85+
*/
4986
interface TableHeader {
87+
/** The flash page size in log2 format. */
5088
pageSizeLog2: number;
89+
/** The flash page size in bytes. */
5190
pageSize: number;
91+
/** The number of regions described in the table. */
5292
regionCount: number;
93+
/** The length in bytes of the table, excluding this header. */
5394
tableLength: number;
95+
/** The Flash Regions Table format version. */
5496
version: number;
97+
/** The address of this table header (useful for calculation row offsets). */
5598
startAddress: number;
99+
/** The end address of this table header. */
56100
endAddress: number;
57101
}
58102

@@ -80,7 +124,7 @@ enum RegionHeaderOffset {
80124
regionCount = pageSizeLog2 + NUM_REG_LEN_BYTES,
81125
tableLength = regionCount + TABLE_LEN_LEN_BYTES,
82126
version = tableLength + VERSION_LEN_BYTES,
83-
magic_1 = version + MAGIC_1_LEN_BYTES,
127+
magic1 = version + MAGIC_1_LEN_BYTES,
84128
}
85129

86130
// Magic numbers to identify the Flash Regions Table in flash
@@ -114,44 +158,46 @@ enum RegionRowOffset {
114158
const REGION_ROW_LEN_BYTES = RegionRowOffset.id;
115159

116160
/**
117-
* The "hash type" field in a region row indicates how to interpret the "hash
118-
* data" field.
119-
*/
120-
enum RegionHashType {
121-
/** The hash data is empty. */
122-
empty = 0,
123-
/** The full hash data field is used as a hash of the region in flash */
124-
data = 1,
125-
/** The 4 LSB bytes of the hash data field are used as a pointer */
126-
pointer = 2,
127-
}
128-
129-
/** Indicates the data contain in each of the different regions */
130-
enum RegionId {
131-
softDevice = 1,
132-
microPython = 2,
133-
fs = 3,
134-
}
135-
136-
/**
137-
* .
161+
* Iterates through the provided Intel Hex Memory Map and tries to find the
162+
* Flash Regions Table header, by looking for the magic values at the end of
163+
* each flash page.
164+
*
165+
* TODO: Indicate here what errors can be thrown.
138166
*
139-
* @param iHexMap - .
140-
* @returns {TableHeader}
167+
* @param iHexMap - Intel Hex memory map to scan for the Flash Regions Table.
168+
* @param pSize - Flash page size to scan at the end of each page.
169+
* @returns The table header data.
141170
*/
142-
function getTableHeader(iHexMap: MemoryMap): TableHeader {
171+
function getTableHeader(iHexMap: MemoryMap, pSize: number = 1024): TableHeader {
143172
let endAddress = 0;
144-
for (let i = 4096; i <= 0x80000; i += 4096) {
173+
const magic1ToFind = new Uint8Array(
174+
new Uint32Array([REGION_HEADER_MAGIC_1]).buffer
175+
);
176+
const magic2ToFind = new Uint8Array(
177+
new Uint32Array([REGION_HEADER_MAGIC_2]).buffer
178+
);
179+
const mapEntries = iHexMap.paginate(pSize, 0xff).entries();
180+
for (let iter = mapEntries.next(); !iter.done; iter = mapEntries.next()) {
181+
if (!iter.value) continue;
182+
const blockByteArray: Uint8Array = iter.value[1];
183+
const subArrayMagic2 = blockByteArray.subarray(-RegionHeaderOffset.magic2);
145184
if (
146-
iHexMap.getUint32(i - RegionHeaderOffset.magic2, true) ===
147-
REGION_HEADER_MAGIC_2 &&
148-
iHexMap.getUint32(i - RegionHeaderOffset.magic_1, true) ===
149-
REGION_HEADER_MAGIC_1
185+
areUint8ArraysEqual(subArrayMagic2, magic2ToFind) &&
186+
areUint8ArraysEqual(
187+
blockByteArray.subarray(
188+
-RegionHeaderOffset.magic1,
189+
-(RegionHeaderOffset.magic1 - MAGIC_1_LEN_BYTES)
190+
),
191+
magic1ToFind
192+
)
150193
) {
151-
endAddress = i;
194+
const pageStartAddress: number = iter.value[0];
195+
endAddress = pageStartAddress + pSize;
152196
break;
153197
}
154198
}
199+
// TODO: Throw an error if table is not found.
200+
155201
const version = hexMapUtil.getUint16(
156202
iHexMap,
157203
endAddress - RegionHeaderOffset.version
@@ -169,7 +215,7 @@ function getTableHeader(iHexMap: MemoryMap): TableHeader {
169215
endAddress - RegionHeaderOffset.pageSizeLog2
170216
);
171217
const pageSize = Math.pow(2, pageSizeLog2);
172-
const startAddress = endAddress - RegionHeaderOffset.magic_1;
218+
const startAddress = endAddress - RegionHeaderOffset.magic1;
173219

174220
return {
175221
pageSizeLog2,
@@ -183,10 +229,15 @@ function getTableHeader(iHexMap: MemoryMap): TableHeader {
183229
}
184230

185231
/**
186-
* .
232+
* Parses a Region rows from a Flash Regions Table inside the Intel Hex memory
233+
* map, which ends at the provided rowEndAddress.
234+
*
235+
* Since the Flash Regions Table is placed at the end of a page, we iterate
236+
* from the end to the beginning.
187237
*
188-
* @param iHexMap - .
189-
* @param rowEndAddress - .
238+
* @param iHexMap - Intel Hex memory map to scan for the Flash Regions Table.
239+
* @param rowEndAddress - Address at which the row ends (same as the address
240+
* where the next row or table header starts).
190241
* @returns The Region info from the row.
191242
*/
192243
function getRegionRow(iHexMap: MemoryMap, rowEndAddress: number): RegionRow {
@@ -224,15 +275,19 @@ function getRegionRow(iHexMap: MemoryMap, rowEndAddress: number): RegionRow {
224275
}
225276

226277
/**
227-
* Reads the UICR data from an Intel Hex map and retrieves the MicroPython data.
278+
* Reads the Flash Regions Table data from an Intel Hex map and retrieves the
279+
* MicroPython DeviceMemInfo data.
228280
*
229281
* @throws {Error} When the Magic Header is not present.
282+
* @throws {Error} When the MicroPython or FS regions are not found.
230283
*
231-
* @param intelHexMap - Memory map of the Intel Hex data.
232-
* @returns Object with the decoded UICR MicroPython data.
284+
* @param intelHexMap - Memory map of the Intel Hex to scan.
285+
* @returns Object with the parsed data from the Flash Regions Table.
233286
*/
234-
function getHexMapUicrData(iHexMap: MemoryMap): DeviceMemInfo {
235-
const tableHeader = getTableHeader(iHexMap);
287+
function getHexMapFlashRegionsData(iHexMap: MemoryMap): DeviceMemInfo {
288+
// TODO: There is currently have some "internal" knowledge here and it's
289+
// scanning the flash knowing the page size is 4 KBs
290+
const tableHeader = getTableHeader(iHexMap, 4096);
236291
const regionRows: { [id: string]: RegionRow } = {};
237292
for (let i = 0; i < tableHeader.regionCount; i++) {
238293
const rowEndAddress = tableHeader.startAddress - i * REGION_ROW_LEN_BYTES;
@@ -279,16 +334,17 @@ function getHexMapUicrData(iHexMap: MemoryMap): DeviceMemInfo {
279334
}
280335

281336
/**
282-
* Reads the UICR data from an Intel Hex string and retrieves the MicroPython
283-
* data.
337+
* Reads the Flash Regions Table data from an Intel Hex map and retrieves the
338+
* MicroPython DeviceMemInfo data.
284339
*
285340
* @throws {Error} When the Magic Header is not present.
341+
* @throws {Error} When the MicroPython or FS regions are not found.
286342
*
287343
* @param intelHex - MicroPython Intel Hex string.
288-
* @returns .
344+
* @returns Object with the parsed data from the Flash Regions Table.
289345
*/
290-
function getIntelHexUicrData(intelHex: string): DeviceMemInfo {
291-
return getHexMapUicrData(MemoryMap.fromHex(intelHex));
346+
function getIntelHexFlashRegionsData(intelHex: string): DeviceMemInfo {
347+
return getHexMapFlashRegionsData(MemoryMap.fromHex(intelHex));
292348
}
293349

294-
export { getHexMapUicrData, getIntelHexUicrData };
350+
export { getHexMapFlashRegionsData, getIntelHexFlashRegionsData };

src/hex-mem-info.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ function getHexMapDeviceMemInfo(intelHexMap: MemoryMap): DeviceMemInfo {
1818
errorMsg += err.message + '\n';
1919
}
2020
try {
21-
return flashRegions.getHexMapUicrData(intelHexMap);
21+
return flashRegions.getHexMapFlashRegionsData(intelHexMap);
2222
} catch (err) {
2323
throw new Error(errorMsg + err.message);
2424
}

0 commit comments

Comments
 (0)