mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-04-29 04:04:05 +00:00
When one single device being connected after multiple-levels of PCI-to-PCI bridges, all those bridges may have the same MMIO window. That causes the current static allocator not to consider that window being occupied because of how it detects nested MMIO regions. Remove duplicates in the list of secondary bus MMIO windows to fix that issue. Tracked-On: #8312 Signed-off-by: Junjie Mao <junjie.mao@intel.com>
587 lines
29 KiB
Python
587 lines
29 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (C) 2021-2022 Intel Corporation.
|
|
#
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
#
|
|
|
|
import sys, os, re
|
|
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library'))
|
|
import acrn_config_utilities, lib.error, lib.lib, math
|
|
from collections import namedtuple
|
|
from acrn_config_utilities import get_node
|
|
|
|
# VMSIX devices list
|
|
TSN_DEVS = [("0x8086", "0x4b30"), ("0x8086", "0x4b31"), ("0x8086", "0x4b32"), ("0x8086", "0x4ba0"),
|
|
("0x8086", "0x4ba1"), ("0x8086", "0x4ba2"), ("0x8086", "0x4bb0"), ("0x8086", "0x4bb1"),
|
|
("0x8086", "0x4bb2"), ("0x8086", "0xa0ac"), ("0x8086", "0x43ac"), ("0x8086", "0x43a2")]
|
|
GPIO_DEVS = [("0x8086", "0x4b88"), ("0x8086", "0x4b89")]
|
|
|
|
KNOWN_CAPS_PCI_DEVS_DB = {
|
|
"VMSIX": TSN_DEVS + GPIO_DEVS,
|
|
}
|
|
|
|
# Constants for device name prefix
|
|
IVSHMEM = "IVSHMEM"
|
|
VUART = "VUART"
|
|
PTDEV = "PTDEV"
|
|
LEGACY_VUART = "LEGACY_VUART"
|
|
|
|
# A bar in pci hole must be above this threshold
|
|
# A bar's address below this threshold is for special purpose and should be preserved
|
|
PCI_HOLE_THRESHOLD = 0x100000
|
|
|
|
# IO port address common constants
|
|
# The valid io port address range will fall into [0x0, 0xFFFF]
|
|
# The address below 0xD00 are usually reserved for particular usage.
|
|
# For example: Configuration Space Address and Configuration Space Data
|
|
IO_PORT_MAX_ADDRESS = 0xFFFF
|
|
IO_PORT_THRESHOLD = 0xD00
|
|
|
|
# Common memory size units
|
|
SIZE_K = 1024
|
|
SIZE_M = SIZE_K * 1024
|
|
SIZE_G = SIZE_M * 1024
|
|
|
|
# Bar base alignment constant
|
|
VBAR_ALIGNMENT = 4 * SIZE_K
|
|
|
|
# Memory bar bits
|
|
PREFETCHABLE_BIT = 0x8
|
|
MEMORY_BAR_LOCATABLE_64BITS = 0x4
|
|
|
|
# Pre-launched VM MMIO windows constant
|
|
PRE_LAUNCHED_VM_LOW_MEM_START = 2 * SIZE_G
|
|
PRE_LAUNCHED_VM_LOW_MEM_END = 3.5 * SIZE_G
|
|
PRE_LAUNCHED_VM_HIGH_MEM_START = 256 * SIZE_G
|
|
PRE_LAUNCHED_VM_HIGH_MEM_END = 512 * SIZE_G
|
|
|
|
# Constants for ivshmem
|
|
BAR0_SHEMEM_SIZE = 4 * SIZE_K
|
|
BAR1_SHEMEM_SIZE = 4 * SIZE_K
|
|
BAR2_SHEMEM_ALIGNMENT = 2 * acrn_config_utilities.SIZE_M
|
|
|
|
# Constants for pci vuart
|
|
PCI_VUART_VBAR0_SIZE = 4 * SIZE_K
|
|
PCI_VUART_VBAR1_SIZE = 4 * SIZE_K
|
|
|
|
# Constants for legacy vuart
|
|
LEGACY_VUART_IO_PORT_SIZE = 0x10
|
|
|
|
# Constants for vmsix bar
|
|
VMSIX_VBAR_SIZE = 4 * SIZE_K
|
|
|
|
# Constant for VIRT_ACPI_NVS_ADDR
|
|
"""
|
|
VIRT_ACPI_NVS_ADDR and PRE_RTVM_SW_SRAM_END_GPA
|
|
need to be consistant with the layout of hypervisor\arch\x86\guest\ve820.c
|
|
"""
|
|
VIRT_ACPI_NVS_ADDR = 0x7FF00000
|
|
RESERVED_NVS_AREA = 0xB0000
|
|
|
|
PRE_RTVM_SW_SRAM_END_GPA = (0x7FDFB000 - 1)
|
|
|
|
class AddrWindow(namedtuple(
|
|
"AddrWindow", [
|
|
"start",
|
|
"end"])):
|
|
|
|
PATTERN = re.compile(r"\s*(?P<start>[0-9a-f]+)-(?P<end>[0-9a-f]+) ")
|
|
|
|
@classmethod
|
|
def from_str(cls, value):
|
|
if not isinstance(value, str):
|
|
raise ValueError("value must be a str: {}".format(type(value)))
|
|
|
|
match = cls.PATTERN.fullmatch(value)
|
|
if match:
|
|
return AddrWindow(
|
|
start=int(match.group("start"), 16),
|
|
end=int(match.group("end"), 16))
|
|
else:
|
|
raise ValueError("not an address window: {!r}".format(value))
|
|
|
|
def overlaps(self, other):
|
|
if not isinstance(other, AddrWindow):
|
|
raise TypeError('overlaps() other must be an AddrWindow: {}'.format(type(other)))
|
|
if other.end < self.start:
|
|
return False
|
|
if self.end < other.start:
|
|
return False
|
|
return True
|
|
|
|
def contains(self, other):
|
|
if not isinstance(other, AddrWindow):
|
|
raise TypeError('contains() other must be an AddrWindow: {}'.format(type(other)))
|
|
if other.start >= self.start and other.end <= self.end:
|
|
return True
|
|
return False
|
|
|
|
def insert_vuart_to_dev_dict(scenario_etree, vm_id, devdict_32bits):
|
|
|
|
console_vuart = scenario_etree.xpath(f"./console_vuart[base != 'INVALID_PCI_BASE']/@id")
|
|
for vuart_id in console_vuart:
|
|
devdict_32bits[(f"{VUART}_{vuart_id}", "bar0")] = PCI_VUART_VBAR0_SIZE
|
|
devdict_32bits[(f"{VUART}_{vuart_id}", "bar1")] = PCI_VUART_VBAR1_SIZE
|
|
|
|
vm_name = get_node(f"//vm[@id = '{vm_id}']/name/text()", scenario_etree)
|
|
communication_vuarts = scenario_etree.xpath(f"//vuart_connection[endpoint/vm_name/text() = '{vm_name}']")
|
|
for vuart_id, vuart in enumerate(communication_vuarts, start=1):
|
|
connection_type = get_node(f"./type/text()", vuart)
|
|
if connection_type == "pci":
|
|
devdict_32bits[(f"{VUART}_{vuart_id}", "bar0")] = PCI_VUART_VBAR0_SIZE
|
|
devdict_32bits[(f"{VUART}_{vuart_id}", "bar1")] = PCI_VUART_VBAR1_SIZE
|
|
|
|
def insert_legacy_vuart_to_dev_dict(vm_node, devdict_io_port):
|
|
legacy_vuart = vm_node.xpath(f".//legacy_vuart[base = 'CONFIG_COM_BASE']/@id")
|
|
for vuart_id in legacy_vuart:
|
|
devdict_io_port[(f"{LEGACY_VUART}_{vuart_id}", "base")] = LEGACY_VUART_IO_PORT_SIZE
|
|
|
|
def insert_ivsheme_to_dev_dict(scenario_etree, devdict_32bits, devdict_64bits, vm_id):
|
|
shmem_regions = lib.lib.get_ivshmem_regions_by_tree(scenario_etree)
|
|
if vm_id not in shmem_regions:
|
|
return
|
|
shmems = shmem_regions.get(vm_id)
|
|
for shm in shmems.values():
|
|
try:
|
|
int_size = int(shm.get('size')) * SIZE_M
|
|
except:
|
|
continue
|
|
idx = shm.get('id')
|
|
devdict_32bits[(f"{IVSHMEM}_{idx}", "bar0")] = BAR0_SHEMEM_SIZE
|
|
devdict_32bits[(f"{IVSHMEM}_{idx}", "bar1")] = BAR1_SHEMEM_SIZE
|
|
devdict_64bits[(f"{IVSHMEM}_{idx}", "bar2")] = int_size
|
|
|
|
def insert_pt_devs_to_dev_dict(board_etree, vm_node_etree, devdict_32bits, devdict_64bits):
|
|
pt_devs = vm_node_etree.xpath(f".//pci_dev/text()")
|
|
for pt_dev in pt_devs:
|
|
bdf = pt_dev.split()[0]
|
|
bus = int(bdf.split(':')[0], 16)
|
|
dev = int(bdf.split(":")[1].split('.')[0], 16)
|
|
func = int(bdf.split(":")[1].split('.')[1], 16)
|
|
bdf = lib.lib.BusDevFunc(bus=bus, dev=dev, func=func)
|
|
pt_dev_node = get_node(f"//bus[@type = 'pci' and @address = '{hex(bus)}']/device[@address = '{hex((dev << 16) | func)}']", board_etree)
|
|
if pt_dev_node is not None:
|
|
insert_vmsix_to_dev_dict(pt_dev_node, devdict_32bits)
|
|
pt_dev_resources = pt_dev_node.xpath(".//resource[@type = 'memory' and @len != '0x0' and @id and @width]")
|
|
for pt_dev_resource in pt_dev_resources:
|
|
if int(pt_dev_resource.get('min'), 16) < PCI_HOLE_THRESHOLD:
|
|
continue
|
|
dev_name = str(bdf)
|
|
bar_len = pt_dev_resource.get('len')
|
|
bar_region = pt_dev_resource.get('id')
|
|
bar_width = pt_dev_resource.get('width')
|
|
if bar_width == "32":
|
|
devdict_32bits[(f"{dev_name}", f"{bar_region}")] = int(bar_len, 16)
|
|
else:
|
|
devdict_64bits[(f"{dev_name}", f"{bar_region}")] = int(bar_len, 16)
|
|
|
|
def get_pt_devs_io_port(board_etree, vm_node_etree):
|
|
pt_devs = vm_node_etree.xpath(f".//pci_dev/text()")
|
|
devdict = {}
|
|
for pt_dev in pt_devs:
|
|
bdf = pt_dev.split()[0]
|
|
bus = int(bdf.split(':')[0], 16)
|
|
dev = int(bdf.split(":")[1].split('.')[0], 16)
|
|
func = int(bdf.split(":")[1].split('.')[1], 16)
|
|
bdf = lib.lib.BusDevFunc(bus=bus, dev=dev, func=func)
|
|
pt_dev_node = get_node(f"//bus[@type = 'pci' and @address = '{hex(bus)}']/device[@address = '{hex((dev << 16) | func)}']", board_etree)
|
|
if pt_dev_node is not None:
|
|
pt_dev_resources = pt_dev_node.xpath(".//resource[@type = 'io_port' and @id[starts-with(., 'bar')]]")
|
|
for pt_dev_resource in pt_dev_resources:
|
|
dev_name = str(bdf)
|
|
bar_region = pt_dev_resource.get('id')
|
|
devdict[(f"{dev_name}", f"{bar_region}")] = int(pt_dev_resource.get('min'), 16)
|
|
return devdict
|
|
|
|
def insert_vmsix_to_dev_dict(pt_dev_node, devdict):
|
|
"""
|
|
Allocate an unused mmio window for the first free bar region of a vmsix supported passthrough device.
|
|
1. Check if this passtrhough device is in the list "KNOWN_CAPS_PCI_DEVS_DB" of "VMSIX" suppoeted device.
|
|
2. Find the first unused region index for the vmsix bar.
|
|
3. Allocate an unused mmio window for this bar.
|
|
"""
|
|
vendor = get_node("./vendor/text()", pt_dev_node)
|
|
identifier = get_node("./identifier/text()", pt_dev_node)
|
|
if vendor is None or identifier is None:
|
|
return
|
|
|
|
if (vendor, identifier) in KNOWN_CAPS_PCI_DEVS_DB.get('VMSIX'):
|
|
bar_regions = pt_dev_node.xpath(".//resource[@type = 'memory' and @width]")
|
|
bar_32bits = [bar_region.get('id') for bar_region in bar_regions if bar_region.get('width') == '32']
|
|
bar_32bits_idx_list = [int(bar.split('bar')[-1]) for bar in bar_32bits]
|
|
bar_64bits = [bar_region.get('id') for bar_region in bar_regions if bar_region.get('width') == '64']
|
|
bar_64bits_idx_list_1 = [int(bar.split('bar')[-1]) for bar in bar_64bits]
|
|
bar_64bits_idx_list_2 = [idx + 1 for idx in bar_64bits_idx_list_1]
|
|
|
|
bar_regions_io_port = pt_dev_node.xpath(".//resource[@type = 'io_port' and @id[starts-with(., 'bar')]]/@id")
|
|
bar_io_port_idx_list = [int(bar.split('bar')[-1]) for bar in bar_regions_io_port]
|
|
|
|
used_bar_index = set(bar_32bits_idx_list + bar_64bits_idx_list_1 + bar_64bits_idx_list_2 + bar_io_port_idx_list)
|
|
unused_bar_index = [i for i in range(6) if i not in used_bar_index]
|
|
try:
|
|
next_bar_region = unused_bar_index.pop(0)
|
|
except IndexError:
|
|
raise lib.error.ResourceError(f"Cannot allocate a bar index for vmsix supported device: {vendor}:{identifier}, used bar idx list: {used_bar_index}")
|
|
address = get_node("./@address", pt_dev_node)
|
|
bus = get_node(f"../@address", pt_dev_node)
|
|
if bus is not None and address is not None:
|
|
bdf = lib.lib.BusDevFunc(bus=int(bus, 16), dev=int(address, 16) >> 16, func=int(address, 16) & 0xffff)
|
|
dev_name = str(bdf)
|
|
devdict[(f"{dev_name}", f"bar{next_bar_region}")] = VMSIX_VBAR_SIZE
|
|
|
|
def get_devs_mem_native(board_etree, mems):
|
|
nodes = board_etree.xpath(f"//resource[@type = 'memory' and @len != '0x0' and @id and @width and @min and @max]")
|
|
secondary_pci_nodes = board_etree.xpath(f"//resource[../bus[@type = 'pci'] and @type = 'memory' and @len != '0x0' and @min and @max]")
|
|
secondary_pci_windows = list(set(AddrWindow(int(node.get('min'), 16), int(node.get('max'), 16)) for node in secondary_pci_nodes))
|
|
dev_list = []
|
|
|
|
for node in nodes:
|
|
start = node.get('min')
|
|
end = node.get('max')
|
|
node_window = AddrWindow(int(start, 16), int(end, 16))
|
|
if all(not(w.contains(node_window)) for w in secondary_pci_windows):
|
|
dev_list.append(node_window)
|
|
|
|
# check if there is any nested window
|
|
for i in range(len(secondary_pci_windows)):
|
|
secondary_pci_window = secondary_pci_windows[i]
|
|
if all(not(w.contains(secondary_pci_window)) for w in (secondary_pci_windows[:i] + secondary_pci_windows[i + 1:])):
|
|
dev_list.append(secondary_pci_window)
|
|
|
|
# check if all the mmio window of dev_list fall into pci hole
|
|
return_dev_list = [d for d in dev_list if any(mem.contains(d) for mem in mems)]
|
|
return sorted(return_dev_list)
|
|
|
|
def get_devs_io_port_native(board_etree, io_port_range):
|
|
nodes = board_etree.xpath(f"//device/resource[@type = 'io_port' and @len != '0x0' and @id]")
|
|
dev_list = []
|
|
for node in nodes:
|
|
start = node.get('min')
|
|
end = node.get('max')
|
|
if start is not None and end is not None:
|
|
window = AddrWindow(int(start, 16), int(end, 16))
|
|
for range in io_port_range:
|
|
if window.start >= range.start and window.end <= range.end:
|
|
dev_list.append(window)
|
|
break
|
|
return sorted(dev_list)
|
|
|
|
def get_devs_mem_passthrough(board_etree, scenario_etree):
|
|
"""
|
|
Get all pre-launched vms' passthrough devices' mmio windows in native environment.
|
|
return: list of passtrhough devices' mmio windows.
|
|
"""
|
|
dev_list = []
|
|
pt_devs = scenario_etree.xpath(f"//vm[load_order = 'PRE_LAUNCHED_VM']/pci_devs/pci_dev/text()")
|
|
for pt_dev in pt_devs:
|
|
bdf = pt_dev.split()[0]
|
|
bus = int(bdf.split(':')[0], 16)
|
|
dev = int(bdf.split(":")[1].split('.')[0], 16)
|
|
func = int(bdf.split(":")[1].split('.')[1], 16)
|
|
resources = board_etree.xpath(f"//bus[@address = '{hex(bus)}']/device[@address = '{hex((dev << 16) | func)}'] \
|
|
/resource[@type = 'memory' and @len != '0x0' and @width]")
|
|
for resource in resources:
|
|
start = resource.get('min')
|
|
end = resource.get('max')
|
|
dev_list.append(AddrWindow(int(start, 16), int(end, 16)))
|
|
return dev_list
|
|
|
|
def get_pt_devs_io_port_passthrough_per_vm(board_etree, vm_node):
|
|
"""
|
|
Get all pre-launched vms' passthrough devices' io port addresses in native environment.
|
|
return: list of passtrhough devices' io port addresses.
|
|
"""
|
|
dev_list = []
|
|
pt_devs = vm_node.xpath(f".//pci_devs/pci_dev/text()")
|
|
for pt_dev in pt_devs:
|
|
bdf = pt_dev.split()[0]
|
|
bus = int(bdf.split(':')[0], 16)
|
|
dev = int(bdf.split(":")[1].split('.')[0], 16)
|
|
func = int(bdf.split(":")[1].split('.')[1], 16)
|
|
resources = board_etree.xpath(f"//bus[@address = '{hex(bus)}']/device[@address = '{hex((dev << 16) | func)}'] \
|
|
/resource[@type = 'io_port' and @len != '0x0']")
|
|
for resource in resources:
|
|
start = resource.get('min')
|
|
end = resource.get('max')
|
|
dev_list.append(AddrWindow(int(start, 16), int(end, 16)))
|
|
return dev_list
|
|
|
|
def get_pt_devs_io_port_passthrough(board_etree, scenario_etree):
|
|
"""
|
|
Get all pre-launched vms' passthrough devices' io port addresses in native environment.
|
|
return: list of passtrhough devices' io port addresses.
|
|
"""
|
|
dev_list = []
|
|
vm_nodes = scenario_etree.xpath(f"//vm[load_order = 'PRE_LAUNCHED_VM']")
|
|
for vm_node in vm_nodes:
|
|
dev_list_per_vm = get_pt_devs_io_port_passthrough_per_vm(board_etree, vm_node)
|
|
dev_list = dev_list + dev_list_per_vm
|
|
return dev_list
|
|
|
|
def get_pci_hole_native(board_etree):
|
|
resources_hostbridge = board_etree.xpath("//bus/resource[@type = 'memory' and @len != '0x0' and not(starts-with(@id, 'bar')) and not(@width)]")
|
|
low_mem = set()
|
|
high_mem = set()
|
|
for resource_hostbridge in resources_hostbridge:
|
|
start = resource_hostbridge.get('min')
|
|
end = resource_hostbridge.get('max')
|
|
if start is not None and end is not None and int(start, 16) >= PCI_HOLE_THRESHOLD:
|
|
if int(end,16) < 4 * SIZE_G:
|
|
low_mem.add(AddrWindow(int(start, 16), int(end, 16)))
|
|
else:
|
|
high_mem.add(AddrWindow(int(start, 16), int(end, 16)))
|
|
return list(sorted(low_mem)), list(sorted(high_mem))
|
|
|
|
def get_io_port_range_native(board_etree):
|
|
resources_hostbridge = board_etree.xpath("//bus[@address = '0x0']/resource[@type = 'io_port' and @len != '0x0']")
|
|
io_port_range_list = set()
|
|
for resource_hostbridge in resources_hostbridge:
|
|
start = resource_hostbridge.get('min')
|
|
end = resource_hostbridge.get('max')
|
|
if start is not None and end is not None and \
|
|
int(start, 16) >= IO_PORT_THRESHOLD and int(end, 16) <= IO_PORT_MAX_ADDRESS:
|
|
io_port_range_list.add(AddrWindow(int(start, 16), int(end, 16)))
|
|
return list(sorted(io_port_range_list))
|
|
|
|
def create_device_node(allocation_etree, vm_id, devdict):
|
|
for dev in devdict:
|
|
dev_name = dev[0]
|
|
bar_region = dev[1].split('bar')[-1]
|
|
bar_base = devdict.get(dev)
|
|
|
|
vm_node = get_node(f"/acrn-config/vm[@id = '{vm_id}']", allocation_etree)
|
|
if vm_node is None:
|
|
vm_node = acrn_config_utilities.append_node("/acrn-config/vm", None, allocation_etree, id = vm_id)
|
|
dev_node = get_node(f"./device[@name = '{dev_name}']", vm_node)
|
|
if dev_node is None:
|
|
dev_node = acrn_config_utilities.append_node("./device", None, vm_node, name = dev_name)
|
|
if get_node(f"./bar[@id='{bar_region}']", dev_node) is None:
|
|
acrn_config_utilities.append_node(f"./bar", hex(bar_base), dev_node, id = bar_region)
|
|
if IVSHMEM in dev_name and bar_region == '2':
|
|
acrn_config_utilities.update_text(f"./bar[@id = '2']", hex(bar_base | PREFETCHABLE_BIT | MEMORY_BAR_LOCATABLE_64BITS), dev_node, True)
|
|
|
|
def create_vuart_node(allocation_etree, vm_id, devdict):
|
|
for dev in devdict:
|
|
vuart_id = dev[0][-1]
|
|
bar_base = devdict.get(dev)
|
|
|
|
vm_node = get_node(f"/acrn-config/vm[@id = '{vm_id}']", allocation_etree)
|
|
if vm_node is None:
|
|
vm_node = acrn_config_utilities.append_node("/acrn-config/vm", None, allocation_etree, id = vm_id)
|
|
vuart_node = get_node(f"./legacy_vuart[@id = '{vuart_id}']", vm_node)
|
|
if vuart_node is None:
|
|
vuart_node = acrn_config_utilities.append_node("./legacy_vuart", None, vm_node, id = vuart_id)
|
|
if get_node(f"./base", vuart_node) is None:
|
|
acrn_config_utilities.append_node(f"./base", hex(bar_base), vuart_node)
|
|
|
|
def create_native_pci_hole_node(allocation_etree, low_mem, high_mem):
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/MMIO32_START", hex(low_mem[0].start).upper(), allocation_etree)
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/MMIO32_END", hex(low_mem[-1].end + 1).upper(), allocation_etree)
|
|
if len(high_mem):
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/MMIO64_START", hex(high_mem[0].start).upper(), allocation_etree)
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/MMIO64_END", hex(high_mem[-1].end + 1).upper(), allocation_etree)
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/HI_MMIO_START", hex(high_mem[0].start).upper(), allocation_etree)
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/HI_MMIO_END", hex(high_mem[0].end + 1).upper(), allocation_etree)
|
|
else:
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/MMIO64_START", "~0".upper(), allocation_etree)
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/MMIO64_END", "~0", allocation_etree)
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/HI_MMIO_START", "~0".upper(), allocation_etree)
|
|
acrn_config_utilities.append_node("/acrn-config/hv/MMIO/HI_MMIO_END", "0", allocation_etree)
|
|
|
|
def get_free_addr(windowslist, used, size, alignment):
|
|
if not size:
|
|
raise ValueError(f"allocate size cannot be: {size}")
|
|
if not windowslist:
|
|
raise ValueError(f"No address range is specified:{windowslist}")
|
|
|
|
alignment = max(alignment, size)
|
|
for w in windowslist:
|
|
new_w_start = acrn_config_utilities.round_up(w.start, alignment)
|
|
window = AddrWindow(start = new_w_start, end = new_w_start + size - 1)
|
|
for u in used:
|
|
if window.overlaps(u):
|
|
new_u_end = acrn_config_utilities.round_up(u.end + 1, alignment)
|
|
window = AddrWindow(start = new_u_end, end = new_u_end + size - 1)
|
|
continue
|
|
if window.overlaps(w):
|
|
return window
|
|
raise lib.error.ResourceError(f"Not enough address window for a device size: {size}, free address windows: {windowslist}, used address windos{used}")
|
|
|
|
def alloc_addr(mems, devdict, used_mem, alignment):
|
|
devdict_list = sorted(devdict.items(), key = lambda t : t[1], reverse = True)
|
|
devdict_base = {}
|
|
for dev_bar in devdict_list:
|
|
bar_name = dev_bar[0]
|
|
bar_length = dev_bar[1]
|
|
bar_window = get_free_addr(mems, used_mem, bar_length, alignment)
|
|
bar_end_addr = bar_window.start + bar_length - 1
|
|
used_mem.append(AddrWindow(bar_window.start, bar_end_addr))
|
|
used_mem.sort()
|
|
devdict_base[bar_name] = bar_window.start
|
|
return devdict_base
|
|
|
|
def allocate_pci_bar(board_etree, scenario_etree, allocation_etree):
|
|
native_low_mem, native_high_mem = get_pci_hole_native(board_etree)
|
|
create_native_pci_hole_node(allocation_etree, native_low_mem, native_high_mem)
|
|
|
|
vm_nodes = scenario_etree.xpath("//vm")
|
|
for vm_node in vm_nodes:
|
|
vm_id = vm_node.get('id')
|
|
|
|
devdict_32bits = {}
|
|
devdict_64bits = {}
|
|
insert_vuart_to_dev_dict(scenario_etree, vm_id, devdict_32bits)
|
|
insert_ivsheme_to_dev_dict(scenario_etree, devdict_32bits, devdict_64bits, vm_id)
|
|
insert_pt_devs_to_dev_dict(board_etree, vm_node, devdict_32bits, devdict_64bits)
|
|
|
|
low_mem = []
|
|
high_mem = []
|
|
used_low_mem = []
|
|
used_high_mem = []
|
|
|
|
load_order = get_node("./load_order/text()", vm_node)
|
|
if load_order is not None and lib.lib.is_pre_launched_vm(load_order):
|
|
low_mem = [AddrWindow(start = PRE_LAUNCHED_VM_LOW_MEM_START, end = PRE_LAUNCHED_VM_LOW_MEM_END - 1)]
|
|
high_mem = [AddrWindow(start = PRE_LAUNCHED_VM_HIGH_MEM_START, end = PRE_LAUNCHED_VM_HIGH_MEM_END - 1)]
|
|
elif load_order is not None and lib.lib.is_service_vm(load_order):
|
|
low_mem = native_low_mem
|
|
high_mem = native_high_mem
|
|
mem_passthrough = get_devs_mem_passthrough(board_etree, scenario_etree)
|
|
used_low_mem_native = get_devs_mem_native(board_etree, low_mem)
|
|
used_high_mem_native = get_devs_mem_native(board_etree, high_mem)
|
|
# release the passthrough devices mmio windows from Service VM
|
|
used_low_mem = [mem for mem in used_low_mem_native if mem not in mem_passthrough]
|
|
used_high_mem = [mem for mem in used_high_mem_native if mem not in mem_passthrough]
|
|
else:
|
|
# fall into else when the load_order is post-launched vm, no mmio allocation is needed
|
|
continue
|
|
|
|
devdict_base_32_bits = alloc_addr(low_mem, devdict_32bits, used_low_mem, VBAR_ALIGNMENT)
|
|
devdict_base_64_bits = alloc_addr(low_mem + high_mem, devdict_64bits, used_low_mem + used_high_mem, VBAR_ALIGNMENT)
|
|
create_device_node(allocation_etree, vm_id, devdict_base_32_bits)
|
|
create_device_node(allocation_etree, vm_id, devdict_base_64_bits)
|
|
|
|
def allocate_io_port(board_etree, scenario_etree, allocation_etree):
|
|
io_port_range_list_native = get_io_port_range_native(board_etree)
|
|
|
|
vm_nodes = scenario_etree.xpath("//vm")
|
|
for vm_node in vm_nodes:
|
|
vm_id = vm_node.get('id')
|
|
|
|
devdict_io_port = {}
|
|
insert_legacy_vuart_to_dev_dict(vm_node, devdict_io_port)
|
|
|
|
io_port_range_list = []
|
|
used_io_port_list = []
|
|
|
|
load_order = get_node("./load_order/text()", vm_node)
|
|
if load_order is not None and lib.lib.is_service_vm(load_order):
|
|
io_port_range_list = io_port_range_list_native
|
|
io_port_passthrough = get_pt_devs_io_port_passthrough(board_etree, scenario_etree)
|
|
used_io_port_list_native = get_devs_io_port_native(board_etree, io_port_range_list_native)
|
|
# release the passthrough devices io port address from Service VM
|
|
used_io_port_list = [io_port for io_port in used_io_port_list_native if io_port not in io_port_passthrough]
|
|
else:
|
|
io_port_range_list = [AddrWindow(start = IO_PORT_THRESHOLD, end = IO_PORT_MAX_ADDRESS)]
|
|
used_io_port_list = get_pt_devs_io_port_passthrough_per_vm(board_etree, vm_node)
|
|
|
|
devdict_base_io_port = alloc_addr(io_port_range_list, devdict_io_port, used_io_port_list, 0)
|
|
create_vuart_node(allocation_etree, vm_id, devdict_base_io_port)
|
|
|
|
def allocate_ssram_region(board_etree, scenario_etree, allocation_etree):
|
|
# Guest physical address of the SW SRAM allocated to a pre-launched VM
|
|
ssram_area_max_size = 0
|
|
enabled = get_node("//SSRAM_ENABLED/text()", scenario_etree)
|
|
if enabled == "y":
|
|
pre_rt_vms = get_node("//vm[load_order = 'PRE_LAUNCHED_VM' and vm_type = 'RTVM']", scenario_etree)
|
|
if pre_rt_vms is not None:
|
|
vm_id = pre_rt_vms.get("id")
|
|
l3_sw_sram = board_etree.xpath("//cache[@level='3']/capability[@id='Software SRAM']")
|
|
if l3_sw_sram:
|
|
# Calculate SSRAM area size. Containing all cache parts
|
|
top = 0
|
|
base = 0
|
|
for ssram in board_etree.xpath("//cache/capability[@id='Software SRAM']"):
|
|
entry_base = int(get_node("./start/text()", ssram), 16)
|
|
entry_size = int(get_node("./size/text()", ssram))
|
|
top = (entry_base + entry_size) if top < (entry_base + entry_size) else top
|
|
base = entry_base if base == 0 or entry_base < base else base
|
|
ssram_area_max_size = math.ceil((top - base)/0x1000) * 0x1000
|
|
|
|
allocation_vm_node = get_node(f"/acrn-config/vm[@id = '{vm_id}']", allocation_etree)
|
|
if allocation_vm_node is None:
|
|
allocation_vm_node = acrn_config_utilities.append_node("/acrn-config/vm", None, allocation_etree, id = vm_id)
|
|
acrn_config_utilities.append_node("./ssram/start_gpa", hex(PRE_RTVM_SW_SRAM_END_GPA - ssram_area_max_size + 1), allocation_vm_node)
|
|
acrn_config_utilities.append_node("./ssram/end_gpa", hex(PRE_RTVM_SW_SRAM_END_GPA), allocation_vm_node)
|
|
acrn_config_utilities.append_node("./ssram/max_size", str(ssram_area_max_size), allocation_vm_node)
|
|
|
|
def allocate_log_area(board_etree, scenario_etree, allocation_etree):
|
|
tpm2_enabled = get_node(f"//vm[@id = '0']/mmio_resources/TPM2/text()", scenario_etree)
|
|
if tpm2_enabled is None or tpm2_enabled == 'n':
|
|
return
|
|
|
|
if get_node("//capability[@id='log_area']", board_etree) is not None:
|
|
log_area_min_len_native = int(get_node(f"//log_area_minimum_length/text()", board_etree), 16)
|
|
log_area_start_address = acrn_config_utilities.round_up(VIRT_ACPI_NVS_ADDR, 0x10000) + RESERVED_NVS_AREA
|
|
allocation_vm_node = get_node(f"/acrn-config/vm[@id = '0']", allocation_etree)
|
|
if allocation_vm_node is None:
|
|
allocation_vm_node = acrn_config_utilities.append_node("/acrn-config/vm", None, allocation_etree, id = '0')
|
|
acrn_config_utilities.append_node("./log_area_start_address", hex(log_area_start_address).upper(), allocation_vm_node)
|
|
acrn_config_utilities.append_node("./log_area_minimum_length", hex(log_area_min_len_native).upper(), allocation_vm_node)
|
|
|
|
def pt_dev_io_port_passthrough(board_etree, scenario_etree, allocation_etree):
|
|
vm_nodes = scenario_etree.xpath("//vm")
|
|
for vm_node in vm_nodes:
|
|
vm_id = vm_node.get('id')
|
|
devdict_io_port = get_pt_devs_io_port(board_etree, vm_node)
|
|
create_device_node(allocation_etree, vm_id, devdict_io_port)
|
|
|
|
"""
|
|
Pre-launched VM gpa layout:
|
|
+--------------------------------------------------+ <--End of VM high pci hole
|
|
| 64 bits vbar of emulated PCI devices | Offset 0x8000000000
|
|
+--------------------------------------------------+ <--Start of VM high pci hole
|
|
| | Offset 0x4000000000
|
|
... ...
|
|
| |
|
|
+--------------------------------------------------+ <--End of VM low pci hole
|
|
| 32 and 64 bits vbar of emulated PCI devices | Offset 0xE0000000
|
|
+--------------------------------------------------+ <--Start of VM low pci hole
|
|
| | Offset 0x80000000
|
|
... ...
|
|
| TPM2 log area at 0x7FFB0000 |
|
|
... ...
|
|
+--------------------------------------------------+ <--End of SSRAM area, at Offset 0x7FDFB000
|
|
| SSRAM area |
|
|
+--------------------------------------------------+ <--Start of SSRAM area
|
|
| | (Depends on the host SSRAM area size)
|
|
... ...
|
|
| |
|
|
+--------------------------------------------------+ <--Offset 0
|
|
|
|
Service VM gpa layout:
|
|
+--------------------------------------------------+ <--End of native high pci hole
|
|
| 64 bits vbar of emulated PCI devices |
|
|
+--------------------------------------------------+ <--Start of native high pci hole
|
|
| |
|
|
... ...
|
|
| |
|
|
+--------------------------------------------------+ <--End of native low pci hole
|
|
| 32 and 64 bits vbar of emulated PCI devices |
|
|
+--------------------------------------------------+ <--Start of native low pci hole
|
|
| |
|
|
... ...
|
|
| |
|
|
| |
|
|
| |
|
|
+--------------------------------------------------+ <--Offset 0
|
|
|
|
"""
|
|
def fn(board_etree, scenario_etree, allocation_etree):
|
|
allocate_ssram_region(board_etree, scenario_etree, allocation_etree)
|
|
allocate_log_area(board_etree, scenario_etree, allocation_etree)
|
|
allocate_pci_bar(board_etree, scenario_etree, allocation_etree)
|
|
allocate_io_port(board_etree, scenario_etree, allocation_etree)
|
|
pt_dev_io_port_passthrough(board_etree, scenario_etree, allocation_etree)
|