mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
627 lines
20 KiB
Python
Executable File
627 lines
20 KiB
Python
Executable File
import sys
|
||
import os
|
||
import glob
|
||
from pathlib import Path
|
||
import json
|
||
from json.decoder import JSONDecodeError
|
||
from utils import cert_sb_content_util
|
||
import utils.toc_common
|
||
from printInfo import *
|
||
|
||
EXIT_WITH_ERROR = 1
|
||
|
||
certPath = Path("cert/")
|
||
imagePath = Path("build/images/")
|
||
|
||
|
||
# we need to pad 12 bytes to put the image into a 16-byte aligned address
|
||
CERT_CHAIN_SIZE = (840 * 2) + 868 + 12 # 2560 bytes (0xA00)
|
||
# we pad out to have 16-byte aligned sizes = 880 total size
|
||
CONT_CERT_SIZE = 868 + 12
|
||
# we need to pad 12 bytes to put the image into a 16-byte aligned address
|
||
CERT_CHAIN_SIZE = (840 * 2) + 868 + 12
|
||
|
||
def getImageSize(file):
|
||
"""
|
||
find the image size
|
||
"""
|
||
|
||
try:
|
||
fsize = os.path.getsize(file)
|
||
|
||
except FileNotFoundError:
|
||
print("ERROR: The file " + file + " was not found in build/ directory!")
|
||
print("Please check the file and try again")
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
except:
|
||
print(sys.exc_info()[0])
|
||
print("ERROR: getting the image size for file " + file)
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
#print("OS file size: " + str(fsize))
|
||
|
||
padlen = 0
|
||
|
||
if fsize % 16:
|
||
# If we have to pad out
|
||
newsize = fsize + (16 - (fsize % 16))
|
||
#print("Size should be " + str(newsize))
|
||
padlen = (16 - (fsize % 16))
|
||
|
||
return fsize, padlen
|
||
|
||
|
||
def validateHexAddress(address, param, image):
|
||
"""
|
||
check address
|
||
"""
|
||
if (address[0:2].lower() != '0x'):
|
||
print(param + ' for image ' + image + ' should in in hex!')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
try:
|
||
val = int(address, 0)
|
||
|
||
except ValueError:
|
||
print('Invalid address in ' + param + ' for image ' + image)
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
|
||
|
||
|
||
def globalToLocalAddress(globalAddress):
|
||
"""
|
||
convert addresses
|
||
"""
|
||
# Global to Local Addresses Convertion is based on this table
|
||
#Rgn SE Address Region (CM0+) RegSize Global add Region Coverage
|
||
# EXAMPLE FOR REV_A0 DEVICES
|
||
#1 0x6000_0000 - 0x8000_0000 512MB 0x0000_0000 - 0x2000_0000 Boot register, CVM, XNVM, Host Peripherals, Power, Firewall, MHU
|
||
#2 0x8000_0000 - 0x8200_0000 32MB 0x4800_0000 - 0x4A00_0000 EXPMST0 Shared Peripherals
|
||
#3 0x8800_0000 - 0x8900_0000 16MB 0x5000_0000 - 0x5100_0000 EXTSYS0 HP-CM55 TCM
|
||
#4 0x9000_0000 - 0x9800_0000 128MB 0x6000_0000 - 0x6800_0000 EXTSYS1 TCMs
|
||
#5 0x9800_0000 - 0x9C00_0000 64MB 0x7000_0000 - 0x7400_0000 Low Power Peripherals (VBAT at 0x7000 and LPAON at 0x7100)
|
||
#6 0xA000_0000 - 0xA800_0000 128MB 0x8000_0000 - 0x8800-0000 OCVM
|
||
#7 0xC000_0000 – 0xE000_0000 512MB 0xD000_0000 – 0xF000_0000 OctalSPI0 and OctalSPI1
|
||
#
|
||
#The upper region addresses are exclusive for the purpose of readability
|
||
#(e.g. instead of 0x7FFF_FFFF the upper address is listed as 0x8000_0000)
|
||
|
||
# Steps for the conversion"
|
||
# 1. Check if provided global address is in one of the above regions
|
||
# 2. if in region, apply the corresponding Delta to convert to a Local address
|
||
# 3. If NOT in region, we exit with error and terminate the program
|
||
|
||
#Rgn Delta
|
||
#1 + 0x6000-0000
|
||
#2 + 0x3800-0000
|
||
#3 + 0x3800-0000
|
||
#4 + 0x3000-0000
|
||
#5 + 0x2800-0000
|
||
#6 + 0x2000-0000
|
||
#7 - 0x1000-0000
|
||
|
||
|
||
# Global addresses definition for REV_Ax devices
|
||
regions_Ax = [
|
||
['0x00000000','0x20000000'],
|
||
['0x48000000','0x4A000000'],
|
||
['0x50000000','0x51000000'],
|
||
['0x60000000','0x68000000'],
|
||
['0x70000000','0x74000000'],
|
||
['0x80000000','0x88000000'],
|
||
['0xD0000000','0xF0000000']
|
||
]
|
||
|
||
deltas_Ax = [
|
||
['+','0x60000000'],
|
||
['+','0x38000000'],
|
||
['+','0x38000000'],
|
||
['+','0x30000000'],
|
||
['+','0x28000000'],
|
||
['+','0x20000000'],
|
||
['-','0x10000000']
|
||
]
|
||
|
||
# Global addresses definition for REV_B0 devices
|
||
regions_B0 = [
|
||
['0x02000000','0x02400000'], # SRAM0_BASE
|
||
['0x08000000','0x08280000'], # SRAM1_BASE
|
||
['0x50000000','0x50040000'], # M55_HP_ITCM_BASE
|
||
['0x50800000','0x50900000'], # M55_HP_DTCM_BASE
|
||
['0x58000000','0x58040000'], # M55_HE_ITCM_BASE
|
||
['0x58800000','0x58840000'], # M55_HE_DTCM_BASE
|
||
['0x62000000','0x62100000'], # MODEM_ITCM_BASE
|
||
['0x62100000','0x62200000'], # MODEM_DTCM_BASE
|
||
['0x63000000','0x63080000'], # DSP_ITCM_BASE
|
||
['0x63100000','0x63300000'], # DSP_DTCM_BASE
|
||
['0x60000000','0x60040000'], # GNSS_ITCM_BASE
|
||
['0x60040000','0x600C0000'], # GNSS_DTCM_BASE
|
||
['0x80000000','0x80600000'] # MRAM_BASE
|
||
]
|
||
|
||
deltas_B0 = [
|
||
['+','0x60000000'],
|
||
['+','0x60000000'],
|
||
['+','0x44000000'],
|
||
['+','0x44000000'],
|
||
['+','0x40000000'],
|
||
['+','0x40000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x10000000']
|
||
]
|
||
|
||
# Global addresses definition for Spark devices
|
||
regions_Spark = [
|
||
['0x00000000','0x20000000'],
|
||
['0x40000000','0x50000000'],
|
||
['0x50000000','0x501E0000'],
|
||
['0x58000000','0x5C000000'],
|
||
['0x60000000','0x64000000'],
|
||
['0x80000000','0x84000000'],
|
||
['0xA0000000','0xE0000000']
|
||
]
|
||
|
||
deltas_Spark = [
|
||
['+','0x60000000'],
|
||
['+','0x40000000'],
|
||
['+','0x44000000'],
|
||
['+','0x40000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x10000000'],
|
||
['+','0x00000000']
|
||
]
|
||
|
||
# Global addresses definition for REV_B0 devices
|
||
regions_Eagle = [
|
||
['0x02000000','0x02400000'], # SRAM0_BASE
|
||
['0x08000000','0x08280000'], # SRAM1_BASE
|
||
['0x50000000','0x50040000'], # M55_HP_ITCM_BASE
|
||
['0x50800000','0x50900000'], # M55_HP_DTCM_BASE
|
||
['0x58000000','0x58040000'], # M55_HE_ITCM_BASE
|
||
['0x58800000','0x58840000'], # M55_HE_DTCM_BASE
|
||
['0x62000000','0x62100000'], # MODEM_ITCM_BASE
|
||
['0x62100000','0x62200000'], # MODEM_DTCM_BASE
|
||
['0x63000000','0x63080000'], # DSP_ITCM_BASE
|
||
['0x63100000','0x63300000'], # DSP_DTCM_BASE
|
||
['0x60000000','0x60040000'], # GNSS_ITCM_BASE
|
||
['0x60040000','0x600C0000'], # GNSS_DTCM_BASE
|
||
['0x80000000','0x80600000'] # MRAM_BASE
|
||
]
|
||
|
||
deltas_Eagle = [
|
||
['+','0x60000000'],
|
||
['+','0x60000000'],
|
||
['+','0x44000000'],
|
||
['+','0x44000000'],
|
||
['+','0x40000000'],
|
||
['+','0x40000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x3C000000'],
|
||
['+','0x10000000']
|
||
]
|
||
|
||
# create a list of valid global regions
|
||
#print(utils.config.DEVICE_REVISION)
|
||
if utils.config.DEVICE_FEATURE_SET == 'Fusion':
|
||
if utils.config.DEVICE_REVISION == 'A1':
|
||
regions = regions_Ax
|
||
deltas = deltas_Ax
|
||
else:
|
||
regions = regions_B0
|
||
deltas = deltas_B0
|
||
|
||
elif utils.config.DEVICE_FEATURE_SET == 'Spark':
|
||
regions = regions_Spark
|
||
deltas = deltas_Spark
|
||
elif utils.config.DEVICE_FEATURE_SET == 'Eagle':
|
||
regions = regions_Eagle
|
||
deltas = deltas_Eagle
|
||
else:
|
||
print('ERROR: Invalid Device type!')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
|
||
#print(globalAddress)
|
||
localAddress = int(globalAddress, 16)
|
||
notFound = True
|
||
idx = 0
|
||
for address in regions:
|
||
LOWER = int(address[0], 16)
|
||
UPPER = int(address[1], 16)
|
||
if localAddress >= LOWER and localAddress < UPPER:
|
||
#print('Address inside boundaries - region ' + str(idx))
|
||
notFound = False
|
||
break
|
||
idx += 1
|
||
|
||
if notFound:
|
||
print('ERROR: Invalid Global Address. Please check!')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
delta = int(deltas[idx][1], 16)
|
||
if deltas[idx][0] == '+':
|
||
localAddress += delta
|
||
elif deltas[idx][0] == '-':
|
||
localAddress -= delta
|
||
else:
|
||
print('ERROR in determining the right operation in global address conversion!')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
#print(hex(localAddress))
|
||
return hex(localAddress)
|
||
|
||
def initCfgOptions(keys):
|
||
"""
|
||
CFG options
|
||
"""
|
||
options = {}
|
||
|
||
for i in keys:
|
||
options[i] = 'none'
|
||
|
||
return options
|
||
|
||
def dict_raise_on_duplicates(ordered_pairs):
|
||
"""
|
||
Reject duplicate keys.
|
||
"""
|
||
d = {}
|
||
for k, v in ordered_pairs:
|
||
if k in d:
|
||
raise ValueError("Duplicate key: %r" % (k,))
|
||
else:
|
||
d[k] = v
|
||
return d
|
||
|
||
def read_json_file(file, attributes, flags, cpus):
|
||
"""
|
||
read the json file
|
||
"""
|
||
global numImages
|
||
|
||
try:
|
||
f = open(file, 'r')
|
||
|
||
except FileNotFoundError:
|
||
print('ERROR: file ' + file + ' does not exist!')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
except:
|
||
print('ERROR opening configuration file')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
|
||
try:
|
||
cfg = json.load(f, object_pairs_hook=dict_raise_on_duplicates)
|
||
|
||
except JSONDecodeError as e:
|
||
print("ERROR in JSON file.")
|
||
print(str(e))
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
except ValueError as v:
|
||
print("ERROR in JSON file:")
|
||
print(str(v))
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
except:
|
||
print("ERROR: Unknown error loading JSON file")
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
f.close()
|
||
|
||
images = []
|
||
for image in cfg:
|
||
#print('IMAGE:')
|
||
#print(image, cfg[image])
|
||
if len(image) == 0:
|
||
print("ERROR: image description can't be empty!","Please correct it in configuration file")
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
# ensure the image descriptor is 8 characters (complete with '\0' otherwise)
|
||
n = 8 - len(image)
|
||
imgDesc = image.ljust(n + len(image), '\0')
|
||
imgOptions = initCfgOptions(attributes)
|
||
userOptions = cfg[image]
|
||
imgOptions['identifier'] = imgDesc[:8]
|
||
#print('Analyze options')
|
||
for opt in userOptions:
|
||
#print(opt, userOptions[opt])
|
||
if imgOptions.get(opt) is None:
|
||
print('ERROR: Invalid key: "' + opt + \
|
||
'" in configuration file, image: ' + image)
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
else:
|
||
if isinstance(userOptions[opt], list):
|
||
for s in userOptions[opt]:
|
||
if s.upper() not in flags:
|
||
print('ERROR: flag "' + s + '" in image '
|
||
+ image + ' is not supported')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
imgOptions[opt] = [s.upper() for s in userOptions[opt]]
|
||
else:
|
||
if opt in ['loadAddress','mramAddress']:
|
||
validateHexAddress(userOptions[opt], opt, image)
|
||
if opt == 'loadAddress':
|
||
if userOptions[opt][0:3] == '0x8':
|
||
print('ERROR: An MRAM address can not be used in loadAddress attribute')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
if opt == 'cpu_id':
|
||
if userOptions[opt] not in cpus:
|
||
print('ERROR: cpu_id "' + userOptions[opt] + '" in image ' + image + ' is not supported')
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
imgOptions[opt] = userOptions[opt]
|
||
|
||
images.append(imgOptions)
|
||
|
||
return images
|
||
|
||
def compressImage(file):
|
||
"""
|
||
compression
|
||
"""
|
||
print('Compressing image: ' + file)
|
||
if sys.platform == "linux":
|
||
os.system('chmod +x ./utils/lzf-lnx')
|
||
os.system('./utils/lzf-lnx -cfb 4096 ' + file)
|
||
else:
|
||
os.system('utils\\lzf.exe -cfb 4096 ' + file)
|
||
|
||
def addContentCertificate(file, binary):
|
||
"""
|
||
addContentCertificate
|
||
"""
|
||
certFile = "SB" + binary + ".crt"
|
||
certFile = certPath / certFile
|
||
#print("Copying Cont.Cert: " + str(certFile))
|
||
file.write(getBlob(certFile))
|
||
# pad 12 bytes to put the image in a 16-byte aligned address
|
||
file.write(b'\x00' * 12)
|
||
|
||
|
||
def createContentCerts(fwsections, prefix):
|
||
"""
|
||
create content certificates
|
||
"""
|
||
print('Creating Content Certificates...')
|
||
for sec in fwsections:
|
||
|
||
if sec['disabled'] is True:
|
||
continue
|
||
|
||
#print(sec)
|
||
#print(imagePath)
|
||
|
||
try:
|
||
file = open(imagePath / "images.txt", "w")
|
||
except:
|
||
print(sys.exc_info()[0])
|
||
print("[ERROR] creating template file for Content Certificates")
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
|
||
# create the ContentCertificate
|
||
#
|
||
# swLoadVerifyScheme values in Content Certificates:
|
||
#
|
||
# SERAMX : 0 - load and verify
|
||
# XIP (verify in flash) : 1 - (Encryption Not Allowed)
|
||
# IMAGE : 2 - verify in memory (LOAD flag set) (Encryption is allowed and optional)
|
||
#
|
||
|
||
encrypt = '0'
|
||
# Content Certificate for SERAM needs to have Local Addresses
|
||
sec['mramAddress'] = globalToLocalAddress(sec['mramAddress'])
|
||
if sec['identifier'].upper()[:5] == 'SERAM':
|
||
configFile = prefix + 'SBContent_lvs_0.cfg'
|
||
mem_load_address = sec['loadAddress']
|
||
# Encryption allowed if load-verify-scheme = 0
|
||
if 'ENCRYPT' in sec['flags']:
|
||
encrypt = '1'
|
||
|
||
else:
|
||
if 'LOAD' in sec['flags']:
|
||
# use
|
||
# load-verify-scheme = 2
|
||
# aes-ce-id = 2
|
||
configFile = prefix + 'SBContent_lvs_2.cfg'
|
||
mem_load_address = globalToLocalAddress(sec['loadAddress'])
|
||
# Encryption ONLY allowed if load-verify-scheme = 2 (LOAD flag set)
|
||
if 'ENCRYPT' in sec['flags']:
|
||
encrypt = '1'
|
||
else:
|
||
# use
|
||
# load-verify-scheme = 1
|
||
# aes-ce-id = 0 (no encryption allowed if verify in flash)
|
||
configFile = prefix + 'SBContent_lvs_1.cfg'
|
||
mem_load_address = '0xFFFFFFFF'
|
||
|
||
fsize = sec['size']
|
||
if 'COMPRESS' in sec['flags']:
|
||
fsize = sec['uncompressedSize']
|
||
|
||
printInfo('Content Certificate1:')
|
||
#print("../build/" + sec['binary'] + " " + str(mem_load_address) + " " + \
|
||
# str(sec['mramAddress']) + " " + str(fsize) + " " + encrypt)
|
||
file.write("../build/images/" + sec['binary'] + \
|
||
" " + str(mem_load_address) + \
|
||
" " + str(sec['mramAddress']) + \
|
||
" " + str(fsize) + \
|
||
" " + encrypt)
|
||
file.close()
|
||
|
||
os.chdir(os.getcwd() + '/utils/')
|
||
cert_sb_content_util.main(["-c", 'cfg/'+configFile, "-l", '../build/logs/SBContent.log'])
|
||
os.chdir(os.getcwd() + '/../')
|
||
|
||
# check if file exists (and remove it) before renaming it
|
||
#certFile = "SB" + sec['binary'] + ".crt"
|
||
certFile = "SB" + sec['binary'] + "_" + str(sec['mramAddress']) + ".crt"
|
||
printInfo(certFile)
|
||
if (os.path.exists(certPath / certFile)):
|
||
os.remove(certPath / certFile)
|
||
|
||
fileName = prefix + "SBContent.crt"
|
||
os.rename(certPath / fileName, certPath / certFile)
|
||
# save cert. name in images list
|
||
sec['cert'] = certFile
|
||
|
||
# remove ICVSBContent_Cert.txt file (not used)
|
||
textFile = prefix + "SBContent_Cert.txt"
|
||
if (os.path.exists(certPath / textFile)):
|
||
os.remove(certPath / textFile)
|
||
|
||
# remove images.txt - temporal template file (not used)
|
||
textFile = "images.txt"
|
||
if (os.path.exists(imagePath / textFile)):
|
||
os.remove(imagePath / textFile)
|
||
|
||
|
||
def getObjectType(objType, switcher):
|
||
"""
|
||
get object type
|
||
"""
|
||
return switcher.get(objType.strip(), 4)
|
||
|
||
def getCPUID(objCPU, switcher):
|
||
"""
|
||
fet CPU ID
|
||
"""
|
||
|
||
return switcher.get(objCPU, 0xF)
|
||
|
||
|
||
def getObjectFlags(objFlags):
|
||
"""
|
||
getObjectFlags
|
||
"""
|
||
flagInt = 0
|
||
for f in objFlags:
|
||
if f == 'COMPRESS':
|
||
flagInt += 0x10
|
||
elif f == 'LOAD':
|
||
flagInt += 0x20
|
||
elif f == 'BOOT':
|
||
flagInt += 0x40
|
||
elif f == 'ENCRYPT':
|
||
flagInt += 0x80
|
||
elif f == 'DEFERRED':
|
||
flagInt += 0x100
|
||
|
||
return flagInt
|
||
|
||
def getObjectVersion(objVersion):
|
||
"""
|
||
"""
|
||
if objVersion == 'none':
|
||
return 0
|
||
|
||
ver = objVersion.split('.')
|
||
res = 0
|
||
idx = 3
|
||
for n in ver:
|
||
if idx < 1:
|
||
break
|
||
res += (int(n) * (256 ** idx))
|
||
idx -= 1
|
||
return res
|
||
|
||
def getBlob(file):
|
||
"""
|
||
get the Blob
|
||
"""
|
||
f = open(file, 'rb') # TODO: This needs to check for open fail
|
||
blob = f.read()
|
||
f.close()
|
||
return blob
|
||
|
||
def scanFiles(fileList, action):
|
||
"""
|
||
scanFiles
|
||
"""
|
||
filesProcesed = 0
|
||
for i in range(len(fileList)):
|
||
files = glob.glob(fileList[i])
|
||
for file in files:
|
||
if action == 'list':
|
||
if i == 0:
|
||
print('\nThe following files will be deleted:\n')
|
||
print('\t' + file)
|
||
elif action == 'delete':
|
||
os.remove(file)
|
||
filesProcesed += 1
|
||
|
||
return filesProcesed
|
||
|
||
def cleanBuild(fileList):
|
||
"""
|
||
cleanBuild
|
||
"""
|
||
|
||
files = scanFiles(fileList, 'list')
|
||
|
||
if files == 0:
|
||
print('\nThere were no files to delete')
|
||
sys.exit()
|
||
|
||
print('\nPress Y to confirm, or any other key to exit')
|
||
confirmResponse = input('> ')
|
||
if confirmResponse.upper() == 'Y' or confirmResponse.upper() == 'YES':
|
||
print('Deleting files...')
|
||
scanFiles(fileList, 'delete')
|
||
print('\nThe build workspace was cleaned!', '(' + str(files) + ' files were deleted)')
|
||
else:
|
||
print('The operation was aborted!')
|
||
|
||
def validateOptions(fwsections):
|
||
# validate options in fwsections - exit if errors
|
||
for sec in fwsections:
|
||
if 'COMPRESS' in sec['flags']:
|
||
# enforce compression is not allowed encryption is used
|
||
if 'ENCRYPT' in sec['flags']:
|
||
print('[ERROR] Compression is not allowed for encrypted images!')
|
||
print('[ERROR] Please correct the configuration: '
|
||
+ str(sec['flags'])
|
||
+ ' in image '
|
||
+ sec['identifier'])
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
# enforce compression is not allowed for XIP use case
|
||
if 'LOAD' not in sec['flags'] and sec['cpu_id'] != 'none':
|
||
print('[ERROR] Compression is not allowed for XIP images!')
|
||
print('[ERROR] Please correct the configuration: '
|
||
+ str(sec['flags'])
|
||
+ ' in image '
|
||
+ sec['identifier'])
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
# if LOAD flag is used, loadAddress should be valid
|
||
if 'LOAD' in sec['flags']:
|
||
if sec['loadAddress'] == 'none':
|
||
print('[ERROR] if LOAD flag is used, a loadAddress must be specified!')
|
||
print('[ERROR] Please correct the configuration in image '
|
||
+ sec['identifier'])
|
||
sys.exit(EXIT_WITH_ERROR)
|
||
|
||
# if BOOT flag is used, CPU_ID should be valid
|
||
if 'BOOT' in sec['flags']:
|
||
if sec['cpu_id'] == 'none':
|
||
print('[ERROR] if BOOT flag is used, a cpu_id must be specified!')
|
||
print('[ERROR] Please correct the configuration in image '
|
||
+ sec['identifier'])
|
||
sys.exit(EXIT_WITH_ERROR)
|