1*87252e1bSXiao Guangrong /* 2*87252e1bSXiao Guangrong * NVDIMM ACPI Implementation 3*87252e1bSXiao Guangrong * 4*87252e1bSXiao Guangrong * Copyright(C) 2015 Intel Corporation. 5*87252e1bSXiao Guangrong * 6*87252e1bSXiao Guangrong * Author: 7*87252e1bSXiao Guangrong * Xiao Guangrong <guangrong.xiao@linux.intel.com> 8*87252e1bSXiao Guangrong * 9*87252e1bSXiao Guangrong * NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) 10*87252e1bSXiao Guangrong * and the DSM specification can be found at: 11*87252e1bSXiao Guangrong * http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf 12*87252e1bSXiao Guangrong * 13*87252e1bSXiao Guangrong * Currently, it only supports PMEM Virtualization. 14*87252e1bSXiao Guangrong * 15*87252e1bSXiao Guangrong * This library is free software; you can redistribute it and/or 16*87252e1bSXiao Guangrong * modify it under the terms of the GNU Lesser General Public 17*87252e1bSXiao Guangrong * License as published by the Free Software Foundation; either 18*87252e1bSXiao Guangrong * version 2 of the License, or (at your option) any later version. 19*87252e1bSXiao Guangrong * 20*87252e1bSXiao Guangrong * This library is distributed in the hope that it will be useful, 21*87252e1bSXiao Guangrong * but WITHOUT ANY WARRANTY; without even the implied warranty of 22*87252e1bSXiao Guangrong * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23*87252e1bSXiao Guangrong * Lesser General Public License for more details. 24*87252e1bSXiao Guangrong * 25*87252e1bSXiao Guangrong * You should have received a copy of the GNU Lesser General Public 26*87252e1bSXiao Guangrong * License along with this library; if not, see <http://www.gnu.org/licenses/> 27*87252e1bSXiao Guangrong */ 28*87252e1bSXiao Guangrong 29*87252e1bSXiao Guangrong #include "hw/acpi/acpi.h" 30*87252e1bSXiao Guangrong #include "hw/acpi/aml-build.h" 31*87252e1bSXiao Guangrong #include "hw/mem/nvdimm.h" 32*87252e1bSXiao Guangrong 33*87252e1bSXiao Guangrong static int nvdimm_plugged_device_list(Object *obj, void *opaque) 34*87252e1bSXiao Guangrong { 35*87252e1bSXiao Guangrong GSList **list = opaque; 36*87252e1bSXiao Guangrong 37*87252e1bSXiao Guangrong if (object_dynamic_cast(obj, TYPE_NVDIMM)) { 38*87252e1bSXiao Guangrong DeviceState *dev = DEVICE(obj); 39*87252e1bSXiao Guangrong 40*87252e1bSXiao Guangrong if (dev->realized) { /* only realized NVDIMMs matter */ 41*87252e1bSXiao Guangrong *list = g_slist_append(*list, DEVICE(obj)); 42*87252e1bSXiao Guangrong } 43*87252e1bSXiao Guangrong } 44*87252e1bSXiao Guangrong 45*87252e1bSXiao Guangrong object_child_foreach(obj, nvdimm_plugged_device_list, opaque); 46*87252e1bSXiao Guangrong return 0; 47*87252e1bSXiao Guangrong } 48*87252e1bSXiao Guangrong 49*87252e1bSXiao Guangrong /* 50*87252e1bSXiao Guangrong * inquire plugged NVDIMM devices and link them into the list which is 51*87252e1bSXiao Guangrong * returned to the caller. 52*87252e1bSXiao Guangrong * 53*87252e1bSXiao Guangrong * Note: it is the caller's responsibility to free the list to avoid 54*87252e1bSXiao Guangrong * memory leak. 55*87252e1bSXiao Guangrong */ 56*87252e1bSXiao Guangrong static GSList *nvdimm_get_plugged_device_list(void) 57*87252e1bSXiao Guangrong { 58*87252e1bSXiao Guangrong GSList *list = NULL; 59*87252e1bSXiao Guangrong 60*87252e1bSXiao Guangrong object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list, 61*87252e1bSXiao Guangrong &list); 62*87252e1bSXiao Guangrong return list; 63*87252e1bSXiao Guangrong } 64*87252e1bSXiao Guangrong 65*87252e1bSXiao Guangrong #define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ 66*87252e1bSXiao Guangrong { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ 67*87252e1bSXiao Guangrong (b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \ 68*87252e1bSXiao Guangrong (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } 69*87252e1bSXiao Guangrong 70*87252e1bSXiao Guangrong /* 71*87252e1bSXiao Guangrong * define Byte Addressable Persistent Memory (PM) Region according to 72*87252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure. 73*87252e1bSXiao Guangrong */ 74*87252e1bSXiao Guangrong static const uint8_t nvdimm_nfit_spa_uuid[] = 75*87252e1bSXiao Guangrong NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, 76*87252e1bSXiao Guangrong 0x18, 0xb7, 0x8c, 0xdb); 77*87252e1bSXiao Guangrong 78*87252e1bSXiao Guangrong /* 79*87252e1bSXiao Guangrong * NVDIMM Firmware Interface Table 80*87252e1bSXiao Guangrong * @signature: "NFIT" 81*87252e1bSXiao Guangrong * 82*87252e1bSXiao Guangrong * It provides information that allows OSPM to enumerate NVDIMM present in 83*87252e1bSXiao Guangrong * the platform and associate system physical address ranges created by the 84*87252e1bSXiao Guangrong * NVDIMMs. 85*87252e1bSXiao Guangrong * 86*87252e1bSXiao Guangrong * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) 87*87252e1bSXiao Guangrong */ 88*87252e1bSXiao Guangrong struct NvdimmNfitHeader { 89*87252e1bSXiao Guangrong ACPI_TABLE_HEADER_DEF 90*87252e1bSXiao Guangrong uint32_t reserved; 91*87252e1bSXiao Guangrong } QEMU_PACKED; 92*87252e1bSXiao Guangrong typedef struct NvdimmNfitHeader NvdimmNfitHeader; 93*87252e1bSXiao Guangrong 94*87252e1bSXiao Guangrong /* 95*87252e1bSXiao Guangrong * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware 96*87252e1bSXiao Guangrong * Interface Table (NFIT). 97*87252e1bSXiao Guangrong */ 98*87252e1bSXiao Guangrong 99*87252e1bSXiao Guangrong /* 100*87252e1bSXiao Guangrong * System Physical Address Range Structure 101*87252e1bSXiao Guangrong * 102*87252e1bSXiao Guangrong * It describes the system physical address ranges occupied by NVDIMMs and 103*87252e1bSXiao Guangrong * the types of the regions. 104*87252e1bSXiao Guangrong */ 105*87252e1bSXiao Guangrong struct NvdimmNfitSpa { 106*87252e1bSXiao Guangrong uint16_t type; 107*87252e1bSXiao Guangrong uint16_t length; 108*87252e1bSXiao Guangrong uint16_t spa_index; 109*87252e1bSXiao Guangrong uint16_t flags; 110*87252e1bSXiao Guangrong uint32_t reserved; 111*87252e1bSXiao Guangrong uint32_t proximity_domain; 112*87252e1bSXiao Guangrong uint8_t type_guid[16]; 113*87252e1bSXiao Guangrong uint64_t spa_base; 114*87252e1bSXiao Guangrong uint64_t spa_length; 115*87252e1bSXiao Guangrong uint64_t mem_attr; 116*87252e1bSXiao Guangrong } QEMU_PACKED; 117*87252e1bSXiao Guangrong typedef struct NvdimmNfitSpa NvdimmNfitSpa; 118*87252e1bSXiao Guangrong 119*87252e1bSXiao Guangrong /* 120*87252e1bSXiao Guangrong * Memory Device to System Physical Address Range Mapping Structure 121*87252e1bSXiao Guangrong * 122*87252e1bSXiao Guangrong * It enables identifying each NVDIMM region and the corresponding SPA 123*87252e1bSXiao Guangrong * describing the memory interleave 124*87252e1bSXiao Guangrong */ 125*87252e1bSXiao Guangrong struct NvdimmNfitMemDev { 126*87252e1bSXiao Guangrong uint16_t type; 127*87252e1bSXiao Guangrong uint16_t length; 128*87252e1bSXiao Guangrong uint32_t nfit_handle; 129*87252e1bSXiao Guangrong uint16_t phys_id; 130*87252e1bSXiao Guangrong uint16_t region_id; 131*87252e1bSXiao Guangrong uint16_t spa_index; 132*87252e1bSXiao Guangrong uint16_t dcr_index; 133*87252e1bSXiao Guangrong uint64_t region_len; 134*87252e1bSXiao Guangrong uint64_t region_offset; 135*87252e1bSXiao Guangrong uint64_t region_dpa; 136*87252e1bSXiao Guangrong uint16_t interleave_index; 137*87252e1bSXiao Guangrong uint16_t interleave_ways; 138*87252e1bSXiao Guangrong uint16_t flags; 139*87252e1bSXiao Guangrong uint16_t reserved; 140*87252e1bSXiao Guangrong } QEMU_PACKED; 141*87252e1bSXiao Guangrong typedef struct NvdimmNfitMemDev NvdimmNfitMemDev; 142*87252e1bSXiao Guangrong 143*87252e1bSXiao Guangrong /* 144*87252e1bSXiao Guangrong * NVDIMM Control Region Structure 145*87252e1bSXiao Guangrong * 146*87252e1bSXiao Guangrong * It describes the NVDIMM and if applicable, Block Control Window. 147*87252e1bSXiao Guangrong */ 148*87252e1bSXiao Guangrong struct NvdimmNfitControlRegion { 149*87252e1bSXiao Guangrong uint16_t type; 150*87252e1bSXiao Guangrong uint16_t length; 151*87252e1bSXiao Guangrong uint16_t dcr_index; 152*87252e1bSXiao Guangrong uint16_t vendor_id; 153*87252e1bSXiao Guangrong uint16_t device_id; 154*87252e1bSXiao Guangrong uint16_t revision_id; 155*87252e1bSXiao Guangrong uint16_t sub_vendor_id; 156*87252e1bSXiao Guangrong uint16_t sub_device_id; 157*87252e1bSXiao Guangrong uint16_t sub_revision_id; 158*87252e1bSXiao Guangrong uint8_t reserved[6]; 159*87252e1bSXiao Guangrong uint32_t serial_number; 160*87252e1bSXiao Guangrong uint16_t fic; 161*87252e1bSXiao Guangrong uint16_t num_bcw; 162*87252e1bSXiao Guangrong uint64_t bcw_size; 163*87252e1bSXiao Guangrong uint64_t cmd_offset; 164*87252e1bSXiao Guangrong uint64_t cmd_size; 165*87252e1bSXiao Guangrong uint64_t status_offset; 166*87252e1bSXiao Guangrong uint64_t status_size; 167*87252e1bSXiao Guangrong uint16_t flags; 168*87252e1bSXiao Guangrong uint8_t reserved2[6]; 169*87252e1bSXiao Guangrong } QEMU_PACKED; 170*87252e1bSXiao Guangrong typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; 171*87252e1bSXiao Guangrong 172*87252e1bSXiao Guangrong /* 173*87252e1bSXiao Guangrong * Module serial number is a unique number for each device. We use the 174*87252e1bSXiao Guangrong * slot id of NVDIMM device to generate this number so that each device 175*87252e1bSXiao Guangrong * associates with a different number. 176*87252e1bSXiao Guangrong * 177*87252e1bSXiao Guangrong * 0x123456 is a magic number we arbitrarily chose. 178*87252e1bSXiao Guangrong */ 179*87252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_sn(int slot) 180*87252e1bSXiao Guangrong { 181*87252e1bSXiao Guangrong return 0x123456 + slot; 182*87252e1bSXiao Guangrong } 183*87252e1bSXiao Guangrong 184*87252e1bSXiao Guangrong /* 185*87252e1bSXiao Guangrong * handle is used to uniquely associate nfit_memdev structure with NVDIMM 186*87252e1bSXiao Guangrong * ACPI device - nfit_memdev.nfit_handle matches with the value returned 187*87252e1bSXiao Guangrong * by ACPI device _ADR method. 188*87252e1bSXiao Guangrong * 189*87252e1bSXiao Guangrong * We generate the handle with the slot id of NVDIMM device and reserve 190*87252e1bSXiao Guangrong * 0 for NVDIMM root device. 191*87252e1bSXiao Guangrong */ 192*87252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_handle(int slot) 193*87252e1bSXiao Guangrong { 194*87252e1bSXiao Guangrong return slot + 1; 195*87252e1bSXiao Guangrong } 196*87252e1bSXiao Guangrong 197*87252e1bSXiao Guangrong /* 198*87252e1bSXiao Guangrong * index uniquely identifies the structure, 0 is reserved which indicates 199*87252e1bSXiao Guangrong * that the structure is not valid or the associated structure is not 200*87252e1bSXiao Guangrong * present. 201*87252e1bSXiao Guangrong * 202*87252e1bSXiao Guangrong * Each NVDIMM device needs two indexes, one for nfit_spa and another for 203*87252e1bSXiao Guangrong * nfit_dc which are generated by the slot id of NVDIMM device. 204*87252e1bSXiao Guangrong */ 205*87252e1bSXiao Guangrong static uint16_t nvdimm_slot_to_spa_index(int slot) 206*87252e1bSXiao Guangrong { 207*87252e1bSXiao Guangrong return (slot + 1) << 1; 208*87252e1bSXiao Guangrong } 209*87252e1bSXiao Guangrong 210*87252e1bSXiao Guangrong /* See the comments of nvdimm_slot_to_spa_index(). */ 211*87252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_dcr_index(int slot) 212*87252e1bSXiao Guangrong { 213*87252e1bSXiao Guangrong return nvdimm_slot_to_spa_index(slot) + 1; 214*87252e1bSXiao Guangrong } 215*87252e1bSXiao Guangrong 216*87252e1bSXiao Guangrong /* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */ 217*87252e1bSXiao Guangrong static void 218*87252e1bSXiao Guangrong nvdimm_build_structure_spa(GArray *structures, DeviceState *dev) 219*87252e1bSXiao Guangrong { 220*87252e1bSXiao Guangrong NvdimmNfitSpa *nfit_spa; 221*87252e1bSXiao Guangrong uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, 222*87252e1bSXiao Guangrong NULL); 223*87252e1bSXiao Guangrong uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, 224*87252e1bSXiao Guangrong NULL); 225*87252e1bSXiao Guangrong uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, 226*87252e1bSXiao Guangrong NULL); 227*87252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 228*87252e1bSXiao Guangrong NULL); 229*87252e1bSXiao Guangrong 230*87252e1bSXiao Guangrong nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa)); 231*87252e1bSXiao Guangrong 232*87252e1bSXiao Guangrong nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range 233*87252e1bSXiao Guangrong Structure */); 234*87252e1bSXiao Guangrong nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa)); 235*87252e1bSXiao Guangrong nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 236*87252e1bSXiao Guangrong 237*87252e1bSXiao Guangrong /* 238*87252e1bSXiao Guangrong * Control region is strict as all the device info, such as SN, index, 239*87252e1bSXiao Guangrong * is associated with slot id. 240*87252e1bSXiao Guangrong */ 241*87252e1bSXiao Guangrong nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for 242*87252e1bSXiao Guangrong management during hot add/online 243*87252e1bSXiao Guangrong operation */ | 244*87252e1bSXiao Guangrong 2 /* Data in Proximity Domain field is 245*87252e1bSXiao Guangrong valid*/); 246*87252e1bSXiao Guangrong 247*87252e1bSXiao Guangrong /* NUMA node. */ 248*87252e1bSXiao Guangrong nfit_spa->proximity_domain = cpu_to_le32(node); 249*87252e1bSXiao Guangrong /* the region reported as PMEM. */ 250*87252e1bSXiao Guangrong memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid, 251*87252e1bSXiao Guangrong sizeof(nvdimm_nfit_spa_uuid)); 252*87252e1bSXiao Guangrong 253*87252e1bSXiao Guangrong nfit_spa->spa_base = cpu_to_le64(addr); 254*87252e1bSXiao Guangrong nfit_spa->spa_length = cpu_to_le64(size); 255*87252e1bSXiao Guangrong 256*87252e1bSXiao Guangrong /* It is the PMEM and can be cached as writeback. */ 257*87252e1bSXiao Guangrong nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ | 258*87252e1bSXiao Guangrong 0x8000ULL /* EFI_MEMORY_NV */); 259*87252e1bSXiao Guangrong } 260*87252e1bSXiao Guangrong 261*87252e1bSXiao Guangrong /* 262*87252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping 263*87252e1bSXiao Guangrong * Structure 264*87252e1bSXiao Guangrong */ 265*87252e1bSXiao Guangrong static void 266*87252e1bSXiao Guangrong nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev) 267*87252e1bSXiao Guangrong { 268*87252e1bSXiao Guangrong NvdimmNfitMemDev *nfit_memdev; 269*87252e1bSXiao Guangrong uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, 270*87252e1bSXiao Guangrong NULL); 271*87252e1bSXiao Guangrong uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, 272*87252e1bSXiao Guangrong NULL); 273*87252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 274*87252e1bSXiao Guangrong NULL); 275*87252e1bSXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 276*87252e1bSXiao Guangrong 277*87252e1bSXiao Guangrong nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev)); 278*87252e1bSXiao Guangrong 279*87252e1bSXiao Guangrong nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address 280*87252e1bSXiao Guangrong Range Map Structure*/); 281*87252e1bSXiao Guangrong nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev)); 282*87252e1bSXiao Guangrong nfit_memdev->nfit_handle = cpu_to_le32(handle); 283*87252e1bSXiao Guangrong 284*87252e1bSXiao Guangrong /* 285*87252e1bSXiao Guangrong * associate memory device with System Physical Address Range 286*87252e1bSXiao Guangrong * Structure. 287*87252e1bSXiao Guangrong */ 288*87252e1bSXiao Guangrong nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 289*87252e1bSXiao Guangrong /* associate memory device with Control Region Structure. */ 290*87252e1bSXiao Guangrong nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 291*87252e1bSXiao Guangrong 292*87252e1bSXiao Guangrong /* The memory region on the device. */ 293*87252e1bSXiao Guangrong nfit_memdev->region_len = cpu_to_le64(size); 294*87252e1bSXiao Guangrong nfit_memdev->region_dpa = cpu_to_le64(addr); 295*87252e1bSXiao Guangrong 296*87252e1bSXiao Guangrong /* Only one interleave for PMEM. */ 297*87252e1bSXiao Guangrong nfit_memdev->interleave_ways = cpu_to_le16(1); 298*87252e1bSXiao Guangrong } 299*87252e1bSXiao Guangrong 300*87252e1bSXiao Guangrong /* 301*87252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure. 302*87252e1bSXiao Guangrong */ 303*87252e1bSXiao Guangrong static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) 304*87252e1bSXiao Guangrong { 305*87252e1bSXiao Guangrong NvdimmNfitControlRegion *nfit_dcr; 306*87252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 307*87252e1bSXiao Guangrong NULL); 308*87252e1bSXiao Guangrong uint32_t sn = nvdimm_slot_to_sn(slot); 309*87252e1bSXiao Guangrong 310*87252e1bSXiao Guangrong nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr)); 311*87252e1bSXiao Guangrong 312*87252e1bSXiao Guangrong nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */); 313*87252e1bSXiao Guangrong nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr)); 314*87252e1bSXiao Guangrong nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 315*87252e1bSXiao Guangrong 316*87252e1bSXiao Guangrong /* vendor: Intel. */ 317*87252e1bSXiao Guangrong nfit_dcr->vendor_id = cpu_to_le16(0x8086); 318*87252e1bSXiao Guangrong nfit_dcr->device_id = cpu_to_le16(1); 319*87252e1bSXiao Guangrong 320*87252e1bSXiao Guangrong /* The _DSM method is following Intel's DSM specification. */ 321*87252e1bSXiao Guangrong nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported 322*87252e1bSXiao Guangrong in ACPI 6.0 is 1. */); 323*87252e1bSXiao Guangrong nfit_dcr->serial_number = cpu_to_le32(sn); 324*87252e1bSXiao Guangrong nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter 325*87252e1bSXiao Guangrong 2: NVDIMM Device Specific Method 326*87252e1bSXiao Guangrong (DSM) in DSM Spec Rev1.*/); 327*87252e1bSXiao Guangrong } 328*87252e1bSXiao Guangrong 329*87252e1bSXiao Guangrong static GArray *nvdimm_build_device_structure(GSList *device_list) 330*87252e1bSXiao Guangrong { 331*87252e1bSXiao Guangrong GArray *structures = g_array_new(false, true /* clear */, 1); 332*87252e1bSXiao Guangrong 333*87252e1bSXiao Guangrong for (; device_list; device_list = device_list->next) { 334*87252e1bSXiao Guangrong DeviceState *dev = device_list->data; 335*87252e1bSXiao Guangrong 336*87252e1bSXiao Guangrong /* build System Physical Address Range Structure. */ 337*87252e1bSXiao Guangrong nvdimm_build_structure_spa(structures, dev); 338*87252e1bSXiao Guangrong 339*87252e1bSXiao Guangrong /* 340*87252e1bSXiao Guangrong * build Memory Device to System Physical Address Range Mapping 341*87252e1bSXiao Guangrong * Structure. 342*87252e1bSXiao Guangrong */ 343*87252e1bSXiao Guangrong nvdimm_build_structure_memdev(structures, dev); 344*87252e1bSXiao Guangrong 345*87252e1bSXiao Guangrong /* build NVDIMM Control Region Structure. */ 346*87252e1bSXiao Guangrong nvdimm_build_structure_dcr(structures, dev); 347*87252e1bSXiao Guangrong } 348*87252e1bSXiao Guangrong 349*87252e1bSXiao Guangrong return structures; 350*87252e1bSXiao Guangrong } 351*87252e1bSXiao Guangrong 352*87252e1bSXiao Guangrong static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets, 353*87252e1bSXiao Guangrong GArray *table_data, GArray *linker) 354*87252e1bSXiao Guangrong { 355*87252e1bSXiao Guangrong GArray *structures = nvdimm_build_device_structure(device_list); 356*87252e1bSXiao Guangrong void *header; 357*87252e1bSXiao Guangrong 358*87252e1bSXiao Guangrong acpi_add_table(table_offsets, table_data); 359*87252e1bSXiao Guangrong 360*87252e1bSXiao Guangrong /* NFIT header. */ 361*87252e1bSXiao Guangrong header = acpi_data_push(table_data, sizeof(NvdimmNfitHeader)); 362*87252e1bSXiao Guangrong /* NVDIMM device structures. */ 363*87252e1bSXiao Guangrong g_array_append_vals(table_data, structures->data, structures->len); 364*87252e1bSXiao Guangrong 365*87252e1bSXiao Guangrong build_header(linker, table_data, header, "NFIT", 366*87252e1bSXiao Guangrong sizeof(NvdimmNfitHeader) + structures->len, 1, NULL); 367*87252e1bSXiao Guangrong g_array_free(structures, true); 368*87252e1bSXiao Guangrong } 369*87252e1bSXiao Guangrong 370*87252e1bSXiao Guangrong void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, 371*87252e1bSXiao Guangrong GArray *linker) 372*87252e1bSXiao Guangrong { 373*87252e1bSXiao Guangrong GSList *device_list; 374*87252e1bSXiao Guangrong 375*87252e1bSXiao Guangrong /* no NVDIMM device is plugged. */ 376*87252e1bSXiao Guangrong device_list = nvdimm_get_plugged_device_list(); 377*87252e1bSXiao Guangrong if (!device_list) { 378*87252e1bSXiao Guangrong return; 379*87252e1bSXiao Guangrong } 380*87252e1bSXiao Guangrong nvdimm_build_nfit(device_list, table_offsets, table_data, linker); 381*87252e1bSXiao Guangrong g_slist_free(device_list); 382*87252e1bSXiao Guangrong } 383