tools/manifest_tools/manifest_common.py (453 lines of code) (raw):
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT license.
"""
from __future__ import print_function
from __future__ import unicode_literals
import ctypes
import sys
import os
import traceback
import json
from Crypto.PublicKey import RSA
from Crypto.PublicKey import ECC
from Crypto.Signature import PKCS1_v1_5
from Crypto.Signature import DSS
from Crypto.Hash import SHA256
from Crypto.Hash import SHA384
from Crypto.Hash import SHA512
import manifest_types
import manifest_parser
PFM_MAGIC_NUM = int ("0x504D", 16)
CFM_MAGIC_NUM = int ("0xA592", 16)
PCD_MAGIC_NUM = int ("0x1029", 16)
PFM_V2_MAGIC_NUM = int ("0x706D", 16)
V2_BASE_TYPE_ID = int ("0xff", 16)
V2_PLATFORM_TYPE_ID = int ("0x00", 16)
PFM_V2_FLASH_DEVICE_TYPE_ID = int ("0x10", 16)
PFM_V2_FW_TYPE_ID = int ("0x11", 16)
PFM_V2_FW_VERSION_TYPE_ID = int ("0x12", 16)
PCD_V2_ROT_TYPE_ID = int ("0x40", 16)
PCD_V2_SPI_FLASH_PORT_TYPE_ID = int ("0x41", 16)
PCD_V2_I2C_POWER_CONTROLLER_TYPE_ID = int ("0x42", 16)
PCD_V2_DIRECT_COMPONENT_TYPE_ID = int ("0x43", 16)
PCD_V2_MCTP_BRIDGE_COMPONENT_TYPE_ID = int ("0x44", 16)
CFM_V2_COMPONENT_DEVICE_TYPE_ID = int ("0x70", 16)
CFM_V2_PMR_TYPE_ID = int ("0x71", 16)
CFM_V2_PMR_DIGEST_TYPE_ID = int ("0x72", 16)
CFM_V2_MEASUREMENT_TYPE_ID = int ("0x73", 16)
CFM_V2_MEASUREMENT_DATA_TYPE_ID = int ("0x74", 16)
CFM_V2_ALLOWABLE_DATA_TYPE_ID = int ("0x75", 16)
CFM_V2_ALLOWABLE_PFM_TYPE_ID = int ("0x76", 16)
CFM_V2_ALLOWABLE_CFM_TYPE_ID = int ("0x77", 16)
CFM_V2_ALLOWABLE_PCD_TYPE_ID = int ("0x78", 16)
CFM_V2_ALLOWABLE_ID_TYPE_ID = int ("0x79", 16)
CFM_V2_ROOT_CA_TYPE_ID = int ("0x7A", 16)
class manifest_header (ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [('length', ctypes.c_ushort),
('magic', ctypes.c_ushort),
('id', ctypes.c_uint),
('sig_length', ctypes.c_ushort),
('sig_type', ctypes.c_ubyte),
('reserved', ctypes.c_ubyte)]
class manifest_toc_header (ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [('entry_count', ctypes.c_ubyte),
('hash_count', ctypes.c_ubyte),
('hash_type', ctypes.c_ubyte),
('reserved', ctypes.c_ubyte)]
class manifest_toc_entry (ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [('type_id', ctypes.c_ubyte),
('parent', ctypes.c_ubyte),
('format', ctypes.c_ubyte),
('hash_id', ctypes.c_ubyte),
('offset', ctypes.c_ushort),
('length', ctypes.c_ushort)]
def get_key_from_dict (dictionary, key, group, required=True):
"""
Grab a value from dictionary
:param dictionary: Dictionary to utilize
:param key: Key to fetch
:param group: Group this key belongs to, used for error reporting purposes
:param required: Boolean indicating whether this key is necessary for a valid manifest
:return Value if found
"""
if key not in dictionary:
if required:
raise KeyError ("Failed to generate manifest: {0} missing {1}".format (group, key))
else:
return None
else:
return dictionary[key]
def load_config (config_file):
"""
Load configuration options from file
:param config_file: Path for a text file containing config options
:return Parsed configuration
"""
config = {}
config["xml_list"] = []
config["prv_key_path"] = ""
config["output"] = ""
config["key_size"] = ""
config["hash_type"] = ""
config["key_type"] = ""
config["max_rw_sections"] = ""
config["cfm"] = ""
with open (config_file, 'r') as fh:
data = fh.readlines ()
if not data:
raise IOError ("Could not load configuration file: {0}".format (config_file))
for string in data:
string = string.replace ("\n", "")
string = string.replace ("\r", "")
if string.startswith ("ID"):
config["id"] = string.split ("=")[-1].strip ()
elif string.startswith ("Output"):
config["output"] = string.split ("=")[-1].strip ()
elif string.startswith ("KeySize"):
config["key_size"] = string.split ("=")[-1].strip ()
elif string.startswith ("KeyType"):
config["key_type"] = string.split ("=")[-1].strip ()
elif string.startswith ("HashType"):
config["hash_type"] = string.split ("=")[-1].strip ()
elif string.startswith ("Key"):
config["prv_key_path"] = string.split ("=")[-1].strip ()
elif string.startswith ("MaxRWSections"):
config["max_rw_sections"] = string.split ("=")[-1].strip ()
elif string.startswith ("CFM"):
config["cfm"] = string.split ("=")[-1].strip ()
elif string.startswith ("ComponentMap"):
config["component_map"] = string.split("=")[-1].strip ()
else:
config["xml_list"].append (string)
return config
def load_key (key_type, key_size, prv_key_path):
"""
Load private RSA/ECC key to sign manifest from provided path. If no valid key can be imported,
key size will be what is provided. Otherwise, key size will be size of key imported
:param key_type: Provided key type
:param key_size: Provided key_size
:param prv_key_path: Provided private key path
:return <Sign manifest or not> <key_size> <Key to use for signing>
"""
if prv_key_path:
try:
key = None
if key_type == 1:
key = ECC.import_key (open (prv_key_path).read())
keysize = int (key.pointQ.size_in_bytes ())
else:
key = RSA.importKey (open (prv_key_path).read())
keysize = int (key.n.bit_length ()/8)
except Exception:
raise IOError ("Provided {0} key could not be imported: {1}".format (
"ECC" if key_type == 1 else "RSA", prv_key_path))
return True, keysize, key
else:
print ("No RSA private key provided in config, unsigned manifest will be generated.")
return False, key_size, None
def load_component_map (map_file_path):
"""
Load component map from provided JSON filepath
:param map_file_path: Component type and ID mapping file to parse
:return Dictionary mapping component types to component IDs
"""
component_map = {}
try:
with open (map_file_path, newline="") as map_file:
component_map = json.load (map_file)
except Exception:
raise IOError ("Component map could not be loaded from provided file: {0}".format (
map_file_path))
return component_map
def add_component_mapping (component_type, map_file_path):
"""
Add component type to ID mapping in file at provided JSON filepath
:param component_type: Component type to map
:param map_file_path: Component type and ID mapping file
:return New component ID mapped to provided component type
"""
components = {}
next_component_id = 0
try:
with open (map_file_path, newline="") as map_file:
components = json.load (map_file)
if components is not None and len (components) > 0:
component_ids = sorted (list (components.values()))
next_component_id = int (component_ids[-1]) + 1
except Exception:
raise IOError ("Component map could not be loaded from provided file: {1}".format (
map_file_path))
try:
with open (map_file_path, 'w', newline="") as map_file:
components.update ({ component_type: next_component_id })
json.dump (components, map_file, indent=4, separators=(',',': '))
except Exception:
raise IOError ("Component map could not be updated in provided file: {1}".format (
map_file_path))
return next_component_id
def generate_manifest_header (manifest_id, key_size, manifest_type, hash_type, key_type,
manifest_version):
"""
Create a manifest header
:param manifest_id: Manifest id
:param key_size: Size of signing key, optional
:param manifest_type: Manifest type
:param hash_type: Hashing algorithm
:param key_type: Signing key algorithm, optional
:param manifest_version: Manifest version
:return Instance of a manifest header
"""
if manifest_type == manifest_types.PFM:
if manifest_version == manifest_types.VERSION_1:
magic_num = PFM_MAGIC_NUM
else:
magic_num = PFM_V2_MAGIC_NUM
elif manifest_type == manifest_types.CFM:
magic_num = CFM_MAGIC_NUM
elif manifest_type == manifest_types.PCD:
magic_num = PCD_MAGIC_NUM
else:
raise ValueError ("Unknown manifest type: {0}".format (manifest_type))
sig_len = 0
sig_type = 0
key_strength = 0
if key_size != None:
sig_len = key_size
if key_type != None:
if key_type == 1:
sig_len = ((key_size + 1) * 2) + 6
key_strength = 2 if key_size == 66 else 1 if key_size == 48 else 0
else:
key_strength = 2 if key_size == 512 else 1 if key_size == 384 else 0
sig_type |= key_type << 6 | key_strength << 3
if hash_type != None:
sig_type |= hash_type << 0
return manifest_header (0, magic_num, int (manifest_id), sig_len, sig_type, 0)
def load_xmls (config_filename, max_num_xmls, xml_type):
"""
Load XMLs listed in config file
:param config_filename: Path to config file
:param max_num_xmls: Maximum number of XMLs that can be loaded, set to None if no limit
:param xml_type: Type of XML
:return list of XML elements, boolean indicating whether to sign output or not, key size,
key to use for signing, output ID, output filename and manifest xml version, boolean for
whether XML is for an empty manifest, number of non-contiguous RW sections supported,
selection list, component type to ID map, component map file
"""
config = load_config (config_filename)
key_size = None
prv_key_path = None
key_type = 0
hash_type = None
sign = False
empty = False
max_rw_sections = 3
selection_list = None
component_map = None
component_map_file = ""
if "key_type" in config and config["key_type"]:
if config["key_type"] == "ECC":
key_type = 1
if "hash_type" in config and config["hash_type"]:
if config["hash_type"] == "SHA512":
hash_type = 2
elif config["hash_type"] == "SHA384":
hash_type = 1
else:
hash_type = 0
if "key_size" in config and config["key_size"]:
key_size = int (config["key_size"])
if "prv_key_path" in config and config["prv_key_path"]:
prv_key_path = config["prv_key_path"]
if "max_rw_sections" in config and config["max_rw_sections"]:
max_rw_sections = int (config["max_rw_sections"])
if max_rw_sections > 6:
raise RuntimeError ("Maximum RW sections cannot be greater than 6")
if "cfm" in config and config["cfm"]:
selection_list = manifest_parser.load_and_process_selection_xml (config["cfm"])
elif xml_type is manifest_types.CFM:
raise RuntimeError ("Missing CFM XML")
if "component_map" in config and config["component_map"]:
component_map_file = config["component_map"]
component_map = load_component_map (component_map_file)
if max_num_xmls and (len (config["xml_list"]) > max_num_xmls):
raise RuntimeError ("Too many XML files provided: {0}".format (len (config["xml_list"])))
sign, key_size, key = load_key (key_type, key_size, prv_key_path)
processed_xml = {}
xml_version = None
matching_xml_found = False
for xml in config["xml_list"]:
parsed_xml, curr_xml_version, empty = manifest_parser.load_and_process_xml (xml, xml_type,
selection_list)
if parsed_xml is None:
continue
if xml_version is None:
xml_version = curr_xml_version
if xml_version != curr_xml_version:
raise RuntimeError (
"Failed to generate manifest: XML version is different - {0}".format (xml))
for previous_file, previous_xml in processed_xml.items():
if ((xml_type == manifest_types.PFM) and \
(previous_xml.get('version_id') == parsed_xml.get('version_id'))):
if (previous_xml == parsed_xml):
matching_xml_found = True
break
else:
raise RuntimeError (
"Failed to generate manifest: XML files {0} and {1} have same version " \
"string {2}, but different data".format (previous_file, xml, previous_xml.get ('version_id')))
if matching_xml_found:
matching_xml_found = False
continue
processed_xml.update ({xml:parsed_xml})
if "id" in config:
manifest_id = config["id"]
else:
manifest_id = list (processed_xml.items())[0][1]["version"]
if len (processed_xml) == 0:
empty = True
return processed_xml, sign, key_size, key, key_type, hash_type, manifest_id, config["output"], \
xml_version, empty, max_rw_sections, selection_list, component_map, component_map_file
def write_manifest (xml_version, sign, manifest, key, key_size, key_type, output_filename,
manifest_length, sig_length):
"""
Write manifest generated to provided path.
:param xml_version: manifest xml version
:param sign: Boolean indicating whether to sign manifest or not
:param manifest: Generated manifest to write
:param key: Key to use for signing
:param key_size: Size of key used for signing
:param key_type: Type of Key used for signing
:param output_filename: Name to use for output file
:param manifest_length: The manifest length
:param sig_length: Signature length
"""
check_maximum (ctypes.sizeof (manifest), 65535 - sig_length, "Manifest length")
if ctypes.sizeof (manifest) != manifest_length:
raise ValueError ("Manifest doesn't match output size")
check_maximum (key_type, 1, "Key type")
sha_algo = SHA512 if key_size == 512 else SHA384 if key_size == 384 else SHA256
if xml_version == manifest_types.VERSION_1 and key_type == 1:
raise ValueError ("Manifest Signing key type not supported for version 1 xml")
if sign:
manifest_hash_buf = (ctypes.c_ubyte * manifest_length) ()
ctypes.memmove (ctypes.addressof (manifest_hash_buf), ctypes.addressof (manifest),
manifest_length)
h = sha_algo.new (manifest_hash_buf)
if key_type == 1:
signer = DSS.new (key, 'fips-186-3', 'der')
else:
signer = PKCS1_v1_5.new (key)
signature = signer.sign (h)
signature_buf_len = len (signature) if len (signature) < sig_length else sig_length
signature_buf = (ctypes.c_ubyte * signature_buf_len).from_buffer_copy (signature)
manifest_buf = (ctypes.c_char * (manifest_length + sig_length)) ()
ctypes.memset (manifest_buf, 0, manifest_length + sig_length)
ctypes.memmove (ctypes.byref (manifest_buf, manifest_length),
ctypes.addressof (signature_buf), signature_buf_len)
else:
manifest_buf = (ctypes.c_char * (manifest_length)) ()
out_dir = os.path.dirname (os.path.abspath (output_filename))
if not os.path.exists (out_dir):
os.makedirs (out_dir)
with open (output_filename, 'wb') as fh:
ctypes.memmove (ctypes.byref (manifest_buf), ctypes.addressof (manifest), manifest_length)
fh.write (manifest_buf)
def generate_manifest_toc_header (fw_id_list, hash_type, empty):
"""
Create a manifest table of contents header
:param fw_id_list: List of FW elements that have different IDs
:param hash_type: Hash to be used
:param empty: flag indicating if empty manifest
:return Instance of a manifest table of contents header
"""
entries = 1
if hash_type is None or hash_type > 2:
raise ValueError ("Invalid manifest hash type: {0}".format (hash_type))
if not empty:
entries += 1
for count in fw_id_list.values ():
entries += (count + 1)
return manifest_toc_header (entries, entries, hash_type, 0)
def generate_hash (element, hash_engine):
"""
Generate hash of a ctypes element
:param element: Element to hash
:param hash_engine: Hashing engine to utilize
:return Buffer with digest
"""
# Copy the element instance to a bytearray. Passing element directly to the hash API
# gives TypeError: Object type <class> cannot be passed to C code.
element_size = ctypes.sizeof (element)
element_buf = (ctypes.c_ubyte * element_size) ()
ctypes.memmove (ctypes.addressof (element_buf), ctypes.addressof (element), element_size)
hash_object = hash_engine.new (element_buf)
hash_buf = (ctypes.c_ubyte * hash_object.digest_size).from_buffer_copy (hash_object.digest ())
return hash_buf
def get_platform_id_from_xml_list (xml_list):
"""
Determine platform ID from an XML list
:param xml_list: List of parsed XML files to process for platform ID
:return The platform ID
"""
platform_id = None
for filename, xml in xml_list.items ():
if "platform_id" not in xml:
raise KeyError ("Failed to generate manifest: XML has no platform id - {0}".format (
filename))
if platform_id:
if platform_id != xml["platform_id"]:
raise ValueError (
"Failed to generate manifest: Version platform ids don't match - ({0}, {1})".format (
platform_id, xml["platform_id"]))
else:
platform_id = xml["platform_id"]
return platform_id
def get_hash_engine (hash_type):
"""
Initialize a hash engine instance.
:param hash_type: Hashing algorithm to use
:return Hash engine object
"""
if hash_type == 0:
hash_engine = SHA256
elif hash_type == 1:
hash_engine = SHA384
elif hash_type == 2:
hash_engine = SHA512
else:
raise ValueError ("Invalid manifest hash type: {0}".format (hash_type))
return hash_engine
def get_hash_len (hash_type):
"""
Get hash len.
:param hash_type: Hashing algorithm to use
:return Hash len
"""
if hash_type == 0:
return 32
elif hash_type == 1:
return 48
elif hash_type == 2:
return 64
else:
raise ValueError ("Invalid manifest hash type: {0}".format (hash_type))
def generate_platform_id_buf (xml_platform_id, hash_engine):
"""
Create a platform ID object from parsed XML list
:param xml_platform_id: List of parsed XML of platform id to be included in the object
:param hash_engine: Hashing engine
:return Instance of a platform ID object, object's TOC entry, object hash
"""
platform_id_str = get_key_from_dict (xml_platform_id, "platform_id", "Platform ID")
platform_id_str_len = len (platform_id_str)
check_maximum (platform_id_str_len, 255, "Platform ID {0} string length".format (
platform_id_str))
padding, padding_len = generate_4byte_padding_buf (platform_id_str_len)
reserved = (ctypes.c_ubyte * 3) ()
ctypes.memset (reserved, 0, ctypes.sizeof (ctypes.c_ubyte) * 3)
class platform_id_element (ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [('platform_id_length', ctypes.c_ubyte),
('reserved', ctypes.c_ubyte * 3),
('platform_id', ctypes.c_char * platform_id_str_len),
('platform_id_padding', ctypes.c_ubyte * padding_len)]
platform_id = platform_id_element (platform_id_str_len, reserved,
platform_id_str.encode ('utf-8'), padding)
platform_id_len = ctypes.sizeof (platform_id)
platform_id_toc_entry = manifest_toc_entry (V2_PLATFORM_TYPE_ID, V2_BASE_TYPE_ID, 1, 0, 0,
platform_id_len)
platform_id_hash = generate_hash (platform_id, hash_engine)
return platform_id, platform_id_toc_entry, platform_id_hash
def generate_toc (hash_engine, hash_type, toc_list, hash_list):
"""
Create manifest table of contents from list of pregenerated TOC entries and hash list for all
elements
:param hash_engine: Hashing engine
:param hash_type: Hashing algorithm
:param toc_list: List of TOC entries to be included in the TOC
:param hash_list: List of hashes for all elements in manifest. Hash list ordering must match
toc_list's
:return TOC buffer
"""
if len (toc_list) != len (hash_list):
raise ValueError ("toc_list and hash_list lengths dont match: {0} vs {1}".format (
len (toc_list), len (hash_list)))
check_maximum (len (toc_list), 255, "Number of ToC elements")
num_entries = len (toc_list)
hash_len = hash_engine.digest_size
toc_len = ctypes.sizeof (manifest_toc_header) + \
(ctypes.sizeof (manifest_toc_entry) + hash_len) * num_entries
toc = (ctypes.c_ubyte * toc_len) ()
toc_header_len = ctypes.sizeof (manifest_toc_header)
toc_header = manifest_toc_header (num_entries, num_entries, hash_type, 0)
ctypes.memmove (ctypes.addressof (toc), ctypes.addressof (toc_header), toc_header_len)
offset = ctypes.sizeof (manifest_header) + toc_len + hash_len
hash_id = 0
toc_entry_len = ctypes.sizeof (manifest_toc_entry)
toc_offset = toc_header_len
for entry in toc_list:
entry.offset = offset
offset += entry.length
entry.hash_id = hash_id
hash_id += 1
ctypes.memmove (ctypes.addressof (toc) + toc_offset, ctypes.addressof (entry),
toc_entry_len)
toc_offset += toc_entry_len
for hash_entry in hash_list:
ctypes.memmove (ctypes.addressof (toc) + toc_offset, ctypes.addressof (hash_entry),
hash_len)
toc_offset += hash_len
table_hash = generate_hash (toc, hash_engine)
toc_w_hash = (ctypes.c_ubyte * (toc_len + hash_len)) ()
ctypes.memmove (ctypes.addressof (toc_w_hash), ctypes.addressof (toc), toc_len)
ctypes.memmove (ctypes.addressof (toc_w_hash) + toc_offset, ctypes.addressof (table_hash),
hash_len)
return toc_w_hash
def generate_manifest (hash_engine, hash_type, manifest_id, manifest_type, xml_version, sign, key,
key_size, key_type, toc_list, hash_list, elements_list, elements_len, output):
"""
Generate manifest from element, hash, and toc entries list
:param hash_engine: Hashing engine
:param hash_type: Hashing algorithm
:param manifest_id: Manifest id
:param manifest_type: Manifest type
:param xml_version: Manifest XML version
:param sign: Boolean indicating whether to sign manifest or not
:param key: Key to use for signing
:param key_size: Size of signing key, optional
:param key_type: Signing key algorithm, optional
:param toc_list: List of TOC entries to be included in the TOC
:param hash_list: List of hashes for all elements in manifest. Hash list ordering must match
toc_list's
:param elements_list: List of elements to be included in manifest
:param elements_len: Length of all elements' buffers
:param output: Output filename
"""
manifest_len = elements_len
manifest_header = generate_manifest_header (manifest_id, key_size, manifest_type, hash_type,
key_type, xml_version)
manifest_header_len = ctypes.sizeof (manifest_header)
manifest_len += manifest_header_len
toc = generate_toc (hash_engine, hash_type, toc_list, hash_list)
toc_len = ctypes.sizeof (toc)
manifest_len += toc_len
manifest_header.length = manifest_len + manifest_header.sig_length
manifest_buf = (ctypes.c_ubyte * manifest_len) ()
offset = 0
ctypes.memmove (ctypes.addressof (manifest_buf) + offset, ctypes.addressof (manifest_header),
manifest_header_len)
offset += manifest_header_len
ctypes.memmove (ctypes.addressof (manifest_buf) + offset, ctypes.addressof (toc), toc_len)
offset += toc_len
for element in elements_list:
element_len = ctypes.sizeof (element)
ctypes.memmove (ctypes.addressof (manifest_buf) + offset, ctypes.addressof (element),
element_len)
offset += element_len
write_manifest (xml_version, sign, manifest_buf, key, key_size, key_type, output,
manifest_header.length - manifest_header.sig_length, manifest_header.sig_length)
def check_maximum (value, maximum, value_name):
"""
Compare value to maximum and raise exception beyond limit
:param value: value to check
:param maximum: Maximum value to check against
:param value_name: String name of value
"""
if value > maximum:
raise ValueError ("{0} value greater than maximum: {1} vs {2}".format (value_name,
value, maximum))
def generate_4byte_padding_buf (length):
"""
Create buffer to pad a segment to 4 byte boundaries
:param length: Length of segment to pad
:return padding buffer, padding buffer length
"""
padding_len = ((length + 3) & (~3)) - length
padding = (ctypes.c_ubyte * padding_len) ()
ctypes.memset (padding, 0, ctypes.sizeof (ctypes.c_ubyte) * padding_len)
return padding, padding_len
def check_region_address_validity (start_addr, end_addr, check_alignment=True):
"""
Ensure end address comes after start address and optionally start and end address of a region
are 64kB aligned
:param start_addr: Region start address
:param end_addr: Region end address
:param check_alignment: Check if addresses are 64kB aligned
"""
if check_alignment:
if (start_addr & 0xFFFF) != 0:
raise ValueError ("Region start address (0x{0}) is not 64kB aligned".format (
format (start_addr, '08x')))
if (end_addr & 0xFFFF) != 0xFFFF:
raise ValueError ("Region end address (0x{0}) is not 64kB aligned".format (
format (end_addr, '08x')))
if end_addr <= start_addr:
raise ValueError ("Invalid region addresses: (0x{0}) to (0x{1})".format (
format (start_addr, '08x'), format (end_addr, '08x')))
def move_list_to_buffer (buffer, offset, entry_list):
"""
Move entries from list into buffer sequentially. List entries must be ctype struct instances
:param buffer: Buffer to move list entries into
:param offset: Starting offset into buffer
:param entry_list: List of entries to move sequentially into buffer
:return buffer with list entries
"""
for entry in entry_list:
entry_len = ctypes.sizeof (entry)
ctypes.memmove (ctypes.addressof (buffer) + offset, ctypes.addressof (entry), entry_len)
offset += entry_len
return offset
def check_if_regions_contiguous (region1, region2):
"""
Check if the 2 provided regions are contiguous
:param region1: Addresses of first region
:param region2: Addresses of second region
:return True if the regions are contiguous, False if not
"""
return (((region1[1] + 1) == region2[0]) or ((region2[1] + 1) == region1[0]))
def check_if_regions_overlap (region1, region2):
"""
Check if the 2 provided regions overlap
:param region1: Addresses of first region
:param region2: Addresses of second region
:return True if the regions overlap, False if not
"""
largest_start_addr = max (region1[0], region2[0])
smallest_end_addr = min (region1[1], region2[1])
return (largest_start_addr <= smallest_end_addr)