trogdor: support mbn_version 6 with python build scripts

Developer/Reviewer, be aware of this patch from Mistral:
 https://review.coreboot.org/c/coreboot/+/33425/18

Change-Id: I020d1e4d4f5c948948e1b39dd18af1d0e860c279
Signed-off-by: T Michael Turney <mturney@codeaurora.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/35506
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
This commit is contained in:
T Michael Turney
2019-08-07 14:26:32 -07:00
committed by Julius Werner
parent 01bfa53f77
commit bcd62f5737
3 changed files with 112 additions and 70 deletions

View File

@@ -44,11 +44,13 @@
# #
# when who what, where, why # when who what, where, why
# -------- --- ------------------------------------------------------ # -------- --- ------------------------------------------------------
# 05/21/19 rissha Added --mbn_version to add MBN header accordingly
# 03/26/18 tv Added -e to enable extended MBNV5 support # 03/26/18 tv Added -e to enable extended MBNV5 support
# 09/04/15 et Added -x and -d to embed xbl_sec ELF # 09/04/15 et Added -x and -d to embed xbl_sec ELF
# 02/11/15 ck Fixed missing elf type check in ZI OOB feature # 02/11/15 ck Fixed missing elf type check in ZI OOB feature
# 11/04/14 ck Updated calls to mbn_tools functions # 11/04/14 ck Updated calls to mbn_tools functions
# 10/22/14 ck Added -z option to remove out of bounds ZI segments when converting from 64 to 32 # 10/22/14 ck Added -z option to remove out of bounds ZI segments when
# converting from 64 to 32
# 10/10/14 ck Added -c option and logic to enable elf type swapping # 10/10/14 ck Added -c option and logic to enable elf type swapping
# 09/12/14 ck Added single file logic # 09/12/14 ck Added single file logic
# 08/29/14 ck Added no_hash option # 08/29/14 ck Added no_hash option
@@ -119,6 +121,10 @@ def main():
help="Removes ZI segments that have addresses greater" + \ help="Removes ZI segments that have addresses greater" + \
" than 32 bits when converting from a 64 to 32 bit ELF") " than 32 bits when converting from a 64 to 32 bit ELF")
parser.add_option("--mbn_version",
action="store", type="int", dest="mbn_version",
help="Add mbn header in elf image. '3', '5' or '6'")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
if not options.elf_inp_file1: if not options.elf_inp_file1:
@@ -206,11 +212,13 @@ def main():
else: else:
zi_oob_enabled = True zi_oob_enabled = True
if options.elf_inp_xbl_sec: header_version = 3
is_ext_mbn_v5 = True
else:
is_ext_mbn_v5 = False
if options.elf_inp_xbl_sec:
header_version = 5
if options.mbn_version:
header_version = options.mbn_version
mbn_type = 'elf' mbn_type = 'elf'
header_format = 'reg' header_format = 'reg'
@@ -244,7 +252,7 @@ def main():
is_elf_xbl_sec_64_bit, is_elf_xbl_sec_64_bit,
is_out_elf_64_bit, is_out_elf_64_bit,
zi_oob_enabled, zi_oob_enabled,
is_ext_mbn_v5) header_version)
# Hash the image if user did not explicitly say not to # Hash the image if user did not explicitly say not to
@@ -259,7 +267,8 @@ def main():
source_elf, source_elf,
target_hash, target_hash,
elf_out_file_name = target_phdr_elf, elf_out_file_name = target_phdr_elf,
secure_type = image_header_secflag) secure_type = image_header_secflag,
header_version = header_version )
if rv: if rv:
raise RuntimeError, "Failed to run pboot_gen_elf" raise RuntimeError, "Failed to run pboot_gen_elf"
@@ -269,8 +278,8 @@ def main():
target_hash, target_hash,
target_hash_hd, target_hash_hd,
image_header_secflag, image_header_secflag,
is_ext_mbn_v5, elf_file_name = source_elf,
elf_file_name = source_elf) header_version = header_version)
if rv: if rv:
raise RuntimeError, "Failed to create image header for hash segment" raise RuntimeError, "Failed to create image header for hash segment"
@@ -305,7 +314,7 @@ def merge_elfs(env,
is_elf_xbl_sec_64_bit, is_elf_xbl_sec_64_bit,
is_out_elf_64_bit, is_out_elf_64_bit,
zi_oob_enabled, zi_oob_enabled,
is_ext_mbn_v5): header_version):
[elf_header1, phdr_table1] = \ [elf_header1, phdr_table1] = \
mbn_tools.preprocess_elf_file(elf_in_file_name1) mbn_tools.preprocess_elf_file(elf_in_file_name1)
@@ -663,12 +672,12 @@ def merge_elfs(env,
new_phdr.p_paddr = phys_virt_addr new_phdr.p_paddr = phys_virt_addr
new_phdr.p_filesz = os.path.getsize(elf_in_file_xbl_sec) new_phdr.p_filesz = os.path.getsize(elf_in_file_xbl_sec)
new_phdr.p_memsz = new_phdr.p_filesz new_phdr.p_memsz = new_phdr.p_filesz
if is_ext_mbn_v5 == True: if header_version >= 5:
new_phdr.p_flags = (0x5 | new_phdr.p_flags = (0x5 |
(mbn_tools.MI_PBT_XBL_SEC_SEGMENT << (mbn_tools.MI_PBT_XBL_SEC_SEGMENT <<
mbn_tools.MI_PBT_FLAG_SEGMENT_TYPE_SHIFT)); mbn_tools.MI_PBT_FLAG_SEGMENT_TYPE_SHIFT));
else: else:
new_phdr.p_flags = 0x5 new_phdr.p_flags = 0x5
new_phdr.p_align = 0x1000 new_phdr.p_align = 0x1000
else: else:
# Converting from 64 to 32 elf requires data size validation # Converting from 64 to 32 elf requires data size validation
@@ -677,12 +686,12 @@ def merge_elfs(env,
new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE) new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE)
new_phdr.p_type = 0x1 # new_phdr.p_type = 0x1 #
new_phdr.p_offset = segment_offset new_phdr.p_offset = segment_offset
if is_ext_mbn_v5 == True: if header_version >= 5:
new_phdr.p_flags = (0x5 | new_phdr.p_flags = (0x5 |
(mbn_tools.MI_PBT_XBL_SEC_SEGMENT << (mbn_tools.MI_PBT_XBL_SEC_SEGMENT <<
mbn_tools.MI_PBT_FLAG_SEGMENT_TYPE_SHIFT)); mbn_tools.MI_PBT_FLAG_SEGMENT_TYPE_SHIFT));
else: else:
new_phdr.p_flags = 0x5 new_phdr.p_flags = 0x5
new_phdr.p_align = 0x1000 new_phdr.p_align = 0x1000
if phys_virt_addr > 0xFFFFFFFF: if phys_virt_addr > 0xFFFFFFFF:

View File

@@ -41,6 +41,7 @@
# #
# when who what, where, why # when who what, where, why
# -------- --- --------------------------------------------------------- # -------- --- ---------------------------------------------------------
# 05/21/18 rissha Added support for extended MBNV6 and Add support for hashing elf segments with SHA384
# 03/22/18 thiru Added support for extended MBNV5. # 03/22/18 thiru Added support for extended MBNV5.
# 06/06/13 yliong CR 497042: Signed and encrypted image is corrupted. MRC features. # 06/06/13 yliong CR 497042: Signed and encrypted image is corrupted. MRC features.
# 03/18/13 dhaval Add support for hashing elf segments with SHA256 and # 03/18/13 dhaval Add support for hashing elf segments with SHA256 and
@@ -64,23 +65,21 @@ import hashlib
#---------------------------------------------------------------------------- #----------------------------------------------------------------------------
# GLOBAL VARIABLES BEGIN # GLOBAL VARIABLES BEGIN
#---------------------------------------------------------------------------- #----------------------------------------------------------------------------
PAD_BYTE_1 = 255 # Padding byte 1s PAD_BYTE_1 = 255 # Padding byte 1s
PAD_BYTE_0 = 0 # Padding byte 0s PAD_BYTE_0 = 0 # Padding byte 0s
SHA256_SIGNATURE_SIZE = 256 # Support SHA256 SHA256_SIGNATURE_SIZE = 256 # Support SHA256
MAX_NUM_ROOT_CERTS = 4 # Maximum number of OEM root certificates MAX_NUM_ROOT_CERTS = 4 # Maximum number of OEM root certificates
MI_BOOT_IMG_HDR_SIZE = 40 # sizeof(mi_boot_image_header_type) MI_BOOT_SBL_HDR_SIZE = 80 # sizeof(sbl_header)
MI_BOOT_SBL_HDR_SIZE = 80 # sizeof(sbl_header) BOOT_HEADER_LENGTH = 20 # Boot Header Number of Elements
BOOT_HEADER_LENGTH = 20 # Boot Header Number of Elements SBL_HEADER_LENGTH = 20 # SBL Header Number of Elements
SBL_HEADER_LENGTH = 20 # SBL Header Number of Elements MAX_PHDR_COUNT = 100 # Maximum allowable program headers
FLASH_PARTI_VERSION = 3 # Flash Partition Version Number CERT_CHAIN_ONEROOT_MAXSIZE = 6*1024 # Default Cert Chain Max Size for one root
MAX_PHDR_COUNT = 100 # Maximum allowable program headers VIRTUAL_BLOCK_SIZE = 131072 # Virtual block size for MCs insertion in SBL1 if ENABLE_VIRTUAL_BLK ON
CERT_CHAIN_ONEROOT_MAXSIZE = 6*1024 # Default Cert Chain Max Size for one root MAGIC_COOKIE_LENGTH = 12 # Length of magic Cookie inserted per VIRTUAL_BLOCK_SIZE
VIRTUAL_BLOCK_SIZE = 131072 # Virtual block size for MCs insertion in SBL1 if ENABLE_VIRTUAL_BLK ON MIN_IMAGE_SIZE_WITH_PAD = 256*1024 # Minimum image size for sbl1 Nand based OTA feature
MAGIC_COOKIE_LENGTH = 12 # Length of magic Cookie inserted per VIRTUAL_BLOCK_SIZE
MIN_IMAGE_SIZE_WITH_PAD = 256*1024 # Minimum image size for sbl1 Nand based OTA feature
SBL_AARCH64 = 0xF # Indicate that SBL is a Aarch64 image SBL_AARCH64 = 0xF # Indicate that SBL is a Aarch64 image
SBL_AARCH32 = 0x0 # Indicate that SBL is a Aarch32 image SBL_AARCH32 = 0x0 # Indicate that SBL is a Aarch32 image
# Magic numbers filled in for boot headers # Magic numbers filled in for boot headers
FLASH_CODE_WORD = 0x844BDCD1 FLASH_CODE_WORD = 0x844BDCD1
@@ -150,7 +149,6 @@ values.
MI_PBT_FLAGS_MASK = 0x0FF00000 MI_PBT_FLAGS_MASK = 0x0FF00000
# Helper defines to help parse ELF program headers # Helper defines to help parse ELF program headers
MI_PROG_BOOT_DIGEST_SIZE = 20
MI_PBT_FLAG_SEGMENT_TYPE_MASK = 0x07000000 MI_PBT_FLAG_SEGMENT_TYPE_MASK = 0x07000000
MI_PBT_FLAG_SEGMENT_TYPE_SHIFT = 0x18 MI_PBT_FLAG_SEGMENT_TYPE_SHIFT = 0x18
MI_PBT_FLAG_PAGE_MODE_MASK = 0x00100000 MI_PBT_FLAG_PAGE_MODE_MASK = 0x00100000
@@ -528,7 +526,7 @@ class SegmentInfo:
class Boot_Hdr: class Boot_Hdr:
def __init__(self, init_val): def __init__(self, init_val):
self.image_id = ImageType.NONE_IMG self.image_id = ImageType.NONE_IMG
self.flash_parti_ver = FLASH_PARTI_VERSION self.flash_parti_ver = 3
self.image_src = init_val self.image_src = init_val
self.image_dest_ptr = init_val self.image_dest_ptr = init_val
self.image_size = init_val self.image_size = init_val
@@ -573,6 +571,10 @@ class Boot_Hdr:
self.reserved_2, self.reserved_2,
self.reserved_3 ] self.reserved_3 ]
if self.flash_parti_ver >= 6:
values.insert(10, self.metadata_size_qti)
values.insert(11, self.metadata_size)
if self.image_dest_ptr >= 0x100000000: if self.image_dest_ptr >= 0x100000000:
values[3] = 0xFFFFFFFF values[3] = 0xFFFFFFFF
@@ -584,8 +586,12 @@ class Boot_Hdr:
# Write 10 entries(40B) or 20 entries(80B) of boot header # Write 10 entries(40B) or 20 entries(80B) of boot header
if write_full_hdr is False: if write_full_hdr is False:
s = struct.Struct('I'* 10) if self.flash_parti_ver >= 6:
values = values[:10] s = struct.Struct('I'* 12)
values = values[:12]
else:
s = struct.Struct('I'* 10)
values = values[:10]
else: else:
s = struct.Struct('I' * self.getLength()) s = struct.Struct('I' * self.getLength())
@@ -904,7 +910,6 @@ def image_header(env, gen_dict,
code_file_name, code_file_name,
output_file_name, output_file_name,
secure_type, secure_type,
is_ext_mbn_v5,
header_format = 'reg', header_format = 'reg',
requires_preamble = False, requires_preamble = False,
preamble_file_name = None, preamble_file_name = None,
@@ -912,7 +917,8 @@ def image_header(env, gen_dict,
write_full_hdr = False, write_full_hdr = False,
in_code_size = None, in_code_size = None,
cert_chain_size_in = CERT_CHAIN_ONEROOT_MAXSIZE, cert_chain_size_in = CERT_CHAIN_ONEROOT_MAXSIZE,
num_of_pages = None): num_of_pages = None,
header_version = None):
# Preliminary checks # Preliminary checks
if (requires_preamble is True) and (preamble_file_name is None): if (requires_preamble is True) and (preamble_file_name is None):
@@ -945,9 +951,12 @@ def image_header(env, gen_dict,
cert_chain_size = 0 cert_chain_size = 0
image_size = code_size image_size = code_size
if header_version:
assert header_version in [3, 5, 6], 'Not a valid MBN header version'
# For ELF or hashed images, image destination will be determined from an ELF input file # For ELF or hashed images, image destination will be determined from an ELF input file
if gen_dict['IMAGE_KEY_MBN_TYPE'] == 'elf': if gen_dict['IMAGE_KEY_MBN_TYPE'] == 'elf':
image_dest = get_hash_address(elf_file_name) + MI_BOOT_IMG_HDR_SIZE image_dest = get_hash_address(elf_file_name) + (header_size(header_version))
elif gen_dict['IMAGE_KEY_MBN_TYPE'] == 'bin': elif gen_dict['IMAGE_KEY_MBN_TYPE'] == 'bin':
image_dest = gen_dict['IMAGE_KEY_IMAGE_DEST'] image_dest = gen_dict['IMAGE_KEY_IMAGE_DEST']
image_source = gen_dict['IMAGE_KEY_IMAGE_SOURCE'] image_source = gen_dict['IMAGE_KEY_IMAGE_SOURCE']
@@ -991,12 +1000,15 @@ def image_header(env, gen_dict,
boot_header.sig_size = signature_size boot_header.sig_size = signature_size
boot_header.cert_chain_ptr = image_dest + code_size + signature_size boot_header.cert_chain_ptr = image_dest + code_size + signature_size
boot_header.cert_chain_size = cert_chain_size boot_header.cert_chain_size = cert_chain_size
boot_header.flash_parti_ver = header_version # version
if is_ext_mbn_v5 == True: if header_version >= 5:
# If platform image integrity check is enabled boot_header.image_src = 0 # sig_size_qc
boot_header.flash_parti_ver = 5 # version boot_header.image_dest_ptr = 0 # cert_chain_size_qc
boot_header.image_src = 0 # sig_size_qc
boot_header.image_dest_ptr = 0 # cert_chain_size_qc if header_version >= 6:
boot_header.metadata_size_qti = 0 # qti_metadata size
boot_header.metadata_size = 0 # oem_metadata size
# If preamble is required, output the preamble file and update the boot_header # If preamble is required, output the preamble file and update the boot_header
if requires_preamble is True: if requires_preamble is True:
@@ -1021,12 +1033,22 @@ def pboot_gen_elf(env, elf_in_file_name,
last_phys_addr = None, last_phys_addr = None,
append_xml_hdr = False, append_xml_hdr = False,
is_sha256_algo = True, is_sha256_algo = True,
cert_chain_size_in = CERT_CHAIN_ONEROOT_MAXSIZE): cert_chain_size_in = CERT_CHAIN_ONEROOT_MAXSIZE,
global MI_PROG_BOOT_DIGEST_SIZE header_version = None):
if (is_sha256_algo is True): sha_algo = 'SHA1'
MI_PROG_BOOT_DIGEST_SIZE = 32 if is_sha256_algo:
sha_algo = 'SHA256'
if header_version >= 6:
sha_algo = 'SHA384'
image_header_size = header_size(header_version)
if (sha_algo == 'SHA384'):
mi_prog_boot_digest_size = 48
elif sha_algo == 'SHA256':
mi_prog_boot_digest_size = 32
else: else:
MI_PROG_BOOT_DIGEST_SIZE = 20 mi_prog_boot_digest_size = 20
# Open Files # Open Files
elf_in_fp = OPEN(elf_in_file_name, "rb") elf_in_fp = OPEN(elf_in_file_name, "rb")
@@ -1052,7 +1074,7 @@ def pboot_gen_elf(env, elf_in_file_name,
elf_header_size = ELF32_HDR_SIZE elf_header_size = ELF32_HDR_SIZE
is_elf64 = False is_elf64 = False
hash = '\0' * MI_PROG_BOOT_DIGEST_SIZE hash = '\0' * mi_prog_boot_digest_size
phdr_start = 0 phdr_start = 0
bytes_to_pad = 0 bytes_to_pad = 0
hash_seg_end = 0 hash_seg_end = 0
@@ -1071,12 +1093,12 @@ def pboot_gen_elf(env, elf_in_file_name,
elf_header.e_phnum += 2 elf_header.e_phnum += 2
# Create an empty hash entry for PHDR_TYPE # Create an empty hash entry for PHDR_TYPE
hash_out_fp.write('\0' * MI_PROG_BOOT_DIGEST_SIZE) hash_out_fp.write('\0' * mi_prog_boot_digest_size)
hashtable_size += MI_PROG_BOOT_DIGEST_SIZE hashtable_size += mi_prog_boot_digest_size
# Create an empty hash entry for the hash segment itself # Create an empty hash entry for the hash segment itself
hash_out_fp.write('\0' * MI_PROG_BOOT_DIGEST_SIZE) hash_out_fp.write('\0' * mi_prog_boot_digest_size)
hashtable_size += MI_PROG_BOOT_DIGEST_SIZE hashtable_size += mi_prog_boot_digest_size
# Begin hash table generation # Begin hash table generation
for i in range(num_phdrs): for i in range(num_phdrs):
@@ -1110,14 +1132,14 @@ def pboot_gen_elf(env, elf_in_file_name,
fbuf = elf_in_fp.read(hash_size) fbuf = elf_in_fp.read(hash_size)
if MI_PBT_CHECK_FLAG_TYPE(curr_phdr.p_flags) is True: if MI_PBT_CHECK_FLAG_TYPE(curr_phdr.p_flags) is True:
hash = generate_hash(fbuf, is_sha256_algo) hash = generate_hash(fbuf, sha_algo)
else: else:
hash = '\0' * MI_PROG_BOOT_DIGEST_SIZE hash = '\0' * mi_prog_boot_digest_size
# Write hash to file # Write hash to file
hash_out_fp.write(hash) hash_out_fp.write(hash)
hashtable_size += MI_PROG_BOOT_DIGEST_SIZE hashtable_size += mi_prog_boot_digest_size
seg_offset += ELF_BLOCK_ALIGN seg_offset += ELF_BLOCK_ALIGN
# Copy the hash entry for all that are PAGED segments and those that are not the PHDR type. This is for # Copy the hash entry for all that are PAGED segments and those that are not the PHDR type. This is for
@@ -1129,14 +1151,14 @@ def pboot_gen_elf(env, elf_in_file_name,
file_buff = elf_in_fp.read(data_len) file_buff = elf_in_fp.read(data_len)
if (MI_PBT_CHECK_FLAG_TYPE(curr_phdr.p_flags) is True) and (data_len > 0): if (MI_PBT_CHECK_FLAG_TYPE(curr_phdr.p_flags) is True) and (data_len > 0):
hash = generate_hash(file_buff, is_sha256_algo) hash = generate_hash(file_buff, sha_algo)
else: else:
hash = '\0' * MI_PROG_BOOT_DIGEST_SIZE hash = '\0' * mi_prog_boot_digest_size
# Write hash to file # Write hash to file
hash_out_fp.write(hash) hash_out_fp.write(hash)
hashtable_size += MI_PROG_BOOT_DIGEST_SIZE hashtable_size += mi_prog_boot_digest_size
# End hash table generation # End hash table generation
# Generate the rest of the ELF output file if specified # Generate the rest of the ELF output file if specified
@@ -1151,7 +1173,7 @@ def pboot_gen_elf(env, elf_in_file_name,
# Initialize the hash table program header # Initialize the hash table program header
[hash_Phdr, pad_hash_segment, hash_tbl_end_addr, hash_tbl_offset] = \ [hash_Phdr, pad_hash_segment, hash_tbl_end_addr, hash_tbl_offset] = \
initialize_hash_phdr(elf_in_file_name, hashtable_size, MI_BOOT_IMG_HDR_SIZE, ELF_BLOCK_ALIGN, is_elf64) initialize_hash_phdr(elf_in_file_name, hashtable_size, image_header_size, ELF_BLOCK_ALIGN, is_elf64)
# Check if hash segment max size parameter was passed # Check if hash segment max size parameter was passed
if (hash_seg_max_size is not None): if (hash_seg_max_size is not None):
@@ -1252,7 +1274,7 @@ def pboot_gen_elf(env, elf_in_file_name,
# Read the program header and compute hash # Read the program header and compute hash
proghdr_buff = elf_out_fp.read(elf_header.e_phnum * phdr_size) proghdr_buff = elf_out_fp.read(elf_header.e_phnum * phdr_size)
hash = generate_hash(elfhdr_buff + proghdr_buff, is_sha256_algo) hash = generate_hash(elfhdr_buff + proghdr_buff, sha_algo)
# Write hash to file as first hash table entry # Write hash to file as first hash table entry
hash_out_fp.seek(0) hash_out_fp.seek(0)
@@ -1592,7 +1614,7 @@ def generate_code_hash(env, elf_in_file_name):
page = page + elf_in_fp.read(bytes_in_page - len(page)) page = page + elf_in_fp.read(bytes_in_page - len(page))
if (len(page) < DP_PAGE_SIZE): if (len(page) < DP_PAGE_SIZE):
page = page + (struct.pack('b', 0) * (DP_PAGE_SIZE - len(page))) page = page + (struct.pack('b', 0) * (DP_PAGE_SIZE - len(page)))
hashes = hashes + [generate_hash(page, True)] hashes = hashes + [generate_hash(page, 'SHA256')]
bytes_left -= bytes_in_page bytes_left -= bytes_in_page
# And write them to the hash segment # And write them to the hash segment
@@ -2101,9 +2123,20 @@ def file_copy_offset(in_fp, in_off, out_fp, out_off, num_bytes):
#---------------------------------------------------------------------------- #----------------------------------------------------------------------------
# sha1/sha256 hash routine wrapper # sha1/sha256 hash routine wrapper
#---------------------------------------------------------------------------- #----------------------------------------------------------------------------
def generate_hash(in_buf, is_sha256_algo): def header_size(header_version):
if header_version >= 6:
return 48
else:
return 40
#----------------------------------------------------------------------------
# sha1/sha256 hash routine wrapper
#----------------------------------------------------------------------------
def generate_hash(in_buf, sha_algo):
# Initialize a SHA1 object from the Python hash library # Initialize a SHA1 object from the Python hash library
if (is_sha256_algo is True): if sha_algo == 'SHA384':
m = hashlib.sha384()
elif sha_algo == 'SHA256':
m = hashlib.sha256() m = hashlib.sha256()
else: else:
m = hashlib.sha1() m = hashlib.sha1()

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python2
#============================================================================ #============================================================================
# #
#/** @file qgpt.py #/** @file qgpt.py