1#!/usr/bin/env python3 2# 3# Copyright (C) 2021-2022 Intel Corporation. 4# 5# SPDX-License-Identifier: BSD-3-Clause 6# 7 8import sys, os, re 9sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library')) 10import acrn_config_utilities, lib.error, lib.lib 11from acrn_config_utilities import get_node 12 13# Constants for device name prefix 14IVSHMEM = "IVSHMEM" 15VUART = "VUART" 16 17# Exception bdf list 18# Some hardware drivers' bdf is hardcoded, the bdf cannot be changed even it is passtrhough devices. 19HARDCODED_BDF_LIST = ["00:0e.0"] 20 21def find_unused_bdf(used_bdf): 22 # never assign 0:00.0 to any emulated devices, it's reserved for pci hostbridge 23 for dev in range(0x1, 0x20): 24 bdf = lib.lib.BusDevFunc(bus=0x00, dev=dev, func=0x0) 25 if all((bdf.dev != in_use_bdf.dev for in_use_bdf in used_bdf)): 26 return bdf 27 raise lib.error.ResourceError(f"Cannot find free bdf, used bdf: {sorted(used_bdf)}") 28 29def insert_vuart_to_dev_dict(scenario_etree, devdict, used): 30 console_vuart = scenario_etree.xpath(f"./console_vuart[base != 'INVALID_PCI_BASE']/@id") 31 for vuart_id in console_vuart: 32 free_bdf = find_unused_bdf(used) 33 devdict[f"{VUART}_{vuart_id}"] = free_bdf 34 used.append(free_bdf) 35 36def insert_ivsheme_to_dev_dict(scenario_etree, devdict, vm_id, used): 37 shmem_regions = lib.lib.get_ivshmem_regions_by_tree(scenario_etree) 38 if vm_id not in shmem_regions: 39 return 40 shmems = shmem_regions.get(vm_id) 41 for shm in shmems.values(): 42 bdf = lib.lib.BusDevFunc.from_str(shm.get('vbdf')) 43 devdict[f"{IVSHMEM}_{shm.get('id')}"] = bdf 44 used.append(bdf) 45 46def insert_pt_devs_to_dev_dict(vm_node_etree, devdict, used): 47 """ 48 Assign an unused bdf to each of passtrhough devices. 49 If a passtrhough device's bdf is in the list of HARDCODED_BDF_LIST, this device should apply the same bdf as native one. 50 Calls find_unused_bdf to assign an unused bdf for the rest of passtrhough devices except the ones in HARDCODED_BDF_LIST. 51 """ 52 pt_devs = vm_node_etree.xpath(f".//pci_dev/text()") 53 # assign the bdf of the devices in HARDCODED_BDF_LIST 54 for pt_dev in pt_devs: 55 bdf_string = pt_dev.split()[0] 56 if bdf_string in HARDCODED_BDF_LIST: 57 bdf = lib.lib.BusDevFunc.from_str(bdf_string) 58 dev_name = str(bdf) 59 devdict[dev_name] = bdf 60 used.append(bdf) 61 62 # remove the pt_dev nodes which are in HARDCODED_BDF_LIST 63 pt_devs = [pt_dev for pt_dev in pt_devs if lib.lib.BusDevFunc.from_str(bdf_string) not in used] 64 65 # call find_unused_bdf to assign an unused bdf for other passthrough devices except the ones in HARDCODED_BDF_LIST 66 for pt_dev in pt_devs: 67 bdf = lib.lib.BusDevFunc.from_str(pt_dev.split()[0]) 68 free_bdf = find_unused_bdf(used) 69 dev_name = str(bdf) 70 devdict[dev_name] = free_bdf 71 used.append(free_bdf) 72 73def get_devs_bdf_native(board_etree): 74 """ 75 Get all pci devices' bdf in native environment. 76 return: list of pci devices' bdf 77 """ 78 nodes = board_etree.xpath(f"//bus[@type = 'pci' and @address = '0x0']/device[@address]") 79 dev_list = [] 80 for node in nodes: 81 address = node.get('address') 82 bus = int(get_node("../@address", node), 16) 83 dev = int(address, 16) >> 16 84 func = int(address, 16) & 0xffff 85 86 # According to section 6.1.1, ACPI Spec 6.4, _ADR of a device object under PCI/PCIe bus can use a special 87 # function number 0xFFFF to refer to all functions of a certain device. Such objects will have their own nodes 88 # in the board XML, but are out of the scope here as we are only interested in concrete BDFs that are already 89 # occupied. 90 # 91 # Thus, if the function number is 0xffff, we simply skip it. 92 if func != 0xffff: 93 dev_list.append(lib.lib.BusDevFunc(bus = bus, dev = dev, func = func)) 94 return dev_list 95 96def get_devs_bdf_passthrough(scenario_etree): 97 """ 98 Get all pre-launched vms' passthrough devices' bdf in native environment. 99 return: list of passtrhough devices' bdf. 100 """ 101 dev_list = [] 102 pt_devs = scenario_etree.xpath(f"//vm[load_order = 'PRE_LAUNCHED_VM']/pci_devs/pci_dev/text()") 103 for pt_dev in pt_devs: 104 bdf = lib.lib.BusDevFunc.from_str(pt_dev.split()[0]) 105 dev_list.append(bdf) 106 return dev_list 107 108def create_device_node(allocation_etree, vm_id, devdict): 109 for dev in devdict: 110 dev_name = dev 111 bdf = devdict.get(dev) 112 vm_node = get_node(f"/acrn-config/vm[@id = '{vm_id}']", allocation_etree) 113 if vm_node is None: 114 vm_node = acrn_config_utilities.append_node("/acrn-config/vm", None, allocation_etree, id = vm_id) 115 dev_node = get_node(f"./device[@name = '{dev_name}']", vm_node) 116 if dev_node is None: 117 dev_node = acrn_config_utilities.append_node("./device", None, vm_node, name = dev_name) 118 if get_node(f"./bus", dev_node) is None: 119 acrn_config_utilities.append_node(f"./bus", f"{bdf.bus:#04x}", dev_node) 120 if get_node(f"./dev", dev_node) is None: 121 acrn_config_utilities.append_node(f"./dev", f"{bdf.dev:#04x}", dev_node) 122 if get_node(f"./func", dev_node) is None: 123 acrn_config_utilities.append_node(f"./func", f"{bdf.func:#04x}", dev_node) 124 125def create_igd_sbdf(board_etree, allocation_etree): 126 """ 127 Extract the integrated GPU bdf from board.xml. If the device is not present, set bdf to "0xFFFF" which indicates the device 128 doesn't exist. 129 """ 130 bus = "0x0" 131 device_node = get_node(f"//bus[@type='pci' and @address='{bus}']/device[@address='0x20000' and vendor='0x8086' and class='0x030000']", board_etree) 132 if device_node is None: 133 acrn_config_utilities.append_node("/acrn-config/hv/MISC_CFG/IGD_SBDF", '0xFFFF', allocation_etree) 134 else: 135 address = device_node.get('address') 136 dev = int(address, 16) >> 16 137 func = int(address, 16) & 0xffff 138 acrn_config_utilities.append_node("/acrn-config/hv/MISC_CFG/IGD_SBDF", f"{(int(bus, 16) << 8) | (dev << 3) | func:#06x}", allocation_etree) 139 140def fn(board_etree, scenario_etree, allocation_etree): 141 create_igd_sbdf(board_etree, allocation_etree) 142 vm_nodes = scenario_etree.xpath("//vm") 143 for vm_node in vm_nodes: 144 vm_id = vm_node.get('id') 145 devdict = {} 146 used = [] 147 load_order = get_node("./load_order/text()", vm_node) 148 if load_order is not None and lib.lib.is_post_launched_vm(load_order): 149 continue 150 151 if load_order is not None and lib.lib.is_service_vm(load_order): 152 native_used = get_devs_bdf_native(board_etree) 153 passthrough_used = get_devs_bdf_passthrough(scenario_etree) 154 used = [bdf for bdf in native_used if bdf not in passthrough_used] 155 if get_node("//@board", scenario_etree) == "tgl-rvp": 156 used.append(lib.lib.BusDevFunc(bus = 0, dev = 1, func = 0)) 157 158 insert_vuart_to_dev_dict(vm_node, devdict, used) 159 insert_ivsheme_to_dev_dict(scenario_etree, devdict, vm_id, used) 160 insert_pt_devs_to_dev_dict(vm_node, devdict, used) 161 create_device_node(allocation_etree, vm_id, devdict) 162