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" 32*5fe79386SXiao Guangrong #include "hw/nvram/fw_cfg.h" 3387252e1bSXiao Guangrong #include "hw/mem/nvdimm.h" 3487252e1bSXiao Guangrong 3587252e1bSXiao Guangrong static int nvdimm_plugged_device_list(Object *obj, void *opaque) 3687252e1bSXiao Guangrong { 3787252e1bSXiao Guangrong GSList **list = opaque; 3887252e1bSXiao Guangrong 3987252e1bSXiao Guangrong if (object_dynamic_cast(obj, TYPE_NVDIMM)) { 4087252e1bSXiao Guangrong DeviceState *dev = DEVICE(obj); 4187252e1bSXiao Guangrong 4287252e1bSXiao Guangrong if (dev->realized) { /* only realized NVDIMMs matter */ 4387252e1bSXiao Guangrong *list = g_slist_append(*list, DEVICE(obj)); 4487252e1bSXiao Guangrong } 4587252e1bSXiao Guangrong } 4687252e1bSXiao Guangrong 4787252e1bSXiao Guangrong object_child_foreach(obj, nvdimm_plugged_device_list, opaque); 4887252e1bSXiao Guangrong return 0; 4987252e1bSXiao Guangrong } 5087252e1bSXiao Guangrong 5187252e1bSXiao Guangrong /* 5287252e1bSXiao Guangrong * inquire plugged NVDIMM devices and link them into the list which is 5387252e1bSXiao Guangrong * returned to the caller. 5487252e1bSXiao Guangrong * 5587252e1bSXiao Guangrong * Note: it is the caller's responsibility to free the list to avoid 5687252e1bSXiao Guangrong * memory leak. 5787252e1bSXiao Guangrong */ 5887252e1bSXiao Guangrong static GSList *nvdimm_get_plugged_device_list(void) 5987252e1bSXiao Guangrong { 6087252e1bSXiao Guangrong GSList *list = NULL; 6187252e1bSXiao Guangrong 6287252e1bSXiao Guangrong object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list, 6387252e1bSXiao Guangrong &list); 6487252e1bSXiao Guangrong return list; 6587252e1bSXiao Guangrong } 6687252e1bSXiao Guangrong 6787252e1bSXiao Guangrong #define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ 6887252e1bSXiao Guangrong { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ 6987252e1bSXiao Guangrong (b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \ 7087252e1bSXiao Guangrong (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } 7187252e1bSXiao Guangrong 7287252e1bSXiao Guangrong /* 7387252e1bSXiao Guangrong * define Byte Addressable Persistent Memory (PM) Region according to 7487252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure. 7587252e1bSXiao Guangrong */ 7687252e1bSXiao Guangrong static const uint8_t nvdimm_nfit_spa_uuid[] = 7787252e1bSXiao Guangrong NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, 7887252e1bSXiao Guangrong 0x18, 0xb7, 0x8c, 0xdb); 7987252e1bSXiao Guangrong 8087252e1bSXiao Guangrong /* 8187252e1bSXiao Guangrong * NVDIMM Firmware Interface Table 8287252e1bSXiao Guangrong * @signature: "NFIT" 8387252e1bSXiao Guangrong * 8487252e1bSXiao Guangrong * It provides information that allows OSPM to enumerate NVDIMM present in 8587252e1bSXiao Guangrong * the platform and associate system physical address ranges created by the 8687252e1bSXiao Guangrong * NVDIMMs. 8787252e1bSXiao Guangrong * 8887252e1bSXiao Guangrong * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) 8987252e1bSXiao Guangrong */ 9087252e1bSXiao Guangrong struct NvdimmNfitHeader { 9187252e1bSXiao Guangrong ACPI_TABLE_HEADER_DEF 9287252e1bSXiao Guangrong uint32_t reserved; 9387252e1bSXiao Guangrong } QEMU_PACKED; 9487252e1bSXiao Guangrong typedef struct NvdimmNfitHeader NvdimmNfitHeader; 9587252e1bSXiao Guangrong 9687252e1bSXiao Guangrong /* 9787252e1bSXiao Guangrong * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware 9887252e1bSXiao Guangrong * Interface Table (NFIT). 9987252e1bSXiao Guangrong */ 10087252e1bSXiao Guangrong 10187252e1bSXiao Guangrong /* 10287252e1bSXiao Guangrong * System Physical Address Range Structure 10387252e1bSXiao Guangrong * 10487252e1bSXiao Guangrong * It describes the system physical address ranges occupied by NVDIMMs and 10587252e1bSXiao Guangrong * the types of the regions. 10687252e1bSXiao Guangrong */ 10787252e1bSXiao Guangrong struct NvdimmNfitSpa { 10887252e1bSXiao Guangrong uint16_t type; 10987252e1bSXiao Guangrong uint16_t length; 11087252e1bSXiao Guangrong uint16_t spa_index; 11187252e1bSXiao Guangrong uint16_t flags; 11287252e1bSXiao Guangrong uint32_t reserved; 11387252e1bSXiao Guangrong uint32_t proximity_domain; 11487252e1bSXiao Guangrong uint8_t type_guid[16]; 11587252e1bSXiao Guangrong uint64_t spa_base; 11687252e1bSXiao Guangrong uint64_t spa_length; 11787252e1bSXiao Guangrong uint64_t mem_attr; 11887252e1bSXiao Guangrong } QEMU_PACKED; 11987252e1bSXiao Guangrong typedef struct NvdimmNfitSpa NvdimmNfitSpa; 12087252e1bSXiao Guangrong 12187252e1bSXiao Guangrong /* 12287252e1bSXiao Guangrong * Memory Device to System Physical Address Range Mapping Structure 12387252e1bSXiao Guangrong * 12487252e1bSXiao Guangrong * It enables identifying each NVDIMM region and the corresponding SPA 12587252e1bSXiao Guangrong * describing the memory interleave 12687252e1bSXiao Guangrong */ 12787252e1bSXiao Guangrong struct NvdimmNfitMemDev { 12887252e1bSXiao Guangrong uint16_t type; 12987252e1bSXiao Guangrong uint16_t length; 13087252e1bSXiao Guangrong uint32_t nfit_handle; 13187252e1bSXiao Guangrong uint16_t phys_id; 13287252e1bSXiao Guangrong uint16_t region_id; 13387252e1bSXiao Guangrong uint16_t spa_index; 13487252e1bSXiao Guangrong uint16_t dcr_index; 13587252e1bSXiao Guangrong uint64_t region_len; 13687252e1bSXiao Guangrong uint64_t region_offset; 13787252e1bSXiao Guangrong uint64_t region_dpa; 13887252e1bSXiao Guangrong uint16_t interleave_index; 13987252e1bSXiao Guangrong uint16_t interleave_ways; 14087252e1bSXiao Guangrong uint16_t flags; 14187252e1bSXiao Guangrong uint16_t reserved; 14287252e1bSXiao Guangrong } QEMU_PACKED; 14387252e1bSXiao Guangrong typedef struct NvdimmNfitMemDev NvdimmNfitMemDev; 14487252e1bSXiao Guangrong 14587252e1bSXiao Guangrong /* 14687252e1bSXiao Guangrong * NVDIMM Control Region Structure 14787252e1bSXiao Guangrong * 14887252e1bSXiao Guangrong * It describes the NVDIMM and if applicable, Block Control Window. 14987252e1bSXiao Guangrong */ 15087252e1bSXiao Guangrong struct NvdimmNfitControlRegion { 15187252e1bSXiao Guangrong uint16_t type; 15287252e1bSXiao Guangrong uint16_t length; 15387252e1bSXiao Guangrong uint16_t dcr_index; 15487252e1bSXiao Guangrong uint16_t vendor_id; 15587252e1bSXiao Guangrong uint16_t device_id; 15687252e1bSXiao Guangrong uint16_t revision_id; 15787252e1bSXiao Guangrong uint16_t sub_vendor_id; 15887252e1bSXiao Guangrong uint16_t sub_device_id; 15987252e1bSXiao Guangrong uint16_t sub_revision_id; 16087252e1bSXiao Guangrong uint8_t reserved[6]; 16187252e1bSXiao Guangrong uint32_t serial_number; 16287252e1bSXiao Guangrong uint16_t fic; 16387252e1bSXiao Guangrong uint16_t num_bcw; 16487252e1bSXiao Guangrong uint64_t bcw_size; 16587252e1bSXiao Guangrong uint64_t cmd_offset; 16687252e1bSXiao Guangrong uint64_t cmd_size; 16787252e1bSXiao Guangrong uint64_t status_offset; 16887252e1bSXiao Guangrong uint64_t status_size; 16987252e1bSXiao Guangrong uint16_t flags; 17087252e1bSXiao Guangrong uint8_t reserved2[6]; 17187252e1bSXiao Guangrong } QEMU_PACKED; 17287252e1bSXiao Guangrong typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; 17387252e1bSXiao Guangrong 17487252e1bSXiao Guangrong /* 17587252e1bSXiao Guangrong * Module serial number is a unique number for each device. We use the 17687252e1bSXiao Guangrong * slot id of NVDIMM device to generate this number so that each device 17787252e1bSXiao Guangrong * associates with a different number. 17887252e1bSXiao Guangrong * 17987252e1bSXiao Guangrong * 0x123456 is a magic number we arbitrarily chose. 18087252e1bSXiao Guangrong */ 18187252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_sn(int slot) 18287252e1bSXiao Guangrong { 18387252e1bSXiao Guangrong return 0x123456 + slot; 18487252e1bSXiao Guangrong } 18587252e1bSXiao Guangrong 18687252e1bSXiao Guangrong /* 18787252e1bSXiao Guangrong * handle is used to uniquely associate nfit_memdev structure with NVDIMM 18887252e1bSXiao Guangrong * ACPI device - nfit_memdev.nfit_handle matches with the value returned 18987252e1bSXiao Guangrong * by ACPI device _ADR method. 19087252e1bSXiao Guangrong * 19187252e1bSXiao Guangrong * We generate the handle with the slot id of NVDIMM device and reserve 19287252e1bSXiao Guangrong * 0 for NVDIMM root device. 19387252e1bSXiao Guangrong */ 19487252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_handle(int slot) 19587252e1bSXiao Guangrong { 19687252e1bSXiao Guangrong return slot + 1; 19787252e1bSXiao Guangrong } 19887252e1bSXiao Guangrong 19987252e1bSXiao Guangrong /* 20087252e1bSXiao Guangrong * index uniquely identifies the structure, 0 is reserved which indicates 20187252e1bSXiao Guangrong * that the structure is not valid or the associated structure is not 20287252e1bSXiao Guangrong * present. 20387252e1bSXiao Guangrong * 20487252e1bSXiao Guangrong * Each NVDIMM device needs two indexes, one for nfit_spa and another for 20587252e1bSXiao Guangrong * nfit_dc which are generated by the slot id of NVDIMM device. 20687252e1bSXiao Guangrong */ 20787252e1bSXiao Guangrong static uint16_t nvdimm_slot_to_spa_index(int slot) 20887252e1bSXiao Guangrong { 20987252e1bSXiao Guangrong return (slot + 1) << 1; 21087252e1bSXiao Guangrong } 21187252e1bSXiao Guangrong 21287252e1bSXiao Guangrong /* See the comments of nvdimm_slot_to_spa_index(). */ 21387252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_dcr_index(int slot) 21487252e1bSXiao Guangrong { 21587252e1bSXiao Guangrong return nvdimm_slot_to_spa_index(slot) + 1; 21687252e1bSXiao Guangrong } 21787252e1bSXiao Guangrong 21887252e1bSXiao Guangrong /* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */ 21987252e1bSXiao Guangrong static void 22087252e1bSXiao Guangrong nvdimm_build_structure_spa(GArray *structures, DeviceState *dev) 22187252e1bSXiao Guangrong { 22287252e1bSXiao Guangrong NvdimmNfitSpa *nfit_spa; 22387252e1bSXiao Guangrong uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, 22487252e1bSXiao Guangrong NULL); 22587252e1bSXiao Guangrong uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, 22687252e1bSXiao Guangrong NULL); 22787252e1bSXiao Guangrong uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, 22887252e1bSXiao Guangrong NULL); 22987252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 23087252e1bSXiao Guangrong NULL); 23187252e1bSXiao Guangrong 23287252e1bSXiao Guangrong nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa)); 23387252e1bSXiao Guangrong 23487252e1bSXiao Guangrong nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range 23587252e1bSXiao Guangrong Structure */); 23687252e1bSXiao Guangrong nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa)); 23787252e1bSXiao Guangrong nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 23887252e1bSXiao Guangrong 23987252e1bSXiao Guangrong /* 24087252e1bSXiao Guangrong * Control region is strict as all the device info, such as SN, index, 24187252e1bSXiao Guangrong * is associated with slot id. 24287252e1bSXiao Guangrong */ 24387252e1bSXiao Guangrong nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for 24487252e1bSXiao Guangrong management during hot add/online 24587252e1bSXiao Guangrong operation */ | 24687252e1bSXiao Guangrong 2 /* Data in Proximity Domain field is 24787252e1bSXiao Guangrong valid*/); 24887252e1bSXiao Guangrong 24987252e1bSXiao Guangrong /* NUMA node. */ 25087252e1bSXiao Guangrong nfit_spa->proximity_domain = cpu_to_le32(node); 25187252e1bSXiao Guangrong /* the region reported as PMEM. */ 25287252e1bSXiao Guangrong memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid, 25387252e1bSXiao Guangrong sizeof(nvdimm_nfit_spa_uuid)); 25487252e1bSXiao Guangrong 25587252e1bSXiao Guangrong nfit_spa->spa_base = cpu_to_le64(addr); 25687252e1bSXiao Guangrong nfit_spa->spa_length = cpu_to_le64(size); 25787252e1bSXiao Guangrong 25887252e1bSXiao Guangrong /* It is the PMEM and can be cached as writeback. */ 25987252e1bSXiao Guangrong nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ | 26087252e1bSXiao Guangrong 0x8000ULL /* EFI_MEMORY_NV */); 26187252e1bSXiao Guangrong } 26287252e1bSXiao Guangrong 26387252e1bSXiao Guangrong /* 26487252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping 26587252e1bSXiao Guangrong * Structure 26687252e1bSXiao Guangrong */ 26787252e1bSXiao Guangrong static void 26887252e1bSXiao Guangrong nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev) 26987252e1bSXiao Guangrong { 27087252e1bSXiao Guangrong NvdimmNfitMemDev *nfit_memdev; 27187252e1bSXiao Guangrong uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, 27287252e1bSXiao Guangrong NULL); 27387252e1bSXiao Guangrong uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, 27487252e1bSXiao Guangrong NULL); 27587252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 27687252e1bSXiao Guangrong NULL); 27787252e1bSXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 27887252e1bSXiao Guangrong 27987252e1bSXiao Guangrong nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev)); 28087252e1bSXiao Guangrong 28187252e1bSXiao Guangrong nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address 28287252e1bSXiao Guangrong Range Map Structure*/); 28387252e1bSXiao Guangrong nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev)); 28487252e1bSXiao Guangrong nfit_memdev->nfit_handle = cpu_to_le32(handle); 28587252e1bSXiao Guangrong 28687252e1bSXiao Guangrong /* 28787252e1bSXiao Guangrong * associate memory device with System Physical Address Range 28887252e1bSXiao Guangrong * Structure. 28987252e1bSXiao Guangrong */ 29087252e1bSXiao Guangrong nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 29187252e1bSXiao Guangrong /* associate memory device with Control Region Structure. */ 29287252e1bSXiao Guangrong nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 29387252e1bSXiao Guangrong 29487252e1bSXiao Guangrong /* The memory region on the device. */ 29587252e1bSXiao Guangrong nfit_memdev->region_len = cpu_to_le64(size); 29687252e1bSXiao Guangrong nfit_memdev->region_dpa = cpu_to_le64(addr); 29787252e1bSXiao Guangrong 29887252e1bSXiao Guangrong /* Only one interleave for PMEM. */ 29987252e1bSXiao Guangrong nfit_memdev->interleave_ways = cpu_to_le16(1); 30087252e1bSXiao Guangrong } 30187252e1bSXiao Guangrong 30287252e1bSXiao Guangrong /* 30387252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure. 30487252e1bSXiao Guangrong */ 30587252e1bSXiao Guangrong static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) 30687252e1bSXiao Guangrong { 30787252e1bSXiao Guangrong NvdimmNfitControlRegion *nfit_dcr; 30887252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 30987252e1bSXiao Guangrong NULL); 31087252e1bSXiao Guangrong uint32_t sn = nvdimm_slot_to_sn(slot); 31187252e1bSXiao Guangrong 31287252e1bSXiao Guangrong nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr)); 31387252e1bSXiao Guangrong 31487252e1bSXiao Guangrong nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */); 31587252e1bSXiao Guangrong nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr)); 31687252e1bSXiao Guangrong nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 31787252e1bSXiao Guangrong 31887252e1bSXiao Guangrong /* vendor: Intel. */ 31987252e1bSXiao Guangrong nfit_dcr->vendor_id = cpu_to_le16(0x8086); 32087252e1bSXiao Guangrong nfit_dcr->device_id = cpu_to_le16(1); 32187252e1bSXiao Guangrong 32287252e1bSXiao Guangrong /* The _DSM method is following Intel's DSM specification. */ 32387252e1bSXiao Guangrong nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported 32487252e1bSXiao Guangrong in ACPI 6.0 is 1. */); 32587252e1bSXiao Guangrong nfit_dcr->serial_number = cpu_to_le32(sn); 32687252e1bSXiao Guangrong nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter 32787252e1bSXiao Guangrong 2: NVDIMM Device Specific Method 32887252e1bSXiao Guangrong (DSM) in DSM Spec Rev1.*/); 32987252e1bSXiao Guangrong } 33087252e1bSXiao Guangrong 33187252e1bSXiao Guangrong static GArray *nvdimm_build_device_structure(GSList *device_list) 33287252e1bSXiao Guangrong { 33387252e1bSXiao Guangrong GArray *structures = g_array_new(false, true /* clear */, 1); 33487252e1bSXiao Guangrong 33587252e1bSXiao Guangrong for (; device_list; device_list = device_list->next) { 33687252e1bSXiao Guangrong DeviceState *dev = device_list->data; 33787252e1bSXiao Guangrong 33887252e1bSXiao Guangrong /* build System Physical Address Range Structure. */ 33987252e1bSXiao Guangrong nvdimm_build_structure_spa(structures, dev); 34087252e1bSXiao Guangrong 34187252e1bSXiao Guangrong /* 34287252e1bSXiao Guangrong * build Memory Device to System Physical Address Range Mapping 34387252e1bSXiao Guangrong * Structure. 34487252e1bSXiao Guangrong */ 34587252e1bSXiao Guangrong nvdimm_build_structure_memdev(structures, dev); 34687252e1bSXiao Guangrong 34787252e1bSXiao Guangrong /* build NVDIMM Control Region Structure. */ 34887252e1bSXiao Guangrong nvdimm_build_structure_dcr(structures, dev); 34987252e1bSXiao Guangrong } 35087252e1bSXiao Guangrong 35187252e1bSXiao Guangrong return structures; 35287252e1bSXiao Guangrong } 35387252e1bSXiao Guangrong 35487252e1bSXiao Guangrong static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets, 35587252e1bSXiao Guangrong GArray *table_data, GArray *linker) 35687252e1bSXiao Guangrong { 35787252e1bSXiao Guangrong GArray *structures = nvdimm_build_device_structure(device_list); 358c8e6c938SHaozhong Zhang unsigned int header; 35987252e1bSXiao Guangrong 36087252e1bSXiao Guangrong acpi_add_table(table_offsets, table_data); 36187252e1bSXiao Guangrong 36287252e1bSXiao Guangrong /* NFIT header. */ 363c8e6c938SHaozhong Zhang header = table_data->len; 364c8e6c938SHaozhong Zhang acpi_data_push(table_data, sizeof(NvdimmNfitHeader)); 36587252e1bSXiao Guangrong /* NVDIMM device structures. */ 36687252e1bSXiao Guangrong g_array_append_vals(table_data, structures->data, structures->len); 36787252e1bSXiao Guangrong 368c8e6c938SHaozhong Zhang build_header(linker, table_data, 369c8e6c938SHaozhong Zhang (void *)(table_data->data + header), "NFIT", 37037ad223cSLaszlo Ersek sizeof(NvdimmNfitHeader) + structures->len, 1, NULL, NULL); 37187252e1bSXiao Guangrong g_array_free(structures, true); 37287252e1bSXiao Guangrong } 37387252e1bSXiao Guangrong 374*5fe79386SXiao Guangrong static uint64_t 375*5fe79386SXiao Guangrong nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size) 376*5fe79386SXiao Guangrong { 377*5fe79386SXiao Guangrong return 0; 378*5fe79386SXiao Guangrong } 379*5fe79386SXiao Guangrong 380*5fe79386SXiao Guangrong static void 381*5fe79386SXiao Guangrong nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) 382*5fe79386SXiao Guangrong { 383*5fe79386SXiao Guangrong } 384*5fe79386SXiao Guangrong 385*5fe79386SXiao Guangrong static const MemoryRegionOps nvdimm_dsm_ops = { 386*5fe79386SXiao Guangrong .read = nvdimm_dsm_read, 387*5fe79386SXiao Guangrong .write = nvdimm_dsm_write, 388*5fe79386SXiao Guangrong .endianness = DEVICE_LITTLE_ENDIAN, 389*5fe79386SXiao Guangrong .valid = { 390*5fe79386SXiao Guangrong .min_access_size = 4, 391*5fe79386SXiao Guangrong .max_access_size = 4, 392*5fe79386SXiao Guangrong }, 393*5fe79386SXiao Guangrong }; 394*5fe79386SXiao Guangrong 395*5fe79386SXiao Guangrong void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, 396*5fe79386SXiao Guangrong FWCfgState *fw_cfg, Object *owner) 397*5fe79386SXiao Guangrong { 398*5fe79386SXiao Guangrong memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state, 399*5fe79386SXiao Guangrong "nvdimm-acpi-io", NVDIMM_ACPI_IO_LEN); 400*5fe79386SXiao Guangrong memory_region_add_subregion(io, NVDIMM_ACPI_IO_BASE, &state->io_mr); 401*5fe79386SXiao Guangrong 402*5fe79386SXiao Guangrong state->dsm_mem = g_array_new(false, true /* clear */, 1); 403*5fe79386SXiao Guangrong acpi_data_push(state->dsm_mem, TARGET_PAGE_SIZE); 404*5fe79386SXiao Guangrong fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data, 405*5fe79386SXiao Guangrong state->dsm_mem->len); 406*5fe79386SXiao Guangrong } 407*5fe79386SXiao Guangrong 40877286395SXiao Guangrong #define NVDIMM_COMMON_DSM "NCAL" 40977286395SXiao Guangrong 41077286395SXiao Guangrong static void nvdimm_build_common_dsm(Aml *dev) 41177286395SXiao Guangrong { 41277286395SXiao Guangrong Aml *method, *ifctx, *function; 41377286395SXiao Guangrong uint8_t byte_list[1]; 41477286395SXiao Guangrong 41577286395SXiao Guangrong method = aml_method(NVDIMM_COMMON_DSM, 4, AML_NOTSERIALIZED); 41677286395SXiao Guangrong function = aml_arg(2); 41777286395SXiao Guangrong 41877286395SXiao Guangrong /* 41977286395SXiao Guangrong * function 0 is called to inquire what functions are supported by 42077286395SXiao Guangrong * OSPM 42177286395SXiao Guangrong */ 42277286395SXiao Guangrong ifctx = aml_if(aml_equal(function, aml_int(0))); 42377286395SXiao Guangrong byte_list[0] = 0 /* No function Supported */; 42477286395SXiao Guangrong aml_append(ifctx, aml_return(aml_buffer(1, byte_list))); 42577286395SXiao Guangrong aml_append(method, ifctx); 42677286395SXiao Guangrong 42777286395SXiao Guangrong /* No function is supported yet. */ 42877286395SXiao Guangrong byte_list[0] = 1 /* Not Supported */; 42977286395SXiao Guangrong aml_append(method, aml_return(aml_buffer(1, byte_list))); 43077286395SXiao Guangrong 43177286395SXiao Guangrong aml_append(dev, method); 43277286395SXiao Guangrong } 43377286395SXiao Guangrong 43477286395SXiao Guangrong static void nvdimm_build_device_dsm(Aml *dev) 43577286395SXiao Guangrong { 43677286395SXiao Guangrong Aml *method; 43777286395SXiao Guangrong 43877286395SXiao Guangrong method = aml_method("_DSM", 4, AML_NOTSERIALIZED); 43977286395SXiao Guangrong aml_append(method, aml_return(aml_call4(NVDIMM_COMMON_DSM, aml_arg(0), 44077286395SXiao Guangrong aml_arg(1), aml_arg(2), aml_arg(3)))); 44177286395SXiao Guangrong aml_append(dev, method); 44277286395SXiao Guangrong } 44377286395SXiao Guangrong 44477286395SXiao Guangrong static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev) 44577286395SXiao Guangrong { 44677286395SXiao Guangrong for (; device_list; device_list = device_list->next) { 44777286395SXiao Guangrong DeviceState *dev = device_list->data; 44877286395SXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 44977286395SXiao Guangrong NULL); 45077286395SXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 45177286395SXiao Guangrong Aml *nvdimm_dev; 45277286395SXiao Guangrong 45377286395SXiao Guangrong nvdimm_dev = aml_device("NV%02X", slot); 45477286395SXiao Guangrong 45577286395SXiao Guangrong /* 45677286395SXiao Guangrong * ACPI 6.0: 9.20 NVDIMM Devices: 45777286395SXiao Guangrong * 45877286395SXiao Guangrong * _ADR object that is used to supply OSPM with unique address 45977286395SXiao Guangrong * of the NVDIMM device. This is done by returning the NFIT Device 46077286395SXiao Guangrong * handle that is used to identify the associated entries in ACPI 46177286395SXiao Guangrong * table NFIT or _FIT. 46277286395SXiao Guangrong */ 46377286395SXiao Guangrong aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle))); 46477286395SXiao Guangrong 46577286395SXiao Guangrong nvdimm_build_device_dsm(nvdimm_dev); 46677286395SXiao Guangrong aml_append(root_dev, nvdimm_dev); 46777286395SXiao Guangrong } 46877286395SXiao Guangrong } 46977286395SXiao Guangrong 47077286395SXiao Guangrong static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, 47177286395SXiao Guangrong GArray *table_data, GArray *linker) 47277286395SXiao Guangrong { 47377286395SXiao Guangrong Aml *ssdt, *sb_scope, *dev; 47477286395SXiao Guangrong 47577286395SXiao Guangrong acpi_add_table(table_offsets, table_data); 47677286395SXiao Guangrong 47777286395SXiao Guangrong ssdt = init_aml_allocator(); 47877286395SXiao Guangrong acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); 47977286395SXiao Guangrong 48077286395SXiao Guangrong sb_scope = aml_scope("\\_SB"); 48177286395SXiao Guangrong 48277286395SXiao Guangrong dev = aml_device("NVDR"); 48377286395SXiao Guangrong 48477286395SXiao Guangrong /* 48577286395SXiao Guangrong * ACPI 6.0: 9.20 NVDIMM Devices: 48677286395SXiao Guangrong * 48777286395SXiao Guangrong * The ACPI Name Space device uses _HID of ACPI0012 to identify the root 48877286395SXiao Guangrong * NVDIMM interface device. Platform firmware is required to contain one 48977286395SXiao Guangrong * such device in _SB scope if NVDIMMs support is exposed by platform to 49077286395SXiao Guangrong * OSPM. 49177286395SXiao Guangrong * For each NVDIMM present or intended to be supported by platform, 49277286395SXiao Guangrong * platform firmware also exposes an ACPI Namespace Device under the 49377286395SXiao Guangrong * root device. 49477286395SXiao Guangrong */ 49577286395SXiao Guangrong aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012"))); 49677286395SXiao Guangrong 49777286395SXiao Guangrong nvdimm_build_common_dsm(dev); 49877286395SXiao Guangrong nvdimm_build_device_dsm(dev); 49977286395SXiao Guangrong 50077286395SXiao Guangrong nvdimm_build_nvdimm_devices(device_list, dev); 50177286395SXiao Guangrong 50277286395SXiao Guangrong aml_append(sb_scope, dev); 50377286395SXiao Guangrong 50477286395SXiao Guangrong aml_append(ssdt, sb_scope); 50577286395SXiao Guangrong /* copy AML table into ACPI tables blob and patch header there */ 50677286395SXiao Guangrong g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); 50777286395SXiao Guangrong build_header(linker, table_data, 50877286395SXiao Guangrong (void *)(table_data->data + table_data->len - ssdt->buf->len), 50937ad223cSLaszlo Ersek "SSDT", ssdt->buf->len, 1, NULL, "NVDIMM"); 51077286395SXiao Guangrong free_aml_allocator(); 51177286395SXiao Guangrong } 51277286395SXiao Guangrong 51387252e1bSXiao Guangrong void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, 51487252e1bSXiao Guangrong GArray *linker) 51587252e1bSXiao Guangrong { 51687252e1bSXiao Guangrong GSList *device_list; 51787252e1bSXiao Guangrong 51887252e1bSXiao Guangrong /* no NVDIMM device is plugged. */ 51987252e1bSXiao Guangrong device_list = nvdimm_get_plugged_device_list(); 52087252e1bSXiao Guangrong if (!device_list) { 52187252e1bSXiao Guangrong return; 52287252e1bSXiao Guangrong } 52387252e1bSXiao Guangrong nvdimm_build_nfit(device_list, table_offsets, table_data, linker); 52477286395SXiao Guangrong nvdimm_build_ssdt(device_list, table_offsets, table_data, linker); 52587252e1bSXiao Guangrong g_slist_free(device_list); 52687252e1bSXiao Guangrong } 527