162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 662306a36Sopenharmony_ci#include <linux/platform_device.h> 762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 862306a36Sopenharmony_ci#include <linux/workqueue.h> 962306a36Sopenharmony_ci#include <linux/libnvdimm.h> 1062306a36Sopenharmony_ci#include <linux/genalloc.h> 1162306a36Sopenharmony_ci#include <linux/vmalloc.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/mutex.h> 1562306a36Sopenharmony_ci#include <linux/ndctl.h> 1662306a36Sopenharmony_ci#include <linux/sizes.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <nd-core.h> 2062306a36Sopenharmony_ci#include <intel.h> 2162306a36Sopenharmony_ci#include <nfit.h> 2262306a36Sopenharmony_ci#include <nd.h> 2362306a36Sopenharmony_ci#include "nfit_test.h" 2462306a36Sopenharmony_ci#include "../watermark.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * Generate an NFIT table to describe the following topology: 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * BUS0: Interleaved PMEM regions, and aliasing with BLK regions 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * (a) (b) DIMM BLK-REGION 3262306a36Sopenharmony_ci * +----------+--------------+----------+---------+ 3362306a36Sopenharmony_ci * +------+ | blk2.0 | pm0.0 | blk2.1 | pm1.0 | 0 region2 3462306a36Sopenharmony_ci * | imc0 +--+- - - - - region0 - - - -+----------+ + 3562306a36Sopenharmony_ci * +--+---+ | blk3.0 | pm0.0 | blk3.1 | pm1.0 | 1 region3 3662306a36Sopenharmony_ci * | +----------+--------------v----------v v 3762306a36Sopenharmony_ci * +--+---+ | | 3862306a36Sopenharmony_ci * | cpu0 | region1 3962306a36Sopenharmony_ci * +--+---+ | | 4062306a36Sopenharmony_ci * | +-------------------------^----------^ ^ 4162306a36Sopenharmony_ci * +--+---+ | blk4.0 | pm1.0 | 2 region4 4262306a36Sopenharmony_ci * | imc1 +--+-------------------------+----------+ + 4362306a36Sopenharmony_ci * +------+ | blk5.0 | pm1.0 | 3 region5 4462306a36Sopenharmony_ci * +-------------------------+----------+-+-------+ 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * +--+---+ 4762306a36Sopenharmony_ci * | cpu1 | 4862306a36Sopenharmony_ci * +--+---+ (Hotplug DIMM) 4962306a36Sopenharmony_ci * | +----------------------------------------------+ 5062306a36Sopenharmony_ci * +--+---+ | blk6.0/pm7.0 | 4 region6/7 5162306a36Sopenharmony_ci * | imc0 +--+----------------------------------------------+ 5262306a36Sopenharmony_ci * +------+ 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * *) In this layout we have four dimms and two memory controllers in one 5662306a36Sopenharmony_ci * socket. Each unique interface (BLK or PMEM) to DPA space 5762306a36Sopenharmony_ci * is identified by a region device with a dynamically assigned id. 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * *) The first portion of dimm0 and dimm1 are interleaved as REGION0. 6062306a36Sopenharmony_ci * A single PMEM namespace "pm0.0" is created using half of the 6162306a36Sopenharmony_ci * REGION0 SPA-range. REGION0 spans dimm0 and dimm1. PMEM namespace 6262306a36Sopenharmony_ci * allocate from from the bottom of a region. The unallocated 6362306a36Sopenharmony_ci * portion of REGION0 aliases with REGION2 and REGION3. That 6462306a36Sopenharmony_ci * unallacted capacity is reclaimed as BLK namespaces ("blk2.0" and 6562306a36Sopenharmony_ci * "blk3.0") starting at the base of each DIMM to offset (a) in those 6662306a36Sopenharmony_ci * DIMMs. "pm0.0", "blk2.0" and "blk3.0" are free-form readable 6762306a36Sopenharmony_ci * names that can be assigned to a namespace. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * *) In the last portion of dimm0 and dimm1 we have an interleaved 7062306a36Sopenharmony_ci * SPA range, REGION1, that spans those two dimms as well as dimm2 7162306a36Sopenharmony_ci * and dimm3. Some of REGION1 allocated to a PMEM namespace named 7262306a36Sopenharmony_ci * "pm1.0" the rest is reclaimed in 4 BLK namespaces (for each 7362306a36Sopenharmony_ci * dimm in the interleave set), "blk2.1", "blk3.1", "blk4.0", and 7462306a36Sopenharmony_ci * "blk5.0". 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * *) The portion of dimm2 and dimm3 that do not participate in the 7762306a36Sopenharmony_ci * REGION1 interleaved SPA range (i.e. the DPA address below offset 7862306a36Sopenharmony_ci * (b) are also included in the "blk4.0" and "blk5.0" namespaces. 7962306a36Sopenharmony_ci * Note, that BLK namespaces need not be contiguous in DPA-space, and 8062306a36Sopenharmony_ci * can consume aliased capacity from multiple interleave sets. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * BUS1: Legacy NVDIMM (single contiguous range) 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * region2 8562306a36Sopenharmony_ci * +---------------------+ 8662306a36Sopenharmony_ci * |---------------------| 8762306a36Sopenharmony_ci * || pm2.0 || 8862306a36Sopenharmony_ci * |---------------------| 8962306a36Sopenharmony_ci * +---------------------+ 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * *) A NFIT-table may describe a simple system-physical-address range 9262306a36Sopenharmony_ci * with no BLK aliasing. This type of region may optionally 9362306a36Sopenharmony_ci * reference an NVDIMM. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cienum { 9662306a36Sopenharmony_ci NUM_PM = 3, 9762306a36Sopenharmony_ci NUM_DCR = 5, 9862306a36Sopenharmony_ci NUM_HINTS = 8, 9962306a36Sopenharmony_ci NUM_BDW = NUM_DCR, 10062306a36Sopenharmony_ci NUM_SPA = NUM_PM + NUM_DCR + NUM_BDW, 10162306a36Sopenharmony_ci NUM_MEM = NUM_DCR + NUM_BDW + 2 /* spa0 iset */ 10262306a36Sopenharmony_ci + 4 /* spa1 iset */ + 1 /* spa11 iset */, 10362306a36Sopenharmony_ci DIMM_SIZE = SZ_32M, 10462306a36Sopenharmony_ci LABEL_SIZE = SZ_128K, 10562306a36Sopenharmony_ci SPA_VCD_SIZE = SZ_4M, 10662306a36Sopenharmony_ci SPA0_SIZE = DIMM_SIZE, 10762306a36Sopenharmony_ci SPA1_SIZE = DIMM_SIZE*2, 10862306a36Sopenharmony_ci SPA2_SIZE = DIMM_SIZE, 10962306a36Sopenharmony_ci BDW_SIZE = 64 << 8, 11062306a36Sopenharmony_ci DCR_SIZE = 12, 11162306a36Sopenharmony_ci NUM_NFITS = 2, /* permit testing multiple NFITs per system */ 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistruct nfit_test_dcr { 11562306a36Sopenharmony_ci __le64 bdw_addr; 11662306a36Sopenharmony_ci __le32 bdw_status; 11762306a36Sopenharmony_ci __u8 aperature[BDW_SIZE]; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define NFIT_DIMM_HANDLE(node, socket, imc, chan, dimm) \ 12162306a36Sopenharmony_ci (((node & 0xfff) << 16) | ((socket & 0xf) << 12) \ 12262306a36Sopenharmony_ci | ((imc & 0xf) << 8) | ((chan & 0xf) << 4) | (dimm & 0xf)) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic u32 handle[] = { 12562306a36Sopenharmony_ci [0] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 0), 12662306a36Sopenharmony_ci [1] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 1), 12762306a36Sopenharmony_ci [2] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 0), 12862306a36Sopenharmony_ci [3] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 1), 12962306a36Sopenharmony_ci [4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0), 13062306a36Sopenharmony_ci [5] = NFIT_DIMM_HANDLE(1, 0, 0, 0, 0), 13162306a36Sopenharmony_ci [6] = NFIT_DIMM_HANDLE(1, 0, 0, 0, 1), 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic unsigned long dimm_fail_cmd_flags[ARRAY_SIZE(handle)]; 13562306a36Sopenharmony_cistatic int dimm_fail_cmd_code[ARRAY_SIZE(handle)]; 13662306a36Sopenharmony_cistruct nfit_test_sec { 13762306a36Sopenharmony_ci u8 state; 13862306a36Sopenharmony_ci u8 ext_state; 13962306a36Sopenharmony_ci u8 old_state; 14062306a36Sopenharmony_ci u8 passphrase[32]; 14162306a36Sopenharmony_ci u8 master_passphrase[32]; 14262306a36Sopenharmony_ci u64 overwrite_end_time; 14362306a36Sopenharmony_ci} dimm_sec_info[NUM_DCR]; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic const struct nd_intel_smart smart_def = { 14662306a36Sopenharmony_ci .flags = ND_INTEL_SMART_HEALTH_VALID 14762306a36Sopenharmony_ci | ND_INTEL_SMART_SPARES_VALID 14862306a36Sopenharmony_ci | ND_INTEL_SMART_ALARM_VALID 14962306a36Sopenharmony_ci | ND_INTEL_SMART_USED_VALID 15062306a36Sopenharmony_ci | ND_INTEL_SMART_SHUTDOWN_VALID 15162306a36Sopenharmony_ci | ND_INTEL_SMART_SHUTDOWN_COUNT_VALID 15262306a36Sopenharmony_ci | ND_INTEL_SMART_MTEMP_VALID 15362306a36Sopenharmony_ci | ND_INTEL_SMART_CTEMP_VALID, 15462306a36Sopenharmony_ci .health = ND_INTEL_SMART_NON_CRITICAL_HEALTH, 15562306a36Sopenharmony_ci .media_temperature = 23 * 16, 15662306a36Sopenharmony_ci .ctrl_temperature = 25 * 16, 15762306a36Sopenharmony_ci .pmic_temperature = 40 * 16, 15862306a36Sopenharmony_ci .spares = 75, 15962306a36Sopenharmony_ci .alarm_flags = ND_INTEL_SMART_SPARE_TRIP 16062306a36Sopenharmony_ci | ND_INTEL_SMART_TEMP_TRIP, 16162306a36Sopenharmony_ci .ait_status = 1, 16262306a36Sopenharmony_ci .life_used = 5, 16362306a36Sopenharmony_ci .shutdown_state = 0, 16462306a36Sopenharmony_ci .shutdown_count = 42, 16562306a36Sopenharmony_ci .vendor_size = 0, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistruct nfit_test_fw { 16962306a36Sopenharmony_ci enum intel_fw_update_state state; 17062306a36Sopenharmony_ci u32 context; 17162306a36Sopenharmony_ci u64 version; 17262306a36Sopenharmony_ci u32 size_received; 17362306a36Sopenharmony_ci u64 end_time; 17462306a36Sopenharmony_ci bool armed; 17562306a36Sopenharmony_ci bool missed_activate; 17662306a36Sopenharmony_ci unsigned long last_activate; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistruct nfit_test { 18062306a36Sopenharmony_ci struct acpi_nfit_desc acpi_desc; 18162306a36Sopenharmony_ci struct platform_device pdev; 18262306a36Sopenharmony_ci struct list_head resources; 18362306a36Sopenharmony_ci void *nfit_buf; 18462306a36Sopenharmony_ci dma_addr_t nfit_dma; 18562306a36Sopenharmony_ci size_t nfit_size; 18662306a36Sopenharmony_ci size_t nfit_filled; 18762306a36Sopenharmony_ci int dcr_idx; 18862306a36Sopenharmony_ci int num_dcr; 18962306a36Sopenharmony_ci int num_pm; 19062306a36Sopenharmony_ci void **dimm; 19162306a36Sopenharmony_ci dma_addr_t *dimm_dma; 19262306a36Sopenharmony_ci void **flush; 19362306a36Sopenharmony_ci dma_addr_t *flush_dma; 19462306a36Sopenharmony_ci void **label; 19562306a36Sopenharmony_ci dma_addr_t *label_dma; 19662306a36Sopenharmony_ci void **spa_set; 19762306a36Sopenharmony_ci dma_addr_t *spa_set_dma; 19862306a36Sopenharmony_ci struct nfit_test_dcr **dcr; 19962306a36Sopenharmony_ci dma_addr_t *dcr_dma; 20062306a36Sopenharmony_ci int (*alloc)(struct nfit_test *t); 20162306a36Sopenharmony_ci void (*setup)(struct nfit_test *t); 20262306a36Sopenharmony_ci int setup_hotplug; 20362306a36Sopenharmony_ci union acpi_object **_fit; 20462306a36Sopenharmony_ci dma_addr_t _fit_dma; 20562306a36Sopenharmony_ci struct ars_state { 20662306a36Sopenharmony_ci struct nd_cmd_ars_status *ars_status; 20762306a36Sopenharmony_ci unsigned long deadline; 20862306a36Sopenharmony_ci spinlock_t lock; 20962306a36Sopenharmony_ci } ars_state; 21062306a36Sopenharmony_ci struct device *dimm_dev[ARRAY_SIZE(handle)]; 21162306a36Sopenharmony_ci struct nd_intel_smart *smart; 21262306a36Sopenharmony_ci struct nd_intel_smart_threshold *smart_threshold; 21362306a36Sopenharmony_ci struct badrange badrange; 21462306a36Sopenharmony_ci struct work_struct work; 21562306a36Sopenharmony_ci struct nfit_test_fw *fw; 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic struct workqueue_struct *nfit_wq; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic struct gen_pool *nfit_pool; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const char zero_key[NVDIMM_PASSPHRASE_LEN]; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic struct nfit_test *to_nfit_test(struct device *dev) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return container_of(pdev, struct nfit_test, pdev); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int nd_intel_test_get_fw_info(struct nfit_test *t, 23262306a36Sopenharmony_ci struct nd_intel_fw_info *nd_cmd, unsigned int buf_len, 23362306a36Sopenharmony_ci int idx) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 23662306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[idx]; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p, buf_len: %u, idx: %d\n", 23962306a36Sopenharmony_ci __func__, t, nd_cmd, buf_len, idx); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci nd_cmd->status = 0; 24562306a36Sopenharmony_ci nd_cmd->storage_size = INTEL_FW_STORAGE_SIZE; 24662306a36Sopenharmony_ci nd_cmd->max_send_len = INTEL_FW_MAX_SEND_LEN; 24762306a36Sopenharmony_ci nd_cmd->query_interval = INTEL_FW_QUERY_INTERVAL; 24862306a36Sopenharmony_ci nd_cmd->max_query_time = INTEL_FW_QUERY_MAX_TIME; 24962306a36Sopenharmony_ci nd_cmd->update_cap = 0; 25062306a36Sopenharmony_ci nd_cmd->fis_version = INTEL_FW_FIS_VERSION; 25162306a36Sopenharmony_ci nd_cmd->run_version = 0; 25262306a36Sopenharmony_ci nd_cmd->updated_version = fw->version; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int nd_intel_test_start_update(struct nfit_test *t, 25862306a36Sopenharmony_ci struct nd_intel_fw_start *nd_cmd, unsigned int buf_len, 25962306a36Sopenharmony_ci int idx) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 26262306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[idx]; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n", 26562306a36Sopenharmony_ci __func__, t, nd_cmd, buf_len, idx); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 26862306a36Sopenharmony_ci return -EINVAL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (fw->state != FW_STATE_NEW) { 27162306a36Sopenharmony_ci /* extended status, FW update in progress */ 27262306a36Sopenharmony_ci nd_cmd->status = 0x10007; 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci fw->state = FW_STATE_IN_PROGRESS; 27762306a36Sopenharmony_ci fw->context++; 27862306a36Sopenharmony_ci fw->size_received = 0; 27962306a36Sopenharmony_ci nd_cmd->status = 0; 28062306a36Sopenharmony_ci nd_cmd->context = fw->context; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci dev_dbg(dev, "%s: context issued: %#x\n", __func__, nd_cmd->context); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int nd_intel_test_send_data(struct nfit_test *t, 28862306a36Sopenharmony_ci struct nd_intel_fw_send_data *nd_cmd, unsigned int buf_len, 28962306a36Sopenharmony_ci int idx) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 29262306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[idx]; 29362306a36Sopenharmony_ci u32 *status = (u32 *)&nd_cmd->data[nd_cmd->length]; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n", 29662306a36Sopenharmony_ci __func__, t, nd_cmd, buf_len, idx); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 29962306a36Sopenharmony_ci return -EINVAL; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dev_dbg(dev, "%s: cmd->status: %#x\n", __func__, *status); 30362306a36Sopenharmony_ci dev_dbg(dev, "%s: cmd->data[0]: %#x\n", __func__, nd_cmd->data[0]); 30462306a36Sopenharmony_ci dev_dbg(dev, "%s: cmd->data[%u]: %#x\n", __func__, nd_cmd->length-1, 30562306a36Sopenharmony_ci nd_cmd->data[nd_cmd->length-1]); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (fw->state != FW_STATE_IN_PROGRESS) { 30862306a36Sopenharmony_ci dev_dbg(dev, "%s: not in IN_PROGRESS state\n", __func__); 30962306a36Sopenharmony_ci *status = 0x5; 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (nd_cmd->context != fw->context) { 31462306a36Sopenharmony_ci dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n", 31562306a36Sopenharmony_ci __func__, nd_cmd->context, fw->context); 31662306a36Sopenharmony_ci *status = 0x10007; 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* 32162306a36Sopenharmony_ci * check offset + len > size of fw storage 32262306a36Sopenharmony_ci * check length is > max send length 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci if (nd_cmd->offset + nd_cmd->length > INTEL_FW_STORAGE_SIZE || 32562306a36Sopenharmony_ci nd_cmd->length > INTEL_FW_MAX_SEND_LEN) { 32662306a36Sopenharmony_ci *status = 0x3; 32762306a36Sopenharmony_ci dev_dbg(dev, "%s: buffer boundary violation\n", __func__); 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci fw->size_received += nd_cmd->length; 33262306a36Sopenharmony_ci dev_dbg(dev, "%s: copying %u bytes, %u bytes so far\n", 33362306a36Sopenharmony_ci __func__, nd_cmd->length, fw->size_received); 33462306a36Sopenharmony_ci *status = 0; 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int nd_intel_test_finish_fw(struct nfit_test *t, 33962306a36Sopenharmony_ci struct nd_intel_fw_finish_update *nd_cmd, 34062306a36Sopenharmony_ci unsigned int buf_len, int idx) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 34362306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[idx]; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n", 34662306a36Sopenharmony_ci __func__, t, nd_cmd, buf_len, idx); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (fw->state == FW_STATE_UPDATED) { 34962306a36Sopenharmony_ci /* update already done, need activation */ 35062306a36Sopenharmony_ci nd_cmd->status = 0x20007; 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci dev_dbg(dev, "%s: context: %#x ctrl_flags: %#x\n", 35562306a36Sopenharmony_ci __func__, nd_cmd->context, nd_cmd->ctrl_flags); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci switch (nd_cmd->ctrl_flags) { 35862306a36Sopenharmony_ci case 0: /* finish */ 35962306a36Sopenharmony_ci if (nd_cmd->context != fw->context) { 36062306a36Sopenharmony_ci dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n", 36162306a36Sopenharmony_ci __func__, nd_cmd->context, 36262306a36Sopenharmony_ci fw->context); 36362306a36Sopenharmony_ci nd_cmd->status = 0x10007; 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci nd_cmd->status = 0; 36762306a36Sopenharmony_ci fw->state = FW_STATE_VERIFY; 36862306a36Sopenharmony_ci /* set 1 second of time for firmware "update" */ 36962306a36Sopenharmony_ci fw->end_time = jiffies + HZ; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci case 1: /* abort */ 37362306a36Sopenharmony_ci fw->size_received = 0; 37462306a36Sopenharmony_ci /* successfully aborted status */ 37562306a36Sopenharmony_ci nd_cmd->status = 0x40007; 37662306a36Sopenharmony_ci fw->state = FW_STATE_NEW; 37762306a36Sopenharmony_ci dev_dbg(dev, "%s: abort successful\n", __func__); 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci default: /* bad control flag */ 38162306a36Sopenharmony_ci dev_warn(dev, "%s: unknown control flag: %#x\n", 38262306a36Sopenharmony_ci __func__, nd_cmd->ctrl_flags); 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic int nd_intel_test_finish_query(struct nfit_test *t, 39062306a36Sopenharmony_ci struct nd_intel_fw_finish_query *nd_cmd, 39162306a36Sopenharmony_ci unsigned int buf_len, int idx) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 39462306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[idx]; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n", 39762306a36Sopenharmony_ci __func__, t, nd_cmd, buf_len, idx); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 40062306a36Sopenharmony_ci return -EINVAL; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (nd_cmd->context != fw->context) { 40362306a36Sopenharmony_ci dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n", 40462306a36Sopenharmony_ci __func__, nd_cmd->context, fw->context); 40562306a36Sopenharmony_ci nd_cmd->status = 0x10007; 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci dev_dbg(dev, "%s context: %#x\n", __func__, nd_cmd->context); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci switch (fw->state) { 41262306a36Sopenharmony_ci case FW_STATE_NEW: 41362306a36Sopenharmony_ci nd_cmd->updated_fw_rev = 0; 41462306a36Sopenharmony_ci nd_cmd->status = 0; 41562306a36Sopenharmony_ci dev_dbg(dev, "%s: new state\n", __func__); 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci case FW_STATE_IN_PROGRESS: 41962306a36Sopenharmony_ci /* sequencing error */ 42062306a36Sopenharmony_ci nd_cmd->status = 0x40007; 42162306a36Sopenharmony_ci nd_cmd->updated_fw_rev = 0; 42262306a36Sopenharmony_ci dev_dbg(dev, "%s: sequence error\n", __func__); 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci case FW_STATE_VERIFY: 42662306a36Sopenharmony_ci if (time_is_after_jiffies64(fw->end_time)) { 42762306a36Sopenharmony_ci nd_cmd->updated_fw_rev = 0; 42862306a36Sopenharmony_ci nd_cmd->status = 0x20007; 42962306a36Sopenharmony_ci dev_dbg(dev, "%s: still verifying\n", __func__); 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci dev_dbg(dev, "%s: transition out verify\n", __func__); 43362306a36Sopenharmony_ci fw->state = FW_STATE_UPDATED; 43462306a36Sopenharmony_ci fw->missed_activate = false; 43562306a36Sopenharmony_ci fallthrough; 43662306a36Sopenharmony_ci case FW_STATE_UPDATED: 43762306a36Sopenharmony_ci nd_cmd->status = 0; 43862306a36Sopenharmony_ci /* bogus test version */ 43962306a36Sopenharmony_ci fw->version = nd_cmd->updated_fw_rev = 44062306a36Sopenharmony_ci INTEL_FW_FAKE_VERSION; 44162306a36Sopenharmony_ci dev_dbg(dev, "%s: updated\n", __func__); 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci default: /* we should never get here */ 44562306a36Sopenharmony_ci return -EINVAL; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int nfit_test_cmd_get_config_size(struct nd_cmd_get_config_size *nd_cmd, 45262306a36Sopenharmony_ci unsigned int buf_len) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 45562306a36Sopenharmony_ci return -EINVAL; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci nd_cmd->status = 0; 45862306a36Sopenharmony_ci nd_cmd->config_size = LABEL_SIZE; 45962306a36Sopenharmony_ci nd_cmd->max_xfer = SZ_4K; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int nfit_test_cmd_get_config_data(struct nd_cmd_get_config_data_hdr 46562306a36Sopenharmony_ci *nd_cmd, unsigned int buf_len, void *label) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci unsigned int len, offset = nd_cmd->in_offset; 46862306a36Sopenharmony_ci int rc; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci if (offset >= LABEL_SIZE) 47362306a36Sopenharmony_ci return -EINVAL; 47462306a36Sopenharmony_ci if (nd_cmd->in_length + sizeof(*nd_cmd) > buf_len) 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci nd_cmd->status = 0; 47862306a36Sopenharmony_ci len = min(nd_cmd->in_length, LABEL_SIZE - offset); 47962306a36Sopenharmony_ci memcpy(nd_cmd->out_buf, label + offset, len); 48062306a36Sopenharmony_ci rc = buf_len - sizeof(*nd_cmd) - len; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return rc; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd, 48662306a36Sopenharmony_ci unsigned int buf_len, void *label) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci unsigned int len, offset = nd_cmd->in_offset; 48962306a36Sopenharmony_ci u32 *status; 49062306a36Sopenharmony_ci int rc; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 49362306a36Sopenharmony_ci return -EINVAL; 49462306a36Sopenharmony_ci if (offset >= LABEL_SIZE) 49562306a36Sopenharmony_ci return -EINVAL; 49662306a36Sopenharmony_ci if (nd_cmd->in_length + sizeof(*nd_cmd) + 4 > buf_len) 49762306a36Sopenharmony_ci return -EINVAL; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci status = (void *)nd_cmd + nd_cmd->in_length + sizeof(*nd_cmd); 50062306a36Sopenharmony_ci *status = 0; 50162306a36Sopenharmony_ci len = min(nd_cmd->in_length, LABEL_SIZE - offset); 50262306a36Sopenharmony_ci memcpy(label + offset, nd_cmd->in_buf, len); 50362306a36Sopenharmony_ci rc = buf_len - sizeof(*nd_cmd) - (len + 4); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return rc; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci#define NFIT_TEST_CLEAR_ERR_UNIT 256 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, 51162306a36Sopenharmony_ci unsigned int buf_len) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci int ars_recs; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 51662306a36Sopenharmony_ci return -EINVAL; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* for testing, only store up to n records that fit within 4k */ 51962306a36Sopenharmony_ci ars_recs = SZ_4K / sizeof(struct nd_ars_record); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status) 52262306a36Sopenharmony_ci + ars_recs * sizeof(struct nd_ars_record); 52362306a36Sopenharmony_ci nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; 52462306a36Sopenharmony_ci nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic void post_ars_status(struct ars_state *ars_state, 53062306a36Sopenharmony_ci struct badrange *badrange, u64 addr, u64 len) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct nd_cmd_ars_status *ars_status; 53362306a36Sopenharmony_ci struct nd_ars_record *ars_record; 53462306a36Sopenharmony_ci struct badrange_entry *be; 53562306a36Sopenharmony_ci u64 end = addr + len - 1; 53662306a36Sopenharmony_ci int i = 0; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ars_state->deadline = jiffies + 1*HZ; 53962306a36Sopenharmony_ci ars_status = ars_state->ars_status; 54062306a36Sopenharmony_ci ars_status->status = 0; 54162306a36Sopenharmony_ci ars_status->address = addr; 54262306a36Sopenharmony_ci ars_status->length = len; 54362306a36Sopenharmony_ci ars_status->type = ND_ARS_PERSISTENT; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci spin_lock(&badrange->lock); 54662306a36Sopenharmony_ci list_for_each_entry(be, &badrange->list, list) { 54762306a36Sopenharmony_ci u64 be_end = be->start + be->length - 1; 54862306a36Sopenharmony_ci u64 rstart, rend; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* skip entries outside the range */ 55162306a36Sopenharmony_ci if (be_end < addr || be->start > end) 55262306a36Sopenharmony_ci continue; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci rstart = (be->start < addr) ? addr : be->start; 55562306a36Sopenharmony_ci rend = (be_end < end) ? be_end : end; 55662306a36Sopenharmony_ci ars_record = &ars_status->records[i]; 55762306a36Sopenharmony_ci ars_record->handle = 0; 55862306a36Sopenharmony_ci ars_record->err_address = rstart; 55962306a36Sopenharmony_ci ars_record->length = rend - rstart + 1; 56062306a36Sopenharmony_ci i++; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci spin_unlock(&badrange->lock); 56362306a36Sopenharmony_ci ars_status->num_records = i; 56462306a36Sopenharmony_ci ars_status->out_length = sizeof(struct nd_cmd_ars_status) 56562306a36Sopenharmony_ci + i * sizeof(struct nd_ars_record); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int nfit_test_cmd_ars_start(struct nfit_test *t, 56962306a36Sopenharmony_ci struct ars_state *ars_state, 57062306a36Sopenharmony_ci struct nd_cmd_ars_start *ars_start, unsigned int buf_len, 57162306a36Sopenharmony_ci int *cmd_rc) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci if (buf_len < sizeof(*ars_start)) 57462306a36Sopenharmony_ci return -EINVAL; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci spin_lock(&ars_state->lock); 57762306a36Sopenharmony_ci if (time_before(jiffies, ars_state->deadline)) { 57862306a36Sopenharmony_ci ars_start->status = NFIT_ARS_START_BUSY; 57962306a36Sopenharmony_ci *cmd_rc = -EBUSY; 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci ars_start->status = 0; 58262306a36Sopenharmony_ci ars_start->scrub_time = 1; 58362306a36Sopenharmony_ci post_ars_status(ars_state, &t->badrange, ars_start->address, 58462306a36Sopenharmony_ci ars_start->length); 58562306a36Sopenharmony_ci *cmd_rc = 0; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci spin_unlock(&ars_state->lock); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int nfit_test_cmd_ars_status(struct ars_state *ars_state, 59362306a36Sopenharmony_ci struct nd_cmd_ars_status *ars_status, unsigned int buf_len, 59462306a36Sopenharmony_ci int *cmd_rc) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci if (buf_len < ars_state->ars_status->out_length) 59762306a36Sopenharmony_ci return -EINVAL; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci spin_lock(&ars_state->lock); 60062306a36Sopenharmony_ci if (time_before(jiffies, ars_state->deadline)) { 60162306a36Sopenharmony_ci memset(ars_status, 0, buf_len); 60262306a36Sopenharmony_ci ars_status->status = NFIT_ARS_STATUS_BUSY; 60362306a36Sopenharmony_ci ars_status->out_length = sizeof(*ars_status); 60462306a36Sopenharmony_ci *cmd_rc = -EBUSY; 60562306a36Sopenharmony_ci } else { 60662306a36Sopenharmony_ci memcpy(ars_status, ars_state->ars_status, 60762306a36Sopenharmony_ci ars_state->ars_status->out_length); 60862306a36Sopenharmony_ci *cmd_rc = 0; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci spin_unlock(&ars_state->lock); 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int nfit_test_cmd_clear_error(struct nfit_test *t, 61562306a36Sopenharmony_ci struct nd_cmd_clear_error *clear_err, 61662306a36Sopenharmony_ci unsigned int buf_len, int *cmd_rc) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1; 61962306a36Sopenharmony_ci if (buf_len < sizeof(*clear_err)) 62062306a36Sopenharmony_ci return -EINVAL; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if ((clear_err->address & mask) || (clear_err->length & mask)) 62362306a36Sopenharmony_ci return -EINVAL; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci badrange_forget(&t->badrange, clear_err->address, clear_err->length); 62662306a36Sopenharmony_ci clear_err->status = 0; 62762306a36Sopenharmony_ci clear_err->cleared = clear_err->length; 62862306a36Sopenharmony_ci *cmd_rc = 0; 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistruct region_search_spa { 63362306a36Sopenharmony_ci u64 addr; 63462306a36Sopenharmony_ci struct nd_region *region; 63562306a36Sopenharmony_ci}; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic int is_region_device(struct device *dev) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci return !strncmp(dev->kobj.name, "region", 6); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int nfit_test_search_region_spa(struct device *dev, void *data) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct region_search_spa *ctx = data; 64562306a36Sopenharmony_ci struct nd_region *nd_region; 64662306a36Sopenharmony_ci resource_size_t ndr_end; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (!is_region_device(dev)) 64962306a36Sopenharmony_ci return 0; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci nd_region = to_nd_region(dev); 65262306a36Sopenharmony_ci ndr_end = nd_region->ndr_start + nd_region->ndr_size; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (ctx->addr >= nd_region->ndr_start && ctx->addr < ndr_end) { 65562306a36Sopenharmony_ci ctx->region = nd_region; 65662306a36Sopenharmony_ci return 1; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return 0; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int nfit_test_search_spa(struct nvdimm_bus *bus, 66362306a36Sopenharmony_ci struct nd_cmd_translate_spa *spa) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci int ret; 66662306a36Sopenharmony_ci struct nd_region *nd_region = NULL; 66762306a36Sopenharmony_ci struct nvdimm *nvdimm = NULL; 66862306a36Sopenharmony_ci struct nd_mapping *nd_mapping = NULL; 66962306a36Sopenharmony_ci struct region_search_spa ctx = { 67062306a36Sopenharmony_ci .addr = spa->spa, 67162306a36Sopenharmony_ci .region = NULL, 67262306a36Sopenharmony_ci }; 67362306a36Sopenharmony_ci u64 dpa; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ret = device_for_each_child(&bus->dev, &ctx, 67662306a36Sopenharmony_ci nfit_test_search_region_spa); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (!ret) 67962306a36Sopenharmony_ci return -ENODEV; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci nd_region = ctx.region; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci dpa = ctx.addr - nd_region->ndr_start; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* 68662306a36Sopenharmony_ci * last dimm is selected for test 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci nd_mapping = &nd_region->mapping[nd_region->ndr_mappings - 1]; 68962306a36Sopenharmony_ci nvdimm = nd_mapping->nvdimm; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci spa->devices[0].nfit_device_handle = handle[nvdimm->id]; 69262306a36Sopenharmony_ci spa->num_nvdimms = 1; 69362306a36Sopenharmony_ci spa->devices[0].dpa = dpa; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int nfit_test_cmd_translate_spa(struct nvdimm_bus *bus, 69962306a36Sopenharmony_ci struct nd_cmd_translate_spa *spa, unsigned int buf_len) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci if (buf_len < spa->translate_length) 70262306a36Sopenharmony_ci return -EINVAL; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (nfit_test_search_spa(bus, spa) < 0 || !spa->num_nvdimms) 70562306a36Sopenharmony_ci spa->status = 2; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int nfit_test_cmd_smart(struct nd_intel_smart *smart, unsigned int buf_len, 71162306a36Sopenharmony_ci struct nd_intel_smart *smart_data) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci if (buf_len < sizeof(*smart)) 71462306a36Sopenharmony_ci return -EINVAL; 71562306a36Sopenharmony_ci memcpy(smart, smart_data, sizeof(*smart)); 71662306a36Sopenharmony_ci return 0; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int nfit_test_cmd_smart_threshold( 72062306a36Sopenharmony_ci struct nd_intel_smart_threshold *out, 72162306a36Sopenharmony_ci unsigned int buf_len, 72262306a36Sopenharmony_ci struct nd_intel_smart_threshold *smart_t) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci if (buf_len < sizeof(*smart_t)) 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci memcpy(out, smart_t, sizeof(*smart_t)); 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic void smart_notify(struct device *bus_dev, 73162306a36Sopenharmony_ci struct device *dimm_dev, struct nd_intel_smart *smart, 73262306a36Sopenharmony_ci struct nd_intel_smart_threshold *thresh) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci dev_dbg(dimm_dev, "%s: alarm: %#x spares: %d (%d) mtemp: %d (%d) ctemp: %d (%d)\n", 73562306a36Sopenharmony_ci __func__, thresh->alarm_control, thresh->spares, 73662306a36Sopenharmony_ci smart->spares, thresh->media_temperature, 73762306a36Sopenharmony_ci smart->media_temperature, thresh->ctrl_temperature, 73862306a36Sopenharmony_ci smart->ctrl_temperature); 73962306a36Sopenharmony_ci if (((thresh->alarm_control & ND_INTEL_SMART_SPARE_TRIP) 74062306a36Sopenharmony_ci && smart->spares 74162306a36Sopenharmony_ci <= thresh->spares) 74262306a36Sopenharmony_ci || ((thresh->alarm_control & ND_INTEL_SMART_TEMP_TRIP) 74362306a36Sopenharmony_ci && smart->media_temperature 74462306a36Sopenharmony_ci >= thresh->media_temperature) 74562306a36Sopenharmony_ci || ((thresh->alarm_control & ND_INTEL_SMART_CTEMP_TRIP) 74662306a36Sopenharmony_ci && smart->ctrl_temperature 74762306a36Sopenharmony_ci >= thresh->ctrl_temperature) 74862306a36Sopenharmony_ci || (smart->health != ND_INTEL_SMART_NON_CRITICAL_HEALTH) 74962306a36Sopenharmony_ci || (smart->shutdown_state != 0)) { 75062306a36Sopenharmony_ci device_lock(bus_dev); 75162306a36Sopenharmony_ci __acpi_nvdimm_notify(dimm_dev, 0x81); 75262306a36Sopenharmony_ci device_unlock(bus_dev); 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int nfit_test_cmd_smart_set_threshold( 75762306a36Sopenharmony_ci struct nd_intel_smart_set_threshold *in, 75862306a36Sopenharmony_ci unsigned int buf_len, 75962306a36Sopenharmony_ci struct nd_intel_smart_threshold *thresh, 76062306a36Sopenharmony_ci struct nd_intel_smart *smart, 76162306a36Sopenharmony_ci struct device *bus_dev, struct device *dimm_dev) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci unsigned int size; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci size = sizeof(*in) - 4; 76662306a36Sopenharmony_ci if (buf_len < size) 76762306a36Sopenharmony_ci return -EINVAL; 76862306a36Sopenharmony_ci memcpy(thresh->data, in, size); 76962306a36Sopenharmony_ci in->status = 0; 77062306a36Sopenharmony_ci smart_notify(bus_dev, dimm_dev, smart, thresh); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci return 0; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int nfit_test_cmd_smart_inject( 77662306a36Sopenharmony_ci struct nd_intel_smart_inject *inj, 77762306a36Sopenharmony_ci unsigned int buf_len, 77862306a36Sopenharmony_ci struct nd_intel_smart_threshold *thresh, 77962306a36Sopenharmony_ci struct nd_intel_smart *smart, 78062306a36Sopenharmony_ci struct device *bus_dev, struct device *dimm_dev) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci if (buf_len != sizeof(*inj)) 78362306a36Sopenharmony_ci return -EINVAL; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (inj->flags & ND_INTEL_SMART_INJECT_MTEMP) { 78662306a36Sopenharmony_ci if (inj->mtemp_enable) 78762306a36Sopenharmony_ci smart->media_temperature = inj->media_temperature; 78862306a36Sopenharmony_ci else 78962306a36Sopenharmony_ci smart->media_temperature = smart_def.media_temperature; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci if (inj->flags & ND_INTEL_SMART_INJECT_SPARE) { 79262306a36Sopenharmony_ci if (inj->spare_enable) 79362306a36Sopenharmony_ci smart->spares = inj->spares; 79462306a36Sopenharmony_ci else 79562306a36Sopenharmony_ci smart->spares = smart_def.spares; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci if (inj->flags & ND_INTEL_SMART_INJECT_FATAL) { 79862306a36Sopenharmony_ci if (inj->fatal_enable) 79962306a36Sopenharmony_ci smart->health = ND_INTEL_SMART_FATAL_HEALTH; 80062306a36Sopenharmony_ci else 80162306a36Sopenharmony_ci smart->health = ND_INTEL_SMART_NON_CRITICAL_HEALTH; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci if (inj->flags & ND_INTEL_SMART_INJECT_SHUTDOWN) { 80462306a36Sopenharmony_ci if (inj->unsafe_shutdown_enable) { 80562306a36Sopenharmony_ci smart->shutdown_state = 1; 80662306a36Sopenharmony_ci smart->shutdown_count++; 80762306a36Sopenharmony_ci } else 80862306a36Sopenharmony_ci smart->shutdown_state = 0; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci inj->status = 0; 81162306a36Sopenharmony_ci smart_notify(bus_dev, dimm_dev, smart, thresh); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic void uc_error_notify(struct work_struct *work) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct nfit_test *t = container_of(work, typeof(*t), work); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci __acpi_nfit_notify(&t->pdev.dev, t, NFIT_NOTIFY_UC_MEMORY_ERROR); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int nfit_test_cmd_ars_error_inject(struct nfit_test *t, 82462306a36Sopenharmony_ci struct nd_cmd_ars_err_inj *err_inj, unsigned int buf_len) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci int rc; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (buf_len != sizeof(*err_inj)) { 82962306a36Sopenharmony_ci rc = -EINVAL; 83062306a36Sopenharmony_ci goto err; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (err_inj->err_inj_spa_range_length <= 0) { 83462306a36Sopenharmony_ci rc = -EINVAL; 83562306a36Sopenharmony_ci goto err; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci rc = badrange_add(&t->badrange, err_inj->err_inj_spa_range_base, 83962306a36Sopenharmony_ci err_inj->err_inj_spa_range_length); 84062306a36Sopenharmony_ci if (rc < 0) 84162306a36Sopenharmony_ci goto err; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (err_inj->err_inj_options & (1 << ND_ARS_ERR_INJ_OPT_NOTIFY)) 84462306a36Sopenharmony_ci queue_work(nfit_wq, &t->work); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci err_inj->status = 0; 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cierr: 85062306a36Sopenharmony_ci err_inj->status = NFIT_ARS_INJECT_INVALID; 85162306a36Sopenharmony_ci return rc; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic int nfit_test_cmd_ars_inject_clear(struct nfit_test *t, 85562306a36Sopenharmony_ci struct nd_cmd_ars_err_inj_clr *err_clr, unsigned int buf_len) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci int rc; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (buf_len != sizeof(*err_clr)) { 86062306a36Sopenharmony_ci rc = -EINVAL; 86162306a36Sopenharmony_ci goto err; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (err_clr->err_inj_clr_spa_range_length <= 0) { 86562306a36Sopenharmony_ci rc = -EINVAL; 86662306a36Sopenharmony_ci goto err; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci badrange_forget(&t->badrange, err_clr->err_inj_clr_spa_range_base, 87062306a36Sopenharmony_ci err_clr->err_inj_clr_spa_range_length); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci err_clr->status = 0; 87362306a36Sopenharmony_ci return 0; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cierr: 87662306a36Sopenharmony_ci err_clr->status = NFIT_ARS_INJECT_INVALID; 87762306a36Sopenharmony_ci return rc; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic int nfit_test_cmd_ars_inject_status(struct nfit_test *t, 88162306a36Sopenharmony_ci struct nd_cmd_ars_err_inj_stat *err_stat, 88262306a36Sopenharmony_ci unsigned int buf_len) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci struct badrange_entry *be; 88562306a36Sopenharmony_ci int max = SZ_4K / sizeof(struct nd_error_stat_query_record); 88662306a36Sopenharmony_ci int i = 0; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci err_stat->status = 0; 88962306a36Sopenharmony_ci spin_lock(&t->badrange.lock); 89062306a36Sopenharmony_ci list_for_each_entry(be, &t->badrange.list, list) { 89162306a36Sopenharmony_ci err_stat->record[i].err_inj_stat_spa_range_base = be->start; 89262306a36Sopenharmony_ci err_stat->record[i].err_inj_stat_spa_range_length = be->length; 89362306a36Sopenharmony_ci i++; 89462306a36Sopenharmony_ci if (i > max) 89562306a36Sopenharmony_ci break; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci spin_unlock(&t->badrange.lock); 89862306a36Sopenharmony_ci err_stat->inj_err_rec_count = i; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci return 0; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic int nd_intel_test_cmd_set_lss_status(struct nfit_test *t, 90462306a36Sopenharmony_ci struct nd_intel_lss *nd_cmd, unsigned int buf_len) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (buf_len < sizeof(*nd_cmd)) 90962306a36Sopenharmony_ci return -EINVAL; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci switch (nd_cmd->enable) { 91262306a36Sopenharmony_ci case 0: 91362306a36Sopenharmony_ci nd_cmd->status = 0; 91462306a36Sopenharmony_ci dev_dbg(dev, "%s: Latch System Shutdown Status disabled\n", 91562306a36Sopenharmony_ci __func__); 91662306a36Sopenharmony_ci break; 91762306a36Sopenharmony_ci case 1: 91862306a36Sopenharmony_ci nd_cmd->status = 0; 91962306a36Sopenharmony_ci dev_dbg(dev, "%s: Latch System Shutdown Status enabled\n", 92062306a36Sopenharmony_ci __func__); 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci default: 92362306a36Sopenharmony_ci dev_warn(dev, "Unknown enable value: %#x\n", nd_cmd->enable); 92462306a36Sopenharmony_ci nd_cmd->status = 0x3; 92562306a36Sopenharmony_ci break; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return 0; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic int override_return_code(int dimm, unsigned int func, int rc) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci if ((1 << func) & dimm_fail_cmd_flags[dimm]) { 93562306a36Sopenharmony_ci if (dimm_fail_cmd_code[dimm]) 93662306a36Sopenharmony_ci return dimm_fail_cmd_code[dimm]; 93762306a36Sopenharmony_ci return -EIO; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci return rc; 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic int nd_intel_test_cmd_security_status(struct nfit_test *t, 94362306a36Sopenharmony_ci struct nd_intel_get_security_state *nd_cmd, 94462306a36Sopenharmony_ci unsigned int buf_len, int dimm) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 94762306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci nd_cmd->status = 0; 95062306a36Sopenharmony_ci nd_cmd->state = sec->state; 95162306a36Sopenharmony_ci nd_cmd->extended_state = sec->ext_state; 95262306a36Sopenharmony_ci dev_dbg(dev, "security state (%#x) returned\n", nd_cmd->state); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int nd_intel_test_cmd_unlock_unit(struct nfit_test *t, 95862306a36Sopenharmony_ci struct nd_intel_unlock_unit *nd_cmd, 95962306a36Sopenharmony_ci unsigned int buf_len, int dimm) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 96262306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (!(sec->state & ND_INTEL_SEC_STATE_LOCKED) || 96562306a36Sopenharmony_ci (sec->state & ND_INTEL_SEC_STATE_FROZEN)) { 96662306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; 96762306a36Sopenharmony_ci dev_dbg(dev, "unlock unit: invalid state: %#x\n", 96862306a36Sopenharmony_ci sec->state); 96962306a36Sopenharmony_ci } else if (memcmp(nd_cmd->passphrase, sec->passphrase, 97062306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE) != 0) { 97162306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; 97262306a36Sopenharmony_ci dev_dbg(dev, "unlock unit: invalid passphrase\n"); 97362306a36Sopenharmony_ci } else { 97462306a36Sopenharmony_ci nd_cmd->status = 0; 97562306a36Sopenharmony_ci sec->state = ND_INTEL_SEC_STATE_ENABLED; 97662306a36Sopenharmony_ci dev_dbg(dev, "Unit unlocked\n"); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci dev_dbg(dev, "unlocking status returned: %#x\n", nd_cmd->status); 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic int nd_intel_test_cmd_set_pass(struct nfit_test *t, 98462306a36Sopenharmony_ci struct nd_intel_set_passphrase *nd_cmd, 98562306a36Sopenharmony_ci unsigned int buf_len, int dimm) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 98862306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (sec->state & ND_INTEL_SEC_STATE_FROZEN) { 99162306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; 99262306a36Sopenharmony_ci dev_dbg(dev, "set passphrase: wrong security state\n"); 99362306a36Sopenharmony_ci } else if (memcmp(nd_cmd->old_pass, sec->passphrase, 99462306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE) != 0) { 99562306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; 99662306a36Sopenharmony_ci dev_dbg(dev, "set passphrase: wrong passphrase\n"); 99762306a36Sopenharmony_ci } else { 99862306a36Sopenharmony_ci memcpy(sec->passphrase, nd_cmd->new_pass, 99962306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE); 100062306a36Sopenharmony_ci sec->state |= ND_INTEL_SEC_STATE_ENABLED; 100162306a36Sopenharmony_ci nd_cmd->status = 0; 100262306a36Sopenharmony_ci dev_dbg(dev, "passphrase updated\n"); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic int nd_intel_test_cmd_freeze_lock(struct nfit_test *t, 100962306a36Sopenharmony_ci struct nd_intel_freeze_lock *nd_cmd, 101062306a36Sopenharmony_ci unsigned int buf_len, int dimm) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 101362306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED)) { 101662306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; 101762306a36Sopenharmony_ci dev_dbg(dev, "freeze lock: wrong security state\n"); 101862306a36Sopenharmony_ci } else { 101962306a36Sopenharmony_ci sec->state |= ND_INTEL_SEC_STATE_FROZEN; 102062306a36Sopenharmony_ci nd_cmd->status = 0; 102162306a36Sopenharmony_ci dev_dbg(dev, "security frozen\n"); 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return 0; 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int nd_intel_test_cmd_disable_pass(struct nfit_test *t, 102862306a36Sopenharmony_ci struct nd_intel_disable_passphrase *nd_cmd, 102962306a36Sopenharmony_ci unsigned int buf_len, int dimm) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 103262306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED) || 103562306a36Sopenharmony_ci (sec->state & ND_INTEL_SEC_STATE_FROZEN)) { 103662306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; 103762306a36Sopenharmony_ci dev_dbg(dev, "disable passphrase: wrong security state\n"); 103862306a36Sopenharmony_ci } else if (memcmp(nd_cmd->passphrase, sec->passphrase, 103962306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE) != 0) { 104062306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; 104162306a36Sopenharmony_ci dev_dbg(dev, "disable passphrase: wrong passphrase\n"); 104262306a36Sopenharmony_ci } else { 104362306a36Sopenharmony_ci memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); 104462306a36Sopenharmony_ci sec->state = 0; 104562306a36Sopenharmony_ci dev_dbg(dev, "disable passphrase: done\n"); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return 0; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic int nd_intel_test_cmd_secure_erase(struct nfit_test *t, 105262306a36Sopenharmony_ci struct nd_intel_secure_erase *nd_cmd, 105362306a36Sopenharmony_ci unsigned int buf_len, int dimm) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 105662306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (sec->state & ND_INTEL_SEC_STATE_FROZEN) { 105962306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; 106062306a36Sopenharmony_ci dev_dbg(dev, "secure erase: wrong security state\n"); 106162306a36Sopenharmony_ci } else if (memcmp(nd_cmd->passphrase, sec->passphrase, 106262306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE) != 0) { 106362306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; 106462306a36Sopenharmony_ci dev_dbg(dev, "secure erase: wrong passphrase\n"); 106562306a36Sopenharmony_ci } else { 106662306a36Sopenharmony_ci if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED) 106762306a36Sopenharmony_ci && (memcmp(nd_cmd->passphrase, zero_key, 106862306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE) != 0)) { 106962306a36Sopenharmony_ci dev_dbg(dev, "invalid zero key\n"); 107062306a36Sopenharmony_ci return 0; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); 107362306a36Sopenharmony_ci memset(sec->master_passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); 107462306a36Sopenharmony_ci sec->state = 0; 107562306a36Sopenharmony_ci sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; 107662306a36Sopenharmony_ci dev_dbg(dev, "secure erase: done\n"); 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cistatic int nd_intel_test_cmd_overwrite(struct nfit_test *t, 108362306a36Sopenharmony_ci struct nd_intel_overwrite *nd_cmd, 108462306a36Sopenharmony_ci unsigned int buf_len, int dimm) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 108762306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if ((sec->state & ND_INTEL_SEC_STATE_ENABLED) && 109062306a36Sopenharmony_ci memcmp(nd_cmd->passphrase, sec->passphrase, 109162306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE) != 0) { 109262306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; 109362306a36Sopenharmony_ci dev_dbg(dev, "overwrite: wrong passphrase\n"); 109462306a36Sopenharmony_ci return 0; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci sec->old_state = sec->state; 109862306a36Sopenharmony_ci sec->state = ND_INTEL_SEC_STATE_OVERWRITE; 109962306a36Sopenharmony_ci dev_dbg(dev, "overwrite progressing.\n"); 110062306a36Sopenharmony_ci sec->overwrite_end_time = get_jiffies_64() + 5 * HZ; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic int nd_intel_test_cmd_query_overwrite(struct nfit_test *t, 110662306a36Sopenharmony_ci struct nd_intel_query_overwrite *nd_cmd, 110762306a36Sopenharmony_ci unsigned int buf_len, int dimm) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 111062306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (!(sec->state & ND_INTEL_SEC_STATE_OVERWRITE)) { 111362306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR; 111462306a36Sopenharmony_ci return 0; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (time_is_before_jiffies64(sec->overwrite_end_time)) { 111862306a36Sopenharmony_ci sec->overwrite_end_time = 0; 111962306a36Sopenharmony_ci sec->state = sec->old_state; 112062306a36Sopenharmony_ci sec->old_state = 0; 112162306a36Sopenharmony_ci sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; 112262306a36Sopenharmony_ci dev_dbg(dev, "overwrite is complete\n"); 112362306a36Sopenharmony_ci } else 112462306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_OQUERY_INPROGRESS; 112562306a36Sopenharmony_ci return 0; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic int nd_intel_test_cmd_master_set_pass(struct nfit_test *t, 112962306a36Sopenharmony_ci struct nd_intel_set_master_passphrase *nd_cmd, 113062306a36Sopenharmony_ci unsigned int buf_len, int dimm) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 113362306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (!(sec->ext_state & ND_INTEL_SEC_ESTATE_ENABLED)) { 113662306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_NOT_SUPPORTED; 113762306a36Sopenharmony_ci dev_dbg(dev, "master set passphrase: in wrong state\n"); 113862306a36Sopenharmony_ci } else if (sec->ext_state & ND_INTEL_SEC_ESTATE_PLIMIT) { 113962306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; 114062306a36Sopenharmony_ci dev_dbg(dev, "master set passphrase: in wrong security state\n"); 114162306a36Sopenharmony_ci } else if (memcmp(nd_cmd->old_pass, sec->master_passphrase, 114262306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE) != 0) { 114362306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; 114462306a36Sopenharmony_ci dev_dbg(dev, "master set passphrase: wrong passphrase\n"); 114562306a36Sopenharmony_ci } else { 114662306a36Sopenharmony_ci memcpy(sec->master_passphrase, nd_cmd->new_pass, 114762306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE); 114862306a36Sopenharmony_ci sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; 114962306a36Sopenharmony_ci dev_dbg(dev, "master passphrase: updated\n"); 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci return 0; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cistatic int nd_intel_test_cmd_master_secure_erase(struct nfit_test *t, 115662306a36Sopenharmony_ci struct nd_intel_master_secure_erase *nd_cmd, 115762306a36Sopenharmony_ci unsigned int buf_len, int dimm) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 116062306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (!(sec->ext_state & ND_INTEL_SEC_ESTATE_ENABLED)) { 116362306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_NOT_SUPPORTED; 116462306a36Sopenharmony_ci dev_dbg(dev, "master secure erase: in wrong state\n"); 116562306a36Sopenharmony_ci } else if (sec->ext_state & ND_INTEL_SEC_ESTATE_PLIMIT) { 116662306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; 116762306a36Sopenharmony_ci dev_dbg(dev, "master secure erase: in wrong security state\n"); 116862306a36Sopenharmony_ci } else if (memcmp(nd_cmd->passphrase, sec->master_passphrase, 116962306a36Sopenharmony_ci ND_INTEL_PASSPHRASE_SIZE) != 0) { 117062306a36Sopenharmony_ci nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; 117162306a36Sopenharmony_ci dev_dbg(dev, "master secure erase: wrong passphrase\n"); 117262306a36Sopenharmony_ci } else { 117362306a36Sopenharmony_ci /* we do not erase master state passphrase ever */ 117462306a36Sopenharmony_ci sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; 117562306a36Sopenharmony_ci memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); 117662306a36Sopenharmony_ci sec->state = 0; 117762306a36Sopenharmony_ci dev_dbg(dev, "master secure erase: done\n"); 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci return 0; 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic unsigned long last_activate; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic int nvdimm_bus_intel_fw_activate_businfo(struct nfit_test *t, 118662306a36Sopenharmony_ci struct nd_intel_bus_fw_activate_businfo *nd_cmd, 118762306a36Sopenharmony_ci unsigned int buf_len) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci int i, armed = 0; 119062306a36Sopenharmony_ci int state; 119162306a36Sopenharmony_ci u64 tmo; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci for (i = 0; i < NUM_DCR; i++) { 119462306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[i]; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (fw->armed) 119762306a36Sopenharmony_ci armed++; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci /* 120162306a36Sopenharmony_ci * Emulate 3 second activation max, and 1 second incremental 120262306a36Sopenharmony_ci * quiesce time per dimm requiring multiple activates to get all 120362306a36Sopenharmony_ci * DIMMs updated. 120462306a36Sopenharmony_ci */ 120562306a36Sopenharmony_ci if (armed) 120662306a36Sopenharmony_ci state = ND_INTEL_FWA_ARMED; 120762306a36Sopenharmony_ci else if (!last_activate || time_after(jiffies, last_activate + 3 * HZ)) 120862306a36Sopenharmony_ci state = ND_INTEL_FWA_IDLE; 120962306a36Sopenharmony_ci else 121062306a36Sopenharmony_ci state = ND_INTEL_FWA_BUSY; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci tmo = armed * USEC_PER_SEC; 121362306a36Sopenharmony_ci *nd_cmd = (struct nd_intel_bus_fw_activate_businfo) { 121462306a36Sopenharmony_ci .capability = ND_INTEL_BUS_FWA_CAP_FWQUIESCE 121562306a36Sopenharmony_ci | ND_INTEL_BUS_FWA_CAP_OSQUIESCE 121662306a36Sopenharmony_ci | ND_INTEL_BUS_FWA_CAP_RESET, 121762306a36Sopenharmony_ci .state = state, 121862306a36Sopenharmony_ci .activate_tmo = tmo, 121962306a36Sopenharmony_ci .cpu_quiesce_tmo = tmo, 122062306a36Sopenharmony_ci .io_quiesce_tmo = tmo, 122162306a36Sopenharmony_ci .max_quiesce_tmo = 3 * USEC_PER_SEC, 122262306a36Sopenharmony_ci }; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci return 0; 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic int nvdimm_bus_intel_fw_activate(struct nfit_test *t, 122862306a36Sopenharmony_ci struct nd_intel_bus_fw_activate *nd_cmd, 122962306a36Sopenharmony_ci unsigned int buf_len) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct nd_intel_bus_fw_activate_businfo info; 123262306a36Sopenharmony_ci u32 status = 0; 123362306a36Sopenharmony_ci int i; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci nvdimm_bus_intel_fw_activate_businfo(t, &info, sizeof(info)); 123662306a36Sopenharmony_ci if (info.state == ND_INTEL_FWA_BUSY) 123762306a36Sopenharmony_ci status = ND_INTEL_BUS_FWA_STATUS_BUSY; 123862306a36Sopenharmony_ci else if (info.activate_tmo > info.max_quiesce_tmo) 123962306a36Sopenharmony_ci status = ND_INTEL_BUS_FWA_STATUS_TMO; 124062306a36Sopenharmony_ci else if (info.state == ND_INTEL_FWA_IDLE) 124162306a36Sopenharmony_ci status = ND_INTEL_BUS_FWA_STATUS_NOARM; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci dev_dbg(&t->pdev.dev, "status: %d\n", status); 124462306a36Sopenharmony_ci nd_cmd->status = status; 124562306a36Sopenharmony_ci if (status && status != ND_INTEL_BUS_FWA_STATUS_TMO) 124662306a36Sopenharmony_ci return 0; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci last_activate = jiffies; 124962306a36Sopenharmony_ci for (i = 0; i < NUM_DCR; i++) { 125062306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[i]; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci if (!fw->armed) 125362306a36Sopenharmony_ci continue; 125462306a36Sopenharmony_ci if (fw->state != FW_STATE_UPDATED) 125562306a36Sopenharmony_ci fw->missed_activate = true; 125662306a36Sopenharmony_ci else 125762306a36Sopenharmony_ci fw->state = FW_STATE_NEW; 125862306a36Sopenharmony_ci fw->armed = false; 125962306a36Sopenharmony_ci fw->last_activate = last_activate; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci return 0; 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_cistatic int nd_intel_test_cmd_fw_activate_dimminfo(struct nfit_test *t, 126662306a36Sopenharmony_ci struct nd_intel_fw_activate_dimminfo *nd_cmd, 126762306a36Sopenharmony_ci unsigned int buf_len, int dimm) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci struct nd_intel_bus_fw_activate_businfo info; 127062306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[dimm]; 127162306a36Sopenharmony_ci u32 result, state; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci nvdimm_bus_intel_fw_activate_businfo(t, &info, sizeof(info)); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci if (info.state == ND_INTEL_FWA_BUSY) 127662306a36Sopenharmony_ci state = ND_INTEL_FWA_BUSY; 127762306a36Sopenharmony_ci else if (info.state == ND_INTEL_FWA_IDLE) 127862306a36Sopenharmony_ci state = ND_INTEL_FWA_IDLE; 127962306a36Sopenharmony_ci else if (fw->armed) 128062306a36Sopenharmony_ci state = ND_INTEL_FWA_ARMED; 128162306a36Sopenharmony_ci else 128262306a36Sopenharmony_ci state = ND_INTEL_FWA_IDLE; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci result = ND_INTEL_DIMM_FWA_NONE; 128562306a36Sopenharmony_ci if (last_activate && fw->last_activate == last_activate && 128662306a36Sopenharmony_ci state == ND_INTEL_FWA_IDLE) { 128762306a36Sopenharmony_ci if (fw->missed_activate) 128862306a36Sopenharmony_ci result = ND_INTEL_DIMM_FWA_NOTSTAGED; 128962306a36Sopenharmony_ci else 129062306a36Sopenharmony_ci result = ND_INTEL_DIMM_FWA_SUCCESS; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci *nd_cmd = (struct nd_intel_fw_activate_dimminfo) { 129462306a36Sopenharmony_ci .result = result, 129562306a36Sopenharmony_ci .state = state, 129662306a36Sopenharmony_ci }; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci return 0; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic int nd_intel_test_cmd_fw_activate_arm(struct nfit_test *t, 130262306a36Sopenharmony_ci struct nd_intel_fw_activate_arm *nd_cmd, 130362306a36Sopenharmony_ci unsigned int buf_len, int dimm) 130462306a36Sopenharmony_ci{ 130562306a36Sopenharmony_ci struct nfit_test_fw *fw = &t->fw[dimm]; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci fw->armed = nd_cmd->activate_arm == ND_INTEL_DIMM_FWA_ARM; 130862306a36Sopenharmony_ci nd_cmd->status = 0; 130962306a36Sopenharmony_ci return 0; 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_cistatic int get_dimm(struct nfit_mem *nfit_mem, unsigned int func) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci int i; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci /* lookup per-dimm data */ 131762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(handle); i++) 131862306a36Sopenharmony_ci if (__to_nfit_memdev(nfit_mem)->device_handle == handle[i]) 131962306a36Sopenharmony_ci break; 132062306a36Sopenharmony_ci if (i >= ARRAY_SIZE(handle)) 132162306a36Sopenharmony_ci return -ENXIO; 132262306a36Sopenharmony_ci return i; 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic void nfit_ctl_dbg(struct acpi_nfit_desc *acpi_desc, 132662306a36Sopenharmony_ci struct nvdimm *nvdimm, unsigned int cmd, void *buf, 132762306a36Sopenharmony_ci unsigned int len) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc); 133062306a36Sopenharmony_ci unsigned int func = cmd; 133162306a36Sopenharmony_ci unsigned int family = 0; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (cmd == ND_CMD_CALL) { 133462306a36Sopenharmony_ci struct nd_cmd_pkg *pkg = buf; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci len = pkg->nd_size_in; 133762306a36Sopenharmony_ci family = pkg->nd_family; 133862306a36Sopenharmony_ci buf = pkg->nd_payload; 133962306a36Sopenharmony_ci func = pkg->nd_command; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci dev_dbg(&t->pdev.dev, "%s family: %d cmd: %d: func: %d input length: %d\n", 134262306a36Sopenharmony_ci nvdimm ? nvdimm_name(nvdimm) : "bus", family, cmd, func, 134362306a36Sopenharmony_ci len); 134462306a36Sopenharmony_ci print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 16, 4, 134562306a36Sopenharmony_ci buf, min(len, 256u), true); 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, 134962306a36Sopenharmony_ci struct nvdimm *nvdimm, unsigned int cmd, void *buf, 135062306a36Sopenharmony_ci unsigned int buf_len, int *cmd_rc) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 135362306a36Sopenharmony_ci struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc); 135462306a36Sopenharmony_ci unsigned int func = cmd; 135562306a36Sopenharmony_ci int i, rc = 0, __cmd_rc; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci if (!cmd_rc) 135862306a36Sopenharmony_ci cmd_rc = &__cmd_rc; 135962306a36Sopenharmony_ci *cmd_rc = 0; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci nfit_ctl_dbg(acpi_desc, nvdimm, cmd, buf, buf_len); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci if (nvdimm) { 136462306a36Sopenharmony_ci struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); 136562306a36Sopenharmony_ci unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci if (!nfit_mem) 136862306a36Sopenharmony_ci return -ENOTTY; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci if (cmd == ND_CMD_CALL) { 137162306a36Sopenharmony_ci struct nd_cmd_pkg *call_pkg = buf; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out; 137462306a36Sopenharmony_ci buf = (void *) call_pkg->nd_payload; 137562306a36Sopenharmony_ci func = call_pkg->nd_command; 137662306a36Sopenharmony_ci if (call_pkg->nd_family != nfit_mem->family) 137762306a36Sopenharmony_ci return -ENOTTY; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci i = get_dimm(nfit_mem, func); 138062306a36Sopenharmony_ci if (i < 0) 138162306a36Sopenharmony_ci return i; 138262306a36Sopenharmony_ci if (i >= NUM_DCR) { 138362306a36Sopenharmony_ci dev_WARN_ONCE(&t->pdev.dev, 1, 138462306a36Sopenharmony_ci "ND_CMD_CALL only valid for nfit_test0\n"); 138562306a36Sopenharmony_ci return -EINVAL; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci switch (func) { 138962306a36Sopenharmony_ci case NVDIMM_INTEL_GET_SECURITY_STATE: 139062306a36Sopenharmony_ci rc = nd_intel_test_cmd_security_status(t, 139162306a36Sopenharmony_ci buf, buf_len, i); 139262306a36Sopenharmony_ci break; 139362306a36Sopenharmony_ci case NVDIMM_INTEL_UNLOCK_UNIT: 139462306a36Sopenharmony_ci rc = nd_intel_test_cmd_unlock_unit(t, 139562306a36Sopenharmony_ci buf, buf_len, i); 139662306a36Sopenharmony_ci break; 139762306a36Sopenharmony_ci case NVDIMM_INTEL_SET_PASSPHRASE: 139862306a36Sopenharmony_ci rc = nd_intel_test_cmd_set_pass(t, 139962306a36Sopenharmony_ci buf, buf_len, i); 140062306a36Sopenharmony_ci break; 140162306a36Sopenharmony_ci case NVDIMM_INTEL_DISABLE_PASSPHRASE: 140262306a36Sopenharmony_ci rc = nd_intel_test_cmd_disable_pass(t, 140362306a36Sopenharmony_ci buf, buf_len, i); 140462306a36Sopenharmony_ci break; 140562306a36Sopenharmony_ci case NVDIMM_INTEL_FREEZE_LOCK: 140662306a36Sopenharmony_ci rc = nd_intel_test_cmd_freeze_lock(t, 140762306a36Sopenharmony_ci buf, buf_len, i); 140862306a36Sopenharmony_ci break; 140962306a36Sopenharmony_ci case NVDIMM_INTEL_SECURE_ERASE: 141062306a36Sopenharmony_ci rc = nd_intel_test_cmd_secure_erase(t, 141162306a36Sopenharmony_ci buf, buf_len, i); 141262306a36Sopenharmony_ci break; 141362306a36Sopenharmony_ci case NVDIMM_INTEL_OVERWRITE: 141462306a36Sopenharmony_ci rc = nd_intel_test_cmd_overwrite(t, 141562306a36Sopenharmony_ci buf, buf_len, i); 141662306a36Sopenharmony_ci break; 141762306a36Sopenharmony_ci case NVDIMM_INTEL_QUERY_OVERWRITE: 141862306a36Sopenharmony_ci rc = nd_intel_test_cmd_query_overwrite(t, 141962306a36Sopenharmony_ci buf, buf_len, i); 142062306a36Sopenharmony_ci break; 142162306a36Sopenharmony_ci case NVDIMM_INTEL_SET_MASTER_PASSPHRASE: 142262306a36Sopenharmony_ci rc = nd_intel_test_cmd_master_set_pass(t, 142362306a36Sopenharmony_ci buf, buf_len, i); 142462306a36Sopenharmony_ci break; 142562306a36Sopenharmony_ci case NVDIMM_INTEL_MASTER_SECURE_ERASE: 142662306a36Sopenharmony_ci rc = nd_intel_test_cmd_master_secure_erase(t, 142762306a36Sopenharmony_ci buf, buf_len, i); 142862306a36Sopenharmony_ci break; 142962306a36Sopenharmony_ci case NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO: 143062306a36Sopenharmony_ci rc = nd_intel_test_cmd_fw_activate_dimminfo( 143162306a36Sopenharmony_ci t, buf, buf_len, i); 143262306a36Sopenharmony_ci break; 143362306a36Sopenharmony_ci case NVDIMM_INTEL_FW_ACTIVATE_ARM: 143462306a36Sopenharmony_ci rc = nd_intel_test_cmd_fw_activate_arm( 143562306a36Sopenharmony_ci t, buf, buf_len, i); 143662306a36Sopenharmony_ci break; 143762306a36Sopenharmony_ci case ND_INTEL_ENABLE_LSS_STATUS: 143862306a36Sopenharmony_ci rc = nd_intel_test_cmd_set_lss_status(t, 143962306a36Sopenharmony_ci buf, buf_len); 144062306a36Sopenharmony_ci break; 144162306a36Sopenharmony_ci case ND_INTEL_FW_GET_INFO: 144262306a36Sopenharmony_ci rc = nd_intel_test_get_fw_info(t, buf, 144362306a36Sopenharmony_ci buf_len, i); 144462306a36Sopenharmony_ci break; 144562306a36Sopenharmony_ci case ND_INTEL_FW_START_UPDATE: 144662306a36Sopenharmony_ci rc = nd_intel_test_start_update(t, buf, 144762306a36Sopenharmony_ci buf_len, i); 144862306a36Sopenharmony_ci break; 144962306a36Sopenharmony_ci case ND_INTEL_FW_SEND_DATA: 145062306a36Sopenharmony_ci rc = nd_intel_test_send_data(t, buf, 145162306a36Sopenharmony_ci buf_len, i); 145262306a36Sopenharmony_ci break; 145362306a36Sopenharmony_ci case ND_INTEL_FW_FINISH_UPDATE: 145462306a36Sopenharmony_ci rc = nd_intel_test_finish_fw(t, buf, 145562306a36Sopenharmony_ci buf_len, i); 145662306a36Sopenharmony_ci break; 145762306a36Sopenharmony_ci case ND_INTEL_FW_FINISH_QUERY: 145862306a36Sopenharmony_ci rc = nd_intel_test_finish_query(t, buf, 145962306a36Sopenharmony_ci buf_len, i); 146062306a36Sopenharmony_ci break; 146162306a36Sopenharmony_ci case ND_INTEL_SMART: 146262306a36Sopenharmony_ci rc = nfit_test_cmd_smart(buf, buf_len, 146362306a36Sopenharmony_ci &t->smart[i]); 146462306a36Sopenharmony_ci break; 146562306a36Sopenharmony_ci case ND_INTEL_SMART_THRESHOLD: 146662306a36Sopenharmony_ci rc = nfit_test_cmd_smart_threshold(buf, 146762306a36Sopenharmony_ci buf_len, 146862306a36Sopenharmony_ci &t->smart_threshold[i]); 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci case ND_INTEL_SMART_SET_THRESHOLD: 147162306a36Sopenharmony_ci rc = nfit_test_cmd_smart_set_threshold(buf, 147262306a36Sopenharmony_ci buf_len, 147362306a36Sopenharmony_ci &t->smart_threshold[i], 147462306a36Sopenharmony_ci &t->smart[i], 147562306a36Sopenharmony_ci &t->pdev.dev, t->dimm_dev[i]); 147662306a36Sopenharmony_ci break; 147762306a36Sopenharmony_ci case ND_INTEL_SMART_INJECT: 147862306a36Sopenharmony_ci rc = nfit_test_cmd_smart_inject(buf, 147962306a36Sopenharmony_ci buf_len, 148062306a36Sopenharmony_ci &t->smart_threshold[i], 148162306a36Sopenharmony_ci &t->smart[i], 148262306a36Sopenharmony_ci &t->pdev.dev, t->dimm_dev[i]); 148362306a36Sopenharmony_ci break; 148462306a36Sopenharmony_ci default: 148562306a36Sopenharmony_ci return -ENOTTY; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci return override_return_code(i, func, rc); 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci if (!test_bit(cmd, &cmd_mask) 149162306a36Sopenharmony_ci || !test_bit(func, &nfit_mem->dsm_mask)) 149262306a36Sopenharmony_ci return -ENOTTY; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci i = get_dimm(nfit_mem, func); 149562306a36Sopenharmony_ci if (i < 0) 149662306a36Sopenharmony_ci return i; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci switch (func) { 149962306a36Sopenharmony_ci case ND_CMD_GET_CONFIG_SIZE: 150062306a36Sopenharmony_ci rc = nfit_test_cmd_get_config_size(buf, buf_len); 150162306a36Sopenharmony_ci break; 150262306a36Sopenharmony_ci case ND_CMD_GET_CONFIG_DATA: 150362306a36Sopenharmony_ci rc = nfit_test_cmd_get_config_data(buf, buf_len, 150462306a36Sopenharmony_ci t->label[i - t->dcr_idx]); 150562306a36Sopenharmony_ci break; 150662306a36Sopenharmony_ci case ND_CMD_SET_CONFIG_DATA: 150762306a36Sopenharmony_ci rc = nfit_test_cmd_set_config_data(buf, buf_len, 150862306a36Sopenharmony_ci t->label[i - t->dcr_idx]); 150962306a36Sopenharmony_ci break; 151062306a36Sopenharmony_ci default: 151162306a36Sopenharmony_ci return -ENOTTY; 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci return override_return_code(i, func, rc); 151462306a36Sopenharmony_ci } else { 151562306a36Sopenharmony_ci struct ars_state *ars_state = &t->ars_state; 151662306a36Sopenharmony_ci struct nd_cmd_pkg *call_pkg = buf; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (!nd_desc) 151962306a36Sopenharmony_ci return -ENOTTY; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci if (cmd == ND_CMD_CALL && call_pkg->nd_family 152262306a36Sopenharmony_ci == NVDIMM_BUS_FAMILY_NFIT) { 152362306a36Sopenharmony_ci func = call_pkg->nd_command; 152462306a36Sopenharmony_ci buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out; 152562306a36Sopenharmony_ci buf = (void *) call_pkg->nd_payload; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci switch (func) { 152862306a36Sopenharmony_ci case NFIT_CMD_TRANSLATE_SPA: 152962306a36Sopenharmony_ci rc = nfit_test_cmd_translate_spa( 153062306a36Sopenharmony_ci acpi_desc->nvdimm_bus, buf, buf_len); 153162306a36Sopenharmony_ci return rc; 153262306a36Sopenharmony_ci case NFIT_CMD_ARS_INJECT_SET: 153362306a36Sopenharmony_ci rc = nfit_test_cmd_ars_error_inject(t, buf, 153462306a36Sopenharmony_ci buf_len); 153562306a36Sopenharmony_ci return rc; 153662306a36Sopenharmony_ci case NFIT_CMD_ARS_INJECT_CLEAR: 153762306a36Sopenharmony_ci rc = nfit_test_cmd_ars_inject_clear(t, buf, 153862306a36Sopenharmony_ci buf_len); 153962306a36Sopenharmony_ci return rc; 154062306a36Sopenharmony_ci case NFIT_CMD_ARS_INJECT_GET: 154162306a36Sopenharmony_ci rc = nfit_test_cmd_ars_inject_status(t, buf, 154262306a36Sopenharmony_ci buf_len); 154362306a36Sopenharmony_ci return rc; 154462306a36Sopenharmony_ci default: 154562306a36Sopenharmony_ci return -ENOTTY; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci } else if (cmd == ND_CMD_CALL && call_pkg->nd_family 154862306a36Sopenharmony_ci == NVDIMM_BUS_FAMILY_INTEL) { 154962306a36Sopenharmony_ci func = call_pkg->nd_command; 155062306a36Sopenharmony_ci buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out; 155162306a36Sopenharmony_ci buf = (void *) call_pkg->nd_payload; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci switch (func) { 155462306a36Sopenharmony_ci case NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO: 155562306a36Sopenharmony_ci rc = nvdimm_bus_intel_fw_activate_businfo(t, 155662306a36Sopenharmony_ci buf, buf_len); 155762306a36Sopenharmony_ci return rc; 155862306a36Sopenharmony_ci case NVDIMM_BUS_INTEL_FW_ACTIVATE: 155962306a36Sopenharmony_ci rc = nvdimm_bus_intel_fw_activate(t, buf, 156062306a36Sopenharmony_ci buf_len); 156162306a36Sopenharmony_ci return rc; 156262306a36Sopenharmony_ci default: 156362306a36Sopenharmony_ci return -ENOTTY; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci } else if (cmd == ND_CMD_CALL) 156662306a36Sopenharmony_ci return -ENOTTY; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask)) 156962306a36Sopenharmony_ci return -ENOTTY; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci switch (func) { 157262306a36Sopenharmony_ci case ND_CMD_ARS_CAP: 157362306a36Sopenharmony_ci rc = nfit_test_cmd_ars_cap(buf, buf_len); 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci case ND_CMD_ARS_START: 157662306a36Sopenharmony_ci rc = nfit_test_cmd_ars_start(t, ars_state, buf, 157762306a36Sopenharmony_ci buf_len, cmd_rc); 157862306a36Sopenharmony_ci break; 157962306a36Sopenharmony_ci case ND_CMD_ARS_STATUS: 158062306a36Sopenharmony_ci rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len, 158162306a36Sopenharmony_ci cmd_rc); 158262306a36Sopenharmony_ci break; 158362306a36Sopenharmony_ci case ND_CMD_CLEAR_ERROR: 158462306a36Sopenharmony_ci rc = nfit_test_cmd_clear_error(t, buf, buf_len, cmd_rc); 158562306a36Sopenharmony_ci break; 158662306a36Sopenharmony_ci default: 158762306a36Sopenharmony_ci return -ENOTTY; 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci return rc; 159262306a36Sopenharmony_ci} 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nfit_test_lock); 159562306a36Sopenharmony_cistatic struct nfit_test *instances[NUM_NFITS]; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_cistatic void release_nfit_res(void *data) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci struct nfit_test_resource *nfit_res = data; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci spin_lock(&nfit_test_lock); 160262306a36Sopenharmony_ci list_del(&nfit_res->list); 160362306a36Sopenharmony_ci spin_unlock(&nfit_test_lock); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci if (resource_size(&nfit_res->res) >= DIMM_SIZE) 160662306a36Sopenharmony_ci gen_pool_free(nfit_pool, nfit_res->res.start, 160762306a36Sopenharmony_ci resource_size(&nfit_res->res)); 160862306a36Sopenharmony_ci vfree(nfit_res->buf); 160962306a36Sopenharmony_ci kfree(nfit_res); 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cistatic void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, 161362306a36Sopenharmony_ci void *buf) 161462306a36Sopenharmony_ci{ 161562306a36Sopenharmony_ci struct device *dev = &t->pdev.dev; 161662306a36Sopenharmony_ci struct nfit_test_resource *nfit_res = kzalloc(sizeof(*nfit_res), 161762306a36Sopenharmony_ci GFP_KERNEL); 161862306a36Sopenharmony_ci int rc; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (!buf || !nfit_res || !*dma) 162162306a36Sopenharmony_ci goto err; 162262306a36Sopenharmony_ci rc = devm_add_action(dev, release_nfit_res, nfit_res); 162362306a36Sopenharmony_ci if (rc) 162462306a36Sopenharmony_ci goto err; 162562306a36Sopenharmony_ci INIT_LIST_HEAD(&nfit_res->list); 162662306a36Sopenharmony_ci memset(buf, 0, size); 162762306a36Sopenharmony_ci nfit_res->dev = dev; 162862306a36Sopenharmony_ci nfit_res->buf = buf; 162962306a36Sopenharmony_ci nfit_res->res.start = *dma; 163062306a36Sopenharmony_ci nfit_res->res.end = *dma + size - 1; 163162306a36Sopenharmony_ci nfit_res->res.name = "NFIT"; 163262306a36Sopenharmony_ci spin_lock_init(&nfit_res->lock); 163362306a36Sopenharmony_ci INIT_LIST_HEAD(&nfit_res->requests); 163462306a36Sopenharmony_ci spin_lock(&nfit_test_lock); 163562306a36Sopenharmony_ci list_add(&nfit_res->list, &t->resources); 163662306a36Sopenharmony_ci spin_unlock(&nfit_test_lock); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci return nfit_res->buf; 163962306a36Sopenharmony_ci err: 164062306a36Sopenharmony_ci if (*dma && size >= DIMM_SIZE) 164162306a36Sopenharmony_ci gen_pool_free(nfit_pool, *dma, size); 164262306a36Sopenharmony_ci if (buf) 164362306a36Sopenharmony_ci vfree(buf); 164462306a36Sopenharmony_ci kfree(nfit_res); 164562306a36Sopenharmony_ci return NULL; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic void *test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma) 164962306a36Sopenharmony_ci{ 165062306a36Sopenharmony_ci struct genpool_data_align data = { 165162306a36Sopenharmony_ci .align = SZ_128M, 165262306a36Sopenharmony_ci }; 165362306a36Sopenharmony_ci void *buf = vmalloc(size); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci if (size >= DIMM_SIZE) 165662306a36Sopenharmony_ci *dma = gen_pool_alloc_algo(nfit_pool, size, 165762306a36Sopenharmony_ci gen_pool_first_fit_align, &data); 165862306a36Sopenharmony_ci else 165962306a36Sopenharmony_ci *dma = (unsigned long) buf; 166062306a36Sopenharmony_ci return __test_alloc(t, size, dma, buf); 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic struct nfit_test_resource *nfit_test_lookup(resource_size_t addr) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci int i; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(instances); i++) { 166862306a36Sopenharmony_ci struct nfit_test_resource *n, *nfit_res = NULL; 166962306a36Sopenharmony_ci struct nfit_test *t = instances[i]; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if (!t) 167262306a36Sopenharmony_ci continue; 167362306a36Sopenharmony_ci spin_lock(&nfit_test_lock); 167462306a36Sopenharmony_ci list_for_each_entry(n, &t->resources, list) { 167562306a36Sopenharmony_ci if (addr >= n->res.start && (addr < n->res.start 167662306a36Sopenharmony_ci + resource_size(&n->res))) { 167762306a36Sopenharmony_ci nfit_res = n; 167862306a36Sopenharmony_ci break; 167962306a36Sopenharmony_ci } else if (addr >= (unsigned long) n->buf 168062306a36Sopenharmony_ci && (addr < (unsigned long) n->buf 168162306a36Sopenharmony_ci + resource_size(&n->res))) { 168262306a36Sopenharmony_ci nfit_res = n; 168362306a36Sopenharmony_ci break; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci } 168662306a36Sopenharmony_ci spin_unlock(&nfit_test_lock); 168762306a36Sopenharmony_ci if (nfit_res) 168862306a36Sopenharmony_ci return nfit_res; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci return NULL; 169262306a36Sopenharmony_ci} 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_cistatic int ars_state_init(struct device *dev, struct ars_state *ars_state) 169562306a36Sopenharmony_ci{ 169662306a36Sopenharmony_ci /* for testing, only store up to n records that fit within 4k */ 169762306a36Sopenharmony_ci ars_state->ars_status = devm_kzalloc(dev, 169862306a36Sopenharmony_ci sizeof(struct nd_cmd_ars_status) + SZ_4K, GFP_KERNEL); 169962306a36Sopenharmony_ci if (!ars_state->ars_status) 170062306a36Sopenharmony_ci return -ENOMEM; 170162306a36Sopenharmony_ci spin_lock_init(&ars_state->lock); 170262306a36Sopenharmony_ci return 0; 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_cistatic void put_dimms(void *data) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci struct nfit_test *t = data; 170862306a36Sopenharmony_ci int i; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci for (i = 0; i < t->num_dcr; i++) 171162306a36Sopenharmony_ci if (t->dimm_dev[i]) 171262306a36Sopenharmony_ci device_unregister(t->dimm_dev[i]); 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cistatic struct class *nfit_test_dimm; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_cistatic int dimm_name_to_id(struct device *dev) 171862306a36Sopenharmony_ci{ 171962306a36Sopenharmony_ci int dimm; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci if (sscanf(dev_name(dev), "test_dimm%d", &dimm) != 1) 172262306a36Sopenharmony_ci return -ENXIO; 172362306a36Sopenharmony_ci return dimm; 172462306a36Sopenharmony_ci} 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_cistatic ssize_t handle_show(struct device *dev, struct device_attribute *attr, 172762306a36Sopenharmony_ci char *buf) 172862306a36Sopenharmony_ci{ 172962306a36Sopenharmony_ci int dimm = dimm_name_to_id(dev); 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (dimm < 0) 173262306a36Sopenharmony_ci return dimm; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci return sprintf(buf, "%#x\n", handle[dimm]); 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ciDEVICE_ATTR_RO(handle); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_cistatic ssize_t fail_cmd_show(struct device *dev, struct device_attribute *attr, 173962306a36Sopenharmony_ci char *buf) 174062306a36Sopenharmony_ci{ 174162306a36Sopenharmony_ci int dimm = dimm_name_to_id(dev); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci if (dimm < 0) 174462306a36Sopenharmony_ci return dimm; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci return sprintf(buf, "%#lx\n", dimm_fail_cmd_flags[dimm]); 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_cistatic ssize_t fail_cmd_store(struct device *dev, struct device_attribute *attr, 175062306a36Sopenharmony_ci const char *buf, size_t size) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci int dimm = dimm_name_to_id(dev); 175362306a36Sopenharmony_ci unsigned long val; 175462306a36Sopenharmony_ci ssize_t rc; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci if (dimm < 0) 175762306a36Sopenharmony_ci return dimm; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci rc = kstrtol(buf, 0, &val); 176062306a36Sopenharmony_ci if (rc) 176162306a36Sopenharmony_ci return rc; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci dimm_fail_cmd_flags[dimm] = val; 176462306a36Sopenharmony_ci return size; 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(fail_cmd); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic ssize_t fail_cmd_code_show(struct device *dev, struct device_attribute *attr, 176962306a36Sopenharmony_ci char *buf) 177062306a36Sopenharmony_ci{ 177162306a36Sopenharmony_ci int dimm = dimm_name_to_id(dev); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci if (dimm < 0) 177462306a36Sopenharmony_ci return dimm; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci return sprintf(buf, "%d\n", dimm_fail_cmd_code[dimm]); 177762306a36Sopenharmony_ci} 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cistatic ssize_t fail_cmd_code_store(struct device *dev, struct device_attribute *attr, 178062306a36Sopenharmony_ci const char *buf, size_t size) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci int dimm = dimm_name_to_id(dev); 178362306a36Sopenharmony_ci unsigned long val; 178462306a36Sopenharmony_ci ssize_t rc; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci if (dimm < 0) 178762306a36Sopenharmony_ci return dimm; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci rc = kstrtol(buf, 0, &val); 179062306a36Sopenharmony_ci if (rc) 179162306a36Sopenharmony_ci return rc; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci dimm_fail_cmd_code[dimm] = val; 179462306a36Sopenharmony_ci return size; 179562306a36Sopenharmony_ci} 179662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(fail_cmd_code); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_cistatic ssize_t lock_dimm_store(struct device *dev, 179962306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 180062306a36Sopenharmony_ci{ 180162306a36Sopenharmony_ci int dimm = dimm_name_to_id(dev); 180262306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[dimm]; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci sec->state = ND_INTEL_SEC_STATE_ENABLED | ND_INTEL_SEC_STATE_LOCKED; 180562306a36Sopenharmony_ci return size; 180662306a36Sopenharmony_ci} 180762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(lock_dimm); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_cistatic struct attribute *nfit_test_dimm_attributes[] = { 181062306a36Sopenharmony_ci &dev_attr_fail_cmd.attr, 181162306a36Sopenharmony_ci &dev_attr_fail_cmd_code.attr, 181262306a36Sopenharmony_ci &dev_attr_handle.attr, 181362306a36Sopenharmony_ci &dev_attr_lock_dimm.attr, 181462306a36Sopenharmony_ci NULL, 181562306a36Sopenharmony_ci}; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_cistatic struct attribute_group nfit_test_dimm_attribute_group = { 181862306a36Sopenharmony_ci .attrs = nfit_test_dimm_attributes, 181962306a36Sopenharmony_ci}; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_cistatic const struct attribute_group *nfit_test_dimm_attribute_groups[] = { 182262306a36Sopenharmony_ci &nfit_test_dimm_attribute_group, 182362306a36Sopenharmony_ci NULL, 182462306a36Sopenharmony_ci}; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_cistatic int nfit_test_dimm_init(struct nfit_test *t) 182762306a36Sopenharmony_ci{ 182862306a36Sopenharmony_ci int i; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci if (devm_add_action_or_reset(&t->pdev.dev, put_dimms, t)) 183162306a36Sopenharmony_ci return -ENOMEM; 183262306a36Sopenharmony_ci for (i = 0; i < t->num_dcr; i++) { 183362306a36Sopenharmony_ci t->dimm_dev[i] = device_create_with_groups(nfit_test_dimm, 183462306a36Sopenharmony_ci &t->pdev.dev, 0, NULL, 183562306a36Sopenharmony_ci nfit_test_dimm_attribute_groups, 183662306a36Sopenharmony_ci "test_dimm%d", i + t->dcr_idx); 183762306a36Sopenharmony_ci if (!t->dimm_dev[i]) 183862306a36Sopenharmony_ci return -ENOMEM; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci return 0; 184162306a36Sopenharmony_ci} 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_cistatic void nfit_security_init(struct nfit_test *t) 184462306a36Sopenharmony_ci{ 184562306a36Sopenharmony_ci int i; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci for (i = 0; i < t->num_dcr; i++) { 184862306a36Sopenharmony_ci struct nfit_test_sec *sec = &dimm_sec_info[i]; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; 185162306a36Sopenharmony_ci } 185262306a36Sopenharmony_ci} 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_cistatic void smart_init(struct nfit_test *t) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci int i; 185762306a36Sopenharmony_ci const struct nd_intel_smart_threshold smart_t_data = { 185862306a36Sopenharmony_ci .alarm_control = ND_INTEL_SMART_SPARE_TRIP 185962306a36Sopenharmony_ci | ND_INTEL_SMART_TEMP_TRIP, 186062306a36Sopenharmony_ci .media_temperature = 40 * 16, 186162306a36Sopenharmony_ci .ctrl_temperature = 30 * 16, 186262306a36Sopenharmony_ci .spares = 5, 186362306a36Sopenharmony_ci }; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci for (i = 0; i < t->num_dcr; i++) { 186662306a36Sopenharmony_ci memcpy(&t->smart[i], &smart_def, sizeof(smart_def)); 186762306a36Sopenharmony_ci memcpy(&t->smart_threshold[i], &smart_t_data, 186862306a36Sopenharmony_ci sizeof(smart_t_data)); 186962306a36Sopenharmony_ci } 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistatic size_t sizeof_spa(struct acpi_nfit_system_address *spa) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci /* until spa location cookie support is added... */ 187562306a36Sopenharmony_ci return sizeof(*spa) - 8; 187662306a36Sopenharmony_ci} 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_cistatic int nfit_test0_alloc(struct nfit_test *t) 187962306a36Sopenharmony_ci{ 188062306a36Sopenharmony_ci struct acpi_nfit_system_address *spa = NULL; 188162306a36Sopenharmony_ci struct acpi_nfit_flush_address *flush; 188262306a36Sopenharmony_ci size_t nfit_size = sizeof_spa(spa) * NUM_SPA 188362306a36Sopenharmony_ci + sizeof(struct acpi_nfit_memory_map) * NUM_MEM 188462306a36Sopenharmony_ci + sizeof(struct acpi_nfit_control_region) * NUM_DCR 188562306a36Sopenharmony_ci + offsetof(struct acpi_nfit_control_region, 188662306a36Sopenharmony_ci window_size) * NUM_DCR 188762306a36Sopenharmony_ci + sizeof(struct acpi_nfit_data_region) * NUM_BDW 188862306a36Sopenharmony_ci + struct_size(flush, hint_address, NUM_HINTS) * NUM_DCR 188962306a36Sopenharmony_ci + sizeof(struct acpi_nfit_capabilities); 189062306a36Sopenharmony_ci int i; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci t->nfit_buf = test_alloc(t, nfit_size, &t->nfit_dma); 189362306a36Sopenharmony_ci if (!t->nfit_buf) 189462306a36Sopenharmony_ci return -ENOMEM; 189562306a36Sopenharmony_ci t->nfit_size = nfit_size; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci t->spa_set[0] = test_alloc(t, SPA0_SIZE, &t->spa_set_dma[0]); 189862306a36Sopenharmony_ci if (!t->spa_set[0]) 189962306a36Sopenharmony_ci return -ENOMEM; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci t->spa_set[1] = test_alloc(t, SPA1_SIZE, &t->spa_set_dma[1]); 190262306a36Sopenharmony_ci if (!t->spa_set[1]) 190362306a36Sopenharmony_ci return -ENOMEM; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci t->spa_set[2] = test_alloc(t, SPA0_SIZE, &t->spa_set_dma[2]); 190662306a36Sopenharmony_ci if (!t->spa_set[2]) 190762306a36Sopenharmony_ci return -ENOMEM; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci for (i = 0; i < t->num_dcr; i++) { 191062306a36Sopenharmony_ci t->dimm[i] = test_alloc(t, DIMM_SIZE, &t->dimm_dma[i]); 191162306a36Sopenharmony_ci if (!t->dimm[i]) 191262306a36Sopenharmony_ci return -ENOMEM; 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci t->label[i] = test_alloc(t, LABEL_SIZE, &t->label_dma[i]); 191562306a36Sopenharmony_ci if (!t->label[i]) 191662306a36Sopenharmony_ci return -ENOMEM; 191762306a36Sopenharmony_ci sprintf(t->label[i], "label%d", i); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci t->flush[i] = test_alloc(t, max(PAGE_SIZE, 192062306a36Sopenharmony_ci sizeof(u64) * NUM_HINTS), 192162306a36Sopenharmony_ci &t->flush_dma[i]); 192262306a36Sopenharmony_ci if (!t->flush[i]) 192362306a36Sopenharmony_ci return -ENOMEM; 192462306a36Sopenharmony_ci } 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci for (i = 0; i < t->num_dcr; i++) { 192762306a36Sopenharmony_ci t->dcr[i] = test_alloc(t, LABEL_SIZE, &t->dcr_dma[i]); 192862306a36Sopenharmony_ci if (!t->dcr[i]) 192962306a36Sopenharmony_ci return -ENOMEM; 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci t->_fit = test_alloc(t, sizeof(union acpi_object **), &t->_fit_dma); 193362306a36Sopenharmony_ci if (!t->_fit) 193462306a36Sopenharmony_ci return -ENOMEM; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci if (nfit_test_dimm_init(t)) 193762306a36Sopenharmony_ci return -ENOMEM; 193862306a36Sopenharmony_ci smart_init(t); 193962306a36Sopenharmony_ci nfit_security_init(t); 194062306a36Sopenharmony_ci return ars_state_init(&t->pdev.dev, &t->ars_state); 194162306a36Sopenharmony_ci} 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_cistatic int nfit_test1_alloc(struct nfit_test *t) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci struct acpi_nfit_system_address *spa = NULL; 194662306a36Sopenharmony_ci size_t nfit_size = sizeof_spa(spa) * 2 194762306a36Sopenharmony_ci + sizeof(struct acpi_nfit_memory_map) * 2 194862306a36Sopenharmony_ci + offsetof(struct acpi_nfit_control_region, window_size) * 2; 194962306a36Sopenharmony_ci int i; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci t->nfit_buf = test_alloc(t, nfit_size, &t->nfit_dma); 195262306a36Sopenharmony_ci if (!t->nfit_buf) 195362306a36Sopenharmony_ci return -ENOMEM; 195462306a36Sopenharmony_ci t->nfit_size = nfit_size; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci t->spa_set[0] = test_alloc(t, SPA2_SIZE, &t->spa_set_dma[0]); 195762306a36Sopenharmony_ci if (!t->spa_set[0]) 195862306a36Sopenharmony_ci return -ENOMEM; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci for (i = 0; i < t->num_dcr; i++) { 196162306a36Sopenharmony_ci t->label[i] = test_alloc(t, LABEL_SIZE, &t->label_dma[i]); 196262306a36Sopenharmony_ci if (!t->label[i]) 196362306a36Sopenharmony_ci return -ENOMEM; 196462306a36Sopenharmony_ci sprintf(t->label[i], "label%d", i); 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci t->spa_set[1] = test_alloc(t, SPA_VCD_SIZE, &t->spa_set_dma[1]); 196862306a36Sopenharmony_ci if (!t->spa_set[1]) 196962306a36Sopenharmony_ci return -ENOMEM; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci if (nfit_test_dimm_init(t)) 197262306a36Sopenharmony_ci return -ENOMEM; 197362306a36Sopenharmony_ci smart_init(t); 197462306a36Sopenharmony_ci return ars_state_init(&t->pdev.dev, &t->ars_state); 197562306a36Sopenharmony_ci} 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_cistatic void dcr_common_init(struct acpi_nfit_control_region *dcr) 197862306a36Sopenharmony_ci{ 197962306a36Sopenharmony_ci dcr->vendor_id = 0xabcd; 198062306a36Sopenharmony_ci dcr->device_id = 0; 198162306a36Sopenharmony_ci dcr->revision_id = 1; 198262306a36Sopenharmony_ci dcr->valid_fields = 1; 198362306a36Sopenharmony_ci dcr->manufacturing_location = 0xa; 198462306a36Sopenharmony_ci dcr->manufacturing_date = cpu_to_be16(2016); 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_cistatic void nfit_test0_setup(struct nfit_test *t) 198862306a36Sopenharmony_ci{ 198962306a36Sopenharmony_ci const int flush_hint_size = sizeof(struct acpi_nfit_flush_address) 199062306a36Sopenharmony_ci + (sizeof(u64) * NUM_HINTS); 199162306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc; 199262306a36Sopenharmony_ci struct acpi_nfit_memory_map *memdev; 199362306a36Sopenharmony_ci void *nfit_buf = t->nfit_buf; 199462306a36Sopenharmony_ci struct acpi_nfit_system_address *spa; 199562306a36Sopenharmony_ci struct acpi_nfit_control_region *dcr; 199662306a36Sopenharmony_ci struct acpi_nfit_data_region *bdw; 199762306a36Sopenharmony_ci struct acpi_nfit_flush_address *flush; 199862306a36Sopenharmony_ci struct acpi_nfit_capabilities *pcap; 199962306a36Sopenharmony_ci unsigned int offset = 0, i; 200062306a36Sopenharmony_ci unsigned long *acpi_mask; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci /* 200362306a36Sopenharmony_ci * spa0 (interleave first half of dimm0 and dimm1, note storage 200462306a36Sopenharmony_ci * does not actually alias the related block-data-window 200562306a36Sopenharmony_ci * regions) 200662306a36Sopenharmony_ci */ 200762306a36Sopenharmony_ci spa = nfit_buf; 200862306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 200962306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 201062306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16); 201162306a36Sopenharmony_ci spa->range_index = 0+1; 201262306a36Sopenharmony_ci spa->address = t->spa_set_dma[0]; 201362306a36Sopenharmony_ci spa->length = SPA0_SIZE; 201462306a36Sopenharmony_ci offset += spa->header.length; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci /* 201762306a36Sopenharmony_ci * spa1 (interleave last half of the 4 DIMMS, note storage 201862306a36Sopenharmony_ci * does not actually alias the related block-data-window 201962306a36Sopenharmony_ci * regions) 202062306a36Sopenharmony_ci */ 202162306a36Sopenharmony_ci spa = nfit_buf + offset; 202262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 202362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 202462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16); 202562306a36Sopenharmony_ci spa->range_index = 1+1; 202662306a36Sopenharmony_ci spa->address = t->spa_set_dma[1]; 202762306a36Sopenharmony_ci spa->length = SPA1_SIZE; 202862306a36Sopenharmony_ci offset += spa->header.length; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci /* spa2 (dcr0) dimm0 */ 203162306a36Sopenharmony_ci spa = nfit_buf + offset; 203262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 203362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 203462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); 203562306a36Sopenharmony_ci spa->range_index = 2+1; 203662306a36Sopenharmony_ci spa->address = t->dcr_dma[0]; 203762306a36Sopenharmony_ci spa->length = DCR_SIZE; 203862306a36Sopenharmony_ci offset += spa->header.length; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci /* spa3 (dcr1) dimm1 */ 204162306a36Sopenharmony_ci spa = nfit_buf + offset; 204262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 204362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 204462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); 204562306a36Sopenharmony_ci spa->range_index = 3+1; 204662306a36Sopenharmony_ci spa->address = t->dcr_dma[1]; 204762306a36Sopenharmony_ci spa->length = DCR_SIZE; 204862306a36Sopenharmony_ci offset += spa->header.length; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci /* spa4 (dcr2) dimm2 */ 205162306a36Sopenharmony_ci spa = nfit_buf + offset; 205262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 205362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 205462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); 205562306a36Sopenharmony_ci spa->range_index = 4+1; 205662306a36Sopenharmony_ci spa->address = t->dcr_dma[2]; 205762306a36Sopenharmony_ci spa->length = DCR_SIZE; 205862306a36Sopenharmony_ci offset += spa->header.length; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci /* spa5 (dcr3) dimm3 */ 206162306a36Sopenharmony_ci spa = nfit_buf + offset; 206262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 206362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 206462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); 206562306a36Sopenharmony_ci spa->range_index = 5+1; 206662306a36Sopenharmony_ci spa->address = t->dcr_dma[3]; 206762306a36Sopenharmony_ci spa->length = DCR_SIZE; 206862306a36Sopenharmony_ci offset += spa->header.length; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci /* spa6 (bdw for dcr0) dimm0 */ 207162306a36Sopenharmony_ci spa = nfit_buf + offset; 207262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 207362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 207462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); 207562306a36Sopenharmony_ci spa->range_index = 6+1; 207662306a36Sopenharmony_ci spa->address = t->dimm_dma[0]; 207762306a36Sopenharmony_ci spa->length = DIMM_SIZE; 207862306a36Sopenharmony_ci offset += spa->header.length; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci /* spa7 (bdw for dcr1) dimm1 */ 208162306a36Sopenharmony_ci spa = nfit_buf + offset; 208262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 208362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 208462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); 208562306a36Sopenharmony_ci spa->range_index = 7+1; 208662306a36Sopenharmony_ci spa->address = t->dimm_dma[1]; 208762306a36Sopenharmony_ci spa->length = DIMM_SIZE; 208862306a36Sopenharmony_ci offset += spa->header.length; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci /* spa8 (bdw for dcr2) dimm2 */ 209162306a36Sopenharmony_ci spa = nfit_buf + offset; 209262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 209362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 209462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); 209562306a36Sopenharmony_ci spa->range_index = 8+1; 209662306a36Sopenharmony_ci spa->address = t->dimm_dma[2]; 209762306a36Sopenharmony_ci spa->length = DIMM_SIZE; 209862306a36Sopenharmony_ci offset += spa->header.length; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci /* spa9 (bdw for dcr3) dimm3 */ 210162306a36Sopenharmony_ci spa = nfit_buf + offset; 210262306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 210362306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 210462306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); 210562306a36Sopenharmony_ci spa->range_index = 9+1; 210662306a36Sopenharmony_ci spa->address = t->dimm_dma[3]; 210762306a36Sopenharmony_ci spa->length = DIMM_SIZE; 210862306a36Sopenharmony_ci offset += spa->header.length; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci /* mem-region0 (spa0, dimm0) */ 211162306a36Sopenharmony_ci memdev = nfit_buf + offset; 211262306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 211362306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 211462306a36Sopenharmony_ci memdev->device_handle = handle[0]; 211562306a36Sopenharmony_ci memdev->physical_id = 0; 211662306a36Sopenharmony_ci memdev->region_id = 0; 211762306a36Sopenharmony_ci memdev->range_index = 0+1; 211862306a36Sopenharmony_ci memdev->region_index = 4+1; 211962306a36Sopenharmony_ci memdev->region_size = SPA0_SIZE/2; 212062306a36Sopenharmony_ci memdev->region_offset = 1; 212162306a36Sopenharmony_ci memdev->address = 0; 212262306a36Sopenharmony_ci memdev->interleave_index = 0; 212362306a36Sopenharmony_ci memdev->interleave_ways = 2; 212462306a36Sopenharmony_ci offset += memdev->header.length; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci /* mem-region1 (spa0, dimm1) */ 212762306a36Sopenharmony_ci memdev = nfit_buf + offset; 212862306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 212962306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 213062306a36Sopenharmony_ci memdev->device_handle = handle[1]; 213162306a36Sopenharmony_ci memdev->physical_id = 1; 213262306a36Sopenharmony_ci memdev->region_id = 0; 213362306a36Sopenharmony_ci memdev->range_index = 0+1; 213462306a36Sopenharmony_ci memdev->region_index = 5+1; 213562306a36Sopenharmony_ci memdev->region_size = SPA0_SIZE/2; 213662306a36Sopenharmony_ci memdev->region_offset = (1 << 8); 213762306a36Sopenharmony_ci memdev->address = 0; 213862306a36Sopenharmony_ci memdev->interleave_index = 0; 213962306a36Sopenharmony_ci memdev->interleave_ways = 2; 214062306a36Sopenharmony_ci memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; 214162306a36Sopenharmony_ci offset += memdev->header.length; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci /* mem-region2 (spa1, dimm0) */ 214462306a36Sopenharmony_ci memdev = nfit_buf + offset; 214562306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 214662306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 214762306a36Sopenharmony_ci memdev->device_handle = handle[0]; 214862306a36Sopenharmony_ci memdev->physical_id = 0; 214962306a36Sopenharmony_ci memdev->region_id = 1; 215062306a36Sopenharmony_ci memdev->range_index = 1+1; 215162306a36Sopenharmony_ci memdev->region_index = 4+1; 215262306a36Sopenharmony_ci memdev->region_size = SPA1_SIZE/4; 215362306a36Sopenharmony_ci memdev->region_offset = (1 << 16); 215462306a36Sopenharmony_ci memdev->address = SPA0_SIZE/2; 215562306a36Sopenharmony_ci memdev->interleave_index = 0; 215662306a36Sopenharmony_ci memdev->interleave_ways = 4; 215762306a36Sopenharmony_ci memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; 215862306a36Sopenharmony_ci offset += memdev->header.length; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci /* mem-region3 (spa1, dimm1) */ 216162306a36Sopenharmony_ci memdev = nfit_buf + offset; 216262306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 216362306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 216462306a36Sopenharmony_ci memdev->device_handle = handle[1]; 216562306a36Sopenharmony_ci memdev->physical_id = 1; 216662306a36Sopenharmony_ci memdev->region_id = 1; 216762306a36Sopenharmony_ci memdev->range_index = 1+1; 216862306a36Sopenharmony_ci memdev->region_index = 5+1; 216962306a36Sopenharmony_ci memdev->region_size = SPA1_SIZE/4; 217062306a36Sopenharmony_ci memdev->region_offset = (1 << 24); 217162306a36Sopenharmony_ci memdev->address = SPA0_SIZE/2; 217262306a36Sopenharmony_ci memdev->interleave_index = 0; 217362306a36Sopenharmony_ci memdev->interleave_ways = 4; 217462306a36Sopenharmony_ci offset += memdev->header.length; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci /* mem-region4 (spa1, dimm2) */ 217762306a36Sopenharmony_ci memdev = nfit_buf + offset; 217862306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 217962306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 218062306a36Sopenharmony_ci memdev->device_handle = handle[2]; 218162306a36Sopenharmony_ci memdev->physical_id = 2; 218262306a36Sopenharmony_ci memdev->region_id = 0; 218362306a36Sopenharmony_ci memdev->range_index = 1+1; 218462306a36Sopenharmony_ci memdev->region_index = 6+1; 218562306a36Sopenharmony_ci memdev->region_size = SPA1_SIZE/4; 218662306a36Sopenharmony_ci memdev->region_offset = (1ULL << 32); 218762306a36Sopenharmony_ci memdev->address = SPA0_SIZE/2; 218862306a36Sopenharmony_ci memdev->interleave_index = 0; 218962306a36Sopenharmony_ci memdev->interleave_ways = 4; 219062306a36Sopenharmony_ci memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; 219162306a36Sopenharmony_ci offset += memdev->header.length; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci /* mem-region5 (spa1, dimm3) */ 219462306a36Sopenharmony_ci memdev = nfit_buf + offset; 219562306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 219662306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 219762306a36Sopenharmony_ci memdev->device_handle = handle[3]; 219862306a36Sopenharmony_ci memdev->physical_id = 3; 219962306a36Sopenharmony_ci memdev->region_id = 0; 220062306a36Sopenharmony_ci memdev->range_index = 1+1; 220162306a36Sopenharmony_ci memdev->region_index = 7+1; 220262306a36Sopenharmony_ci memdev->region_size = SPA1_SIZE/4; 220362306a36Sopenharmony_ci memdev->region_offset = (1ULL << 40); 220462306a36Sopenharmony_ci memdev->address = SPA0_SIZE/2; 220562306a36Sopenharmony_ci memdev->interleave_index = 0; 220662306a36Sopenharmony_ci memdev->interleave_ways = 4; 220762306a36Sopenharmony_ci offset += memdev->header.length; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci /* mem-region6 (spa/dcr0, dimm0) */ 221062306a36Sopenharmony_ci memdev = nfit_buf + offset; 221162306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 221262306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 221362306a36Sopenharmony_ci memdev->device_handle = handle[0]; 221462306a36Sopenharmony_ci memdev->physical_id = 0; 221562306a36Sopenharmony_ci memdev->region_id = 0; 221662306a36Sopenharmony_ci memdev->range_index = 2+1; 221762306a36Sopenharmony_ci memdev->region_index = 0+1; 221862306a36Sopenharmony_ci memdev->region_size = 0; 221962306a36Sopenharmony_ci memdev->region_offset = 0; 222062306a36Sopenharmony_ci memdev->address = 0; 222162306a36Sopenharmony_ci memdev->interleave_index = 0; 222262306a36Sopenharmony_ci memdev->interleave_ways = 1; 222362306a36Sopenharmony_ci offset += memdev->header.length; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci /* mem-region7 (spa/dcr1, dimm1) */ 222662306a36Sopenharmony_ci memdev = nfit_buf + offset; 222762306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 222862306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 222962306a36Sopenharmony_ci memdev->device_handle = handle[1]; 223062306a36Sopenharmony_ci memdev->physical_id = 1; 223162306a36Sopenharmony_ci memdev->region_id = 0; 223262306a36Sopenharmony_ci memdev->range_index = 3+1; 223362306a36Sopenharmony_ci memdev->region_index = 1+1; 223462306a36Sopenharmony_ci memdev->region_size = 0; 223562306a36Sopenharmony_ci memdev->region_offset = 0; 223662306a36Sopenharmony_ci memdev->address = 0; 223762306a36Sopenharmony_ci memdev->interleave_index = 0; 223862306a36Sopenharmony_ci memdev->interleave_ways = 1; 223962306a36Sopenharmony_ci offset += memdev->header.length; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci /* mem-region8 (spa/dcr2, dimm2) */ 224262306a36Sopenharmony_ci memdev = nfit_buf + offset; 224362306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 224462306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 224562306a36Sopenharmony_ci memdev->device_handle = handle[2]; 224662306a36Sopenharmony_ci memdev->physical_id = 2; 224762306a36Sopenharmony_ci memdev->region_id = 0; 224862306a36Sopenharmony_ci memdev->range_index = 4+1; 224962306a36Sopenharmony_ci memdev->region_index = 2+1; 225062306a36Sopenharmony_ci memdev->region_size = 0; 225162306a36Sopenharmony_ci memdev->region_offset = 0; 225262306a36Sopenharmony_ci memdev->address = 0; 225362306a36Sopenharmony_ci memdev->interleave_index = 0; 225462306a36Sopenharmony_ci memdev->interleave_ways = 1; 225562306a36Sopenharmony_ci offset += memdev->header.length; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci /* mem-region9 (spa/dcr3, dimm3) */ 225862306a36Sopenharmony_ci memdev = nfit_buf + offset; 225962306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 226062306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 226162306a36Sopenharmony_ci memdev->device_handle = handle[3]; 226262306a36Sopenharmony_ci memdev->physical_id = 3; 226362306a36Sopenharmony_ci memdev->region_id = 0; 226462306a36Sopenharmony_ci memdev->range_index = 5+1; 226562306a36Sopenharmony_ci memdev->region_index = 3+1; 226662306a36Sopenharmony_ci memdev->region_size = 0; 226762306a36Sopenharmony_ci memdev->region_offset = 0; 226862306a36Sopenharmony_ci memdev->address = 0; 226962306a36Sopenharmony_ci memdev->interleave_index = 0; 227062306a36Sopenharmony_ci memdev->interleave_ways = 1; 227162306a36Sopenharmony_ci offset += memdev->header.length; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci /* mem-region10 (spa/bdw0, dimm0) */ 227462306a36Sopenharmony_ci memdev = nfit_buf + offset; 227562306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 227662306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 227762306a36Sopenharmony_ci memdev->device_handle = handle[0]; 227862306a36Sopenharmony_ci memdev->physical_id = 0; 227962306a36Sopenharmony_ci memdev->region_id = 0; 228062306a36Sopenharmony_ci memdev->range_index = 6+1; 228162306a36Sopenharmony_ci memdev->region_index = 0+1; 228262306a36Sopenharmony_ci memdev->region_size = 0; 228362306a36Sopenharmony_ci memdev->region_offset = 0; 228462306a36Sopenharmony_ci memdev->address = 0; 228562306a36Sopenharmony_ci memdev->interleave_index = 0; 228662306a36Sopenharmony_ci memdev->interleave_ways = 1; 228762306a36Sopenharmony_ci offset += memdev->header.length; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci /* mem-region11 (spa/bdw1, dimm1) */ 229062306a36Sopenharmony_ci memdev = nfit_buf + offset; 229162306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 229262306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 229362306a36Sopenharmony_ci memdev->device_handle = handle[1]; 229462306a36Sopenharmony_ci memdev->physical_id = 1; 229562306a36Sopenharmony_ci memdev->region_id = 0; 229662306a36Sopenharmony_ci memdev->range_index = 7+1; 229762306a36Sopenharmony_ci memdev->region_index = 1+1; 229862306a36Sopenharmony_ci memdev->region_size = 0; 229962306a36Sopenharmony_ci memdev->region_offset = 0; 230062306a36Sopenharmony_ci memdev->address = 0; 230162306a36Sopenharmony_ci memdev->interleave_index = 0; 230262306a36Sopenharmony_ci memdev->interleave_ways = 1; 230362306a36Sopenharmony_ci offset += memdev->header.length; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci /* mem-region12 (spa/bdw2, dimm2) */ 230662306a36Sopenharmony_ci memdev = nfit_buf + offset; 230762306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 230862306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 230962306a36Sopenharmony_ci memdev->device_handle = handle[2]; 231062306a36Sopenharmony_ci memdev->physical_id = 2; 231162306a36Sopenharmony_ci memdev->region_id = 0; 231262306a36Sopenharmony_ci memdev->range_index = 8+1; 231362306a36Sopenharmony_ci memdev->region_index = 2+1; 231462306a36Sopenharmony_ci memdev->region_size = 0; 231562306a36Sopenharmony_ci memdev->region_offset = 0; 231662306a36Sopenharmony_ci memdev->address = 0; 231762306a36Sopenharmony_ci memdev->interleave_index = 0; 231862306a36Sopenharmony_ci memdev->interleave_ways = 1; 231962306a36Sopenharmony_ci offset += memdev->header.length; 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci /* mem-region13 (spa/dcr3, dimm3) */ 232262306a36Sopenharmony_ci memdev = nfit_buf + offset; 232362306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 232462306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 232562306a36Sopenharmony_ci memdev->device_handle = handle[3]; 232662306a36Sopenharmony_ci memdev->physical_id = 3; 232762306a36Sopenharmony_ci memdev->region_id = 0; 232862306a36Sopenharmony_ci memdev->range_index = 9+1; 232962306a36Sopenharmony_ci memdev->region_index = 3+1; 233062306a36Sopenharmony_ci memdev->region_size = 0; 233162306a36Sopenharmony_ci memdev->region_offset = 0; 233262306a36Sopenharmony_ci memdev->address = 0; 233362306a36Sopenharmony_ci memdev->interleave_index = 0; 233462306a36Sopenharmony_ci memdev->interleave_ways = 1; 233562306a36Sopenharmony_ci memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; 233662306a36Sopenharmony_ci offset += memdev->header.length; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci /* dcr-descriptor0: blk */ 233962306a36Sopenharmony_ci dcr = nfit_buf + offset; 234062306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 234162306a36Sopenharmony_ci dcr->header.length = sizeof(*dcr); 234262306a36Sopenharmony_ci dcr->region_index = 0+1; 234362306a36Sopenharmony_ci dcr_common_init(dcr); 234462306a36Sopenharmony_ci dcr->serial_number = ~handle[0]; 234562306a36Sopenharmony_ci dcr->code = NFIT_FIC_BLK; 234662306a36Sopenharmony_ci dcr->windows = 1; 234762306a36Sopenharmony_ci dcr->window_size = DCR_SIZE; 234862306a36Sopenharmony_ci dcr->command_offset = 0; 234962306a36Sopenharmony_ci dcr->command_size = 8; 235062306a36Sopenharmony_ci dcr->status_offset = 8; 235162306a36Sopenharmony_ci dcr->status_size = 4; 235262306a36Sopenharmony_ci offset += dcr->header.length; 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci /* dcr-descriptor1: blk */ 235562306a36Sopenharmony_ci dcr = nfit_buf + offset; 235662306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 235762306a36Sopenharmony_ci dcr->header.length = sizeof(*dcr); 235862306a36Sopenharmony_ci dcr->region_index = 1+1; 235962306a36Sopenharmony_ci dcr_common_init(dcr); 236062306a36Sopenharmony_ci dcr->serial_number = ~handle[1]; 236162306a36Sopenharmony_ci dcr->code = NFIT_FIC_BLK; 236262306a36Sopenharmony_ci dcr->windows = 1; 236362306a36Sopenharmony_ci dcr->window_size = DCR_SIZE; 236462306a36Sopenharmony_ci dcr->command_offset = 0; 236562306a36Sopenharmony_ci dcr->command_size = 8; 236662306a36Sopenharmony_ci dcr->status_offset = 8; 236762306a36Sopenharmony_ci dcr->status_size = 4; 236862306a36Sopenharmony_ci offset += dcr->header.length; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci /* dcr-descriptor2: blk */ 237162306a36Sopenharmony_ci dcr = nfit_buf + offset; 237262306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 237362306a36Sopenharmony_ci dcr->header.length = sizeof(*dcr); 237462306a36Sopenharmony_ci dcr->region_index = 2+1; 237562306a36Sopenharmony_ci dcr_common_init(dcr); 237662306a36Sopenharmony_ci dcr->serial_number = ~handle[2]; 237762306a36Sopenharmony_ci dcr->code = NFIT_FIC_BLK; 237862306a36Sopenharmony_ci dcr->windows = 1; 237962306a36Sopenharmony_ci dcr->window_size = DCR_SIZE; 238062306a36Sopenharmony_ci dcr->command_offset = 0; 238162306a36Sopenharmony_ci dcr->command_size = 8; 238262306a36Sopenharmony_ci dcr->status_offset = 8; 238362306a36Sopenharmony_ci dcr->status_size = 4; 238462306a36Sopenharmony_ci offset += dcr->header.length; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci /* dcr-descriptor3: blk */ 238762306a36Sopenharmony_ci dcr = nfit_buf + offset; 238862306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 238962306a36Sopenharmony_ci dcr->header.length = sizeof(*dcr); 239062306a36Sopenharmony_ci dcr->region_index = 3+1; 239162306a36Sopenharmony_ci dcr_common_init(dcr); 239262306a36Sopenharmony_ci dcr->serial_number = ~handle[3]; 239362306a36Sopenharmony_ci dcr->code = NFIT_FIC_BLK; 239462306a36Sopenharmony_ci dcr->windows = 1; 239562306a36Sopenharmony_ci dcr->window_size = DCR_SIZE; 239662306a36Sopenharmony_ci dcr->command_offset = 0; 239762306a36Sopenharmony_ci dcr->command_size = 8; 239862306a36Sopenharmony_ci dcr->status_offset = 8; 239962306a36Sopenharmony_ci dcr->status_size = 4; 240062306a36Sopenharmony_ci offset += dcr->header.length; 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci /* dcr-descriptor0: pmem */ 240362306a36Sopenharmony_ci dcr = nfit_buf + offset; 240462306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 240562306a36Sopenharmony_ci dcr->header.length = offsetof(struct acpi_nfit_control_region, 240662306a36Sopenharmony_ci window_size); 240762306a36Sopenharmony_ci dcr->region_index = 4+1; 240862306a36Sopenharmony_ci dcr_common_init(dcr); 240962306a36Sopenharmony_ci dcr->serial_number = ~handle[0]; 241062306a36Sopenharmony_ci dcr->code = NFIT_FIC_BYTEN; 241162306a36Sopenharmony_ci dcr->windows = 0; 241262306a36Sopenharmony_ci offset += dcr->header.length; 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci /* dcr-descriptor1: pmem */ 241562306a36Sopenharmony_ci dcr = nfit_buf + offset; 241662306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 241762306a36Sopenharmony_ci dcr->header.length = offsetof(struct acpi_nfit_control_region, 241862306a36Sopenharmony_ci window_size); 241962306a36Sopenharmony_ci dcr->region_index = 5+1; 242062306a36Sopenharmony_ci dcr_common_init(dcr); 242162306a36Sopenharmony_ci dcr->serial_number = ~handle[1]; 242262306a36Sopenharmony_ci dcr->code = NFIT_FIC_BYTEN; 242362306a36Sopenharmony_ci dcr->windows = 0; 242462306a36Sopenharmony_ci offset += dcr->header.length; 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci /* dcr-descriptor2: pmem */ 242762306a36Sopenharmony_ci dcr = nfit_buf + offset; 242862306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 242962306a36Sopenharmony_ci dcr->header.length = offsetof(struct acpi_nfit_control_region, 243062306a36Sopenharmony_ci window_size); 243162306a36Sopenharmony_ci dcr->region_index = 6+1; 243262306a36Sopenharmony_ci dcr_common_init(dcr); 243362306a36Sopenharmony_ci dcr->serial_number = ~handle[2]; 243462306a36Sopenharmony_ci dcr->code = NFIT_FIC_BYTEN; 243562306a36Sopenharmony_ci dcr->windows = 0; 243662306a36Sopenharmony_ci offset += dcr->header.length; 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci /* dcr-descriptor3: pmem */ 243962306a36Sopenharmony_ci dcr = nfit_buf + offset; 244062306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 244162306a36Sopenharmony_ci dcr->header.length = offsetof(struct acpi_nfit_control_region, 244262306a36Sopenharmony_ci window_size); 244362306a36Sopenharmony_ci dcr->region_index = 7+1; 244462306a36Sopenharmony_ci dcr_common_init(dcr); 244562306a36Sopenharmony_ci dcr->serial_number = ~handle[3]; 244662306a36Sopenharmony_ci dcr->code = NFIT_FIC_BYTEN; 244762306a36Sopenharmony_ci dcr->windows = 0; 244862306a36Sopenharmony_ci offset += dcr->header.length; 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci /* bdw0 (spa/dcr0, dimm0) */ 245162306a36Sopenharmony_ci bdw = nfit_buf + offset; 245262306a36Sopenharmony_ci bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; 245362306a36Sopenharmony_ci bdw->header.length = sizeof(*bdw); 245462306a36Sopenharmony_ci bdw->region_index = 0+1; 245562306a36Sopenharmony_ci bdw->windows = 1; 245662306a36Sopenharmony_ci bdw->offset = 0; 245762306a36Sopenharmony_ci bdw->size = BDW_SIZE; 245862306a36Sopenharmony_ci bdw->capacity = DIMM_SIZE; 245962306a36Sopenharmony_ci bdw->start_address = 0; 246062306a36Sopenharmony_ci offset += bdw->header.length; 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci /* bdw1 (spa/dcr1, dimm1) */ 246362306a36Sopenharmony_ci bdw = nfit_buf + offset; 246462306a36Sopenharmony_ci bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; 246562306a36Sopenharmony_ci bdw->header.length = sizeof(*bdw); 246662306a36Sopenharmony_ci bdw->region_index = 1+1; 246762306a36Sopenharmony_ci bdw->windows = 1; 246862306a36Sopenharmony_ci bdw->offset = 0; 246962306a36Sopenharmony_ci bdw->size = BDW_SIZE; 247062306a36Sopenharmony_ci bdw->capacity = DIMM_SIZE; 247162306a36Sopenharmony_ci bdw->start_address = 0; 247262306a36Sopenharmony_ci offset += bdw->header.length; 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci /* bdw2 (spa/dcr2, dimm2) */ 247562306a36Sopenharmony_ci bdw = nfit_buf + offset; 247662306a36Sopenharmony_ci bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; 247762306a36Sopenharmony_ci bdw->header.length = sizeof(*bdw); 247862306a36Sopenharmony_ci bdw->region_index = 2+1; 247962306a36Sopenharmony_ci bdw->windows = 1; 248062306a36Sopenharmony_ci bdw->offset = 0; 248162306a36Sopenharmony_ci bdw->size = BDW_SIZE; 248262306a36Sopenharmony_ci bdw->capacity = DIMM_SIZE; 248362306a36Sopenharmony_ci bdw->start_address = 0; 248462306a36Sopenharmony_ci offset += bdw->header.length; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci /* bdw3 (spa/dcr3, dimm3) */ 248762306a36Sopenharmony_ci bdw = nfit_buf + offset; 248862306a36Sopenharmony_ci bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; 248962306a36Sopenharmony_ci bdw->header.length = sizeof(*bdw); 249062306a36Sopenharmony_ci bdw->region_index = 3+1; 249162306a36Sopenharmony_ci bdw->windows = 1; 249262306a36Sopenharmony_ci bdw->offset = 0; 249362306a36Sopenharmony_ci bdw->size = BDW_SIZE; 249462306a36Sopenharmony_ci bdw->capacity = DIMM_SIZE; 249562306a36Sopenharmony_ci bdw->start_address = 0; 249662306a36Sopenharmony_ci offset += bdw->header.length; 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci /* flush0 (dimm0) */ 249962306a36Sopenharmony_ci flush = nfit_buf + offset; 250062306a36Sopenharmony_ci flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; 250162306a36Sopenharmony_ci flush->header.length = flush_hint_size; 250262306a36Sopenharmony_ci flush->device_handle = handle[0]; 250362306a36Sopenharmony_ci flush->hint_count = NUM_HINTS; 250462306a36Sopenharmony_ci for (i = 0; i < NUM_HINTS; i++) 250562306a36Sopenharmony_ci flush->hint_address[i] = t->flush_dma[0] + i * sizeof(u64); 250662306a36Sopenharmony_ci offset += flush->header.length; 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci /* flush1 (dimm1) */ 250962306a36Sopenharmony_ci flush = nfit_buf + offset; 251062306a36Sopenharmony_ci flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; 251162306a36Sopenharmony_ci flush->header.length = flush_hint_size; 251262306a36Sopenharmony_ci flush->device_handle = handle[1]; 251362306a36Sopenharmony_ci flush->hint_count = NUM_HINTS; 251462306a36Sopenharmony_ci for (i = 0; i < NUM_HINTS; i++) 251562306a36Sopenharmony_ci flush->hint_address[i] = t->flush_dma[1] + i * sizeof(u64); 251662306a36Sopenharmony_ci offset += flush->header.length; 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci /* flush2 (dimm2) */ 251962306a36Sopenharmony_ci flush = nfit_buf + offset; 252062306a36Sopenharmony_ci flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; 252162306a36Sopenharmony_ci flush->header.length = flush_hint_size; 252262306a36Sopenharmony_ci flush->device_handle = handle[2]; 252362306a36Sopenharmony_ci flush->hint_count = NUM_HINTS; 252462306a36Sopenharmony_ci for (i = 0; i < NUM_HINTS; i++) 252562306a36Sopenharmony_ci flush->hint_address[i] = t->flush_dma[2] + i * sizeof(u64); 252662306a36Sopenharmony_ci offset += flush->header.length; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci /* flush3 (dimm3) */ 252962306a36Sopenharmony_ci flush = nfit_buf + offset; 253062306a36Sopenharmony_ci flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; 253162306a36Sopenharmony_ci flush->header.length = flush_hint_size; 253262306a36Sopenharmony_ci flush->device_handle = handle[3]; 253362306a36Sopenharmony_ci flush->hint_count = NUM_HINTS; 253462306a36Sopenharmony_ci for (i = 0; i < NUM_HINTS; i++) 253562306a36Sopenharmony_ci flush->hint_address[i] = t->flush_dma[3] + i * sizeof(u64); 253662306a36Sopenharmony_ci offset += flush->header.length; 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci /* platform capabilities */ 253962306a36Sopenharmony_ci pcap = nfit_buf + offset; 254062306a36Sopenharmony_ci pcap->header.type = ACPI_NFIT_TYPE_CAPABILITIES; 254162306a36Sopenharmony_ci pcap->header.length = sizeof(*pcap); 254262306a36Sopenharmony_ci pcap->highest_capability = 1; 254362306a36Sopenharmony_ci pcap->capabilities = ACPI_NFIT_CAPABILITY_MEM_FLUSH; 254462306a36Sopenharmony_ci offset += pcap->header.length; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci if (t->setup_hotplug) { 254762306a36Sopenharmony_ci /* dcr-descriptor4: blk */ 254862306a36Sopenharmony_ci dcr = nfit_buf + offset; 254962306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 255062306a36Sopenharmony_ci dcr->header.length = sizeof(*dcr); 255162306a36Sopenharmony_ci dcr->region_index = 8+1; 255262306a36Sopenharmony_ci dcr_common_init(dcr); 255362306a36Sopenharmony_ci dcr->serial_number = ~handle[4]; 255462306a36Sopenharmony_ci dcr->code = NFIT_FIC_BLK; 255562306a36Sopenharmony_ci dcr->windows = 1; 255662306a36Sopenharmony_ci dcr->window_size = DCR_SIZE; 255762306a36Sopenharmony_ci dcr->command_offset = 0; 255862306a36Sopenharmony_ci dcr->command_size = 8; 255962306a36Sopenharmony_ci dcr->status_offset = 8; 256062306a36Sopenharmony_ci dcr->status_size = 4; 256162306a36Sopenharmony_ci offset += dcr->header.length; 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci /* dcr-descriptor4: pmem */ 256462306a36Sopenharmony_ci dcr = nfit_buf + offset; 256562306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 256662306a36Sopenharmony_ci dcr->header.length = offsetof(struct acpi_nfit_control_region, 256762306a36Sopenharmony_ci window_size); 256862306a36Sopenharmony_ci dcr->region_index = 9+1; 256962306a36Sopenharmony_ci dcr_common_init(dcr); 257062306a36Sopenharmony_ci dcr->serial_number = ~handle[4]; 257162306a36Sopenharmony_ci dcr->code = NFIT_FIC_BYTEN; 257262306a36Sopenharmony_ci dcr->windows = 0; 257362306a36Sopenharmony_ci offset += dcr->header.length; 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci /* bdw4 (spa/dcr4, dimm4) */ 257662306a36Sopenharmony_ci bdw = nfit_buf + offset; 257762306a36Sopenharmony_ci bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; 257862306a36Sopenharmony_ci bdw->header.length = sizeof(*bdw); 257962306a36Sopenharmony_ci bdw->region_index = 8+1; 258062306a36Sopenharmony_ci bdw->windows = 1; 258162306a36Sopenharmony_ci bdw->offset = 0; 258262306a36Sopenharmony_ci bdw->size = BDW_SIZE; 258362306a36Sopenharmony_ci bdw->capacity = DIMM_SIZE; 258462306a36Sopenharmony_ci bdw->start_address = 0; 258562306a36Sopenharmony_ci offset += bdw->header.length; 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci /* spa10 (dcr4) dimm4 */ 258862306a36Sopenharmony_ci spa = nfit_buf + offset; 258962306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 259062306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 259162306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); 259262306a36Sopenharmony_ci spa->range_index = 10+1; 259362306a36Sopenharmony_ci spa->address = t->dcr_dma[4]; 259462306a36Sopenharmony_ci spa->length = DCR_SIZE; 259562306a36Sopenharmony_ci offset += spa->header.length; 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci /* 259862306a36Sopenharmony_ci * spa11 (single-dimm interleave for hotplug, note storage 259962306a36Sopenharmony_ci * does not actually alias the related block-data-window 260062306a36Sopenharmony_ci * regions) 260162306a36Sopenharmony_ci */ 260262306a36Sopenharmony_ci spa = nfit_buf + offset; 260362306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 260462306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 260562306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16); 260662306a36Sopenharmony_ci spa->range_index = 11+1; 260762306a36Sopenharmony_ci spa->address = t->spa_set_dma[2]; 260862306a36Sopenharmony_ci spa->length = SPA0_SIZE; 260962306a36Sopenharmony_ci offset += spa->header.length; 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_ci /* spa12 (bdw for dcr4) dimm4 */ 261262306a36Sopenharmony_ci spa = nfit_buf + offset; 261362306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 261462306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 261562306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); 261662306a36Sopenharmony_ci spa->range_index = 12+1; 261762306a36Sopenharmony_ci spa->address = t->dimm_dma[4]; 261862306a36Sopenharmony_ci spa->length = DIMM_SIZE; 261962306a36Sopenharmony_ci offset += spa->header.length; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci /* mem-region14 (spa/dcr4, dimm4) */ 262262306a36Sopenharmony_ci memdev = nfit_buf + offset; 262362306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 262462306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 262562306a36Sopenharmony_ci memdev->device_handle = handle[4]; 262662306a36Sopenharmony_ci memdev->physical_id = 4; 262762306a36Sopenharmony_ci memdev->region_id = 0; 262862306a36Sopenharmony_ci memdev->range_index = 10+1; 262962306a36Sopenharmony_ci memdev->region_index = 8+1; 263062306a36Sopenharmony_ci memdev->region_size = 0; 263162306a36Sopenharmony_ci memdev->region_offset = 0; 263262306a36Sopenharmony_ci memdev->address = 0; 263362306a36Sopenharmony_ci memdev->interleave_index = 0; 263462306a36Sopenharmony_ci memdev->interleave_ways = 1; 263562306a36Sopenharmony_ci offset += memdev->header.length; 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci /* mem-region15 (spa11, dimm4) */ 263862306a36Sopenharmony_ci memdev = nfit_buf + offset; 263962306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 264062306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 264162306a36Sopenharmony_ci memdev->device_handle = handle[4]; 264262306a36Sopenharmony_ci memdev->physical_id = 4; 264362306a36Sopenharmony_ci memdev->region_id = 0; 264462306a36Sopenharmony_ci memdev->range_index = 11+1; 264562306a36Sopenharmony_ci memdev->region_index = 9+1; 264662306a36Sopenharmony_ci memdev->region_size = SPA0_SIZE; 264762306a36Sopenharmony_ci memdev->region_offset = (1ULL << 48); 264862306a36Sopenharmony_ci memdev->address = 0; 264962306a36Sopenharmony_ci memdev->interleave_index = 0; 265062306a36Sopenharmony_ci memdev->interleave_ways = 1; 265162306a36Sopenharmony_ci memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; 265262306a36Sopenharmony_ci offset += memdev->header.length; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci /* mem-region16 (spa/bdw4, dimm4) */ 265562306a36Sopenharmony_ci memdev = nfit_buf + offset; 265662306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 265762306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 265862306a36Sopenharmony_ci memdev->device_handle = handle[4]; 265962306a36Sopenharmony_ci memdev->physical_id = 4; 266062306a36Sopenharmony_ci memdev->region_id = 0; 266162306a36Sopenharmony_ci memdev->range_index = 12+1; 266262306a36Sopenharmony_ci memdev->region_index = 8+1; 266362306a36Sopenharmony_ci memdev->region_size = 0; 266462306a36Sopenharmony_ci memdev->region_offset = 0; 266562306a36Sopenharmony_ci memdev->address = 0; 266662306a36Sopenharmony_ci memdev->interleave_index = 0; 266762306a36Sopenharmony_ci memdev->interleave_ways = 1; 266862306a36Sopenharmony_ci offset += memdev->header.length; 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci /* flush3 (dimm4) */ 267162306a36Sopenharmony_ci flush = nfit_buf + offset; 267262306a36Sopenharmony_ci flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; 267362306a36Sopenharmony_ci flush->header.length = flush_hint_size; 267462306a36Sopenharmony_ci flush->device_handle = handle[4]; 267562306a36Sopenharmony_ci flush->hint_count = NUM_HINTS; 267662306a36Sopenharmony_ci for (i = 0; i < NUM_HINTS; i++) 267762306a36Sopenharmony_ci flush->hint_address[i] = t->flush_dma[4] 267862306a36Sopenharmony_ci + i * sizeof(u64); 267962306a36Sopenharmony_ci offset += flush->header.length; 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci /* sanity check to make sure we've filled the buffer */ 268262306a36Sopenharmony_ci WARN_ON(offset != t->nfit_size); 268362306a36Sopenharmony_ci } 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci t->nfit_filled = offset; 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0], 268862306a36Sopenharmony_ci SPA0_SIZE); 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci acpi_desc = &t->acpi_desc; 269162306a36Sopenharmony_ci set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en); 269262306a36Sopenharmony_ci set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en); 269362306a36Sopenharmony_ci set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en); 269462306a36Sopenharmony_ci set_bit(ND_INTEL_SMART, &acpi_desc->dimm_cmd_force_en); 269562306a36Sopenharmony_ci set_bit(ND_INTEL_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en); 269662306a36Sopenharmony_ci set_bit(ND_INTEL_SMART_SET_THRESHOLD, &acpi_desc->dimm_cmd_force_en); 269762306a36Sopenharmony_ci set_bit(ND_INTEL_SMART_INJECT, &acpi_desc->dimm_cmd_force_en); 269862306a36Sopenharmony_ci set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en); 269962306a36Sopenharmony_ci set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en); 270062306a36Sopenharmony_ci set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en); 270162306a36Sopenharmony_ci set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en); 270262306a36Sopenharmony_ci set_bit(ND_CMD_CALL, &acpi_desc->bus_cmd_force_en); 270362306a36Sopenharmony_ci set_bit(NFIT_CMD_TRANSLATE_SPA, &acpi_desc->bus_dsm_mask); 270462306a36Sopenharmony_ci set_bit(NFIT_CMD_ARS_INJECT_SET, &acpi_desc->bus_dsm_mask); 270562306a36Sopenharmony_ci set_bit(NFIT_CMD_ARS_INJECT_CLEAR, &acpi_desc->bus_dsm_mask); 270662306a36Sopenharmony_ci set_bit(NFIT_CMD_ARS_INJECT_GET, &acpi_desc->bus_dsm_mask); 270762306a36Sopenharmony_ci set_bit(ND_INTEL_FW_GET_INFO, &acpi_desc->dimm_cmd_force_en); 270862306a36Sopenharmony_ci set_bit(ND_INTEL_FW_START_UPDATE, &acpi_desc->dimm_cmd_force_en); 270962306a36Sopenharmony_ci set_bit(ND_INTEL_FW_SEND_DATA, &acpi_desc->dimm_cmd_force_en); 271062306a36Sopenharmony_ci set_bit(ND_INTEL_FW_FINISH_UPDATE, &acpi_desc->dimm_cmd_force_en); 271162306a36Sopenharmony_ci set_bit(ND_INTEL_FW_FINISH_QUERY, &acpi_desc->dimm_cmd_force_en); 271262306a36Sopenharmony_ci set_bit(ND_INTEL_ENABLE_LSS_STATUS, &acpi_desc->dimm_cmd_force_en); 271362306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_GET_SECURITY_STATE, 271462306a36Sopenharmony_ci &acpi_desc->dimm_cmd_force_en); 271562306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_SET_PASSPHRASE, &acpi_desc->dimm_cmd_force_en); 271662306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, 271762306a36Sopenharmony_ci &acpi_desc->dimm_cmd_force_en); 271862306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_UNLOCK_UNIT, &acpi_desc->dimm_cmd_force_en); 271962306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_FREEZE_LOCK, &acpi_desc->dimm_cmd_force_en); 272062306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_SECURE_ERASE, &acpi_desc->dimm_cmd_force_en); 272162306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_OVERWRITE, &acpi_desc->dimm_cmd_force_en); 272262306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &acpi_desc->dimm_cmd_force_en); 272362306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_SET_MASTER_PASSPHRASE, 272462306a36Sopenharmony_ci &acpi_desc->dimm_cmd_force_en); 272562306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_MASTER_SECURE_ERASE, 272662306a36Sopenharmony_ci &acpi_desc->dimm_cmd_force_en); 272762306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO, &acpi_desc->dimm_cmd_force_en); 272862306a36Sopenharmony_ci set_bit(NVDIMM_INTEL_FW_ACTIVATE_ARM, &acpi_desc->dimm_cmd_force_en); 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci acpi_mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL]; 273162306a36Sopenharmony_ci set_bit(NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO, acpi_mask); 273262306a36Sopenharmony_ci set_bit(NVDIMM_BUS_INTEL_FW_ACTIVATE, acpi_mask); 273362306a36Sopenharmony_ci} 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_cistatic void nfit_test1_setup(struct nfit_test *t) 273662306a36Sopenharmony_ci{ 273762306a36Sopenharmony_ci size_t offset; 273862306a36Sopenharmony_ci void *nfit_buf = t->nfit_buf; 273962306a36Sopenharmony_ci struct acpi_nfit_memory_map *memdev; 274062306a36Sopenharmony_ci struct acpi_nfit_control_region *dcr; 274162306a36Sopenharmony_ci struct acpi_nfit_system_address *spa; 274262306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc; 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci offset = 0; 274562306a36Sopenharmony_ci /* spa0 (flat range with no bdw aliasing) */ 274662306a36Sopenharmony_ci spa = nfit_buf + offset; 274762306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 274862306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 274962306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16); 275062306a36Sopenharmony_ci spa->range_index = 0+1; 275162306a36Sopenharmony_ci spa->address = t->spa_set_dma[0]; 275262306a36Sopenharmony_ci spa->length = SPA2_SIZE; 275362306a36Sopenharmony_ci offset += spa->header.length; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci /* virtual cd region */ 275662306a36Sopenharmony_ci spa = nfit_buf + offset; 275762306a36Sopenharmony_ci spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; 275862306a36Sopenharmony_ci spa->header.length = sizeof_spa(spa); 275962306a36Sopenharmony_ci memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_VCD), 16); 276062306a36Sopenharmony_ci spa->range_index = 0; 276162306a36Sopenharmony_ci spa->address = t->spa_set_dma[1]; 276262306a36Sopenharmony_ci spa->length = SPA_VCD_SIZE; 276362306a36Sopenharmony_ci offset += spa->header.length; 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci /* mem-region0 (spa0, dimm0) */ 276662306a36Sopenharmony_ci memdev = nfit_buf + offset; 276762306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 276862306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 276962306a36Sopenharmony_ci memdev->device_handle = handle[5]; 277062306a36Sopenharmony_ci memdev->physical_id = 0; 277162306a36Sopenharmony_ci memdev->region_id = 0; 277262306a36Sopenharmony_ci memdev->range_index = 0+1; 277362306a36Sopenharmony_ci memdev->region_index = 0+1; 277462306a36Sopenharmony_ci memdev->region_size = SPA2_SIZE; 277562306a36Sopenharmony_ci memdev->region_offset = 0; 277662306a36Sopenharmony_ci memdev->address = 0; 277762306a36Sopenharmony_ci memdev->interleave_index = 0; 277862306a36Sopenharmony_ci memdev->interleave_ways = 1; 277962306a36Sopenharmony_ci memdev->flags = ACPI_NFIT_MEM_SAVE_FAILED | ACPI_NFIT_MEM_RESTORE_FAILED 278062306a36Sopenharmony_ci | ACPI_NFIT_MEM_FLUSH_FAILED | ACPI_NFIT_MEM_HEALTH_OBSERVED 278162306a36Sopenharmony_ci | ACPI_NFIT_MEM_NOT_ARMED; 278262306a36Sopenharmony_ci offset += memdev->header.length; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci /* dcr-descriptor0 */ 278562306a36Sopenharmony_ci dcr = nfit_buf + offset; 278662306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 278762306a36Sopenharmony_ci dcr->header.length = offsetof(struct acpi_nfit_control_region, 278862306a36Sopenharmony_ci window_size); 278962306a36Sopenharmony_ci dcr->region_index = 0+1; 279062306a36Sopenharmony_ci dcr_common_init(dcr); 279162306a36Sopenharmony_ci dcr->serial_number = ~handle[5]; 279262306a36Sopenharmony_ci dcr->code = NFIT_FIC_BYTE; 279362306a36Sopenharmony_ci dcr->windows = 0; 279462306a36Sopenharmony_ci offset += dcr->header.length; 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci memdev = nfit_buf + offset; 279762306a36Sopenharmony_ci memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; 279862306a36Sopenharmony_ci memdev->header.length = sizeof(*memdev); 279962306a36Sopenharmony_ci memdev->device_handle = handle[6]; 280062306a36Sopenharmony_ci memdev->physical_id = 0; 280162306a36Sopenharmony_ci memdev->region_id = 0; 280262306a36Sopenharmony_ci memdev->range_index = 0; 280362306a36Sopenharmony_ci memdev->region_index = 0+2; 280462306a36Sopenharmony_ci memdev->region_size = SPA2_SIZE; 280562306a36Sopenharmony_ci memdev->region_offset = 0; 280662306a36Sopenharmony_ci memdev->address = 0; 280762306a36Sopenharmony_ci memdev->interleave_index = 0; 280862306a36Sopenharmony_ci memdev->interleave_ways = 1; 280962306a36Sopenharmony_ci memdev->flags = ACPI_NFIT_MEM_MAP_FAILED; 281062306a36Sopenharmony_ci offset += memdev->header.length; 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci /* dcr-descriptor1 */ 281362306a36Sopenharmony_ci dcr = nfit_buf + offset; 281462306a36Sopenharmony_ci dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; 281562306a36Sopenharmony_ci dcr->header.length = offsetof(struct acpi_nfit_control_region, 281662306a36Sopenharmony_ci window_size); 281762306a36Sopenharmony_ci dcr->region_index = 0+2; 281862306a36Sopenharmony_ci dcr_common_init(dcr); 281962306a36Sopenharmony_ci dcr->serial_number = ~handle[6]; 282062306a36Sopenharmony_ci dcr->code = NFIT_FIC_BYTE; 282162306a36Sopenharmony_ci dcr->windows = 0; 282262306a36Sopenharmony_ci offset += dcr->header.length; 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci /* sanity check to make sure we've filled the buffer */ 282562306a36Sopenharmony_ci WARN_ON(offset != t->nfit_size); 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_ci t->nfit_filled = offset; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0], 283062306a36Sopenharmony_ci SPA2_SIZE); 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci acpi_desc = &t->acpi_desc; 283362306a36Sopenharmony_ci set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en); 283462306a36Sopenharmony_ci set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en); 283562306a36Sopenharmony_ci set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en); 283662306a36Sopenharmony_ci set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en); 283762306a36Sopenharmony_ci set_bit(ND_INTEL_ENABLE_LSS_STATUS, &acpi_desc->dimm_cmd_force_en); 283862306a36Sopenharmony_ci set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en); 283962306a36Sopenharmony_ci set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en); 284062306a36Sopenharmony_ci set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en); 284162306a36Sopenharmony_ci} 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_cistatic unsigned long nfit_ctl_handle; 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ciunion acpi_object *result; 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_cistatic union acpi_object *nfit_test_evaluate_dsm(acpi_handle handle, 284862306a36Sopenharmony_ci const guid_t *guid, u64 rev, u64 func, union acpi_object *argv4) 284962306a36Sopenharmony_ci{ 285062306a36Sopenharmony_ci if (handle != &nfit_ctl_handle) 285162306a36Sopenharmony_ci return ERR_PTR(-ENXIO); 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci return result; 285462306a36Sopenharmony_ci} 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_cistatic int setup_result(void *buf, size_t size) 285762306a36Sopenharmony_ci{ 285862306a36Sopenharmony_ci result = kmalloc(sizeof(union acpi_object) + size, GFP_KERNEL); 285962306a36Sopenharmony_ci if (!result) 286062306a36Sopenharmony_ci return -ENOMEM; 286162306a36Sopenharmony_ci result->package.type = ACPI_TYPE_BUFFER, 286262306a36Sopenharmony_ci result->buffer.pointer = (void *) (result + 1); 286362306a36Sopenharmony_ci result->buffer.length = size; 286462306a36Sopenharmony_ci memcpy(result->buffer.pointer, buf, size); 286562306a36Sopenharmony_ci memset(buf, 0, size); 286662306a36Sopenharmony_ci return 0; 286762306a36Sopenharmony_ci} 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_cistatic int nfit_ctl_test(struct device *dev) 287062306a36Sopenharmony_ci{ 287162306a36Sopenharmony_ci int rc, cmd_rc; 287262306a36Sopenharmony_ci struct nvdimm *nvdimm; 287362306a36Sopenharmony_ci struct acpi_device *adev; 287462306a36Sopenharmony_ci struct nfit_mem *nfit_mem; 287562306a36Sopenharmony_ci struct nd_ars_record *record; 287662306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc; 287762306a36Sopenharmony_ci const u64 test_val = 0x0123456789abcdefULL; 287862306a36Sopenharmony_ci unsigned long mask, cmd_size, offset; 287962306a36Sopenharmony_ci struct nfit_ctl_test_cmd { 288062306a36Sopenharmony_ci struct nd_cmd_pkg pkg; 288162306a36Sopenharmony_ci union { 288262306a36Sopenharmony_ci struct nd_cmd_get_config_size cfg_size; 288362306a36Sopenharmony_ci struct nd_cmd_clear_error clear_err; 288462306a36Sopenharmony_ci struct nd_cmd_ars_status ars_stat; 288562306a36Sopenharmony_ci struct nd_cmd_ars_cap ars_cap; 288662306a36Sopenharmony_ci struct nd_intel_bus_fw_activate_businfo fwa_info; 288762306a36Sopenharmony_ci char buf[sizeof(struct nd_cmd_ars_status) 288862306a36Sopenharmony_ci + sizeof(struct nd_ars_record)]; 288962306a36Sopenharmony_ci }; 289062306a36Sopenharmony_ci } cmd; 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL); 289362306a36Sopenharmony_ci if (!adev) 289462306a36Sopenharmony_ci return -ENOMEM; 289562306a36Sopenharmony_ci *adev = (struct acpi_device) { 289662306a36Sopenharmony_ci .handle = &nfit_ctl_handle, 289762306a36Sopenharmony_ci .dev = { 289862306a36Sopenharmony_ci .init_name = "test-adev", 289962306a36Sopenharmony_ci }, 290062306a36Sopenharmony_ci }; 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); 290362306a36Sopenharmony_ci if (!acpi_desc) 290462306a36Sopenharmony_ci return -ENOMEM; 290562306a36Sopenharmony_ci *acpi_desc = (struct acpi_nfit_desc) { 290662306a36Sopenharmony_ci .nd_desc = { 290762306a36Sopenharmony_ci .cmd_mask = 1UL << ND_CMD_ARS_CAP 290862306a36Sopenharmony_ci | 1UL << ND_CMD_ARS_START 290962306a36Sopenharmony_ci | 1UL << ND_CMD_ARS_STATUS 291062306a36Sopenharmony_ci | 1UL << ND_CMD_CLEAR_ERROR 291162306a36Sopenharmony_ci | 1UL << ND_CMD_CALL, 291262306a36Sopenharmony_ci .module = THIS_MODULE, 291362306a36Sopenharmony_ci .provider_name = "ACPI.NFIT", 291462306a36Sopenharmony_ci .ndctl = acpi_nfit_ctl, 291562306a36Sopenharmony_ci .bus_family_mask = 1UL << NVDIMM_BUS_FAMILY_NFIT 291662306a36Sopenharmony_ci | 1UL << NVDIMM_BUS_FAMILY_INTEL, 291762306a36Sopenharmony_ci }, 291862306a36Sopenharmony_ci .bus_dsm_mask = 1UL << NFIT_CMD_TRANSLATE_SPA 291962306a36Sopenharmony_ci | 1UL << NFIT_CMD_ARS_INJECT_SET 292062306a36Sopenharmony_ci | 1UL << NFIT_CMD_ARS_INJECT_CLEAR 292162306a36Sopenharmony_ci | 1UL << NFIT_CMD_ARS_INJECT_GET, 292262306a36Sopenharmony_ci .family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL] = 292362306a36Sopenharmony_ci NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK, 292462306a36Sopenharmony_ci .dev = &adev->dev, 292562306a36Sopenharmony_ci }; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci nfit_mem = devm_kzalloc(dev, sizeof(*nfit_mem), GFP_KERNEL); 292862306a36Sopenharmony_ci if (!nfit_mem) 292962306a36Sopenharmony_ci return -ENOMEM; 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci mask = 1UL << ND_CMD_SMART | 1UL << ND_CMD_SMART_THRESHOLD 293262306a36Sopenharmony_ci | 1UL << ND_CMD_DIMM_FLAGS | 1UL << ND_CMD_GET_CONFIG_SIZE 293362306a36Sopenharmony_ci | 1UL << ND_CMD_GET_CONFIG_DATA | 1UL << ND_CMD_SET_CONFIG_DATA 293462306a36Sopenharmony_ci | 1UL << ND_CMD_VENDOR; 293562306a36Sopenharmony_ci *nfit_mem = (struct nfit_mem) { 293662306a36Sopenharmony_ci .adev = adev, 293762306a36Sopenharmony_ci .family = NVDIMM_FAMILY_INTEL, 293862306a36Sopenharmony_ci .dsm_mask = mask, 293962306a36Sopenharmony_ci }; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci nvdimm = devm_kzalloc(dev, sizeof(*nvdimm), GFP_KERNEL); 294262306a36Sopenharmony_ci if (!nvdimm) 294362306a36Sopenharmony_ci return -ENOMEM; 294462306a36Sopenharmony_ci *nvdimm = (struct nvdimm) { 294562306a36Sopenharmony_ci .provider_data = nfit_mem, 294662306a36Sopenharmony_ci .cmd_mask = mask, 294762306a36Sopenharmony_ci .dev = { 294862306a36Sopenharmony_ci .init_name = "test-dimm", 294962306a36Sopenharmony_ci }, 295062306a36Sopenharmony_ci }; 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci /* basic checkout of a typical 'get config size' command */ 295462306a36Sopenharmony_ci cmd_size = sizeof(cmd.cfg_size); 295562306a36Sopenharmony_ci cmd.cfg_size = (struct nd_cmd_get_config_size) { 295662306a36Sopenharmony_ci .status = 0, 295762306a36Sopenharmony_ci .config_size = SZ_128K, 295862306a36Sopenharmony_ci .max_xfer = SZ_4K, 295962306a36Sopenharmony_ci }; 296062306a36Sopenharmony_ci rc = setup_result(cmd.buf, cmd_size); 296162306a36Sopenharmony_ci if (rc) 296262306a36Sopenharmony_ci return rc; 296362306a36Sopenharmony_ci rc = acpi_nfit_ctl(&acpi_desc->nd_desc, nvdimm, ND_CMD_GET_CONFIG_SIZE, 296462306a36Sopenharmony_ci cmd.buf, cmd_size, &cmd_rc); 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_ci if (rc < 0 || cmd_rc || cmd.cfg_size.status != 0 296762306a36Sopenharmony_ci || cmd.cfg_size.config_size != SZ_128K 296862306a36Sopenharmony_ci || cmd.cfg_size.max_xfer != SZ_4K) { 296962306a36Sopenharmony_ci dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", 297062306a36Sopenharmony_ci __func__, __LINE__, rc, cmd_rc); 297162306a36Sopenharmony_ci return -EIO; 297262306a36Sopenharmony_ci } 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci /* test ars_status with zero output */ 297662306a36Sopenharmony_ci cmd_size = offsetof(struct nd_cmd_ars_status, address); 297762306a36Sopenharmony_ci cmd.ars_stat = (struct nd_cmd_ars_status) { 297862306a36Sopenharmony_ci .out_length = 0, 297962306a36Sopenharmony_ci }; 298062306a36Sopenharmony_ci rc = setup_result(cmd.buf, cmd_size); 298162306a36Sopenharmony_ci if (rc) 298262306a36Sopenharmony_ci return rc; 298362306a36Sopenharmony_ci rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS, 298462306a36Sopenharmony_ci cmd.buf, cmd_size, &cmd_rc); 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci if (rc < 0 || cmd_rc) { 298762306a36Sopenharmony_ci dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", 298862306a36Sopenharmony_ci __func__, __LINE__, rc, cmd_rc); 298962306a36Sopenharmony_ci return -EIO; 299062306a36Sopenharmony_ci } 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci /* test ars_cap with benign extended status */ 299462306a36Sopenharmony_ci cmd_size = sizeof(cmd.ars_cap); 299562306a36Sopenharmony_ci cmd.ars_cap = (struct nd_cmd_ars_cap) { 299662306a36Sopenharmony_ci .status = ND_ARS_PERSISTENT << 16, 299762306a36Sopenharmony_ci }; 299862306a36Sopenharmony_ci offset = offsetof(struct nd_cmd_ars_cap, status); 299962306a36Sopenharmony_ci rc = setup_result(cmd.buf + offset, cmd_size - offset); 300062306a36Sopenharmony_ci if (rc) 300162306a36Sopenharmony_ci return rc; 300262306a36Sopenharmony_ci rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_CAP, 300362306a36Sopenharmony_ci cmd.buf, cmd_size, &cmd_rc); 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci if (rc < 0 || cmd_rc) { 300662306a36Sopenharmony_ci dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", 300762306a36Sopenharmony_ci __func__, __LINE__, rc, cmd_rc); 300862306a36Sopenharmony_ci return -EIO; 300962306a36Sopenharmony_ci } 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci /* test ars_status with 'status' trimmed from 'out_length' */ 301362306a36Sopenharmony_ci cmd_size = sizeof(cmd.ars_stat) + sizeof(struct nd_ars_record); 301462306a36Sopenharmony_ci cmd.ars_stat = (struct nd_cmd_ars_status) { 301562306a36Sopenharmony_ci .out_length = cmd_size - 4, 301662306a36Sopenharmony_ci }; 301762306a36Sopenharmony_ci record = &cmd.ars_stat.records[0]; 301862306a36Sopenharmony_ci *record = (struct nd_ars_record) { 301962306a36Sopenharmony_ci .length = test_val, 302062306a36Sopenharmony_ci }; 302162306a36Sopenharmony_ci rc = setup_result(cmd.buf, cmd_size); 302262306a36Sopenharmony_ci if (rc) 302362306a36Sopenharmony_ci return rc; 302462306a36Sopenharmony_ci rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS, 302562306a36Sopenharmony_ci cmd.buf, cmd_size, &cmd_rc); 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci if (rc < 0 || cmd_rc || record->length != test_val) { 302862306a36Sopenharmony_ci dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", 302962306a36Sopenharmony_ci __func__, __LINE__, rc, cmd_rc); 303062306a36Sopenharmony_ci return -EIO; 303162306a36Sopenharmony_ci } 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci /* test ars_status with 'Output (Size)' including 'status' */ 303562306a36Sopenharmony_ci cmd_size = sizeof(cmd.ars_stat) + sizeof(struct nd_ars_record); 303662306a36Sopenharmony_ci cmd.ars_stat = (struct nd_cmd_ars_status) { 303762306a36Sopenharmony_ci .out_length = cmd_size, 303862306a36Sopenharmony_ci }; 303962306a36Sopenharmony_ci record = &cmd.ars_stat.records[0]; 304062306a36Sopenharmony_ci *record = (struct nd_ars_record) { 304162306a36Sopenharmony_ci .length = test_val, 304262306a36Sopenharmony_ci }; 304362306a36Sopenharmony_ci rc = setup_result(cmd.buf, cmd_size); 304462306a36Sopenharmony_ci if (rc) 304562306a36Sopenharmony_ci return rc; 304662306a36Sopenharmony_ci rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS, 304762306a36Sopenharmony_ci cmd.buf, cmd_size, &cmd_rc); 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci if (rc < 0 || cmd_rc || record->length != test_val) { 305062306a36Sopenharmony_ci dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", 305162306a36Sopenharmony_ci __func__, __LINE__, rc, cmd_rc); 305262306a36Sopenharmony_ci return -EIO; 305362306a36Sopenharmony_ci } 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci /* test extended status for get_config_size results in failure */ 305762306a36Sopenharmony_ci cmd_size = sizeof(cmd.cfg_size); 305862306a36Sopenharmony_ci cmd.cfg_size = (struct nd_cmd_get_config_size) { 305962306a36Sopenharmony_ci .status = 1 << 16, 306062306a36Sopenharmony_ci }; 306162306a36Sopenharmony_ci rc = setup_result(cmd.buf, cmd_size); 306262306a36Sopenharmony_ci if (rc) 306362306a36Sopenharmony_ci return rc; 306462306a36Sopenharmony_ci rc = acpi_nfit_ctl(&acpi_desc->nd_desc, nvdimm, ND_CMD_GET_CONFIG_SIZE, 306562306a36Sopenharmony_ci cmd.buf, cmd_size, &cmd_rc); 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci if (rc < 0 || cmd_rc >= 0) { 306862306a36Sopenharmony_ci dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", 306962306a36Sopenharmony_ci __func__, __LINE__, rc, cmd_rc); 307062306a36Sopenharmony_ci return -EIO; 307162306a36Sopenharmony_ci } 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci /* test clear error */ 307462306a36Sopenharmony_ci cmd_size = sizeof(cmd.clear_err); 307562306a36Sopenharmony_ci cmd.clear_err = (struct nd_cmd_clear_error) { 307662306a36Sopenharmony_ci .length = 512, 307762306a36Sopenharmony_ci .cleared = 512, 307862306a36Sopenharmony_ci }; 307962306a36Sopenharmony_ci rc = setup_result(cmd.buf, cmd_size); 308062306a36Sopenharmony_ci if (rc) 308162306a36Sopenharmony_ci return rc; 308262306a36Sopenharmony_ci rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CLEAR_ERROR, 308362306a36Sopenharmony_ci cmd.buf, cmd_size, &cmd_rc); 308462306a36Sopenharmony_ci if (rc < 0 || cmd_rc) { 308562306a36Sopenharmony_ci dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", 308662306a36Sopenharmony_ci __func__, __LINE__, rc, cmd_rc); 308762306a36Sopenharmony_ci return -EIO; 308862306a36Sopenharmony_ci } 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci /* test firmware activate bus info */ 309162306a36Sopenharmony_ci cmd_size = sizeof(cmd.fwa_info); 309262306a36Sopenharmony_ci cmd = (struct nfit_ctl_test_cmd) { 309362306a36Sopenharmony_ci .pkg = { 309462306a36Sopenharmony_ci .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO, 309562306a36Sopenharmony_ci .nd_family = NVDIMM_BUS_FAMILY_INTEL, 309662306a36Sopenharmony_ci .nd_size_out = cmd_size, 309762306a36Sopenharmony_ci .nd_fw_size = cmd_size, 309862306a36Sopenharmony_ci }, 309962306a36Sopenharmony_ci .fwa_info = { 310062306a36Sopenharmony_ci .state = ND_INTEL_FWA_IDLE, 310162306a36Sopenharmony_ci .capability = ND_INTEL_BUS_FWA_CAP_FWQUIESCE 310262306a36Sopenharmony_ci | ND_INTEL_BUS_FWA_CAP_OSQUIESCE, 310362306a36Sopenharmony_ci .activate_tmo = 1, 310462306a36Sopenharmony_ci .cpu_quiesce_tmo = 1, 310562306a36Sopenharmony_ci .io_quiesce_tmo = 1, 310662306a36Sopenharmony_ci .max_quiesce_tmo = 1, 310762306a36Sopenharmony_ci }, 310862306a36Sopenharmony_ci }; 310962306a36Sopenharmony_ci rc = setup_result(cmd.buf, cmd_size); 311062306a36Sopenharmony_ci if (rc) 311162306a36Sopenharmony_ci return rc; 311262306a36Sopenharmony_ci rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CALL, 311362306a36Sopenharmony_ci &cmd, sizeof(cmd.pkg) + cmd_size, &cmd_rc); 311462306a36Sopenharmony_ci if (rc < 0 || cmd_rc) { 311562306a36Sopenharmony_ci dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", 311662306a36Sopenharmony_ci __func__, __LINE__, rc, cmd_rc); 311762306a36Sopenharmony_ci return -EIO; 311862306a36Sopenharmony_ci } 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci return 0; 312162306a36Sopenharmony_ci} 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_cistatic int nfit_test_probe(struct platform_device *pdev) 312462306a36Sopenharmony_ci{ 312562306a36Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc; 312662306a36Sopenharmony_ci struct acpi_nfit_desc *acpi_desc; 312762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 312862306a36Sopenharmony_ci struct nfit_test *nfit_test; 312962306a36Sopenharmony_ci struct nfit_mem *nfit_mem; 313062306a36Sopenharmony_ci union acpi_object *obj; 313162306a36Sopenharmony_ci int rc; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci if (strcmp(dev_name(&pdev->dev), "nfit_test.0") == 0) { 313462306a36Sopenharmony_ci rc = nfit_ctl_test(&pdev->dev); 313562306a36Sopenharmony_ci if (rc) 313662306a36Sopenharmony_ci return rc; 313762306a36Sopenharmony_ci } 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci nfit_test = to_nfit_test(&pdev->dev); 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_ci /* common alloc */ 314262306a36Sopenharmony_ci if (nfit_test->num_dcr) { 314362306a36Sopenharmony_ci int num = nfit_test->num_dcr; 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci nfit_test->dimm = devm_kcalloc(dev, num, sizeof(void *), 314662306a36Sopenharmony_ci GFP_KERNEL); 314762306a36Sopenharmony_ci nfit_test->dimm_dma = devm_kcalloc(dev, num, sizeof(dma_addr_t), 314862306a36Sopenharmony_ci GFP_KERNEL); 314962306a36Sopenharmony_ci nfit_test->flush = devm_kcalloc(dev, num, sizeof(void *), 315062306a36Sopenharmony_ci GFP_KERNEL); 315162306a36Sopenharmony_ci nfit_test->flush_dma = devm_kcalloc(dev, num, sizeof(dma_addr_t), 315262306a36Sopenharmony_ci GFP_KERNEL); 315362306a36Sopenharmony_ci nfit_test->label = devm_kcalloc(dev, num, sizeof(void *), 315462306a36Sopenharmony_ci GFP_KERNEL); 315562306a36Sopenharmony_ci nfit_test->label_dma = devm_kcalloc(dev, num, 315662306a36Sopenharmony_ci sizeof(dma_addr_t), GFP_KERNEL); 315762306a36Sopenharmony_ci nfit_test->dcr = devm_kcalloc(dev, num, 315862306a36Sopenharmony_ci sizeof(struct nfit_test_dcr *), GFP_KERNEL); 315962306a36Sopenharmony_ci nfit_test->dcr_dma = devm_kcalloc(dev, num, 316062306a36Sopenharmony_ci sizeof(dma_addr_t), GFP_KERNEL); 316162306a36Sopenharmony_ci nfit_test->smart = devm_kcalloc(dev, num, 316262306a36Sopenharmony_ci sizeof(struct nd_intel_smart), GFP_KERNEL); 316362306a36Sopenharmony_ci nfit_test->smart_threshold = devm_kcalloc(dev, num, 316462306a36Sopenharmony_ci sizeof(struct nd_intel_smart_threshold), 316562306a36Sopenharmony_ci GFP_KERNEL); 316662306a36Sopenharmony_ci nfit_test->fw = devm_kcalloc(dev, num, 316762306a36Sopenharmony_ci sizeof(struct nfit_test_fw), GFP_KERNEL); 316862306a36Sopenharmony_ci if (nfit_test->dimm && nfit_test->dimm_dma && nfit_test->label 316962306a36Sopenharmony_ci && nfit_test->label_dma && nfit_test->dcr 317062306a36Sopenharmony_ci && nfit_test->dcr_dma && nfit_test->flush 317162306a36Sopenharmony_ci && nfit_test->flush_dma 317262306a36Sopenharmony_ci && nfit_test->fw) 317362306a36Sopenharmony_ci /* pass */; 317462306a36Sopenharmony_ci else 317562306a36Sopenharmony_ci return -ENOMEM; 317662306a36Sopenharmony_ci } 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci if (nfit_test->num_pm) { 317962306a36Sopenharmony_ci int num = nfit_test->num_pm; 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci nfit_test->spa_set = devm_kcalloc(dev, num, sizeof(void *), 318262306a36Sopenharmony_ci GFP_KERNEL); 318362306a36Sopenharmony_ci nfit_test->spa_set_dma = devm_kcalloc(dev, num, 318462306a36Sopenharmony_ci sizeof(dma_addr_t), GFP_KERNEL); 318562306a36Sopenharmony_ci if (nfit_test->spa_set && nfit_test->spa_set_dma) 318662306a36Sopenharmony_ci /* pass */; 318762306a36Sopenharmony_ci else 318862306a36Sopenharmony_ci return -ENOMEM; 318962306a36Sopenharmony_ci } 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci /* per-nfit specific alloc */ 319262306a36Sopenharmony_ci if (nfit_test->alloc(nfit_test)) 319362306a36Sopenharmony_ci return -ENOMEM; 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci nfit_test->setup(nfit_test); 319662306a36Sopenharmony_ci acpi_desc = &nfit_test->acpi_desc; 319762306a36Sopenharmony_ci acpi_nfit_desc_init(acpi_desc, &pdev->dev); 319862306a36Sopenharmony_ci nd_desc = &acpi_desc->nd_desc; 319962306a36Sopenharmony_ci nd_desc->provider_name = NULL; 320062306a36Sopenharmony_ci nd_desc->module = THIS_MODULE; 320162306a36Sopenharmony_ci nd_desc->ndctl = nfit_test_ctl; 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_buf, 320462306a36Sopenharmony_ci nfit_test->nfit_filled); 320562306a36Sopenharmony_ci if (rc) 320662306a36Sopenharmony_ci return rc; 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci rc = devm_add_action_or_reset(&pdev->dev, acpi_nfit_shutdown, acpi_desc); 320962306a36Sopenharmony_ci if (rc) 321062306a36Sopenharmony_ci return rc; 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci if (nfit_test->setup != nfit_test0_setup) 321362306a36Sopenharmony_ci return 0; 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci nfit_test->setup_hotplug = 1; 321662306a36Sopenharmony_ci nfit_test->setup(nfit_test); 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci obj = kzalloc(sizeof(*obj), GFP_KERNEL); 321962306a36Sopenharmony_ci if (!obj) 322062306a36Sopenharmony_ci return -ENOMEM; 322162306a36Sopenharmony_ci obj->type = ACPI_TYPE_BUFFER; 322262306a36Sopenharmony_ci obj->buffer.length = nfit_test->nfit_size; 322362306a36Sopenharmony_ci obj->buffer.pointer = nfit_test->nfit_buf; 322462306a36Sopenharmony_ci *(nfit_test->_fit) = obj; 322562306a36Sopenharmony_ci __acpi_nfit_notify(&pdev->dev, nfit_test, 0x80); 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci /* associate dimm devices with nfit_mem data for notification testing */ 322862306a36Sopenharmony_ci mutex_lock(&acpi_desc->init_mutex); 322962306a36Sopenharmony_ci list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { 323062306a36Sopenharmony_ci u32 nfit_handle = __to_nfit_memdev(nfit_mem)->device_handle; 323162306a36Sopenharmony_ci int i; 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(handle); i++) 323462306a36Sopenharmony_ci if (nfit_handle == handle[i]) 323562306a36Sopenharmony_ci dev_set_drvdata(nfit_test->dimm_dev[i], 323662306a36Sopenharmony_ci nfit_mem); 323762306a36Sopenharmony_ci } 323862306a36Sopenharmony_ci mutex_unlock(&acpi_desc->init_mutex); 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci return 0; 324162306a36Sopenharmony_ci} 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_cistatic void nfit_test_release(struct device *dev) 324462306a36Sopenharmony_ci{ 324562306a36Sopenharmony_ci struct nfit_test *nfit_test = to_nfit_test(dev); 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci kfree(nfit_test); 324862306a36Sopenharmony_ci} 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_cistatic const struct platform_device_id nfit_test_id[] = { 325162306a36Sopenharmony_ci { KBUILD_MODNAME }, 325262306a36Sopenharmony_ci { }, 325362306a36Sopenharmony_ci}; 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_cistatic struct platform_driver nfit_test_driver = { 325662306a36Sopenharmony_ci .probe = nfit_test_probe, 325762306a36Sopenharmony_ci .driver = { 325862306a36Sopenharmony_ci .name = KBUILD_MODNAME, 325962306a36Sopenharmony_ci }, 326062306a36Sopenharmony_ci .id_table = nfit_test_id, 326162306a36Sopenharmony_ci}; 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_cistatic __init int nfit_test_init(void) 326462306a36Sopenharmony_ci{ 326562306a36Sopenharmony_ci int rc, i; 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci pmem_test(); 326862306a36Sopenharmony_ci libnvdimm_test(); 326962306a36Sopenharmony_ci acpi_nfit_test(); 327062306a36Sopenharmony_ci device_dax_test(); 327162306a36Sopenharmony_ci dax_pmem_test(); 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_ci nfit_test_setup(nfit_test_lookup, nfit_test_evaluate_dsm); 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci nfit_wq = create_singlethread_workqueue("nfit"); 327662306a36Sopenharmony_ci if (!nfit_wq) 327762306a36Sopenharmony_ci return -ENOMEM; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci nfit_test_dimm = class_create("nfit_test_dimm"); 328062306a36Sopenharmony_ci if (IS_ERR(nfit_test_dimm)) { 328162306a36Sopenharmony_ci rc = PTR_ERR(nfit_test_dimm); 328262306a36Sopenharmony_ci goto err_register; 328362306a36Sopenharmony_ci } 328462306a36Sopenharmony_ci 328562306a36Sopenharmony_ci nfit_pool = gen_pool_create(ilog2(SZ_4M), NUMA_NO_NODE); 328662306a36Sopenharmony_ci if (!nfit_pool) { 328762306a36Sopenharmony_ci rc = -ENOMEM; 328862306a36Sopenharmony_ci goto err_register; 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci if (gen_pool_add(nfit_pool, SZ_4G, SZ_4G, NUMA_NO_NODE)) { 329262306a36Sopenharmony_ci rc = -ENOMEM; 329362306a36Sopenharmony_ci goto err_register; 329462306a36Sopenharmony_ci } 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_ci for (i = 0; i < NUM_NFITS; i++) { 329762306a36Sopenharmony_ci struct nfit_test *nfit_test; 329862306a36Sopenharmony_ci struct platform_device *pdev; 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci nfit_test = kzalloc(sizeof(*nfit_test), GFP_KERNEL); 330162306a36Sopenharmony_ci if (!nfit_test) { 330262306a36Sopenharmony_ci rc = -ENOMEM; 330362306a36Sopenharmony_ci goto err_register; 330462306a36Sopenharmony_ci } 330562306a36Sopenharmony_ci INIT_LIST_HEAD(&nfit_test->resources); 330662306a36Sopenharmony_ci badrange_init(&nfit_test->badrange); 330762306a36Sopenharmony_ci switch (i) { 330862306a36Sopenharmony_ci case 0: 330962306a36Sopenharmony_ci nfit_test->num_pm = NUM_PM; 331062306a36Sopenharmony_ci nfit_test->dcr_idx = 0; 331162306a36Sopenharmony_ci nfit_test->num_dcr = NUM_DCR; 331262306a36Sopenharmony_ci nfit_test->alloc = nfit_test0_alloc; 331362306a36Sopenharmony_ci nfit_test->setup = nfit_test0_setup; 331462306a36Sopenharmony_ci break; 331562306a36Sopenharmony_ci case 1: 331662306a36Sopenharmony_ci nfit_test->num_pm = 2; 331762306a36Sopenharmony_ci nfit_test->dcr_idx = NUM_DCR; 331862306a36Sopenharmony_ci nfit_test->num_dcr = 2; 331962306a36Sopenharmony_ci nfit_test->alloc = nfit_test1_alloc; 332062306a36Sopenharmony_ci nfit_test->setup = nfit_test1_setup; 332162306a36Sopenharmony_ci break; 332262306a36Sopenharmony_ci default: 332362306a36Sopenharmony_ci rc = -EINVAL; 332462306a36Sopenharmony_ci goto err_register; 332562306a36Sopenharmony_ci } 332662306a36Sopenharmony_ci pdev = &nfit_test->pdev; 332762306a36Sopenharmony_ci pdev->name = KBUILD_MODNAME; 332862306a36Sopenharmony_ci pdev->id = i; 332962306a36Sopenharmony_ci pdev->dev.release = nfit_test_release; 333062306a36Sopenharmony_ci rc = platform_device_register(pdev); 333162306a36Sopenharmony_ci if (rc) { 333262306a36Sopenharmony_ci put_device(&pdev->dev); 333362306a36Sopenharmony_ci goto err_register; 333462306a36Sopenharmony_ci } 333562306a36Sopenharmony_ci get_device(&pdev->dev); 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci rc = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 333862306a36Sopenharmony_ci if (rc) 333962306a36Sopenharmony_ci goto err_register; 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci instances[i] = nfit_test; 334262306a36Sopenharmony_ci INIT_WORK(&nfit_test->work, uc_error_notify); 334362306a36Sopenharmony_ci } 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci rc = platform_driver_register(&nfit_test_driver); 334662306a36Sopenharmony_ci if (rc) 334762306a36Sopenharmony_ci goto err_register; 334862306a36Sopenharmony_ci return 0; 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_ci err_register: 335162306a36Sopenharmony_ci if (nfit_pool) 335262306a36Sopenharmony_ci gen_pool_destroy(nfit_pool); 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci destroy_workqueue(nfit_wq); 335562306a36Sopenharmony_ci for (i = 0; i < NUM_NFITS; i++) 335662306a36Sopenharmony_ci if (instances[i]) 335762306a36Sopenharmony_ci platform_device_unregister(&instances[i]->pdev); 335862306a36Sopenharmony_ci nfit_test_teardown(); 335962306a36Sopenharmony_ci for (i = 0; i < NUM_NFITS; i++) 336062306a36Sopenharmony_ci if (instances[i]) 336162306a36Sopenharmony_ci put_device(&instances[i]->pdev.dev); 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci return rc; 336462306a36Sopenharmony_ci} 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_cistatic __exit void nfit_test_exit(void) 336762306a36Sopenharmony_ci{ 336862306a36Sopenharmony_ci int i; 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci destroy_workqueue(nfit_wq); 337162306a36Sopenharmony_ci for (i = 0; i < NUM_NFITS; i++) 337262306a36Sopenharmony_ci platform_device_unregister(&instances[i]->pdev); 337362306a36Sopenharmony_ci platform_driver_unregister(&nfit_test_driver); 337462306a36Sopenharmony_ci nfit_test_teardown(); 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci gen_pool_destroy(nfit_pool); 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci for (i = 0; i < NUM_NFITS; i++) 337962306a36Sopenharmony_ci put_device(&instances[i]->pdev.dev); 338062306a36Sopenharmony_ci class_destroy(nfit_test_dimm); 338162306a36Sopenharmony_ci} 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_cimodule_init(nfit_test_init); 338462306a36Sopenharmony_cimodule_exit(nfit_test_exit); 338562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 338662306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 3387