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 36cf7c0ff5SXiao Guangrong static int nvdimm_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 *list = g_slist_append(*list, DEVICE(obj)); 4287252e1bSXiao Guangrong } 4387252e1bSXiao Guangrong 44cf7c0ff5SXiao Guangrong object_child_foreach(obj, nvdimm_device_list, opaque); 4587252e1bSXiao Guangrong return 0; 4687252e1bSXiao Guangrong } 4787252e1bSXiao Guangrong 4887252e1bSXiao Guangrong /* 49cf7c0ff5SXiao Guangrong * inquire NVDIMM devices and link them into the list which is 5087252e1bSXiao Guangrong * returned to the caller. 5187252e1bSXiao Guangrong * 5287252e1bSXiao Guangrong * Note: it is the caller's responsibility to free the list to avoid 5387252e1bSXiao Guangrong * memory leak. 5487252e1bSXiao Guangrong */ 55cf7c0ff5SXiao Guangrong static GSList *nvdimm_get_device_list(void) 5687252e1bSXiao Guangrong { 5787252e1bSXiao Guangrong GSList *list = NULL; 5887252e1bSXiao Guangrong 59cf7c0ff5SXiao Guangrong object_child_foreach(qdev_get_machine(), nvdimm_device_list, &list); 6087252e1bSXiao Guangrong return list; 6187252e1bSXiao Guangrong } 6287252e1bSXiao Guangrong 6387252e1bSXiao Guangrong #define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ 6487252e1bSXiao Guangrong { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ 6587252e1bSXiao Guangrong (b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \ 6687252e1bSXiao Guangrong (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } 6787252e1bSXiao Guangrong 6887252e1bSXiao Guangrong /* 6987252e1bSXiao Guangrong * define Byte Addressable Persistent Memory (PM) Region according to 7087252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure. 7187252e1bSXiao Guangrong */ 7287252e1bSXiao Guangrong static const uint8_t nvdimm_nfit_spa_uuid[] = 7387252e1bSXiao Guangrong NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, 7487252e1bSXiao Guangrong 0x18, 0xb7, 0x8c, 0xdb); 7587252e1bSXiao Guangrong 7687252e1bSXiao Guangrong /* 7787252e1bSXiao Guangrong * NVDIMM Firmware Interface Table 7887252e1bSXiao Guangrong * @signature: "NFIT" 7987252e1bSXiao Guangrong * 8087252e1bSXiao Guangrong * It provides information that allows OSPM to enumerate NVDIMM present in 8187252e1bSXiao Guangrong * the platform and associate system physical address ranges created by the 8287252e1bSXiao Guangrong * NVDIMMs. 8387252e1bSXiao Guangrong * 8487252e1bSXiao Guangrong * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) 8587252e1bSXiao Guangrong */ 8687252e1bSXiao Guangrong struct NvdimmNfitHeader { 8787252e1bSXiao Guangrong ACPI_TABLE_HEADER_DEF 8887252e1bSXiao Guangrong uint32_t reserved; 8987252e1bSXiao Guangrong } QEMU_PACKED; 9087252e1bSXiao Guangrong typedef struct NvdimmNfitHeader NvdimmNfitHeader; 9187252e1bSXiao Guangrong 9287252e1bSXiao Guangrong /* 9387252e1bSXiao Guangrong * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware 9487252e1bSXiao Guangrong * Interface Table (NFIT). 9587252e1bSXiao Guangrong */ 9687252e1bSXiao Guangrong 9787252e1bSXiao Guangrong /* 9887252e1bSXiao Guangrong * System Physical Address Range Structure 9987252e1bSXiao Guangrong * 10087252e1bSXiao Guangrong * It describes the system physical address ranges occupied by NVDIMMs and 10187252e1bSXiao Guangrong * the types of the regions. 10287252e1bSXiao Guangrong */ 10387252e1bSXiao Guangrong struct NvdimmNfitSpa { 10487252e1bSXiao Guangrong uint16_t type; 10587252e1bSXiao Guangrong uint16_t length; 10687252e1bSXiao Guangrong uint16_t spa_index; 10787252e1bSXiao Guangrong uint16_t flags; 10887252e1bSXiao Guangrong uint32_t reserved; 10987252e1bSXiao Guangrong uint32_t proximity_domain; 11087252e1bSXiao Guangrong uint8_t type_guid[16]; 11187252e1bSXiao Guangrong uint64_t spa_base; 11287252e1bSXiao Guangrong uint64_t spa_length; 11387252e1bSXiao Guangrong uint64_t mem_attr; 11487252e1bSXiao Guangrong } QEMU_PACKED; 11587252e1bSXiao Guangrong typedef struct NvdimmNfitSpa NvdimmNfitSpa; 11687252e1bSXiao Guangrong 11787252e1bSXiao Guangrong /* 11887252e1bSXiao Guangrong * Memory Device to System Physical Address Range Mapping Structure 11987252e1bSXiao Guangrong * 12087252e1bSXiao Guangrong * It enables identifying each NVDIMM region and the corresponding SPA 12187252e1bSXiao Guangrong * describing the memory interleave 12287252e1bSXiao Guangrong */ 12387252e1bSXiao Guangrong struct NvdimmNfitMemDev { 12487252e1bSXiao Guangrong uint16_t type; 12587252e1bSXiao Guangrong uint16_t length; 12687252e1bSXiao Guangrong uint32_t nfit_handle; 12787252e1bSXiao Guangrong uint16_t phys_id; 12887252e1bSXiao Guangrong uint16_t region_id; 12987252e1bSXiao Guangrong uint16_t spa_index; 13087252e1bSXiao Guangrong uint16_t dcr_index; 13187252e1bSXiao Guangrong uint64_t region_len; 13287252e1bSXiao Guangrong uint64_t region_offset; 13387252e1bSXiao Guangrong uint64_t region_dpa; 13487252e1bSXiao Guangrong uint16_t interleave_index; 13587252e1bSXiao Guangrong uint16_t interleave_ways; 13687252e1bSXiao Guangrong uint16_t flags; 13787252e1bSXiao Guangrong uint16_t reserved; 13887252e1bSXiao Guangrong } QEMU_PACKED; 13987252e1bSXiao Guangrong typedef struct NvdimmNfitMemDev NvdimmNfitMemDev; 14087252e1bSXiao Guangrong 14187252e1bSXiao Guangrong /* 14287252e1bSXiao Guangrong * NVDIMM Control Region Structure 14387252e1bSXiao Guangrong * 14487252e1bSXiao Guangrong * It describes the NVDIMM and if applicable, Block Control Window. 14587252e1bSXiao Guangrong */ 14687252e1bSXiao Guangrong struct NvdimmNfitControlRegion { 14787252e1bSXiao Guangrong uint16_t type; 14887252e1bSXiao Guangrong uint16_t length; 14987252e1bSXiao Guangrong uint16_t dcr_index; 15087252e1bSXiao Guangrong uint16_t vendor_id; 15187252e1bSXiao Guangrong uint16_t device_id; 15287252e1bSXiao Guangrong uint16_t revision_id; 15387252e1bSXiao Guangrong uint16_t sub_vendor_id; 15487252e1bSXiao Guangrong uint16_t sub_device_id; 15587252e1bSXiao Guangrong uint16_t sub_revision_id; 15687252e1bSXiao Guangrong uint8_t reserved[6]; 15787252e1bSXiao Guangrong uint32_t serial_number; 15887252e1bSXiao Guangrong uint16_t fic; 15987252e1bSXiao Guangrong uint16_t num_bcw; 16087252e1bSXiao Guangrong uint64_t bcw_size; 16187252e1bSXiao Guangrong uint64_t cmd_offset; 16287252e1bSXiao Guangrong uint64_t cmd_size; 16387252e1bSXiao Guangrong uint64_t status_offset; 16487252e1bSXiao Guangrong uint64_t status_size; 16587252e1bSXiao Guangrong uint16_t flags; 16687252e1bSXiao Guangrong uint8_t reserved2[6]; 16787252e1bSXiao Guangrong } QEMU_PACKED; 16887252e1bSXiao Guangrong typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; 16987252e1bSXiao Guangrong 17087252e1bSXiao Guangrong /* 17187252e1bSXiao Guangrong * Module serial number is a unique number for each device. We use the 17287252e1bSXiao Guangrong * slot id of NVDIMM device to generate this number so that each device 17387252e1bSXiao Guangrong * associates with a different number. 17487252e1bSXiao Guangrong * 17587252e1bSXiao Guangrong * 0x123456 is a magic number we arbitrarily chose. 17687252e1bSXiao Guangrong */ 17787252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_sn(int slot) 17887252e1bSXiao Guangrong { 17987252e1bSXiao Guangrong return 0x123456 + slot; 18087252e1bSXiao Guangrong } 18187252e1bSXiao Guangrong 18287252e1bSXiao Guangrong /* 18387252e1bSXiao Guangrong * handle is used to uniquely associate nfit_memdev structure with NVDIMM 18487252e1bSXiao Guangrong * ACPI device - nfit_memdev.nfit_handle matches with the value returned 18587252e1bSXiao Guangrong * by ACPI device _ADR method. 18687252e1bSXiao Guangrong * 18787252e1bSXiao Guangrong * We generate the handle with the slot id of NVDIMM device and reserve 18887252e1bSXiao Guangrong * 0 for NVDIMM root device. 18987252e1bSXiao Guangrong */ 19087252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_handle(int slot) 19187252e1bSXiao Guangrong { 19287252e1bSXiao Guangrong return slot + 1; 19387252e1bSXiao Guangrong } 19487252e1bSXiao Guangrong 19587252e1bSXiao Guangrong /* 19687252e1bSXiao Guangrong * index uniquely identifies the structure, 0 is reserved which indicates 19787252e1bSXiao Guangrong * that the structure is not valid or the associated structure is not 19887252e1bSXiao Guangrong * present. 19987252e1bSXiao Guangrong * 20087252e1bSXiao Guangrong * Each NVDIMM device needs two indexes, one for nfit_spa and another for 20187252e1bSXiao Guangrong * nfit_dc which are generated by the slot id of NVDIMM device. 20287252e1bSXiao Guangrong */ 20387252e1bSXiao Guangrong static uint16_t nvdimm_slot_to_spa_index(int slot) 20487252e1bSXiao Guangrong { 20587252e1bSXiao Guangrong return (slot + 1) << 1; 20687252e1bSXiao Guangrong } 20787252e1bSXiao Guangrong 20887252e1bSXiao Guangrong /* See the comments of nvdimm_slot_to_spa_index(). */ 20987252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_dcr_index(int slot) 21087252e1bSXiao Guangrong { 21187252e1bSXiao Guangrong return nvdimm_slot_to_spa_index(slot) + 1; 21287252e1bSXiao Guangrong } 21387252e1bSXiao Guangrong 2145797dcdcSXiao Guangrong static NVDIMMDevice *nvdimm_get_device_by_handle(uint32_t handle) 2155797dcdcSXiao Guangrong { 2165797dcdcSXiao Guangrong NVDIMMDevice *nvdimm = NULL; 217cf7c0ff5SXiao Guangrong GSList *list, *device_list = nvdimm_get_device_list(); 2185797dcdcSXiao Guangrong 2195797dcdcSXiao Guangrong for (list = device_list; list; list = list->next) { 2205797dcdcSXiao Guangrong NVDIMMDevice *nvd = list->data; 2215797dcdcSXiao Guangrong int slot = object_property_get_int(OBJECT(nvd), PC_DIMM_SLOT_PROP, 2225797dcdcSXiao Guangrong NULL); 2235797dcdcSXiao Guangrong 2245797dcdcSXiao Guangrong if (nvdimm_slot_to_handle(slot) == handle) { 2255797dcdcSXiao Guangrong nvdimm = nvd; 2265797dcdcSXiao Guangrong break; 2275797dcdcSXiao Guangrong } 2285797dcdcSXiao Guangrong } 2295797dcdcSXiao Guangrong 2305797dcdcSXiao Guangrong g_slist_free(device_list); 2315797dcdcSXiao Guangrong return nvdimm; 2325797dcdcSXiao Guangrong } 2335797dcdcSXiao Guangrong 23487252e1bSXiao Guangrong /* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */ 23587252e1bSXiao Guangrong static void 23687252e1bSXiao Guangrong nvdimm_build_structure_spa(GArray *structures, DeviceState *dev) 23787252e1bSXiao Guangrong { 23887252e1bSXiao Guangrong NvdimmNfitSpa *nfit_spa; 23987252e1bSXiao Guangrong uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, 24087252e1bSXiao Guangrong NULL); 24187252e1bSXiao Guangrong uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, 24287252e1bSXiao Guangrong NULL); 24387252e1bSXiao Guangrong uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, 24487252e1bSXiao Guangrong NULL); 24587252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 24687252e1bSXiao Guangrong NULL); 24787252e1bSXiao Guangrong 24887252e1bSXiao Guangrong nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa)); 24987252e1bSXiao Guangrong 25087252e1bSXiao Guangrong nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range 25187252e1bSXiao Guangrong Structure */); 25287252e1bSXiao Guangrong nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa)); 25387252e1bSXiao Guangrong nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 25487252e1bSXiao Guangrong 25587252e1bSXiao Guangrong /* 25687252e1bSXiao Guangrong * Control region is strict as all the device info, such as SN, index, 25787252e1bSXiao Guangrong * is associated with slot id. 25887252e1bSXiao Guangrong */ 25987252e1bSXiao Guangrong nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for 26087252e1bSXiao Guangrong management during hot add/online 26187252e1bSXiao Guangrong operation */ | 26287252e1bSXiao Guangrong 2 /* Data in Proximity Domain field is 26387252e1bSXiao Guangrong valid*/); 26487252e1bSXiao Guangrong 26587252e1bSXiao Guangrong /* NUMA node. */ 26687252e1bSXiao Guangrong nfit_spa->proximity_domain = cpu_to_le32(node); 26787252e1bSXiao Guangrong /* the region reported as PMEM. */ 26887252e1bSXiao Guangrong memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid, 26987252e1bSXiao Guangrong sizeof(nvdimm_nfit_spa_uuid)); 27087252e1bSXiao Guangrong 27187252e1bSXiao Guangrong nfit_spa->spa_base = cpu_to_le64(addr); 27287252e1bSXiao Guangrong nfit_spa->spa_length = cpu_to_le64(size); 27387252e1bSXiao Guangrong 27487252e1bSXiao Guangrong /* It is the PMEM and can be cached as writeback. */ 27587252e1bSXiao Guangrong nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ | 27687252e1bSXiao Guangrong 0x8000ULL /* EFI_MEMORY_NV */); 27787252e1bSXiao Guangrong } 27887252e1bSXiao Guangrong 27987252e1bSXiao Guangrong /* 28087252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping 28187252e1bSXiao Guangrong * Structure 28287252e1bSXiao Guangrong */ 28387252e1bSXiao Guangrong static void 28487252e1bSXiao Guangrong nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev) 28587252e1bSXiao Guangrong { 28687252e1bSXiao Guangrong NvdimmNfitMemDev *nfit_memdev; 28787252e1bSXiao Guangrong uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, 28887252e1bSXiao Guangrong NULL); 28987252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 29087252e1bSXiao Guangrong NULL); 29187252e1bSXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 29287252e1bSXiao Guangrong 29387252e1bSXiao Guangrong nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev)); 29487252e1bSXiao Guangrong 29587252e1bSXiao Guangrong nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address 29687252e1bSXiao Guangrong Range Map Structure*/); 29787252e1bSXiao Guangrong nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev)); 29887252e1bSXiao Guangrong nfit_memdev->nfit_handle = cpu_to_le32(handle); 29987252e1bSXiao Guangrong 30087252e1bSXiao Guangrong /* 30187252e1bSXiao Guangrong * associate memory device with System Physical Address Range 30287252e1bSXiao Guangrong * Structure. 30387252e1bSXiao Guangrong */ 30487252e1bSXiao Guangrong nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 30587252e1bSXiao Guangrong /* associate memory device with Control Region Structure. */ 30687252e1bSXiao Guangrong nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 30787252e1bSXiao Guangrong 30887252e1bSXiao Guangrong /* The memory region on the device. */ 30987252e1bSXiao Guangrong nfit_memdev->region_len = cpu_to_le64(size); 3106ab0c4bdSXiao Guangrong /* The device address starts from 0. */ 3116ab0c4bdSXiao Guangrong nfit_memdev->region_dpa = cpu_to_le64(0); 31287252e1bSXiao Guangrong 31387252e1bSXiao Guangrong /* Only one interleave for PMEM. */ 31487252e1bSXiao Guangrong nfit_memdev->interleave_ways = cpu_to_le16(1); 31587252e1bSXiao Guangrong } 31687252e1bSXiao Guangrong 31787252e1bSXiao Guangrong /* 31887252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure. 31987252e1bSXiao Guangrong */ 32087252e1bSXiao Guangrong static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) 32187252e1bSXiao Guangrong { 32287252e1bSXiao Guangrong NvdimmNfitControlRegion *nfit_dcr; 32387252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 32487252e1bSXiao Guangrong NULL); 32587252e1bSXiao Guangrong uint32_t sn = nvdimm_slot_to_sn(slot); 32687252e1bSXiao Guangrong 32787252e1bSXiao Guangrong nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr)); 32887252e1bSXiao Guangrong 32987252e1bSXiao Guangrong nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */); 33087252e1bSXiao Guangrong nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr)); 33187252e1bSXiao Guangrong nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 33287252e1bSXiao Guangrong 33387252e1bSXiao Guangrong /* vendor: Intel. */ 33487252e1bSXiao Guangrong nfit_dcr->vendor_id = cpu_to_le16(0x8086); 33587252e1bSXiao Guangrong nfit_dcr->device_id = cpu_to_le16(1); 33687252e1bSXiao Guangrong 33787252e1bSXiao Guangrong /* The _DSM method is following Intel's DSM specification. */ 33887252e1bSXiao Guangrong nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported 33987252e1bSXiao Guangrong in ACPI 6.0 is 1. */); 34087252e1bSXiao Guangrong nfit_dcr->serial_number = cpu_to_le32(sn); 34187252e1bSXiao Guangrong nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter 34287252e1bSXiao Guangrong 2: NVDIMM Device Specific Method 34387252e1bSXiao Guangrong (DSM) in DSM Spec Rev1.*/); 34487252e1bSXiao Guangrong } 34587252e1bSXiao Guangrong 34675b0713eSXiao Guangrong static GArray *nvdimm_build_device_structure(void) 34787252e1bSXiao Guangrong { 348cf7c0ff5SXiao Guangrong GSList *device_list = nvdimm_get_device_list(); 34987252e1bSXiao Guangrong GArray *structures = g_array_new(false, true /* clear */, 1); 35087252e1bSXiao Guangrong 35187252e1bSXiao Guangrong for (; device_list; device_list = device_list->next) { 35287252e1bSXiao Guangrong DeviceState *dev = device_list->data; 35387252e1bSXiao Guangrong 35487252e1bSXiao Guangrong /* build System Physical Address Range Structure. */ 35587252e1bSXiao Guangrong nvdimm_build_structure_spa(structures, dev); 35687252e1bSXiao Guangrong 35787252e1bSXiao Guangrong /* 35887252e1bSXiao Guangrong * build Memory Device to System Physical Address Range Mapping 35987252e1bSXiao Guangrong * Structure. 36087252e1bSXiao Guangrong */ 36187252e1bSXiao Guangrong nvdimm_build_structure_memdev(structures, dev); 36287252e1bSXiao Guangrong 36387252e1bSXiao Guangrong /* build NVDIMM Control Region Structure. */ 36487252e1bSXiao Guangrong nvdimm_build_structure_dcr(structures, dev); 36587252e1bSXiao Guangrong } 36675b0713eSXiao Guangrong g_slist_free(device_list); 36787252e1bSXiao Guangrong 36887252e1bSXiao Guangrong return structures; 36987252e1bSXiao Guangrong } 37087252e1bSXiao Guangrong 37175b0713eSXiao Guangrong static void nvdimm_init_fit_buffer(NvdimmFitBuffer *fit_buf) 37275b0713eSXiao Guangrong { 37375b0713eSXiao Guangrong fit_buf->fit = g_array_new(false, true /* clear */, 1); 37475b0713eSXiao Guangrong } 37575b0713eSXiao Guangrong 37675b0713eSXiao Guangrong static void nvdimm_build_fit_buffer(NvdimmFitBuffer *fit_buf) 37775b0713eSXiao Guangrong { 37875b0713eSXiao Guangrong g_array_free(fit_buf->fit, true); 37975b0713eSXiao Guangrong fit_buf->fit = nvdimm_build_device_structure(); 38075b0713eSXiao Guangrong fit_buf->dirty = true; 38175b0713eSXiao Guangrong } 38275b0713eSXiao Guangrong 383284197e4SXiao Guangrong void nvdimm_plug(AcpiNVDIMMState *state) 38475b0713eSXiao Guangrong { 38575b0713eSXiao Guangrong nvdimm_build_fit_buffer(&state->fit_buf); 38675b0713eSXiao Guangrong } 38775b0713eSXiao Guangrong 38875b0713eSXiao Guangrong static void nvdimm_build_nfit(AcpiNVDIMMState *state, GArray *table_offsets, 3890e9b9edaSIgor Mammedov GArray *table_data, BIOSLinker *linker) 39087252e1bSXiao Guangrong { 39175b0713eSXiao Guangrong NvdimmFitBuffer *fit_buf = &state->fit_buf; 392c8e6c938SHaozhong Zhang unsigned int header; 39387252e1bSXiao Guangrong 39487252e1bSXiao Guangrong acpi_add_table(table_offsets, table_data); 39587252e1bSXiao Guangrong 39687252e1bSXiao Guangrong /* NFIT header. */ 397c8e6c938SHaozhong Zhang header = table_data->len; 398c8e6c938SHaozhong Zhang acpi_data_push(table_data, sizeof(NvdimmNfitHeader)); 39987252e1bSXiao Guangrong /* NVDIMM device structures. */ 40075b0713eSXiao Guangrong g_array_append_vals(table_data, fit_buf->fit->data, fit_buf->fit->len); 40187252e1bSXiao Guangrong 402c8e6c938SHaozhong Zhang build_header(linker, table_data, 403c8e6c938SHaozhong Zhang (void *)(table_data->data + header), "NFIT", 40475b0713eSXiao Guangrong sizeof(NvdimmNfitHeader) + fit_buf->fit->len, 1, NULL, NULL); 40587252e1bSXiao Guangrong } 40687252e1bSXiao Guangrong 40718c440e1SXiao Guangrong struct NvdimmDsmIn { 40818c440e1SXiao Guangrong uint32_t handle; 40918c440e1SXiao Guangrong uint32_t revision; 41018c440e1SXiao Guangrong uint32_t function; 41118c440e1SXiao Guangrong /* the remaining size in the page is used by arg3. */ 41218c440e1SXiao Guangrong union { 41335c5a52dSPaolo Bonzini uint8_t arg3[4084]; 41418c440e1SXiao Guangrong }; 41518c440e1SXiao Guangrong } QEMU_PACKED; 41618c440e1SXiao Guangrong typedef struct NvdimmDsmIn NvdimmDsmIn; 41735c5a52dSPaolo Bonzini QEMU_BUILD_BUG_ON(sizeof(NvdimmDsmIn) != 4096); 41818c440e1SXiao Guangrong 41918c440e1SXiao Guangrong struct NvdimmDsmOut { 42018c440e1SXiao Guangrong /* the size of buffer filled by QEMU. */ 42118c440e1SXiao Guangrong uint32_t len; 42235c5a52dSPaolo Bonzini uint8_t data[4092]; 42318c440e1SXiao Guangrong } QEMU_PACKED; 42418c440e1SXiao Guangrong typedef struct NvdimmDsmOut NvdimmDsmOut; 42535c5a52dSPaolo Bonzini QEMU_BUILD_BUG_ON(sizeof(NvdimmDsmOut) != 4096); 42618c440e1SXiao Guangrong 427f7df22deSXiao Guangrong struct NvdimmDsmFunc0Out { 428f7df22deSXiao Guangrong /* the size of buffer filled by QEMU. */ 429f7df22deSXiao Guangrong uint32_t len; 430f7df22deSXiao Guangrong uint32_t supported_func; 431f7df22deSXiao Guangrong } QEMU_PACKED; 432f7df22deSXiao Guangrong typedef struct NvdimmDsmFunc0Out NvdimmDsmFunc0Out; 433f7df22deSXiao Guangrong 434f7df22deSXiao Guangrong struct NvdimmDsmFuncNoPayloadOut { 435f7df22deSXiao Guangrong /* the size of buffer filled by QEMU. */ 436f7df22deSXiao Guangrong uint32_t len; 437f7df22deSXiao Guangrong uint32_t func_ret_status; 438f7df22deSXiao Guangrong } QEMU_PACKED; 439f7df22deSXiao Guangrong typedef struct NvdimmDsmFuncNoPayloadOut NvdimmDsmFuncNoPayloadOut; 440f7df22deSXiao Guangrong 4415797dcdcSXiao Guangrong struct NvdimmFuncGetLabelSizeOut { 4425797dcdcSXiao Guangrong /* the size of buffer filled by QEMU. */ 4435797dcdcSXiao Guangrong uint32_t len; 4445797dcdcSXiao Guangrong uint32_t func_ret_status; /* return status code. */ 4455797dcdcSXiao Guangrong uint32_t label_size; /* the size of label data area. */ 4465797dcdcSXiao Guangrong /* 4475797dcdcSXiao Guangrong * Maximum size of the namespace label data length supported by 4485797dcdcSXiao Guangrong * the platform in Get/Set Namespace Label Data functions. 4495797dcdcSXiao Guangrong */ 4505797dcdcSXiao Guangrong uint32_t max_xfer; 4515797dcdcSXiao Guangrong } QEMU_PACKED; 4525797dcdcSXiao Guangrong typedef struct NvdimmFuncGetLabelSizeOut NvdimmFuncGetLabelSizeOut; 4535797dcdcSXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelSizeOut) > 4096); 4545797dcdcSXiao Guangrong 4552b9e57fcSXiao Guangrong struct NvdimmFuncGetLabelDataIn { 4562b9e57fcSXiao Guangrong uint32_t offset; /* the offset in the namespace label data area. */ 4572b9e57fcSXiao Guangrong uint32_t length; /* the size of data is to be read via the function. */ 4582b9e57fcSXiao Guangrong } QEMU_PACKED; 4592b9e57fcSXiao Guangrong typedef struct NvdimmFuncGetLabelDataIn NvdimmFuncGetLabelDataIn; 4602b9e57fcSXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataIn) + 4612b9e57fcSXiao Guangrong offsetof(NvdimmDsmIn, arg3) > 4096); 4622b9e57fcSXiao Guangrong 4635797dcdcSXiao Guangrong struct NvdimmFuncGetLabelDataOut { 4645797dcdcSXiao Guangrong /* the size of buffer filled by QEMU. */ 4655797dcdcSXiao Guangrong uint32_t len; 4665797dcdcSXiao Guangrong uint32_t func_ret_status; /* return status code. */ 4675797dcdcSXiao Guangrong uint8_t out_buf[0]; /* the data got via Get Namesapce Label function. */ 4685797dcdcSXiao Guangrong } QEMU_PACKED; 4695797dcdcSXiao Guangrong typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut; 4702b9e57fcSXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > 4096); 4715797dcdcSXiao Guangrong 4725797dcdcSXiao Guangrong struct NvdimmFuncSetLabelDataIn { 4735797dcdcSXiao Guangrong uint32_t offset; /* the offset in the namespace label data area. */ 4745797dcdcSXiao Guangrong uint32_t length; /* the size of data is to be written via the function. */ 4755797dcdcSXiao Guangrong uint8_t in_buf[0]; /* the data written to label data area. */ 4765797dcdcSXiao Guangrong } QEMU_PACKED; 4775797dcdcSXiao Guangrong typedef struct NvdimmFuncSetLabelDataIn NvdimmFuncSetLabelDataIn; 47814e44198SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncSetLabelDataIn) + 47914e44198SXiao Guangrong offsetof(NvdimmDsmIn, arg3) > 4096); 4805797dcdcSXiao Guangrong 481806864d9SXiao Guangrong struct NvdimmFuncReadFITIn { 482*7adbce63SXiao Guangrong uint32_t offset; /* the offset into FIT buffer. */ 483806864d9SXiao Guangrong } QEMU_PACKED; 484806864d9SXiao Guangrong typedef struct NvdimmFuncReadFITIn NvdimmFuncReadFITIn; 485806864d9SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncReadFITIn) + 486806864d9SXiao Guangrong offsetof(NvdimmDsmIn, arg3) > 4096); 487806864d9SXiao Guangrong 488806864d9SXiao Guangrong struct NvdimmFuncReadFITOut { 489806864d9SXiao Guangrong /* the size of buffer filled by QEMU. */ 490806864d9SXiao Guangrong uint32_t len; 491806864d9SXiao Guangrong uint32_t func_ret_status; /* return status code. */ 492806864d9SXiao Guangrong uint8_t fit[0]; /* the FIT data. */ 493806864d9SXiao Guangrong } QEMU_PACKED; 494806864d9SXiao Guangrong typedef struct NvdimmFuncReadFITOut NvdimmFuncReadFITOut; 495806864d9SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncReadFITOut) > 4096); 496806864d9SXiao Guangrong 497189f4d56SXiao Guangrong static void 498189f4d56SXiao Guangrong nvdimm_dsm_function0(uint32_t supported_func, hwaddr dsm_mem_addr) 499189f4d56SXiao Guangrong { 500189f4d56SXiao Guangrong NvdimmDsmFunc0Out func0 = { 501189f4d56SXiao Guangrong .len = cpu_to_le32(sizeof(func0)), 502189f4d56SXiao Guangrong .supported_func = cpu_to_le32(supported_func), 503189f4d56SXiao Guangrong }; 504189f4d56SXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof(func0)); 505189f4d56SXiao Guangrong } 506189f4d56SXiao Guangrong 507189f4d56SXiao Guangrong static void 508189f4d56SXiao Guangrong nvdimm_dsm_no_payload(uint32_t func_ret_status, hwaddr dsm_mem_addr) 509189f4d56SXiao Guangrong { 510189f4d56SXiao Guangrong NvdimmDsmFuncNoPayloadOut out = { 511189f4d56SXiao Guangrong .len = cpu_to_le32(sizeof(out)), 512189f4d56SXiao Guangrong .func_ret_status = cpu_to_le32(func_ret_status), 513189f4d56SXiao Guangrong }; 514189f4d56SXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out)); 515189f4d56SXiao Guangrong } 516189f4d56SXiao Guangrong 517c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_SUCCESS 0 /* Success */ 518c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_UNSUPPORT 1 /* Not Supported */ 519c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_NOMEMDEV 2 /* Non-Existing Memory Device */ 520c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_INVALID 3 /* Invalid Input Parameters */ 521c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_FIT_CHANGED 0x100 /* FIT Changed */ 522c2fa3075SXiao Guangrong 523806864d9SXiao Guangrong #define NVDIMM_QEMU_RSVD_HANDLE_ROOT 0x10000 524806864d9SXiao Guangrong 525806864d9SXiao Guangrong /* Read FIT data, defined in docs/specs/acpi_nvdimm.txt. */ 526806864d9SXiao Guangrong static void nvdimm_dsm_func_read_fit(AcpiNVDIMMState *state, NvdimmDsmIn *in, 527806864d9SXiao Guangrong hwaddr dsm_mem_addr) 528806864d9SXiao Guangrong { 529806864d9SXiao Guangrong NvdimmFitBuffer *fit_buf = &state->fit_buf; 530806864d9SXiao Guangrong NvdimmFuncReadFITIn *read_fit; 531806864d9SXiao Guangrong NvdimmFuncReadFITOut *read_fit_out; 532806864d9SXiao Guangrong GArray *fit; 533806864d9SXiao Guangrong uint32_t read_len = 0, func_ret_status; 534806864d9SXiao Guangrong int size; 535806864d9SXiao Guangrong 536806864d9SXiao Guangrong read_fit = (NvdimmFuncReadFITIn *)in->arg3; 537806864d9SXiao Guangrong le32_to_cpus(&read_fit->offset); 538806864d9SXiao Guangrong 539806864d9SXiao Guangrong fit = fit_buf->fit; 540806864d9SXiao Guangrong 541806864d9SXiao Guangrong nvdimm_debug("Read FIT: offset %#x FIT size %#x Dirty %s.\n", 542806864d9SXiao Guangrong read_fit->offset, fit->len, fit_buf->dirty ? "Yes" : "No"); 543806864d9SXiao Guangrong 544806864d9SXiao Guangrong if (read_fit->offset > fit->len) { 545c2fa3075SXiao Guangrong func_ret_status = NVDIMM_DSM_RET_STATUS_INVALID; 546806864d9SXiao Guangrong goto exit; 547806864d9SXiao Guangrong } 548806864d9SXiao Guangrong 549806864d9SXiao Guangrong /* It is the first time to read FIT. */ 550806864d9SXiao Guangrong if (!read_fit->offset) { 551806864d9SXiao Guangrong fit_buf->dirty = false; 552806864d9SXiao Guangrong } else if (fit_buf->dirty) { /* FIT has been changed during RFIT. */ 553c2fa3075SXiao Guangrong func_ret_status = NVDIMM_DSM_RET_STATUS_FIT_CHANGED; 554806864d9SXiao Guangrong goto exit; 555806864d9SXiao Guangrong } 556806864d9SXiao Guangrong 557c2fa3075SXiao Guangrong func_ret_status = NVDIMM_DSM_RET_STATUS_SUCCESS; 558806864d9SXiao Guangrong read_len = MIN(fit->len - read_fit->offset, 559806864d9SXiao Guangrong 4096 - sizeof(NvdimmFuncReadFITOut)); 560806864d9SXiao Guangrong 561806864d9SXiao Guangrong exit: 562806864d9SXiao Guangrong size = sizeof(NvdimmFuncReadFITOut) + read_len; 563806864d9SXiao Guangrong read_fit_out = g_malloc(size); 564806864d9SXiao Guangrong 565806864d9SXiao Guangrong read_fit_out->len = cpu_to_le32(size); 566806864d9SXiao Guangrong read_fit_out->func_ret_status = cpu_to_le32(func_ret_status); 567806864d9SXiao Guangrong memcpy(read_fit_out->fit, fit->data + read_fit->offset, read_len); 568806864d9SXiao Guangrong 569806864d9SXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, read_fit_out, size); 570806864d9SXiao Guangrong 571806864d9SXiao Guangrong g_free(read_fit_out); 572806864d9SXiao Guangrong } 573806864d9SXiao Guangrong 574806864d9SXiao Guangrong static void nvdimm_dsm_reserved_root(AcpiNVDIMMState *state, NvdimmDsmIn *in, 575806864d9SXiao Guangrong hwaddr dsm_mem_addr) 576806864d9SXiao Guangrong { 577806864d9SXiao Guangrong switch (in->function) { 578806864d9SXiao Guangrong case 0x0: 579806864d9SXiao Guangrong nvdimm_dsm_function0(0x1 | 1 << 1 /* Read FIT */, dsm_mem_addr); 580806864d9SXiao Guangrong return; 581806864d9SXiao Guangrong case 0x1 /* Read FIT */: 582806864d9SXiao Guangrong nvdimm_dsm_func_read_fit(state, in, dsm_mem_addr); 583806864d9SXiao Guangrong return; 584806864d9SXiao Guangrong } 585806864d9SXiao Guangrong 586c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); 587806864d9SXiao Guangrong } 588806864d9SXiao Guangrong 589189f4d56SXiao Guangrong static void nvdimm_dsm_root(NvdimmDsmIn *in, hwaddr dsm_mem_addr) 590189f4d56SXiao Guangrong { 591189f4d56SXiao Guangrong /* 592189f4d56SXiao Guangrong * function 0 is called to inquire which functions are supported by 593189f4d56SXiao Guangrong * OSPM 594189f4d56SXiao Guangrong */ 595189f4d56SXiao Guangrong if (!in->function) { 596189f4d56SXiao Guangrong nvdimm_dsm_function0(0 /* No function supported other than 597189f4d56SXiao Guangrong function 0 */, dsm_mem_addr); 598189f4d56SXiao Guangrong return; 599189f4d56SXiao Guangrong } 600189f4d56SXiao Guangrong 601189f4d56SXiao Guangrong /* No function except function 0 is supported yet. */ 602c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); 603189f4d56SXiao Guangrong } 604189f4d56SXiao Guangrong 6055797dcdcSXiao Guangrong /* 6065797dcdcSXiao Guangrong * the max transfer size is the max size transferred by both a 6075797dcdcSXiao Guangrong * 'Get Namespace Label Data' function and a 'Set Namespace Label Data' 6085797dcdcSXiao Guangrong * function. 6095797dcdcSXiao Guangrong */ 6105797dcdcSXiao Guangrong static uint32_t nvdimm_get_max_xfer_label_size(void) 6115797dcdcSXiao Guangrong { 6125797dcdcSXiao Guangrong uint32_t max_get_size, max_set_size, dsm_memory_size = 4096; 6135797dcdcSXiao Guangrong 6145797dcdcSXiao Guangrong /* 6155797dcdcSXiao Guangrong * the max data ACPI can read one time which is transferred by 6165797dcdcSXiao Guangrong * the response of 'Get Namespace Label Data' function. 6175797dcdcSXiao Guangrong */ 6185797dcdcSXiao Guangrong max_get_size = dsm_memory_size - sizeof(NvdimmFuncGetLabelDataOut); 6195797dcdcSXiao Guangrong 6205797dcdcSXiao Guangrong /* 6215797dcdcSXiao Guangrong * the max data ACPI can write one time which is transferred by 6225797dcdcSXiao Guangrong * 'Set Namespace Label Data' function. 6235797dcdcSXiao Guangrong */ 6245797dcdcSXiao Guangrong max_set_size = dsm_memory_size - offsetof(NvdimmDsmIn, arg3) - 6255797dcdcSXiao Guangrong sizeof(NvdimmFuncSetLabelDataIn); 6265797dcdcSXiao Guangrong 6275797dcdcSXiao Guangrong return MIN(max_get_size, max_set_size); 6285797dcdcSXiao Guangrong } 6295797dcdcSXiao Guangrong 6305797dcdcSXiao Guangrong /* 6315797dcdcSXiao Guangrong * DSM Spec Rev1 4.4 Get Namespace Label Size (Function Index 4). 6325797dcdcSXiao Guangrong * 6335797dcdcSXiao Guangrong * It gets the size of Namespace Label data area and the max data size 6345797dcdcSXiao Guangrong * that Get/Set Namespace Label Data functions can transfer. 6355797dcdcSXiao Guangrong */ 6365797dcdcSXiao Guangrong static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr) 6375797dcdcSXiao Guangrong { 6385797dcdcSXiao Guangrong NvdimmFuncGetLabelSizeOut label_size_out = { 6395797dcdcSXiao Guangrong .len = cpu_to_le32(sizeof(label_size_out)), 6405797dcdcSXiao Guangrong }; 6415797dcdcSXiao Guangrong uint32_t label_size, mxfer; 6425797dcdcSXiao Guangrong 6435797dcdcSXiao Guangrong label_size = nvdimm->label_size; 6445797dcdcSXiao Guangrong mxfer = nvdimm_get_max_xfer_label_size(); 6455797dcdcSXiao Guangrong 6465797dcdcSXiao Guangrong nvdimm_debug("label_size %#x, max_xfer %#x.\n", label_size, mxfer); 6475797dcdcSXiao Guangrong 648c2fa3075SXiao Guangrong label_size_out.func_ret_status = cpu_to_le32(NVDIMM_DSM_RET_STATUS_SUCCESS); 6495797dcdcSXiao Guangrong label_size_out.label_size = cpu_to_le32(label_size); 6505797dcdcSXiao Guangrong label_size_out.max_xfer = cpu_to_le32(mxfer); 6515797dcdcSXiao Guangrong 6525797dcdcSXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &label_size_out, 6535797dcdcSXiao Guangrong sizeof(label_size_out)); 6545797dcdcSXiao Guangrong } 6555797dcdcSXiao Guangrong 6562b9e57fcSXiao Guangrong static uint32_t nvdimm_rw_label_data_check(NVDIMMDevice *nvdimm, 6572b9e57fcSXiao Guangrong uint32_t offset, uint32_t length) 6582b9e57fcSXiao Guangrong { 659c2fa3075SXiao Guangrong uint32_t ret = NVDIMM_DSM_RET_STATUS_INVALID; 6602b9e57fcSXiao Guangrong 6612b9e57fcSXiao Guangrong if (offset + length < offset) { 6622b9e57fcSXiao Guangrong nvdimm_debug("offset %#x + length %#x is overflow.\n", offset, 6632b9e57fcSXiao Guangrong length); 6642b9e57fcSXiao Guangrong return ret; 6652b9e57fcSXiao Guangrong } 6662b9e57fcSXiao Guangrong 6672b9e57fcSXiao Guangrong if (nvdimm->label_size < offset + length) { 6682b9e57fcSXiao Guangrong nvdimm_debug("position %#x is beyond label data (len = %" PRIx64 ").\n", 6692b9e57fcSXiao Guangrong offset + length, nvdimm->label_size); 6702b9e57fcSXiao Guangrong return ret; 6712b9e57fcSXiao Guangrong } 6722b9e57fcSXiao Guangrong 6732b9e57fcSXiao Guangrong if (length > nvdimm_get_max_xfer_label_size()) { 6742b9e57fcSXiao Guangrong nvdimm_debug("length (%#x) is larger than max_xfer (%#x).\n", 6752b9e57fcSXiao Guangrong length, nvdimm_get_max_xfer_label_size()); 6762b9e57fcSXiao Guangrong return ret; 6772b9e57fcSXiao Guangrong } 6782b9e57fcSXiao Guangrong 679c2fa3075SXiao Guangrong return NVDIMM_DSM_RET_STATUS_SUCCESS; 6802b9e57fcSXiao Guangrong } 6812b9e57fcSXiao Guangrong 6822b9e57fcSXiao Guangrong /* 6832b9e57fcSXiao Guangrong * DSM Spec Rev1 4.5 Get Namespace Label Data (Function Index 5). 6842b9e57fcSXiao Guangrong */ 6852b9e57fcSXiao Guangrong static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, 6862b9e57fcSXiao Guangrong hwaddr dsm_mem_addr) 6872b9e57fcSXiao Guangrong { 6882b9e57fcSXiao Guangrong NVDIMMClass *nvc = NVDIMM_GET_CLASS(nvdimm); 6892b9e57fcSXiao Guangrong NvdimmFuncGetLabelDataIn *get_label_data; 6902b9e57fcSXiao Guangrong NvdimmFuncGetLabelDataOut *get_label_data_out; 6912b9e57fcSXiao Guangrong uint32_t status; 6922b9e57fcSXiao Guangrong int size; 6932b9e57fcSXiao Guangrong 6942b9e57fcSXiao Guangrong get_label_data = (NvdimmFuncGetLabelDataIn *)in->arg3; 6952b9e57fcSXiao Guangrong le32_to_cpus(&get_label_data->offset); 6962b9e57fcSXiao Guangrong le32_to_cpus(&get_label_data->length); 6972b9e57fcSXiao Guangrong 6982b9e57fcSXiao Guangrong nvdimm_debug("Read Label Data: offset %#x length %#x.\n", 6992b9e57fcSXiao Guangrong get_label_data->offset, get_label_data->length); 7002b9e57fcSXiao Guangrong 7012b9e57fcSXiao Guangrong status = nvdimm_rw_label_data_check(nvdimm, get_label_data->offset, 7022b9e57fcSXiao Guangrong get_label_data->length); 703c2fa3075SXiao Guangrong if (status != NVDIMM_DSM_RET_STATUS_SUCCESS) { 7042b9e57fcSXiao Guangrong nvdimm_dsm_no_payload(status, dsm_mem_addr); 7052b9e57fcSXiao Guangrong return; 7062b9e57fcSXiao Guangrong } 7072b9e57fcSXiao Guangrong 7082b9e57fcSXiao Guangrong size = sizeof(*get_label_data_out) + get_label_data->length; 7092b9e57fcSXiao Guangrong assert(size <= 4096); 7102b9e57fcSXiao Guangrong get_label_data_out = g_malloc(size); 7112b9e57fcSXiao Guangrong 7122b9e57fcSXiao Guangrong get_label_data_out->len = cpu_to_le32(size); 713c2fa3075SXiao Guangrong get_label_data_out->func_ret_status = 714c2fa3075SXiao Guangrong cpu_to_le32(NVDIMM_DSM_RET_STATUS_SUCCESS); 7152b9e57fcSXiao Guangrong nvc->read_label_data(nvdimm, get_label_data_out->out_buf, 7162b9e57fcSXiao Guangrong get_label_data->length, get_label_data->offset); 7172b9e57fcSXiao Guangrong 7182b9e57fcSXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, get_label_data_out, size); 7192b9e57fcSXiao Guangrong g_free(get_label_data_out); 7202b9e57fcSXiao Guangrong } 7212b9e57fcSXiao Guangrong 72214e44198SXiao Guangrong /* 72314e44198SXiao Guangrong * DSM Spec Rev1 4.6 Set Namespace Label Data (Function Index 6). 72414e44198SXiao Guangrong */ 72514e44198SXiao Guangrong static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, 72614e44198SXiao Guangrong hwaddr dsm_mem_addr) 72714e44198SXiao Guangrong { 72814e44198SXiao Guangrong NVDIMMClass *nvc = NVDIMM_GET_CLASS(nvdimm); 72914e44198SXiao Guangrong NvdimmFuncSetLabelDataIn *set_label_data; 73014e44198SXiao Guangrong uint32_t status; 73114e44198SXiao Guangrong 73214e44198SXiao Guangrong set_label_data = (NvdimmFuncSetLabelDataIn *)in->arg3; 73314e44198SXiao Guangrong 73414e44198SXiao Guangrong le32_to_cpus(&set_label_data->offset); 73514e44198SXiao Guangrong le32_to_cpus(&set_label_data->length); 73614e44198SXiao Guangrong 73714e44198SXiao Guangrong nvdimm_debug("Write Label Data: offset %#x length %#x.\n", 73814e44198SXiao Guangrong set_label_data->offset, set_label_data->length); 73914e44198SXiao Guangrong 74014e44198SXiao Guangrong status = nvdimm_rw_label_data_check(nvdimm, set_label_data->offset, 74114e44198SXiao Guangrong set_label_data->length); 742c2fa3075SXiao Guangrong if (status != NVDIMM_DSM_RET_STATUS_SUCCESS) { 74314e44198SXiao Guangrong nvdimm_dsm_no_payload(status, dsm_mem_addr); 74414e44198SXiao Guangrong return; 74514e44198SXiao Guangrong } 74614e44198SXiao Guangrong 74753000638SHaozhong Zhang assert(offsetof(NvdimmDsmIn, arg3) + 74853000638SHaozhong Zhang sizeof(*set_label_data) + set_label_data->length <= 4096); 74914e44198SXiao Guangrong 75014e44198SXiao Guangrong nvc->write_label_data(nvdimm, set_label_data->in_buf, 75114e44198SXiao Guangrong set_label_data->length, set_label_data->offset); 752c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_SUCCESS, dsm_mem_addr); 75314e44198SXiao Guangrong } 75414e44198SXiao Guangrong 755189f4d56SXiao Guangrong static void nvdimm_dsm_device(NvdimmDsmIn *in, hwaddr dsm_mem_addr) 756189f4d56SXiao Guangrong { 7575797dcdcSXiao Guangrong NVDIMMDevice *nvdimm = nvdimm_get_device_by_handle(in->handle); 7585797dcdcSXiao Guangrong 759189f4d56SXiao Guangrong /* See the comments in nvdimm_dsm_root(). */ 760189f4d56SXiao Guangrong if (!in->function) { 7615797dcdcSXiao Guangrong uint32_t supported_func = 0; 7625797dcdcSXiao Guangrong 7635797dcdcSXiao Guangrong if (nvdimm && nvdimm->label_size) { 7645797dcdcSXiao Guangrong supported_func |= 0x1 /* Bit 0 indicates whether there is 7655797dcdcSXiao Guangrong support for any functions other 7665797dcdcSXiao Guangrong than function 0. */ | 7672b9e57fcSXiao Guangrong 1 << 4 /* Get Namespace Label Size */ | 76814e44198SXiao Guangrong 1 << 5 /* Get Namespace Label Data */ | 76914e44198SXiao Guangrong 1 << 6 /* Set Namespace Label Data */; 7705797dcdcSXiao Guangrong } 7715797dcdcSXiao Guangrong nvdimm_dsm_function0(supported_func, dsm_mem_addr); 772189f4d56SXiao Guangrong return; 773189f4d56SXiao Guangrong } 774189f4d56SXiao Guangrong 7755797dcdcSXiao Guangrong if (!nvdimm) { 776c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_NOMEMDEV, 7775797dcdcSXiao Guangrong dsm_mem_addr); 7785797dcdcSXiao Guangrong return; 7795797dcdcSXiao Guangrong } 7805797dcdcSXiao Guangrong 7815797dcdcSXiao Guangrong /* Encode DSM function according to DSM Spec Rev1. */ 7825797dcdcSXiao Guangrong switch (in->function) { 7835797dcdcSXiao Guangrong case 4 /* Get Namespace Label Size */: 7845797dcdcSXiao Guangrong if (nvdimm->label_size) { 7855797dcdcSXiao Guangrong nvdimm_dsm_label_size(nvdimm, dsm_mem_addr); 7865797dcdcSXiao Guangrong return; 7875797dcdcSXiao Guangrong } 7885797dcdcSXiao Guangrong break; 7892b9e57fcSXiao Guangrong case 5 /* Get Namespace Label Data */: 7902b9e57fcSXiao Guangrong if (nvdimm->label_size) { 7912b9e57fcSXiao Guangrong nvdimm_dsm_get_label_data(nvdimm, in, dsm_mem_addr); 7922b9e57fcSXiao Guangrong return; 7932b9e57fcSXiao Guangrong } 7942b9e57fcSXiao Guangrong break; 79514e44198SXiao Guangrong case 0x6 /* Set Namespace Label Data */: 79614e44198SXiao Guangrong if (nvdimm->label_size) { 79714e44198SXiao Guangrong nvdimm_dsm_set_label_data(nvdimm, in, dsm_mem_addr); 79814e44198SXiao Guangrong return; 79914e44198SXiao Guangrong } 80014e44198SXiao Guangrong break; 8015797dcdcSXiao Guangrong } 8025797dcdcSXiao Guangrong 803c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); 804189f4d56SXiao Guangrong } 805189f4d56SXiao Guangrong 8065fe79386SXiao Guangrong static uint64_t 8075fe79386SXiao Guangrong nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size) 8085fe79386SXiao Guangrong { 809f7df22deSXiao Guangrong nvdimm_debug("BUG: we never read _DSM IO Port.\n"); 8105fe79386SXiao Guangrong return 0; 8115fe79386SXiao Guangrong } 8125fe79386SXiao Guangrong 8135fe79386SXiao Guangrong static void 8145fe79386SXiao Guangrong nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) 8155fe79386SXiao Guangrong { 816806864d9SXiao Guangrong AcpiNVDIMMState *state = opaque; 817f7df22deSXiao Guangrong NvdimmDsmIn *in; 818f7df22deSXiao Guangrong hwaddr dsm_mem_addr = val; 819f7df22deSXiao Guangrong 820f7df22deSXiao Guangrong nvdimm_debug("dsm memory address %#" HWADDR_PRIx ".\n", dsm_mem_addr); 821f7df22deSXiao Guangrong 822f7df22deSXiao Guangrong /* 823f7df22deSXiao Guangrong * The DSM memory is mapped to guest address space so an evil guest 824f7df22deSXiao Guangrong * can change its content while we are doing DSM emulation. Avoid 825f7df22deSXiao Guangrong * this by copying DSM memory to QEMU local memory. 826f7df22deSXiao Guangrong */ 82735c5a52dSPaolo Bonzini in = g_new(NvdimmDsmIn, 1); 82835c5a52dSPaolo Bonzini cpu_physical_memory_read(dsm_mem_addr, in, sizeof(*in)); 829f7df22deSXiao Guangrong 830f7df22deSXiao Guangrong le32_to_cpus(&in->revision); 831f7df22deSXiao Guangrong le32_to_cpus(&in->function); 832f7df22deSXiao Guangrong le32_to_cpus(&in->handle); 833f7df22deSXiao Guangrong 834f7df22deSXiao Guangrong nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision, 835f7df22deSXiao Guangrong in->handle, in->function); 836f7df22deSXiao Guangrong 837d15fc53fSXiao Guangrong if (in->revision != 0x1 /* Currently we only support DSM Spec Rev1. */) { 838d15fc53fSXiao Guangrong nvdimm_debug("Revision %#x is not supported, expect %#x.\n", 839d15fc53fSXiao Guangrong in->revision, 0x1); 840c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); 841d15fc53fSXiao Guangrong goto exit; 842d15fc53fSXiao Guangrong } 843d15fc53fSXiao Guangrong 844806864d9SXiao Guangrong if (in->handle == NVDIMM_QEMU_RSVD_HANDLE_ROOT) { 845806864d9SXiao Guangrong nvdimm_dsm_reserved_root(state, in, dsm_mem_addr); 846806864d9SXiao Guangrong goto exit; 847806864d9SXiao Guangrong } 848806864d9SXiao Guangrong 849189f4d56SXiao Guangrong /* Handle 0 is reserved for NVDIMM Root Device. */ 850189f4d56SXiao Guangrong if (!in->handle) { 851189f4d56SXiao Guangrong nvdimm_dsm_root(in, dsm_mem_addr); 852189f4d56SXiao Guangrong goto exit; 853f7df22deSXiao Guangrong } 854f7df22deSXiao Guangrong 855189f4d56SXiao Guangrong nvdimm_dsm_device(in, dsm_mem_addr); 856189f4d56SXiao Guangrong 857189f4d56SXiao Guangrong exit: 858f7df22deSXiao Guangrong g_free(in); 8595fe79386SXiao Guangrong } 8605fe79386SXiao Guangrong 8615fe79386SXiao Guangrong static const MemoryRegionOps nvdimm_dsm_ops = { 8625fe79386SXiao Guangrong .read = nvdimm_dsm_read, 8635fe79386SXiao Guangrong .write = nvdimm_dsm_write, 8645fe79386SXiao Guangrong .endianness = DEVICE_LITTLE_ENDIAN, 8655fe79386SXiao Guangrong .valid = { 8665fe79386SXiao Guangrong .min_access_size = 4, 8675fe79386SXiao Guangrong .max_access_size = 4, 8685fe79386SXiao Guangrong }, 8695fe79386SXiao Guangrong }; 8705fe79386SXiao Guangrong 87175f27498SXiao Guangrong void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev) 87275f27498SXiao Guangrong { 87375f27498SXiao Guangrong if (dev->hotplugged) { 87475f27498SXiao Guangrong acpi_send_event(DEVICE(hotplug_dev), ACPI_NVDIMM_HOTPLUG_STATUS); 87575f27498SXiao Guangrong } 87675f27498SXiao Guangrong } 87775f27498SXiao Guangrong 8785fe79386SXiao Guangrong void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, 8795fe79386SXiao Guangrong FWCfgState *fw_cfg, Object *owner) 8805fe79386SXiao Guangrong { 8815fe79386SXiao Guangrong memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state, 8825fe79386SXiao Guangrong "nvdimm-acpi-io", NVDIMM_ACPI_IO_LEN); 8835fe79386SXiao Guangrong memory_region_add_subregion(io, NVDIMM_ACPI_IO_BASE, &state->io_mr); 8845fe79386SXiao Guangrong 8855fe79386SXiao Guangrong state->dsm_mem = g_array_new(false, true /* clear */, 1); 88635c5a52dSPaolo Bonzini acpi_data_push(state->dsm_mem, sizeof(NvdimmDsmIn)); 8875fe79386SXiao Guangrong fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data, 8885fe79386SXiao Guangrong state->dsm_mem->len); 88975b0713eSXiao Guangrong 89075b0713eSXiao Guangrong nvdimm_init_fit_buffer(&state->fit_buf); 8915fe79386SXiao Guangrong } 8925fe79386SXiao Guangrong 89377286395SXiao Guangrong #define NVDIMM_COMMON_DSM "NCAL" 894b9951413SXiao Guangrong #define NVDIMM_ACPI_MEM_ADDR "MEMA" 89577286395SXiao Guangrong 8963ae66c45SXiao Guangrong #define NVDIMM_DSM_MEMORY "NRAM" 8973ae66c45SXiao Guangrong #define NVDIMM_DSM_IOPORT "NPIO" 8983ae66c45SXiao Guangrong 8993ae66c45SXiao Guangrong #define NVDIMM_DSM_NOTIFY "NTFI" 9003ae66c45SXiao Guangrong #define NVDIMM_DSM_HANDLE "HDLE" 9013ae66c45SXiao Guangrong #define NVDIMM_DSM_REVISION "REVS" 9023ae66c45SXiao Guangrong #define NVDIMM_DSM_FUNCTION "FUNC" 9033ae66c45SXiao Guangrong #define NVDIMM_DSM_ARG3 "FARG" 9043ae66c45SXiao Guangrong 9053ae66c45SXiao Guangrong #define NVDIMM_DSM_OUT_BUF_SIZE "RLEN" 9063ae66c45SXiao Guangrong #define NVDIMM_DSM_OUT_BUF "ODAT" 9073ae66c45SXiao Guangrong 908806864d9SXiao Guangrong #define NVDIMM_DSM_RFIT_STATUS "RSTA" 909806864d9SXiao Guangrong 910806864d9SXiao Guangrong #define NVDIMM_QEMU_RSVD_UUID "648B9CF2-CDA1-4312-8AD9-49C4AF32BD62" 911806864d9SXiao Guangrong 91277286395SXiao Guangrong static void nvdimm_build_common_dsm(Aml *dev) 91377286395SXiao Guangrong { 914806864d9SXiao Guangrong Aml *method, *ifctx, *function, *handle, *uuid, *dsm_mem, *elsectx2; 91590623ebfSXiao Guangrong Aml *elsectx, *unsupport, *unpatched, *expected_uuid, *uuid_invalid; 916fa1a448dSXiao Guangrong Aml *pckg, *pckg_index, *pckg_buf, *field, *dsm_out_buf, *dsm_out_buf_size; 91777286395SXiao Guangrong uint8_t byte_list[1]; 91877286395SXiao Guangrong 919732b530cSXiao Guangrong method = aml_method(NVDIMM_COMMON_DSM, 5, AML_SERIALIZED); 92090623ebfSXiao Guangrong uuid = aml_arg(0); 92177286395SXiao Guangrong function = aml_arg(2); 92290623ebfSXiao Guangrong handle = aml_arg(4); 923c0b3b863SXiao Guangrong dsm_mem = aml_local(6); 92448bee476SXiao Guangrong dsm_out_buf = aml_local(7); 925c0b3b863SXiao Guangrong 926c0b3b863SXiao Guangrong aml_append(method, aml_store(aml_name(NVDIMM_ACPI_MEM_ADDR), dsm_mem)); 927c0b3b863SXiao Guangrong 928c0b3b863SXiao Guangrong /* map DSM memory and IO into ACPI namespace. */ 9293ae66c45SXiao Guangrong aml_append(method, aml_operation_region(NVDIMM_DSM_IOPORT, AML_SYSTEM_IO, 930c0b3b863SXiao Guangrong aml_int(NVDIMM_ACPI_IO_BASE), NVDIMM_ACPI_IO_LEN)); 9313ae66c45SXiao Guangrong aml_append(method, aml_operation_region(NVDIMM_DSM_MEMORY, 9323ae66c45SXiao Guangrong AML_SYSTEM_MEMORY, dsm_mem, sizeof(NvdimmDsmIn))); 933c0b3b863SXiao Guangrong 934c0b3b863SXiao Guangrong /* 935c0b3b863SXiao Guangrong * DSM notifier: 9363ae66c45SXiao Guangrong * NVDIMM_DSM_NOTIFY: write the address of DSM memory and notify QEMU to 9373ae66c45SXiao Guangrong * emulate the access. 938c0b3b863SXiao Guangrong * 939c0b3b863SXiao Guangrong * It is the IO port so that accessing them will cause VM-exit, the 940c0b3b863SXiao Guangrong * control will be transferred to QEMU. 941c0b3b863SXiao Guangrong */ 9423ae66c45SXiao Guangrong field = aml_field(NVDIMM_DSM_IOPORT, AML_DWORD_ACC, AML_NOLOCK, 9433ae66c45SXiao Guangrong AML_PRESERVE); 9443ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_NOTIFY, 945c0b3b863SXiao Guangrong sizeof(uint32_t) * BITS_PER_BYTE)); 946c0b3b863SXiao Guangrong aml_append(method, field); 947c0b3b863SXiao Guangrong 948c0b3b863SXiao Guangrong /* 949c0b3b863SXiao Guangrong * DSM input: 9503ae66c45SXiao Guangrong * NVDIMM_DSM_HANDLE: store device's handle, it's zero if the _DSM call 9513ae66c45SXiao Guangrong * happens on NVDIMM Root Device. 9523ae66c45SXiao Guangrong * NVDIMM_DSM_REVISION: store the Arg1 of _DSM call. 9533ae66c45SXiao Guangrong * NVDIMM_DSM_FUNCTION: store the Arg2 of _DSM call. 9543ae66c45SXiao Guangrong * NVDIMM_DSM_ARG3: store the Arg3 of _DSM call which is a Package 9553ae66c45SXiao Guangrong * containing function-specific arguments. 956c0b3b863SXiao Guangrong * 957c0b3b863SXiao Guangrong * They are RAM mapping on host so that these accesses never cause 958c0b3b863SXiao Guangrong * VM-EXIT. 959c0b3b863SXiao Guangrong */ 9603ae66c45SXiao Guangrong field = aml_field(NVDIMM_DSM_MEMORY, AML_DWORD_ACC, AML_NOLOCK, 9613ae66c45SXiao Guangrong AML_PRESERVE); 9623ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_HANDLE, 963c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, handle)) * BITS_PER_BYTE)); 9643ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_REVISION, 965c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, revision)) * BITS_PER_BYTE)); 9663ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_FUNCTION, 967c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, function)) * BITS_PER_BYTE)); 9683ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_ARG3, 969c0b3b863SXiao Guangrong (sizeof(NvdimmDsmIn) - offsetof(NvdimmDsmIn, arg3)) * BITS_PER_BYTE)); 970c0b3b863SXiao Guangrong aml_append(method, field); 971c0b3b863SXiao Guangrong 972c0b3b863SXiao Guangrong /* 973c0b3b863SXiao Guangrong * DSM output: 9743ae66c45SXiao Guangrong * NVDIMM_DSM_OUT_BUF_SIZE: the size of the buffer filled by QEMU. 9753ae66c45SXiao Guangrong * NVDIMM_DSM_OUT_BUF: the buffer QEMU uses to store the result. 976c0b3b863SXiao Guangrong * 977c0b3b863SXiao Guangrong * Since the page is reused by both input and out, the input data 978c0b3b863SXiao Guangrong * will be lost after storing new result into ODAT so we should fetch 979c0b3b863SXiao Guangrong * all the input data before writing the result. 980c0b3b863SXiao Guangrong */ 9813ae66c45SXiao Guangrong field = aml_field(NVDIMM_DSM_MEMORY, AML_DWORD_ACC, AML_NOLOCK, 9823ae66c45SXiao Guangrong AML_PRESERVE); 9833ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_OUT_BUF_SIZE, 984c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmOut, len)) * BITS_PER_BYTE)); 9853ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_OUT_BUF, 986c0b3b863SXiao Guangrong (sizeof(NvdimmDsmOut) - offsetof(NvdimmDsmOut, data)) * BITS_PER_BYTE)); 987c0b3b863SXiao Guangrong aml_append(method, field); 98818c440e1SXiao Guangrong 98918c440e1SXiao Guangrong /* 99018c440e1SXiao Guangrong * do not support any method if DSM memory address has not been 99118c440e1SXiao Guangrong * patched. 99218c440e1SXiao Guangrong */ 99390623ebfSXiao Guangrong unpatched = aml_equal(dsm_mem, aml_int(0x0)); 99490623ebfSXiao Guangrong 99590623ebfSXiao Guangrong expected_uuid = aml_local(0); 99690623ebfSXiao Guangrong 99790623ebfSXiao Guangrong ifctx = aml_if(aml_equal(handle, aml_int(0x0))); 99890623ebfSXiao Guangrong aml_append(ifctx, aml_store( 99990623ebfSXiao Guangrong aml_touuid("2F10E7A4-9E91-11E4-89D3-123B93F75CBA") 100090623ebfSXiao Guangrong /* UUID for NVDIMM Root Device */, expected_uuid)); 100190623ebfSXiao Guangrong aml_append(method, ifctx); 100290623ebfSXiao Guangrong elsectx = aml_else(); 1003806864d9SXiao Guangrong ifctx = aml_if(aml_equal(handle, aml_int(NVDIMM_QEMU_RSVD_HANDLE_ROOT))); 1004806864d9SXiao Guangrong aml_append(ifctx, aml_store(aml_touuid(NVDIMM_QEMU_RSVD_UUID 1005806864d9SXiao Guangrong /* UUID for QEMU internal use */), expected_uuid)); 1006806864d9SXiao Guangrong aml_append(elsectx, ifctx); 1007806864d9SXiao Guangrong elsectx2 = aml_else(); 1008806864d9SXiao Guangrong aml_append(elsectx2, aml_store( 100990623ebfSXiao Guangrong aml_touuid("4309AC30-0D11-11E4-9191-0800200C9A66") 101090623ebfSXiao Guangrong /* UUID for NVDIMM Devices */, expected_uuid)); 1011806864d9SXiao Guangrong aml_append(elsectx, elsectx2); 101290623ebfSXiao Guangrong aml_append(method, elsectx); 101390623ebfSXiao Guangrong 101490623ebfSXiao Guangrong uuid_invalid = aml_lnot(aml_equal(uuid, expected_uuid)); 101590623ebfSXiao Guangrong 101690623ebfSXiao Guangrong unsupport = aml_if(aml_or(unpatched, uuid_invalid, NULL)); 101777286395SXiao Guangrong 101877286395SXiao Guangrong /* 101977286395SXiao Guangrong * function 0 is called to inquire what functions are supported by 102077286395SXiao Guangrong * OSPM 102177286395SXiao Guangrong */ 102277286395SXiao Guangrong ifctx = aml_if(aml_equal(function, aml_int(0))); 102377286395SXiao Guangrong byte_list[0] = 0 /* No function Supported */; 102477286395SXiao Guangrong aml_append(ifctx, aml_return(aml_buffer(1, byte_list))); 102590623ebfSXiao Guangrong aml_append(unsupport, ifctx); 102677286395SXiao Guangrong 102777286395SXiao Guangrong /* No function is supported yet. */ 1028c2fa3075SXiao Guangrong byte_list[0] = NVDIMM_DSM_RET_STATUS_UNSUPPORT; 102990623ebfSXiao Guangrong aml_append(unsupport, aml_return(aml_buffer(1, byte_list))); 103090623ebfSXiao Guangrong aml_append(method, unsupport); 103177286395SXiao Guangrong 103218c440e1SXiao Guangrong /* 103318c440e1SXiao Guangrong * The HDLE indicates the DSM function is issued from which device, 1034732b530cSXiao Guangrong * it reserves 0 for root device and is the handle for NVDIMM devices. 1035732b530cSXiao Guangrong * See the comments in nvdimm_slot_to_handle(). 103618c440e1SXiao Guangrong */ 10373ae66c45SXiao Guangrong aml_append(method, aml_store(handle, aml_name(NVDIMM_DSM_HANDLE))); 10383ae66c45SXiao Guangrong aml_append(method, aml_store(aml_arg(1), aml_name(NVDIMM_DSM_REVISION))); 10393ae66c45SXiao Guangrong aml_append(method, aml_store(aml_arg(2), aml_name(NVDIMM_DSM_FUNCTION))); 104018c440e1SXiao Guangrong 104118c440e1SXiao Guangrong /* 10424568c948SXiao Guangrong * The fourth parameter (Arg3) of _DSM is a package which contains 10434568c948SXiao Guangrong * a buffer, the layout of the buffer is specified by UUID (Arg0), 10444568c948SXiao Guangrong * Revision ID (Arg1) and Function Index (Arg2) which are documented 10454568c948SXiao Guangrong * in the DSM Spec. 10464568c948SXiao Guangrong */ 10474568c948SXiao Guangrong pckg = aml_arg(3); 10484568c948SXiao Guangrong ifctx = aml_if(aml_and(aml_equal(aml_object_type(pckg), 10494568c948SXiao Guangrong aml_int(4 /* Package */)) /* It is a Package? */, 10504568c948SXiao Guangrong aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */, 10514568c948SXiao Guangrong NULL)); 10524568c948SXiao Guangrong 10534568c948SXiao Guangrong pckg_index = aml_local(2); 10544568c948SXiao Guangrong pckg_buf = aml_local(3); 10554568c948SXiao Guangrong aml_append(ifctx, aml_store(aml_index(pckg, aml_int(0)), pckg_index)); 10564568c948SXiao Guangrong aml_append(ifctx, aml_store(aml_derefof(pckg_index), pckg_buf)); 10573ae66c45SXiao Guangrong aml_append(ifctx, aml_store(pckg_buf, aml_name(NVDIMM_DSM_ARG3))); 10584568c948SXiao Guangrong aml_append(method, ifctx); 10594568c948SXiao Guangrong 10604568c948SXiao Guangrong /* 106118c440e1SXiao Guangrong * tell QEMU about the real address of DSM memory, then QEMU 106218c440e1SXiao Guangrong * gets the control and fills the result in DSM memory. 106318c440e1SXiao Guangrong */ 10643ae66c45SXiao Guangrong aml_append(method, aml_store(dsm_mem, aml_name(NVDIMM_DSM_NOTIFY))); 106518c440e1SXiao Guangrong 1066fa1a448dSXiao Guangrong dsm_out_buf_size = aml_local(1); 1067d51d1d7eSXiao Guangrong /* RLEN is not included in the payload returned to guest. */ 10683ae66c45SXiao Guangrong aml_append(method, aml_subtract(aml_name(NVDIMM_DSM_OUT_BUF_SIZE), 10693ae66c45SXiao Guangrong aml_int(4), dsm_out_buf_size)); 1070fa1a448dSXiao Guangrong aml_append(method, aml_store(aml_shiftleft(dsm_out_buf_size, aml_int(3)), 1071fa1a448dSXiao Guangrong dsm_out_buf_size)); 10723ae66c45SXiao Guangrong aml_append(method, aml_create_field(aml_name(NVDIMM_DSM_OUT_BUF), 10733ae66c45SXiao Guangrong aml_int(0), dsm_out_buf_size, "OBUF")); 107418c440e1SXiao Guangrong aml_append(method, aml_concatenate(aml_buffer(0, NULL), aml_name("OBUF"), 107548bee476SXiao Guangrong dsm_out_buf)); 107648bee476SXiao Guangrong aml_append(method, aml_return(dsm_out_buf)); 107777286395SXiao Guangrong aml_append(dev, method); 107877286395SXiao Guangrong } 107977286395SXiao Guangrong 1080732b530cSXiao Guangrong static void nvdimm_build_device_dsm(Aml *dev, uint32_t handle) 108177286395SXiao Guangrong { 108277286395SXiao Guangrong Aml *method; 108377286395SXiao Guangrong 108477286395SXiao Guangrong method = aml_method("_DSM", 4, AML_NOTSERIALIZED); 1085732b530cSXiao Guangrong aml_append(method, aml_return(aml_call5(NVDIMM_COMMON_DSM, aml_arg(0), 1086732b530cSXiao Guangrong aml_arg(1), aml_arg(2), aml_arg(3), 1087732b530cSXiao Guangrong aml_int(handle)))); 108877286395SXiao Guangrong aml_append(dev, method); 108977286395SXiao Guangrong } 109077286395SXiao Guangrong 1091806864d9SXiao Guangrong static void nvdimm_build_fit(Aml *dev) 1092806864d9SXiao Guangrong { 1093806864d9SXiao Guangrong Aml *method, *pkg, *buf, *buf_size, *offset, *call_result; 1094806864d9SXiao Guangrong Aml *whilectx, *ifcond, *ifctx, *elsectx, *fit; 1095806864d9SXiao Guangrong 1096806864d9SXiao Guangrong buf = aml_local(0); 1097806864d9SXiao Guangrong buf_size = aml_local(1); 1098806864d9SXiao Guangrong fit = aml_local(2); 1099806864d9SXiao Guangrong 1100806864d9SXiao Guangrong aml_append(dev, aml_create_dword_field(aml_buffer(4, NULL), 1101806864d9SXiao Guangrong aml_int(0), NVDIMM_DSM_RFIT_STATUS)); 1102806864d9SXiao Guangrong 1103806864d9SXiao Guangrong /* build helper function, RFIT. */ 1104806864d9SXiao Guangrong method = aml_method("RFIT", 1, AML_SERIALIZED); 1105806864d9SXiao Guangrong aml_append(method, aml_create_dword_field(aml_buffer(4, NULL), 1106806864d9SXiao Guangrong aml_int(0), "OFST")); 1107806864d9SXiao Guangrong 1108806864d9SXiao Guangrong /* prepare input package. */ 1109806864d9SXiao Guangrong pkg = aml_package(1); 1110806864d9SXiao Guangrong aml_append(method, aml_store(aml_arg(0), aml_name("OFST"))); 1111806864d9SXiao Guangrong aml_append(pkg, aml_name("OFST")); 1112806864d9SXiao Guangrong 1113806864d9SXiao Guangrong /* call Read_FIT function. */ 1114806864d9SXiao Guangrong call_result = aml_call5(NVDIMM_COMMON_DSM, 1115806864d9SXiao Guangrong aml_touuid(NVDIMM_QEMU_RSVD_UUID), 1116806864d9SXiao Guangrong aml_int(1) /* Revision 1 */, 1117806864d9SXiao Guangrong aml_int(0x1) /* Read FIT */, 1118806864d9SXiao Guangrong pkg, aml_int(NVDIMM_QEMU_RSVD_HANDLE_ROOT)); 1119806864d9SXiao Guangrong aml_append(method, aml_store(call_result, buf)); 1120806864d9SXiao Guangrong 1121806864d9SXiao Guangrong /* handle _DSM result. */ 1122806864d9SXiao Guangrong aml_append(method, aml_create_dword_field(buf, 1123806864d9SXiao Guangrong aml_int(0) /* offset at byte 0 */, "STAU")); 1124806864d9SXiao Guangrong 1125806864d9SXiao Guangrong aml_append(method, aml_store(aml_name("STAU"), 1126806864d9SXiao Guangrong aml_name(NVDIMM_DSM_RFIT_STATUS))); 1127806864d9SXiao Guangrong 1128806864d9SXiao Guangrong /* if something is wrong during _DSM. */ 1129c2fa3075SXiao Guangrong ifcond = aml_equal(aml_int(NVDIMM_DSM_RET_STATUS_SUCCESS), 1130c2fa3075SXiao Guangrong aml_name("STAU")); 1131806864d9SXiao Guangrong ifctx = aml_if(aml_lnot(ifcond)); 1132806864d9SXiao Guangrong aml_append(ifctx, aml_return(aml_buffer(0, NULL))); 1133806864d9SXiao Guangrong aml_append(method, ifctx); 1134806864d9SXiao Guangrong 1135806864d9SXiao Guangrong aml_append(method, aml_store(aml_sizeof(buf), buf_size)); 1136806864d9SXiao Guangrong aml_append(method, aml_subtract(buf_size, 1137806864d9SXiao Guangrong aml_int(4) /* the size of "STAU" */, 1138806864d9SXiao Guangrong buf_size)); 1139806864d9SXiao Guangrong 1140806864d9SXiao Guangrong /* if we read the end of fit. */ 1141806864d9SXiao Guangrong ifctx = aml_if(aml_equal(buf_size, aml_int(0))); 1142806864d9SXiao Guangrong aml_append(ifctx, aml_return(aml_buffer(0, NULL))); 1143806864d9SXiao Guangrong aml_append(method, ifctx); 1144806864d9SXiao Guangrong 1145806864d9SXiao Guangrong aml_append(method, aml_create_field(buf, 1146806864d9SXiao Guangrong aml_int(4 * BITS_PER_BYTE), /* offset at byte 4.*/ 1147880f3612SXiao Guangrong aml_shiftleft(buf_size, aml_int(3)), "BUFF")); 1148806864d9SXiao Guangrong aml_append(method, aml_return(aml_name("BUFF"))); 1149806864d9SXiao Guangrong aml_append(dev, method); 1150806864d9SXiao Guangrong 1151806864d9SXiao Guangrong /* build _FIT. */ 1152806864d9SXiao Guangrong method = aml_method("_FIT", 0, AML_SERIALIZED); 1153806864d9SXiao Guangrong offset = aml_local(3); 1154806864d9SXiao Guangrong 1155806864d9SXiao Guangrong aml_append(method, aml_store(aml_buffer(0, NULL), fit)); 1156806864d9SXiao Guangrong aml_append(method, aml_store(aml_int(0), offset)); 1157806864d9SXiao Guangrong 1158806864d9SXiao Guangrong whilectx = aml_while(aml_int(1)); 1159806864d9SXiao Guangrong aml_append(whilectx, aml_store(aml_call1("RFIT", offset), buf)); 1160806864d9SXiao Guangrong aml_append(whilectx, aml_store(aml_sizeof(buf), buf_size)); 1161806864d9SXiao Guangrong 1162806864d9SXiao Guangrong /* 1163806864d9SXiao Guangrong * if fit buffer was changed during RFIT, read from the beginning 1164806864d9SXiao Guangrong * again. 1165806864d9SXiao Guangrong */ 1166806864d9SXiao Guangrong ifctx = aml_if(aml_equal(aml_name(NVDIMM_DSM_RFIT_STATUS), 1167c2fa3075SXiao Guangrong aml_int(NVDIMM_DSM_RET_STATUS_FIT_CHANGED))); 1168806864d9SXiao Guangrong aml_append(ifctx, aml_store(aml_buffer(0, NULL), fit)); 1169806864d9SXiao Guangrong aml_append(ifctx, aml_store(aml_int(0), offset)); 1170806864d9SXiao Guangrong aml_append(whilectx, ifctx); 1171806864d9SXiao Guangrong 1172806864d9SXiao Guangrong elsectx = aml_else(); 1173806864d9SXiao Guangrong 1174806864d9SXiao Guangrong /* finish fit read if no data is read out. */ 1175806864d9SXiao Guangrong ifctx = aml_if(aml_equal(buf_size, aml_int(0))); 1176806864d9SXiao Guangrong aml_append(ifctx, aml_return(fit)); 1177806864d9SXiao Guangrong aml_append(elsectx, ifctx); 1178806864d9SXiao Guangrong 1179806864d9SXiao Guangrong /* update the offset. */ 1180806864d9SXiao Guangrong aml_append(elsectx, aml_add(offset, buf_size, offset)); 1181806864d9SXiao Guangrong /* append the data we read out to the fit buffer. */ 1182806864d9SXiao Guangrong aml_append(elsectx, aml_concatenate(fit, buf, fit)); 1183806864d9SXiao Guangrong aml_append(whilectx, elsectx); 1184806864d9SXiao Guangrong aml_append(method, whilectx); 1185806864d9SXiao Guangrong 1186806864d9SXiao Guangrong aml_append(dev, method); 1187806864d9SXiao Guangrong } 1188806864d9SXiao Guangrong 1189bdfd065bSXiao Guangrong static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots) 119077286395SXiao Guangrong { 1191bdfd065bSXiao Guangrong uint32_t slot; 1192bdfd065bSXiao Guangrong 1193bdfd065bSXiao Guangrong for (slot = 0; slot < ram_slots; slot++) { 119477286395SXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 119577286395SXiao Guangrong Aml *nvdimm_dev; 119677286395SXiao Guangrong 119777286395SXiao Guangrong nvdimm_dev = aml_device("NV%02X", slot); 119877286395SXiao Guangrong 119977286395SXiao Guangrong /* 120077286395SXiao Guangrong * ACPI 6.0: 9.20 NVDIMM Devices: 120177286395SXiao Guangrong * 120277286395SXiao Guangrong * _ADR object that is used to supply OSPM with unique address 120377286395SXiao Guangrong * of the NVDIMM device. This is done by returning the NFIT Device 120477286395SXiao Guangrong * handle that is used to identify the associated entries in ACPI 120577286395SXiao Guangrong * table NFIT or _FIT. 120677286395SXiao Guangrong */ 120777286395SXiao Guangrong aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle))); 120877286395SXiao Guangrong 1209732b530cSXiao Guangrong nvdimm_build_device_dsm(nvdimm_dev, handle); 121077286395SXiao Guangrong aml_append(root_dev, nvdimm_dev); 121177286395SXiao Guangrong } 121277286395SXiao Guangrong } 121377286395SXiao Guangrong 1214bdfd065bSXiao Guangrong static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, 1215bdfd065bSXiao Guangrong BIOSLinker *linker, GArray *dsm_dma_arrea, 1216bdfd065bSXiao Guangrong uint32_t ram_slots) 121777286395SXiao Guangrong { 1218c0b3b863SXiao Guangrong Aml *ssdt, *sb_scope, *dev; 1219b9951413SXiao Guangrong int mem_addr_offset, nvdimm_ssdt; 122077286395SXiao Guangrong 122177286395SXiao Guangrong acpi_add_table(table_offsets, table_data); 122277286395SXiao Guangrong 122377286395SXiao Guangrong ssdt = init_aml_allocator(); 122477286395SXiao Guangrong acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); 122577286395SXiao Guangrong 122677286395SXiao Guangrong sb_scope = aml_scope("\\_SB"); 122777286395SXiao Guangrong 122877286395SXiao Guangrong dev = aml_device("NVDR"); 122977286395SXiao Guangrong 123077286395SXiao Guangrong /* 123177286395SXiao Guangrong * ACPI 6.0: 9.20 NVDIMM Devices: 123277286395SXiao Guangrong * 123377286395SXiao Guangrong * The ACPI Name Space device uses _HID of ACPI0012 to identify the root 123477286395SXiao Guangrong * NVDIMM interface device. Platform firmware is required to contain one 123577286395SXiao Guangrong * such device in _SB scope if NVDIMMs support is exposed by platform to 123677286395SXiao Guangrong * OSPM. 123777286395SXiao Guangrong * For each NVDIMM present or intended to be supported by platform, 123877286395SXiao Guangrong * platform firmware also exposes an ACPI Namespace Device under the 123977286395SXiao Guangrong * root device. 124077286395SXiao Guangrong */ 124177286395SXiao Guangrong aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012"))); 124277286395SXiao Guangrong 124377286395SXiao Guangrong nvdimm_build_common_dsm(dev); 1244732b530cSXiao Guangrong 1245732b530cSXiao Guangrong /* 0 is reserved for root device. */ 1246732b530cSXiao Guangrong nvdimm_build_device_dsm(dev, 0); 1247806864d9SXiao Guangrong nvdimm_build_fit(dev); 124877286395SXiao Guangrong 1249bdfd065bSXiao Guangrong nvdimm_build_nvdimm_devices(dev, ram_slots); 125077286395SXiao Guangrong 125177286395SXiao Guangrong aml_append(sb_scope, dev); 125277286395SXiao Guangrong aml_append(ssdt, sb_scope); 1253b9951413SXiao Guangrong 1254b9951413SXiao Guangrong nvdimm_ssdt = table_data->len; 1255b9951413SXiao Guangrong 125677286395SXiao Guangrong /* copy AML table into ACPI tables blob and patch header there */ 125777286395SXiao Guangrong g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); 1258b9951413SXiao Guangrong mem_addr_offset = build_append_named_dword(table_data, 1259b9951413SXiao Guangrong NVDIMM_ACPI_MEM_ADDR); 1260b9951413SXiao Guangrong 1261ad9671b8SIgor Mammedov bios_linker_loader_alloc(linker, 1262ad9671b8SIgor Mammedov NVDIMM_DSM_MEM_FILE, dsm_dma_arrea, 1263ad9671b8SIgor Mammedov sizeof(NvdimmDsmIn), false /* high memory */); 12644678124bSIgor Mammedov bios_linker_loader_add_pointer(linker, 12654678124bSIgor Mammedov ACPI_BUILD_TABLE_FILE, mem_addr_offset, sizeof(uint32_t), 12664678124bSIgor Mammedov NVDIMM_DSM_MEM_FILE, 0); 126777286395SXiao Guangrong build_header(linker, table_data, 1268b9951413SXiao Guangrong (void *)(table_data->data + nvdimm_ssdt), 1269b9951413SXiao Guangrong "SSDT", table_data->len - nvdimm_ssdt, 1, NULL, "NVDIMM"); 127077286395SXiao Guangrong free_aml_allocator(); 127177286395SXiao Guangrong } 127277286395SXiao Guangrong 127387252e1bSXiao Guangrong void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, 127475b0713eSXiao Guangrong BIOSLinker *linker, AcpiNVDIMMState *state, 1275bdfd065bSXiao Guangrong uint32_t ram_slots) 127687252e1bSXiao Guangrong { 1277264813cbSXiao Guangrong GSList *device_list; 1278bdfd065bSXiao Guangrong 1279264813cbSXiao Guangrong /* no nvdimm device can be plugged. */ 1280264813cbSXiao Guangrong if (!ram_slots) { 1281264813cbSXiao Guangrong return; 1282264813cbSXiao Guangrong } 1283264813cbSXiao Guangrong 128475b0713eSXiao Guangrong nvdimm_build_ssdt(table_offsets, table_data, linker, state->dsm_mem, 1285bdfd065bSXiao Guangrong ram_slots); 1286264813cbSXiao Guangrong 1287cf7c0ff5SXiao Guangrong device_list = nvdimm_get_device_list(); 1288264813cbSXiao Guangrong /* no NVDIMM device is plugged. */ 1289264813cbSXiao Guangrong if (!device_list) { 1290264813cbSXiao Guangrong return; 1291bdfd065bSXiao Guangrong } 1292264813cbSXiao Guangrong 1293264813cbSXiao Guangrong nvdimm_build_nfit(state, table_offsets, table_data, linker); 1294264813cbSXiao Guangrong g_slist_free(device_list); 1295bdfd065bSXiao Guangrong } 1296