feat: add bootloader DFU protocol validation
This commit is contained in:
@ -75,7 +75,7 @@ class FirmwareFileSelectionService {
|
||||
final FirmwareFilePicker _filePicker;
|
||||
final SessionIdGenerator _sessionIdGenerator;
|
||||
|
||||
Future<FirmwareFileSelectionResult> selectAndPrepareDfuV1() async {
|
||||
Future<FirmwareFileSelectionResult> selectAndPrepareBootloaderDfu() async {
|
||||
final FirmwarePickerSelection? selection;
|
||||
try {
|
||||
selection = await _filePicker.pickFirmwareFile();
|
||||
@ -127,15 +127,30 @@ class FirmwareFileSelectionService {
|
||||
);
|
||||
}
|
||||
|
||||
final metadata = DfuV1FirmwareMetadata(
|
||||
final imageValidationFailure = _validateBootloaderImage(
|
||||
selection.fileBytes,
|
||||
fileName,
|
||||
);
|
||||
if (imageValidationFailure != null) {
|
||||
return FirmwareFileSelectionResult.failed(imageValidationFailure);
|
||||
}
|
||||
|
||||
final vectorStackPointer = _readLeU32(selection.fileBytes, 0);
|
||||
final vectorReset = _readLeU32(selection.fileBytes, 4);
|
||||
|
||||
final metadata = BootloaderDfuFirmwareMetadata(
|
||||
totalLength: selection.fileBytes.length,
|
||||
crc32: DfuProtocol.crc32(selection.fileBytes),
|
||||
crc32: BootloaderDfuProtocol.crc32(selection.fileBytes),
|
||||
appStart: universalShifterDfuAppStart,
|
||||
imageVersion: 0,
|
||||
sessionId: _sessionIdGenerator() & 0xFF,
|
||||
flags: universalShifterDfuFlagNone,
|
||||
vectorStackPointer: vectorStackPointer,
|
||||
vectorReset: vectorReset,
|
||||
);
|
||||
|
||||
return FirmwareFileSelectionResult.success(
|
||||
DfuV1PreparedFirmware(
|
||||
BootloaderDfuPreparedFirmware(
|
||||
fileName: fileName,
|
||||
filePath: selection.filePath,
|
||||
fileBytes: selection.fileBytes,
|
||||
@ -148,6 +163,58 @@ class FirmwareFileSelectionService {
|
||||
return fileName.toLowerCase().endsWith('.bin');
|
||||
}
|
||||
|
||||
FirmwareSelectionFailure? _validateBootloaderImage(
|
||||
Uint8List imageBytes,
|
||||
String fileName,
|
||||
) {
|
||||
if (imageBytes.length < universalShifterDfuMinimumImageLengthBytes) {
|
||||
return FirmwareSelectionFailure(
|
||||
reason: FirmwareSelectionFailureReason.imageTooSmall,
|
||||
message:
|
||||
'Selected firmware file "$fileName" is too small for a bootloader application image. Need at least $universalShifterDfuMinimumImageLengthBytes bytes.',
|
||||
);
|
||||
}
|
||||
|
||||
if (imageBytes.length > universalShifterDfuAppSlotSizeBytes) {
|
||||
return FirmwareSelectionFailure(
|
||||
reason: FirmwareSelectionFailureReason.imageTooLarge,
|
||||
message:
|
||||
'Selected firmware file "$fileName" is ${imageBytes.length} bytes, which exceeds the $universalShifterDfuAppSlotSizeBytes byte application slot.',
|
||||
);
|
||||
}
|
||||
|
||||
final vectorStackPointer = _readLeU32(imageBytes, 0);
|
||||
final vectorReset = _readLeU32(imageBytes, 4);
|
||||
final resetAddress = vectorReset & ~0x1;
|
||||
final imageEnd = universalShifterDfuAppStart + imageBytes.length;
|
||||
|
||||
if (vectorStackPointer < universalShifterDfuRamStart ||
|
||||
vectorStackPointer > universalShifterDfuRamEnd ||
|
||||
(vectorStackPointer & 0x3) != 0) {
|
||||
return FirmwareSelectionFailure(
|
||||
reason: FirmwareSelectionFailureReason.invalidVectorTable,
|
||||
message:
|
||||
'Selected firmware file "$fileName" has an invalid initial stack pointer (0x${vectorStackPointer.toRadixString(16).padLeft(8, '0').toUpperCase()}).',
|
||||
);
|
||||
}
|
||||
|
||||
if ((vectorReset & 0x1) == 0 ||
|
||||
resetAddress < universalShifterDfuAppStart + 8 ||
|
||||
resetAddress >= imageEnd) {
|
||||
return FirmwareSelectionFailure(
|
||||
reason: FirmwareSelectionFailureReason.invalidVectorTable,
|
||||
message:
|
||||
'Selected firmware file "$fileName" has an invalid reset vector (0x${vectorReset.toRadixString(16).padLeft(8, '0').toUpperCase()}). Ensure the image starts at application address 0x${universalShifterDfuAppStart.toRadixString(16).padLeft(8, '0').toUpperCase()}.',
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
int _readLeU32(Uint8List bytes, int offset) {
|
||||
return ByteData.sublistView(bytes).getUint32(offset, Endian.little);
|
||||
}
|
||||
|
||||
static int _randomSessionId() {
|
||||
return Random.secure().nextInt(256);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user