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