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 18f1e5e2eeSChetan Pant * version 2.1 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" 301439f213SDongjiu Geng #include "qemu/uuid.h" 31c3b0cf6eSVishal Verma #include "qapi/error.h" 3287252e1bSXiao Guangrong #include "hw/acpi/acpi.h" 3387252e1bSXiao Guangrong #include "hw/acpi/aml-build.h" 34b9951413SXiao Guangrong #include "hw/acpi/bios-linker-loader.h" 355fe79386SXiao Guangrong #include "hw/nvram/fw_cfg.h" 3687252e1bSXiao Guangrong #include "hw/mem/nvdimm.h" 373f350f6bSShivaprasad G Bhat #include "qemu/nvdimm-utils.h" 38*e4bcec0cSRobert Hoo #include "trace.h" 3987252e1bSXiao Guangrong 4087252e1bSXiao Guangrong /* 4187252e1bSXiao Guangrong * define Byte Addressable Persistent Memory (PM) Region according to 4287252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure. 4387252e1bSXiao Guangrong */ 4487252e1bSXiao Guangrong static const uint8_t nvdimm_nfit_spa_uuid[] = 451439f213SDongjiu Geng UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, 4687252e1bSXiao Guangrong 0x18, 0xb7, 0x8c, 0xdb); 4787252e1bSXiao Guangrong 4887252e1bSXiao Guangrong /* 4987252e1bSXiao Guangrong * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware 5087252e1bSXiao Guangrong * Interface Table (NFIT). 5187252e1bSXiao Guangrong */ 5287252e1bSXiao Guangrong 5387252e1bSXiao Guangrong /* 5487252e1bSXiao Guangrong * System Physical Address Range Structure 5587252e1bSXiao Guangrong * 5687252e1bSXiao Guangrong * It describes the system physical address ranges occupied by NVDIMMs and 5787252e1bSXiao Guangrong * the types of the regions. 5887252e1bSXiao Guangrong */ 5987252e1bSXiao Guangrong struct NvdimmNfitSpa { 6087252e1bSXiao Guangrong uint16_t type; 6187252e1bSXiao Guangrong uint16_t length; 6287252e1bSXiao Guangrong uint16_t spa_index; 6387252e1bSXiao Guangrong uint16_t flags; 6487252e1bSXiao Guangrong uint32_t reserved; 6587252e1bSXiao Guangrong uint32_t proximity_domain; 6687252e1bSXiao Guangrong uint8_t type_guid[16]; 6787252e1bSXiao Guangrong uint64_t spa_base; 6887252e1bSXiao Guangrong uint64_t spa_length; 6987252e1bSXiao Guangrong uint64_t mem_attr; 7087252e1bSXiao Guangrong } QEMU_PACKED; 7187252e1bSXiao Guangrong typedef struct NvdimmNfitSpa NvdimmNfitSpa; 7287252e1bSXiao Guangrong 7387252e1bSXiao Guangrong /* 7487252e1bSXiao Guangrong * Memory Device to System Physical Address Range Mapping Structure 7587252e1bSXiao Guangrong * 7687252e1bSXiao Guangrong * It enables identifying each NVDIMM region and the corresponding SPA 7787252e1bSXiao Guangrong * describing the memory interleave 7887252e1bSXiao Guangrong */ 7987252e1bSXiao Guangrong struct NvdimmNfitMemDev { 8087252e1bSXiao Guangrong uint16_t type; 8187252e1bSXiao Guangrong uint16_t length; 8287252e1bSXiao Guangrong uint32_t nfit_handle; 8387252e1bSXiao Guangrong uint16_t phys_id; 8487252e1bSXiao Guangrong uint16_t region_id; 8587252e1bSXiao Guangrong uint16_t spa_index; 8687252e1bSXiao Guangrong uint16_t dcr_index; 8787252e1bSXiao Guangrong uint64_t region_len; 8887252e1bSXiao Guangrong uint64_t region_offset; 8987252e1bSXiao Guangrong uint64_t region_dpa; 9087252e1bSXiao Guangrong uint16_t interleave_index; 9187252e1bSXiao Guangrong uint16_t interleave_ways; 9287252e1bSXiao Guangrong uint16_t flags; 9387252e1bSXiao Guangrong uint16_t reserved; 9487252e1bSXiao Guangrong } QEMU_PACKED; 9587252e1bSXiao Guangrong typedef struct NvdimmNfitMemDev NvdimmNfitMemDev; 9687252e1bSXiao Guangrong 97cb836434SHaozhong Zhang #define ACPI_NFIT_MEM_NOT_ARMED (1 << 3) 98cb836434SHaozhong Zhang 9987252e1bSXiao Guangrong /* 10087252e1bSXiao Guangrong * NVDIMM Control Region Structure 10187252e1bSXiao Guangrong * 10287252e1bSXiao Guangrong * It describes the NVDIMM and if applicable, Block Control Window. 10387252e1bSXiao Guangrong */ 10487252e1bSXiao Guangrong struct NvdimmNfitControlRegion { 10587252e1bSXiao Guangrong uint16_t type; 10687252e1bSXiao Guangrong uint16_t length; 10787252e1bSXiao Guangrong uint16_t dcr_index; 10887252e1bSXiao Guangrong uint16_t vendor_id; 10987252e1bSXiao Guangrong uint16_t device_id; 11087252e1bSXiao Guangrong uint16_t revision_id; 11187252e1bSXiao Guangrong uint16_t sub_vendor_id; 11287252e1bSXiao Guangrong uint16_t sub_device_id; 11387252e1bSXiao Guangrong uint16_t sub_revision_id; 11487252e1bSXiao Guangrong uint8_t reserved[6]; 11587252e1bSXiao Guangrong uint32_t serial_number; 11687252e1bSXiao Guangrong uint16_t fic; 11787252e1bSXiao Guangrong uint16_t num_bcw; 11887252e1bSXiao Guangrong uint64_t bcw_size; 11987252e1bSXiao Guangrong uint64_t cmd_offset; 12087252e1bSXiao Guangrong uint64_t cmd_size; 12187252e1bSXiao Guangrong uint64_t status_offset; 12287252e1bSXiao Guangrong uint64_t status_size; 12387252e1bSXiao Guangrong uint16_t flags; 12487252e1bSXiao Guangrong uint8_t reserved2[6]; 12587252e1bSXiao Guangrong } QEMU_PACKED; 12687252e1bSXiao Guangrong typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; 12787252e1bSXiao Guangrong 12887252e1bSXiao Guangrong /* 1299ab3aad2SRoss Zwisler * NVDIMM Platform Capabilities Structure 1309ab3aad2SRoss Zwisler * 1319ab3aad2SRoss Zwisler * Defined in section 5.2.25.9 of ACPI 6.2 Errata A, September 2017 1329ab3aad2SRoss Zwisler */ 1339ab3aad2SRoss Zwisler struct NvdimmNfitPlatformCaps { 1349ab3aad2SRoss Zwisler uint16_t type; 1359ab3aad2SRoss Zwisler uint16_t length; 1369ab3aad2SRoss Zwisler uint8_t highest_cap; 1379ab3aad2SRoss Zwisler uint8_t reserved[3]; 1389ab3aad2SRoss Zwisler uint32_t capabilities; 1399ab3aad2SRoss Zwisler uint8_t reserved2[4]; 1409ab3aad2SRoss Zwisler } QEMU_PACKED; 1419ab3aad2SRoss Zwisler typedef struct NvdimmNfitPlatformCaps NvdimmNfitPlatformCaps; 1429ab3aad2SRoss Zwisler 1439ab3aad2SRoss Zwisler /* 14487252e1bSXiao Guangrong * Module serial number is a unique number for each device. We use the 14587252e1bSXiao Guangrong * slot id of NVDIMM device to generate this number so that each device 14687252e1bSXiao Guangrong * associates with a different number. 14787252e1bSXiao Guangrong * 14887252e1bSXiao Guangrong * 0x123456 is a magic number we arbitrarily chose. 14987252e1bSXiao Guangrong */ 15087252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_sn(int slot) 15187252e1bSXiao Guangrong { 15287252e1bSXiao Guangrong return 0x123456 + slot; 15387252e1bSXiao Guangrong } 15487252e1bSXiao Guangrong 15587252e1bSXiao Guangrong /* 15687252e1bSXiao Guangrong * handle is used to uniquely associate nfit_memdev structure with NVDIMM 15787252e1bSXiao Guangrong * ACPI device - nfit_memdev.nfit_handle matches with the value returned 15887252e1bSXiao Guangrong * by ACPI device _ADR method. 15987252e1bSXiao Guangrong * 16087252e1bSXiao Guangrong * We generate the handle with the slot id of NVDIMM device and reserve 16187252e1bSXiao Guangrong * 0 for NVDIMM root device. 16287252e1bSXiao Guangrong */ 16387252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_handle(int slot) 16487252e1bSXiao Guangrong { 16587252e1bSXiao Guangrong return slot + 1; 16687252e1bSXiao Guangrong } 16787252e1bSXiao Guangrong 16887252e1bSXiao Guangrong /* 16987252e1bSXiao Guangrong * index uniquely identifies the structure, 0 is reserved which indicates 17087252e1bSXiao Guangrong * that the structure is not valid or the associated structure is not 17187252e1bSXiao Guangrong * present. 17287252e1bSXiao Guangrong * 17387252e1bSXiao Guangrong * Each NVDIMM device needs two indexes, one for nfit_spa and another for 17487252e1bSXiao Guangrong * nfit_dc which are generated by the slot id of NVDIMM device. 17587252e1bSXiao Guangrong */ 17687252e1bSXiao Guangrong static uint16_t nvdimm_slot_to_spa_index(int slot) 17787252e1bSXiao Guangrong { 17887252e1bSXiao Guangrong return (slot + 1) << 1; 17987252e1bSXiao Guangrong } 18087252e1bSXiao Guangrong 18187252e1bSXiao Guangrong /* See the comments of nvdimm_slot_to_spa_index(). */ 18287252e1bSXiao Guangrong static uint32_t nvdimm_slot_to_dcr_index(int slot) 18387252e1bSXiao Guangrong { 18487252e1bSXiao Guangrong return nvdimm_slot_to_spa_index(slot) + 1; 18587252e1bSXiao Guangrong } 18687252e1bSXiao Guangrong 1875797dcdcSXiao Guangrong static NVDIMMDevice *nvdimm_get_device_by_handle(uint32_t handle) 1885797dcdcSXiao Guangrong { 1895797dcdcSXiao Guangrong NVDIMMDevice *nvdimm = NULL; 190cf7c0ff5SXiao Guangrong GSList *list, *device_list = nvdimm_get_device_list(); 1915797dcdcSXiao Guangrong 1925797dcdcSXiao Guangrong for (list = device_list; list; list = list->next) { 1935797dcdcSXiao Guangrong NVDIMMDevice *nvd = list->data; 1945797dcdcSXiao Guangrong int slot = object_property_get_int(OBJECT(nvd), PC_DIMM_SLOT_PROP, 1955797dcdcSXiao Guangrong NULL); 1965797dcdcSXiao Guangrong 1975797dcdcSXiao Guangrong if (nvdimm_slot_to_handle(slot) == handle) { 1985797dcdcSXiao Guangrong nvdimm = nvd; 1995797dcdcSXiao Guangrong break; 2005797dcdcSXiao Guangrong } 2015797dcdcSXiao Guangrong } 2025797dcdcSXiao Guangrong 2035797dcdcSXiao Guangrong g_slist_free(device_list); 2045797dcdcSXiao Guangrong return nvdimm; 2055797dcdcSXiao Guangrong } 2065797dcdcSXiao Guangrong 20787252e1bSXiao Guangrong /* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */ 20887252e1bSXiao Guangrong static void 20987252e1bSXiao Guangrong nvdimm_build_structure_spa(GArray *structures, DeviceState *dev) 21087252e1bSXiao Guangrong { 21187252e1bSXiao Guangrong NvdimmNfitSpa *nfit_spa; 2129ed442b8SMarc-André Lureau uint64_t addr = object_property_get_uint(OBJECT(dev), PC_DIMM_ADDR_PROP, 21387252e1bSXiao Guangrong NULL); 214b053ef61SMarc-André Lureau uint64_t size = object_property_get_uint(OBJECT(dev), PC_DIMM_SIZE_PROP, 21587252e1bSXiao Guangrong NULL); 2169ed442b8SMarc-André Lureau uint32_t node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, 21787252e1bSXiao Guangrong NULL); 21887252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 21987252e1bSXiao Guangrong NULL); 22087252e1bSXiao Guangrong 22187252e1bSXiao Guangrong nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa)); 22287252e1bSXiao Guangrong 22387252e1bSXiao Guangrong nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range 22487252e1bSXiao Guangrong Structure */); 22587252e1bSXiao Guangrong nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa)); 22687252e1bSXiao Guangrong nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 22787252e1bSXiao Guangrong 22887252e1bSXiao Guangrong /* 22987252e1bSXiao Guangrong * Control region is strict as all the device info, such as SN, index, 23087252e1bSXiao Guangrong * is associated with slot id. 23187252e1bSXiao Guangrong */ 23287252e1bSXiao Guangrong nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for 23387252e1bSXiao Guangrong management during hot add/online 23487252e1bSXiao Guangrong operation */ | 23587252e1bSXiao Guangrong 2 /* Data in Proximity Domain field is 23687252e1bSXiao Guangrong valid*/); 23787252e1bSXiao Guangrong 23887252e1bSXiao Guangrong /* NUMA node. */ 23987252e1bSXiao Guangrong nfit_spa->proximity_domain = cpu_to_le32(node); 24087252e1bSXiao Guangrong /* the region reported as PMEM. */ 24187252e1bSXiao Guangrong memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid, 24287252e1bSXiao Guangrong sizeof(nvdimm_nfit_spa_uuid)); 24387252e1bSXiao Guangrong 24487252e1bSXiao Guangrong nfit_spa->spa_base = cpu_to_le64(addr); 24587252e1bSXiao Guangrong nfit_spa->spa_length = cpu_to_le64(size); 24687252e1bSXiao Guangrong 24787252e1bSXiao Guangrong /* It is the PMEM and can be cached as writeback. */ 24887252e1bSXiao Guangrong nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ | 24987252e1bSXiao Guangrong 0x8000ULL /* EFI_MEMORY_NV */); 25087252e1bSXiao Guangrong } 25187252e1bSXiao Guangrong 25287252e1bSXiao Guangrong /* 25387252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping 25487252e1bSXiao Guangrong * Structure 25587252e1bSXiao Guangrong */ 25687252e1bSXiao Guangrong static void 25787252e1bSXiao Guangrong nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev) 25887252e1bSXiao Guangrong { 25987252e1bSXiao Guangrong NvdimmNfitMemDev *nfit_memdev; 260cb836434SHaozhong Zhang NVDIMMDevice *nvdimm = NVDIMM(OBJECT(dev)); 261b053ef61SMarc-André Lureau uint64_t size = object_property_get_uint(OBJECT(dev), PC_DIMM_SIZE_PROP, 26287252e1bSXiao Guangrong NULL); 26387252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 26487252e1bSXiao Guangrong NULL); 26587252e1bSXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 26687252e1bSXiao Guangrong 26787252e1bSXiao Guangrong nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev)); 26887252e1bSXiao Guangrong 26987252e1bSXiao Guangrong nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address 27087252e1bSXiao Guangrong Range Map Structure*/); 27187252e1bSXiao Guangrong nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev)); 27287252e1bSXiao Guangrong nfit_memdev->nfit_handle = cpu_to_le32(handle); 27387252e1bSXiao Guangrong 27487252e1bSXiao Guangrong /* 27587252e1bSXiao Guangrong * associate memory device with System Physical Address Range 27687252e1bSXiao Guangrong * Structure. 27787252e1bSXiao Guangrong */ 27887252e1bSXiao Guangrong nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); 27987252e1bSXiao Guangrong /* associate memory device with Control Region Structure. */ 28087252e1bSXiao Guangrong nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 28187252e1bSXiao Guangrong 28287252e1bSXiao Guangrong /* The memory region on the device. */ 28387252e1bSXiao Guangrong nfit_memdev->region_len = cpu_to_le64(size); 2846ab0c4bdSXiao Guangrong /* The device address starts from 0. */ 2856ab0c4bdSXiao Guangrong nfit_memdev->region_dpa = cpu_to_le64(0); 28687252e1bSXiao Guangrong 28787252e1bSXiao Guangrong /* Only one interleave for PMEM. */ 28887252e1bSXiao Guangrong nfit_memdev->interleave_ways = cpu_to_le16(1); 289cb836434SHaozhong Zhang 290cb836434SHaozhong Zhang if (nvdimm->unarmed) { 291cb836434SHaozhong Zhang nfit_memdev->flags |= cpu_to_le16(ACPI_NFIT_MEM_NOT_ARMED); 292cb836434SHaozhong Zhang } 29387252e1bSXiao Guangrong } 29487252e1bSXiao Guangrong 29587252e1bSXiao Guangrong /* 29687252e1bSXiao Guangrong * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure. 29787252e1bSXiao Guangrong */ 29887252e1bSXiao Guangrong static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) 29987252e1bSXiao Guangrong { 30087252e1bSXiao Guangrong NvdimmNfitControlRegion *nfit_dcr; 30187252e1bSXiao Guangrong int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, 30287252e1bSXiao Guangrong NULL); 30387252e1bSXiao Guangrong uint32_t sn = nvdimm_slot_to_sn(slot); 30487252e1bSXiao Guangrong 30587252e1bSXiao Guangrong nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr)); 30687252e1bSXiao Guangrong 30787252e1bSXiao Guangrong nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */); 30887252e1bSXiao Guangrong nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr)); 30987252e1bSXiao Guangrong nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); 31087252e1bSXiao Guangrong 31187252e1bSXiao Guangrong /* vendor: Intel. */ 31287252e1bSXiao Guangrong nfit_dcr->vendor_id = cpu_to_le16(0x8086); 31387252e1bSXiao Guangrong nfit_dcr->device_id = cpu_to_le16(1); 31487252e1bSXiao Guangrong 31587252e1bSXiao Guangrong /* The _DSM method is following Intel's DSM specification. */ 31687252e1bSXiao Guangrong nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported 31787252e1bSXiao Guangrong in ACPI 6.0 is 1. */); 31887252e1bSXiao Guangrong nfit_dcr->serial_number = cpu_to_le32(sn); 31920fdef58SHaozhong Zhang nfit_dcr->fic = cpu_to_le16(0x301 /* Format Interface Code: 32020fdef58SHaozhong Zhang Byte addressable, no energy backed. 32120fdef58SHaozhong Zhang See ACPI 6.2, sect 5.2.25.6 and 32220fdef58SHaozhong Zhang JEDEC Annex L Release 3. */); 32387252e1bSXiao Guangrong } 32487252e1bSXiao Guangrong 3259ab3aad2SRoss Zwisler /* 3269ab3aad2SRoss Zwisler * ACPI 6.2 Errata A: 5.2.25.9 NVDIMM Platform Capabilities Structure 3279ab3aad2SRoss Zwisler */ 3289ab3aad2SRoss Zwisler static void 3299ab3aad2SRoss Zwisler nvdimm_build_structure_caps(GArray *structures, uint32_t capabilities) 3309ab3aad2SRoss Zwisler { 3319ab3aad2SRoss Zwisler NvdimmNfitPlatformCaps *nfit_caps; 3329ab3aad2SRoss Zwisler 3339ab3aad2SRoss Zwisler nfit_caps = acpi_data_push(structures, sizeof(*nfit_caps)); 3349ab3aad2SRoss Zwisler 3359ab3aad2SRoss Zwisler nfit_caps->type = cpu_to_le16(7 /* NVDIMM Platform Capabilities */); 3369ab3aad2SRoss Zwisler nfit_caps->length = cpu_to_le16(sizeof(*nfit_caps)); 3379ab3aad2SRoss Zwisler nfit_caps->highest_cap = 31 - clz32(capabilities); 3389ab3aad2SRoss Zwisler nfit_caps->capabilities = cpu_to_le32(capabilities); 3399ab3aad2SRoss Zwisler } 3409ab3aad2SRoss Zwisler 341c1404bdeSEric Auger static GArray *nvdimm_build_device_structure(NVDIMMState *state) 34287252e1bSXiao Guangrong { 3435c243345SLi Zhijian GSList *device_list, *list = nvdimm_get_device_list(); 34487252e1bSXiao Guangrong GArray *structures = g_array_new(false, true /* clear */, 1); 34587252e1bSXiao Guangrong 3465c243345SLi Zhijian for (device_list = list; device_list; device_list = device_list->next) { 34787252e1bSXiao Guangrong DeviceState *dev = device_list->data; 34887252e1bSXiao Guangrong 34987252e1bSXiao Guangrong /* build System Physical Address Range Structure. */ 35087252e1bSXiao Guangrong nvdimm_build_structure_spa(structures, dev); 35187252e1bSXiao Guangrong 35287252e1bSXiao Guangrong /* 35387252e1bSXiao Guangrong * build Memory Device to System Physical Address Range Mapping 35487252e1bSXiao Guangrong * Structure. 35587252e1bSXiao Guangrong */ 35687252e1bSXiao Guangrong nvdimm_build_structure_memdev(structures, dev); 35787252e1bSXiao Guangrong 35887252e1bSXiao Guangrong /* build NVDIMM Control Region Structure. */ 35987252e1bSXiao Guangrong nvdimm_build_structure_dcr(structures, dev); 36087252e1bSXiao Guangrong } 3615c243345SLi Zhijian g_slist_free(list); 36287252e1bSXiao Guangrong 36311c39b5cSRoss Zwisler if (state->persistence) { 36411c39b5cSRoss Zwisler nvdimm_build_structure_caps(structures, state->persistence); 3659ab3aad2SRoss Zwisler } 3669ab3aad2SRoss Zwisler 36787252e1bSXiao Guangrong return structures; 36887252e1bSXiao Guangrong } 36987252e1bSXiao Guangrong 37075b0713eSXiao Guangrong static void nvdimm_init_fit_buffer(NvdimmFitBuffer *fit_buf) 37175b0713eSXiao Guangrong { 37275b0713eSXiao Guangrong fit_buf->fit = g_array_new(false, true /* clear */, 1); 37375b0713eSXiao Guangrong } 37475b0713eSXiao Guangrong 375c1404bdeSEric Auger static void nvdimm_build_fit_buffer(NVDIMMState *state) 37675b0713eSXiao Guangrong { 3779ab3aad2SRoss Zwisler NvdimmFitBuffer *fit_buf = &state->fit_buf; 3789ab3aad2SRoss Zwisler 37975b0713eSXiao Guangrong g_array_free(fit_buf->fit, true); 3809ab3aad2SRoss Zwisler fit_buf->fit = nvdimm_build_device_structure(state); 38175b0713eSXiao Guangrong fit_buf->dirty = true; 38275b0713eSXiao Guangrong } 38375b0713eSXiao Guangrong 384c1404bdeSEric Auger void nvdimm_plug(NVDIMMState *state) 38575b0713eSXiao Guangrong { 3869ab3aad2SRoss Zwisler nvdimm_build_fit_buffer(state); 38775b0713eSXiao Guangrong } 38875b0713eSXiao Guangrong 3897d1823beSIgor Mammedov /* 3907d1823beSIgor Mammedov * NVDIMM Firmware Interface Table 3917d1823beSIgor Mammedov * @signature: "NFIT" 3927d1823beSIgor Mammedov * 3937d1823beSIgor Mammedov * It provides information that allows OSPM to enumerate NVDIMM present in 3947d1823beSIgor Mammedov * the platform and associate system physical address ranges created by the 3957d1823beSIgor Mammedov * NVDIMMs. 3967d1823beSIgor Mammedov * 3977d1823beSIgor Mammedov * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) 3987d1823beSIgor Mammedov */ 3997d1823beSIgor Mammedov 400c1404bdeSEric Auger static void nvdimm_build_nfit(NVDIMMState *state, GArray *table_offsets, 401602b4582SMarian Postevca GArray *table_data, BIOSLinker *linker, 402602b4582SMarian Postevca const char *oem_id, const char *oem_table_id) 40387252e1bSXiao Guangrong { 40475b0713eSXiao Guangrong NvdimmFitBuffer *fit_buf = &state->fit_buf; 4057d1823beSIgor Mammedov AcpiTable table = { .sig = "NFIT", .rev = 1, 4067d1823beSIgor Mammedov .oem_id = oem_id, .oem_table_id = oem_table_id }; 40787252e1bSXiao Guangrong 40887252e1bSXiao Guangrong acpi_add_table(table_offsets, table_data); 40987252e1bSXiao Guangrong 4107d1823beSIgor Mammedov acpi_table_begin(&table, table_data); 4117d1823beSIgor Mammedov /* Reserved */ 4127d1823beSIgor Mammedov build_append_int_noprefix(table_data, 0, 4); 41387252e1bSXiao Guangrong /* NVDIMM device structures. */ 41475b0713eSXiao Guangrong g_array_append_vals(table_data, fit_buf->fit->data, fit_buf->fit->len); 4157d1823beSIgor Mammedov acpi_table_end(linker, &table); 41687252e1bSXiao Guangrong } 41787252e1bSXiao Guangrong 418cb88ebd7SXiao Guangrong #define NVDIMM_DSM_MEMORY_SIZE 4096 419cb88ebd7SXiao Guangrong 42018c440e1SXiao Guangrong struct NvdimmDsmIn { 42118c440e1SXiao Guangrong uint32_t handle; 42218c440e1SXiao Guangrong uint32_t revision; 42318c440e1SXiao Guangrong uint32_t function; 42418c440e1SXiao Guangrong /* the remaining size in the page is used by arg3. */ 42518c440e1SXiao Guangrong union { 42635c5a52dSPaolo Bonzini uint8_t arg3[4084]; 42718c440e1SXiao Guangrong }; 42818c440e1SXiao Guangrong } QEMU_PACKED; 42918c440e1SXiao Guangrong typedef struct NvdimmDsmIn NvdimmDsmIn; 430cb88ebd7SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmDsmIn) != NVDIMM_DSM_MEMORY_SIZE); 43118c440e1SXiao Guangrong 43218c440e1SXiao Guangrong struct NvdimmDsmOut { 43318c440e1SXiao Guangrong /* the size of buffer filled by QEMU. */ 43418c440e1SXiao Guangrong uint32_t len; 43535c5a52dSPaolo Bonzini uint8_t data[4092]; 43618c440e1SXiao Guangrong } QEMU_PACKED; 43718c440e1SXiao Guangrong typedef struct NvdimmDsmOut NvdimmDsmOut; 438cb88ebd7SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmDsmOut) != NVDIMM_DSM_MEMORY_SIZE); 43918c440e1SXiao Guangrong 440f7df22deSXiao Guangrong struct NvdimmDsmFunc0Out { 441f7df22deSXiao Guangrong /* the size of buffer filled by QEMU. */ 442f7df22deSXiao Guangrong uint32_t len; 443f7df22deSXiao Guangrong uint32_t supported_func; 444f7df22deSXiao Guangrong } QEMU_PACKED; 445f7df22deSXiao Guangrong typedef struct NvdimmDsmFunc0Out NvdimmDsmFunc0Out; 446f7df22deSXiao Guangrong 447f7df22deSXiao Guangrong struct NvdimmDsmFuncNoPayloadOut { 448f7df22deSXiao Guangrong /* the size of buffer filled by QEMU. */ 449f7df22deSXiao Guangrong uint32_t len; 450f7df22deSXiao Guangrong uint32_t func_ret_status; 451f7df22deSXiao Guangrong } QEMU_PACKED; 452f7df22deSXiao Guangrong typedef struct NvdimmDsmFuncNoPayloadOut NvdimmDsmFuncNoPayloadOut; 453f7df22deSXiao Guangrong 4545797dcdcSXiao Guangrong struct NvdimmFuncGetLabelSizeOut { 4555797dcdcSXiao Guangrong /* the size of buffer filled by QEMU. */ 4565797dcdcSXiao Guangrong uint32_t len; 4575797dcdcSXiao Guangrong uint32_t func_ret_status; /* return status code. */ 4585797dcdcSXiao Guangrong uint32_t label_size; /* the size of label data area. */ 4595797dcdcSXiao Guangrong /* 4605797dcdcSXiao Guangrong * Maximum size of the namespace label data length supported by 4615797dcdcSXiao Guangrong * the platform in Get/Set Namespace Label Data functions. 4625797dcdcSXiao Guangrong */ 4635797dcdcSXiao Guangrong uint32_t max_xfer; 4645797dcdcSXiao Guangrong } QEMU_PACKED; 4655797dcdcSXiao Guangrong typedef struct NvdimmFuncGetLabelSizeOut NvdimmFuncGetLabelSizeOut; 466cb88ebd7SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelSizeOut) > NVDIMM_DSM_MEMORY_SIZE); 4675797dcdcSXiao Guangrong 4682b9e57fcSXiao Guangrong struct NvdimmFuncGetLabelDataIn { 4692b9e57fcSXiao Guangrong uint32_t offset; /* the offset in the namespace label data area. */ 4702b9e57fcSXiao Guangrong uint32_t length; /* the size of data is to be read via the function. */ 4712b9e57fcSXiao Guangrong } QEMU_PACKED; 4722b9e57fcSXiao Guangrong typedef struct NvdimmFuncGetLabelDataIn NvdimmFuncGetLabelDataIn; 4732b9e57fcSXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataIn) + 474cb88ebd7SXiao Guangrong offsetof(NvdimmDsmIn, arg3) > NVDIMM_DSM_MEMORY_SIZE); 4752b9e57fcSXiao Guangrong 4765797dcdcSXiao Guangrong struct NvdimmFuncGetLabelDataOut { 4775797dcdcSXiao Guangrong /* the size of buffer filled by QEMU. */ 4785797dcdcSXiao Guangrong uint32_t len; 4795797dcdcSXiao Guangrong uint32_t func_ret_status; /* return status code. */ 480a0984714SDr. David Alan Gilbert uint8_t out_buf[]; /* the data got via Get Namespace Label function. */ 4815797dcdcSXiao Guangrong } QEMU_PACKED; 4825797dcdcSXiao Guangrong typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut; 483cb88ebd7SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > NVDIMM_DSM_MEMORY_SIZE); 4845797dcdcSXiao Guangrong 4855797dcdcSXiao Guangrong struct NvdimmFuncSetLabelDataIn { 4865797dcdcSXiao Guangrong uint32_t offset; /* the offset in the namespace label data area. */ 4875797dcdcSXiao Guangrong uint32_t length; /* the size of data is to be written via the function. */ 488f7795e40SPhilippe Mathieu-Daudé uint8_t in_buf[]; /* the data written to label data area. */ 4895797dcdcSXiao Guangrong } QEMU_PACKED; 4905797dcdcSXiao Guangrong typedef struct NvdimmFuncSetLabelDataIn NvdimmFuncSetLabelDataIn; 49114e44198SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncSetLabelDataIn) + 492cb88ebd7SXiao Guangrong offsetof(NvdimmDsmIn, arg3) > NVDIMM_DSM_MEMORY_SIZE); 4935797dcdcSXiao Guangrong 494806864d9SXiao Guangrong struct NvdimmFuncReadFITIn { 4957adbce63SXiao Guangrong uint32_t offset; /* the offset into FIT buffer. */ 496806864d9SXiao Guangrong } QEMU_PACKED; 497806864d9SXiao Guangrong typedef struct NvdimmFuncReadFITIn NvdimmFuncReadFITIn; 498806864d9SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncReadFITIn) + 499cb88ebd7SXiao Guangrong offsetof(NvdimmDsmIn, arg3) > NVDIMM_DSM_MEMORY_SIZE); 500806864d9SXiao Guangrong 501806864d9SXiao Guangrong struct NvdimmFuncReadFITOut { 502806864d9SXiao Guangrong /* the size of buffer filled by QEMU. */ 503806864d9SXiao Guangrong uint32_t len; 504806864d9SXiao Guangrong uint32_t func_ret_status; /* return status code. */ 505f7795e40SPhilippe Mathieu-Daudé uint8_t fit[]; /* the FIT data. */ 506806864d9SXiao Guangrong } QEMU_PACKED; 507806864d9SXiao Guangrong typedef struct NvdimmFuncReadFITOut NvdimmFuncReadFITOut; 508cb88ebd7SXiao Guangrong QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncReadFITOut) > NVDIMM_DSM_MEMORY_SIZE); 509806864d9SXiao Guangrong 510189f4d56SXiao Guangrong static void 511189f4d56SXiao Guangrong nvdimm_dsm_function0(uint32_t supported_func, hwaddr dsm_mem_addr) 512189f4d56SXiao Guangrong { 513189f4d56SXiao Guangrong NvdimmDsmFunc0Out func0 = { 514189f4d56SXiao Guangrong .len = cpu_to_le32(sizeof(func0)), 515189f4d56SXiao Guangrong .supported_func = cpu_to_le32(supported_func), 516189f4d56SXiao Guangrong }; 517189f4d56SXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof(func0)); 518189f4d56SXiao Guangrong } 519189f4d56SXiao Guangrong 520189f4d56SXiao Guangrong static void 521189f4d56SXiao Guangrong nvdimm_dsm_no_payload(uint32_t func_ret_status, hwaddr dsm_mem_addr) 522189f4d56SXiao Guangrong { 523189f4d56SXiao Guangrong NvdimmDsmFuncNoPayloadOut out = { 524189f4d56SXiao Guangrong .len = cpu_to_le32(sizeof(out)), 525189f4d56SXiao Guangrong .func_ret_status = cpu_to_le32(func_ret_status), 526189f4d56SXiao Guangrong }; 527189f4d56SXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out)); 528189f4d56SXiao Guangrong } 529189f4d56SXiao Guangrong 530c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_SUCCESS 0 /* Success */ 531c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_UNSUPPORT 1 /* Not Supported */ 532c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_NOMEMDEV 2 /* Non-Existing Memory Device */ 533c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_INVALID 3 /* Invalid Input Parameters */ 534c2fa3075SXiao Guangrong #define NVDIMM_DSM_RET_STATUS_FIT_CHANGED 0x100 /* FIT Changed */ 535c2fa3075SXiao Guangrong 536806864d9SXiao Guangrong #define NVDIMM_QEMU_RSVD_HANDLE_ROOT 0x10000 537806864d9SXiao Guangrong 538806864d9SXiao Guangrong /* Read FIT data, defined in docs/specs/acpi_nvdimm.txt. */ 539c1404bdeSEric Auger static void nvdimm_dsm_func_read_fit(NVDIMMState *state, NvdimmDsmIn *in, 540806864d9SXiao Guangrong hwaddr dsm_mem_addr) 541806864d9SXiao Guangrong { 542806864d9SXiao Guangrong NvdimmFitBuffer *fit_buf = &state->fit_buf; 543806864d9SXiao Guangrong NvdimmFuncReadFITIn *read_fit; 544806864d9SXiao Guangrong NvdimmFuncReadFITOut *read_fit_out; 545806864d9SXiao Guangrong GArray *fit; 546806864d9SXiao Guangrong uint32_t read_len = 0, func_ret_status; 547806864d9SXiao Guangrong int size; 548806864d9SXiao Guangrong 549806864d9SXiao Guangrong read_fit = (NvdimmFuncReadFITIn *)in->arg3; 550435cc3e4SPeter Maydell read_fit->offset = le32_to_cpu(read_fit->offset); 551806864d9SXiao Guangrong 552806864d9SXiao Guangrong fit = fit_buf->fit; 553806864d9SXiao Guangrong 554*e4bcec0cSRobert Hoo trace_acpi_nvdimm_read_fit(read_fit->offset, fit->len, 555*e4bcec0cSRobert Hoo fit_buf->dirty ? "Yes" : "No"); 556806864d9SXiao Guangrong 557806864d9SXiao Guangrong if (read_fit->offset > fit->len) { 558c2fa3075SXiao Guangrong func_ret_status = NVDIMM_DSM_RET_STATUS_INVALID; 559806864d9SXiao Guangrong goto exit; 560806864d9SXiao Guangrong } 561806864d9SXiao Guangrong 562806864d9SXiao Guangrong /* It is the first time to read FIT. */ 563806864d9SXiao Guangrong if (!read_fit->offset) { 564806864d9SXiao Guangrong fit_buf->dirty = false; 565806864d9SXiao Guangrong } else if (fit_buf->dirty) { /* FIT has been changed during RFIT. */ 566c2fa3075SXiao Guangrong func_ret_status = NVDIMM_DSM_RET_STATUS_FIT_CHANGED; 567806864d9SXiao Guangrong goto exit; 568806864d9SXiao Guangrong } 569806864d9SXiao Guangrong 570c2fa3075SXiao Guangrong func_ret_status = NVDIMM_DSM_RET_STATUS_SUCCESS; 571806864d9SXiao Guangrong read_len = MIN(fit->len - read_fit->offset, 572cb88ebd7SXiao Guangrong NVDIMM_DSM_MEMORY_SIZE - sizeof(NvdimmFuncReadFITOut)); 573806864d9SXiao Guangrong 574806864d9SXiao Guangrong exit: 575806864d9SXiao Guangrong size = sizeof(NvdimmFuncReadFITOut) + read_len; 576806864d9SXiao Guangrong read_fit_out = g_malloc(size); 577806864d9SXiao Guangrong 578806864d9SXiao Guangrong read_fit_out->len = cpu_to_le32(size); 579806864d9SXiao Guangrong read_fit_out->func_ret_status = cpu_to_le32(func_ret_status); 580806864d9SXiao Guangrong memcpy(read_fit_out->fit, fit->data + read_fit->offset, read_len); 581806864d9SXiao Guangrong 582806864d9SXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, read_fit_out, size); 583806864d9SXiao Guangrong 584806864d9SXiao Guangrong g_free(read_fit_out); 585806864d9SXiao Guangrong } 586806864d9SXiao Guangrong 5875a33db78SXiao Guangrong static void 588c1404bdeSEric Auger nvdimm_dsm_handle_reserved_root_method(NVDIMMState *state, 5895a33db78SXiao Guangrong NvdimmDsmIn *in, hwaddr dsm_mem_addr) 590806864d9SXiao Guangrong { 591806864d9SXiao Guangrong switch (in->function) { 592806864d9SXiao Guangrong case 0x0: 593806864d9SXiao Guangrong nvdimm_dsm_function0(0x1 | 1 << 1 /* Read FIT */, dsm_mem_addr); 594806864d9SXiao Guangrong return; 595806864d9SXiao Guangrong case 0x1 /* Read FIT */: 596806864d9SXiao Guangrong nvdimm_dsm_func_read_fit(state, in, dsm_mem_addr); 597806864d9SXiao Guangrong return; 598806864d9SXiao Guangrong } 599806864d9SXiao Guangrong 600c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); 601806864d9SXiao Guangrong } 602806864d9SXiao Guangrong 603189f4d56SXiao Guangrong static void nvdimm_dsm_root(NvdimmDsmIn *in, hwaddr dsm_mem_addr) 604189f4d56SXiao Guangrong { 605189f4d56SXiao Guangrong /* 606189f4d56SXiao Guangrong * function 0 is called to inquire which functions are supported by 607189f4d56SXiao Guangrong * OSPM 608189f4d56SXiao Guangrong */ 609189f4d56SXiao Guangrong if (!in->function) { 610189f4d56SXiao Guangrong nvdimm_dsm_function0(0 /* No function supported other than 611189f4d56SXiao Guangrong function 0 */, dsm_mem_addr); 612189f4d56SXiao Guangrong return; 613189f4d56SXiao Guangrong } 614189f4d56SXiao Guangrong 615189f4d56SXiao Guangrong /* No function except function 0 is supported yet. */ 616c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); 617189f4d56SXiao Guangrong } 618189f4d56SXiao Guangrong 6195797dcdcSXiao Guangrong /* 6205797dcdcSXiao Guangrong * the max transfer size is the max size transferred by both a 6215797dcdcSXiao Guangrong * 'Get Namespace Label Data' function and a 'Set Namespace Label Data' 6225797dcdcSXiao Guangrong * function. 6235797dcdcSXiao Guangrong */ 6245797dcdcSXiao Guangrong static uint32_t nvdimm_get_max_xfer_label_size(void) 6255797dcdcSXiao Guangrong { 626cb88ebd7SXiao Guangrong uint32_t max_get_size, max_set_size, dsm_memory_size; 627cb88ebd7SXiao Guangrong 628cb88ebd7SXiao Guangrong dsm_memory_size = NVDIMM_DSM_MEMORY_SIZE; 6295797dcdcSXiao Guangrong 6305797dcdcSXiao Guangrong /* 6315797dcdcSXiao Guangrong * the max data ACPI can read one time which is transferred by 6325797dcdcSXiao Guangrong * the response of 'Get Namespace Label Data' function. 6335797dcdcSXiao Guangrong */ 6345797dcdcSXiao Guangrong max_get_size = dsm_memory_size - sizeof(NvdimmFuncGetLabelDataOut); 6355797dcdcSXiao Guangrong 6365797dcdcSXiao Guangrong /* 6375797dcdcSXiao Guangrong * the max data ACPI can write one time which is transferred by 6385797dcdcSXiao Guangrong * 'Set Namespace Label Data' function. 6395797dcdcSXiao Guangrong */ 6405797dcdcSXiao Guangrong max_set_size = dsm_memory_size - offsetof(NvdimmDsmIn, arg3) - 6415797dcdcSXiao Guangrong sizeof(NvdimmFuncSetLabelDataIn); 6425797dcdcSXiao Guangrong 6435797dcdcSXiao Guangrong return MIN(max_get_size, max_set_size); 6445797dcdcSXiao Guangrong } 6455797dcdcSXiao Guangrong 6465797dcdcSXiao Guangrong /* 6475797dcdcSXiao Guangrong * DSM Spec Rev1 4.4 Get Namespace Label Size (Function Index 4). 6485797dcdcSXiao Guangrong * 6495797dcdcSXiao Guangrong * It gets the size of Namespace Label data area and the max data size 6505797dcdcSXiao Guangrong * that Get/Set Namespace Label Data functions can transfer. 6515797dcdcSXiao Guangrong */ 6525797dcdcSXiao Guangrong static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr) 6535797dcdcSXiao Guangrong { 6545797dcdcSXiao Guangrong NvdimmFuncGetLabelSizeOut label_size_out = { 6555797dcdcSXiao Guangrong .len = cpu_to_le32(sizeof(label_size_out)), 6565797dcdcSXiao Guangrong }; 6575797dcdcSXiao Guangrong uint32_t label_size, mxfer; 6585797dcdcSXiao Guangrong 6595797dcdcSXiao Guangrong label_size = nvdimm->label_size; 6605797dcdcSXiao Guangrong mxfer = nvdimm_get_max_xfer_label_size(); 6615797dcdcSXiao Guangrong 662*e4bcec0cSRobert Hoo trace_acpi_nvdimm_label_info(label_size, mxfer); 6635797dcdcSXiao Guangrong 664c2fa3075SXiao Guangrong label_size_out.func_ret_status = cpu_to_le32(NVDIMM_DSM_RET_STATUS_SUCCESS); 6655797dcdcSXiao Guangrong label_size_out.label_size = cpu_to_le32(label_size); 6665797dcdcSXiao Guangrong label_size_out.max_xfer = cpu_to_le32(mxfer); 6675797dcdcSXiao Guangrong 6685797dcdcSXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, &label_size_out, 6695797dcdcSXiao Guangrong sizeof(label_size_out)); 6705797dcdcSXiao Guangrong } 6715797dcdcSXiao Guangrong 6722b9e57fcSXiao Guangrong static uint32_t nvdimm_rw_label_data_check(NVDIMMDevice *nvdimm, 6732b9e57fcSXiao Guangrong uint32_t offset, uint32_t length) 6742b9e57fcSXiao Guangrong { 675c2fa3075SXiao Guangrong uint32_t ret = NVDIMM_DSM_RET_STATUS_INVALID; 6762b9e57fcSXiao Guangrong 6772b9e57fcSXiao Guangrong if (offset + length < offset) { 678*e4bcec0cSRobert Hoo trace_acpi_nvdimm_label_overflow(offset, length); 6792b9e57fcSXiao Guangrong return ret; 6802b9e57fcSXiao Guangrong } 6812b9e57fcSXiao Guangrong 6822b9e57fcSXiao Guangrong if (nvdimm->label_size < offset + length) { 683*e4bcec0cSRobert Hoo trace_acpi_nvdimm_label_oversize(offset + length, nvdimm->label_size); 6842b9e57fcSXiao Guangrong return ret; 6852b9e57fcSXiao Guangrong } 6862b9e57fcSXiao Guangrong 6872b9e57fcSXiao Guangrong if (length > nvdimm_get_max_xfer_label_size()) { 688*e4bcec0cSRobert Hoo trace_acpi_nvdimm_label_xfer_exceed(length, 689*e4bcec0cSRobert Hoo nvdimm_get_max_xfer_label_size()); 6902b9e57fcSXiao Guangrong return ret; 6912b9e57fcSXiao Guangrong } 6922b9e57fcSXiao Guangrong 693c2fa3075SXiao Guangrong return NVDIMM_DSM_RET_STATUS_SUCCESS; 6942b9e57fcSXiao Guangrong } 6952b9e57fcSXiao Guangrong 6962b9e57fcSXiao Guangrong /* 6972b9e57fcSXiao Guangrong * DSM Spec Rev1 4.5 Get Namespace Label Data (Function Index 5). 6982b9e57fcSXiao Guangrong */ 6992b9e57fcSXiao Guangrong static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, 7002b9e57fcSXiao Guangrong hwaddr dsm_mem_addr) 7012b9e57fcSXiao Guangrong { 7022b9e57fcSXiao Guangrong NVDIMMClass *nvc = NVDIMM_GET_CLASS(nvdimm); 7032b9e57fcSXiao Guangrong NvdimmFuncGetLabelDataIn *get_label_data; 7042b9e57fcSXiao Guangrong NvdimmFuncGetLabelDataOut *get_label_data_out; 7052b9e57fcSXiao Guangrong uint32_t status; 7062b9e57fcSXiao Guangrong int size; 7072b9e57fcSXiao Guangrong 7082b9e57fcSXiao Guangrong get_label_data = (NvdimmFuncGetLabelDataIn *)in->arg3; 709435cc3e4SPeter Maydell get_label_data->offset = le32_to_cpu(get_label_data->offset); 710435cc3e4SPeter Maydell get_label_data->length = le32_to_cpu(get_label_data->length); 7112b9e57fcSXiao Guangrong 712*e4bcec0cSRobert Hoo trace_acpi_nvdimm_read_label(get_label_data->offset, 713*e4bcec0cSRobert Hoo get_label_data->length); 7142b9e57fcSXiao Guangrong 7152b9e57fcSXiao Guangrong status = nvdimm_rw_label_data_check(nvdimm, get_label_data->offset, 7162b9e57fcSXiao Guangrong get_label_data->length); 717c2fa3075SXiao Guangrong if (status != NVDIMM_DSM_RET_STATUS_SUCCESS) { 7182b9e57fcSXiao Guangrong nvdimm_dsm_no_payload(status, dsm_mem_addr); 7192b9e57fcSXiao Guangrong return; 7202b9e57fcSXiao Guangrong } 7212b9e57fcSXiao Guangrong 7222b9e57fcSXiao Guangrong size = sizeof(*get_label_data_out) + get_label_data->length; 723cb88ebd7SXiao Guangrong assert(size <= NVDIMM_DSM_MEMORY_SIZE); 7242b9e57fcSXiao Guangrong get_label_data_out = g_malloc(size); 7252b9e57fcSXiao Guangrong 7262b9e57fcSXiao Guangrong get_label_data_out->len = cpu_to_le32(size); 727c2fa3075SXiao Guangrong get_label_data_out->func_ret_status = 728c2fa3075SXiao Guangrong cpu_to_le32(NVDIMM_DSM_RET_STATUS_SUCCESS); 7292b9e57fcSXiao Guangrong nvc->read_label_data(nvdimm, get_label_data_out->out_buf, 7302b9e57fcSXiao Guangrong get_label_data->length, get_label_data->offset); 7312b9e57fcSXiao Guangrong 7322b9e57fcSXiao Guangrong cpu_physical_memory_write(dsm_mem_addr, get_label_data_out, size); 7332b9e57fcSXiao Guangrong g_free(get_label_data_out); 7342b9e57fcSXiao Guangrong } 7352b9e57fcSXiao Guangrong 73614e44198SXiao Guangrong /* 73714e44198SXiao Guangrong * DSM Spec Rev1 4.6 Set Namespace Label Data (Function Index 6). 73814e44198SXiao Guangrong */ 73914e44198SXiao Guangrong static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, 74014e44198SXiao Guangrong hwaddr dsm_mem_addr) 74114e44198SXiao Guangrong { 74214e44198SXiao Guangrong NVDIMMClass *nvc = NVDIMM_GET_CLASS(nvdimm); 74314e44198SXiao Guangrong NvdimmFuncSetLabelDataIn *set_label_data; 74414e44198SXiao Guangrong uint32_t status; 74514e44198SXiao Guangrong 74614e44198SXiao Guangrong set_label_data = (NvdimmFuncSetLabelDataIn *)in->arg3; 74714e44198SXiao Guangrong 748435cc3e4SPeter Maydell set_label_data->offset = le32_to_cpu(set_label_data->offset); 749435cc3e4SPeter Maydell set_label_data->length = le32_to_cpu(set_label_data->length); 75014e44198SXiao Guangrong 751*e4bcec0cSRobert Hoo trace_acpi_nvdimm_write_label(set_label_data->offset, 752*e4bcec0cSRobert Hoo set_label_data->length); 75314e44198SXiao Guangrong 75414e44198SXiao Guangrong status = nvdimm_rw_label_data_check(nvdimm, set_label_data->offset, 75514e44198SXiao Guangrong set_label_data->length); 756c2fa3075SXiao Guangrong if (status != NVDIMM_DSM_RET_STATUS_SUCCESS) { 75714e44198SXiao Guangrong nvdimm_dsm_no_payload(status, dsm_mem_addr); 75814e44198SXiao Guangrong return; 75914e44198SXiao Guangrong } 76014e44198SXiao Guangrong 761cb88ebd7SXiao Guangrong assert(offsetof(NvdimmDsmIn, arg3) + sizeof(*set_label_data) + 762cb88ebd7SXiao Guangrong set_label_data->length <= NVDIMM_DSM_MEMORY_SIZE); 76314e44198SXiao Guangrong 76414e44198SXiao Guangrong nvc->write_label_data(nvdimm, set_label_data->in_buf, 76514e44198SXiao Guangrong set_label_data->length, set_label_data->offset); 766c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_SUCCESS, dsm_mem_addr); 76714e44198SXiao Guangrong } 76814e44198SXiao Guangrong 769189f4d56SXiao Guangrong static void nvdimm_dsm_device(NvdimmDsmIn *in, hwaddr dsm_mem_addr) 770189f4d56SXiao Guangrong { 7715797dcdcSXiao Guangrong NVDIMMDevice *nvdimm = nvdimm_get_device_by_handle(in->handle); 7725797dcdcSXiao Guangrong 773189f4d56SXiao Guangrong /* See the comments in nvdimm_dsm_root(). */ 774189f4d56SXiao Guangrong if (!in->function) { 7755797dcdcSXiao Guangrong uint32_t supported_func = 0; 7765797dcdcSXiao Guangrong 7775797dcdcSXiao Guangrong if (nvdimm && nvdimm->label_size) { 7785797dcdcSXiao Guangrong supported_func |= 0x1 /* Bit 0 indicates whether there is 7795797dcdcSXiao Guangrong support for any functions other 7805797dcdcSXiao Guangrong than function 0. */ | 7812b9e57fcSXiao Guangrong 1 << 4 /* Get Namespace Label Size */ | 78214e44198SXiao Guangrong 1 << 5 /* Get Namespace Label Data */ | 78314e44198SXiao Guangrong 1 << 6 /* Set Namespace Label Data */; 7845797dcdcSXiao Guangrong } 7855797dcdcSXiao Guangrong nvdimm_dsm_function0(supported_func, dsm_mem_addr); 786189f4d56SXiao Guangrong return; 787189f4d56SXiao Guangrong } 788189f4d56SXiao Guangrong 7895797dcdcSXiao Guangrong if (!nvdimm) { 790c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_NOMEMDEV, 7915797dcdcSXiao Guangrong dsm_mem_addr); 7925797dcdcSXiao Guangrong return; 7935797dcdcSXiao Guangrong } 7945797dcdcSXiao Guangrong 7955797dcdcSXiao Guangrong /* Encode DSM function according to DSM Spec Rev1. */ 7965797dcdcSXiao Guangrong switch (in->function) { 7975797dcdcSXiao Guangrong case 4 /* Get Namespace Label Size */: 7985797dcdcSXiao Guangrong if (nvdimm->label_size) { 7995797dcdcSXiao Guangrong nvdimm_dsm_label_size(nvdimm, dsm_mem_addr); 8005797dcdcSXiao Guangrong return; 8015797dcdcSXiao Guangrong } 8025797dcdcSXiao Guangrong break; 8032b9e57fcSXiao Guangrong case 5 /* Get Namespace Label Data */: 8042b9e57fcSXiao Guangrong if (nvdimm->label_size) { 8052b9e57fcSXiao Guangrong nvdimm_dsm_get_label_data(nvdimm, in, dsm_mem_addr); 8062b9e57fcSXiao Guangrong return; 8072b9e57fcSXiao Guangrong } 8082b9e57fcSXiao Guangrong break; 80914e44198SXiao Guangrong case 0x6 /* Set Namespace Label Data */: 81014e44198SXiao Guangrong if (nvdimm->label_size) { 81114e44198SXiao Guangrong nvdimm_dsm_set_label_data(nvdimm, in, dsm_mem_addr); 81214e44198SXiao Guangrong return; 81314e44198SXiao Guangrong } 81414e44198SXiao Guangrong break; 8155797dcdcSXiao Guangrong } 8165797dcdcSXiao Guangrong 817c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); 818189f4d56SXiao Guangrong } 819189f4d56SXiao Guangrong 8205fe79386SXiao Guangrong static uint64_t 8215fe79386SXiao Guangrong nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size) 8225fe79386SXiao Guangrong { 823*e4bcec0cSRobert Hoo trace_acpi_nvdimm_read_io_port(); 8245fe79386SXiao Guangrong return 0; 8255fe79386SXiao Guangrong } 8265fe79386SXiao Guangrong 8275fe79386SXiao Guangrong static void 8285fe79386SXiao Guangrong nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) 8295fe79386SXiao Guangrong { 830c1404bdeSEric Auger NVDIMMState *state = opaque; 831f7df22deSXiao Guangrong NvdimmDsmIn *in; 832f7df22deSXiao Guangrong hwaddr dsm_mem_addr = val; 833f7df22deSXiao Guangrong 834*e4bcec0cSRobert Hoo trace_acpi_nvdimm_dsm_mem_addr(dsm_mem_addr); 835f7df22deSXiao Guangrong 836f7df22deSXiao Guangrong /* 837f7df22deSXiao Guangrong * The DSM memory is mapped to guest address space so an evil guest 838f7df22deSXiao Guangrong * can change its content while we are doing DSM emulation. Avoid 839f7df22deSXiao Guangrong * this by copying DSM memory to QEMU local memory. 840f7df22deSXiao Guangrong */ 84135c5a52dSPaolo Bonzini in = g_new(NvdimmDsmIn, 1); 84235c5a52dSPaolo Bonzini cpu_physical_memory_read(dsm_mem_addr, in, sizeof(*in)); 843f7df22deSXiao Guangrong 844435cc3e4SPeter Maydell in->revision = le32_to_cpu(in->revision); 845435cc3e4SPeter Maydell in->function = le32_to_cpu(in->function); 846435cc3e4SPeter Maydell in->handle = le32_to_cpu(in->handle); 847f7df22deSXiao Guangrong 848*e4bcec0cSRobert Hoo trace_acpi_nvdimm_dsm_info(in->revision, in->handle, in->function); 849f7df22deSXiao Guangrong 850d15fc53fSXiao Guangrong if (in->revision != 0x1 /* Currently we only support DSM Spec Rev1. */) { 851*e4bcec0cSRobert Hoo trace_acpi_nvdimm_invalid_revision(in->revision); 852c2fa3075SXiao Guangrong nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); 853d15fc53fSXiao Guangrong goto exit; 854d15fc53fSXiao Guangrong } 855d15fc53fSXiao Guangrong 856806864d9SXiao Guangrong if (in->handle == NVDIMM_QEMU_RSVD_HANDLE_ROOT) { 8575a33db78SXiao Guangrong nvdimm_dsm_handle_reserved_root_method(state, in, dsm_mem_addr); 858806864d9SXiao Guangrong goto exit; 859806864d9SXiao Guangrong } 860806864d9SXiao Guangrong 861189f4d56SXiao Guangrong /* Handle 0 is reserved for NVDIMM Root Device. */ 862189f4d56SXiao Guangrong if (!in->handle) { 863189f4d56SXiao Guangrong nvdimm_dsm_root(in, dsm_mem_addr); 864189f4d56SXiao Guangrong goto exit; 865f7df22deSXiao Guangrong } 866f7df22deSXiao Guangrong 867189f4d56SXiao Guangrong nvdimm_dsm_device(in, dsm_mem_addr); 868189f4d56SXiao Guangrong 869189f4d56SXiao Guangrong exit: 870f7df22deSXiao Guangrong g_free(in); 8715fe79386SXiao Guangrong } 8725fe79386SXiao Guangrong 8735fe79386SXiao Guangrong static const MemoryRegionOps nvdimm_dsm_ops = { 8745fe79386SXiao Guangrong .read = nvdimm_dsm_read, 8755fe79386SXiao Guangrong .write = nvdimm_dsm_write, 8765fe79386SXiao Guangrong .endianness = DEVICE_LITTLE_ENDIAN, 8775fe79386SXiao Guangrong .valid = { 8785fe79386SXiao Guangrong .min_access_size = 4, 8795fe79386SXiao Guangrong .max_access_size = 4, 8805fe79386SXiao Guangrong }, 8815fe79386SXiao Guangrong }; 8825fe79386SXiao Guangrong 88375f27498SXiao Guangrong void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev) 88475f27498SXiao Guangrong { 88575f27498SXiao Guangrong if (dev->hotplugged) { 88675f27498SXiao Guangrong acpi_send_event(DEVICE(hotplug_dev), ACPI_NVDIMM_HOTPLUG_STATUS); 88775f27498SXiao Guangrong } 88875f27498SXiao Guangrong } 88975f27498SXiao Guangrong 890c1404bdeSEric Auger void nvdimm_init_acpi_state(NVDIMMState *state, MemoryRegion *io, 8915c94b826SKwangwoo Lee struct AcpiGenericAddress dsm_io, 8925fe79386SXiao Guangrong FWCfgState *fw_cfg, Object *owner) 8935fe79386SXiao Guangrong { 8945c94b826SKwangwoo Lee state->dsm_io = dsm_io; 8955fe79386SXiao Guangrong memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state, 8965c94b826SKwangwoo Lee "nvdimm-acpi-io", dsm_io.bit_width >> 3); 8975c94b826SKwangwoo Lee memory_region_add_subregion(io, dsm_io.address, &state->io_mr); 8985fe79386SXiao Guangrong 8995fe79386SXiao Guangrong state->dsm_mem = g_array_new(false, true /* clear */, 1); 90035c5a52dSPaolo Bonzini acpi_data_push(state->dsm_mem, sizeof(NvdimmDsmIn)); 9015fe79386SXiao Guangrong fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data, 9025fe79386SXiao Guangrong state->dsm_mem->len); 90375b0713eSXiao Guangrong 90475b0713eSXiao Guangrong nvdimm_init_fit_buffer(&state->fit_buf); 9055fe79386SXiao Guangrong } 9065fe79386SXiao Guangrong 90777286395SXiao Guangrong #define NVDIMM_COMMON_DSM "NCAL" 908b9951413SXiao Guangrong #define NVDIMM_ACPI_MEM_ADDR "MEMA" 90977286395SXiao Guangrong 9103ae66c45SXiao Guangrong #define NVDIMM_DSM_MEMORY "NRAM" 9113ae66c45SXiao Guangrong #define NVDIMM_DSM_IOPORT "NPIO" 9123ae66c45SXiao Guangrong 9133ae66c45SXiao Guangrong #define NVDIMM_DSM_NOTIFY "NTFI" 9143ae66c45SXiao Guangrong #define NVDIMM_DSM_HANDLE "HDLE" 9153ae66c45SXiao Guangrong #define NVDIMM_DSM_REVISION "REVS" 9163ae66c45SXiao Guangrong #define NVDIMM_DSM_FUNCTION "FUNC" 9173ae66c45SXiao Guangrong #define NVDIMM_DSM_ARG3 "FARG" 9183ae66c45SXiao Guangrong 9193ae66c45SXiao Guangrong #define NVDIMM_DSM_OUT_BUF_SIZE "RLEN" 9203ae66c45SXiao Guangrong #define NVDIMM_DSM_OUT_BUF "ODAT" 9213ae66c45SXiao Guangrong 922806864d9SXiao Guangrong #define NVDIMM_DSM_RFIT_STATUS "RSTA" 923806864d9SXiao Guangrong 924806864d9SXiao Guangrong #define NVDIMM_QEMU_RSVD_UUID "648B9CF2-CDA1-4312-8AD9-49C4AF32BD62" 925806864d9SXiao Guangrong 9265c94b826SKwangwoo Lee static void nvdimm_build_common_dsm(Aml *dev, 9275c94b826SKwangwoo Lee NVDIMMState *nvdimm_state) 92877286395SXiao Guangrong { 929806864d9SXiao Guangrong Aml *method, *ifctx, *function, *handle, *uuid, *dsm_mem, *elsectx2; 93090623ebfSXiao Guangrong Aml *elsectx, *unsupport, *unpatched, *expected_uuid, *uuid_invalid; 931fa1a448dSXiao Guangrong Aml *pckg, *pckg_index, *pckg_buf, *field, *dsm_out_buf, *dsm_out_buf_size; 93271b0269aSShameer Kolothum Aml *whilectx, *offset; 93377286395SXiao Guangrong uint8_t byte_list[1]; 9345c94b826SKwangwoo Lee AmlRegionSpace rs; 93577286395SXiao Guangrong 936732b530cSXiao Guangrong method = aml_method(NVDIMM_COMMON_DSM, 5, AML_SERIALIZED); 93790623ebfSXiao Guangrong uuid = aml_arg(0); 93877286395SXiao Guangrong function = aml_arg(2); 93990623ebfSXiao Guangrong handle = aml_arg(4); 940c0b3b863SXiao Guangrong dsm_mem = aml_local(6); 94148bee476SXiao Guangrong dsm_out_buf = aml_local(7); 942c0b3b863SXiao Guangrong 943c0b3b863SXiao Guangrong aml_append(method, aml_store(aml_name(NVDIMM_ACPI_MEM_ADDR), dsm_mem)); 944c0b3b863SXiao Guangrong 9455c94b826SKwangwoo Lee if (nvdimm_state->dsm_io.space_id == AML_AS_SYSTEM_IO) { 9465c94b826SKwangwoo Lee rs = AML_SYSTEM_IO; 9475c94b826SKwangwoo Lee } else { 9485c94b826SKwangwoo Lee rs = AML_SYSTEM_MEMORY; 9495c94b826SKwangwoo Lee } 9505c94b826SKwangwoo Lee 951c0b3b863SXiao Guangrong /* map DSM memory and IO into ACPI namespace. */ 9525c94b826SKwangwoo Lee aml_append(method, aml_operation_region(NVDIMM_DSM_IOPORT, rs, 9535c94b826SKwangwoo Lee aml_int(nvdimm_state->dsm_io.address), 9545c94b826SKwangwoo Lee nvdimm_state->dsm_io.bit_width >> 3)); 9553ae66c45SXiao Guangrong aml_append(method, aml_operation_region(NVDIMM_DSM_MEMORY, 9563ae66c45SXiao Guangrong AML_SYSTEM_MEMORY, dsm_mem, sizeof(NvdimmDsmIn))); 957c0b3b863SXiao Guangrong 958c0b3b863SXiao Guangrong /* 959c0b3b863SXiao Guangrong * DSM notifier: 9603ae66c45SXiao Guangrong * NVDIMM_DSM_NOTIFY: write the address of DSM memory and notify QEMU to 9613ae66c45SXiao Guangrong * emulate the access. 962c0b3b863SXiao Guangrong * 963c0b3b863SXiao Guangrong * It is the IO port so that accessing them will cause VM-exit, the 964c0b3b863SXiao Guangrong * control will be transferred to QEMU. 965c0b3b863SXiao Guangrong */ 9663ae66c45SXiao Guangrong field = aml_field(NVDIMM_DSM_IOPORT, AML_DWORD_ACC, AML_NOLOCK, 9673ae66c45SXiao Guangrong AML_PRESERVE); 9683ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_NOTIFY, 9695c94b826SKwangwoo Lee nvdimm_state->dsm_io.bit_width)); 970c0b3b863SXiao Guangrong aml_append(method, field); 971c0b3b863SXiao Guangrong 972c0b3b863SXiao Guangrong /* 973c0b3b863SXiao Guangrong * DSM input: 9743ae66c45SXiao Guangrong * NVDIMM_DSM_HANDLE: store device's handle, it's zero if the _DSM call 9753ae66c45SXiao Guangrong * happens on NVDIMM Root Device. 9763ae66c45SXiao Guangrong * NVDIMM_DSM_REVISION: store the Arg1 of _DSM call. 9773ae66c45SXiao Guangrong * NVDIMM_DSM_FUNCTION: store the Arg2 of _DSM call. 9783ae66c45SXiao Guangrong * NVDIMM_DSM_ARG3: store the Arg3 of _DSM call which is a Package 9793ae66c45SXiao Guangrong * containing function-specific arguments. 980c0b3b863SXiao Guangrong * 981c0b3b863SXiao Guangrong * They are RAM mapping on host so that these accesses never cause 982c0b3b863SXiao Guangrong * VM-EXIT. 983c0b3b863SXiao Guangrong */ 9843ae66c45SXiao Guangrong field = aml_field(NVDIMM_DSM_MEMORY, AML_DWORD_ACC, AML_NOLOCK, 9853ae66c45SXiao Guangrong AML_PRESERVE); 9863ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_HANDLE, 987c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, handle)) * BITS_PER_BYTE)); 9883ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_REVISION, 989c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, revision)) * BITS_PER_BYTE)); 9903ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_FUNCTION, 991c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmIn, function)) * BITS_PER_BYTE)); 9923ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_ARG3, 993c0b3b863SXiao Guangrong (sizeof(NvdimmDsmIn) - offsetof(NvdimmDsmIn, arg3)) * BITS_PER_BYTE)); 994c0b3b863SXiao Guangrong aml_append(method, field); 995c0b3b863SXiao Guangrong 996c0b3b863SXiao Guangrong /* 997c0b3b863SXiao Guangrong * DSM output: 9983ae66c45SXiao Guangrong * NVDIMM_DSM_OUT_BUF_SIZE: the size of the buffer filled by QEMU. 9993ae66c45SXiao Guangrong * NVDIMM_DSM_OUT_BUF: the buffer QEMU uses to store the result. 1000c0b3b863SXiao Guangrong * 1001c0b3b863SXiao Guangrong * Since the page is reused by both input and out, the input data 1002c0b3b863SXiao Guangrong * will be lost after storing new result into ODAT so we should fetch 1003c0b3b863SXiao Guangrong * all the input data before writing the result. 1004c0b3b863SXiao Guangrong */ 10053ae66c45SXiao Guangrong field = aml_field(NVDIMM_DSM_MEMORY, AML_DWORD_ACC, AML_NOLOCK, 10063ae66c45SXiao Guangrong AML_PRESERVE); 10073ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_OUT_BUF_SIZE, 1008c0b3b863SXiao Guangrong sizeof(typeof_field(NvdimmDsmOut, len)) * BITS_PER_BYTE)); 10093ae66c45SXiao Guangrong aml_append(field, aml_named_field(NVDIMM_DSM_OUT_BUF, 1010c0b3b863SXiao Guangrong (sizeof(NvdimmDsmOut) - offsetof(NvdimmDsmOut, data)) * BITS_PER_BYTE)); 1011c0b3b863SXiao Guangrong aml_append(method, field); 101218c440e1SXiao Guangrong 101318c440e1SXiao Guangrong /* 101418c440e1SXiao Guangrong * do not support any method if DSM memory address has not been 101518c440e1SXiao Guangrong * patched. 101618c440e1SXiao Guangrong */ 101790623ebfSXiao Guangrong unpatched = aml_equal(dsm_mem, aml_int(0x0)); 101890623ebfSXiao Guangrong 101990623ebfSXiao Guangrong expected_uuid = aml_local(0); 102090623ebfSXiao Guangrong 102190623ebfSXiao Guangrong ifctx = aml_if(aml_equal(handle, aml_int(0x0))); 102290623ebfSXiao Guangrong aml_append(ifctx, aml_store( 102390623ebfSXiao Guangrong aml_touuid("2F10E7A4-9E91-11E4-89D3-123B93F75CBA") 102490623ebfSXiao Guangrong /* UUID for NVDIMM Root Device */, expected_uuid)); 102590623ebfSXiao Guangrong aml_append(method, ifctx); 102690623ebfSXiao Guangrong elsectx = aml_else(); 1027806864d9SXiao Guangrong ifctx = aml_if(aml_equal(handle, aml_int(NVDIMM_QEMU_RSVD_HANDLE_ROOT))); 1028806864d9SXiao Guangrong aml_append(ifctx, aml_store(aml_touuid(NVDIMM_QEMU_RSVD_UUID 1029806864d9SXiao Guangrong /* UUID for QEMU internal use */), expected_uuid)); 1030806864d9SXiao Guangrong aml_append(elsectx, ifctx); 1031806864d9SXiao Guangrong elsectx2 = aml_else(); 1032806864d9SXiao Guangrong aml_append(elsectx2, aml_store( 103390623ebfSXiao Guangrong aml_touuid("4309AC30-0D11-11E4-9191-0800200C9A66") 103490623ebfSXiao Guangrong /* UUID for NVDIMM Devices */, expected_uuid)); 1035806864d9SXiao Guangrong aml_append(elsectx, elsectx2); 103690623ebfSXiao Guangrong aml_append(method, elsectx); 103790623ebfSXiao Guangrong 103890623ebfSXiao Guangrong uuid_invalid = aml_lnot(aml_equal(uuid, expected_uuid)); 103990623ebfSXiao Guangrong 104090623ebfSXiao Guangrong unsupport = aml_if(aml_or(unpatched, uuid_invalid, NULL)); 104177286395SXiao Guangrong 104277286395SXiao Guangrong /* 104377286395SXiao Guangrong * function 0 is called to inquire what functions are supported by 104477286395SXiao Guangrong * OSPM 104577286395SXiao Guangrong */ 104677286395SXiao Guangrong ifctx = aml_if(aml_equal(function, aml_int(0))); 104777286395SXiao Guangrong byte_list[0] = 0 /* No function Supported */; 104877286395SXiao Guangrong aml_append(ifctx, aml_return(aml_buffer(1, byte_list))); 104990623ebfSXiao Guangrong aml_append(unsupport, ifctx); 105077286395SXiao Guangrong 105177286395SXiao Guangrong /* No function is supported yet. */ 1052c2fa3075SXiao Guangrong byte_list[0] = NVDIMM_DSM_RET_STATUS_UNSUPPORT; 105390623ebfSXiao Guangrong aml_append(unsupport, aml_return(aml_buffer(1, byte_list))); 105490623ebfSXiao Guangrong aml_append(method, unsupport); 105577286395SXiao Guangrong 105618c440e1SXiao Guangrong /* 105718c440e1SXiao Guangrong * The HDLE indicates the DSM function is issued from which device, 1058732b530cSXiao Guangrong * it reserves 0 for root device and is the handle for NVDIMM devices. 1059732b530cSXiao Guangrong * See the comments in nvdimm_slot_to_handle(). 106018c440e1SXiao Guangrong */ 10613ae66c45SXiao Guangrong aml_append(method, aml_store(handle, aml_name(NVDIMM_DSM_HANDLE))); 10623ae66c45SXiao Guangrong aml_append(method, aml_store(aml_arg(1), aml_name(NVDIMM_DSM_REVISION))); 1063ac265cacSWei Yang aml_append(method, aml_store(function, aml_name(NVDIMM_DSM_FUNCTION))); 106418c440e1SXiao Guangrong 106518c440e1SXiao Guangrong /* 10664568c948SXiao Guangrong * The fourth parameter (Arg3) of _DSM is a package which contains 10674568c948SXiao Guangrong * a buffer, the layout of the buffer is specified by UUID (Arg0), 10684568c948SXiao Guangrong * Revision ID (Arg1) and Function Index (Arg2) which are documented 10694568c948SXiao Guangrong * in the DSM Spec. 10704568c948SXiao Guangrong */ 10714568c948SXiao Guangrong pckg = aml_arg(3); 10724568c948SXiao Guangrong ifctx = aml_if(aml_and(aml_equal(aml_object_type(pckg), 10734568c948SXiao Guangrong aml_int(4 /* Package */)) /* It is a Package? */, 10744568c948SXiao Guangrong aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */, 10754568c948SXiao Guangrong NULL)); 10764568c948SXiao Guangrong 10774568c948SXiao Guangrong pckg_index = aml_local(2); 10784568c948SXiao Guangrong pckg_buf = aml_local(3); 10794568c948SXiao Guangrong aml_append(ifctx, aml_store(aml_index(pckg, aml_int(0)), pckg_index)); 10804568c948SXiao Guangrong aml_append(ifctx, aml_store(aml_derefof(pckg_index), pckg_buf)); 10813ae66c45SXiao Guangrong aml_append(ifctx, aml_store(pckg_buf, aml_name(NVDIMM_DSM_ARG3))); 10824568c948SXiao Guangrong aml_append(method, ifctx); 10834568c948SXiao Guangrong 10844568c948SXiao Guangrong /* 108518c440e1SXiao Guangrong * tell QEMU about the real address of DSM memory, then QEMU 108618c440e1SXiao Guangrong * gets the control and fills the result in DSM memory. 108718c440e1SXiao Guangrong */ 10883ae66c45SXiao Guangrong aml_append(method, aml_store(dsm_mem, aml_name(NVDIMM_DSM_NOTIFY))); 108918c440e1SXiao Guangrong 1090fa1a448dSXiao Guangrong dsm_out_buf_size = aml_local(1); 1091d51d1d7eSXiao Guangrong /* RLEN is not included in the payload returned to guest. */ 10923ae66c45SXiao Guangrong aml_append(method, aml_subtract(aml_name(NVDIMM_DSM_OUT_BUF_SIZE), 10933ae66c45SXiao Guangrong aml_int(4), dsm_out_buf_size)); 109471b0269aSShameer Kolothum 109571b0269aSShameer Kolothum /* 109671b0269aSShameer Kolothum * As per ACPI spec 6.3, Table 19-419 Object Conversion Rules, if 109771b0269aSShameer Kolothum * the Buffer Field <= to the size of an Integer (in bits), it will 109871b0269aSShameer Kolothum * be treated as an integer. Moreover, the integer size depends on 109971b0269aSShameer Kolothum * DSDT tables revision number. If revision number is < 2, integer 110071b0269aSShameer Kolothum * size is 32 bits, otherwise it is 64 bits. 110171b0269aSShameer Kolothum * Because of this CreateField() canot be used if RLEN < Integer Size. 110271b0269aSShameer Kolothum * 110371b0269aSShameer Kolothum * Also please note that APCI ASL operator SizeOf() doesn't support 110471b0269aSShameer Kolothum * Integer and there isn't any other way to figure out the Integer 110571b0269aSShameer Kolothum * size. Hence we assume 8 byte as Integer size and if RLEN < 8 bytes, 110671b0269aSShameer Kolothum * build dsm_out_buf byte by byte. 110771b0269aSShameer Kolothum */ 110871b0269aSShameer Kolothum ifctx = aml_if(aml_lless(dsm_out_buf_size, aml_int(8))); 110971b0269aSShameer Kolothum offset = aml_local(2); 111071b0269aSShameer Kolothum aml_append(ifctx, aml_store(aml_int(0), offset)); 111171b0269aSShameer Kolothum aml_append(ifctx, aml_name_decl("TBUF", aml_buffer(1, NULL))); 111271b0269aSShameer Kolothum aml_append(ifctx, aml_store(aml_buffer(0, NULL), dsm_out_buf)); 111371b0269aSShameer Kolothum 111471b0269aSShameer Kolothum whilectx = aml_while(aml_lless(offset, dsm_out_buf_size)); 111571b0269aSShameer Kolothum /* Copy 1 byte at offset from ODAT to temporary buffer(TBUF). */ 111671b0269aSShameer Kolothum aml_append(whilectx, aml_store(aml_derefof(aml_index( 111771b0269aSShameer Kolothum aml_name(NVDIMM_DSM_OUT_BUF), offset)), 111871b0269aSShameer Kolothum aml_index(aml_name("TBUF"), aml_int(0)))); 111971b0269aSShameer Kolothum aml_append(whilectx, aml_concatenate(dsm_out_buf, aml_name("TBUF"), 112071b0269aSShameer Kolothum dsm_out_buf)); 112171b0269aSShameer Kolothum aml_append(whilectx, aml_increment(offset)); 112271b0269aSShameer Kolothum aml_append(ifctx, whilectx); 112371b0269aSShameer Kolothum 112471b0269aSShameer Kolothum aml_append(ifctx, aml_return(dsm_out_buf)); 112571b0269aSShameer Kolothum aml_append(method, ifctx); 112671b0269aSShameer Kolothum 112771b0269aSShameer Kolothum /* If RLEN >= Integer size, just use CreateField() operator */ 1128fa1a448dSXiao Guangrong aml_append(method, aml_store(aml_shiftleft(dsm_out_buf_size, aml_int(3)), 1129fa1a448dSXiao Guangrong dsm_out_buf_size)); 11303ae66c45SXiao Guangrong aml_append(method, aml_create_field(aml_name(NVDIMM_DSM_OUT_BUF), 11313ae66c45SXiao Guangrong aml_int(0), dsm_out_buf_size, "OBUF")); 113271b0269aSShameer Kolothum aml_append(method, aml_return(aml_name("OBUF"))); 113371b0269aSShameer Kolothum 113477286395SXiao Guangrong aml_append(dev, method); 113577286395SXiao Guangrong } 113677286395SXiao Guangrong 1137732b530cSXiao Guangrong static void nvdimm_build_device_dsm(Aml *dev, uint32_t handle) 113877286395SXiao Guangrong { 113977286395SXiao Guangrong Aml *method; 114077286395SXiao Guangrong 114177286395SXiao Guangrong method = aml_method("_DSM", 4, AML_NOTSERIALIZED); 1142732b530cSXiao Guangrong aml_append(method, aml_return(aml_call5(NVDIMM_COMMON_DSM, aml_arg(0), 1143732b530cSXiao Guangrong aml_arg(1), aml_arg(2), aml_arg(3), 1144732b530cSXiao Guangrong aml_int(handle)))); 114577286395SXiao Guangrong aml_append(dev, method); 114677286395SXiao Guangrong } 114777286395SXiao Guangrong 1148806864d9SXiao Guangrong static void nvdimm_build_fit(Aml *dev) 1149806864d9SXiao Guangrong { 1150806864d9SXiao Guangrong Aml *method, *pkg, *buf, *buf_size, *offset, *call_result; 1151806864d9SXiao Guangrong Aml *whilectx, *ifcond, *ifctx, *elsectx, *fit; 1152806864d9SXiao Guangrong 1153806864d9SXiao Guangrong buf = aml_local(0); 1154806864d9SXiao Guangrong buf_size = aml_local(1); 1155806864d9SXiao Guangrong fit = aml_local(2); 1156806864d9SXiao Guangrong 1157aef056c1SXiao Guangrong aml_append(dev, aml_name_decl(NVDIMM_DSM_RFIT_STATUS, aml_int(0))); 1158806864d9SXiao Guangrong 1159806864d9SXiao Guangrong /* build helper function, RFIT. */ 1160806864d9SXiao Guangrong method = aml_method("RFIT", 1, AML_SERIALIZED); 1161aef056c1SXiao Guangrong aml_append(method, aml_name_decl("OFST", aml_int(0))); 1162806864d9SXiao Guangrong 1163806864d9SXiao Guangrong /* prepare input package. */ 1164806864d9SXiao Guangrong pkg = aml_package(1); 1165806864d9SXiao Guangrong aml_append(method, aml_store(aml_arg(0), aml_name("OFST"))); 1166806864d9SXiao Guangrong aml_append(pkg, aml_name("OFST")); 1167806864d9SXiao Guangrong 1168806864d9SXiao Guangrong /* call Read_FIT function. */ 1169806864d9SXiao Guangrong call_result = aml_call5(NVDIMM_COMMON_DSM, 1170806864d9SXiao Guangrong aml_touuid(NVDIMM_QEMU_RSVD_UUID), 1171806864d9SXiao Guangrong aml_int(1) /* Revision 1 */, 1172806864d9SXiao Guangrong aml_int(0x1) /* Read FIT */, 1173806864d9SXiao Guangrong pkg, aml_int(NVDIMM_QEMU_RSVD_HANDLE_ROOT)); 1174806864d9SXiao Guangrong aml_append(method, aml_store(call_result, buf)); 1175806864d9SXiao Guangrong 1176806864d9SXiao Guangrong /* handle _DSM result. */ 1177806864d9SXiao Guangrong aml_append(method, aml_create_dword_field(buf, 1178806864d9SXiao Guangrong aml_int(0) /* offset at byte 0 */, "STAU")); 1179806864d9SXiao Guangrong 1180806864d9SXiao Guangrong aml_append(method, aml_store(aml_name("STAU"), 1181806864d9SXiao Guangrong aml_name(NVDIMM_DSM_RFIT_STATUS))); 1182806864d9SXiao Guangrong 1183806864d9SXiao Guangrong /* if something is wrong during _DSM. */ 1184c2fa3075SXiao Guangrong ifcond = aml_equal(aml_int(NVDIMM_DSM_RET_STATUS_SUCCESS), 1185c2fa3075SXiao Guangrong aml_name("STAU")); 1186806864d9SXiao Guangrong ifctx = aml_if(aml_lnot(ifcond)); 1187806864d9SXiao Guangrong aml_append(ifctx, aml_return(aml_buffer(0, NULL))); 1188806864d9SXiao Guangrong aml_append(method, ifctx); 1189806864d9SXiao Guangrong 1190806864d9SXiao Guangrong aml_append(method, aml_store(aml_sizeof(buf), buf_size)); 1191806864d9SXiao Guangrong aml_append(method, aml_subtract(buf_size, 1192806864d9SXiao Guangrong aml_int(4) /* the size of "STAU" */, 1193806864d9SXiao Guangrong buf_size)); 1194806864d9SXiao Guangrong 1195806864d9SXiao Guangrong /* if we read the end of fit. */ 1196806864d9SXiao Guangrong ifctx = aml_if(aml_equal(buf_size, aml_int(0))); 1197806864d9SXiao Guangrong aml_append(ifctx, aml_return(aml_buffer(0, NULL))); 1198806864d9SXiao Guangrong aml_append(method, ifctx); 1199806864d9SXiao Guangrong 1200806864d9SXiao Guangrong aml_append(method, aml_create_field(buf, 1201806864d9SXiao Guangrong aml_int(4 * BITS_PER_BYTE), /* offset at byte 4.*/ 1202880f3612SXiao Guangrong aml_shiftleft(buf_size, aml_int(3)), "BUFF")); 1203806864d9SXiao Guangrong aml_append(method, aml_return(aml_name("BUFF"))); 1204806864d9SXiao Guangrong aml_append(dev, method); 1205806864d9SXiao Guangrong 1206806864d9SXiao Guangrong /* build _FIT. */ 1207806864d9SXiao Guangrong method = aml_method("_FIT", 0, AML_SERIALIZED); 1208806864d9SXiao Guangrong offset = aml_local(3); 1209806864d9SXiao Guangrong 1210806864d9SXiao Guangrong aml_append(method, aml_store(aml_buffer(0, NULL), fit)); 1211806864d9SXiao Guangrong aml_append(method, aml_store(aml_int(0), offset)); 1212806864d9SXiao Guangrong 1213806864d9SXiao Guangrong whilectx = aml_while(aml_int(1)); 1214806864d9SXiao Guangrong aml_append(whilectx, aml_store(aml_call1("RFIT", offset), buf)); 1215806864d9SXiao Guangrong aml_append(whilectx, aml_store(aml_sizeof(buf), buf_size)); 1216806864d9SXiao Guangrong 1217806864d9SXiao Guangrong /* 1218806864d9SXiao Guangrong * if fit buffer was changed during RFIT, read from the beginning 1219806864d9SXiao Guangrong * again. 1220806864d9SXiao Guangrong */ 1221806864d9SXiao Guangrong ifctx = aml_if(aml_equal(aml_name(NVDIMM_DSM_RFIT_STATUS), 1222c2fa3075SXiao Guangrong aml_int(NVDIMM_DSM_RET_STATUS_FIT_CHANGED))); 1223806864d9SXiao Guangrong aml_append(ifctx, aml_store(aml_buffer(0, NULL), fit)); 1224806864d9SXiao Guangrong aml_append(ifctx, aml_store(aml_int(0), offset)); 1225806864d9SXiao Guangrong aml_append(whilectx, ifctx); 1226806864d9SXiao Guangrong 1227806864d9SXiao Guangrong elsectx = aml_else(); 1228806864d9SXiao Guangrong 1229806864d9SXiao Guangrong /* finish fit read if no data is read out. */ 1230806864d9SXiao Guangrong ifctx = aml_if(aml_equal(buf_size, aml_int(0))); 1231806864d9SXiao Guangrong aml_append(ifctx, aml_return(fit)); 1232806864d9SXiao Guangrong aml_append(elsectx, ifctx); 1233806864d9SXiao Guangrong 1234806864d9SXiao Guangrong /* update the offset. */ 1235806864d9SXiao Guangrong aml_append(elsectx, aml_add(offset, buf_size, offset)); 1236806864d9SXiao Guangrong /* append the data we read out to the fit buffer. */ 1237806864d9SXiao Guangrong aml_append(elsectx, aml_concatenate(fit, buf, fit)); 1238806864d9SXiao Guangrong aml_append(whilectx, elsectx); 1239806864d9SXiao Guangrong aml_append(method, whilectx); 1240806864d9SXiao Guangrong 1241806864d9SXiao Guangrong aml_append(dev, method); 1242806864d9SXiao Guangrong } 1243806864d9SXiao Guangrong 1244bdfd065bSXiao Guangrong static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots) 124577286395SXiao Guangrong { 1246bdfd065bSXiao Guangrong uint32_t slot; 1247bdfd065bSXiao Guangrong 1248bdfd065bSXiao Guangrong for (slot = 0; slot < ram_slots; slot++) { 124977286395SXiao Guangrong uint32_t handle = nvdimm_slot_to_handle(slot); 125077286395SXiao Guangrong Aml *nvdimm_dev; 125177286395SXiao Guangrong 125277286395SXiao Guangrong nvdimm_dev = aml_device("NV%02X", slot); 125377286395SXiao Guangrong 125477286395SXiao Guangrong /* 125577286395SXiao Guangrong * ACPI 6.0: 9.20 NVDIMM Devices: 125677286395SXiao Guangrong * 125777286395SXiao Guangrong * _ADR object that is used to supply OSPM with unique address 125877286395SXiao Guangrong * of the NVDIMM device. This is done by returning the NFIT Device 125977286395SXiao Guangrong * handle that is used to identify the associated entries in ACPI 126077286395SXiao Guangrong * table NFIT or _FIT. 126177286395SXiao Guangrong */ 126277286395SXiao Guangrong aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle))); 126377286395SXiao Guangrong 1264732b530cSXiao Guangrong nvdimm_build_device_dsm(nvdimm_dev, handle); 126577286395SXiao Guangrong aml_append(root_dev, nvdimm_dev); 126677286395SXiao Guangrong } 126777286395SXiao Guangrong } 126877286395SXiao Guangrong 1269bdfd065bSXiao Guangrong static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, 12705c94b826SKwangwoo Lee BIOSLinker *linker, 12715c94b826SKwangwoo Lee NVDIMMState *nvdimm_state, 1272602b4582SMarian Postevca uint32_t ram_slots, const char *oem_id) 127377286395SXiao Guangrong { 1274de67dd1bSIgor Mammedov int mem_addr_offset; 1275c0b3b863SXiao Guangrong Aml *ssdt, *sb_scope, *dev; 1276de67dd1bSIgor Mammedov AcpiTable table = { .sig = "SSDT", .rev = 1, 1277de67dd1bSIgor Mammedov .oem_id = oem_id, .oem_table_id = "NVDIMM" }; 127877286395SXiao Guangrong 127977286395SXiao Guangrong acpi_add_table(table_offsets, table_data); 128077286395SXiao Guangrong 1281de67dd1bSIgor Mammedov acpi_table_begin(&table, table_data); 128277286395SXiao Guangrong ssdt = init_aml_allocator(); 128377286395SXiao Guangrong sb_scope = aml_scope("\\_SB"); 128477286395SXiao Guangrong 128577286395SXiao Guangrong dev = aml_device("NVDR"); 128677286395SXiao Guangrong 128777286395SXiao Guangrong /* 128877286395SXiao Guangrong * ACPI 6.0: 9.20 NVDIMM Devices: 128977286395SXiao Guangrong * 129077286395SXiao Guangrong * The ACPI Name Space device uses _HID of ACPI0012 to identify the root 129177286395SXiao Guangrong * NVDIMM interface device. Platform firmware is required to contain one 129277286395SXiao Guangrong * such device in _SB scope if NVDIMMs support is exposed by platform to 129377286395SXiao Guangrong * OSPM. 129477286395SXiao Guangrong * For each NVDIMM present or intended to be supported by platform, 129577286395SXiao Guangrong * platform firmware also exposes an ACPI Namespace Device under the 129677286395SXiao Guangrong * root device. 129777286395SXiao Guangrong */ 129877286395SXiao Guangrong aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012"))); 129977286395SXiao Guangrong 13005c94b826SKwangwoo Lee nvdimm_build_common_dsm(dev, nvdimm_state); 1301732b530cSXiao Guangrong 1302732b530cSXiao Guangrong /* 0 is reserved for root device. */ 1303732b530cSXiao Guangrong nvdimm_build_device_dsm(dev, 0); 1304806864d9SXiao Guangrong nvdimm_build_fit(dev); 130577286395SXiao Guangrong 1306bdfd065bSXiao Guangrong nvdimm_build_nvdimm_devices(dev, ram_slots); 130777286395SXiao Guangrong 130877286395SXiao Guangrong aml_append(sb_scope, dev); 130977286395SXiao Guangrong aml_append(ssdt, sb_scope); 1310b9951413SXiao Guangrong 131177286395SXiao Guangrong /* copy AML table into ACPI tables blob and patch header there */ 131277286395SXiao Guangrong g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); 1313b9951413SXiao Guangrong mem_addr_offset = build_append_named_dword(table_data, 1314b9951413SXiao Guangrong NVDIMM_ACPI_MEM_ADDR); 1315b9951413SXiao Guangrong 1316ad9671b8SIgor Mammedov bios_linker_loader_alloc(linker, 13175c94b826SKwangwoo Lee NVDIMM_DSM_MEM_FILE, nvdimm_state->dsm_mem, 1318ad9671b8SIgor Mammedov sizeof(NvdimmDsmIn), false /* high memory */); 13194678124bSIgor Mammedov bios_linker_loader_add_pointer(linker, 13204678124bSIgor Mammedov ACPI_BUILD_TABLE_FILE, mem_addr_offset, sizeof(uint32_t), 13214678124bSIgor Mammedov NVDIMM_DSM_MEM_FILE, 0); 132277286395SXiao Guangrong free_aml_allocator(); 1323de67dd1bSIgor Mammedov /* 1324de67dd1bSIgor Mammedov * must be executed as the last so that pointer patching command above 1325de67dd1bSIgor Mammedov * would be executed by guest before it recalculated checksum which were 1326de67dd1bSIgor Mammedov * scheduled by acpi_table_end() 1327de67dd1bSIgor Mammedov */ 1328de67dd1bSIgor Mammedov acpi_table_end(linker, &table); 132977286395SXiao Guangrong } 133077286395SXiao Guangrong 1331c3b0cf6eSVishal Verma void nvdimm_build_srat(GArray *table_data) 1332c3b0cf6eSVishal Verma { 13335c243345SLi Zhijian GSList *device_list, *list = nvdimm_get_device_list(); 1334c3b0cf6eSVishal Verma 13355c243345SLi Zhijian for (device_list = list; device_list; device_list = device_list->next) { 1336c3b0cf6eSVishal Verma DeviceState *dev = device_list->data; 1337c3b0cf6eSVishal Verma Object *obj = OBJECT(dev); 1338c3b0cf6eSVishal Verma uint64_t addr, size; 1339c3b0cf6eSVishal Verma int node; 1340c3b0cf6eSVishal Verma 1341c3b0cf6eSVishal Verma node = object_property_get_int(obj, PC_DIMM_NODE_PROP, &error_abort); 1342c3b0cf6eSVishal Verma addr = object_property_get_uint(obj, PC_DIMM_ADDR_PROP, &error_abort); 1343c3b0cf6eSVishal Verma size = object_property_get_uint(obj, PC_DIMM_SIZE_PROP, &error_abort); 1344c3b0cf6eSVishal Verma 1345e5b6d55aSIgor Mammedov build_srat_memory(table_data, addr, size, node, 1346c3b0cf6eSVishal Verma MEM_AFFINITY_ENABLED | MEM_AFFINITY_NON_VOLATILE); 1347c3b0cf6eSVishal Verma } 13485c243345SLi Zhijian g_slist_free(list); 1349c3b0cf6eSVishal Verma } 1350c3b0cf6eSVishal Verma 135187252e1bSXiao Guangrong void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, 1352c1404bdeSEric Auger BIOSLinker *linker, NVDIMMState *state, 1353602b4582SMarian Postevca uint32_t ram_slots, const char *oem_id, 1354602b4582SMarian Postevca const char *oem_table_id) 135587252e1bSXiao Guangrong { 1356264813cbSXiao Guangrong GSList *device_list; 1357bdfd065bSXiao Guangrong 1358264813cbSXiao Guangrong /* no nvdimm device can be plugged. */ 1359264813cbSXiao Guangrong if (!ram_slots) { 1360264813cbSXiao Guangrong return; 1361264813cbSXiao Guangrong } 1362264813cbSXiao Guangrong 13635c94b826SKwangwoo Lee nvdimm_build_ssdt(table_offsets, table_data, linker, state, 1364602b4582SMarian Postevca ram_slots, oem_id); 1365264813cbSXiao Guangrong 1366cf7c0ff5SXiao Guangrong device_list = nvdimm_get_device_list(); 1367264813cbSXiao Guangrong /* no NVDIMM device is plugged. */ 1368264813cbSXiao Guangrong if (!device_list) { 1369264813cbSXiao Guangrong return; 1370bdfd065bSXiao Guangrong } 1371264813cbSXiao Guangrong 1372602b4582SMarian Postevca nvdimm_build_nfit(state, table_offsets, table_data, linker, 1373602b4582SMarian Postevca oem_id, oem_table_id); 1374264813cbSXiao Guangrong g_slist_free(device_list); 1375bdfd065bSXiao Guangrong } 1376