| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 5 | | - * it under the terms of version 2 of the GNU General Public License as |
|---|
| 6 | | - * published by the Free Software Foundation. |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 9 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 11 | | - * General Public License for more details. |
|---|
| 12 | 4 | */ |
|---|
| 13 | 5 | #include <linux/list_sort.h> |
|---|
| 14 | 6 | #include <linux/libnvdimm.h> |
|---|
| 15 | 7 | #include <linux/module.h> |
|---|
| 8 | +#include <linux/nospec.h> |
|---|
| 16 | 9 | #include <linux/mutex.h> |
|---|
| 17 | 10 | #include <linux/ndctl.h> |
|---|
| 18 | 11 | #include <linux/sysfs.h> |
|---|
| .. | .. |
|---|
| 24 | 17 | #include <linux/nd.h> |
|---|
| 25 | 18 | #include <asm/cacheflush.h> |
|---|
| 26 | 19 | #include <acpi/nfit.h> |
|---|
| 20 | +#include "intel.h" |
|---|
| 27 | 21 | #include "nfit.h" |
|---|
| 28 | 22 | |
|---|
| 29 | 23 | /* |
|---|
| .. | .. |
|---|
| 54 | 48 | module_param(no_init_ars, bool, 0644); |
|---|
| 55 | 49 | MODULE_PARM_DESC(no_init_ars, "Skip ARS run at nfit init time"); |
|---|
| 56 | 50 | |
|---|
| 51 | +static bool force_labels; |
|---|
| 52 | +module_param(force_labels, bool, 0444); |
|---|
| 53 | +MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods"); |
|---|
| 54 | + |
|---|
| 57 | 55 | LIST_HEAD(acpi_descs); |
|---|
| 58 | 56 | DEFINE_MUTEX(acpi_desc_lock); |
|---|
| 59 | 57 | |
|---|
| .. | .. |
|---|
| 76 | 74 | } |
|---|
| 77 | 75 | EXPORT_SYMBOL(to_nfit_uuid); |
|---|
| 78 | 76 | |
|---|
| 79 | | -static struct acpi_nfit_desc *to_acpi_nfit_desc( |
|---|
| 80 | | - struct nvdimm_bus_descriptor *nd_desc) |
|---|
| 77 | +static const guid_t *to_nfit_bus_uuid(int family) |
|---|
| 81 | 78 | { |
|---|
| 82 | | - return container_of(nd_desc, struct acpi_nfit_desc, nd_desc); |
|---|
| 79 | + if (WARN_ONCE(family == NVDIMM_BUS_FAMILY_NFIT, |
|---|
| 80 | + "only secondary bus families can be translated\n")) |
|---|
| 81 | + return NULL; |
|---|
| 82 | + /* |
|---|
| 83 | + * The index of bus UUIDs starts immediately following the last |
|---|
| 84 | + * NVDIMM/leaf family. |
|---|
| 85 | + */ |
|---|
| 86 | + return to_nfit_uuid(family + NVDIMM_FAMILY_MAX); |
|---|
| 83 | 87 | } |
|---|
| 84 | 88 | |
|---|
| 85 | 89 | static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc) |
|---|
| .. | .. |
|---|
| 191 | 195 | * In the _LSI, _LSR, _LSW case the locked status is |
|---|
| 192 | 196 | * communicated via the read/write commands |
|---|
| 193 | 197 | */ |
|---|
| 194 | | - if (nfit_mem->has_lsr) |
|---|
| 198 | + if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) |
|---|
| 195 | 199 | break; |
|---|
| 196 | 200 | |
|---|
| 197 | 201 | if (status >> 16 & ND_CONFIG_LOCKED) |
|---|
| 198 | 202 | return -EACCES; |
|---|
| 199 | 203 | break; |
|---|
| 200 | 204 | case ND_CMD_GET_CONFIG_DATA: |
|---|
| 201 | | - if (nfit_mem->has_lsr && status == ACPI_LABELS_LOCKED) |
|---|
| 205 | + if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags) |
|---|
| 206 | + && status == ACPI_LABELS_LOCKED) |
|---|
| 202 | 207 | return -EACCES; |
|---|
| 203 | 208 | break; |
|---|
| 204 | 209 | case ND_CMD_SET_CONFIG_DATA: |
|---|
| 205 | | - if (nfit_mem->has_lsw && status == ACPI_LABELS_LOCKED) |
|---|
| 210 | + if (test_bit(NFIT_MEM_LSW, &nfit_mem->flags) |
|---|
| 211 | + && status == ACPI_LABELS_LOCKED) |
|---|
| 206 | 212 | return -EACCES; |
|---|
| 207 | 213 | break; |
|---|
| 208 | 214 | default: |
|---|
| .. | .. |
|---|
| 367 | 373 | |
|---|
| 368 | 374 | static u8 nfit_dsm_revid(unsigned family, unsigned func) |
|---|
| 369 | 375 | { |
|---|
| 370 | | - static const u8 revid_table[NVDIMM_FAMILY_MAX+1][32] = { |
|---|
| 376 | + static const u8 revid_table[NVDIMM_FAMILY_MAX+1][NVDIMM_CMD_MAX+1] = { |
|---|
| 371 | 377 | [NVDIMM_FAMILY_INTEL] = { |
|---|
| 372 | | - [NVDIMM_INTEL_GET_MODES] = 2, |
|---|
| 373 | | - [NVDIMM_INTEL_GET_FWINFO] = 2, |
|---|
| 374 | | - [NVDIMM_INTEL_START_FWUPDATE] = 2, |
|---|
| 375 | | - [NVDIMM_INTEL_SEND_FWUPDATE] = 2, |
|---|
| 376 | | - [NVDIMM_INTEL_FINISH_FWUPDATE] = 2, |
|---|
| 377 | | - [NVDIMM_INTEL_QUERY_FWUPDATE] = 2, |
|---|
| 378 | | - [NVDIMM_INTEL_SET_THRESHOLD] = 2, |
|---|
| 379 | | - [NVDIMM_INTEL_INJECT_ERROR] = 2, |
|---|
| 378 | + [NVDIMM_INTEL_GET_MODES ... |
|---|
| 379 | + NVDIMM_INTEL_FW_ACTIVATE_ARM] = 2, |
|---|
| 380 | 380 | }, |
|---|
| 381 | 381 | }; |
|---|
| 382 | 382 | u8 id; |
|---|
| 383 | 383 | |
|---|
| 384 | 384 | if (family > NVDIMM_FAMILY_MAX) |
|---|
| 385 | 385 | return 0; |
|---|
| 386 | | - if (func > 31) |
|---|
| 386 | + if (func > NVDIMM_CMD_MAX) |
|---|
| 387 | 387 | return 0; |
|---|
| 388 | 388 | id = revid_table[family][func]; |
|---|
| 389 | 389 | if (id == 0) |
|---|
| .. | .. |
|---|
| 391 | 391 | return id; |
|---|
| 392 | 392 | } |
|---|
| 393 | 393 | |
|---|
| 394 | +static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func) |
|---|
| 395 | +{ |
|---|
| 396 | + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); |
|---|
| 397 | + |
|---|
| 398 | + if (nfit_mem && nfit_mem->family == NVDIMM_FAMILY_INTEL |
|---|
| 399 | + && func >= NVDIMM_INTEL_GET_SECURITY_STATE |
|---|
| 400 | + && func <= NVDIMM_INTEL_MASTER_SECURE_ERASE) |
|---|
| 401 | + return IS_ENABLED(CONFIG_NFIT_SECURITY_DEBUG); |
|---|
| 402 | + return true; |
|---|
| 403 | +} |
|---|
| 404 | + |
|---|
| 394 | 405 | static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd, |
|---|
| 395 | | - struct nd_cmd_pkg *call_pkg) |
|---|
| 406 | + struct nd_cmd_pkg *call_pkg, int *family) |
|---|
| 396 | 407 | { |
|---|
| 397 | 408 | if (call_pkg) { |
|---|
| 398 | 409 | int i; |
|---|
| .. | .. |
|---|
| 403 | 414 | for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++) |
|---|
| 404 | 415 | if (call_pkg->nd_reserved2[i]) |
|---|
| 405 | 416 | return -EINVAL; |
|---|
| 417 | + *family = call_pkg->nd_family; |
|---|
| 406 | 418 | return call_pkg->nd_command; |
|---|
| 407 | 419 | } |
|---|
| 408 | 420 | |
|---|
| .. | .. |
|---|
| 424 | 436 | int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, |
|---|
| 425 | 437 | unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) |
|---|
| 426 | 438 | { |
|---|
| 427 | | - struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); |
|---|
| 439 | + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); |
|---|
| 428 | 440 | struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); |
|---|
| 429 | 441 | union acpi_object in_obj, in_buf, *out_obj; |
|---|
| 430 | 442 | const struct nd_cmd_desc *desc = NULL; |
|---|
| .. | .. |
|---|
| 436 | 448 | acpi_handle handle; |
|---|
| 437 | 449 | const guid_t *guid; |
|---|
| 438 | 450 | int func, rc, i; |
|---|
| 451 | + int family = 0; |
|---|
| 439 | 452 | |
|---|
| 440 | 453 | if (cmd_rc) |
|---|
| 441 | 454 | *cmd_rc = -EINVAL; |
|---|
| 442 | 455 | |
|---|
| 443 | 456 | if (cmd == ND_CMD_CALL) |
|---|
| 444 | 457 | call_pkg = buf; |
|---|
| 445 | | - func = cmd_to_func(nfit_mem, cmd, call_pkg); |
|---|
| 458 | + func = cmd_to_func(nfit_mem, cmd, call_pkg, &family); |
|---|
| 446 | 459 | if (func < 0) |
|---|
| 447 | 460 | return func; |
|---|
| 448 | 461 | |
|---|
| .. | .. |
|---|
| 464 | 477 | |
|---|
| 465 | 478 | cmd_name = nvdimm_bus_cmd_name(cmd); |
|---|
| 466 | 479 | cmd_mask = nd_desc->cmd_mask; |
|---|
| 467 | | - dsm_mask = nd_desc->bus_dsm_mask; |
|---|
| 480 | + if (cmd == ND_CMD_CALL && call_pkg->nd_family) { |
|---|
| 481 | + family = call_pkg->nd_family; |
|---|
| 482 | + if (family > NVDIMM_BUS_FAMILY_MAX || |
|---|
| 483 | + !test_bit(family, &nd_desc->bus_family_mask)) |
|---|
| 484 | + return -EINVAL; |
|---|
| 485 | + family = array_index_nospec(family, |
|---|
| 486 | + NVDIMM_BUS_FAMILY_MAX + 1); |
|---|
| 487 | + dsm_mask = acpi_desc->family_dsm_mask[family]; |
|---|
| 488 | + guid = to_nfit_bus_uuid(family); |
|---|
| 489 | + } else { |
|---|
| 490 | + dsm_mask = acpi_desc->bus_dsm_mask; |
|---|
| 491 | + guid = to_nfit_uuid(NFIT_DEV_BUS); |
|---|
| 492 | + } |
|---|
| 468 | 493 | desc = nd_cmd_bus_desc(cmd); |
|---|
| 469 | | - guid = to_nfit_uuid(NFIT_DEV_BUS); |
|---|
| 470 | 494 | handle = adev->handle; |
|---|
| 471 | 495 | dimm_name = "bus"; |
|---|
| 472 | 496 | } |
|---|
| .. | .. |
|---|
| 478 | 502 | * Check for a valid command. For ND_CMD_CALL, we also have to |
|---|
| 479 | 503 | * make sure that the DSM function is supported. |
|---|
| 480 | 504 | */ |
|---|
| 481 | | - if (cmd == ND_CMD_CALL && !test_bit(func, &dsm_mask)) |
|---|
| 505 | + if (cmd == ND_CMD_CALL && |
|---|
| 506 | + (func > NVDIMM_CMD_MAX || !test_bit(func, &dsm_mask))) |
|---|
| 482 | 507 | return -ENOTTY; |
|---|
| 483 | 508 | else if (!test_bit(cmd, &cmd_mask)) |
|---|
| 484 | 509 | return -ENOTTY; |
|---|
| .. | .. |
|---|
| 501 | 526 | in_buf.buffer.length = call_pkg->nd_size_in; |
|---|
| 502 | 527 | } |
|---|
| 503 | 528 | |
|---|
| 504 | | - dev_dbg(dev, "%s cmd: %d: func: %d input length: %d\n", |
|---|
| 505 | | - dimm_name, cmd, func, in_buf.buffer.length); |
|---|
| 506 | | - print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, |
|---|
| 507 | | - in_buf.buffer.pointer, |
|---|
| 508 | | - min_t(u32, 256, in_buf.buffer.length), true); |
|---|
| 529 | + dev_dbg(dev, "%s cmd: %d: family: %d func: %d input length: %d\n", |
|---|
| 530 | + dimm_name, cmd, family, func, in_buf.buffer.length); |
|---|
| 531 | + if (payload_dumpable(nvdimm, func)) |
|---|
| 532 | + print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, |
|---|
| 533 | + in_buf.buffer.pointer, |
|---|
| 534 | + min_t(u32, 256, in_buf.buffer.length), true); |
|---|
| 509 | 535 | |
|---|
| 510 | 536 | /* call the BIOS, prefer the named methods over _DSM if available */ |
|---|
| 511 | | - if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE && nfit_mem->has_lsr) |
|---|
| 537 | + if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE |
|---|
| 538 | + && test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) |
|---|
| 512 | 539 | out_obj = acpi_label_info(handle); |
|---|
| 513 | | - else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && nfit_mem->has_lsr) { |
|---|
| 540 | + else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA |
|---|
| 541 | + && test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) { |
|---|
| 514 | 542 | struct nd_cmd_get_config_data_hdr *p = buf; |
|---|
| 515 | 543 | |
|---|
| 516 | 544 | out_obj = acpi_label_read(handle, p->in_offset, p->in_length); |
|---|
| 517 | 545 | } else if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA |
|---|
| 518 | | - && nfit_mem->has_lsw) { |
|---|
| 546 | + && test_bit(NFIT_MEM_LSW, &nfit_mem->flags)) { |
|---|
| 519 | 547 | struct nd_cmd_set_config_hdr *p = buf; |
|---|
| 520 | 548 | |
|---|
| 521 | 549 | out_obj = acpi_label_write(handle, p->in_offset, p->in_length, |
|---|
| .. | .. |
|---|
| 1220 | 1248 | { |
|---|
| 1221 | 1249 | struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); |
|---|
| 1222 | 1250 | struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); |
|---|
| 1251 | + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); |
|---|
| 1223 | 1252 | |
|---|
| 1224 | | - return sprintf(buf, "%#lx\n", nd_desc->bus_dsm_mask); |
|---|
| 1253 | + return sprintf(buf, "%#lx\n", acpi_desc->bus_dsm_mask); |
|---|
| 1225 | 1254 | } |
|---|
| 1226 | 1255 | static struct device_attribute dev_attr_bus_dsm_mask = |
|---|
| 1227 | 1256 | __ATTR(dsm_mask, 0444, bus_dsm_mask_show, NULL); |
|---|
| .. | .. |
|---|
| 1265 | 1294 | if (rc) |
|---|
| 1266 | 1295 | return rc; |
|---|
| 1267 | 1296 | |
|---|
| 1268 | | - device_lock(dev); |
|---|
| 1297 | + nfit_device_lock(dev); |
|---|
| 1269 | 1298 | nd_desc = dev_get_drvdata(dev); |
|---|
| 1270 | 1299 | if (nd_desc) { |
|---|
| 1271 | 1300 | struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); |
|---|
| .. | .. |
|---|
| 1282 | 1311 | break; |
|---|
| 1283 | 1312 | } |
|---|
| 1284 | 1313 | } |
|---|
| 1285 | | - device_unlock(dev); |
|---|
| 1314 | + nfit_device_unlock(dev); |
|---|
| 1286 | 1315 | if (rc) |
|---|
| 1287 | 1316 | return rc; |
|---|
| 1288 | 1317 | return size; |
|---|
| .. | .. |
|---|
| 1302 | 1331 | ssize_t rc = -ENXIO; |
|---|
| 1303 | 1332 | bool busy; |
|---|
| 1304 | 1333 | |
|---|
| 1305 | | - device_lock(dev); |
|---|
| 1334 | + nfit_device_lock(dev); |
|---|
| 1306 | 1335 | nd_desc = dev_get_drvdata(dev); |
|---|
| 1307 | 1336 | if (!nd_desc) { |
|---|
| 1308 | | - device_unlock(dev); |
|---|
| 1337 | + nfit_device_unlock(dev); |
|---|
| 1309 | 1338 | return rc; |
|---|
| 1310 | 1339 | } |
|---|
| 1311 | 1340 | acpi_desc = to_acpi_desc(nd_desc); |
|---|
| .. | .. |
|---|
| 1322 | 1351 | } |
|---|
| 1323 | 1352 | |
|---|
| 1324 | 1353 | mutex_unlock(&acpi_desc->init_mutex); |
|---|
| 1325 | | - device_unlock(dev); |
|---|
| 1354 | + nfit_device_unlock(dev); |
|---|
| 1326 | 1355 | return rc; |
|---|
| 1327 | 1356 | } |
|---|
| 1328 | 1357 | |
|---|
| .. | .. |
|---|
| 1339 | 1368 | if (val != 1) |
|---|
| 1340 | 1369 | return -EINVAL; |
|---|
| 1341 | 1370 | |
|---|
| 1342 | | - device_lock(dev); |
|---|
| 1371 | + nfit_device_lock(dev); |
|---|
| 1343 | 1372 | nd_desc = dev_get_drvdata(dev); |
|---|
| 1344 | 1373 | if (nd_desc) { |
|---|
| 1345 | 1374 | struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); |
|---|
| 1346 | 1375 | |
|---|
| 1347 | 1376 | rc = acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG); |
|---|
| 1348 | 1377 | } |
|---|
| 1349 | | - device_unlock(dev); |
|---|
| 1378 | + nfit_device_unlock(dev); |
|---|
| 1350 | 1379 | if (rc) |
|---|
| 1351 | 1380 | return rc; |
|---|
| 1352 | 1381 | return size; |
|---|
| .. | .. |
|---|
| 1364 | 1393 | |
|---|
| 1365 | 1394 | static umode_t nfit_visible(struct kobject *kobj, struct attribute *a, int n) |
|---|
| 1366 | 1395 | { |
|---|
| 1367 | | - struct device *dev = container_of(kobj, struct device, kobj); |
|---|
| 1396 | + struct device *dev = kobj_to_dev(kobj); |
|---|
| 1368 | 1397 | struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); |
|---|
| 1369 | 1398 | |
|---|
| 1370 | | - if (a == &dev_attr_scrub.attr && !ars_supported(nvdimm_bus)) |
|---|
| 1371 | | - return 0; |
|---|
| 1399 | + if (a == &dev_attr_scrub.attr) |
|---|
| 1400 | + return ars_supported(nvdimm_bus) ? a->mode : 0; |
|---|
| 1401 | + |
|---|
| 1402 | + if (a == &dev_attr_firmware_activate_noidle.attr) |
|---|
| 1403 | + return intel_fwa_supported(nvdimm_bus) ? a->mode : 0; |
|---|
| 1404 | + |
|---|
| 1372 | 1405 | return a->mode; |
|---|
| 1373 | 1406 | } |
|---|
| 1374 | 1407 | |
|---|
| .. | .. |
|---|
| 1377 | 1410 | &dev_attr_scrub.attr, |
|---|
| 1378 | 1411 | &dev_attr_hw_error_scrub.attr, |
|---|
| 1379 | 1412 | &dev_attr_bus_dsm_mask.attr, |
|---|
| 1413 | + &dev_attr_firmware_activate_noidle.attr, |
|---|
| 1380 | 1414 | NULL, |
|---|
| 1381 | 1415 | }; |
|---|
| 1382 | 1416 | |
|---|
| .. | .. |
|---|
| 1387 | 1421 | }; |
|---|
| 1388 | 1422 | |
|---|
| 1389 | 1423 | static const struct attribute_group *acpi_nfit_attribute_groups[] = { |
|---|
| 1390 | | - &nvdimm_bus_attribute_group, |
|---|
| 1391 | 1424 | &acpi_nfit_attribute_group, |
|---|
| 1392 | 1425 | NULL, |
|---|
| 1393 | 1426 | }; |
|---|
| .. | .. |
|---|
| 1588 | 1621 | static ssize_t flags_show(struct device *dev, |
|---|
| 1589 | 1622 | struct device_attribute *attr, char *buf) |
|---|
| 1590 | 1623 | { |
|---|
| 1591 | | - u16 flags = to_nfit_memdev(dev)->flags; |
|---|
| 1624 | + struct nvdimm *nvdimm = to_nvdimm(dev); |
|---|
| 1625 | + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); |
|---|
| 1626 | + u16 flags = __to_nfit_memdev(nfit_mem)->flags; |
|---|
| 1627 | + |
|---|
| 1628 | + if (test_bit(NFIT_MEM_DIRTY, &nfit_mem->flags)) |
|---|
| 1629 | + flags |= ACPI_NFIT_MEM_FLUSH_FAILED; |
|---|
| 1592 | 1630 | |
|---|
| 1593 | 1631 | return sprintf(buf, "%s%s%s%s%s%s%s\n", |
|---|
| 1594 | 1632 | flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "", |
|---|
| .. | .. |
|---|
| 1604 | 1642 | static ssize_t id_show(struct device *dev, |
|---|
| 1605 | 1643 | struct device_attribute *attr, char *buf) |
|---|
| 1606 | 1644 | { |
|---|
| 1607 | | - struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); |
|---|
| 1645 | + struct nvdimm *nvdimm = to_nvdimm(dev); |
|---|
| 1646 | + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); |
|---|
| 1608 | 1647 | |
|---|
| 1609 | | - if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID) |
|---|
| 1610 | | - return sprintf(buf, "%04x-%02x-%04x-%08x\n", |
|---|
| 1611 | | - be16_to_cpu(dcr->vendor_id), |
|---|
| 1612 | | - dcr->manufacturing_location, |
|---|
| 1613 | | - be16_to_cpu(dcr->manufacturing_date), |
|---|
| 1614 | | - be32_to_cpu(dcr->serial_number)); |
|---|
| 1615 | | - else |
|---|
| 1616 | | - return sprintf(buf, "%04x-%08x\n", |
|---|
| 1617 | | - be16_to_cpu(dcr->vendor_id), |
|---|
| 1618 | | - be32_to_cpu(dcr->serial_number)); |
|---|
| 1648 | + return sprintf(buf, "%s\n", nfit_mem->id); |
|---|
| 1619 | 1649 | } |
|---|
| 1620 | 1650 | static DEVICE_ATTR_RO(id); |
|---|
| 1651 | + |
|---|
| 1652 | +static ssize_t dirty_shutdown_show(struct device *dev, |
|---|
| 1653 | + struct device_attribute *attr, char *buf) |
|---|
| 1654 | +{ |
|---|
| 1655 | + struct nvdimm *nvdimm = to_nvdimm(dev); |
|---|
| 1656 | + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); |
|---|
| 1657 | + |
|---|
| 1658 | + return sprintf(buf, "%d\n", nfit_mem->dirty_shutdown); |
|---|
| 1659 | +} |
|---|
| 1660 | +static DEVICE_ATTR_RO(dirty_shutdown); |
|---|
| 1621 | 1661 | |
|---|
| 1622 | 1662 | static struct attribute *acpi_nfit_dimm_attributes[] = { |
|---|
| 1623 | 1663 | &dev_attr_handle.attr, |
|---|
| .. | .. |
|---|
| 1636 | 1676 | &dev_attr_id.attr, |
|---|
| 1637 | 1677 | &dev_attr_family.attr, |
|---|
| 1638 | 1678 | &dev_attr_dsm_mask.attr, |
|---|
| 1679 | + &dev_attr_dirty_shutdown.attr, |
|---|
| 1639 | 1680 | NULL, |
|---|
| 1640 | 1681 | }; |
|---|
| 1641 | 1682 | |
|---|
| 1642 | 1683 | static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj, |
|---|
| 1643 | 1684 | struct attribute *a, int n) |
|---|
| 1644 | 1685 | { |
|---|
| 1645 | | - struct device *dev = container_of(kobj, struct device, kobj); |
|---|
| 1686 | + struct device *dev = kobj_to_dev(kobj); |
|---|
| 1646 | 1687 | struct nvdimm *nvdimm = to_nvdimm(dev); |
|---|
| 1688 | + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); |
|---|
| 1647 | 1689 | |
|---|
| 1648 | 1690 | if (!to_nfit_dcr(dev)) { |
|---|
| 1649 | 1691 | /* Without a dcr only the memdev attributes can be surfaced */ |
|---|
| .. | .. |
|---|
| 1657 | 1699 | |
|---|
| 1658 | 1700 | if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1) |
|---|
| 1659 | 1701 | return 0; |
|---|
| 1702 | + |
|---|
| 1703 | + if (!test_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags) |
|---|
| 1704 | + && a == &dev_attr_dirty_shutdown.attr) |
|---|
| 1705 | + return 0; |
|---|
| 1706 | + |
|---|
| 1660 | 1707 | return a->mode; |
|---|
| 1661 | 1708 | } |
|---|
| 1662 | 1709 | |
|---|
| .. | .. |
|---|
| 1667 | 1714 | }; |
|---|
| 1668 | 1715 | |
|---|
| 1669 | 1716 | static const struct attribute_group *acpi_nfit_dimm_attribute_groups[] = { |
|---|
| 1670 | | - &nvdimm_attribute_group, |
|---|
| 1671 | | - &nd_device_attribute_group, |
|---|
| 1672 | 1717 | &acpi_nfit_dimm_attribute_group, |
|---|
| 1673 | 1718 | NULL, |
|---|
| 1674 | 1719 | }; |
|---|
| .. | .. |
|---|
| 1718 | 1763 | struct acpi_device *adev = data; |
|---|
| 1719 | 1764 | struct device *dev = &adev->dev; |
|---|
| 1720 | 1765 | |
|---|
| 1721 | | - device_lock(dev->parent); |
|---|
| 1766 | + nfit_device_lock(dev->parent); |
|---|
| 1722 | 1767 | __acpi_nvdimm_notify(dev, event); |
|---|
| 1723 | | - device_unlock(dev->parent); |
|---|
| 1768 | + nfit_device_unlock(dev->parent); |
|---|
| 1724 | 1769 | } |
|---|
| 1725 | 1770 | |
|---|
| 1726 | 1771 | static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method) |
|---|
| .. | .. |
|---|
| 1735 | 1780 | return false; |
|---|
| 1736 | 1781 | } |
|---|
| 1737 | 1782 | |
|---|
| 1783 | +__weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem) |
|---|
| 1784 | +{ |
|---|
| 1785 | + struct device *dev = &nfit_mem->adev->dev; |
|---|
| 1786 | + struct nd_intel_smart smart = { 0 }; |
|---|
| 1787 | + union acpi_object in_buf = { |
|---|
| 1788 | + .buffer.type = ACPI_TYPE_BUFFER, |
|---|
| 1789 | + .buffer.length = 0, |
|---|
| 1790 | + }; |
|---|
| 1791 | + union acpi_object in_obj = { |
|---|
| 1792 | + .package.type = ACPI_TYPE_PACKAGE, |
|---|
| 1793 | + .package.count = 1, |
|---|
| 1794 | + .package.elements = &in_buf, |
|---|
| 1795 | + }; |
|---|
| 1796 | + const u8 func = ND_INTEL_SMART; |
|---|
| 1797 | + const guid_t *guid = to_nfit_uuid(nfit_mem->family); |
|---|
| 1798 | + u8 revid = nfit_dsm_revid(nfit_mem->family, func); |
|---|
| 1799 | + struct acpi_device *adev = nfit_mem->adev; |
|---|
| 1800 | + acpi_handle handle = adev->handle; |
|---|
| 1801 | + union acpi_object *out_obj; |
|---|
| 1802 | + |
|---|
| 1803 | + if ((nfit_mem->dsm_mask & (1 << func)) == 0) |
|---|
| 1804 | + return; |
|---|
| 1805 | + |
|---|
| 1806 | + out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj); |
|---|
| 1807 | + if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER |
|---|
| 1808 | + || out_obj->buffer.length < sizeof(smart)) { |
|---|
| 1809 | + dev_dbg(dev->parent, "%s: failed to retrieve initial health\n", |
|---|
| 1810 | + dev_name(dev)); |
|---|
| 1811 | + ACPI_FREE(out_obj); |
|---|
| 1812 | + return; |
|---|
| 1813 | + } |
|---|
| 1814 | + memcpy(&smart, out_obj->buffer.pointer, sizeof(smart)); |
|---|
| 1815 | + ACPI_FREE(out_obj); |
|---|
| 1816 | + |
|---|
| 1817 | + if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) { |
|---|
| 1818 | + if (smart.shutdown_state) |
|---|
| 1819 | + set_bit(NFIT_MEM_DIRTY, &nfit_mem->flags); |
|---|
| 1820 | + } |
|---|
| 1821 | + |
|---|
| 1822 | + if (smart.flags & ND_INTEL_SMART_SHUTDOWN_COUNT_VALID) { |
|---|
| 1823 | + set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags); |
|---|
| 1824 | + nfit_mem->dirty_shutdown = smart.shutdown_count; |
|---|
| 1825 | + } |
|---|
| 1826 | +} |
|---|
| 1827 | + |
|---|
| 1828 | +static void populate_shutdown_status(struct nfit_mem *nfit_mem) |
|---|
| 1829 | +{ |
|---|
| 1830 | + /* |
|---|
| 1831 | + * For DIMMs that provide a dynamic facility to retrieve a |
|---|
| 1832 | + * dirty-shutdown status and/or a dirty-shutdown count, cache |
|---|
| 1833 | + * these values in nfit_mem. |
|---|
| 1834 | + */ |
|---|
| 1835 | + if (nfit_mem->family == NVDIMM_FAMILY_INTEL) |
|---|
| 1836 | + nfit_intel_shutdown_status(nfit_mem); |
|---|
| 1837 | +} |
|---|
| 1838 | + |
|---|
| 1738 | 1839 | static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, |
|---|
| 1739 | 1840 | struct nfit_mem *nfit_mem, u32 device_handle) |
|---|
| 1740 | 1841 | { |
|---|
| 1842 | + struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc; |
|---|
| 1741 | 1843 | struct acpi_device *adev, *adev_dimm; |
|---|
| 1742 | 1844 | struct device *dev = acpi_desc->dev; |
|---|
| 1743 | 1845 | unsigned long dsm_mask, label_mask; |
|---|
| 1744 | 1846 | const guid_t *guid; |
|---|
| 1745 | 1847 | int i; |
|---|
| 1746 | 1848 | int family = -1; |
|---|
| 1849 | + struct acpi_nfit_control_region *dcr = nfit_mem->dcr; |
|---|
| 1747 | 1850 | |
|---|
| 1748 | 1851 | /* nfit test assumes 1:1 relationship between commands and dsms */ |
|---|
| 1749 | 1852 | nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en; |
|---|
| 1750 | 1853 | nfit_mem->family = NVDIMM_FAMILY_INTEL; |
|---|
| 1854 | + set_bit(NVDIMM_FAMILY_INTEL, &nd_desc->dimm_family_mask); |
|---|
| 1855 | + |
|---|
| 1856 | + if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID) |
|---|
| 1857 | + sprintf(nfit_mem->id, "%04x-%02x-%04x-%08x", |
|---|
| 1858 | + be16_to_cpu(dcr->vendor_id), |
|---|
| 1859 | + dcr->manufacturing_location, |
|---|
| 1860 | + be16_to_cpu(dcr->manufacturing_date), |
|---|
| 1861 | + be32_to_cpu(dcr->serial_number)); |
|---|
| 1862 | + else |
|---|
| 1863 | + sprintf(nfit_mem->id, "%04x-%08x", |
|---|
| 1864 | + be16_to_cpu(dcr->vendor_id), |
|---|
| 1865 | + be32_to_cpu(dcr->serial_number)); |
|---|
| 1866 | + |
|---|
| 1751 | 1867 | adev = to_acpi_dev(acpi_desc); |
|---|
| 1752 | | - if (!adev) |
|---|
| 1868 | + if (!adev) { |
|---|
| 1869 | + /* unit test case */ |
|---|
| 1870 | + populate_shutdown_status(nfit_mem); |
|---|
| 1753 | 1871 | return 0; |
|---|
| 1872 | + } |
|---|
| 1754 | 1873 | |
|---|
| 1755 | 1874 | adev_dimm = acpi_find_child_device(adev, device_handle, false); |
|---|
| 1756 | 1875 | nfit_mem->adev = adev_dimm; |
|---|
| .. | .. |
|---|
| 1785 | 1904 | * Note, that checking for function0 (bit0) tells us if any commands |
|---|
| 1786 | 1905 | * are reachable through this GUID. |
|---|
| 1787 | 1906 | */ |
|---|
| 1907 | + clear_bit(NVDIMM_FAMILY_INTEL, &nd_desc->dimm_family_mask); |
|---|
| 1788 | 1908 | for (i = 0; i <= NVDIMM_FAMILY_MAX; i++) |
|---|
| 1789 | | - if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) |
|---|
| 1909 | + if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) { |
|---|
| 1910 | + set_bit(i, &nd_desc->dimm_family_mask); |
|---|
| 1790 | 1911 | if (family < 0 || i == default_dsm_family) |
|---|
| 1791 | 1912 | family = i; |
|---|
| 1913 | + } |
|---|
| 1792 | 1914 | |
|---|
| 1793 | 1915 | /* limit the supported commands to those that are publicly documented */ |
|---|
| 1794 | 1916 | nfit_mem->family = family; |
|---|
| .. | .. |
|---|
| 1837 | 1959 | | 1 << ND_CMD_SET_CONFIG_DATA; |
|---|
| 1838 | 1960 | if (family == NVDIMM_FAMILY_INTEL |
|---|
| 1839 | 1961 | && (dsm_mask & label_mask) == label_mask) |
|---|
| 1840 | | - return 0; |
|---|
| 1962 | + /* skip _LS{I,R,W} enabling */; |
|---|
| 1963 | + else { |
|---|
| 1964 | + if (acpi_nvdimm_has_method(adev_dimm, "_LSI") |
|---|
| 1965 | + && acpi_nvdimm_has_method(adev_dimm, "_LSR")) { |
|---|
| 1966 | + dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev)); |
|---|
| 1967 | + set_bit(NFIT_MEM_LSR, &nfit_mem->flags); |
|---|
| 1968 | + } |
|---|
| 1841 | 1969 | |
|---|
| 1842 | | - if (acpi_nvdimm_has_method(adev_dimm, "_LSI") |
|---|
| 1843 | | - && acpi_nvdimm_has_method(adev_dimm, "_LSR")) { |
|---|
| 1844 | | - dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev)); |
|---|
| 1845 | | - nfit_mem->has_lsr = true; |
|---|
| 1970 | + if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags) |
|---|
| 1971 | + && acpi_nvdimm_has_method(adev_dimm, "_LSW")) { |
|---|
| 1972 | + dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); |
|---|
| 1973 | + set_bit(NFIT_MEM_LSW, &nfit_mem->flags); |
|---|
| 1974 | + } |
|---|
| 1975 | + |
|---|
| 1976 | + /* |
|---|
| 1977 | + * Quirk read-only label configurations to preserve |
|---|
| 1978 | + * access to label-less namespaces by default. |
|---|
| 1979 | + */ |
|---|
| 1980 | + if (!test_bit(NFIT_MEM_LSW, &nfit_mem->flags) |
|---|
| 1981 | + && !force_labels) { |
|---|
| 1982 | + dev_dbg(dev, "%s: No _LSW, disable labels\n", |
|---|
| 1983 | + dev_name(&adev_dimm->dev)); |
|---|
| 1984 | + clear_bit(NFIT_MEM_LSR, &nfit_mem->flags); |
|---|
| 1985 | + } else |
|---|
| 1986 | + dev_dbg(dev, "%s: Force enable labels\n", |
|---|
| 1987 | + dev_name(&adev_dimm->dev)); |
|---|
| 1846 | 1988 | } |
|---|
| 1847 | 1989 | |
|---|
| 1848 | | - if (nfit_mem->has_lsr && acpi_nvdimm_has_method(adev_dimm, "_LSW")) { |
|---|
| 1849 | | - dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); |
|---|
| 1850 | | - nfit_mem->has_lsw = true; |
|---|
| 1851 | | - } |
|---|
| 1990 | + populate_shutdown_status(nfit_mem); |
|---|
| 1852 | 1991 | |
|---|
| 1853 | 1992 | return 0; |
|---|
| 1854 | 1993 | } |
|---|
| .. | .. |
|---|
| 1879 | 2018 | mutex_unlock(&acpi_desc->init_mutex); |
|---|
| 1880 | 2019 | } |
|---|
| 1881 | 2020 | |
|---|
| 2021 | +static const struct nvdimm_security_ops *acpi_nfit_get_security_ops(int family) |
|---|
| 2022 | +{ |
|---|
| 2023 | + switch (family) { |
|---|
| 2024 | + case NVDIMM_FAMILY_INTEL: |
|---|
| 2025 | + return intel_security_ops; |
|---|
| 2026 | + default: |
|---|
| 2027 | + return NULL; |
|---|
| 2028 | + } |
|---|
| 2029 | +} |
|---|
| 2030 | + |
|---|
| 2031 | +static const struct nvdimm_fw_ops *acpi_nfit_get_fw_ops( |
|---|
| 2032 | + struct nfit_mem *nfit_mem) |
|---|
| 2033 | +{ |
|---|
| 2034 | + unsigned long mask; |
|---|
| 2035 | + struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc; |
|---|
| 2036 | + struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc; |
|---|
| 2037 | + |
|---|
| 2038 | + if (!nd_desc->fw_ops) |
|---|
| 2039 | + return NULL; |
|---|
| 2040 | + |
|---|
| 2041 | + if (nfit_mem->family != NVDIMM_FAMILY_INTEL) |
|---|
| 2042 | + return NULL; |
|---|
| 2043 | + |
|---|
| 2044 | + mask = nfit_mem->dsm_mask & NVDIMM_INTEL_FW_ACTIVATE_CMDMASK; |
|---|
| 2045 | + if (mask != NVDIMM_INTEL_FW_ACTIVATE_CMDMASK) |
|---|
| 2046 | + return NULL; |
|---|
| 2047 | + |
|---|
| 2048 | + return intel_fw_ops; |
|---|
| 2049 | +} |
|---|
| 2050 | + |
|---|
| 1882 | 2051 | static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) |
|---|
| 1883 | 2052 | { |
|---|
| 1884 | 2053 | struct nfit_mem *nfit_mem; |
|---|
| .. | .. |
|---|
| 1899 | 2068 | continue; |
|---|
| 1900 | 2069 | } |
|---|
| 1901 | 2070 | |
|---|
| 1902 | | - if (nfit_mem->bdw && nfit_mem->memdev_pmem) |
|---|
| 2071 | + if (nfit_mem->bdw && nfit_mem->memdev_pmem) { |
|---|
| 1903 | 2072 | set_bit(NDD_ALIASING, &flags); |
|---|
| 2073 | + set_bit(NDD_LABELING, &flags); |
|---|
| 2074 | + } |
|---|
| 1904 | 2075 | |
|---|
| 1905 | 2076 | /* collate flags across all memdevs for this dimm */ |
|---|
| 1906 | 2077 | list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { |
|---|
| .. | .. |
|---|
| 1936 | 2107 | cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK; |
|---|
| 1937 | 2108 | } |
|---|
| 1938 | 2109 | |
|---|
| 1939 | | - if (nfit_mem->has_lsr) { |
|---|
| 2110 | + /* Quirk to ignore LOCAL for labels on HYPERV DIMMs */ |
|---|
| 2111 | + if (nfit_mem->family == NVDIMM_FAMILY_HYPERV) |
|---|
| 2112 | + set_bit(NDD_NOBLK, &flags); |
|---|
| 2113 | + |
|---|
| 2114 | + if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) { |
|---|
| 1940 | 2115 | set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask); |
|---|
| 1941 | 2116 | set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask); |
|---|
| 1942 | 2117 | } |
|---|
| 1943 | | - if (nfit_mem->has_lsw) |
|---|
| 2118 | + if (test_bit(NFIT_MEM_LSW, &nfit_mem->flags)) |
|---|
| 1944 | 2119 | set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask); |
|---|
| 1945 | 2120 | |
|---|
| 1946 | 2121 | flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush |
|---|
| 1947 | 2122 | : NULL; |
|---|
| 1948 | | - nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, |
|---|
| 2123 | + nvdimm = __nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, |
|---|
| 1949 | 2124 | acpi_nfit_dimm_attribute_groups, |
|---|
| 1950 | 2125 | flags, cmd_mask, flush ? flush->hint_count : 0, |
|---|
| 1951 | | - nfit_mem->flush_wpq); |
|---|
| 2126 | + nfit_mem->flush_wpq, &nfit_mem->id[0], |
|---|
| 2127 | + acpi_nfit_get_security_ops(nfit_mem->family), |
|---|
| 2128 | + acpi_nfit_get_fw_ops(nfit_mem)); |
|---|
| 1952 | 2129 | if (!nvdimm) |
|---|
| 1953 | 2130 | return -ENOMEM; |
|---|
| 1954 | 2131 | |
|---|
| .. | .. |
|---|
| 1958 | 2135 | if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0) |
|---|
| 1959 | 2136 | continue; |
|---|
| 1960 | 2137 | |
|---|
| 1961 | | - dev_info(acpi_desc->dev, "%s flags:%s%s%s%s%s\n", |
|---|
| 2138 | + dev_err(acpi_desc->dev, "Error found in NVDIMM %s flags:%s%s%s%s%s\n", |
|---|
| 1962 | 2139 | nvdimm_name(nvdimm), |
|---|
| 1963 | 2140 | mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "", |
|---|
| 1964 | 2141 | mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"", |
|---|
| .. | .. |
|---|
| 2002 | 2179 | * these commands. |
|---|
| 2003 | 2180 | */ |
|---|
| 2004 | 2181 | enum nfit_aux_cmds { |
|---|
| 2005 | | - NFIT_CMD_TRANSLATE_SPA = 5, |
|---|
| 2006 | | - NFIT_CMD_ARS_INJECT_SET = 7, |
|---|
| 2007 | | - NFIT_CMD_ARS_INJECT_CLEAR = 8, |
|---|
| 2008 | | - NFIT_CMD_ARS_INJECT_GET = 9, |
|---|
| 2182 | + NFIT_CMD_TRANSLATE_SPA = 5, |
|---|
| 2183 | + NFIT_CMD_ARS_INJECT_SET = 7, |
|---|
| 2184 | + NFIT_CMD_ARS_INJECT_CLEAR = 8, |
|---|
| 2185 | + NFIT_CMD_ARS_INJECT_GET = 9, |
|---|
| 2009 | 2186 | }; |
|---|
| 2010 | 2187 | |
|---|
| 2011 | 2188 | static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc) |
|---|
| 2012 | 2189 | { |
|---|
| 2013 | 2190 | struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc; |
|---|
| 2014 | 2191 | const guid_t *guid = to_nfit_uuid(NFIT_DEV_BUS); |
|---|
| 2192 | + unsigned long dsm_mask, *mask; |
|---|
| 2015 | 2193 | struct acpi_device *adev; |
|---|
| 2016 | | - unsigned long dsm_mask; |
|---|
| 2017 | 2194 | int i; |
|---|
| 2018 | 2195 | |
|---|
| 2019 | | - nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en; |
|---|
| 2020 | | - nd_desc->bus_dsm_mask = acpi_desc->bus_nfit_cmd_force_en; |
|---|
| 2196 | + set_bit(ND_CMD_CALL, &nd_desc->cmd_mask); |
|---|
| 2197 | + set_bit(NVDIMM_BUS_FAMILY_NFIT, &nd_desc->bus_family_mask); |
|---|
| 2198 | + |
|---|
| 2199 | + /* enable nfit_test to inject bus command emulation */ |
|---|
| 2200 | + if (acpi_desc->bus_cmd_force_en) { |
|---|
| 2201 | + nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en; |
|---|
| 2202 | + mask = &nd_desc->bus_family_mask; |
|---|
| 2203 | + if (acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL]) { |
|---|
| 2204 | + set_bit(NVDIMM_BUS_FAMILY_INTEL, mask); |
|---|
| 2205 | + nd_desc->fw_ops = intel_bus_fw_ops; |
|---|
| 2206 | + } |
|---|
| 2207 | + } |
|---|
| 2208 | + |
|---|
| 2021 | 2209 | adev = to_acpi_dev(acpi_desc); |
|---|
| 2022 | 2210 | if (!adev) |
|---|
| 2023 | 2211 | return; |
|---|
| .. | .. |
|---|
| 2025 | 2213 | for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++) |
|---|
| 2026 | 2214 | if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i)) |
|---|
| 2027 | 2215 | set_bit(i, &nd_desc->cmd_mask); |
|---|
| 2028 | | - set_bit(ND_CMD_CALL, &nd_desc->cmd_mask); |
|---|
| 2029 | 2216 | |
|---|
| 2030 | 2217 | dsm_mask = |
|---|
| 2031 | 2218 | (1 << ND_CMD_ARS_CAP) | |
|---|
| .. | .. |
|---|
| 2038 | 2225 | (1 << NFIT_CMD_ARS_INJECT_GET); |
|---|
| 2039 | 2226 | for_each_set_bit(i, &dsm_mask, BITS_PER_LONG) |
|---|
| 2040 | 2227 | if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i)) |
|---|
| 2041 | | - set_bit(i, &nd_desc->bus_dsm_mask); |
|---|
| 2228 | + set_bit(i, &acpi_desc->bus_dsm_mask); |
|---|
| 2229 | + |
|---|
| 2230 | + /* Enumerate allowed NVDIMM_BUS_FAMILY_INTEL commands */ |
|---|
| 2231 | + dsm_mask = NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK; |
|---|
| 2232 | + guid = to_nfit_bus_uuid(NVDIMM_BUS_FAMILY_INTEL); |
|---|
| 2233 | + mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL]; |
|---|
| 2234 | + for_each_set_bit(i, &dsm_mask, BITS_PER_LONG) |
|---|
| 2235 | + if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i)) |
|---|
| 2236 | + set_bit(i, mask); |
|---|
| 2237 | + |
|---|
| 2238 | + if (*mask == dsm_mask) { |
|---|
| 2239 | + set_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask); |
|---|
| 2240 | + nd_desc->fw_ops = intel_bus_fw_ops; |
|---|
| 2241 | + } |
|---|
| 2042 | 2242 | } |
|---|
| 2043 | 2243 | |
|---|
| 2044 | 2244 | static ssize_t range_index_show(struct device *dev, |
|---|
| .. | .. |
|---|
| 2062 | 2262 | }; |
|---|
| 2063 | 2263 | |
|---|
| 2064 | 2264 | static const struct attribute_group *acpi_nfit_region_attribute_groups[] = { |
|---|
| 2065 | | - &nd_region_attribute_group, |
|---|
| 2066 | | - &nd_mapping_attribute_group, |
|---|
| 2067 | | - &nd_device_attribute_group, |
|---|
| 2068 | | - &nd_numa_attribute_group, |
|---|
| 2069 | 2265 | &acpi_nfit_region_attribute_group, |
|---|
| 2070 | 2266 | NULL, |
|---|
| 2071 | 2267 | }; |
|---|
| .. | .. |
|---|
| 2162 | 2358 | nd_set = devm_kzalloc(dev, sizeof(*nd_set), GFP_KERNEL); |
|---|
| 2163 | 2359 | if (!nd_set) |
|---|
| 2164 | 2360 | return -ENOMEM; |
|---|
| 2165 | | - ndr_desc->nd_set = nd_set; |
|---|
| 2166 | | - guid_copy(&nd_set->type_guid, (guid_t *) spa->range_guid); |
|---|
| 2361 | + import_guid(&nd_set->type_guid, spa->range_guid); |
|---|
| 2167 | 2362 | |
|---|
| 2168 | 2363 | info = devm_kzalloc(dev, sizeof_nfit_set_info(nr), GFP_KERNEL); |
|---|
| 2169 | 2364 | if (!info) |
|---|
| .. | .. |
|---|
| 2441 | 2636 | nfit_blk->bdw_offset = nfit_mem->bdw->offset; |
|---|
| 2442 | 2637 | mmio = &nfit_blk->mmio[BDW]; |
|---|
| 2443 | 2638 | mmio->addr.base = devm_nvdimm_memremap(dev, nfit_mem->spa_bdw->address, |
|---|
| 2444 | | - nfit_mem->spa_bdw->length, nd_blk_memremap_flags(ndbr)); |
|---|
| 2639 | + nfit_mem->spa_bdw->length, nd_blk_memremap_flags(ndbr)); |
|---|
| 2445 | 2640 | if (!mmio->addr.base) { |
|---|
| 2446 | 2641 | dev_dbg(dev, "%s failed to map bdw\n", |
|---|
| 2447 | 2642 | nvdimm_name(nvdimm)); |
|---|
| .. | .. |
|---|
| 2814 | 3009 | ndr_desc->res = &res; |
|---|
| 2815 | 3010 | ndr_desc->provider_data = nfit_spa; |
|---|
| 2816 | 3011 | ndr_desc->attr_groups = acpi_nfit_region_attribute_groups; |
|---|
| 2817 | | - if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) |
|---|
| 2818 | | - ndr_desc->numa_node = acpi_map_pxm_to_online_node( |
|---|
| 2819 | | - spa->proximity_domain); |
|---|
| 2820 | | - else |
|---|
| 3012 | + if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) { |
|---|
| 3013 | + ndr_desc->numa_node = pxm_to_online_node(spa->proximity_domain); |
|---|
| 3014 | + ndr_desc->target_node = pxm_to_node(spa->proximity_domain); |
|---|
| 3015 | + } else { |
|---|
| 2821 | 3016 | ndr_desc->numa_node = NUMA_NO_NODE; |
|---|
| 3017 | + ndr_desc->target_node = NUMA_NO_NODE; |
|---|
| 3018 | + } |
|---|
| 3019 | + |
|---|
| 3020 | + /* Fallback to address based numa information if node lookup failed */ |
|---|
| 3021 | + if (ndr_desc->numa_node == NUMA_NO_NODE) { |
|---|
| 3022 | + ndr_desc->numa_node = memory_add_physaddr_to_nid(spa->address); |
|---|
| 3023 | + dev_info(acpi_desc->dev, "changing numa node from %d to %d for nfit region [%pa-%pa]", |
|---|
| 3024 | + NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end); |
|---|
| 3025 | + } |
|---|
| 3026 | + if (ndr_desc->target_node == NUMA_NO_NODE) { |
|---|
| 3027 | + ndr_desc->target_node = phys_to_target_node(spa->address); |
|---|
| 3028 | + dev_info(acpi_desc->dev, "changing target node from %d to %d for nfit region [%pa-%pa]", |
|---|
| 3029 | + NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end); |
|---|
| 3030 | + } |
|---|
| 2822 | 3031 | |
|---|
| 2823 | 3032 | /* |
|---|
| 2824 | 3033 | * Persistence domain bits are hierarchical, if |
|---|
| .. | .. |
|---|
| 3142 | 3351 | static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc) |
|---|
| 3143 | 3352 | { |
|---|
| 3144 | 3353 | struct nfit_spa *nfit_spa; |
|---|
| 3145 | | - int rc; |
|---|
| 3354 | + int rc, do_sched_ars = 0; |
|---|
| 3146 | 3355 | |
|---|
| 3147 | 3356 | set_bit(ARS_VALID, &acpi_desc->scrub_flags); |
|---|
| 3148 | 3357 | list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { |
|---|
| .. | .. |
|---|
| 3154 | 3363 | } |
|---|
| 3155 | 3364 | } |
|---|
| 3156 | 3365 | |
|---|
| 3157 | | - list_for_each_entry(nfit_spa, &acpi_desc->spas, list) |
|---|
| 3366 | + list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { |
|---|
| 3158 | 3367 | switch (nfit_spa_type(nfit_spa->spa)) { |
|---|
| 3159 | 3368 | case NFIT_SPA_VOLATILE: |
|---|
| 3160 | 3369 | case NFIT_SPA_PM: |
|---|
| .. | .. |
|---|
| 3162 | 3371 | rc = ars_register(acpi_desc, nfit_spa); |
|---|
| 3163 | 3372 | if (rc) |
|---|
| 3164 | 3373 | return rc; |
|---|
| 3374 | + |
|---|
| 3375 | + /* |
|---|
| 3376 | + * Kick off background ARS if at least one |
|---|
| 3377 | + * region successfully registered ARS |
|---|
| 3378 | + */ |
|---|
| 3379 | + if (!test_bit(ARS_FAILED, &nfit_spa->ars_state)) |
|---|
| 3380 | + do_sched_ars++; |
|---|
| 3165 | 3381 | break; |
|---|
| 3166 | 3382 | case NFIT_SPA_BDW: |
|---|
| 3167 | 3383 | /* nothing to register */ |
|---|
| .. | .. |
|---|
| 3180 | 3396 | /* don't register unknown regions */ |
|---|
| 3181 | 3397 | break; |
|---|
| 3182 | 3398 | } |
|---|
| 3399 | + } |
|---|
| 3183 | 3400 | |
|---|
| 3184 | | - sched_ars(acpi_desc); |
|---|
| 3401 | + if (do_sched_ars) |
|---|
| 3402 | + sched_ars(acpi_desc); |
|---|
| 3185 | 3403 | return 0; |
|---|
| 3186 | 3404 | } |
|---|
| 3187 | 3405 | |
|---|
| .. | .. |
|---|
| 3318 | 3536 | |
|---|
| 3319 | 3537 | static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc) |
|---|
| 3320 | 3538 | { |
|---|
| 3321 | | - struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); |
|---|
| 3539 | + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); |
|---|
| 3322 | 3540 | struct device *dev = acpi_desc->dev; |
|---|
| 3323 | 3541 | |
|---|
| 3324 | 3542 | /* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */ |
|---|
| 3325 | | - device_lock(dev); |
|---|
| 3326 | | - device_unlock(dev); |
|---|
| 3543 | + nfit_device_lock(dev); |
|---|
| 3544 | + nfit_device_unlock(dev); |
|---|
| 3327 | 3545 | |
|---|
| 3328 | 3546 | /* Bounce the init_mutex to complete initial registration */ |
|---|
| 3329 | 3547 | mutex_lock(&acpi_desc->init_mutex); |
|---|
| .. | .. |
|---|
| 3332 | 3550 | return 0; |
|---|
| 3333 | 3551 | } |
|---|
| 3334 | 3552 | |
|---|
| 3335 | | -static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, |
|---|
| 3553 | +static int __acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, |
|---|
| 3336 | 3554 | struct nvdimm *nvdimm, unsigned int cmd) |
|---|
| 3337 | 3555 | { |
|---|
| 3338 | | - struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); |
|---|
| 3556 | + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); |
|---|
| 3339 | 3557 | |
|---|
| 3340 | 3558 | if (nvdimm) |
|---|
| 3341 | 3559 | return 0; |
|---|
| .. | .. |
|---|
| 3345 | 3563 | /* |
|---|
| 3346 | 3564 | * The kernel and userspace may race to initiate a scrub, but |
|---|
| 3347 | 3565 | * the scrub thread is prepared to lose that initial race. It |
|---|
| 3348 | | - * just needs guarantees that any ars it initiates are not |
|---|
| 3349 | | - * interrupted by any intervening start reqeusts from userspace. |
|---|
| 3566 | + * just needs guarantees that any ARS it initiates are not |
|---|
| 3567 | + * interrupted by any intervening start requests from userspace. |
|---|
| 3350 | 3568 | */ |
|---|
| 3351 | 3569 | if (work_busy(&acpi_desc->dwork.work)) |
|---|
| 3352 | 3570 | return -EBUSY; |
|---|
| 3353 | 3571 | |
|---|
| 3354 | 3572 | return 0; |
|---|
| 3573 | +} |
|---|
| 3574 | + |
|---|
| 3575 | +/* |
|---|
| 3576 | + * Prevent security and firmware activate commands from being issued via |
|---|
| 3577 | + * ioctl. |
|---|
| 3578 | + */ |
|---|
| 3579 | +static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, |
|---|
| 3580 | + struct nvdimm *nvdimm, unsigned int cmd, void *buf) |
|---|
| 3581 | +{ |
|---|
| 3582 | + struct nd_cmd_pkg *call_pkg = buf; |
|---|
| 3583 | + unsigned int func; |
|---|
| 3584 | + |
|---|
| 3585 | + if (nvdimm && cmd == ND_CMD_CALL && |
|---|
| 3586 | + call_pkg->nd_family == NVDIMM_FAMILY_INTEL) { |
|---|
| 3587 | + func = call_pkg->nd_command; |
|---|
| 3588 | + if (func > NVDIMM_CMD_MAX || |
|---|
| 3589 | + (1 << func) & NVDIMM_INTEL_DENY_CMDMASK) |
|---|
| 3590 | + return -EOPNOTSUPP; |
|---|
| 3591 | + } |
|---|
| 3592 | + |
|---|
| 3593 | + /* block all non-nfit bus commands */ |
|---|
| 3594 | + if (!nvdimm && cmd == ND_CMD_CALL && |
|---|
| 3595 | + call_pkg->nd_family != NVDIMM_BUS_FAMILY_NFIT) |
|---|
| 3596 | + return -EOPNOTSUPP; |
|---|
| 3597 | + |
|---|
| 3598 | + return __acpi_nfit_clear_to_send(nd_desc, nvdimm, cmd); |
|---|
| 3355 | 3599 | } |
|---|
| 3356 | 3600 | |
|---|
| 3357 | 3601 | int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, |
|---|
| .. | .. |
|---|
| 3450 | 3694 | * acpi_nfit_ars_rescan() submissions have had a chance to |
|---|
| 3451 | 3695 | * either submit or see ->cancel set. |
|---|
| 3452 | 3696 | */ |
|---|
| 3453 | | - device_lock(bus_dev); |
|---|
| 3454 | | - device_unlock(bus_dev); |
|---|
| 3697 | + nfit_device_lock(bus_dev); |
|---|
| 3698 | + nfit_device_unlock(bus_dev); |
|---|
| 3455 | 3699 | |
|---|
| 3456 | 3700 | flush_workqueue(nfit_wq); |
|---|
| 3457 | 3701 | } |
|---|
| .. | .. |
|---|
| 3469 | 3713 | |
|---|
| 3470 | 3714 | status = acpi_get_table(ACPI_SIG_NFIT, 0, &tbl); |
|---|
| 3471 | 3715 | if (ACPI_FAILURE(status)) { |
|---|
| 3472 | | - /* This is ok, we could have an nvdimm hotplugged later */ |
|---|
| 3716 | + /* The NVDIMM root device allows OS to trigger enumeration of |
|---|
| 3717 | + * NVDIMMs through NFIT at boot time and re-enumeration at |
|---|
| 3718 | + * root level via the _FIT method during runtime. |
|---|
| 3719 | + * This is ok to return 0 here, we could have an nvdimm |
|---|
| 3720 | + * hotplugged later and evaluate _FIT method which returns |
|---|
| 3721 | + * data in the format of a series of NFIT Structures. |
|---|
| 3722 | + */ |
|---|
| 3473 | 3723 | dev_dbg(dev, "failed to find NFIT at startup\n"); |
|---|
| 3474 | 3724 | return 0; |
|---|
| 3475 | 3725 | } |
|---|
| .. | .. |
|---|
| 3588 | 3838 | |
|---|
| 3589 | 3839 | static void acpi_nfit_notify(struct acpi_device *adev, u32 event) |
|---|
| 3590 | 3840 | { |
|---|
| 3591 | | - device_lock(&adev->dev); |
|---|
| 3841 | + nfit_device_lock(&adev->dev); |
|---|
| 3592 | 3842 | __acpi_nfit_notify(&adev->dev, adev->handle, event); |
|---|
| 3593 | | - device_unlock(&adev->dev); |
|---|
| 3843 | + nfit_device_unlock(&adev->dev); |
|---|
| 3594 | 3844 | } |
|---|
| 3595 | 3845 | |
|---|
| 3596 | 3846 | static const struct acpi_device_id acpi_nfit_ids[] = { |
|---|
| .. | .. |
|---|
| 3636 | 3886 | guid_parse(UUID_NFIT_DIMM_N_HPE2, &nfit_uuid[NFIT_DEV_DIMM_N_HPE2]); |
|---|
| 3637 | 3887 | guid_parse(UUID_NFIT_DIMM_N_MSFT, &nfit_uuid[NFIT_DEV_DIMM_N_MSFT]); |
|---|
| 3638 | 3888 | guid_parse(UUID_NFIT_DIMM_N_HYPERV, &nfit_uuid[NFIT_DEV_DIMM_N_HYPERV]); |
|---|
| 3889 | + guid_parse(UUID_INTEL_BUS, &nfit_uuid[NFIT_BUS_INTEL]); |
|---|
| 3639 | 3890 | |
|---|
| 3640 | 3891 | nfit_wq = create_singlethread_workqueue("nfit"); |
|---|
| 3641 | 3892 | if (!nfit_wq) |
|---|