187252e1bSXiao Guangrong /* 287252e1bSXiao Guangrong * NVDIMM ACPI Implementation 387252e1bSXiao Guangrong * 487252e1bSXiao Guangrong * Copyright(C) 2015 Intel Corporation. 587252e1bSXiao Guangrong * 687252e1bSXiao Guangrong * Author: 787252e1bSXiao Guangrong * Xiao Guangrong <guangrong.xiao@linux.intel.com> 887252e1bSXiao Guangrong * 987252e1bSXiao Guangrong * NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) 1087252e1bSXiao Guangrong * and the DSM specification can be found at: 1187252e1bSXiao Guangrong * http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf 1287252e1bSXiao Guangrong * 1387252e1bSXiao Guangrong * Currently, it only supports PMEM Virtualization. 1487252e1bSXiao Guangrong * 1587252e1bSXiao Guangrong * This library is free software; you can redistribute it and/or 1687252e1bSXiao Guangrong * modify it under the terms of the GNU Lesser General Public 1787252e1bSXiao Guangrong * License as published by the Free Software Foundation; either 1887252e1bSXiao Guangrong * version 2 of the License, or (at your option) any later version. 1987252e1bSXiao Guangrong * 2087252e1bSXiao Guangrong * This library is distributed in the hope that it will be useful, 2187252e1bSXiao Guangrong * but WITHOUT ANY WARRANTY; without even the implied warranty of 2287252e1bSXiao Guangrong * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2387252e1bSXiao Guangrong * Lesser General Public License for more details. 2487252e1bSXiao Guangrong * 2587252e1bSXiao Guangrong * You should have received a copy of the GNU Lesser General Public 2687252e1bSXiao Guangrong * License along with this library; if not, see <http://www.gnu.org/licenses/> 2787252e1bSXiao Guangrong */ 2887252e1bSXiao Guangrong 29b6a0aa05SPeter Maydell #include "qemu/osdep.h" 3087252e1bSXiao Guangrong #include "hw/acpi/acpi.h" 3187252e1bSXiao Guangrong #include "hw/acpi/aml-build.h" 32b9951413SXiao Guangrong #include "hw/acpi/bios-linker-loader.h" 335fe79386SXiao Guangrong #include "hw/nvram/fw_cfg.h" 3487252e1bSXiao Guangrong #include "hw/mem/nvdimm.h" 3587252e1bSXiao Guangrong 3687252e1bSXiao Guangrong static int nvdimm_plugged_device_list(Object *obj, void *opaque) 3787252e1bSXiao Guangrong { 3887252e1bSXiao Guangrong GSList **list = opaque; 3987252e1bSXiao Guangrong 4087252e1bSXiao Guangrong if (object_dynamic_cast(obj, TYPE_NVDIMM)) { 4187252e1bSXiao Guangrong DeviceState *dev = DEVICE(obj); 4287252e1bSXiao Guangrong 4387252e1bSXiao Guangrong if (dev->realized) { /* only realized NVDIMMs matter */ 4487252e1bSXiao Guangrong *list = g_slist_append(*list, DEVICE(obj)); 4587252e1bSXiao Guangrong } 4687252e1bSXiao Guangrong } 4787252e1bSXiao Guangrong 4887252e1bSXiao Guangrong object_child_foreach(obj, nvdimm_plugged_device_list, opaque); 4987252e1bSXiao Guangrong return 0; 5087252e1bSXiao Guangrong } 5187252e1bSXiao Guangrong 5287252e1bSXiao Guangrong /* 5387252e1bSXiao Guangrong * inquire plugged NVDIMM devices and link them into the list which is 5487252e1bSXiao Guangrong * returned to the caller. 5587252e1bSXiao Guangrong * 5687252e1bSXiao Guangrong * Note: it is the caller's responsibility to free the list to avoid 5787252e1bSXiao Guangrong * memory leak. 5887252e1bSXiao Guangrong */ 5987252e1bSXiao Guangrong static GSList *nvdimm_get_plugged_device_list(void) 6087252e1bSXiao Guangrong { 6187252e1bSXiao Guangrong GSList *list = NULL; 6287252e1bSXiao Guangrong 6387252e1bSXiao Guangrong object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list, 6487252e1bSXiao Guangrong &list); 6587252e1bSXiao Guangrong return list; 6687252e1bSXiao Guangrong } 6787252e1bSXiao Guangrong 6887252e1bSXiao Guangrong #define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ 6987252e1bSXiao Guangrong { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ 7087252e1bSXiao Guangrong (b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \ 7187252e1bSXiao Guangrong (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } 7287252e1bSXiao Guangrong 7387252e1bSXiao Guangrong /* 7487252e1bSXiao Guangrong * define Byte Addressable Persistent Memory (PM) Region according to 7587252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure. 7687252e1bSXiao Guangrong */ 7787252e1bSXiao Guangrong static const uint8_t nvdimm_nfit_spa_uuid[] = 7887252e1bSXiao Guangrong NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, 7987252e1bSXiao Guangrong 0x18, 0xb7, 0x8c, 0xdb); 8087252e1bSXiao Guangrong 8187252e1bSXiao Guangrong /* 8287252e1bSXiao Guangrong * NVDIMM Firmware Interface Table 8387252e1bSXiao Guangrong * @signature: "NFIT" 8487252e1bSXiao Guangrong * 8587252e1bSXiao Guangrong * It provides information that allows OSPM to enumerate NVDIMM present in 8687252e1bSXiao Guangrong * the platform and associate system physical address ranges created by the 8787252e1bSXiao Guangrong * NVDIMMs. 8887252e1bSXiao Guangrong * 8987252e1bSXiao Guangrong * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) 9087252e1bSXiao Guangrong */ 9187252e1bSXiao Guangrong struct NvdimmNfitHeader { 9287252e1bSXiao Guangrong ACPI_TABLE_HEADER_DEF 9387252e1bSXiao Guangrong uint32_t reserved; 9487252e1bSXiao Guangrong } QEMU_PACKED; 9587252e1bSXiao Guangrong typedef struct NvdimmNfitHeader NvdimmNfitHeader; 9687252e1bSXiao Guangrong 9787252e1bSXiao Guangrong /* 9887252e1bSXiao Guangrong * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware 9987252e1bSXiao Guangrong * Interface Table (NFIT). 10087252e1bSXiao Guangrong */ 10187252e1bSXiao Guangrong 10287252e1bSXiao Guangrong /* 10387252e1bSXiao Guangrong * System Physical Address Range Structure 10487252e1bSXiao Guangrong * 10587252e1bSXiao Guangrong * It describes the system physical address ranges occupied by NVDIMMs and 10687252e1bSXiao Guangrong * the types of the regions. 10787252e1bSXiao Guangrong */ 10887252e1bSXiao Guangrong struct NvdimmNfitSpa { 10987252e1bSXiao Guangrong uint16_t type; 11087252e1bSXiao Guangrong uint16_t length; 11187252e1bSXiao Guangrong uint16_t spa_index; 11287252e1bSXiao Guangrong uint16_t flags; 11387252e1bSXiao Guangrong uint32_t reserved; 11487252e1bSXiao Guangrong uint32_t proximity_domain; 11587252e1bSXiao Guangrong uint8_t type_guid[16]; 11687252e1bSXiao Guangrong uint64_t spa_base; 11787252e1bSXiao Guangrong uint64_t spa_length; 11887252e1bSXiao Guangrong uint64_t mem_attr; 11987252e1bSXiao Guangrong } QEMU_PACKED; 12087252e1bSXiao Guangrong typedef struct NvdimmNfitSpa NvdimmNfitSpa; 12187252e1bSXiao Guangrong 12287252e1bSXiao Guangrong /* 12387252e1bSXiao Guangrong * Memory Device to System Physical Address Range Mapping Structure 12487252e1bSXiao Guangrong * 12587252e1bSXiao Guangrong * It enables identifying each NVDIMM region and the corresponding SPA 12687252e1bSXiao Guangrong * describing the memory interleave 12787252e1bSXiao Guangrong */ 12887252e1bSXiao Guangrong struct NvdimmNfitMemDev { 12987252e1bSXiao Guangrong uint16_t type; 13087252e1bSXiao Guangrong uint16_t length; 13187252e1bSXiao Guangrong uint32_t nfit_handle; 13287252e1bSXiao Guangrong uint16_t phys_id; 13387252e1bSXiao Guangrong uint16_t region_id; 13487252e1bSXiao Guangrong uint16_t spa_index; 13587252e1bSXiao Guangrong uint16_t dcr_index; 13687252e1bSXiao Guangrong uint64_t region_len; 13787252e1bSXiao Guangrong uint64_t region_offset; 13887252e1bSXiao Guangrong uint64_t region_dpa; 13987252e1bSXiao Guangrong uint16_t interleave_index; 14087252e1bSXiao Guangrong uint16_t interleave_ways; 14187252e1bSXiao Guangrong uint16_t flags; 14287252e1bSXiao Guangrong uint16_t reserved; 14387252e1bSXiao Guangrong } QEMU_PACKED; 14487252e1bSXiao Guangrong typedef struct NvdimmNfitMemDev NvdimmNfitMemDev; 14587252e1bSXiao Guangrong 14687252e1bSXiao Guangrong /* 14787252e1bSXiao Guangrong * NVDIMM Control Region Structure 14887252e1bSXiao Guangrong * 14987252e1bSXiao Guangrong * It describes the NVDIMM and if applicable, Block Control Window. 15087252e1bSXiao Guangrong */ 15187252e1bSXiao Guangrong struct NvdimmNfitControlRegion { 15287252e1bSXiao Guangrong uint16_t type; 15387252e1bSXiao Guangrong uint16_t length; 15487252e1bSXiao Guangrong uint16_t dcr_index; 15587252e1bSXiao Guangrong uint16_t vendor_id; 15687252e1bSXiao Guangrong uint16_t device_id; 15787252e1bSXiao Guangrong uint16_t revision_id; 15887252e1bSXiao Guangrong uint16_t sub_vendor_id; 15987252e1bSXiao Guangrong uint16_t sub_device_id; 16087252e1bSXiao Guangrong uint16_t sub_revision_id; 16187252e1bSXiao Guangrong uint8_t reserved[6]; 16287252e1bSXiao Guangrong uint32_t serial_number; 16387252e1bSXiao Guangrong uint16_t fic; 16487252e1bSXiao Guangrong uint16_t num_bcw; 16587252e1bSXiao Guangrong uint64_t bcw_size; 16687252e1bSXiao Guangrong uint64_t cmd_offset; 16787252e1bSXiao Guangrong uint64_t cmd_size; 16887252e1bSXiao Guangrong uint64_t status_offset; 16987252e1bSXiao Guangrong uint64_t status_size; 17087252e1bSXiao Guangrong uint16_t flags; 17187252e1bSXiao Guangrong uint8_t reserved2[6]; 17287252e1bSXiao Guangrong } QEMU_PACKED; 17387252e1bSXiao Guangrong typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; 17487252e1bSXiao Guangrong 17587252e1bSXiao Guangrong /* 17687252e1bSXiao Guangrong * Module serial number is a unique number for each device. We use the 17787252e1bSXiao Guangrong * slot id of NVDIMM device to generate this number so that each device 17887252e1bSXiao Guangrong * associates with a different number. 17987252e1bSXiao Guangrong * 18087252e1bSXiao Guangrong * 0x123456 is a magic number we arbitrarily chose. 18187252e1bSXiao Guangrong */ 18287252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_sn(int slot) 18387252e1bSXiao Guangrong { 18487252e1bSXiao Guangrong return 0x123456 + slot; 18587252e1bSXiao Guangrong } 18687252e1bSXiao Guangrong 18787252e1bSXiao Guangrong /* 18887252e1bSXiao Guangrong * handle is used to uniquely associate nfit_memdev structure with NVDIMM 18987252e1bSXiao Guangrong * ACPI device - nfit_memdev.nfit_handle matches with the value returned 19087252e1bSXiao Guangrong * by ACPI device _ADR method. 19187252e1bSXiao Guangrong * 19287252e1bSXiao Guangrong * We generate the handle with the slot id of NVDIMM device and reserve 19387252e1bSXiao Guangrong * 0 for NVDIMM root device. 19487252e1bSXiao Guangrong */ 19587252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_handle(int slot) 19687252e1bSXiao Guangrong { 19787252e1bSXiao Guangrong return slot + 1; 19887252e1bSXiao Guangrong } 19987252e1bSXiao Guangrong 20087252e1bSXiao Guangrong /* 20187252e1bSXiao Guangrong * index uniquely identifies the structure, 0 is reserved which indicates 20287252e1bSXiao Guangrong * that the structure is not valid or the associated structure is not 20387252e1bSXiao Guangrong * present. 20487252e1bSXiao Guangrong * 20587252e1bSXiao Guangrong * Each NVDIMM device needs two indexes, one for nfit_spa and another for 20687252e1bSXiao Guangrong * nfit_dc which are generated by the slot id of NVDIMM device. 20787252e1bSXiao Guangrong */ 20887252e1bSXiao Guangrong static uint16_t nvdimm_slot_to_spa_index(int slot) 20987252e1bSXiao Guangrong { 21087252e1bSXiao Guangrong return (slot + 1) << 1; 21187252e1bSXiao Guangrong } 21287252e1bSXiao Guangrong 21387252e1bSXiao Guangrong /* See the comments of nvdimm_slot_to_spa_index(). */ 21487252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_dcr_index(int slot) 21587252e1bSXiao Guangrong { 21687252e1bSXiao Guangrong return nvdimm_slot_to_spa_index(slot) + 1; 21787252e1bSXiao Guangrong } 21887252e1bSXiao Guangrong 2195797dcdcSXiao Guangrong static NVDIMMDevice *nvdimm_get_device_by_handle(uint32_t handle) 2205797dcdcSXiao Guangrong { 2215797dcdcSXiao Guangrong NVDIMMDevice *nvdimm = NULL; 2225797dcdcSXiao Guangrong GSList *list, *device_list = nvdimm_get_plugged_device_list(); 2235797dcdcSXiao Guangrong 2245797dcdcSXiao Guangrong for (list = device_list; list; list = list->next) { 2255797dcdcSXiao Guangrong NVDIMMDevice *nvd = list->data; 2265797dcdcSXiao Guangrong int slot = object_property_get_int(OBJECT(nvd), PC_DIMM_SLOT_PROP, 2275797dcdcSXiao Guangrong NULL); 2285797dcdcSXiao Guangrong 2295797dcdcSXiao Guangrong if (nvdimm_slot_to_handle(slot) == handle) { 2305797dcdcSXiao Guangrong nvdimm = nvd; 2315797dcdcSXiao Guangrong break; 2325797dcdcSXiao Guangrong } 2335797dcdcSXiao Guangrong } 2345797dcdcSXiao Guangrong 2355797dcdcSXiao Guangrong g_slist_free(device_list); 2365797dcdcSXiao Guangrong return nvdimm; 2375797dcdcSXiao Guangrong } 2385797dcdcSXiao Guangrong 23987252e1bSXiao Guangrong /* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */ 24087252e1bSXiao Guangrong static void 24187252e1bSXiao Guangrong nvdimm_build_structure_spa(GArray *structures, DeviceState *dev) 24287252e1bSXiao Guangrong { 24387252e1bSXiao Guangrong NvdimmNfitSpa *nfit_spa; 24487252e1bSXiao Guangrong uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, 24587252e1bSXiao Guangrong NULL); 24687252e1bSXiao Guangrong uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, 24787252e1bSXiao Guangrong NULL); 24887252e1bSXiao Guangrong uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, 24987252e1bSXiao Guangrong NULL); 25087252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 25187252e1bSXiao Guangrong NULL); 25287252e1bSXiao Guangrong 25387252e1bSXiao Guangrong nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa)); 25487252e1bSXiao Guangrong 25587252e1bSXiao Guangrong nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range 25687252e1bSXiao Guangrong Structure */); 25787252e1bSXiao Guangrong nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa)); 25887252e1bSXiao Guangrong nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 25987252e1bSXiao Guangrong 26087252e1bSXiao Guangrong /* 26187252e1bSXiao Guangrong * Control region is strict as all the device info, such as SN, index, 26287252e1bSXiao Guangrong * is associated with slot id. 26387252e1bSXiao Guangrong */ 26487252e1bSXiao Guangrong nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for 26587252e1bSXiao Guangrong management during hot add/online 26687252e1bSXiao Guangrong operation */ | 26787252e1bSXiao Guangrong 2 /* Data in Proximity Domain field is 26887252e1bSXiao Guangrong valid*/); 26987252e1bSXiao Guangrong 27087252e1bSXiao Guangrong /* NUMA node. */ 27187252e1bSXiao Guangrong nfit_spa->proximity_domain = cpu_to_le32(node); 27287252e1bSXiao Guangrong /* the region reported as PMEM. */ 27387252e1bSXiao Guangrong memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid, 27487252e1bSXiao Guangrong sizeof(nvdimm_nfit_spa_uuid)); 27587252e1bSXiao Guangrong 27687252e1bSXiao Guangrong nfit_spa->spa_base = cpu_to_le64(addr); 27787252e1bSXiao Guangrong nfit_spa->spa_length = cpu_to_le64(size); 27887252e1bSXiao Guangrong 27987252e1bSXiao Guangrong /* It is the PMEM and can be cached as writeback. */ 28087252e1bSXiao Guangrong nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ | 28187252e1bSXiao Guangrong 0x8000ULL /* EFI_MEMORY_NV */); 28287252e1bSXiao Guangrong } 28387252e1bSXiao Guangrong 28487252e1bSXiao Guangrong /* 28587252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping 28687252e1bSXiao Guangrong * Structure 28787252e1bSXiao Guangrong */ 28887252e1bSXiao Guangrong static void 28987252e1bSXiao Guangrong nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev) 29087252e1bSXiao Guangrong { 29187252e1bSXiao Guangrong NvdimmNfitMemDev *nfit_memdev; 29287252e1bSXiao Guangrong uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, 29387252e1bSXiao Guangrong NULL); 29487252e1bSXiao Guangrong uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, 29587252e1bSXiao Guangrong NULL); 29687252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 29787252e1bSXiao Guangrong NULL); 29887252e1bSXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 29987252e1bSXiao Guangrong 30087252e1bSXiao Guangrong nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev)); 30187252e1bSXiao Guangrong 30287252e1bSXiao Guangrong nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address 30387252e1bSXiao Guangrong Range Map Structure*/); 30487252e1bSXiao Guangrong nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev)); 30587252e1bSXiao Guangrong nfit_memdev->nfit_handle = cpu_to_le32(handle); 30687252e1bSXiao Guangrong 30787252e1bSXiao Guangrong /* 30887252e1bSXiao Guangrong * associate memory device with System Physical Address Range 30987252e1bSXiao Guangrong * Structure. 31087252e1bSXiao Guangrong */ 31187252e1bSXiao Guangrong nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 31287252e1bSXiao Guangrong /* associate memory device with Control Region Structure. */ 31387252e1bSXiao Guangrong nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 31487252e1bSXiao Guangrong 31587252e1bSXiao Guangrong /* The memory region on the device. */ 31687252e1bSXiao Guangrong nfit_memdev->region_len = cpu_to_le64(size); 31787252e1bSXiao Guangrong nfit_memdev->region_dpa = cpu_to_le64(addr); 31887252e1bSXiao Guangrong 31987252e1bSXiao Guangrong /* Only one interleave for PMEM. */ 32087252e1bSXiao Guangrong nfit_memdev->interleave_ways = cpu_to_le16(1); 32187252e1bSXiao Guangrong } 32287252e1bSXiao Guangrong 32387252e1bSXiao Guangrong /* 32487252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure. 32587252e1bSXiao Guangrong */ 32687252e1bSXiao Guangrong static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) 32787252e1bSXiao Guangrong { 32887252e1bSXiao Guangrong NvdimmNfitControlRegion *nfit_dcr; 32987252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 33087252e1bSXiao Guangrong NULL); 33187252e1bSXiao Guangrong uint32_t sn = nvdimm_slot_to_sn(slot); 33287252e1bSXiao Guangrong 33387252e1bSXiao Guangrong nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr)); 33487252e1bSXiao Guangrong 33587252e1bSXiao Guangrong nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */); 33687252e1bSXiao Guangrong nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr)); 33787252e1bSXiao Guangrong nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 33887252e1bSXiao Guangrong 33987252e1bSXiao Guangrong /* vendor: Intel. */ 34087252e1bSXiao Guangrong nfit_dcr->vendor_id = cpu_to_le16(0x8086); 34187252e1bSXiao Guangrong nfit_dcr->device_id = cpu_to_le16(1); 34287252e1bSXiao Guangrong 34387252e1bSXiao Guangrong /* The _DSM method is following Intel's DSM specification. */ 34487252e1bSXiao Guangrong nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported 34587252e1bSXiao Guangrong in ACPI 6.0 is 1. */); 34687252e1bSXiao Guangrong nfit_dcr->serial_number = cpu_to_le32(sn); 34787252e1bSXiao Guangrong nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter 34887252e1bSXiao Guangrong 2: NVDIMM Device Specific Method 34987252e1bSXiao Guangrong (DSM) in DSM Spec Rev1.*/); 35087252e1bSXiao Guangrong } 35187252e1bSXiao Guangrong 35287252e1bSXiao Guangrong static GArray *nvdimm_build_device_structure(GSList *device_list) 35387252e1bSXiao Guangrong { 35487252e1bSXiao Guangrong GArray *structures = g_array_new(false, true /* clear */, 1); 35587252e1bSXiao Guangrong 35687252e1bSXiao Guangrong for (; device_list; device_list = device_list->next) { 35787252e1bSXiao Guangrong DeviceState *dev = device_list->data; 35887252e1bSXiao Guangrong 35987252e1bSXiao Guangrong /* build System Physical Address Range Structure. */ 36087252e1bSXiao Guangrong nvdimm_build_structure_spa(structures, dev); 36187252e1bSXiao Guangrong 36287252e1bSXiao Guangrong /* 36387252e1bSXiao Guangrong * build Memory Device to System Physical Address Range Mapping 36487252e1bSXiao Guangrong * Structure. 36587252e1bSXiao Guangrong */ 36687252e1bSXiao Guangrong nvdimm_build_structure_memdev(structures, dev); 36787252e1bSXiao Guangrong 36887252e1bSXiao Guangrong /* build NVDIMM Control Region Structure. */ 36987252e1bSXiao Guangrong nvdimm_build_structure_dcr(structures, dev); 37087252e1bSXiao Guangrong } 37187252e1bSXiao Guangrong 37287252e1bSXiao Guangrong return structures; 37387252e1bSXiao Guangrong } 37487252e1bSXiao Guangrong 37587252e1bSXiao Guangrong static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets, 3760e9b9edaSIgor Mammedov GArray *table_data, BIOSLinker *linker) 37787252e1bSXiao Guangrong { 37887252e1bSXiao Guangrong GArray *structures = nvdimm_build_device_structure(device_list); 379c8e6c938SHaozhong Zhang unsigned int header; 38087252e1bSXiao Guangrong 38187252e1bSXiao Guangrong acpi_add_table(table_offsets, table_data); 38287252e1bSXiao Guangrong 38387252e1bSXiao Guangrong /* NFIT header. */ 384c8e6c938SHaozhong Zhang header = table_data->len; 385c8e6c938SHaozhong Zhang acpi_data_push(table_data, sizeof(NvdimmNfitHeader)); 38687252e1bSXiao Guangrong /* NVDIMM device structures. */ 38787252e1bSXiao Guangrong g_array_append_vals(table_data, structures->data, structures->len); 38887252e1bSXiao Guangrong 389c8e6c938SHaozhong Zhang build_header(linker, table_data, 390c8e6c938SHaozhong Zhang (void *)(table_data->data + header), "NFIT", 39137ad223cSLaszlo Ersek sizeof(NvdimmNfitHeader) + structures->len, 1, NULL, NULL); 39287252e1bSXiao Guangrong g_array_free(structures, true); 39387252e1bSXiao Guangrong } 39487252e1bSXiao Guangrong 39518c440e1SXiao Guangrong struct NvdimmDsmIn { 39618c440e1SXiao Guangrong uint32_t handle; 39718c440e1SXiao Guangrong uint32_t revision; 39818c440e1SXiao Guangrong uint32_t function; 39918c440e1SXiao Guangrong /* the remaining size in the page is used by arg3. */ 40018c440e1SXiao Guangrong union { 40135c5a52dSPaolo Bonzini uint8_t arg3[4084]; 40218c440e1SXiao Guangrong }; 40318c440e1SXiao Guangrong } QEMU_PACKED; 40418c440e1SXiao Guangrong typedef struct NvdimmDsmIn NvdimmDsmIn; 40535c5a52dSPaolo Bonzini QEMU_BUILD_BUG_ON(sizeof(NvdimmDsmIn) != 4096); 40618c440e1SXiao Guangrong 40718c440e1SXiao Guangrong struct NvdimmDsmOut { 40818c440e1SXiao Guangrong /* the size of buffer filled by QEMU. */ 40918c440e1SXiao Guangrong uint32_t len; 41035c5a52dSPaolo Bonzini uint8_t data[4092]; 41118c440e1SXiao Guangrong } QEMU_PACKED; 41218c440e1SXiao Guangrong typedef struct NvdimmDsmOut NvdimmDsmOut; 41335c5a52dSPaolo Bonzini QEMU_BUILD_BUG_ON(sizeof(NvdimmDsmOut) != 4096); 41418c440e1SXiao Guangrong 415f7df22deSXiao Guangrong struct NvdimmDsmFunc0Out { 416f7df22deSXiao Guangrong /* the size of buffer filled by QEMU. */ 417f7df22deSXiao Guangrong uint32_t len; 418f7df22deSXiao Guangrong uint32_t supported_func; 419f7df22deSXiao Guangrong } QEMU_PACKED; 420f7df22deSXiao Guangrong typedef struct NvdimmDsmFunc0Out NvdimmDsmFunc0Out; 421f7df22deSXiao Guangrong 422f7df22deSXiao Guangrong struct NvdimmDsmFuncNoPayloadOut { 423f7df22deSXiao Guangrong /* the size of buffer filled by QEMU. */ 424f7df22deSXiao Guangrong uint32_t len; 425f7df22deSXiao Guangrong uint32_t func_ret_status; 426f7df22deSXiao Guangrong } QEMU_PACKED; 427f7df22deSXiao Guangrong typedef struct NvdimmDsmFuncNoPayloadOut NvdimmDsmFuncNoPayloadOut; 428f7df22deSXiao Guangrong 4295797dcdcSXiao Guangrong struct NvdimmFuncGetLabelSizeOut { 4305797dcdcSXiao Guangrong /* the size of buffer filled by QEMU. */ 4315797dcdcSXiao Guangrong uint32_t len; 4325797dcdcSXiao Guangrong uint32_t func_ret_status; /* return status code. */ 4335797dcdcSXiao Guangrong uint32_t label_size; /* the size of label data area. */ 4345797dcdcSXiao Guangrong /* 4355797dcdcSXiao Guangrong * Maximum size of the namespace label data length supported by 4365797dcdcSXiao Guangrong * the platform in Get/Set Namespace Label Data functions. 4375797dcdcSXiao Guangrong */ 4385797dcdcSXiao Guangrong uint32_t max_xfer; 4395797dcdcSXiao Guangrong } QEMU_PACKED; 4405797dcdcSXiao Guangrong typedef struct NvdimmFuncGetLabelSizeOut NvdimmFuncGetLabelSizeOut; 4415797dcdcSXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelSizeOut) > 4096); 4425797dcdcSXiao Guangrong 4432b9e57fcSXiao Guangrong struct NvdimmFuncGetLabelDataIn { 4442b9e57fcSXiao Guangrong uint32_t offset; /* the offset in the namespace label data area. */ 4452b9e57fcSXiao Guangrong uint32_t length; /* the size of data is to be read via the function. */ 4462b9e57fcSXiao Guangrong } QEMU_PACKED; 4472b9e57fcSXiao Guangrong typedef struct NvdimmFuncGetLabelDataIn NvdimmFuncGetLabelDataIn; 4482b9e57fcSXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataIn) + 4492b9e57fcSXiao Guangrong offsetof(NvdimmDsmIn, arg3) > 4096); 4502b9e57fcSXiao Guangrong 4515797dcdcSXiao Guangrong struct NvdimmFuncGetLabelDataOut { 4525797dcdcSXiao Guangrong /* the size of buffer filled by QEMU. */ 4535797dcdcSXiao Guangrong uint32_t len; 4545797dcdcSXiao Guangrong uint32_t func_ret_status; /* return status code. */ 4555797dcdcSXiao Guangrong uint8_t out_buf[0]; /* the data got via Get Namesapce Label function. */ 4565797dcdcSXiao Guangrong } QEMU_PACKED; 4575797dcdcSXiao Guangrong typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut; 4582b9e57fcSXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > 4096); 4595797dcdcSXiao Guangrong 4605797dcdcSXiao Guangrong struct NvdimmFuncSetLabelDataIn { 4615797dcdcSXiao Guangrong uint32_t offset; /* the offset in the namespace label data area. */ 4625797dcdcSXiao Guangrong uint32_t length; /* the size of data is to be written via the function. */ 4635797dcdcSXiao Guangrong uint8_t in_buf[0]; /* the data written to label data area. */ 4645797dcdcSXiao Guangrong } QEMU_PACKED; 4655797dcdcSXiao Guangrong typedef struct NvdimmFuncSetLabelDataIn NvdimmFuncSetLabelDataIn; 46614e44198SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncSetLabelDataIn) + 46714e44198SXiao Guangrong offsetof(NvdimmDsmIn, arg3) > 4096); 4685797dcdcSXiao Guangrong 469189f4d56SXiao Guangrong static void 470189f4d56SXiao Guangrong nvdimm_dsm_function0(uint32_t supported_func, hwaddr dsm_mem_addr) 471189f4d56SXiao Guangrong { 472189f4d56SXiao Guangrong NvdimmDsmFunc0Out func0 = { 473189f4d56SXiao Guangrong .len = cpu_to_le32(sizeof(func0)), 474189f4d56SXiao Guangrong .supported_func = cpu_to_le32(supported_func), 475189f4d56SXiao Guangrong }; 476189f4d56SXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof(func0)); 477189f4d56SXiao Guangrong } 478189f4d56SXiao Guangrong 479189f4d56SXiao Guangrong static void 480189f4d56SXiao Guangrong nvdimm_dsm_no_payload(uint32_t func_ret_status, hwaddr dsm_mem_addr) 481189f4d56SXiao Guangrong { 482189f4d56SXiao Guangrong NvdimmDsmFuncNoPayloadOut out = { 483189f4d56SXiao Guangrong .len = cpu_to_le32(sizeof(out)), 484189f4d56SXiao Guangrong .func_ret_status = cpu_to_le32(func_ret_status), 485189f4d56SXiao Guangrong }; 486189f4d56SXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out)); 487189f4d56SXiao Guangrong } 488189f4d56SXiao Guangrong 489189f4d56SXiao Guangrong static void nvdimm_dsm_root(NvdimmDsmIn *in, hwaddr dsm_mem_addr) 490189f4d56SXiao Guangrong { 491189f4d56SXiao Guangrong /* 492189f4d56SXiao Guangrong * function 0 is called to inquire which functions are supported by 493189f4d56SXiao Guangrong * OSPM 494189f4d56SXiao Guangrong */ 495189f4d56SXiao Guangrong if (!in->function) { 496189f4d56SXiao Guangrong nvdimm_dsm_function0(0 /* No function supported other than 497189f4d56SXiao Guangrong function 0 */, dsm_mem_addr); 498189f4d56SXiao Guangrong return; 499189f4d56SXiao Guangrong } 500189f4d56SXiao Guangrong 501189f4d56SXiao Guangrong /* No function except function 0 is supported yet. */ 502189f4d56SXiao Guangrong nvdimm_dsm_no_payload(1 /* Not Supported */, dsm_mem_addr); 503189f4d56SXiao Guangrong } 504189f4d56SXiao Guangrong 5055797dcdcSXiao Guangrong /* 5065797dcdcSXiao Guangrong * the max transfer size is the max size transferred by both a 5075797dcdcSXiao Guangrong * 'Get Namespace Label Data' function and a 'Set Namespace Label Data' 5085797dcdcSXiao Guangrong * function. 5095797dcdcSXiao Guangrong */ 5105797dcdcSXiao Guangrong static uint32_t nvdimm_get_max_xfer_label_size(void) 5115797dcdcSXiao Guangrong { 5125797dcdcSXiao Guangrong uint32_t max_get_size, max_set_size, dsm_memory_size = 4096; 5135797dcdcSXiao Guangrong 5145797dcdcSXiao Guangrong /* 5155797dcdcSXiao Guangrong * the max data ACPI can read one time which is transferred by 5165797dcdcSXiao Guangrong * the response of 'Get Namespace Label Data' function. 5175797dcdcSXiao Guangrong */ 5185797dcdcSXiao Guangrong max_get_size = dsm_memory_size - sizeof(NvdimmFuncGetLabelDataOut); 5195797dcdcSXiao Guangrong 5205797dcdcSXiao Guangrong /* 5215797dcdcSXiao Guangrong * the max data ACPI can write one time which is transferred by 5225797dcdcSXiao Guangrong * 'Set Namespace Label Data' function. 5235797dcdcSXiao Guangrong */ 5245797dcdcSXiao Guangrong max_set_size = dsm_memory_size - offsetof(NvdimmDsmIn, arg3) - 5255797dcdcSXiao Guangrong sizeof(NvdimmFuncSetLabelDataIn); 5265797dcdcSXiao Guangrong 5275797dcdcSXiao Guangrong return MIN(max_get_size, max_set_size); 5285797dcdcSXiao Guangrong } 5295797dcdcSXiao Guangrong 5305797dcdcSXiao Guangrong /* 5315797dcdcSXiao Guangrong * DSM Spec Rev1 4.4 Get Namespace Label Size (Function Index 4). 5325797dcdcSXiao Guangrong * 5335797dcdcSXiao Guangrong * It gets the size of Namespace Label data area and the max data size 5345797dcdcSXiao Guangrong * that Get/Set Namespace Label Data functions can transfer. 5355797dcdcSXiao Guangrong */ 5365797dcdcSXiao Guangrong static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr) 5375797dcdcSXiao Guangrong { 5385797dcdcSXiao Guangrong NvdimmFuncGetLabelSizeOut label_size_out = { 5395797dcdcSXiao Guangrong .len = cpu_to_le32(sizeof(label_size_out)), 5405797dcdcSXiao Guangrong }; 5415797dcdcSXiao Guangrong uint32_t label_size, mxfer; 5425797dcdcSXiao Guangrong 5435797dcdcSXiao Guangrong label_size = nvdimm->label_size; 5445797dcdcSXiao Guangrong mxfer = nvdimm_get_max_xfer_label_size(); 5455797dcdcSXiao Guangrong 5465797dcdcSXiao Guangrong nvdimm_debug("label_size %#x, max_xfer %#x.\n", label_size, mxfer); 5475797dcdcSXiao Guangrong 5485797dcdcSXiao Guangrong label_size_out.func_ret_status = cpu_to_le32(0 /* Success */); 5495797dcdcSXiao Guangrong label_size_out.label_size = cpu_to_le32(label_size); 5505797dcdcSXiao Guangrong label_size_out.max_xfer = cpu_to_le32(mxfer); 5515797dcdcSXiao Guangrong 5525797dcdcSXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &label_size_out, 5535797dcdcSXiao Guangrong sizeof(label_size_out)); 5545797dcdcSXiao Guangrong } 5555797dcdcSXiao Guangrong 5562b9e57fcSXiao Guangrong static uint32_t nvdimm_rw_label_data_check(NVDIMMDevice *nvdimm, 5572b9e57fcSXiao Guangrong uint32_t offset, uint32_t length) 5582b9e57fcSXiao Guangrong { 5592b9e57fcSXiao Guangrong uint32_t ret = 3 /* Invalid Input Parameters */; 5602b9e57fcSXiao Guangrong 5612b9e57fcSXiao Guangrong if (offset + length < offset) { 5622b9e57fcSXiao Guangrong nvdimm_debug("offset %#x + length %#x is overflow.\n", offset, 5632b9e57fcSXiao Guangrong length); 5642b9e57fcSXiao Guangrong return ret; 5652b9e57fcSXiao Guangrong } 5662b9e57fcSXiao Guangrong 5672b9e57fcSXiao Guangrong if (nvdimm->label_size < offset + length) { 5682b9e57fcSXiao Guangrong nvdimm_debug("position %#x is beyond label data (len = %" PRIx64 ").\n", 5692b9e57fcSXiao Guangrong offset + length, nvdimm->label_size); 5702b9e57fcSXiao Guangrong return ret; 5712b9e57fcSXiao Guangrong } 5722b9e57fcSXiao Guangrong 5732b9e57fcSXiao Guangrong if (length > nvdimm_get_max_xfer_label_size()) { 5742b9e57fcSXiao Guangrong nvdimm_debug("length (%#x) is larger than max_xfer (%#x).\n", 5752b9e57fcSXiao Guangrong length, nvdimm_get_max_xfer_label_size()); 5762b9e57fcSXiao Guangrong return ret; 5772b9e57fcSXiao Guangrong } 5782b9e57fcSXiao Guangrong 5792b9e57fcSXiao Guangrong return 0 /* Success */; 5802b9e57fcSXiao Guangrong } 5812b9e57fcSXiao Guangrong 5822b9e57fcSXiao Guangrong /* 5832b9e57fcSXiao Guangrong * DSM Spec Rev1 4.5 Get Namespace Label Data (Function Index 5). 5842b9e57fcSXiao Guangrong */ 5852b9e57fcSXiao Guangrong static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, 5862b9e57fcSXiao Guangrong hwaddr dsm_mem_addr) 5872b9e57fcSXiao Guangrong { 5882b9e57fcSXiao Guangrong NVDIMMClass *nvc = NVDIMM_GET_CLASS(nvdimm); 5892b9e57fcSXiao Guangrong NvdimmFuncGetLabelDataIn *get_label_data; 5902b9e57fcSXiao Guangrong NvdimmFuncGetLabelDataOut *get_label_data_out; 5912b9e57fcSXiao Guangrong uint32_t status; 5922b9e57fcSXiao Guangrong int size; 5932b9e57fcSXiao Guangrong 5942b9e57fcSXiao Guangrong get_label_data = (NvdimmFuncGetLabelDataIn *)in->arg3; 5952b9e57fcSXiao Guangrong le32_to_cpus(&get_label_data->offset); 5962b9e57fcSXiao Guangrong le32_to_cpus(&get_label_data->length); 5972b9e57fcSXiao Guangrong 5982b9e57fcSXiao Guangrong nvdimm_debug("Read Label Data: offset %#x length %#x.\n", 5992b9e57fcSXiao Guangrong get_label_data->offset, get_label_data->length); 6002b9e57fcSXiao Guangrong 6012b9e57fcSXiao Guangrong status = nvdimm_rw_label_data_check(nvdimm, get_label_data->offset, 6022b9e57fcSXiao Guangrong get_label_data->length); 6032b9e57fcSXiao Guangrong if (status != 0 /* Success */) { 6042b9e57fcSXiao Guangrong nvdimm_dsm_no_payload(status, dsm_mem_addr); 6052b9e57fcSXiao Guangrong return; 6062b9e57fcSXiao Guangrong } 6072b9e57fcSXiao Guangrong 6082b9e57fcSXiao Guangrong size = sizeof(*get_label_data_out) + get_label_data->length; 6092b9e57fcSXiao Guangrong assert(size <= 4096); 6102b9e57fcSXiao Guangrong get_label_data_out = g_malloc(size); 6112b9e57fcSXiao Guangrong 6122b9e57fcSXiao Guangrong get_label_data_out->len = cpu_to_le32(size); 6132b9e57fcSXiao Guangrong get_label_data_out->func_ret_status = cpu_to_le32(0 /* Success */); 6142b9e57fcSXiao Guangrong nvc->read_label_data(nvdimm, get_label_data_out->out_buf, 6152b9e57fcSXiao Guangrong get_label_data->length, get_label_data->offset); 6162b9e57fcSXiao Guangrong 6172b9e57fcSXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, get_label_data_out, size); 6182b9e57fcSXiao Guangrong g_free(get_label_data_out); 6192b9e57fcSXiao Guangrong } 6202b9e57fcSXiao Guangrong 62114e44198SXiao Guangrong /* 62214e44198SXiao Guangrong * DSM Spec Rev1 4.6 Set Namespace Label Data (Function Index 6). 62314e44198SXiao Guangrong */ 62414e44198SXiao Guangrong static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, 62514e44198SXiao Guangrong hwaddr dsm_mem_addr) 62614e44198SXiao Guangrong { 62714e44198SXiao Guangrong NVDIMMClass *nvc = NVDIMM_GET_CLASS(nvdimm); 62814e44198SXiao Guangrong NvdimmFuncSetLabelDataIn *set_label_data; 62914e44198SXiao Guangrong uint32_t status; 63014e44198SXiao Guangrong 63114e44198SXiao Guangrong set_label_data = (NvdimmFuncSetLabelDataIn *)in->arg3; 63214e44198SXiao Guangrong 63314e44198SXiao Guangrong le32_to_cpus(&set_label_data->offset); 63414e44198SXiao Guangrong le32_to_cpus(&set_label_data->length); 63514e44198SXiao Guangrong 63614e44198SXiao Guangrong nvdimm_debug("Write Label Data: offset %#x length %#x.\n", 63714e44198SXiao Guangrong set_label_data->offset, set_label_data->length); 63814e44198SXiao Guangrong 63914e44198SXiao Guangrong status = nvdimm_rw_label_data_check(nvdimm, set_label_data->offset, 64014e44198SXiao Guangrong set_label_data->length); 64114e44198SXiao Guangrong if (status != 0 /* Success */) { 64214e44198SXiao Guangrong nvdimm_dsm_no_payload(status, dsm_mem_addr); 64314e44198SXiao Guangrong return; 64414e44198SXiao Guangrong } 64514e44198SXiao Guangrong 64614e44198SXiao Guangrong assert(sizeof(*in) + sizeof(*set_label_data) + set_label_data->length <= 64714e44198SXiao Guangrong 4096); 64814e44198SXiao Guangrong 64914e44198SXiao Guangrong nvc->write_label_data(nvdimm, set_label_data->in_buf, 65014e44198SXiao Guangrong set_label_data->length, set_label_data->offset); 65114e44198SXiao Guangrong nvdimm_dsm_no_payload(0 /* Success */, dsm_mem_addr); 65214e44198SXiao Guangrong } 65314e44198SXiao Guangrong 654189f4d56SXiao Guangrong static void nvdimm_dsm_device(NvdimmDsmIn *in, hwaddr dsm_mem_addr) 655189f4d56SXiao Guangrong { 6565797dcdcSXiao Guangrong NVDIMMDevice *nvdimm = nvdimm_get_device_by_handle(in->handle); 6575797dcdcSXiao Guangrong 658189f4d56SXiao Guangrong /* See the comments in nvdimm_dsm_root(). */ 659189f4d56SXiao Guangrong if (!in->function) { 6605797dcdcSXiao Guangrong uint32_t supported_func = 0; 6615797dcdcSXiao Guangrong 6625797dcdcSXiao Guangrong if (nvdimm && nvdimm->label_size) { 6635797dcdcSXiao Guangrong supported_func |= 0x1 /* Bit 0 indicates whether there is 6645797dcdcSXiao Guangrong support for any functions other 6655797dcdcSXiao Guangrong than function 0. */ | 6662b9e57fcSXiao Guangrong 1 << 4 /* Get Namespace Label Size */ | 66714e44198SXiao Guangrong 1 << 5 /* Get Namespace Label Data */ | 66814e44198SXiao Guangrong 1 << 6 /* Set Namespace Label Data */; 6695797dcdcSXiao Guangrong } 6705797dcdcSXiao Guangrong nvdimm_dsm_function0(supported_func, dsm_mem_addr); 671189f4d56SXiao Guangrong return; 672189f4d56SXiao Guangrong } 673189f4d56SXiao Guangrong 6745797dcdcSXiao Guangrong if (!nvdimm) { 6755797dcdcSXiao Guangrong nvdimm_dsm_no_payload(2 /* Non-Existing Memory Device */, 6765797dcdcSXiao Guangrong dsm_mem_addr); 6775797dcdcSXiao Guangrong return; 6785797dcdcSXiao Guangrong } 6795797dcdcSXiao Guangrong 6805797dcdcSXiao Guangrong /* Encode DSM function according to DSM Spec Rev1. */ 6815797dcdcSXiao Guangrong switch (in->function) { 6825797dcdcSXiao Guangrong case 4 /* Get Namespace Label Size */: 6835797dcdcSXiao Guangrong if (nvdimm->label_size) { 6845797dcdcSXiao Guangrong nvdimm_dsm_label_size(nvdimm, dsm_mem_addr); 6855797dcdcSXiao Guangrong return; 6865797dcdcSXiao Guangrong } 6875797dcdcSXiao Guangrong break; 6882b9e57fcSXiao Guangrong case 5 /* Get Namespace Label Data */: 6892b9e57fcSXiao Guangrong if (nvdimm->label_size) { 6902b9e57fcSXiao Guangrong nvdimm_dsm_get_label_data(nvdimm, in, dsm_mem_addr); 6912b9e57fcSXiao Guangrong return; 6922b9e57fcSXiao Guangrong } 6932b9e57fcSXiao Guangrong break; 69414e44198SXiao Guangrong case 0x6 /* Set Namespace Label Data */: 69514e44198SXiao Guangrong if (nvdimm->label_size) { 69614e44198SXiao Guangrong nvdimm_dsm_set_label_data(nvdimm, in, dsm_mem_addr); 69714e44198SXiao Guangrong return; 69814e44198SXiao Guangrong } 69914e44198SXiao Guangrong break; 7005797dcdcSXiao Guangrong } 7015797dcdcSXiao Guangrong 702189f4d56SXiao Guangrong nvdimm_dsm_no_payload(1 /* Not Supported */, dsm_mem_addr); 703189f4d56SXiao Guangrong } 704189f4d56SXiao Guangrong 7055fe79386SXiao Guangrong static uint64_t 7065fe79386SXiao Guangrong nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size) 7075fe79386SXiao Guangrong { 708f7df22deSXiao Guangrong nvdimm_debug("BUG: we never read _DSM IO Port.\n"); 7095fe79386SXiao Guangrong return 0; 7105fe79386SXiao Guangrong } 7115fe79386SXiao Guangrong 7125fe79386SXiao Guangrong static void 7135fe79386SXiao Guangrong nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) 7145fe79386SXiao Guangrong { 715f7df22deSXiao Guangrong NvdimmDsmIn *in; 716f7df22deSXiao Guangrong hwaddr dsm_mem_addr = val; 717f7df22deSXiao Guangrong 718f7df22deSXiao Guangrong nvdimm_debug("dsm memory address %#" HWADDR_PRIx ".\n", dsm_mem_addr); 719f7df22deSXiao Guangrong 720f7df22deSXiao Guangrong /* 721f7df22deSXiao Guangrong * The DSM memory is mapped to guest address space so an evil guest 722f7df22deSXiao Guangrong * can change its content while we are doing DSM emulation. Avoid 723f7df22deSXiao Guangrong * this by copying DSM memory to QEMU local memory. 724f7df22deSXiao Guangrong */ 72535c5a52dSPaolo Bonzini in = g_new(NvdimmDsmIn, 1); 72635c5a52dSPaolo Bonzini cpu_physical_memory_read(dsm_mem_addr, in, sizeof(*in)); 727f7df22deSXiao Guangrong 728f7df22deSXiao Guangrong le32_to_cpus(&in->revision); 729f7df22deSXiao Guangrong le32_to_cpus(&in->function); 730f7df22deSXiao Guangrong le32_to_cpus(&in->handle); 731f7df22deSXiao Guangrong 732f7df22deSXiao Guangrong nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision, 733f7df22deSXiao Guangrong in->handle, in->function); 734f7df22deSXiao Guangrong 735d15fc53fSXiao Guangrong if (in->revision != 0x1 /* Currently we only support DSM Spec Rev1. */) { 736d15fc53fSXiao Guangrong nvdimm_debug("Revision %#x is not supported, expect %#x.\n", 737d15fc53fSXiao Guangrong in->revision, 0x1); 738d15fc53fSXiao Guangrong nvdimm_dsm_no_payload(1 /* Not Supported */, dsm_mem_addr); 739d15fc53fSXiao Guangrong goto exit; 740d15fc53fSXiao Guangrong } 741d15fc53fSXiao Guangrong 742189f4d56SXiao Guangrong /* Handle 0 is reserved for NVDIMM Root Device. */ 743189f4d56SXiao Guangrong if (!in->handle) { 744189f4d56SXiao Guangrong nvdimm_dsm_root(in, dsm_mem_addr); 745189f4d56SXiao Guangrong goto exit; 746f7df22deSXiao Guangrong } 747f7df22deSXiao Guangrong 748189f4d56SXiao Guangrong nvdimm_dsm_device(in, dsm_mem_addr); 749189f4d56SXiao Guangrong 750189f4d56SXiao Guangrong exit: 751f7df22deSXiao Guangrong g_free(in); 7525fe79386SXiao Guangrong } 7535fe79386SXiao Guangrong 7545fe79386SXiao Guangrong static const MemoryRegionOps nvdimm_dsm_ops = { 7555fe79386SXiao Guangrong .read = nvdimm_dsm_read, 7565fe79386SXiao Guangrong .write = nvdimm_dsm_write, 7575fe79386SXiao Guangrong .endianness = DEVICE_LITTLE_ENDIAN, 7585fe79386SXiao Guangrong .valid = { 7595fe79386SXiao Guangrong .min_access_size = 4, 7605fe79386SXiao Guangrong .max_access_size = 4, 7615fe79386SXiao Guangrong }, 7625fe79386SXiao Guangrong }; 7635fe79386SXiao Guangrong 7645fe79386SXiao Guangrong void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, 7655fe79386SXiao Guangrong FWCfgState *fw_cfg, Object *owner) 7665fe79386SXiao Guangrong { 7675fe79386SXiao Guangrong memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state, 7685fe79386SXiao Guangrong "nvdimm-acpi-io", NVDIMM_ACPI_IO_LEN); 7695fe79386SXiao Guangrong memory_region_add_subregion(io, NVDIMM_ACPI_IO_BASE, &state->io_mr); 7705fe79386SXiao Guangrong 7715fe79386SXiao Guangrong state->dsm_mem = g_array_new(false, true /* clear */, 1); 77235c5a52dSPaolo Bonzini acpi_data_push(state->dsm_mem, sizeof(NvdimmDsmIn)); 7735fe79386SXiao Guangrong fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data, 7745fe79386SXiao Guangrong state->dsm_mem->len); 7755fe79386SXiao Guangrong } 7765fe79386SXiao Guangrong 77777286395SXiao Guangrong #define NVDIMM_COMMON_DSM "NCAL" 778b9951413SXiao Guangrong #define NVDIMM_ACPI_MEM_ADDR "MEMA" 77977286395SXiao Guangrong 78077286395SXiao Guangrong static void nvdimm_build_common_dsm(Aml *dev) 78177286395SXiao Guangrong { 78290623ebfSXiao Guangrong Aml *method, *ifctx, *function, *handle, *uuid, *dsm_mem, *result_size; 78390623ebfSXiao Guangrong Aml *elsectx, *unsupport, *unpatched, *expected_uuid, *uuid_invalid; 784*c0b3b863SXiao Guangrong Aml *pckg, *pckg_index, *pckg_buf, *field; 78577286395SXiao Guangrong uint8_t byte_list[1]; 78677286395SXiao Guangrong 787732b530cSXiao Guangrong method = aml_method(NVDIMM_COMMON_DSM, 5, AML_SERIALIZED); 78890623ebfSXiao Guangrong uuid = aml_arg(0); 78977286395SXiao Guangrong function = aml_arg(2); 79090623ebfSXiao Guangrong handle = aml_arg(4); 791*c0b3b863SXiao Guangrong dsm_mem = aml_local(6); 792*c0b3b863SXiao Guangrong 793*c0b3b863SXiao Guangrong aml_append(method, aml_store(aml_name(NVDIMM_ACPI_MEM_ADDR), dsm_mem)); 794*c0b3b863SXiao Guangrong 795*c0b3b863SXiao Guangrong /* map DSM memory and IO into ACPI namespace. */ 796*c0b3b863SXiao Guangrong aml_append(method, aml_operation_region("NPIO", AML_SYSTEM_IO, 797*c0b3b863SXiao Guangrong aml_int(NVDIMM_ACPI_IO_BASE), NVDIMM_ACPI_IO_LEN)); 798*c0b3b863SXiao Guangrong aml_append(method, aml_operation_region("NRAM", AML_SYSTEM_MEMORY, 799*c0b3b863SXiao Guangrong dsm_mem, sizeof(NvdimmDsmIn))); 800*c0b3b863SXiao Guangrong 801*c0b3b863SXiao Guangrong /* 802*c0b3b863SXiao Guangrong * DSM notifier: 803*c0b3b863SXiao Guangrong * NTFI: write the address of DSM memory and notify QEMU to emulate 804*c0b3b863SXiao Guangrong * the access. 805*c0b3b863SXiao Guangrong * 806*c0b3b863SXiao Guangrong * It is the IO port so that accessing them will cause VM-exit, the 807*c0b3b863SXiao Guangrong * control will be transferred to QEMU. 808*c0b3b863SXiao Guangrong */ 809*c0b3b863SXiao Guangrong field = aml_field("NPIO", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); 810*c0b3b863SXiao Guangrong aml_append(field, aml_named_field("NTFI", 811*c0b3b863SXiao Guangrong sizeof(uint32_t) * BITS_PER_BYTE)); 812*c0b3b863SXiao Guangrong aml_append(method, field); 813*c0b3b863SXiao Guangrong 814*c0b3b863SXiao Guangrong /* 815*c0b3b863SXiao Guangrong * DSM input: 816*c0b3b863SXiao Guangrong * HDLE: store device's handle, it's zero if the _DSM call happens 817*c0b3b863SXiao Guangrong * on NVDIMM Root Device. 818*c0b3b863SXiao Guangrong * REVS: store the Arg1 of _DSM call. 819*c0b3b863SXiao Guangrong * FUNC: store the Arg2 of _DSM call. 820*c0b3b863SXiao Guangrong * ARG3: store the Arg3 of _DSM call. 821*c0b3b863SXiao Guangrong * 822*c0b3b863SXiao Guangrong * They are RAM mapping on host so that these accesses never cause 823*c0b3b863SXiao Guangrong * VM-EXIT. 824*c0b3b863SXiao Guangrong */ 825*c0b3b863SXiao Guangrong field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); 826*c0b3b863SXiao Guangrong aml_append(field, aml_named_field("HDLE", 827*c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, handle)) * BITS_PER_BYTE)); 828*c0b3b863SXiao Guangrong aml_append(field, aml_named_field("REVS", 829*c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, revision)) * BITS_PER_BYTE)); 830*c0b3b863SXiao Guangrong aml_append(field, aml_named_field("FUNC", 831*c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, function)) * BITS_PER_BYTE)); 832*c0b3b863SXiao Guangrong aml_append(field, aml_named_field("ARG3", 833*c0b3b863SXiao Guangrong (sizeof(NvdimmDsmIn) - offsetof(NvdimmDsmIn, arg3)) * BITS_PER_BYTE)); 834*c0b3b863SXiao Guangrong aml_append(method, field); 835*c0b3b863SXiao Guangrong 836*c0b3b863SXiao Guangrong /* 837*c0b3b863SXiao Guangrong * DSM output: 838*c0b3b863SXiao Guangrong * RLEN: the size of the buffer filled by QEMU. 839*c0b3b863SXiao Guangrong * ODAT: the buffer QEMU uses to store the result. 840*c0b3b863SXiao Guangrong * 841*c0b3b863SXiao Guangrong * Since the page is reused by both input and out, the input data 842*c0b3b863SXiao Guangrong * will be lost after storing new result into ODAT so we should fetch 843*c0b3b863SXiao Guangrong * all the input data before writing the result. 844*c0b3b863SXiao Guangrong */ 845*c0b3b863SXiao Guangrong field = aml_field("NRAM", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); 846*c0b3b863SXiao Guangrong aml_append(field, aml_named_field("RLEN", 847*c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmOut, len)) * BITS_PER_BYTE)); 848*c0b3b863SXiao Guangrong aml_append(field, aml_named_field("ODAT", 849*c0b3b863SXiao Guangrong (sizeof(NvdimmDsmOut) - offsetof(NvdimmDsmOut, data)) * BITS_PER_BYTE)); 850*c0b3b863SXiao Guangrong aml_append(method, field); 85118c440e1SXiao Guangrong 85218c440e1SXiao Guangrong /* 85318c440e1SXiao Guangrong * do not support any method if DSM memory address has not been 85418c440e1SXiao Guangrong * patched. 85518c440e1SXiao Guangrong */ 85690623ebfSXiao Guangrong unpatched = aml_equal(dsm_mem, aml_int(0x0)); 85790623ebfSXiao Guangrong 85890623ebfSXiao Guangrong expected_uuid = aml_local(0); 85990623ebfSXiao Guangrong 86090623ebfSXiao Guangrong ifctx = aml_if(aml_equal(handle, aml_int(0x0))); 86190623ebfSXiao Guangrong aml_append(ifctx, aml_store( 86290623ebfSXiao Guangrong aml_touuid("2F10E7A4-9E91-11E4-89D3-123B93F75CBA") 86390623ebfSXiao Guangrong /* UUID for NVDIMM Root Device */, expected_uuid)); 86490623ebfSXiao Guangrong aml_append(method, ifctx); 86590623ebfSXiao Guangrong elsectx = aml_else(); 86690623ebfSXiao Guangrong aml_append(elsectx, aml_store( 86790623ebfSXiao Guangrong aml_touuid("4309AC30-0D11-11E4-9191-0800200C9A66") 86890623ebfSXiao Guangrong /* UUID for NVDIMM Devices */, expected_uuid)); 86990623ebfSXiao Guangrong aml_append(method, elsectx); 87090623ebfSXiao Guangrong 87190623ebfSXiao Guangrong uuid_invalid = aml_lnot(aml_equal(uuid, expected_uuid)); 87290623ebfSXiao Guangrong 87390623ebfSXiao Guangrong unsupport = aml_if(aml_or(unpatched, uuid_invalid, NULL)); 87477286395SXiao Guangrong 87577286395SXiao Guangrong /* 87677286395SXiao Guangrong * function 0 is called to inquire what functions are supported by 87777286395SXiao Guangrong * OSPM 87877286395SXiao Guangrong */ 87977286395SXiao Guangrong ifctx = aml_if(aml_equal(function, aml_int(0))); 88077286395SXiao Guangrong byte_list[0] = 0 /* No function Supported */; 88177286395SXiao Guangrong aml_append(ifctx, aml_return(aml_buffer(1, byte_list))); 88290623ebfSXiao Guangrong aml_append(unsupport, ifctx); 88377286395SXiao Guangrong 88477286395SXiao Guangrong /* No function is supported yet. */ 88577286395SXiao Guangrong byte_list[0] = 1 /* Not Supported */; 88690623ebfSXiao Guangrong aml_append(unsupport, aml_return(aml_buffer(1, byte_list))); 88790623ebfSXiao Guangrong aml_append(method, unsupport); 88877286395SXiao Guangrong 88918c440e1SXiao Guangrong /* 89018c440e1SXiao Guangrong * The HDLE indicates the DSM function is issued from which device, 891732b530cSXiao Guangrong * it reserves 0 for root device and is the handle for NVDIMM devices. 892732b530cSXiao Guangrong * See the comments in nvdimm_slot_to_handle(). 89318c440e1SXiao Guangrong */ 89490623ebfSXiao Guangrong aml_append(method, aml_store(handle, aml_name("HDLE"))); 89518c440e1SXiao Guangrong aml_append(method, aml_store(aml_arg(1), aml_name("REVS"))); 89618c440e1SXiao Guangrong aml_append(method, aml_store(aml_arg(2), aml_name("FUNC"))); 89718c440e1SXiao Guangrong 89818c440e1SXiao Guangrong /* 8994568c948SXiao Guangrong * The fourth parameter (Arg3) of _DSM is a package which contains 9004568c948SXiao Guangrong * a buffer, the layout of the buffer is specified by UUID (Arg0), 9014568c948SXiao Guangrong * Revision ID (Arg1) and Function Index (Arg2) which are documented 9024568c948SXiao Guangrong * in the DSM Spec. 9034568c948SXiao Guangrong */ 9044568c948SXiao Guangrong pckg = aml_arg(3); 9054568c948SXiao Guangrong ifctx = aml_if(aml_and(aml_equal(aml_object_type(pckg), 9064568c948SXiao Guangrong aml_int(4 /* Package */)) /* It is a Package? */, 9074568c948SXiao Guangrong aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */, 9084568c948SXiao Guangrong NULL)); 9094568c948SXiao Guangrong 9104568c948SXiao Guangrong pckg_index = aml_local(2); 9114568c948SXiao Guangrong pckg_buf = aml_local(3); 9124568c948SXiao Guangrong aml_append(ifctx, aml_store(aml_index(pckg, aml_int(0)), pckg_index)); 9134568c948SXiao Guangrong aml_append(ifctx, aml_store(aml_derefof(pckg_index), pckg_buf)); 9144568c948SXiao Guangrong aml_append(ifctx, aml_store(pckg_buf, aml_name("ARG3"))); 9154568c948SXiao Guangrong aml_append(method, ifctx); 9164568c948SXiao Guangrong 9174568c948SXiao Guangrong /* 91818c440e1SXiao Guangrong * tell QEMU about the real address of DSM memory, then QEMU 91918c440e1SXiao Guangrong * gets the control and fills the result in DSM memory. 92018c440e1SXiao Guangrong */ 92118c440e1SXiao Guangrong aml_append(method, aml_store(dsm_mem, aml_name("NTFI"))); 92218c440e1SXiao Guangrong 92318c440e1SXiao Guangrong result_size = aml_local(1); 924d51d1d7eSXiao Guangrong /* RLEN is not included in the payload returned to guest. */ 925d51d1d7eSXiao Guangrong aml_append(method, aml_subtract(aml_name("RLEN"), aml_int(4), result_size)); 92618c440e1SXiao Guangrong aml_append(method, aml_store(aml_shiftleft(result_size, aml_int(3)), 92718c440e1SXiao Guangrong result_size)); 92818c440e1SXiao Guangrong aml_append(method, aml_create_field(aml_name("ODAT"), aml_int(0), 92918c440e1SXiao Guangrong result_size, "OBUF")); 93018c440e1SXiao Guangrong aml_append(method, aml_concatenate(aml_buffer(0, NULL), aml_name("OBUF"), 93118c440e1SXiao Guangrong aml_arg(6))); 93218c440e1SXiao Guangrong aml_append(method, aml_return(aml_arg(6))); 93377286395SXiao Guangrong aml_append(dev, method); 93477286395SXiao Guangrong } 93577286395SXiao Guangrong 936732b530cSXiao Guangrong static void nvdimm_build_device_dsm(Aml *dev, uint32_t handle) 93777286395SXiao Guangrong { 93877286395SXiao Guangrong Aml *method; 93977286395SXiao Guangrong 94077286395SXiao Guangrong method = aml_method("_DSM", 4, AML_NOTSERIALIZED); 941732b530cSXiao Guangrong aml_append(method, aml_return(aml_call5(NVDIMM_COMMON_DSM, aml_arg(0), 942732b530cSXiao Guangrong aml_arg(1), aml_arg(2), aml_arg(3), 943732b530cSXiao Guangrong aml_int(handle)))); 94477286395SXiao Guangrong aml_append(dev, method); 94577286395SXiao Guangrong } 94677286395SXiao Guangrong 94777286395SXiao Guangrong static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev) 94877286395SXiao Guangrong { 94977286395SXiao Guangrong for (; device_list; device_list = device_list->next) { 95077286395SXiao Guangrong DeviceState *dev = device_list->data; 95177286395SXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 95277286395SXiao Guangrong NULL); 95377286395SXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 95477286395SXiao Guangrong Aml *nvdimm_dev; 95577286395SXiao Guangrong 95677286395SXiao Guangrong nvdimm_dev = aml_device("NV%02X", slot); 95777286395SXiao Guangrong 95877286395SXiao Guangrong /* 95977286395SXiao Guangrong * ACPI 6.0: 9.20 NVDIMM Devices: 96077286395SXiao Guangrong * 96177286395SXiao Guangrong * _ADR object that is used to supply OSPM with unique address 96277286395SXiao Guangrong * of the NVDIMM device. This is done by returning the NFIT Device 96377286395SXiao Guangrong * handle that is used to identify the associated entries in ACPI 96477286395SXiao Guangrong * table NFIT or _FIT. 96577286395SXiao Guangrong */ 96677286395SXiao Guangrong aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle))); 96777286395SXiao Guangrong 968732b530cSXiao Guangrong nvdimm_build_device_dsm(nvdimm_dev, handle); 96977286395SXiao Guangrong aml_append(root_dev, nvdimm_dev); 97077286395SXiao Guangrong } 97177286395SXiao Guangrong } 97277286395SXiao Guangrong 97377286395SXiao Guangrong static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, 974ad9671b8SIgor Mammedov GArray *table_data, BIOSLinker *linker, 975ad9671b8SIgor Mammedov GArray *dsm_dma_arrea) 97677286395SXiao Guangrong { 977*c0b3b863SXiao Guangrong Aml *ssdt, *sb_scope, *dev; 978b9951413SXiao Guangrong int mem_addr_offset, nvdimm_ssdt; 97977286395SXiao Guangrong 98077286395SXiao Guangrong acpi_add_table(table_offsets, table_data); 98177286395SXiao Guangrong 98277286395SXiao Guangrong ssdt = init_aml_allocator(); 98377286395SXiao Guangrong acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); 98477286395SXiao Guangrong 98577286395SXiao Guangrong sb_scope = aml_scope("\\_SB"); 98677286395SXiao Guangrong 98777286395SXiao Guangrong dev = aml_device("NVDR"); 98877286395SXiao Guangrong 98977286395SXiao Guangrong /* 99077286395SXiao Guangrong * ACPI 6.0: 9.20 NVDIMM Devices: 99177286395SXiao Guangrong * 99277286395SXiao Guangrong * The ACPI Name Space device uses _HID of ACPI0012 to identify the root 99377286395SXiao Guangrong * NVDIMM interface device. Platform firmware is required to contain one 99477286395SXiao Guangrong * such device in _SB scope if NVDIMMs support is exposed by platform to 99577286395SXiao Guangrong * OSPM. 99677286395SXiao Guangrong * For each NVDIMM present or intended to be supported by platform, 99777286395SXiao Guangrong * platform firmware also exposes an ACPI Namespace Device under the 99877286395SXiao Guangrong * root device. 99977286395SXiao Guangrong */ 100077286395SXiao Guangrong aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012"))); 100177286395SXiao Guangrong 100277286395SXiao Guangrong nvdimm_build_common_dsm(dev); 1003732b530cSXiao Guangrong 1004732b530cSXiao Guangrong /* 0 is reserved for root device. */ 1005732b530cSXiao Guangrong nvdimm_build_device_dsm(dev, 0); 100677286395SXiao Guangrong 100777286395SXiao Guangrong nvdimm_build_nvdimm_devices(device_list, dev); 100877286395SXiao Guangrong 100977286395SXiao Guangrong aml_append(sb_scope, dev); 101077286395SXiao Guangrong aml_append(ssdt, sb_scope); 1011b9951413SXiao Guangrong 1012b9951413SXiao Guangrong nvdimm_ssdt = table_data->len; 1013b9951413SXiao Guangrong 101477286395SXiao Guangrong /* copy AML table into ACPI tables blob and patch header there */ 101577286395SXiao Guangrong g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); 1016b9951413SXiao Guangrong mem_addr_offset = build_append_named_dword(table_data, 1017b9951413SXiao Guangrong NVDIMM_ACPI_MEM_ADDR); 1018b9951413SXiao Guangrong 1019ad9671b8SIgor Mammedov bios_linker_loader_alloc(linker, 1020ad9671b8SIgor Mammedov NVDIMM_DSM_MEM_FILE, dsm_dma_arrea, 1021ad9671b8SIgor Mammedov sizeof(NvdimmDsmIn), false /* high memory */); 10224678124bSIgor Mammedov bios_linker_loader_add_pointer(linker, 10234678124bSIgor Mammedov ACPI_BUILD_TABLE_FILE, mem_addr_offset, sizeof(uint32_t), 10244678124bSIgor Mammedov NVDIMM_DSM_MEM_FILE, 0); 102577286395SXiao Guangrong build_header(linker, table_data, 1026b9951413SXiao Guangrong (void *)(table_data->data + nvdimm_ssdt), 1027b9951413SXiao Guangrong "SSDT", table_data->len - nvdimm_ssdt, 1, NULL, "NVDIMM"); 102877286395SXiao Guangrong free_aml_allocator(); 102977286395SXiao Guangrong } 103077286395SXiao Guangrong 103187252e1bSXiao Guangrong void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, 1032ad9671b8SIgor Mammedov BIOSLinker *linker, GArray *dsm_dma_arrea) 103387252e1bSXiao Guangrong { 103487252e1bSXiao Guangrong GSList *device_list; 103587252e1bSXiao Guangrong 103687252e1bSXiao Guangrong /* no NVDIMM device is plugged. */ 103787252e1bSXiao Guangrong device_list = nvdimm_get_plugged_device_list(); 103887252e1bSXiao Guangrong if (!device_list) { 103987252e1bSXiao Guangrong return; 104087252e1bSXiao Guangrong } 104187252e1bSXiao Guangrong nvdimm_build_nfit(device_list, table_offsets, table_data, linker); 1042ad9671b8SIgor Mammedov nvdimm_build_ssdt(device_list, table_offsets, table_data, linker, 1043ad9671b8SIgor Mammedov dsm_dma_arrea); 104487252e1bSXiao Guangrong g_slist_free(device_list); 104587252e1bSXiao Guangrong } 1046