1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. |
4 | */ |
5 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
6 | #include <linux/platform_device.h> |
7 | #include <linux/dma-mapping.h> |
8 | #include <linux/workqueue.h> |
9 | #include <linux/libnvdimm.h> |
10 | #include <linux/genalloc.h> |
11 | #include <linux/vmalloc.h> |
12 | #include <linux/device.h> |
13 | #include <linux/module.h> |
14 | #include <linux/mutex.h> |
15 | #include <linux/ndctl.h> |
16 | #include <linux/sizes.h> |
17 | #include <linux/list.h> |
18 | #include <linux/slab.h> |
19 | #include <nd-core.h> |
20 | #include <intel.h> |
21 | #include <nfit.h> |
22 | #include <nd.h> |
23 | #include "nfit_test.h" |
24 | #include "../watermark.h" |
25 | |
26 | /* |
27 | * Generate an NFIT table to describe the following topology: |
28 | * |
29 | * BUS0: Interleaved PMEM regions, and aliasing with BLK regions |
30 | * |
31 | * (a) (b) DIMM BLK-REGION |
32 | * +----------+--------------+----------+---------+ |
33 | * +------+ | blk2.0 | pm0.0 | blk2.1 | pm1.0 | 0 region2 |
34 | * | imc0 +--+- - - - - region0 - - - -+----------+ + |
35 | * +--+---+ | blk3.0 | pm0.0 | blk3.1 | pm1.0 | 1 region3 |
36 | * | +----------+--------------v----------v v |
37 | * +--+---+ | | |
38 | * | cpu0 | region1 |
39 | * +--+---+ | | |
40 | * | +-------------------------^----------^ ^ |
41 | * +--+---+ | blk4.0 | pm1.0 | 2 region4 |
42 | * | imc1 +--+-------------------------+----------+ + |
43 | * +------+ | blk5.0 | pm1.0 | 3 region5 |
44 | * +-------------------------+----------+-+-------+ |
45 | * |
46 | * +--+---+ |
47 | * | cpu1 | |
48 | * +--+---+ (Hotplug DIMM) |
49 | * | +----------------------------------------------+ |
50 | * +--+---+ | blk6.0/pm7.0 | 4 region6/7 |
51 | * | imc0 +--+----------------------------------------------+ |
52 | * +------+ |
53 | * |
54 | * |
55 | * *) In this layout we have four dimms and two memory controllers in one |
56 | * socket. Each unique interface (BLK or PMEM) to DPA space |
57 | * is identified by a region device with a dynamically assigned id. |
58 | * |
59 | * *) The first portion of dimm0 and dimm1 are interleaved as REGION0. |
60 | * A single PMEM namespace "pm0.0" is created using half of the |
61 | * REGION0 SPA-range. REGION0 spans dimm0 and dimm1. PMEM namespace |
62 | * allocate from from the bottom of a region. The unallocated |
63 | * portion of REGION0 aliases with REGION2 and REGION3. That |
64 | * unallacted capacity is reclaimed as BLK namespaces ("blk2.0" and |
65 | * "blk3.0") starting at the base of each DIMM to offset (a) in those |
66 | * DIMMs. "pm0.0", "blk2.0" and "blk3.0" are free-form readable |
67 | * names that can be assigned to a namespace. |
68 | * |
69 | * *) In the last portion of dimm0 and dimm1 we have an interleaved |
70 | * SPA range, REGION1, that spans those two dimms as well as dimm2 |
71 | * and dimm3. Some of REGION1 allocated to a PMEM namespace named |
72 | * "pm1.0" the rest is reclaimed in 4 BLK namespaces (for each |
73 | * dimm in the interleave set), "blk2.1", "blk3.1", "blk4.0", and |
74 | * "blk5.0". |
75 | * |
76 | * *) The portion of dimm2 and dimm3 that do not participate in the |
77 | * REGION1 interleaved SPA range (i.e. the DPA address below offset |
78 | * (b) are also included in the "blk4.0" and "blk5.0" namespaces. |
79 | * Note, that BLK namespaces need not be contiguous in DPA-space, and |
80 | * can consume aliased capacity from multiple interleave sets. |
81 | * |
82 | * BUS1: Legacy NVDIMM (single contiguous range) |
83 | * |
84 | * region2 |
85 | * +---------------------+ |
86 | * |---------------------| |
87 | * || pm2.0 || |
88 | * |---------------------| |
89 | * +---------------------+ |
90 | * |
91 | * *) A NFIT-table may describe a simple system-physical-address range |
92 | * with no BLK aliasing. This type of region may optionally |
93 | * reference an NVDIMM. |
94 | */ |
95 | enum { |
96 | NUM_PM = 3, |
97 | NUM_DCR = 5, |
98 | NUM_HINTS = 8, |
99 | NUM_BDW = NUM_DCR, |
100 | NUM_SPA = NUM_PM + NUM_DCR + NUM_BDW, |
101 | NUM_MEM = NUM_DCR + NUM_BDW + 2 /* spa0 iset */ |
102 | + 4 /* spa1 iset */ + 1 /* spa11 iset */, |
103 | DIMM_SIZE = SZ_32M, |
104 | LABEL_SIZE = SZ_128K, |
105 | SPA_VCD_SIZE = SZ_4M, |
106 | SPA0_SIZE = DIMM_SIZE, |
107 | SPA1_SIZE = DIMM_SIZE*2, |
108 | SPA2_SIZE = DIMM_SIZE, |
109 | BDW_SIZE = 64 << 8, |
110 | DCR_SIZE = 12, |
111 | NUM_NFITS = 2, /* permit testing multiple NFITs per system */ |
112 | }; |
113 | |
114 | struct nfit_test_dcr { |
115 | __le64 bdw_addr; |
116 | __le32 bdw_status; |
117 | __u8 aperature[BDW_SIZE]; |
118 | }; |
119 | |
120 | #define NFIT_DIMM_HANDLE(node, socket, imc, chan, dimm) \ |
121 | (((node & 0xfff) << 16) | ((socket & 0xf) << 12) \ |
122 | | ((imc & 0xf) << 8) | ((chan & 0xf) << 4) | (dimm & 0xf)) |
123 | |
124 | static u32 handle[] = { |
125 | [0] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 0), |
126 | [1] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 1), |
127 | [2] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 0), |
128 | [3] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 1), |
129 | [4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0), |
130 | [5] = NFIT_DIMM_HANDLE(1, 0, 0, 0, 0), |
131 | [6] = NFIT_DIMM_HANDLE(1, 0, 0, 0, 1), |
132 | }; |
133 | |
134 | static unsigned long dimm_fail_cmd_flags[ARRAY_SIZE(handle)]; |
135 | static int dimm_fail_cmd_code[ARRAY_SIZE(handle)]; |
136 | struct nfit_test_sec { |
137 | u8 state; |
138 | u8 ext_state; |
139 | u8 old_state; |
140 | u8 passphrase[32]; |
141 | u8 master_passphrase[32]; |
142 | u64 overwrite_end_time; |
143 | } dimm_sec_info[NUM_DCR]; |
144 | |
145 | static const struct nd_intel_smart smart_def = { |
146 | .flags = ND_INTEL_SMART_HEALTH_VALID |
147 | | ND_INTEL_SMART_SPARES_VALID |
148 | | ND_INTEL_SMART_ALARM_VALID |
149 | | ND_INTEL_SMART_USED_VALID |
150 | | ND_INTEL_SMART_SHUTDOWN_VALID |
151 | | ND_INTEL_SMART_SHUTDOWN_COUNT_VALID |
152 | | ND_INTEL_SMART_MTEMP_VALID |
153 | | ND_INTEL_SMART_CTEMP_VALID, |
154 | .health = ND_INTEL_SMART_NON_CRITICAL_HEALTH, |
155 | .media_temperature = 23 * 16, |
156 | .ctrl_temperature = 25 * 16, |
157 | .pmic_temperature = 40 * 16, |
158 | .spares = 75, |
159 | .alarm_flags = ND_INTEL_SMART_SPARE_TRIP |
160 | | ND_INTEL_SMART_TEMP_TRIP, |
161 | .ait_status = 1, |
162 | .life_used = 5, |
163 | .shutdown_state = 0, |
164 | .shutdown_count = 42, |
165 | .vendor_size = 0, |
166 | }; |
167 | |
168 | struct nfit_test_fw { |
169 | enum intel_fw_update_state state; |
170 | u32 context; |
171 | u64 version; |
172 | u32 size_received; |
173 | u64 end_time; |
174 | bool armed; |
175 | bool missed_activate; |
176 | unsigned long last_activate; |
177 | }; |
178 | |
179 | struct nfit_test { |
180 | struct acpi_nfit_desc acpi_desc; |
181 | struct platform_device pdev; |
182 | struct list_head resources; |
183 | void *nfit_buf; |
184 | dma_addr_t nfit_dma; |
185 | size_t nfit_size; |
186 | size_t nfit_filled; |
187 | int dcr_idx; |
188 | int num_dcr; |
189 | int num_pm; |
190 | void **dimm; |
191 | dma_addr_t *dimm_dma; |
192 | void **flush; |
193 | dma_addr_t *flush_dma; |
194 | void **label; |
195 | dma_addr_t *label_dma; |
196 | void **spa_set; |
197 | dma_addr_t *spa_set_dma; |
198 | struct nfit_test_dcr **dcr; |
199 | dma_addr_t *dcr_dma; |
200 | int (*alloc)(struct nfit_test *t); |
201 | void (*setup)(struct nfit_test *t); |
202 | int setup_hotplug; |
203 | union acpi_object **_fit; |
204 | dma_addr_t _fit_dma; |
205 | struct ars_state { |
206 | struct nd_cmd_ars_status *ars_status; |
207 | unsigned long deadline; |
208 | spinlock_t lock; |
209 | } ars_state; |
210 | struct device *dimm_dev[ARRAY_SIZE(handle)]; |
211 | struct nd_intel_smart *smart; |
212 | struct nd_intel_smart_threshold *smart_threshold; |
213 | struct badrange badrange; |
214 | struct work_struct work; |
215 | struct nfit_test_fw *fw; |
216 | }; |
217 | |
218 | static struct workqueue_struct *nfit_wq; |
219 | |
220 | static struct gen_pool *nfit_pool; |
221 | |
222 | static const char zero_key[NVDIMM_PASSPHRASE_LEN]; |
223 | |
224 | static struct nfit_test *to_nfit_test(struct device *dev) |
225 | { |
226 | struct platform_device *pdev = to_platform_device(dev); |
227 | |
228 | return container_of(pdev, struct nfit_test, pdev); |
229 | } |
230 | |
231 | static int nd_intel_test_get_fw_info(struct nfit_test *t, |
232 | struct nd_intel_fw_info *nd_cmd, unsigned int buf_len, |
233 | int idx) |
234 | { |
235 | struct device *dev = &t->pdev.dev; |
236 | struct nfit_test_fw *fw = &t->fw[idx]; |
237 | |
238 | dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p, buf_len: %u, idx: %d\n" , |
239 | __func__, t, nd_cmd, buf_len, idx); |
240 | |
241 | if (buf_len < sizeof(*nd_cmd)) |
242 | return -EINVAL; |
243 | |
244 | nd_cmd->status = 0; |
245 | nd_cmd->storage_size = INTEL_FW_STORAGE_SIZE; |
246 | nd_cmd->max_send_len = INTEL_FW_MAX_SEND_LEN; |
247 | nd_cmd->query_interval = INTEL_FW_QUERY_INTERVAL; |
248 | nd_cmd->max_query_time = INTEL_FW_QUERY_MAX_TIME; |
249 | nd_cmd->update_cap = 0; |
250 | nd_cmd->fis_version = INTEL_FW_FIS_VERSION; |
251 | nd_cmd->run_version = 0; |
252 | nd_cmd->updated_version = fw->version; |
253 | |
254 | return 0; |
255 | } |
256 | |
257 | static int nd_intel_test_start_update(struct nfit_test *t, |
258 | struct nd_intel_fw_start *nd_cmd, unsigned int buf_len, |
259 | int idx) |
260 | { |
261 | struct device *dev = &t->pdev.dev; |
262 | struct nfit_test_fw *fw = &t->fw[idx]; |
263 | |
264 | dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n" , |
265 | __func__, t, nd_cmd, buf_len, idx); |
266 | |
267 | if (buf_len < sizeof(*nd_cmd)) |
268 | return -EINVAL; |
269 | |
270 | if (fw->state != FW_STATE_NEW) { |
271 | /* extended status, FW update in progress */ |
272 | nd_cmd->status = 0x10007; |
273 | return 0; |
274 | } |
275 | |
276 | fw->state = FW_STATE_IN_PROGRESS; |
277 | fw->context++; |
278 | fw->size_received = 0; |
279 | nd_cmd->status = 0; |
280 | nd_cmd->context = fw->context; |
281 | |
282 | dev_dbg(dev, "%s: context issued: %#x\n" , __func__, nd_cmd->context); |
283 | |
284 | return 0; |
285 | } |
286 | |
287 | static int nd_intel_test_send_data(struct nfit_test *t, |
288 | struct nd_intel_fw_send_data *nd_cmd, unsigned int buf_len, |
289 | int idx) |
290 | { |
291 | struct device *dev = &t->pdev.dev; |
292 | struct nfit_test_fw *fw = &t->fw[idx]; |
293 | u32 *status = (u32 *)&nd_cmd->data[nd_cmd->length]; |
294 | |
295 | dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n" , |
296 | __func__, t, nd_cmd, buf_len, idx); |
297 | |
298 | if (buf_len < sizeof(*nd_cmd)) |
299 | return -EINVAL; |
300 | |
301 | |
302 | dev_dbg(dev, "%s: cmd->status: %#x\n" , __func__, *status); |
303 | dev_dbg(dev, "%s: cmd->data[0]: %#x\n" , __func__, nd_cmd->data[0]); |
304 | dev_dbg(dev, "%s: cmd->data[%u]: %#x\n" , __func__, nd_cmd->length-1, |
305 | nd_cmd->data[nd_cmd->length-1]); |
306 | |
307 | if (fw->state != FW_STATE_IN_PROGRESS) { |
308 | dev_dbg(dev, "%s: not in IN_PROGRESS state\n" , __func__); |
309 | *status = 0x5; |
310 | return 0; |
311 | } |
312 | |
313 | if (nd_cmd->context != fw->context) { |
314 | dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n" , |
315 | __func__, nd_cmd->context, fw->context); |
316 | *status = 0x10007; |
317 | return 0; |
318 | } |
319 | |
320 | /* |
321 | * check offset + len > size of fw storage |
322 | * check length is > max send length |
323 | */ |
324 | if (nd_cmd->offset + nd_cmd->length > INTEL_FW_STORAGE_SIZE || |
325 | nd_cmd->length > INTEL_FW_MAX_SEND_LEN) { |
326 | *status = 0x3; |
327 | dev_dbg(dev, "%s: buffer boundary violation\n" , __func__); |
328 | return 0; |
329 | } |
330 | |
331 | fw->size_received += nd_cmd->length; |
332 | dev_dbg(dev, "%s: copying %u bytes, %u bytes so far\n" , |
333 | __func__, nd_cmd->length, fw->size_received); |
334 | *status = 0; |
335 | return 0; |
336 | } |
337 | |
338 | static int nd_intel_test_finish_fw(struct nfit_test *t, |
339 | struct nd_intel_fw_finish_update *nd_cmd, |
340 | unsigned int buf_len, int idx) |
341 | { |
342 | struct device *dev = &t->pdev.dev; |
343 | struct nfit_test_fw *fw = &t->fw[idx]; |
344 | |
345 | dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n" , |
346 | __func__, t, nd_cmd, buf_len, idx); |
347 | |
348 | if (fw->state == FW_STATE_UPDATED) { |
349 | /* update already done, need activation */ |
350 | nd_cmd->status = 0x20007; |
351 | return 0; |
352 | } |
353 | |
354 | dev_dbg(dev, "%s: context: %#x ctrl_flags: %#x\n" , |
355 | __func__, nd_cmd->context, nd_cmd->ctrl_flags); |
356 | |
357 | switch (nd_cmd->ctrl_flags) { |
358 | case 0: /* finish */ |
359 | if (nd_cmd->context != fw->context) { |
360 | dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n" , |
361 | __func__, nd_cmd->context, |
362 | fw->context); |
363 | nd_cmd->status = 0x10007; |
364 | return 0; |
365 | } |
366 | nd_cmd->status = 0; |
367 | fw->state = FW_STATE_VERIFY; |
368 | /* set 1 second of time for firmware "update" */ |
369 | fw->end_time = jiffies + HZ; |
370 | break; |
371 | |
372 | case 1: /* abort */ |
373 | fw->size_received = 0; |
374 | /* successfully aborted status */ |
375 | nd_cmd->status = 0x40007; |
376 | fw->state = FW_STATE_NEW; |
377 | dev_dbg(dev, "%s: abort successful\n" , __func__); |
378 | break; |
379 | |
380 | default: /* bad control flag */ |
381 | dev_warn(dev, "%s: unknown control flag: %#x\n" , |
382 | __func__, nd_cmd->ctrl_flags); |
383 | return -EINVAL; |
384 | } |
385 | |
386 | return 0; |
387 | } |
388 | |
389 | static int nd_intel_test_finish_query(struct nfit_test *t, |
390 | struct nd_intel_fw_finish_query *nd_cmd, |
391 | unsigned int buf_len, int idx) |
392 | { |
393 | struct device *dev = &t->pdev.dev; |
394 | struct nfit_test_fw *fw = &t->fw[idx]; |
395 | |
396 | dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n" , |
397 | __func__, t, nd_cmd, buf_len, idx); |
398 | |
399 | if (buf_len < sizeof(*nd_cmd)) |
400 | return -EINVAL; |
401 | |
402 | if (nd_cmd->context != fw->context) { |
403 | dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n" , |
404 | __func__, nd_cmd->context, fw->context); |
405 | nd_cmd->status = 0x10007; |
406 | return 0; |
407 | } |
408 | |
409 | dev_dbg(dev, "%s context: %#x\n" , __func__, nd_cmd->context); |
410 | |
411 | switch (fw->state) { |
412 | case FW_STATE_NEW: |
413 | nd_cmd->updated_fw_rev = 0; |
414 | nd_cmd->status = 0; |
415 | dev_dbg(dev, "%s: new state\n" , __func__); |
416 | break; |
417 | |
418 | case FW_STATE_IN_PROGRESS: |
419 | /* sequencing error */ |
420 | nd_cmd->status = 0x40007; |
421 | nd_cmd->updated_fw_rev = 0; |
422 | dev_dbg(dev, "%s: sequence error\n" , __func__); |
423 | break; |
424 | |
425 | case FW_STATE_VERIFY: |
426 | if (time_is_after_jiffies64(fw->end_time)) { |
427 | nd_cmd->updated_fw_rev = 0; |
428 | nd_cmd->status = 0x20007; |
429 | dev_dbg(dev, "%s: still verifying\n" , __func__); |
430 | break; |
431 | } |
432 | dev_dbg(dev, "%s: transition out verify\n" , __func__); |
433 | fw->state = FW_STATE_UPDATED; |
434 | fw->missed_activate = false; |
435 | fallthrough; |
436 | case FW_STATE_UPDATED: |
437 | nd_cmd->status = 0; |
438 | /* bogus test version */ |
439 | fw->version = nd_cmd->updated_fw_rev = |
440 | INTEL_FW_FAKE_VERSION; |
441 | dev_dbg(dev, "%s: updated\n" , __func__); |
442 | break; |
443 | |
444 | default: /* we should never get here */ |
445 | return -EINVAL; |
446 | } |
447 | |
448 | return 0; |
449 | } |
450 | |
451 | static int nfit_test_cmd_get_config_size(struct nd_cmd_get_config_size *nd_cmd, |
452 | unsigned int buf_len) |
453 | { |
454 | if (buf_len < sizeof(*nd_cmd)) |
455 | return -EINVAL; |
456 | |
457 | nd_cmd->status = 0; |
458 | nd_cmd->config_size = LABEL_SIZE; |
459 | nd_cmd->max_xfer = SZ_4K; |
460 | |
461 | return 0; |
462 | } |
463 | |
464 | static int nfit_test_cmd_get_config_data(struct nd_cmd_get_config_data_hdr |
465 | *nd_cmd, unsigned int buf_len, void *label) |
466 | { |
467 | unsigned int len, offset = nd_cmd->in_offset; |
468 | int rc; |
469 | |
470 | if (buf_len < sizeof(*nd_cmd)) |
471 | return -EINVAL; |
472 | if (offset >= LABEL_SIZE) |
473 | return -EINVAL; |
474 | if (nd_cmd->in_length + sizeof(*nd_cmd) > buf_len) |
475 | return -EINVAL; |
476 | |
477 | nd_cmd->status = 0; |
478 | len = min(nd_cmd->in_length, LABEL_SIZE - offset); |
479 | memcpy(nd_cmd->out_buf, label + offset, len); |
480 | rc = buf_len - sizeof(*nd_cmd) - len; |
481 | |
482 | return rc; |
483 | } |
484 | |
485 | static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd, |
486 | unsigned int buf_len, void *label) |
487 | { |
488 | unsigned int len, offset = nd_cmd->in_offset; |
489 | u32 *status; |
490 | int rc; |
491 | |
492 | if (buf_len < sizeof(*nd_cmd)) |
493 | return -EINVAL; |
494 | if (offset >= LABEL_SIZE) |
495 | return -EINVAL; |
496 | if (nd_cmd->in_length + sizeof(*nd_cmd) + 4 > buf_len) |
497 | return -EINVAL; |
498 | |
499 | status = (void *)nd_cmd + nd_cmd->in_length + sizeof(*nd_cmd); |
500 | *status = 0; |
501 | len = min(nd_cmd->in_length, LABEL_SIZE - offset); |
502 | memcpy(label + offset, nd_cmd->in_buf, len); |
503 | rc = buf_len - sizeof(*nd_cmd) - (len + 4); |
504 | |
505 | return rc; |
506 | } |
507 | |
508 | #define NFIT_TEST_CLEAR_ERR_UNIT 256 |
509 | |
510 | static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, |
511 | unsigned int buf_len) |
512 | { |
513 | int ars_recs; |
514 | |
515 | if (buf_len < sizeof(*nd_cmd)) |
516 | return -EINVAL; |
517 | |
518 | /* for testing, only store up to n records that fit within 4k */ |
519 | ars_recs = SZ_4K / sizeof(struct nd_ars_record); |
520 | |
521 | nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status) |
522 | + ars_recs * sizeof(struct nd_ars_record); |
523 | nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; |
524 | nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT; |
525 | |
526 | return 0; |
527 | } |
528 | |
529 | static void post_ars_status(struct ars_state *ars_state, |
530 | struct badrange *badrange, u64 addr, u64 len) |
531 | { |
532 | struct nd_cmd_ars_status *ars_status; |
533 | struct nd_ars_record *ars_record; |
534 | struct badrange_entry *be; |
535 | u64 end = addr + len - 1; |
536 | int i = 0; |
537 | |
538 | ars_state->deadline = jiffies + 1*HZ; |
539 | ars_status = ars_state->ars_status; |
540 | ars_status->status = 0; |
541 | ars_status->address = addr; |
542 | ars_status->length = len; |
543 | ars_status->type = ND_ARS_PERSISTENT; |
544 | |
545 | spin_lock(lock: &badrange->lock); |
546 | list_for_each_entry(be, &badrange->list, list) { |
547 | u64 be_end = be->start + be->length - 1; |
548 | u64 rstart, rend; |
549 | |
550 | /* skip entries outside the range */ |
551 | if (be_end < addr || be->start > end) |
552 | continue; |
553 | |
554 | rstart = (be->start < addr) ? addr : be->start; |
555 | rend = (be_end < end) ? be_end : end; |
556 | ars_record = &ars_status->records[i]; |
557 | ars_record->handle = 0; |
558 | ars_record->err_address = rstart; |
559 | ars_record->length = rend - rstart + 1; |
560 | i++; |
561 | } |
562 | spin_unlock(lock: &badrange->lock); |
563 | ars_status->num_records = i; |
564 | ars_status->out_length = sizeof(struct nd_cmd_ars_status) |
565 | + i * sizeof(struct nd_ars_record); |
566 | } |
567 | |
568 | static int nfit_test_cmd_ars_start(struct nfit_test *t, |
569 | struct ars_state *ars_state, |
570 | struct nd_cmd_ars_start *ars_start, unsigned int buf_len, |
571 | int *cmd_rc) |
572 | { |
573 | if (buf_len < sizeof(*ars_start)) |
574 | return -EINVAL; |
575 | |
576 | spin_lock(lock: &ars_state->lock); |
577 | if (time_before(jiffies, ars_state->deadline)) { |
578 | ars_start->status = NFIT_ARS_START_BUSY; |
579 | *cmd_rc = -EBUSY; |
580 | } else { |
581 | ars_start->status = 0; |
582 | ars_start->scrub_time = 1; |
583 | post_ars_status(ars_state, badrange: &t->badrange, addr: ars_start->address, |
584 | len: ars_start->length); |
585 | *cmd_rc = 0; |
586 | } |
587 | spin_unlock(lock: &ars_state->lock); |
588 | |
589 | return 0; |
590 | } |
591 | |
592 | static int nfit_test_cmd_ars_status(struct ars_state *ars_state, |
593 | struct nd_cmd_ars_status *ars_status, unsigned int buf_len, |
594 | int *cmd_rc) |
595 | { |
596 | if (buf_len < ars_state->ars_status->out_length) |
597 | return -EINVAL; |
598 | |
599 | spin_lock(lock: &ars_state->lock); |
600 | if (time_before(jiffies, ars_state->deadline)) { |
601 | memset(ars_status, 0, buf_len); |
602 | ars_status->status = NFIT_ARS_STATUS_BUSY; |
603 | ars_status->out_length = sizeof(*ars_status); |
604 | *cmd_rc = -EBUSY; |
605 | } else { |
606 | memcpy(ars_status, ars_state->ars_status, |
607 | ars_state->ars_status->out_length); |
608 | *cmd_rc = 0; |
609 | } |
610 | spin_unlock(lock: &ars_state->lock); |
611 | return 0; |
612 | } |
613 | |
614 | static int nfit_test_cmd_clear_error(struct nfit_test *t, |
615 | struct nd_cmd_clear_error *clear_err, |
616 | unsigned int buf_len, int *cmd_rc) |
617 | { |
618 | const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1; |
619 | if (buf_len < sizeof(*clear_err)) |
620 | return -EINVAL; |
621 | |
622 | if ((clear_err->address & mask) || (clear_err->length & mask)) |
623 | return -EINVAL; |
624 | |
625 | badrange_forget(badrange: &t->badrange, start: clear_err->address, len: clear_err->length); |
626 | clear_err->status = 0; |
627 | clear_err->cleared = clear_err->length; |
628 | *cmd_rc = 0; |
629 | return 0; |
630 | } |
631 | |
632 | struct region_search_spa { |
633 | u64 addr; |
634 | struct nd_region *region; |
635 | }; |
636 | |
637 | static int is_region_device(struct device *dev) |
638 | { |
639 | return !strncmp(dev->kobj.name, "region" , 6); |
640 | } |
641 | |
642 | static int nfit_test_search_region_spa(struct device *dev, void *data) |
643 | { |
644 | struct region_search_spa *ctx = data; |
645 | struct nd_region *nd_region; |
646 | resource_size_t ndr_end; |
647 | |
648 | if (!is_region_device(dev)) |
649 | return 0; |
650 | |
651 | nd_region = to_nd_region(dev); |
652 | ndr_end = nd_region->ndr_start + nd_region->ndr_size; |
653 | |
654 | if (ctx->addr >= nd_region->ndr_start && ctx->addr < ndr_end) { |
655 | ctx->region = nd_region; |
656 | return 1; |
657 | } |
658 | |
659 | return 0; |
660 | } |
661 | |
662 | static int nfit_test_search_spa(struct nvdimm_bus *bus, |
663 | struct nd_cmd_translate_spa *spa) |
664 | { |
665 | int ret; |
666 | struct nd_region *nd_region = NULL; |
667 | struct nvdimm *nvdimm = NULL; |
668 | struct nd_mapping *nd_mapping = NULL; |
669 | struct region_search_spa ctx = { |
670 | .addr = spa->spa, |
671 | .region = NULL, |
672 | }; |
673 | u64 dpa; |
674 | |
675 | ret = device_for_each_child(dev: &bus->dev, data: &ctx, |
676 | fn: nfit_test_search_region_spa); |
677 | |
678 | if (!ret) |
679 | return -ENODEV; |
680 | |
681 | nd_region = ctx.region; |
682 | |
683 | dpa = ctx.addr - nd_region->ndr_start; |
684 | |
685 | /* |
686 | * last dimm is selected for test |
687 | */ |
688 | nd_mapping = &nd_region->mapping[nd_region->ndr_mappings - 1]; |
689 | nvdimm = nd_mapping->nvdimm; |
690 | |
691 | spa->devices[0].nfit_device_handle = handle[nvdimm->id]; |
692 | spa->num_nvdimms = 1; |
693 | spa->devices[0].dpa = dpa; |
694 | |
695 | return 0; |
696 | } |
697 | |
698 | static int nfit_test_cmd_translate_spa(struct nvdimm_bus *bus, |
699 | struct nd_cmd_translate_spa *spa, unsigned int buf_len) |
700 | { |
701 | if (buf_len < spa->translate_length) |
702 | return -EINVAL; |
703 | |
704 | if (nfit_test_search_spa(bus, spa) < 0 || !spa->num_nvdimms) |
705 | spa->status = 2; |
706 | |
707 | return 0; |
708 | } |
709 | |
710 | static int nfit_test_cmd_smart(struct nd_intel_smart *smart, unsigned int buf_len, |
711 | struct nd_intel_smart *smart_data) |
712 | { |
713 | if (buf_len < sizeof(*smart)) |
714 | return -EINVAL; |
715 | memcpy(smart, smart_data, sizeof(*smart)); |
716 | return 0; |
717 | } |
718 | |
719 | static int nfit_test_cmd_smart_threshold( |
720 | struct nd_intel_smart_threshold *out, |
721 | unsigned int buf_len, |
722 | struct nd_intel_smart_threshold *smart_t) |
723 | { |
724 | if (buf_len < sizeof(*smart_t)) |
725 | return -EINVAL; |
726 | memcpy(out, smart_t, sizeof(*smart_t)); |
727 | return 0; |
728 | } |
729 | |
730 | static void smart_notify(struct device *bus_dev, |
731 | struct device *dimm_dev, struct nd_intel_smart *smart, |
732 | struct nd_intel_smart_threshold *thresh) |
733 | { |
734 | dev_dbg(dimm_dev, "%s: alarm: %#x spares: %d (%d) mtemp: %d (%d) ctemp: %d (%d)\n" , |
735 | __func__, thresh->alarm_control, thresh->spares, |
736 | smart->spares, thresh->media_temperature, |
737 | smart->media_temperature, thresh->ctrl_temperature, |
738 | smart->ctrl_temperature); |
739 | if (((thresh->alarm_control & ND_INTEL_SMART_SPARE_TRIP) |
740 | && smart->spares |
741 | <= thresh->spares) |
742 | || ((thresh->alarm_control & ND_INTEL_SMART_TEMP_TRIP) |
743 | && smart->media_temperature |
744 | >= thresh->media_temperature) |
745 | || ((thresh->alarm_control & ND_INTEL_SMART_CTEMP_TRIP) |
746 | && smart->ctrl_temperature |
747 | >= thresh->ctrl_temperature) |
748 | || (smart->health != ND_INTEL_SMART_NON_CRITICAL_HEALTH) |
749 | || (smart->shutdown_state != 0)) { |
750 | device_lock(dev: bus_dev); |
751 | __acpi_nvdimm_notify(dimm_dev, 0x81); |
752 | device_unlock(dev: bus_dev); |
753 | } |
754 | } |
755 | |
756 | static int nfit_test_cmd_smart_set_threshold( |
757 | struct nd_intel_smart_set_threshold *in, |
758 | unsigned int buf_len, |
759 | struct nd_intel_smart_threshold *thresh, |
760 | struct nd_intel_smart *smart, |
761 | struct device *bus_dev, struct device *dimm_dev) |
762 | { |
763 | unsigned int size; |
764 | |
765 | size = sizeof(*in) - 4; |
766 | if (buf_len < size) |
767 | return -EINVAL; |
768 | memcpy(thresh->data, in, size); |
769 | in->status = 0; |
770 | smart_notify(bus_dev, dimm_dev, smart, thresh); |
771 | |
772 | return 0; |
773 | } |
774 | |
775 | static int nfit_test_cmd_smart_inject( |
776 | struct nd_intel_smart_inject *inj, |
777 | unsigned int buf_len, |
778 | struct nd_intel_smart_threshold *thresh, |
779 | struct nd_intel_smart *smart, |
780 | struct device *bus_dev, struct device *dimm_dev) |
781 | { |
782 | if (buf_len != sizeof(*inj)) |
783 | return -EINVAL; |
784 | |
785 | if (inj->flags & ND_INTEL_SMART_INJECT_MTEMP) { |
786 | if (inj->mtemp_enable) |
787 | smart->media_temperature = inj->media_temperature; |
788 | else |
789 | smart->media_temperature = smart_def.media_temperature; |
790 | } |
791 | if (inj->flags & ND_INTEL_SMART_INJECT_SPARE) { |
792 | if (inj->spare_enable) |
793 | smart->spares = inj->spares; |
794 | else |
795 | smart->spares = smart_def.spares; |
796 | } |
797 | if (inj->flags & ND_INTEL_SMART_INJECT_FATAL) { |
798 | if (inj->fatal_enable) |
799 | smart->health = ND_INTEL_SMART_FATAL_HEALTH; |
800 | else |
801 | smart->health = ND_INTEL_SMART_NON_CRITICAL_HEALTH; |
802 | } |
803 | if (inj->flags & ND_INTEL_SMART_INJECT_SHUTDOWN) { |
804 | if (inj->unsafe_shutdown_enable) { |
805 | smart->shutdown_state = 1; |
806 | smart->shutdown_count++; |
807 | } else |
808 | smart->shutdown_state = 0; |
809 | } |
810 | inj->status = 0; |
811 | smart_notify(bus_dev, dimm_dev, smart, thresh); |
812 | |
813 | return 0; |
814 | } |
815 | |
816 | static void uc_error_notify(struct work_struct *work) |
817 | { |
818 | struct nfit_test *t = container_of(work, typeof(*t), work); |
819 | |
820 | __acpi_nfit_notify(&t->pdev.dev, t, NFIT_NOTIFY_UC_MEMORY_ERROR); |
821 | } |
822 | |
823 | static int nfit_test_cmd_ars_error_inject(struct nfit_test *t, |
824 | struct nd_cmd_ars_err_inj *err_inj, unsigned int buf_len) |
825 | { |
826 | int rc; |
827 | |
828 | if (buf_len != sizeof(*err_inj)) { |
829 | rc = -EINVAL; |
830 | goto err; |
831 | } |
832 | |
833 | if (err_inj->err_inj_spa_range_length <= 0) { |
834 | rc = -EINVAL; |
835 | goto err; |
836 | } |
837 | |
838 | rc = badrange_add(badrange: &t->badrange, addr: err_inj->err_inj_spa_range_base, |
839 | length: err_inj->err_inj_spa_range_length); |
840 | if (rc < 0) |
841 | goto err; |
842 | |
843 | if (err_inj->err_inj_options & (1 << ND_ARS_ERR_INJ_OPT_NOTIFY)) |
844 | queue_work(wq: nfit_wq, work: &t->work); |
845 | |
846 | err_inj->status = 0; |
847 | return 0; |
848 | |
849 | err: |
850 | err_inj->status = NFIT_ARS_INJECT_INVALID; |
851 | return rc; |
852 | } |
853 | |
854 | static int nfit_test_cmd_ars_inject_clear(struct nfit_test *t, |
855 | struct nd_cmd_ars_err_inj_clr *err_clr, unsigned int buf_len) |
856 | { |
857 | int rc; |
858 | |
859 | if (buf_len != sizeof(*err_clr)) { |
860 | rc = -EINVAL; |
861 | goto err; |
862 | } |
863 | |
864 | if (err_clr->err_inj_clr_spa_range_length <= 0) { |
865 | rc = -EINVAL; |
866 | goto err; |
867 | } |
868 | |
869 | badrange_forget(badrange: &t->badrange, start: err_clr->err_inj_clr_spa_range_base, |
870 | len: err_clr->err_inj_clr_spa_range_length); |
871 | |
872 | err_clr->status = 0; |
873 | return 0; |
874 | |
875 | err: |
876 | err_clr->status = NFIT_ARS_INJECT_INVALID; |
877 | return rc; |
878 | } |
879 | |
880 | static int nfit_test_cmd_ars_inject_status(struct nfit_test *t, |
881 | struct nd_cmd_ars_err_inj_stat *err_stat, |
882 | unsigned int buf_len) |
883 | { |
884 | struct badrange_entry *be; |
885 | int max = SZ_4K / sizeof(struct nd_error_stat_query_record); |
886 | int i = 0; |
887 | |
888 | err_stat->status = 0; |
889 | spin_lock(lock: &t->badrange.lock); |
890 | list_for_each_entry(be, &t->badrange.list, list) { |
891 | err_stat->record[i].err_inj_stat_spa_range_base = be->start; |
892 | err_stat->record[i].err_inj_stat_spa_range_length = be->length; |
893 | i++; |
894 | if (i > max) |
895 | break; |
896 | } |
897 | spin_unlock(lock: &t->badrange.lock); |
898 | err_stat->inj_err_rec_count = i; |
899 | |
900 | return 0; |
901 | } |
902 | |
903 | static int nd_intel_test_cmd_set_lss_status(struct nfit_test *t, |
904 | struct nd_intel_lss *nd_cmd, unsigned int buf_len) |
905 | { |
906 | struct device *dev = &t->pdev.dev; |
907 | |
908 | if (buf_len < sizeof(*nd_cmd)) |
909 | return -EINVAL; |
910 | |
911 | switch (nd_cmd->enable) { |
912 | case 0: |
913 | nd_cmd->status = 0; |
914 | dev_dbg(dev, "%s: Latch System Shutdown Status disabled\n" , |
915 | __func__); |
916 | break; |
917 | case 1: |
918 | nd_cmd->status = 0; |
919 | dev_dbg(dev, "%s: Latch System Shutdown Status enabled\n" , |
920 | __func__); |
921 | break; |
922 | default: |
923 | dev_warn(dev, "Unknown enable value: %#x\n" , nd_cmd->enable); |
924 | nd_cmd->status = 0x3; |
925 | break; |
926 | } |
927 | |
928 | |
929 | return 0; |
930 | } |
931 | |
932 | static int override_return_code(int dimm, unsigned int func, int rc) |
933 | { |
934 | if ((1 << func) & dimm_fail_cmd_flags[dimm]) { |
935 | if (dimm_fail_cmd_code[dimm]) |
936 | return dimm_fail_cmd_code[dimm]; |
937 | return -EIO; |
938 | } |
939 | return rc; |
940 | } |
941 | |
942 | static int nd_intel_test_cmd_security_status(struct nfit_test *t, |
943 | struct nd_intel_get_security_state *nd_cmd, |
944 | unsigned int buf_len, int dimm) |
945 | { |
946 | struct device *dev = &t->pdev.dev; |
947 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
948 | |
949 | nd_cmd->status = 0; |
950 | nd_cmd->state = sec->state; |
951 | nd_cmd->extended_state = sec->ext_state; |
952 | dev_dbg(dev, "security state (%#x) returned\n" , nd_cmd->state); |
953 | |
954 | return 0; |
955 | } |
956 | |
957 | static int nd_intel_test_cmd_unlock_unit(struct nfit_test *t, |
958 | struct nd_intel_unlock_unit *nd_cmd, |
959 | unsigned int buf_len, int dimm) |
960 | { |
961 | struct device *dev = &t->pdev.dev; |
962 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
963 | |
964 | if (!(sec->state & ND_INTEL_SEC_STATE_LOCKED) || |
965 | (sec->state & ND_INTEL_SEC_STATE_FROZEN)) { |
966 | nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; |
967 | dev_dbg(dev, "unlock unit: invalid state: %#x\n" , |
968 | sec->state); |
969 | } else if (memcmp(nd_cmd->passphrase, sec->passphrase, |
970 | ND_INTEL_PASSPHRASE_SIZE) != 0) { |
971 | nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; |
972 | dev_dbg(dev, "unlock unit: invalid passphrase\n" ); |
973 | } else { |
974 | nd_cmd->status = 0; |
975 | sec->state = ND_INTEL_SEC_STATE_ENABLED; |
976 | dev_dbg(dev, "Unit unlocked\n" ); |
977 | } |
978 | |
979 | dev_dbg(dev, "unlocking status returned: %#x\n" , nd_cmd->status); |
980 | return 0; |
981 | } |
982 | |
983 | static int nd_intel_test_cmd_set_pass(struct nfit_test *t, |
984 | struct nd_intel_set_passphrase *nd_cmd, |
985 | unsigned int buf_len, int dimm) |
986 | { |
987 | struct device *dev = &t->pdev.dev; |
988 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
989 | |
990 | if (sec->state & ND_INTEL_SEC_STATE_FROZEN) { |
991 | nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; |
992 | dev_dbg(dev, "set passphrase: wrong security state\n" ); |
993 | } else if (memcmp(nd_cmd->old_pass, sec->passphrase, |
994 | ND_INTEL_PASSPHRASE_SIZE) != 0) { |
995 | nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; |
996 | dev_dbg(dev, "set passphrase: wrong passphrase\n" ); |
997 | } else { |
998 | memcpy(sec->passphrase, nd_cmd->new_pass, |
999 | ND_INTEL_PASSPHRASE_SIZE); |
1000 | sec->state |= ND_INTEL_SEC_STATE_ENABLED; |
1001 | nd_cmd->status = 0; |
1002 | dev_dbg(dev, "passphrase updated\n" ); |
1003 | } |
1004 | |
1005 | return 0; |
1006 | } |
1007 | |
1008 | static int nd_intel_test_cmd_freeze_lock(struct nfit_test *t, |
1009 | struct nd_intel_freeze_lock *nd_cmd, |
1010 | unsigned int buf_len, int dimm) |
1011 | { |
1012 | struct device *dev = &t->pdev.dev; |
1013 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
1014 | |
1015 | if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED)) { |
1016 | nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; |
1017 | dev_dbg(dev, "freeze lock: wrong security state\n" ); |
1018 | } else { |
1019 | sec->state |= ND_INTEL_SEC_STATE_FROZEN; |
1020 | nd_cmd->status = 0; |
1021 | dev_dbg(dev, "security frozen\n" ); |
1022 | } |
1023 | |
1024 | return 0; |
1025 | } |
1026 | |
1027 | static int nd_intel_test_cmd_disable_pass(struct nfit_test *t, |
1028 | struct nd_intel_disable_passphrase *nd_cmd, |
1029 | unsigned int buf_len, int dimm) |
1030 | { |
1031 | struct device *dev = &t->pdev.dev; |
1032 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
1033 | |
1034 | if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED) || |
1035 | (sec->state & ND_INTEL_SEC_STATE_FROZEN)) { |
1036 | nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; |
1037 | dev_dbg(dev, "disable passphrase: wrong security state\n" ); |
1038 | } else if (memcmp(nd_cmd->passphrase, sec->passphrase, |
1039 | ND_INTEL_PASSPHRASE_SIZE) != 0) { |
1040 | nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; |
1041 | dev_dbg(dev, "disable passphrase: wrong passphrase\n" ); |
1042 | } else { |
1043 | memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); |
1044 | sec->state = 0; |
1045 | dev_dbg(dev, "disable passphrase: done\n" ); |
1046 | } |
1047 | |
1048 | return 0; |
1049 | } |
1050 | |
1051 | static int nd_intel_test_cmd_secure_erase(struct nfit_test *t, |
1052 | struct nd_intel_secure_erase *nd_cmd, |
1053 | unsigned int buf_len, int dimm) |
1054 | { |
1055 | struct device *dev = &t->pdev.dev; |
1056 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
1057 | |
1058 | if (sec->state & ND_INTEL_SEC_STATE_FROZEN) { |
1059 | nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; |
1060 | dev_dbg(dev, "secure erase: wrong security state\n" ); |
1061 | } else if (memcmp(nd_cmd->passphrase, sec->passphrase, |
1062 | ND_INTEL_PASSPHRASE_SIZE) != 0) { |
1063 | nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; |
1064 | dev_dbg(dev, "secure erase: wrong passphrase\n" ); |
1065 | } else { |
1066 | if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED) |
1067 | && (memcmp(nd_cmd->passphrase, zero_key, |
1068 | ND_INTEL_PASSPHRASE_SIZE) != 0)) { |
1069 | dev_dbg(dev, "invalid zero key\n" ); |
1070 | return 0; |
1071 | } |
1072 | memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); |
1073 | memset(sec->master_passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); |
1074 | sec->state = 0; |
1075 | sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; |
1076 | dev_dbg(dev, "secure erase: done\n" ); |
1077 | } |
1078 | |
1079 | return 0; |
1080 | } |
1081 | |
1082 | static int nd_intel_test_cmd_overwrite(struct nfit_test *t, |
1083 | struct nd_intel_overwrite *nd_cmd, |
1084 | unsigned int buf_len, int dimm) |
1085 | { |
1086 | struct device *dev = &t->pdev.dev; |
1087 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
1088 | |
1089 | if ((sec->state & ND_INTEL_SEC_STATE_ENABLED) && |
1090 | memcmp(nd_cmd->passphrase, sec->passphrase, |
1091 | ND_INTEL_PASSPHRASE_SIZE) != 0) { |
1092 | nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; |
1093 | dev_dbg(dev, "overwrite: wrong passphrase\n" ); |
1094 | return 0; |
1095 | } |
1096 | |
1097 | sec->old_state = sec->state; |
1098 | sec->state = ND_INTEL_SEC_STATE_OVERWRITE; |
1099 | dev_dbg(dev, "overwrite progressing.\n" ); |
1100 | sec->overwrite_end_time = get_jiffies_64() + 5 * HZ; |
1101 | |
1102 | return 0; |
1103 | } |
1104 | |
1105 | static int nd_intel_test_cmd_query_overwrite(struct nfit_test *t, |
1106 | struct nd_intel_query_overwrite *nd_cmd, |
1107 | unsigned int buf_len, int dimm) |
1108 | { |
1109 | struct device *dev = &t->pdev.dev; |
1110 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
1111 | |
1112 | if (!(sec->state & ND_INTEL_SEC_STATE_OVERWRITE)) { |
1113 | nd_cmd->status = ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR; |
1114 | return 0; |
1115 | } |
1116 | |
1117 | if (time_is_before_jiffies64(sec->overwrite_end_time)) { |
1118 | sec->overwrite_end_time = 0; |
1119 | sec->state = sec->old_state; |
1120 | sec->old_state = 0; |
1121 | sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; |
1122 | dev_dbg(dev, "overwrite is complete\n" ); |
1123 | } else |
1124 | nd_cmd->status = ND_INTEL_STATUS_OQUERY_INPROGRESS; |
1125 | return 0; |
1126 | } |
1127 | |
1128 | static int nd_intel_test_cmd_master_set_pass(struct nfit_test *t, |
1129 | struct nd_intel_set_master_passphrase *nd_cmd, |
1130 | unsigned int buf_len, int dimm) |
1131 | { |
1132 | struct device *dev = &t->pdev.dev; |
1133 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
1134 | |
1135 | if (!(sec->ext_state & ND_INTEL_SEC_ESTATE_ENABLED)) { |
1136 | nd_cmd->status = ND_INTEL_STATUS_NOT_SUPPORTED; |
1137 | dev_dbg(dev, "master set passphrase: in wrong state\n" ); |
1138 | } else if (sec->ext_state & ND_INTEL_SEC_ESTATE_PLIMIT) { |
1139 | nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; |
1140 | dev_dbg(dev, "master set passphrase: in wrong security state\n" ); |
1141 | } else if (memcmp(nd_cmd->old_pass, sec->master_passphrase, |
1142 | ND_INTEL_PASSPHRASE_SIZE) != 0) { |
1143 | nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; |
1144 | dev_dbg(dev, "master set passphrase: wrong passphrase\n" ); |
1145 | } else { |
1146 | memcpy(sec->master_passphrase, nd_cmd->new_pass, |
1147 | ND_INTEL_PASSPHRASE_SIZE); |
1148 | sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; |
1149 | dev_dbg(dev, "master passphrase: updated\n" ); |
1150 | } |
1151 | |
1152 | return 0; |
1153 | } |
1154 | |
1155 | static int nd_intel_test_cmd_master_secure_erase(struct nfit_test *t, |
1156 | struct nd_intel_master_secure_erase *nd_cmd, |
1157 | unsigned int buf_len, int dimm) |
1158 | { |
1159 | struct device *dev = &t->pdev.dev; |
1160 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
1161 | |
1162 | if (!(sec->ext_state & ND_INTEL_SEC_ESTATE_ENABLED)) { |
1163 | nd_cmd->status = ND_INTEL_STATUS_NOT_SUPPORTED; |
1164 | dev_dbg(dev, "master secure erase: in wrong state\n" ); |
1165 | } else if (sec->ext_state & ND_INTEL_SEC_ESTATE_PLIMIT) { |
1166 | nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; |
1167 | dev_dbg(dev, "master secure erase: in wrong security state\n" ); |
1168 | } else if (memcmp(nd_cmd->passphrase, sec->master_passphrase, |
1169 | ND_INTEL_PASSPHRASE_SIZE) != 0) { |
1170 | nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; |
1171 | dev_dbg(dev, "master secure erase: wrong passphrase\n" ); |
1172 | } else { |
1173 | /* we do not erase master state passphrase ever */ |
1174 | sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; |
1175 | memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); |
1176 | sec->state = 0; |
1177 | dev_dbg(dev, "master secure erase: done\n" ); |
1178 | } |
1179 | |
1180 | return 0; |
1181 | } |
1182 | |
1183 | static unsigned long last_activate; |
1184 | |
1185 | static int nvdimm_bus_intel_fw_activate_businfo(struct nfit_test *t, |
1186 | struct nd_intel_bus_fw_activate_businfo *nd_cmd, |
1187 | unsigned int buf_len) |
1188 | { |
1189 | int i, armed = 0; |
1190 | int state; |
1191 | u64 tmo; |
1192 | |
1193 | for (i = 0; i < NUM_DCR; i++) { |
1194 | struct nfit_test_fw *fw = &t->fw[i]; |
1195 | |
1196 | if (fw->armed) |
1197 | armed++; |
1198 | } |
1199 | |
1200 | /* |
1201 | * Emulate 3 second activation max, and 1 second incremental |
1202 | * quiesce time per dimm requiring multiple activates to get all |
1203 | * DIMMs updated. |
1204 | */ |
1205 | if (armed) |
1206 | state = ND_INTEL_FWA_ARMED; |
1207 | else if (!last_activate || time_after(jiffies, last_activate + 3 * HZ)) |
1208 | state = ND_INTEL_FWA_IDLE; |
1209 | else |
1210 | state = ND_INTEL_FWA_BUSY; |
1211 | |
1212 | tmo = armed * USEC_PER_SEC; |
1213 | *nd_cmd = (struct nd_intel_bus_fw_activate_businfo) { |
1214 | .capability = ND_INTEL_BUS_FWA_CAP_FWQUIESCE |
1215 | | ND_INTEL_BUS_FWA_CAP_OSQUIESCE |
1216 | | ND_INTEL_BUS_FWA_CAP_RESET, |
1217 | .state = state, |
1218 | .activate_tmo = tmo, |
1219 | .cpu_quiesce_tmo = tmo, |
1220 | .io_quiesce_tmo = tmo, |
1221 | .max_quiesce_tmo = 3 * USEC_PER_SEC, |
1222 | }; |
1223 | |
1224 | return 0; |
1225 | } |
1226 | |
1227 | static int nvdimm_bus_intel_fw_activate(struct nfit_test *t, |
1228 | struct nd_intel_bus_fw_activate *nd_cmd, |
1229 | unsigned int buf_len) |
1230 | { |
1231 | struct nd_intel_bus_fw_activate_businfo info; |
1232 | u32 status = 0; |
1233 | int i; |
1234 | |
1235 | nvdimm_bus_intel_fw_activate_businfo(t, nd_cmd: &info, buf_len: sizeof(info)); |
1236 | if (info.state == ND_INTEL_FWA_BUSY) |
1237 | status = ND_INTEL_BUS_FWA_STATUS_BUSY; |
1238 | else if (info.activate_tmo > info.max_quiesce_tmo) |
1239 | status = ND_INTEL_BUS_FWA_STATUS_TMO; |
1240 | else if (info.state == ND_INTEL_FWA_IDLE) |
1241 | status = ND_INTEL_BUS_FWA_STATUS_NOARM; |
1242 | |
1243 | dev_dbg(&t->pdev.dev, "status: %d\n" , status); |
1244 | nd_cmd->status = status; |
1245 | if (status && status != ND_INTEL_BUS_FWA_STATUS_TMO) |
1246 | return 0; |
1247 | |
1248 | last_activate = jiffies; |
1249 | for (i = 0; i < NUM_DCR; i++) { |
1250 | struct nfit_test_fw *fw = &t->fw[i]; |
1251 | |
1252 | if (!fw->armed) |
1253 | continue; |
1254 | if (fw->state != FW_STATE_UPDATED) |
1255 | fw->missed_activate = true; |
1256 | else |
1257 | fw->state = FW_STATE_NEW; |
1258 | fw->armed = false; |
1259 | fw->last_activate = last_activate; |
1260 | } |
1261 | |
1262 | return 0; |
1263 | } |
1264 | |
1265 | static int nd_intel_test_cmd_fw_activate_dimminfo(struct nfit_test *t, |
1266 | struct nd_intel_fw_activate_dimminfo *nd_cmd, |
1267 | unsigned int buf_len, int dimm) |
1268 | { |
1269 | struct nd_intel_bus_fw_activate_businfo info; |
1270 | struct nfit_test_fw *fw = &t->fw[dimm]; |
1271 | u32 result, state; |
1272 | |
1273 | nvdimm_bus_intel_fw_activate_businfo(t, nd_cmd: &info, buf_len: sizeof(info)); |
1274 | |
1275 | if (info.state == ND_INTEL_FWA_BUSY) |
1276 | state = ND_INTEL_FWA_BUSY; |
1277 | else if (info.state == ND_INTEL_FWA_IDLE) |
1278 | state = ND_INTEL_FWA_IDLE; |
1279 | else if (fw->armed) |
1280 | state = ND_INTEL_FWA_ARMED; |
1281 | else |
1282 | state = ND_INTEL_FWA_IDLE; |
1283 | |
1284 | result = ND_INTEL_DIMM_FWA_NONE; |
1285 | if (last_activate && fw->last_activate == last_activate && |
1286 | state == ND_INTEL_FWA_IDLE) { |
1287 | if (fw->missed_activate) |
1288 | result = ND_INTEL_DIMM_FWA_NOTSTAGED; |
1289 | else |
1290 | result = ND_INTEL_DIMM_FWA_SUCCESS; |
1291 | } |
1292 | |
1293 | *nd_cmd = (struct nd_intel_fw_activate_dimminfo) { |
1294 | .result = result, |
1295 | .state = state, |
1296 | }; |
1297 | |
1298 | return 0; |
1299 | } |
1300 | |
1301 | static int nd_intel_test_cmd_fw_activate_arm(struct nfit_test *t, |
1302 | struct nd_intel_fw_activate_arm *nd_cmd, |
1303 | unsigned int buf_len, int dimm) |
1304 | { |
1305 | struct nfit_test_fw *fw = &t->fw[dimm]; |
1306 | |
1307 | fw->armed = nd_cmd->activate_arm == ND_INTEL_DIMM_FWA_ARM; |
1308 | nd_cmd->status = 0; |
1309 | return 0; |
1310 | } |
1311 | |
1312 | static int get_dimm(struct nfit_mem *nfit_mem, unsigned int func) |
1313 | { |
1314 | int i; |
1315 | |
1316 | /* lookup per-dimm data */ |
1317 | for (i = 0; i < ARRAY_SIZE(handle); i++) |
1318 | if (__to_nfit_memdev(nfit_mem)->device_handle == handle[i]) |
1319 | break; |
1320 | if (i >= ARRAY_SIZE(handle)) |
1321 | return -ENXIO; |
1322 | return i; |
1323 | } |
1324 | |
1325 | static void nfit_ctl_dbg(struct acpi_nfit_desc *acpi_desc, |
1326 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
1327 | unsigned int len) |
1328 | { |
1329 | struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc); |
1330 | unsigned int func = cmd; |
1331 | unsigned int family = 0; |
1332 | |
1333 | if (cmd == ND_CMD_CALL) { |
1334 | struct nd_cmd_pkg *pkg = buf; |
1335 | |
1336 | len = pkg->nd_size_in; |
1337 | family = pkg->nd_family; |
1338 | buf = pkg->nd_payload; |
1339 | func = pkg->nd_command; |
1340 | } |
1341 | dev_dbg(&t->pdev.dev, "%s family: %d cmd: %d: func: %d input length: %d\n" , |
1342 | nvdimm ? nvdimm_name(nvdimm) : "bus" , family, cmd, func, |
1343 | len); |
1344 | print_hex_dump_debug("nvdimm in " , DUMP_PREFIX_OFFSET, 16, 4, |
1345 | buf, min(len, 256u), true); |
1346 | } |
1347 | |
1348 | static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, |
1349 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
1350 | unsigned int buf_len, int *cmd_rc) |
1351 | { |
1352 | struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); |
1353 | struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc); |
1354 | unsigned int func = cmd; |
1355 | int i, rc = 0, __cmd_rc; |
1356 | |
1357 | if (!cmd_rc) |
1358 | cmd_rc = &__cmd_rc; |
1359 | *cmd_rc = 0; |
1360 | |
1361 | nfit_ctl_dbg(acpi_desc, nvdimm, cmd, buf, len: buf_len); |
1362 | |
1363 | if (nvdimm) { |
1364 | struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); |
1365 | unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm); |
1366 | |
1367 | if (!nfit_mem) |
1368 | return -ENOTTY; |
1369 | |
1370 | if (cmd == ND_CMD_CALL) { |
1371 | struct nd_cmd_pkg *call_pkg = buf; |
1372 | |
1373 | buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out; |
1374 | buf = (void *) call_pkg->nd_payload; |
1375 | func = call_pkg->nd_command; |
1376 | if (call_pkg->nd_family != nfit_mem->family) |
1377 | return -ENOTTY; |
1378 | |
1379 | i = get_dimm(nfit_mem, func); |
1380 | if (i < 0) |
1381 | return i; |
1382 | if (i >= NUM_DCR) { |
1383 | dev_WARN_ONCE(&t->pdev.dev, 1, |
1384 | "ND_CMD_CALL only valid for nfit_test0\n" ); |
1385 | return -EINVAL; |
1386 | } |
1387 | |
1388 | switch (func) { |
1389 | case NVDIMM_INTEL_GET_SECURITY_STATE: |
1390 | rc = nd_intel_test_cmd_security_status(t, |
1391 | nd_cmd: buf, buf_len, dimm: i); |
1392 | break; |
1393 | case NVDIMM_INTEL_UNLOCK_UNIT: |
1394 | rc = nd_intel_test_cmd_unlock_unit(t, |
1395 | nd_cmd: buf, buf_len, dimm: i); |
1396 | break; |
1397 | case NVDIMM_INTEL_SET_PASSPHRASE: |
1398 | rc = nd_intel_test_cmd_set_pass(t, |
1399 | nd_cmd: buf, buf_len, dimm: i); |
1400 | break; |
1401 | case NVDIMM_INTEL_DISABLE_PASSPHRASE: |
1402 | rc = nd_intel_test_cmd_disable_pass(t, |
1403 | nd_cmd: buf, buf_len, dimm: i); |
1404 | break; |
1405 | case NVDIMM_INTEL_FREEZE_LOCK: |
1406 | rc = nd_intel_test_cmd_freeze_lock(t, |
1407 | nd_cmd: buf, buf_len, dimm: i); |
1408 | break; |
1409 | case NVDIMM_INTEL_SECURE_ERASE: |
1410 | rc = nd_intel_test_cmd_secure_erase(t, |
1411 | nd_cmd: buf, buf_len, dimm: i); |
1412 | break; |
1413 | case NVDIMM_INTEL_OVERWRITE: |
1414 | rc = nd_intel_test_cmd_overwrite(t, |
1415 | nd_cmd: buf, buf_len, dimm: i); |
1416 | break; |
1417 | case NVDIMM_INTEL_QUERY_OVERWRITE: |
1418 | rc = nd_intel_test_cmd_query_overwrite(t, |
1419 | nd_cmd: buf, buf_len, dimm: i); |
1420 | break; |
1421 | case NVDIMM_INTEL_SET_MASTER_PASSPHRASE: |
1422 | rc = nd_intel_test_cmd_master_set_pass(t, |
1423 | nd_cmd: buf, buf_len, dimm: i); |
1424 | break; |
1425 | case NVDIMM_INTEL_MASTER_SECURE_ERASE: |
1426 | rc = nd_intel_test_cmd_master_secure_erase(t, |
1427 | nd_cmd: buf, buf_len, dimm: i); |
1428 | break; |
1429 | case NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO: |
1430 | rc = nd_intel_test_cmd_fw_activate_dimminfo( |
1431 | t, nd_cmd: buf, buf_len, dimm: i); |
1432 | break; |
1433 | case NVDIMM_INTEL_FW_ACTIVATE_ARM: |
1434 | rc = nd_intel_test_cmd_fw_activate_arm( |
1435 | t, nd_cmd: buf, buf_len, dimm: i); |
1436 | break; |
1437 | case ND_INTEL_ENABLE_LSS_STATUS: |
1438 | rc = nd_intel_test_cmd_set_lss_status(t, |
1439 | nd_cmd: buf, buf_len); |
1440 | break; |
1441 | case ND_INTEL_FW_GET_INFO: |
1442 | rc = nd_intel_test_get_fw_info(t, nd_cmd: buf, |
1443 | buf_len, idx: i); |
1444 | break; |
1445 | case ND_INTEL_FW_START_UPDATE: |
1446 | rc = nd_intel_test_start_update(t, nd_cmd: buf, |
1447 | buf_len, idx: i); |
1448 | break; |
1449 | case ND_INTEL_FW_SEND_DATA: |
1450 | rc = nd_intel_test_send_data(t, nd_cmd: buf, |
1451 | buf_len, idx: i); |
1452 | break; |
1453 | case ND_INTEL_FW_FINISH_UPDATE: |
1454 | rc = nd_intel_test_finish_fw(t, nd_cmd: buf, |
1455 | buf_len, idx: i); |
1456 | break; |
1457 | case ND_INTEL_FW_FINISH_QUERY: |
1458 | rc = nd_intel_test_finish_query(t, nd_cmd: buf, |
1459 | buf_len, idx: i); |
1460 | break; |
1461 | case ND_INTEL_SMART: |
1462 | rc = nfit_test_cmd_smart(buf, buf_len, |
1463 | &t->smart[i]); |
1464 | break; |
1465 | case ND_INTEL_SMART_THRESHOLD: |
1466 | rc = nfit_test_cmd_smart_threshold(out: buf, |
1467 | buf_len, |
1468 | smart_t: &t->smart_threshold[i]); |
1469 | break; |
1470 | case ND_INTEL_SMART_SET_THRESHOLD: |
1471 | rc = nfit_test_cmd_smart_set_threshold(buf, |
1472 | buf_len, |
1473 | &t->smart_threshold[i], |
1474 | &t->smart[i], |
1475 | &t->pdev.dev, t->dimm_dev[i]); |
1476 | break; |
1477 | case ND_INTEL_SMART_INJECT: |
1478 | rc = nfit_test_cmd_smart_inject(buf, |
1479 | buf_len, |
1480 | &t->smart_threshold[i], |
1481 | &t->smart[i], |
1482 | &t->pdev.dev, t->dimm_dev[i]); |
1483 | break; |
1484 | default: |
1485 | return -ENOTTY; |
1486 | } |
1487 | return override_return_code(dimm: i, func, rc); |
1488 | } |
1489 | |
1490 | if (!test_bit(cmd, &cmd_mask) |
1491 | || !test_bit(func, &nfit_mem->dsm_mask)) |
1492 | return -ENOTTY; |
1493 | |
1494 | i = get_dimm(nfit_mem, func); |
1495 | if (i < 0) |
1496 | return i; |
1497 | |
1498 | switch (func) { |
1499 | case ND_CMD_GET_CONFIG_SIZE: |
1500 | rc = nfit_test_cmd_get_config_size(nd_cmd: buf, buf_len); |
1501 | break; |
1502 | case ND_CMD_GET_CONFIG_DATA: |
1503 | rc = nfit_test_cmd_get_config_data(nd_cmd: buf, buf_len, |
1504 | label: t->label[i - t->dcr_idx]); |
1505 | break; |
1506 | case ND_CMD_SET_CONFIG_DATA: |
1507 | rc = nfit_test_cmd_set_config_data(nd_cmd: buf, buf_len, |
1508 | label: t->label[i - t->dcr_idx]); |
1509 | break; |
1510 | default: |
1511 | return -ENOTTY; |
1512 | } |
1513 | return override_return_code(dimm: i, func, rc); |
1514 | } else { |
1515 | struct ars_state *ars_state = &t->ars_state; |
1516 | struct nd_cmd_pkg *call_pkg = buf; |
1517 | |
1518 | if (!nd_desc) |
1519 | return -ENOTTY; |
1520 | |
1521 | if (cmd == ND_CMD_CALL && call_pkg->nd_family |
1522 | == NVDIMM_BUS_FAMILY_NFIT) { |
1523 | func = call_pkg->nd_command; |
1524 | buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out; |
1525 | buf = (void *) call_pkg->nd_payload; |
1526 | |
1527 | switch (func) { |
1528 | case NFIT_CMD_TRANSLATE_SPA: |
1529 | rc = nfit_test_cmd_translate_spa( |
1530 | bus: acpi_desc->nvdimm_bus, spa: buf, buf_len); |
1531 | return rc; |
1532 | case NFIT_CMD_ARS_INJECT_SET: |
1533 | rc = nfit_test_cmd_ars_error_inject(t, err_inj: buf, |
1534 | buf_len); |
1535 | return rc; |
1536 | case NFIT_CMD_ARS_INJECT_CLEAR: |
1537 | rc = nfit_test_cmd_ars_inject_clear(t, err_clr: buf, |
1538 | buf_len); |
1539 | return rc; |
1540 | case NFIT_CMD_ARS_INJECT_GET: |
1541 | rc = nfit_test_cmd_ars_inject_status(t, err_stat: buf, |
1542 | buf_len); |
1543 | return rc; |
1544 | default: |
1545 | return -ENOTTY; |
1546 | } |
1547 | } else if (cmd == ND_CMD_CALL && call_pkg->nd_family |
1548 | == NVDIMM_BUS_FAMILY_INTEL) { |
1549 | func = call_pkg->nd_command; |
1550 | buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out; |
1551 | buf = (void *) call_pkg->nd_payload; |
1552 | |
1553 | switch (func) { |
1554 | case NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO: |
1555 | rc = nvdimm_bus_intel_fw_activate_businfo(t, |
1556 | nd_cmd: buf, buf_len); |
1557 | return rc; |
1558 | case NVDIMM_BUS_INTEL_FW_ACTIVATE: |
1559 | rc = nvdimm_bus_intel_fw_activate(t, nd_cmd: buf, |
1560 | buf_len); |
1561 | return rc; |
1562 | default: |
1563 | return -ENOTTY; |
1564 | } |
1565 | } else if (cmd == ND_CMD_CALL) |
1566 | return -ENOTTY; |
1567 | |
1568 | if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask)) |
1569 | return -ENOTTY; |
1570 | |
1571 | switch (func) { |
1572 | case ND_CMD_ARS_CAP: |
1573 | rc = nfit_test_cmd_ars_cap(nd_cmd: buf, buf_len); |
1574 | break; |
1575 | case ND_CMD_ARS_START: |
1576 | rc = nfit_test_cmd_ars_start(t, ars_state, ars_start: buf, |
1577 | buf_len, cmd_rc); |
1578 | break; |
1579 | case ND_CMD_ARS_STATUS: |
1580 | rc = nfit_test_cmd_ars_status(ars_state, ars_status: buf, buf_len, |
1581 | cmd_rc); |
1582 | break; |
1583 | case ND_CMD_CLEAR_ERROR: |
1584 | rc = nfit_test_cmd_clear_error(t, clear_err: buf, buf_len, cmd_rc); |
1585 | break; |
1586 | default: |
1587 | return -ENOTTY; |
1588 | } |
1589 | } |
1590 | |
1591 | return rc; |
1592 | } |
1593 | |
1594 | static DEFINE_SPINLOCK(nfit_test_lock); |
1595 | static struct nfit_test *instances[NUM_NFITS]; |
1596 | |
1597 | static void release_nfit_res(void *data) |
1598 | { |
1599 | struct nfit_test_resource *nfit_res = data; |
1600 | |
1601 | spin_lock(lock: &nfit_test_lock); |
1602 | list_del(entry: &nfit_res->list); |
1603 | spin_unlock(lock: &nfit_test_lock); |
1604 | |
1605 | if (resource_size(res: &nfit_res->res) >= DIMM_SIZE) |
1606 | gen_pool_free(pool: nfit_pool, addr: nfit_res->res.start, |
1607 | size: resource_size(res: &nfit_res->res)); |
1608 | vfree(addr: nfit_res->buf); |
1609 | kfree(objp: nfit_res); |
1610 | } |
1611 | |
1612 | static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, |
1613 | void *buf) |
1614 | { |
1615 | struct device *dev = &t->pdev.dev; |
1616 | struct nfit_test_resource *nfit_res = kzalloc(size: sizeof(*nfit_res), |
1617 | GFP_KERNEL); |
1618 | int rc; |
1619 | |
1620 | if (!buf || !nfit_res || !*dma) |
1621 | goto err; |
1622 | rc = devm_add_action(dev, release_nfit_res, nfit_res); |
1623 | if (rc) |
1624 | goto err; |
1625 | INIT_LIST_HEAD(list: &nfit_res->list); |
1626 | memset(buf, 0, size); |
1627 | nfit_res->dev = dev; |
1628 | nfit_res->buf = buf; |
1629 | nfit_res->res.start = *dma; |
1630 | nfit_res->res.end = *dma + size - 1; |
1631 | nfit_res->res.name = "NFIT" ; |
1632 | spin_lock_init(&nfit_res->lock); |
1633 | INIT_LIST_HEAD(list: &nfit_res->requests); |
1634 | spin_lock(lock: &nfit_test_lock); |
1635 | list_add(new: &nfit_res->list, head: &t->resources); |
1636 | spin_unlock(lock: &nfit_test_lock); |
1637 | |
1638 | return nfit_res->buf; |
1639 | err: |
1640 | if (*dma && size >= DIMM_SIZE) |
1641 | gen_pool_free(pool: nfit_pool, addr: *dma, size); |
1642 | if (buf) |
1643 | vfree(addr: buf); |
1644 | kfree(objp: nfit_res); |
1645 | return NULL; |
1646 | } |
1647 | |
1648 | static void *test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma) |
1649 | { |
1650 | struct genpool_data_align data = { |
1651 | .align = SZ_128M, |
1652 | }; |
1653 | void *buf = vmalloc(size); |
1654 | |
1655 | if (size >= DIMM_SIZE) |
1656 | *dma = gen_pool_alloc_algo(pool: nfit_pool, size, |
1657 | algo: gen_pool_first_fit_align, data: &data); |
1658 | else |
1659 | *dma = (unsigned long) buf; |
1660 | return __test_alloc(t, size, dma, buf); |
1661 | } |
1662 | |
1663 | static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr) |
1664 | { |
1665 | int i; |
1666 | |
1667 | for (i = 0; i < ARRAY_SIZE(instances); i++) { |
1668 | struct nfit_test_resource *n, *nfit_res = NULL; |
1669 | struct nfit_test *t = instances[i]; |
1670 | |
1671 | if (!t) |
1672 | continue; |
1673 | spin_lock(lock: &nfit_test_lock); |
1674 | list_for_each_entry(n, &t->resources, list) { |
1675 | if (addr >= n->res.start && (addr < n->res.start |
1676 | + resource_size(res: &n->res))) { |
1677 | nfit_res = n; |
1678 | break; |
1679 | } else if (addr >= (unsigned long) n->buf |
1680 | && (addr < (unsigned long) n->buf |
1681 | + resource_size(res: &n->res))) { |
1682 | nfit_res = n; |
1683 | break; |
1684 | } |
1685 | } |
1686 | spin_unlock(lock: &nfit_test_lock); |
1687 | if (nfit_res) |
1688 | return nfit_res; |
1689 | } |
1690 | |
1691 | return NULL; |
1692 | } |
1693 | |
1694 | static int ars_state_init(struct device *dev, struct ars_state *ars_state) |
1695 | { |
1696 | /* for testing, only store up to n records that fit within 4k */ |
1697 | ars_state->ars_status = devm_kzalloc(dev, |
1698 | size: sizeof(struct nd_cmd_ars_status) + SZ_4K, GFP_KERNEL); |
1699 | if (!ars_state->ars_status) |
1700 | return -ENOMEM; |
1701 | spin_lock_init(&ars_state->lock); |
1702 | return 0; |
1703 | } |
1704 | |
1705 | static void put_dimms(void *data) |
1706 | { |
1707 | struct nfit_test *t = data; |
1708 | int i; |
1709 | |
1710 | for (i = 0; i < t->num_dcr; i++) |
1711 | if (t->dimm_dev[i]) |
1712 | device_unregister(dev: t->dimm_dev[i]); |
1713 | } |
1714 | |
1715 | static const struct class nfit_test_dimm = { |
1716 | .name = "nfit_test_dimm" , |
1717 | }; |
1718 | |
1719 | static int dimm_name_to_id(struct device *dev) |
1720 | { |
1721 | int dimm; |
1722 | |
1723 | if (sscanf(dev_name(dev), "test_dimm%d" , &dimm) != 1) |
1724 | return -ENXIO; |
1725 | return dimm; |
1726 | } |
1727 | |
1728 | static ssize_t handle_show(struct device *dev, struct device_attribute *attr, |
1729 | char *buf) |
1730 | { |
1731 | int dimm = dimm_name_to_id(dev); |
1732 | |
1733 | if (dimm < 0) |
1734 | return dimm; |
1735 | |
1736 | return sprintf(buf, fmt: "%#x\n" , handle[dimm]); |
1737 | } |
1738 | DEVICE_ATTR_RO(handle); |
1739 | |
1740 | static ssize_t fail_cmd_show(struct device *dev, struct device_attribute *attr, |
1741 | char *buf) |
1742 | { |
1743 | int dimm = dimm_name_to_id(dev); |
1744 | |
1745 | if (dimm < 0) |
1746 | return dimm; |
1747 | |
1748 | return sprintf(buf, fmt: "%#lx\n" , dimm_fail_cmd_flags[dimm]); |
1749 | } |
1750 | |
1751 | static ssize_t fail_cmd_store(struct device *dev, struct device_attribute *attr, |
1752 | const char *buf, size_t size) |
1753 | { |
1754 | int dimm = dimm_name_to_id(dev); |
1755 | unsigned long val; |
1756 | ssize_t rc; |
1757 | |
1758 | if (dimm < 0) |
1759 | return dimm; |
1760 | |
1761 | rc = kstrtol(s: buf, base: 0, res: &val); |
1762 | if (rc) |
1763 | return rc; |
1764 | |
1765 | dimm_fail_cmd_flags[dimm] = val; |
1766 | return size; |
1767 | } |
1768 | static DEVICE_ATTR_RW(fail_cmd); |
1769 | |
1770 | static ssize_t fail_cmd_code_show(struct device *dev, struct device_attribute *attr, |
1771 | char *buf) |
1772 | { |
1773 | int dimm = dimm_name_to_id(dev); |
1774 | |
1775 | if (dimm < 0) |
1776 | return dimm; |
1777 | |
1778 | return sprintf(buf, fmt: "%d\n" , dimm_fail_cmd_code[dimm]); |
1779 | } |
1780 | |
1781 | static ssize_t fail_cmd_code_store(struct device *dev, struct device_attribute *attr, |
1782 | const char *buf, size_t size) |
1783 | { |
1784 | int dimm = dimm_name_to_id(dev); |
1785 | unsigned long val; |
1786 | ssize_t rc; |
1787 | |
1788 | if (dimm < 0) |
1789 | return dimm; |
1790 | |
1791 | rc = kstrtol(s: buf, base: 0, res: &val); |
1792 | if (rc) |
1793 | return rc; |
1794 | |
1795 | dimm_fail_cmd_code[dimm] = val; |
1796 | return size; |
1797 | } |
1798 | static DEVICE_ATTR_RW(fail_cmd_code); |
1799 | |
1800 | static ssize_t lock_dimm_store(struct device *dev, |
1801 | struct device_attribute *attr, const char *buf, size_t size) |
1802 | { |
1803 | int dimm = dimm_name_to_id(dev); |
1804 | struct nfit_test_sec *sec = &dimm_sec_info[dimm]; |
1805 | |
1806 | sec->state = ND_INTEL_SEC_STATE_ENABLED | ND_INTEL_SEC_STATE_LOCKED; |
1807 | return size; |
1808 | } |
1809 | static DEVICE_ATTR_WO(lock_dimm); |
1810 | |
1811 | static struct attribute *nfit_test_dimm_attributes[] = { |
1812 | &dev_attr_fail_cmd.attr, |
1813 | &dev_attr_fail_cmd_code.attr, |
1814 | &dev_attr_handle.attr, |
1815 | &dev_attr_lock_dimm.attr, |
1816 | NULL, |
1817 | }; |
1818 | |
1819 | static struct attribute_group nfit_test_dimm_attribute_group = { |
1820 | .attrs = nfit_test_dimm_attributes, |
1821 | }; |
1822 | |
1823 | static const struct attribute_group *nfit_test_dimm_attribute_groups[] = { |
1824 | &nfit_test_dimm_attribute_group, |
1825 | NULL, |
1826 | }; |
1827 | |
1828 | static int nfit_test_dimm_init(struct nfit_test *t) |
1829 | { |
1830 | int i; |
1831 | |
1832 | if (devm_add_action_or_reset(&t->pdev.dev, put_dimms, t)) |
1833 | return -ENOMEM; |
1834 | for (i = 0; i < t->num_dcr; i++) { |
1835 | t->dimm_dev[i] = device_create_with_groups(cls: &nfit_test_dimm, |
1836 | parent: &t->pdev.dev, devt: 0, NULL, |
1837 | groups: nfit_test_dimm_attribute_groups, |
1838 | fmt: "test_dimm%d" , i + t->dcr_idx); |
1839 | if (!t->dimm_dev[i]) |
1840 | return -ENOMEM; |
1841 | } |
1842 | return 0; |
1843 | } |
1844 | |
1845 | static void nfit_security_init(struct nfit_test *t) |
1846 | { |
1847 | int i; |
1848 | |
1849 | for (i = 0; i < t->num_dcr; i++) { |
1850 | struct nfit_test_sec *sec = &dimm_sec_info[i]; |
1851 | |
1852 | sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; |
1853 | } |
1854 | } |
1855 | |
1856 | static void smart_init(struct nfit_test *t) |
1857 | { |
1858 | int i; |
1859 | const struct nd_intel_smart_threshold smart_t_data = { |
1860 | .alarm_control = ND_INTEL_SMART_SPARE_TRIP |
1861 | | ND_INTEL_SMART_TEMP_TRIP, |
1862 | .media_temperature = 40 * 16, |
1863 | .ctrl_temperature = 30 * 16, |
1864 | .spares = 5, |
1865 | }; |
1866 | |
1867 | for (i = 0; i < t->num_dcr; i++) { |
1868 | memcpy(&t->smart[i], &smart_def, sizeof(smart_def)); |
1869 | memcpy(&t->smart_threshold[i], &smart_t_data, |
1870 | sizeof(smart_t_data)); |
1871 | } |
1872 | } |
1873 | |
1874 | static size_t sizeof_spa(struct acpi_nfit_system_address *spa) |
1875 | { |
1876 | /* until spa location cookie support is added... */ |
1877 | return sizeof(*spa) - 8; |
1878 | } |
1879 | |
1880 | static int nfit_test0_alloc(struct nfit_test *t) |
1881 | { |
1882 | struct acpi_nfit_system_address *spa = NULL; |
1883 | struct acpi_nfit_flush_address *flush; |
1884 | size_t nfit_size = sizeof_spa(spa) * NUM_SPA |
1885 | + sizeof(struct acpi_nfit_memory_map) * NUM_MEM |
1886 | + sizeof(struct acpi_nfit_control_region) * NUM_DCR |
1887 | + offsetof(struct acpi_nfit_control_region, |
1888 | window_size) * NUM_DCR |
1889 | + sizeof(struct acpi_nfit_data_region) * NUM_BDW |
1890 | + struct_size(flush, hint_address, NUM_HINTS) * NUM_DCR |
1891 | + sizeof(struct acpi_nfit_capabilities); |
1892 | int i; |
1893 | |
1894 | t->nfit_buf = test_alloc(t, size: nfit_size, dma: &t->nfit_dma); |
1895 | if (!t->nfit_buf) |
1896 | return -ENOMEM; |
1897 | t->nfit_size = nfit_size; |
1898 | |
1899 | t->spa_set[0] = test_alloc(t, size: SPA0_SIZE, dma: &t->spa_set_dma[0]); |
1900 | if (!t->spa_set[0]) |
1901 | return -ENOMEM; |
1902 | |
1903 | t->spa_set[1] = test_alloc(t, size: SPA1_SIZE, dma: &t->spa_set_dma[1]); |
1904 | if (!t->spa_set[1]) |
1905 | return -ENOMEM; |
1906 | |
1907 | t->spa_set[2] = test_alloc(t, size: SPA0_SIZE, dma: &t->spa_set_dma[2]); |
1908 | if (!t->spa_set[2]) |
1909 | return -ENOMEM; |
1910 | |
1911 | for (i = 0; i < t->num_dcr; i++) { |
1912 | t->dimm[i] = test_alloc(t, size: DIMM_SIZE, dma: &t->dimm_dma[i]); |
1913 | if (!t->dimm[i]) |
1914 | return -ENOMEM; |
1915 | |
1916 | t->label[i] = test_alloc(t, size: LABEL_SIZE, dma: &t->label_dma[i]); |
1917 | if (!t->label[i]) |
1918 | return -ENOMEM; |
1919 | sprintf(buf: t->label[i], fmt: "label%d" , i); |
1920 | |
1921 | t->flush[i] = test_alloc(t, max(PAGE_SIZE, |
1922 | sizeof(u64) * NUM_HINTS), |
1923 | dma: &t->flush_dma[i]); |
1924 | if (!t->flush[i]) |
1925 | return -ENOMEM; |
1926 | } |
1927 | |
1928 | for (i = 0; i < t->num_dcr; i++) { |
1929 | t->dcr[i] = test_alloc(t, size: LABEL_SIZE, dma: &t->dcr_dma[i]); |
1930 | if (!t->dcr[i]) |
1931 | return -ENOMEM; |
1932 | } |
1933 | |
1934 | t->_fit = test_alloc(t, size: sizeof(union acpi_object **), dma: &t->_fit_dma); |
1935 | if (!t->_fit) |
1936 | return -ENOMEM; |
1937 | |
1938 | if (nfit_test_dimm_init(t)) |
1939 | return -ENOMEM; |
1940 | smart_init(t); |
1941 | nfit_security_init(t); |
1942 | return ars_state_init(dev: &t->pdev.dev, ars_state: &t->ars_state); |
1943 | } |
1944 | |
1945 | static int nfit_test1_alloc(struct nfit_test *t) |
1946 | { |
1947 | struct acpi_nfit_system_address *spa = NULL; |
1948 | size_t nfit_size = sizeof_spa(spa) * 2 |
1949 | + sizeof(struct acpi_nfit_memory_map) * 2 |
1950 | + offsetof(struct acpi_nfit_control_region, window_size) * 2; |
1951 | int i; |
1952 | |
1953 | t->nfit_buf = test_alloc(t, size: nfit_size, dma: &t->nfit_dma); |
1954 | if (!t->nfit_buf) |
1955 | return -ENOMEM; |
1956 | t->nfit_size = nfit_size; |
1957 | |
1958 | t->spa_set[0] = test_alloc(t, size: SPA2_SIZE, dma: &t->spa_set_dma[0]); |
1959 | if (!t->spa_set[0]) |
1960 | return -ENOMEM; |
1961 | |
1962 | for (i = 0; i < t->num_dcr; i++) { |
1963 | t->label[i] = test_alloc(t, size: LABEL_SIZE, dma: &t->label_dma[i]); |
1964 | if (!t->label[i]) |
1965 | return -ENOMEM; |
1966 | sprintf(buf: t->label[i], fmt: "label%d" , i); |
1967 | } |
1968 | |
1969 | t->spa_set[1] = test_alloc(t, size: SPA_VCD_SIZE, dma: &t->spa_set_dma[1]); |
1970 | if (!t->spa_set[1]) |
1971 | return -ENOMEM; |
1972 | |
1973 | if (nfit_test_dimm_init(t)) |
1974 | return -ENOMEM; |
1975 | smart_init(t); |
1976 | return ars_state_init(dev: &t->pdev.dev, ars_state: &t->ars_state); |
1977 | } |
1978 | |
1979 | static void dcr_common_init(struct acpi_nfit_control_region *dcr) |
1980 | { |
1981 | dcr->vendor_id = 0xabcd; |
1982 | dcr->device_id = 0; |
1983 | dcr->revision_id = 1; |
1984 | dcr->valid_fields = 1; |
1985 | dcr->manufacturing_location = 0xa; |
1986 | dcr->manufacturing_date = cpu_to_be16(2016); |
1987 | } |
1988 | |
1989 | static void nfit_test0_setup(struct nfit_test *t) |
1990 | { |
1991 | const int flush_hint_size = sizeof(struct acpi_nfit_flush_address) |
1992 | + (sizeof(u64) * NUM_HINTS); |
1993 | struct acpi_nfit_desc *acpi_desc; |
1994 | struct acpi_nfit_memory_map *memdev; |
1995 | void *nfit_buf = t->nfit_buf; |
1996 | struct acpi_nfit_system_address *spa; |
1997 | struct acpi_nfit_control_region *dcr; |
1998 | struct acpi_nfit_data_region *bdw; |
1999 | struct acpi_nfit_flush_address *flush; |
2000 | struct acpi_nfit_capabilities *pcap; |
2001 | unsigned int offset = 0, i; |
2002 | unsigned long *acpi_mask; |
2003 | |
2004 | /* |
2005 | * spa0 (interleave first half of dimm0 and dimm1, note storage |
2006 | * does not actually alias the related block-data-window |
2007 | * regions) |
2008 | */ |
2009 | spa = nfit_buf; |
2010 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2011 | spa->header.length = sizeof_spa(spa); |
2012 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16); |
2013 | spa->range_index = 0+1; |
2014 | spa->address = t->spa_set_dma[0]; |
2015 | spa->length = SPA0_SIZE; |
2016 | offset += spa->header.length; |
2017 | |
2018 | /* |
2019 | * spa1 (interleave last half of the 4 DIMMS, note storage |
2020 | * does not actually alias the related block-data-window |
2021 | * regions) |
2022 | */ |
2023 | spa = nfit_buf + offset; |
2024 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2025 | spa->header.length = sizeof_spa(spa); |
2026 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16); |
2027 | spa->range_index = 1+1; |
2028 | spa->address = t->spa_set_dma[1]; |
2029 | spa->length = SPA1_SIZE; |
2030 | offset += spa->header.length; |
2031 | |
2032 | /* spa2 (dcr0) dimm0 */ |
2033 | spa = nfit_buf + offset; |
2034 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2035 | spa->header.length = sizeof_spa(spa); |
2036 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); |
2037 | spa->range_index = 2+1; |
2038 | spa->address = t->dcr_dma[0]; |
2039 | spa->length = DCR_SIZE; |
2040 | offset += spa->header.length; |
2041 | |
2042 | /* spa3 (dcr1) dimm1 */ |
2043 | spa = nfit_buf + offset; |
2044 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2045 | spa->header.length = sizeof_spa(spa); |
2046 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); |
2047 | spa->range_index = 3+1; |
2048 | spa->address = t->dcr_dma[1]; |
2049 | spa->length = DCR_SIZE; |
2050 | offset += spa->header.length; |
2051 | |
2052 | /* spa4 (dcr2) dimm2 */ |
2053 | spa = nfit_buf + offset; |
2054 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2055 | spa->header.length = sizeof_spa(spa); |
2056 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); |
2057 | spa->range_index = 4+1; |
2058 | spa->address = t->dcr_dma[2]; |
2059 | spa->length = DCR_SIZE; |
2060 | offset += spa->header.length; |
2061 | |
2062 | /* spa5 (dcr3) dimm3 */ |
2063 | spa = nfit_buf + offset; |
2064 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2065 | spa->header.length = sizeof_spa(spa); |
2066 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); |
2067 | spa->range_index = 5+1; |
2068 | spa->address = t->dcr_dma[3]; |
2069 | spa->length = DCR_SIZE; |
2070 | offset += spa->header.length; |
2071 | |
2072 | /* spa6 (bdw for dcr0) dimm0 */ |
2073 | spa = nfit_buf + offset; |
2074 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2075 | spa->header.length = sizeof_spa(spa); |
2076 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); |
2077 | spa->range_index = 6+1; |
2078 | spa->address = t->dimm_dma[0]; |
2079 | spa->length = DIMM_SIZE; |
2080 | offset += spa->header.length; |
2081 | |
2082 | /* spa7 (bdw for dcr1) dimm1 */ |
2083 | spa = nfit_buf + offset; |
2084 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2085 | spa->header.length = sizeof_spa(spa); |
2086 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); |
2087 | spa->range_index = 7+1; |
2088 | spa->address = t->dimm_dma[1]; |
2089 | spa->length = DIMM_SIZE; |
2090 | offset += spa->header.length; |
2091 | |
2092 | /* spa8 (bdw for dcr2) dimm2 */ |
2093 | spa = nfit_buf + offset; |
2094 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2095 | spa->header.length = sizeof_spa(spa); |
2096 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); |
2097 | spa->range_index = 8+1; |
2098 | spa->address = t->dimm_dma[2]; |
2099 | spa->length = DIMM_SIZE; |
2100 | offset += spa->header.length; |
2101 | |
2102 | /* spa9 (bdw for dcr3) dimm3 */ |
2103 | spa = nfit_buf + offset; |
2104 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2105 | spa->header.length = sizeof_spa(spa); |
2106 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); |
2107 | spa->range_index = 9+1; |
2108 | spa->address = t->dimm_dma[3]; |
2109 | spa->length = DIMM_SIZE; |
2110 | offset += spa->header.length; |
2111 | |
2112 | /* mem-region0 (spa0, dimm0) */ |
2113 | memdev = nfit_buf + offset; |
2114 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2115 | memdev->header.length = sizeof(*memdev); |
2116 | memdev->device_handle = handle[0]; |
2117 | memdev->physical_id = 0; |
2118 | memdev->region_id = 0; |
2119 | memdev->range_index = 0+1; |
2120 | memdev->region_index = 4+1; |
2121 | memdev->region_size = SPA0_SIZE/2; |
2122 | memdev->region_offset = 1; |
2123 | memdev->address = 0; |
2124 | memdev->interleave_index = 0; |
2125 | memdev->interleave_ways = 2; |
2126 | offset += memdev->header.length; |
2127 | |
2128 | /* mem-region1 (spa0, dimm1) */ |
2129 | memdev = nfit_buf + offset; |
2130 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2131 | memdev->header.length = sizeof(*memdev); |
2132 | memdev->device_handle = handle[1]; |
2133 | memdev->physical_id = 1; |
2134 | memdev->region_id = 0; |
2135 | memdev->range_index = 0+1; |
2136 | memdev->region_index = 5+1; |
2137 | memdev->region_size = SPA0_SIZE/2; |
2138 | memdev->region_offset = (1 << 8); |
2139 | memdev->address = 0; |
2140 | memdev->interleave_index = 0; |
2141 | memdev->interleave_ways = 2; |
2142 | memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; |
2143 | offset += memdev->header.length; |
2144 | |
2145 | /* mem-region2 (spa1, dimm0) */ |
2146 | memdev = nfit_buf + offset; |
2147 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2148 | memdev->header.length = sizeof(*memdev); |
2149 | memdev->device_handle = handle[0]; |
2150 | memdev->physical_id = 0; |
2151 | memdev->region_id = 1; |
2152 | memdev->range_index = 1+1; |
2153 | memdev->region_index = 4+1; |
2154 | memdev->region_size = SPA1_SIZE/4; |
2155 | memdev->region_offset = (1 << 16); |
2156 | memdev->address = SPA0_SIZE/2; |
2157 | memdev->interleave_index = 0; |
2158 | memdev->interleave_ways = 4; |
2159 | memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; |
2160 | offset += memdev->header.length; |
2161 | |
2162 | /* mem-region3 (spa1, dimm1) */ |
2163 | memdev = nfit_buf + offset; |
2164 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2165 | memdev->header.length = sizeof(*memdev); |
2166 | memdev->device_handle = handle[1]; |
2167 | memdev->physical_id = 1; |
2168 | memdev->region_id = 1; |
2169 | memdev->range_index = 1+1; |
2170 | memdev->region_index = 5+1; |
2171 | memdev->region_size = SPA1_SIZE/4; |
2172 | memdev->region_offset = (1 << 24); |
2173 | memdev->address = SPA0_SIZE/2; |
2174 | memdev->interleave_index = 0; |
2175 | memdev->interleave_ways = 4; |
2176 | offset += memdev->header.length; |
2177 | |
2178 | /* mem-region4 (spa1, dimm2) */ |
2179 | memdev = nfit_buf + offset; |
2180 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2181 | memdev->header.length = sizeof(*memdev); |
2182 | memdev->device_handle = handle[2]; |
2183 | memdev->physical_id = 2; |
2184 | memdev->region_id = 0; |
2185 | memdev->range_index = 1+1; |
2186 | memdev->region_index = 6+1; |
2187 | memdev->region_size = SPA1_SIZE/4; |
2188 | memdev->region_offset = (1ULL << 32); |
2189 | memdev->address = SPA0_SIZE/2; |
2190 | memdev->interleave_index = 0; |
2191 | memdev->interleave_ways = 4; |
2192 | memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; |
2193 | offset += memdev->header.length; |
2194 | |
2195 | /* mem-region5 (spa1, dimm3) */ |
2196 | memdev = nfit_buf + offset; |
2197 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2198 | memdev->header.length = sizeof(*memdev); |
2199 | memdev->device_handle = handle[3]; |
2200 | memdev->physical_id = 3; |
2201 | memdev->region_id = 0; |
2202 | memdev->range_index = 1+1; |
2203 | memdev->region_index = 7+1; |
2204 | memdev->region_size = SPA1_SIZE/4; |
2205 | memdev->region_offset = (1ULL << 40); |
2206 | memdev->address = SPA0_SIZE/2; |
2207 | memdev->interleave_index = 0; |
2208 | memdev->interleave_ways = 4; |
2209 | offset += memdev->header.length; |
2210 | |
2211 | /* mem-region6 (spa/dcr0, dimm0) */ |
2212 | memdev = nfit_buf + offset; |
2213 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2214 | memdev->header.length = sizeof(*memdev); |
2215 | memdev->device_handle = handle[0]; |
2216 | memdev->physical_id = 0; |
2217 | memdev->region_id = 0; |
2218 | memdev->range_index = 2+1; |
2219 | memdev->region_index = 0+1; |
2220 | memdev->region_size = 0; |
2221 | memdev->region_offset = 0; |
2222 | memdev->address = 0; |
2223 | memdev->interleave_index = 0; |
2224 | memdev->interleave_ways = 1; |
2225 | offset += memdev->header.length; |
2226 | |
2227 | /* mem-region7 (spa/dcr1, dimm1) */ |
2228 | memdev = nfit_buf + offset; |
2229 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2230 | memdev->header.length = sizeof(*memdev); |
2231 | memdev->device_handle = handle[1]; |
2232 | memdev->physical_id = 1; |
2233 | memdev->region_id = 0; |
2234 | memdev->range_index = 3+1; |
2235 | memdev->region_index = 1+1; |
2236 | memdev->region_size = 0; |
2237 | memdev->region_offset = 0; |
2238 | memdev->address = 0; |
2239 | memdev->interleave_index = 0; |
2240 | memdev->interleave_ways = 1; |
2241 | offset += memdev->header.length; |
2242 | |
2243 | /* mem-region8 (spa/dcr2, dimm2) */ |
2244 | memdev = nfit_buf + offset; |
2245 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2246 | memdev->header.length = sizeof(*memdev); |
2247 | memdev->device_handle = handle[2]; |
2248 | memdev->physical_id = 2; |
2249 | memdev->region_id = 0; |
2250 | memdev->range_index = 4+1; |
2251 | memdev->region_index = 2+1; |
2252 | memdev->region_size = 0; |
2253 | memdev->region_offset = 0; |
2254 | memdev->address = 0; |
2255 | memdev->interleave_index = 0; |
2256 | memdev->interleave_ways = 1; |
2257 | offset += memdev->header.length; |
2258 | |
2259 | /* mem-region9 (spa/dcr3, dimm3) */ |
2260 | memdev = nfit_buf + offset; |
2261 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2262 | memdev->header.length = sizeof(*memdev); |
2263 | memdev->device_handle = handle[3]; |
2264 | memdev->physical_id = 3; |
2265 | memdev->region_id = 0; |
2266 | memdev->range_index = 5+1; |
2267 | memdev->region_index = 3+1; |
2268 | memdev->region_size = 0; |
2269 | memdev->region_offset = 0; |
2270 | memdev->address = 0; |
2271 | memdev->interleave_index = 0; |
2272 | memdev->interleave_ways = 1; |
2273 | offset += memdev->header.length; |
2274 | |
2275 | /* mem-region10 (spa/bdw0, dimm0) */ |
2276 | memdev = nfit_buf + offset; |
2277 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2278 | memdev->header.length = sizeof(*memdev); |
2279 | memdev->device_handle = handle[0]; |
2280 | memdev->physical_id = 0; |
2281 | memdev->region_id = 0; |
2282 | memdev->range_index = 6+1; |
2283 | memdev->region_index = 0+1; |
2284 | memdev->region_size = 0; |
2285 | memdev->region_offset = 0; |
2286 | memdev->address = 0; |
2287 | memdev->interleave_index = 0; |
2288 | memdev->interleave_ways = 1; |
2289 | offset += memdev->header.length; |
2290 | |
2291 | /* mem-region11 (spa/bdw1, dimm1) */ |
2292 | memdev = nfit_buf + offset; |
2293 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2294 | memdev->header.length = sizeof(*memdev); |
2295 | memdev->device_handle = handle[1]; |
2296 | memdev->physical_id = 1; |
2297 | memdev->region_id = 0; |
2298 | memdev->range_index = 7+1; |
2299 | memdev->region_index = 1+1; |
2300 | memdev->region_size = 0; |
2301 | memdev->region_offset = 0; |
2302 | memdev->address = 0; |
2303 | memdev->interleave_index = 0; |
2304 | memdev->interleave_ways = 1; |
2305 | offset += memdev->header.length; |
2306 | |
2307 | /* mem-region12 (spa/bdw2, dimm2) */ |
2308 | memdev = nfit_buf + offset; |
2309 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2310 | memdev->header.length = sizeof(*memdev); |
2311 | memdev->device_handle = handle[2]; |
2312 | memdev->physical_id = 2; |
2313 | memdev->region_id = 0; |
2314 | memdev->range_index = 8+1; |
2315 | memdev->region_index = 2+1; |
2316 | memdev->region_size = 0; |
2317 | memdev->region_offset = 0; |
2318 | memdev->address = 0; |
2319 | memdev->interleave_index = 0; |
2320 | memdev->interleave_ways = 1; |
2321 | offset += memdev->header.length; |
2322 | |
2323 | /* mem-region13 (spa/dcr3, dimm3) */ |
2324 | memdev = nfit_buf + offset; |
2325 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2326 | memdev->header.length = sizeof(*memdev); |
2327 | memdev->device_handle = handle[3]; |
2328 | memdev->physical_id = 3; |
2329 | memdev->region_id = 0; |
2330 | memdev->range_index = 9+1; |
2331 | memdev->region_index = 3+1; |
2332 | memdev->region_size = 0; |
2333 | memdev->region_offset = 0; |
2334 | memdev->address = 0; |
2335 | memdev->interleave_index = 0; |
2336 | memdev->interleave_ways = 1; |
2337 | memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; |
2338 | offset += memdev->header.length; |
2339 | |
2340 | /* dcr-descriptor0: blk */ |
2341 | dcr = nfit_buf + offset; |
2342 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2343 | dcr->header.length = sizeof(*dcr); |
2344 | dcr->region_index = 0+1; |
2345 | dcr_common_init(dcr); |
2346 | dcr->serial_number = ~handle[0]; |
2347 | dcr->code = NFIT_FIC_BLK; |
2348 | dcr->windows = 1; |
2349 | dcr->window_size = DCR_SIZE; |
2350 | dcr->command_offset = 0; |
2351 | dcr->command_size = 8; |
2352 | dcr->status_offset = 8; |
2353 | dcr->status_size = 4; |
2354 | offset += dcr->header.length; |
2355 | |
2356 | /* dcr-descriptor1: blk */ |
2357 | dcr = nfit_buf + offset; |
2358 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2359 | dcr->header.length = sizeof(*dcr); |
2360 | dcr->region_index = 1+1; |
2361 | dcr_common_init(dcr); |
2362 | dcr->serial_number = ~handle[1]; |
2363 | dcr->code = NFIT_FIC_BLK; |
2364 | dcr->windows = 1; |
2365 | dcr->window_size = DCR_SIZE; |
2366 | dcr->command_offset = 0; |
2367 | dcr->command_size = 8; |
2368 | dcr->status_offset = 8; |
2369 | dcr->status_size = 4; |
2370 | offset += dcr->header.length; |
2371 | |
2372 | /* dcr-descriptor2: blk */ |
2373 | dcr = nfit_buf + offset; |
2374 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2375 | dcr->header.length = sizeof(*dcr); |
2376 | dcr->region_index = 2+1; |
2377 | dcr_common_init(dcr); |
2378 | dcr->serial_number = ~handle[2]; |
2379 | dcr->code = NFIT_FIC_BLK; |
2380 | dcr->windows = 1; |
2381 | dcr->window_size = DCR_SIZE; |
2382 | dcr->command_offset = 0; |
2383 | dcr->command_size = 8; |
2384 | dcr->status_offset = 8; |
2385 | dcr->status_size = 4; |
2386 | offset += dcr->header.length; |
2387 | |
2388 | /* dcr-descriptor3: blk */ |
2389 | dcr = nfit_buf + offset; |
2390 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2391 | dcr->header.length = sizeof(*dcr); |
2392 | dcr->region_index = 3+1; |
2393 | dcr_common_init(dcr); |
2394 | dcr->serial_number = ~handle[3]; |
2395 | dcr->code = NFIT_FIC_BLK; |
2396 | dcr->windows = 1; |
2397 | dcr->window_size = DCR_SIZE; |
2398 | dcr->command_offset = 0; |
2399 | dcr->command_size = 8; |
2400 | dcr->status_offset = 8; |
2401 | dcr->status_size = 4; |
2402 | offset += dcr->header.length; |
2403 | |
2404 | /* dcr-descriptor0: pmem */ |
2405 | dcr = nfit_buf + offset; |
2406 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2407 | dcr->header.length = offsetof(struct acpi_nfit_control_region, |
2408 | window_size); |
2409 | dcr->region_index = 4+1; |
2410 | dcr_common_init(dcr); |
2411 | dcr->serial_number = ~handle[0]; |
2412 | dcr->code = NFIT_FIC_BYTEN; |
2413 | dcr->windows = 0; |
2414 | offset += dcr->header.length; |
2415 | |
2416 | /* dcr-descriptor1: pmem */ |
2417 | dcr = nfit_buf + offset; |
2418 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2419 | dcr->header.length = offsetof(struct acpi_nfit_control_region, |
2420 | window_size); |
2421 | dcr->region_index = 5+1; |
2422 | dcr_common_init(dcr); |
2423 | dcr->serial_number = ~handle[1]; |
2424 | dcr->code = NFIT_FIC_BYTEN; |
2425 | dcr->windows = 0; |
2426 | offset += dcr->header.length; |
2427 | |
2428 | /* dcr-descriptor2: pmem */ |
2429 | dcr = nfit_buf + offset; |
2430 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2431 | dcr->header.length = offsetof(struct acpi_nfit_control_region, |
2432 | window_size); |
2433 | dcr->region_index = 6+1; |
2434 | dcr_common_init(dcr); |
2435 | dcr->serial_number = ~handle[2]; |
2436 | dcr->code = NFIT_FIC_BYTEN; |
2437 | dcr->windows = 0; |
2438 | offset += dcr->header.length; |
2439 | |
2440 | /* dcr-descriptor3: pmem */ |
2441 | dcr = nfit_buf + offset; |
2442 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2443 | dcr->header.length = offsetof(struct acpi_nfit_control_region, |
2444 | window_size); |
2445 | dcr->region_index = 7+1; |
2446 | dcr_common_init(dcr); |
2447 | dcr->serial_number = ~handle[3]; |
2448 | dcr->code = NFIT_FIC_BYTEN; |
2449 | dcr->windows = 0; |
2450 | offset += dcr->header.length; |
2451 | |
2452 | /* bdw0 (spa/dcr0, dimm0) */ |
2453 | bdw = nfit_buf + offset; |
2454 | bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; |
2455 | bdw->header.length = sizeof(*bdw); |
2456 | bdw->region_index = 0+1; |
2457 | bdw->windows = 1; |
2458 | bdw->offset = 0; |
2459 | bdw->size = BDW_SIZE; |
2460 | bdw->capacity = DIMM_SIZE; |
2461 | bdw->start_address = 0; |
2462 | offset += bdw->header.length; |
2463 | |
2464 | /* bdw1 (spa/dcr1, dimm1) */ |
2465 | bdw = nfit_buf + offset; |
2466 | bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; |
2467 | bdw->header.length = sizeof(*bdw); |
2468 | bdw->region_index = 1+1; |
2469 | bdw->windows = 1; |
2470 | bdw->offset = 0; |
2471 | bdw->size = BDW_SIZE; |
2472 | bdw->capacity = DIMM_SIZE; |
2473 | bdw->start_address = 0; |
2474 | offset += bdw->header.length; |
2475 | |
2476 | /* bdw2 (spa/dcr2, dimm2) */ |
2477 | bdw = nfit_buf + offset; |
2478 | bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; |
2479 | bdw->header.length = sizeof(*bdw); |
2480 | bdw->region_index = 2+1; |
2481 | bdw->windows = 1; |
2482 | bdw->offset = 0; |
2483 | bdw->size = BDW_SIZE; |
2484 | bdw->capacity = DIMM_SIZE; |
2485 | bdw->start_address = 0; |
2486 | offset += bdw->header.length; |
2487 | |
2488 | /* bdw3 (spa/dcr3, dimm3) */ |
2489 | bdw = nfit_buf + offset; |
2490 | bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; |
2491 | bdw->header.length = sizeof(*bdw); |
2492 | bdw->region_index = 3+1; |
2493 | bdw->windows = 1; |
2494 | bdw->offset = 0; |
2495 | bdw->size = BDW_SIZE; |
2496 | bdw->capacity = DIMM_SIZE; |
2497 | bdw->start_address = 0; |
2498 | offset += bdw->header.length; |
2499 | |
2500 | /* flush0 (dimm0) */ |
2501 | flush = nfit_buf + offset; |
2502 | flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; |
2503 | flush->header.length = flush_hint_size; |
2504 | flush->device_handle = handle[0]; |
2505 | flush->hint_count = NUM_HINTS; |
2506 | for (i = 0; i < NUM_HINTS; i++) |
2507 | flush->hint_address[i] = t->flush_dma[0] + i * sizeof(u64); |
2508 | offset += flush->header.length; |
2509 | |
2510 | /* flush1 (dimm1) */ |
2511 | flush = nfit_buf + offset; |
2512 | flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; |
2513 | flush->header.length = flush_hint_size; |
2514 | flush->device_handle = handle[1]; |
2515 | flush->hint_count = NUM_HINTS; |
2516 | for (i = 0; i < NUM_HINTS; i++) |
2517 | flush->hint_address[i] = t->flush_dma[1] + i * sizeof(u64); |
2518 | offset += flush->header.length; |
2519 | |
2520 | /* flush2 (dimm2) */ |
2521 | flush = nfit_buf + offset; |
2522 | flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; |
2523 | flush->header.length = flush_hint_size; |
2524 | flush->device_handle = handle[2]; |
2525 | flush->hint_count = NUM_HINTS; |
2526 | for (i = 0; i < NUM_HINTS; i++) |
2527 | flush->hint_address[i] = t->flush_dma[2] + i * sizeof(u64); |
2528 | offset += flush->header.length; |
2529 | |
2530 | /* flush3 (dimm3) */ |
2531 | flush = nfit_buf + offset; |
2532 | flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; |
2533 | flush->header.length = flush_hint_size; |
2534 | flush->device_handle = handle[3]; |
2535 | flush->hint_count = NUM_HINTS; |
2536 | for (i = 0; i < NUM_HINTS; i++) |
2537 | flush->hint_address[i] = t->flush_dma[3] + i * sizeof(u64); |
2538 | offset += flush->header.length; |
2539 | |
2540 | /* platform capabilities */ |
2541 | pcap = nfit_buf + offset; |
2542 | pcap->header.type = ACPI_NFIT_TYPE_CAPABILITIES; |
2543 | pcap->header.length = sizeof(*pcap); |
2544 | pcap->highest_capability = 1; |
2545 | pcap->capabilities = ACPI_NFIT_CAPABILITY_MEM_FLUSH; |
2546 | offset += pcap->header.length; |
2547 | |
2548 | if (t->setup_hotplug) { |
2549 | /* dcr-descriptor4: blk */ |
2550 | dcr = nfit_buf + offset; |
2551 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2552 | dcr->header.length = sizeof(*dcr); |
2553 | dcr->region_index = 8+1; |
2554 | dcr_common_init(dcr); |
2555 | dcr->serial_number = ~handle[4]; |
2556 | dcr->code = NFIT_FIC_BLK; |
2557 | dcr->windows = 1; |
2558 | dcr->window_size = DCR_SIZE; |
2559 | dcr->command_offset = 0; |
2560 | dcr->command_size = 8; |
2561 | dcr->status_offset = 8; |
2562 | dcr->status_size = 4; |
2563 | offset += dcr->header.length; |
2564 | |
2565 | /* dcr-descriptor4: pmem */ |
2566 | dcr = nfit_buf + offset; |
2567 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2568 | dcr->header.length = offsetof(struct acpi_nfit_control_region, |
2569 | window_size); |
2570 | dcr->region_index = 9+1; |
2571 | dcr_common_init(dcr); |
2572 | dcr->serial_number = ~handle[4]; |
2573 | dcr->code = NFIT_FIC_BYTEN; |
2574 | dcr->windows = 0; |
2575 | offset += dcr->header.length; |
2576 | |
2577 | /* bdw4 (spa/dcr4, dimm4) */ |
2578 | bdw = nfit_buf + offset; |
2579 | bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; |
2580 | bdw->header.length = sizeof(*bdw); |
2581 | bdw->region_index = 8+1; |
2582 | bdw->windows = 1; |
2583 | bdw->offset = 0; |
2584 | bdw->size = BDW_SIZE; |
2585 | bdw->capacity = DIMM_SIZE; |
2586 | bdw->start_address = 0; |
2587 | offset += bdw->header.length; |
2588 | |
2589 | /* spa10 (dcr4) dimm4 */ |
2590 | spa = nfit_buf + offset; |
2591 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2592 | spa->header.length = sizeof_spa(spa); |
2593 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16); |
2594 | spa->range_index = 10+1; |
2595 | spa->address = t->dcr_dma[4]; |
2596 | spa->length = DCR_SIZE; |
2597 | offset += spa->header.length; |
2598 | |
2599 | /* |
2600 | * spa11 (single-dimm interleave for hotplug, note storage |
2601 | * does not actually alias the related block-data-window |
2602 | * regions) |
2603 | */ |
2604 | spa = nfit_buf + offset; |
2605 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2606 | spa->header.length = sizeof_spa(spa); |
2607 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16); |
2608 | spa->range_index = 11+1; |
2609 | spa->address = t->spa_set_dma[2]; |
2610 | spa->length = SPA0_SIZE; |
2611 | offset += spa->header.length; |
2612 | |
2613 | /* spa12 (bdw for dcr4) dimm4 */ |
2614 | spa = nfit_buf + offset; |
2615 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2616 | spa->header.length = sizeof_spa(spa); |
2617 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16); |
2618 | spa->range_index = 12+1; |
2619 | spa->address = t->dimm_dma[4]; |
2620 | spa->length = DIMM_SIZE; |
2621 | offset += spa->header.length; |
2622 | |
2623 | /* mem-region14 (spa/dcr4, dimm4) */ |
2624 | memdev = nfit_buf + offset; |
2625 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2626 | memdev->header.length = sizeof(*memdev); |
2627 | memdev->device_handle = handle[4]; |
2628 | memdev->physical_id = 4; |
2629 | memdev->region_id = 0; |
2630 | memdev->range_index = 10+1; |
2631 | memdev->region_index = 8+1; |
2632 | memdev->region_size = 0; |
2633 | memdev->region_offset = 0; |
2634 | memdev->address = 0; |
2635 | memdev->interleave_index = 0; |
2636 | memdev->interleave_ways = 1; |
2637 | offset += memdev->header.length; |
2638 | |
2639 | /* mem-region15 (spa11, dimm4) */ |
2640 | memdev = nfit_buf + offset; |
2641 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2642 | memdev->header.length = sizeof(*memdev); |
2643 | memdev->device_handle = handle[4]; |
2644 | memdev->physical_id = 4; |
2645 | memdev->region_id = 0; |
2646 | memdev->range_index = 11+1; |
2647 | memdev->region_index = 9+1; |
2648 | memdev->region_size = SPA0_SIZE; |
2649 | memdev->region_offset = (1ULL << 48); |
2650 | memdev->address = 0; |
2651 | memdev->interleave_index = 0; |
2652 | memdev->interleave_ways = 1; |
2653 | memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED; |
2654 | offset += memdev->header.length; |
2655 | |
2656 | /* mem-region16 (spa/bdw4, dimm4) */ |
2657 | memdev = nfit_buf + offset; |
2658 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2659 | memdev->header.length = sizeof(*memdev); |
2660 | memdev->device_handle = handle[4]; |
2661 | memdev->physical_id = 4; |
2662 | memdev->region_id = 0; |
2663 | memdev->range_index = 12+1; |
2664 | memdev->region_index = 8+1; |
2665 | memdev->region_size = 0; |
2666 | memdev->region_offset = 0; |
2667 | memdev->address = 0; |
2668 | memdev->interleave_index = 0; |
2669 | memdev->interleave_ways = 1; |
2670 | offset += memdev->header.length; |
2671 | |
2672 | /* flush3 (dimm4) */ |
2673 | flush = nfit_buf + offset; |
2674 | flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS; |
2675 | flush->header.length = flush_hint_size; |
2676 | flush->device_handle = handle[4]; |
2677 | flush->hint_count = NUM_HINTS; |
2678 | for (i = 0; i < NUM_HINTS; i++) |
2679 | flush->hint_address[i] = t->flush_dma[4] |
2680 | + i * sizeof(u64); |
2681 | offset += flush->header.length; |
2682 | |
2683 | /* sanity check to make sure we've filled the buffer */ |
2684 | WARN_ON(offset != t->nfit_size); |
2685 | } |
2686 | |
2687 | t->nfit_filled = offset; |
2688 | |
2689 | post_ars_status(ars_state: &t->ars_state, badrange: &t->badrange, addr: t->spa_set_dma[0], |
2690 | len: SPA0_SIZE); |
2691 | |
2692 | acpi_desc = &t->acpi_desc; |
2693 | set_bit(nr: ND_CMD_GET_CONFIG_SIZE, addr: &acpi_desc->dimm_cmd_force_en); |
2694 | set_bit(nr: ND_CMD_GET_CONFIG_DATA, addr: &acpi_desc->dimm_cmd_force_en); |
2695 | set_bit(nr: ND_CMD_SET_CONFIG_DATA, addr: &acpi_desc->dimm_cmd_force_en); |
2696 | set_bit(ND_INTEL_SMART, addr: &acpi_desc->dimm_cmd_force_en); |
2697 | set_bit(ND_INTEL_SMART_THRESHOLD, addr: &acpi_desc->dimm_cmd_force_en); |
2698 | set_bit(ND_INTEL_SMART_SET_THRESHOLD, addr: &acpi_desc->dimm_cmd_force_en); |
2699 | set_bit(ND_INTEL_SMART_INJECT, addr: &acpi_desc->dimm_cmd_force_en); |
2700 | set_bit(nr: ND_CMD_ARS_CAP, addr: &acpi_desc->bus_cmd_force_en); |
2701 | set_bit(nr: ND_CMD_ARS_START, addr: &acpi_desc->bus_cmd_force_en); |
2702 | set_bit(nr: ND_CMD_ARS_STATUS, addr: &acpi_desc->bus_cmd_force_en); |
2703 | set_bit(nr: ND_CMD_CLEAR_ERROR, addr: &acpi_desc->bus_cmd_force_en); |
2704 | set_bit(nr: ND_CMD_CALL, addr: &acpi_desc->bus_cmd_force_en); |
2705 | set_bit(nr: NFIT_CMD_TRANSLATE_SPA, addr: &acpi_desc->bus_dsm_mask); |
2706 | set_bit(nr: NFIT_CMD_ARS_INJECT_SET, addr: &acpi_desc->bus_dsm_mask); |
2707 | set_bit(nr: NFIT_CMD_ARS_INJECT_CLEAR, addr: &acpi_desc->bus_dsm_mask); |
2708 | set_bit(nr: NFIT_CMD_ARS_INJECT_GET, addr: &acpi_desc->bus_dsm_mask); |
2709 | set_bit(ND_INTEL_FW_GET_INFO, addr: &acpi_desc->dimm_cmd_force_en); |
2710 | set_bit(ND_INTEL_FW_START_UPDATE, addr: &acpi_desc->dimm_cmd_force_en); |
2711 | set_bit(ND_INTEL_FW_SEND_DATA, addr: &acpi_desc->dimm_cmd_force_en); |
2712 | set_bit(ND_INTEL_FW_FINISH_UPDATE, addr: &acpi_desc->dimm_cmd_force_en); |
2713 | set_bit(ND_INTEL_FW_FINISH_QUERY, addr: &acpi_desc->dimm_cmd_force_en); |
2714 | set_bit(ND_INTEL_ENABLE_LSS_STATUS, addr: &acpi_desc->dimm_cmd_force_en); |
2715 | set_bit(NVDIMM_INTEL_GET_SECURITY_STATE, |
2716 | &acpi_desc->dimm_cmd_force_en); |
2717 | set_bit(NVDIMM_INTEL_SET_PASSPHRASE, &acpi_desc->dimm_cmd_force_en); |
2718 | set_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, |
2719 | &acpi_desc->dimm_cmd_force_en); |
2720 | set_bit(NVDIMM_INTEL_UNLOCK_UNIT, &acpi_desc->dimm_cmd_force_en); |
2721 | set_bit(NVDIMM_INTEL_FREEZE_LOCK, &acpi_desc->dimm_cmd_force_en); |
2722 | set_bit(NVDIMM_INTEL_SECURE_ERASE, &acpi_desc->dimm_cmd_force_en); |
2723 | set_bit(NVDIMM_INTEL_OVERWRITE, &acpi_desc->dimm_cmd_force_en); |
2724 | set_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &acpi_desc->dimm_cmd_force_en); |
2725 | set_bit(NVDIMM_INTEL_SET_MASTER_PASSPHRASE, |
2726 | &acpi_desc->dimm_cmd_force_en); |
2727 | set_bit(NVDIMM_INTEL_MASTER_SECURE_ERASE, |
2728 | &acpi_desc->dimm_cmd_force_en); |
2729 | set_bit(NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO, &acpi_desc->dimm_cmd_force_en); |
2730 | set_bit(NVDIMM_INTEL_FW_ACTIVATE_ARM, &acpi_desc->dimm_cmd_force_en); |
2731 | |
2732 | acpi_mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL]; |
2733 | set_bit(NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO, acpi_mask); |
2734 | set_bit(NVDIMM_BUS_INTEL_FW_ACTIVATE, acpi_mask); |
2735 | } |
2736 | |
2737 | static void nfit_test1_setup(struct nfit_test *t) |
2738 | { |
2739 | size_t offset; |
2740 | void *nfit_buf = t->nfit_buf; |
2741 | struct acpi_nfit_memory_map *memdev; |
2742 | struct acpi_nfit_control_region *dcr; |
2743 | struct acpi_nfit_system_address *spa; |
2744 | struct acpi_nfit_desc *acpi_desc; |
2745 | |
2746 | offset = 0; |
2747 | /* spa0 (flat range with no bdw aliasing) */ |
2748 | spa = nfit_buf + offset; |
2749 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2750 | spa->header.length = sizeof_spa(spa); |
2751 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16); |
2752 | spa->range_index = 0+1; |
2753 | spa->address = t->spa_set_dma[0]; |
2754 | spa->length = SPA2_SIZE; |
2755 | offset += spa->header.length; |
2756 | |
2757 | /* virtual cd region */ |
2758 | spa = nfit_buf + offset; |
2759 | spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS; |
2760 | spa->header.length = sizeof_spa(spa); |
2761 | memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_VCD), 16); |
2762 | spa->range_index = 0; |
2763 | spa->address = t->spa_set_dma[1]; |
2764 | spa->length = SPA_VCD_SIZE; |
2765 | offset += spa->header.length; |
2766 | |
2767 | /* mem-region0 (spa0, dimm0) */ |
2768 | memdev = nfit_buf + offset; |
2769 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2770 | memdev->header.length = sizeof(*memdev); |
2771 | memdev->device_handle = handle[5]; |
2772 | memdev->physical_id = 0; |
2773 | memdev->region_id = 0; |
2774 | memdev->range_index = 0+1; |
2775 | memdev->region_index = 0+1; |
2776 | memdev->region_size = SPA2_SIZE; |
2777 | memdev->region_offset = 0; |
2778 | memdev->address = 0; |
2779 | memdev->interleave_index = 0; |
2780 | memdev->interleave_ways = 1; |
2781 | memdev->flags = ACPI_NFIT_MEM_SAVE_FAILED | ACPI_NFIT_MEM_RESTORE_FAILED |
2782 | | ACPI_NFIT_MEM_FLUSH_FAILED | ACPI_NFIT_MEM_HEALTH_OBSERVED |
2783 | | ACPI_NFIT_MEM_NOT_ARMED; |
2784 | offset += memdev->header.length; |
2785 | |
2786 | /* dcr-descriptor0 */ |
2787 | dcr = nfit_buf + offset; |
2788 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2789 | dcr->header.length = offsetof(struct acpi_nfit_control_region, |
2790 | window_size); |
2791 | dcr->region_index = 0+1; |
2792 | dcr_common_init(dcr); |
2793 | dcr->serial_number = ~handle[5]; |
2794 | dcr->code = NFIT_FIC_BYTE; |
2795 | dcr->windows = 0; |
2796 | offset += dcr->header.length; |
2797 | |
2798 | memdev = nfit_buf + offset; |
2799 | memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; |
2800 | memdev->header.length = sizeof(*memdev); |
2801 | memdev->device_handle = handle[6]; |
2802 | memdev->physical_id = 0; |
2803 | memdev->region_id = 0; |
2804 | memdev->range_index = 0; |
2805 | memdev->region_index = 0+2; |
2806 | memdev->region_size = SPA2_SIZE; |
2807 | memdev->region_offset = 0; |
2808 | memdev->address = 0; |
2809 | memdev->interleave_index = 0; |
2810 | memdev->interleave_ways = 1; |
2811 | memdev->flags = ACPI_NFIT_MEM_MAP_FAILED; |
2812 | offset += memdev->header.length; |
2813 | |
2814 | /* dcr-descriptor1 */ |
2815 | dcr = nfit_buf + offset; |
2816 | dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; |
2817 | dcr->header.length = offsetof(struct acpi_nfit_control_region, |
2818 | window_size); |
2819 | dcr->region_index = 0+2; |
2820 | dcr_common_init(dcr); |
2821 | dcr->serial_number = ~handle[6]; |
2822 | dcr->code = NFIT_FIC_BYTE; |
2823 | dcr->windows = 0; |
2824 | offset += dcr->header.length; |
2825 | |
2826 | /* sanity check to make sure we've filled the buffer */ |
2827 | WARN_ON(offset != t->nfit_size); |
2828 | |
2829 | t->nfit_filled = offset; |
2830 | |
2831 | post_ars_status(ars_state: &t->ars_state, badrange: &t->badrange, addr: t->spa_set_dma[0], |
2832 | len: SPA2_SIZE); |
2833 | |
2834 | acpi_desc = &t->acpi_desc; |
2835 | set_bit(nr: ND_CMD_ARS_CAP, addr: &acpi_desc->bus_cmd_force_en); |
2836 | set_bit(nr: ND_CMD_ARS_START, addr: &acpi_desc->bus_cmd_force_en); |
2837 | set_bit(nr: ND_CMD_ARS_STATUS, addr: &acpi_desc->bus_cmd_force_en); |
2838 | set_bit(nr: ND_CMD_CLEAR_ERROR, addr: &acpi_desc->bus_cmd_force_en); |
2839 | set_bit(ND_INTEL_ENABLE_LSS_STATUS, addr: &acpi_desc->dimm_cmd_force_en); |
2840 | set_bit(nr: ND_CMD_GET_CONFIG_SIZE, addr: &acpi_desc->dimm_cmd_force_en); |
2841 | set_bit(nr: ND_CMD_GET_CONFIG_DATA, addr: &acpi_desc->dimm_cmd_force_en); |
2842 | set_bit(nr: ND_CMD_SET_CONFIG_DATA, addr: &acpi_desc->dimm_cmd_force_en); |
2843 | } |
2844 | |
2845 | static unsigned long nfit_ctl_handle; |
2846 | |
2847 | union acpi_object *result; |
2848 | |
2849 | static union acpi_object *nfit_test_evaluate_dsm(acpi_handle handle, |
2850 | const guid_t *guid, u64 rev, u64 func, union acpi_object *argv4) |
2851 | { |
2852 | if (handle != &nfit_ctl_handle) |
2853 | return ERR_PTR(error: -ENXIO); |
2854 | |
2855 | return result; |
2856 | } |
2857 | |
2858 | static int setup_result(void *buf, size_t size) |
2859 | { |
2860 | result = kmalloc(size: sizeof(union acpi_object) + size, GFP_KERNEL); |
2861 | if (!result) |
2862 | return -ENOMEM; |
2863 | result->package.type = ACPI_TYPE_BUFFER, |
2864 | result->buffer.pointer = (void *) (result + 1); |
2865 | result->buffer.length = size; |
2866 | memcpy(result->buffer.pointer, buf, size); |
2867 | memset(buf, 0, size); |
2868 | return 0; |
2869 | } |
2870 | |
2871 | static int nfit_ctl_test(struct device *dev) |
2872 | { |
2873 | int rc, cmd_rc; |
2874 | struct nvdimm *nvdimm; |
2875 | struct acpi_device *adev; |
2876 | struct nfit_mem *nfit_mem; |
2877 | struct nd_ars_record *record; |
2878 | struct acpi_nfit_desc *acpi_desc; |
2879 | const u64 test_val = 0x0123456789abcdefULL; |
2880 | unsigned long mask, cmd_size, offset; |
2881 | struct nfit_ctl_test_cmd { |
2882 | struct nd_cmd_pkg pkg; |
2883 | union { |
2884 | struct nd_cmd_get_config_size cfg_size; |
2885 | struct nd_cmd_clear_error clear_err; |
2886 | struct nd_cmd_ars_status ars_stat; |
2887 | struct nd_cmd_ars_cap ars_cap; |
2888 | struct nd_intel_bus_fw_activate_businfo fwa_info; |
2889 | char buf[sizeof(struct nd_cmd_ars_status) |
2890 | + sizeof(struct nd_ars_record)]; |
2891 | }; |
2892 | } cmd; |
2893 | |
2894 | adev = devm_kzalloc(dev, size: sizeof(*adev), GFP_KERNEL); |
2895 | if (!adev) |
2896 | return -ENOMEM; |
2897 | *adev = (struct acpi_device) { |
2898 | .handle = &nfit_ctl_handle, |
2899 | .dev = { |
2900 | .init_name = "test-adev" , |
2901 | }, |
2902 | }; |
2903 | |
2904 | acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); |
2905 | if (!acpi_desc) |
2906 | return -ENOMEM; |
2907 | *acpi_desc = (struct acpi_nfit_desc) { |
2908 | .nd_desc = { |
2909 | .cmd_mask = 1UL << ND_CMD_ARS_CAP |
2910 | | 1UL << ND_CMD_ARS_START |
2911 | | 1UL << ND_CMD_ARS_STATUS |
2912 | | 1UL << ND_CMD_CLEAR_ERROR |
2913 | | 1UL << ND_CMD_CALL, |
2914 | .module = THIS_MODULE, |
2915 | .provider_name = "ACPI.NFIT" , |
2916 | .ndctl = acpi_nfit_ctl, |
2917 | .bus_family_mask = 1UL << NVDIMM_BUS_FAMILY_NFIT |
2918 | | 1UL << NVDIMM_BUS_FAMILY_INTEL, |
2919 | }, |
2920 | .bus_dsm_mask = 1UL << NFIT_CMD_TRANSLATE_SPA |
2921 | | 1UL << NFIT_CMD_ARS_INJECT_SET |
2922 | | 1UL << NFIT_CMD_ARS_INJECT_CLEAR |
2923 | | 1UL << NFIT_CMD_ARS_INJECT_GET, |
2924 | .family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL] = |
2925 | NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK, |
2926 | .dev = &adev->dev, |
2927 | }; |
2928 | |
2929 | nfit_mem = devm_kzalloc(dev, sizeof(*nfit_mem), GFP_KERNEL); |
2930 | if (!nfit_mem) |
2931 | return -ENOMEM; |
2932 | |
2933 | mask = 1UL << ND_CMD_SMART | 1UL << ND_CMD_SMART_THRESHOLD |
2934 | | 1UL << ND_CMD_DIMM_FLAGS | 1UL << ND_CMD_GET_CONFIG_SIZE |
2935 | | 1UL << ND_CMD_GET_CONFIG_DATA | 1UL << ND_CMD_SET_CONFIG_DATA |
2936 | | 1UL << ND_CMD_VENDOR; |
2937 | *nfit_mem = (struct nfit_mem) { |
2938 | .adev = adev, |
2939 | .family = NVDIMM_FAMILY_INTEL, |
2940 | .dsm_mask = mask, |
2941 | }; |
2942 | |
2943 | nvdimm = devm_kzalloc(dev, sizeof(*nvdimm), GFP_KERNEL); |
2944 | if (!nvdimm) |
2945 | return -ENOMEM; |
2946 | *nvdimm = (struct nvdimm) { |
2947 | .provider_data = nfit_mem, |
2948 | .cmd_mask = mask, |
2949 | .dev = { |
2950 | .init_name = "test-dimm" , |
2951 | }, |
2952 | }; |
2953 | |
2954 | |
2955 | /* basic checkout of a typical 'get config size' command */ |
2956 | cmd_size = sizeof(cmd.cfg_size); |
2957 | cmd.cfg_size = (struct nd_cmd_get_config_size) { |
2958 | .status = 0, |
2959 | .config_size = SZ_128K, |
2960 | .max_xfer = SZ_4K, |
2961 | }; |
2962 | rc = setup_result(buf: cmd.buf, size: cmd_size); |
2963 | if (rc) |
2964 | return rc; |
2965 | rc = acpi_nfit_ctl(&acpi_desc->nd_desc, nvdimm, ND_CMD_GET_CONFIG_SIZE, |
2966 | cmd.buf, cmd_size, &cmd_rc); |
2967 | |
2968 | if (rc < 0 || cmd_rc || cmd.cfg_size.status != 0 |
2969 | || cmd.cfg_size.config_size != SZ_128K |
2970 | || cmd.cfg_size.max_xfer != SZ_4K) { |
2971 | dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n" , |
2972 | __func__, __LINE__, rc, cmd_rc); |
2973 | return -EIO; |
2974 | } |
2975 | |
2976 | |
2977 | /* test ars_status with zero output */ |
2978 | cmd_size = offsetof(struct nd_cmd_ars_status, address); |
2979 | cmd.ars_stat = (struct nd_cmd_ars_status) { |
2980 | .out_length = 0, |
2981 | }; |
2982 | rc = setup_result(buf: cmd.buf, size: cmd_size); |
2983 | if (rc) |
2984 | return rc; |
2985 | rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS, |
2986 | cmd.buf, cmd_size, &cmd_rc); |
2987 | |
2988 | if (rc < 0 || cmd_rc) { |
2989 | dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n" , |
2990 | __func__, __LINE__, rc, cmd_rc); |
2991 | return -EIO; |
2992 | } |
2993 | |
2994 | |
2995 | /* test ars_cap with benign extended status */ |
2996 | cmd_size = sizeof(cmd.ars_cap); |
2997 | cmd.ars_cap = (struct nd_cmd_ars_cap) { |
2998 | .status = ND_ARS_PERSISTENT << 16, |
2999 | }; |
3000 | offset = offsetof(struct nd_cmd_ars_cap, status); |
3001 | rc = setup_result(buf: cmd.buf + offset, size: cmd_size - offset); |
3002 | if (rc) |
3003 | return rc; |
3004 | rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_CAP, |
3005 | cmd.buf, cmd_size, &cmd_rc); |
3006 | |
3007 | if (rc < 0 || cmd_rc) { |
3008 | dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n" , |
3009 | __func__, __LINE__, rc, cmd_rc); |
3010 | return -EIO; |
3011 | } |
3012 | |
3013 | |
3014 | /* test ars_status with 'status' trimmed from 'out_length' */ |
3015 | cmd_size = sizeof(cmd.ars_stat) + sizeof(struct nd_ars_record); |
3016 | cmd.ars_stat = (struct nd_cmd_ars_status) { |
3017 | .out_length = cmd_size - 4, |
3018 | }; |
3019 | record = &cmd.ars_stat.records[0]; |
3020 | *record = (struct nd_ars_record) { |
3021 | .length = test_val, |
3022 | }; |
3023 | rc = setup_result(buf: cmd.buf, size: cmd_size); |
3024 | if (rc) |
3025 | return rc; |
3026 | rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS, |
3027 | cmd.buf, cmd_size, &cmd_rc); |
3028 | |
3029 | if (rc < 0 || cmd_rc || record->length != test_val) { |
3030 | dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n" , |
3031 | __func__, __LINE__, rc, cmd_rc); |
3032 | return -EIO; |
3033 | } |
3034 | |
3035 | |
3036 | /* test ars_status with 'Output (Size)' including 'status' */ |
3037 | cmd_size = sizeof(cmd.ars_stat) + sizeof(struct nd_ars_record); |
3038 | cmd.ars_stat = (struct nd_cmd_ars_status) { |
3039 | .out_length = cmd_size, |
3040 | }; |
3041 | record = &cmd.ars_stat.records[0]; |
3042 | *record = (struct nd_ars_record) { |
3043 | .length = test_val, |
3044 | }; |
3045 | rc = setup_result(buf: cmd.buf, size: cmd_size); |
3046 | if (rc) |
3047 | return rc; |
3048 | rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS, |
3049 | cmd.buf, cmd_size, &cmd_rc); |
3050 | |
3051 | if (rc < 0 || cmd_rc || record->length != test_val) { |
3052 | dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n" , |
3053 | __func__, __LINE__, rc, cmd_rc); |
3054 | return -EIO; |
3055 | } |
3056 | |
3057 | |
3058 | /* test extended status for get_config_size results in failure */ |
3059 | cmd_size = sizeof(cmd.cfg_size); |
3060 | cmd.cfg_size = (struct nd_cmd_get_config_size) { |
3061 | .status = 1 << 16, |
3062 | }; |
3063 | rc = setup_result(buf: cmd.buf, size: cmd_size); |
3064 | if (rc) |
3065 | return rc; |
3066 | rc = acpi_nfit_ctl(&acpi_desc->nd_desc, nvdimm, ND_CMD_GET_CONFIG_SIZE, |
3067 | cmd.buf, cmd_size, &cmd_rc); |
3068 | |
3069 | if (rc < 0 || cmd_rc >= 0) { |
3070 | dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n" , |
3071 | __func__, __LINE__, rc, cmd_rc); |
3072 | return -EIO; |
3073 | } |
3074 | |
3075 | /* test clear error */ |
3076 | cmd_size = sizeof(cmd.clear_err); |
3077 | cmd.clear_err = (struct nd_cmd_clear_error) { |
3078 | .length = 512, |
3079 | .cleared = 512, |
3080 | }; |
3081 | rc = setup_result(buf: cmd.buf, size: cmd_size); |
3082 | if (rc) |
3083 | return rc; |
3084 | rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CLEAR_ERROR, |
3085 | cmd.buf, cmd_size, &cmd_rc); |
3086 | if (rc < 0 || cmd_rc) { |
3087 | dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n" , |
3088 | __func__, __LINE__, rc, cmd_rc); |
3089 | return -EIO; |
3090 | } |
3091 | |
3092 | /* test firmware activate bus info */ |
3093 | cmd_size = sizeof(cmd.fwa_info); |
3094 | cmd = (struct nfit_ctl_test_cmd) { |
3095 | .pkg = { |
3096 | .nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO, |
3097 | .nd_family = NVDIMM_BUS_FAMILY_INTEL, |
3098 | .nd_size_out = cmd_size, |
3099 | .nd_fw_size = cmd_size, |
3100 | }, |
3101 | .fwa_info = { |
3102 | .state = ND_INTEL_FWA_IDLE, |
3103 | .capability = ND_INTEL_BUS_FWA_CAP_FWQUIESCE |
3104 | | ND_INTEL_BUS_FWA_CAP_OSQUIESCE, |
3105 | .activate_tmo = 1, |
3106 | .cpu_quiesce_tmo = 1, |
3107 | .io_quiesce_tmo = 1, |
3108 | .max_quiesce_tmo = 1, |
3109 | }, |
3110 | }; |
3111 | rc = setup_result(buf: cmd.buf, size: cmd_size); |
3112 | if (rc) |
3113 | return rc; |
3114 | rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CALL, |
3115 | &cmd, sizeof(cmd.pkg) + cmd_size, &cmd_rc); |
3116 | if (rc < 0 || cmd_rc) { |
3117 | dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n" , |
3118 | __func__, __LINE__, rc, cmd_rc); |
3119 | return -EIO; |
3120 | } |
3121 | |
3122 | return 0; |
3123 | } |
3124 | |
3125 | static int nfit_test_probe(struct platform_device *pdev) |
3126 | { |
3127 | struct nvdimm_bus_descriptor *nd_desc; |
3128 | struct acpi_nfit_desc *acpi_desc; |
3129 | struct device *dev = &pdev->dev; |
3130 | struct nfit_test *nfit_test; |
3131 | struct nfit_mem *nfit_mem; |
3132 | union acpi_object *obj; |
3133 | int rc; |
3134 | |
3135 | if (strcmp(dev_name(dev: &pdev->dev), "nfit_test.0" ) == 0) { |
3136 | rc = nfit_ctl_test(dev: &pdev->dev); |
3137 | if (rc) |
3138 | return rc; |
3139 | } |
3140 | |
3141 | nfit_test = to_nfit_test(dev: &pdev->dev); |
3142 | |
3143 | /* common alloc */ |
3144 | if (nfit_test->num_dcr) { |
3145 | int num = nfit_test->num_dcr; |
3146 | |
3147 | nfit_test->dimm = devm_kcalloc(dev, n: num, size: sizeof(void *), |
3148 | GFP_KERNEL); |
3149 | nfit_test->dimm_dma = devm_kcalloc(dev, n: num, size: sizeof(dma_addr_t), |
3150 | GFP_KERNEL); |
3151 | nfit_test->flush = devm_kcalloc(dev, n: num, size: sizeof(void *), |
3152 | GFP_KERNEL); |
3153 | nfit_test->flush_dma = devm_kcalloc(dev, n: num, size: sizeof(dma_addr_t), |
3154 | GFP_KERNEL); |
3155 | nfit_test->label = devm_kcalloc(dev, n: num, size: sizeof(void *), |
3156 | GFP_KERNEL); |
3157 | nfit_test->label_dma = devm_kcalloc(dev, n: num, |
3158 | size: sizeof(dma_addr_t), GFP_KERNEL); |
3159 | nfit_test->dcr = devm_kcalloc(dev, n: num, |
3160 | size: sizeof(struct nfit_test_dcr *), GFP_KERNEL); |
3161 | nfit_test->dcr_dma = devm_kcalloc(dev, n: num, |
3162 | size: sizeof(dma_addr_t), GFP_KERNEL); |
3163 | nfit_test->smart = devm_kcalloc(dev, num, |
3164 | sizeof(struct nd_intel_smart), GFP_KERNEL); |
3165 | nfit_test->smart_threshold = devm_kcalloc(dev, n: num, |
3166 | size: sizeof(struct nd_intel_smart_threshold), |
3167 | GFP_KERNEL); |
3168 | nfit_test->fw = devm_kcalloc(dev, n: num, |
3169 | size: sizeof(struct nfit_test_fw), GFP_KERNEL); |
3170 | if (nfit_test->dimm && nfit_test->dimm_dma && nfit_test->label |
3171 | && nfit_test->label_dma && nfit_test->dcr |
3172 | && nfit_test->dcr_dma && nfit_test->flush |
3173 | && nfit_test->flush_dma |
3174 | && nfit_test->fw) |
3175 | /* pass */; |
3176 | else |
3177 | return -ENOMEM; |
3178 | } |
3179 | |
3180 | if (nfit_test->num_pm) { |
3181 | int num = nfit_test->num_pm; |
3182 | |
3183 | nfit_test->spa_set = devm_kcalloc(dev, n: num, size: sizeof(void *), |
3184 | GFP_KERNEL); |
3185 | nfit_test->spa_set_dma = devm_kcalloc(dev, n: num, |
3186 | size: sizeof(dma_addr_t), GFP_KERNEL); |
3187 | if (nfit_test->spa_set && nfit_test->spa_set_dma) |
3188 | /* pass */; |
3189 | else |
3190 | return -ENOMEM; |
3191 | } |
3192 | |
3193 | /* per-nfit specific alloc */ |
3194 | if (nfit_test->alloc(nfit_test)) |
3195 | return -ENOMEM; |
3196 | |
3197 | nfit_test->setup(nfit_test); |
3198 | acpi_desc = &nfit_test->acpi_desc; |
3199 | acpi_nfit_desc_init(acpi_desc, &pdev->dev); |
3200 | nd_desc = &acpi_desc->nd_desc; |
3201 | nd_desc->provider_name = NULL; |
3202 | nd_desc->module = THIS_MODULE; |
3203 | nd_desc->ndctl = nfit_test_ctl; |
3204 | |
3205 | rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_buf, |
3206 | nfit_test->nfit_filled); |
3207 | if (rc) |
3208 | return rc; |
3209 | |
3210 | rc = devm_add_action_or_reset(&pdev->dev, acpi_nfit_shutdown, acpi_desc); |
3211 | if (rc) |
3212 | return rc; |
3213 | |
3214 | if (nfit_test->setup != nfit_test0_setup) |
3215 | return 0; |
3216 | |
3217 | nfit_test->setup_hotplug = 1; |
3218 | nfit_test->setup(nfit_test); |
3219 | |
3220 | obj = kzalloc(size: sizeof(*obj), GFP_KERNEL); |
3221 | if (!obj) |
3222 | return -ENOMEM; |
3223 | obj->type = ACPI_TYPE_BUFFER; |
3224 | obj->buffer.length = nfit_test->nfit_size; |
3225 | obj->buffer.pointer = nfit_test->nfit_buf; |
3226 | *(nfit_test->_fit) = obj; |
3227 | __acpi_nfit_notify(&pdev->dev, nfit_test, 0x80); |
3228 | |
3229 | /* associate dimm devices with nfit_mem data for notification testing */ |
3230 | mutex_lock(&acpi_desc->init_mutex); |
3231 | list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { |
3232 | u32 nfit_handle = __to_nfit_memdev(nfit_mem)->device_handle; |
3233 | int i; |
3234 | |
3235 | for (i = 0; i < ARRAY_SIZE(handle); i++) |
3236 | if (nfit_handle == handle[i]) |
3237 | dev_set_drvdata(dev: nfit_test->dimm_dev[i], |
3238 | data: nfit_mem); |
3239 | } |
3240 | mutex_unlock(lock: &acpi_desc->init_mutex); |
3241 | |
3242 | return 0; |
3243 | } |
3244 | |
3245 | static void nfit_test_release(struct device *dev) |
3246 | { |
3247 | struct nfit_test *nfit_test = to_nfit_test(dev); |
3248 | |
3249 | kfree(objp: nfit_test); |
3250 | } |
3251 | |
3252 | static const struct platform_device_id nfit_test_id[] = { |
3253 | { KBUILD_MODNAME }, |
3254 | { }, |
3255 | }; |
3256 | |
3257 | static struct platform_driver nfit_test_driver = { |
3258 | .probe = nfit_test_probe, |
3259 | .driver = { |
3260 | .name = KBUILD_MODNAME, |
3261 | }, |
3262 | .id_table = nfit_test_id, |
3263 | }; |
3264 | |
3265 | static __init int nfit_test_init(void) |
3266 | { |
3267 | int rc, i; |
3268 | |
3269 | pmem_test(); |
3270 | libnvdimm_test(); |
3271 | acpi_nfit_test(); |
3272 | device_dax_test(); |
3273 | dax_pmem_test(); |
3274 | |
3275 | nfit_test_setup(lookup: nfit_test_lookup, evaluate: nfit_test_evaluate_dsm); |
3276 | |
3277 | nfit_wq = create_singlethread_workqueue("nfit" ); |
3278 | if (!nfit_wq) |
3279 | return -ENOMEM; |
3280 | |
3281 | rc = class_register(class: &nfit_test_dimm); |
3282 | if (rc) |
3283 | goto err_register; |
3284 | |
3285 | nfit_pool = gen_pool_create(ilog2(SZ_4M), NUMA_NO_NODE); |
3286 | if (!nfit_pool) { |
3287 | rc = -ENOMEM; |
3288 | goto err_register; |
3289 | } |
3290 | |
3291 | if (gen_pool_add(pool: nfit_pool, SZ_4G, SZ_4G, NUMA_NO_NODE)) { |
3292 | rc = -ENOMEM; |
3293 | goto err_register; |
3294 | } |
3295 | |
3296 | for (i = 0; i < NUM_NFITS; i++) { |
3297 | struct nfit_test *nfit_test; |
3298 | struct platform_device *pdev; |
3299 | |
3300 | nfit_test = kzalloc(size: sizeof(*nfit_test), GFP_KERNEL); |
3301 | if (!nfit_test) { |
3302 | rc = -ENOMEM; |
3303 | goto err_register; |
3304 | } |
3305 | INIT_LIST_HEAD(list: &nfit_test->resources); |
3306 | badrange_init(badrange: &nfit_test->badrange); |
3307 | switch (i) { |
3308 | case 0: |
3309 | nfit_test->num_pm = NUM_PM; |
3310 | nfit_test->dcr_idx = 0; |
3311 | nfit_test->num_dcr = NUM_DCR; |
3312 | nfit_test->alloc = nfit_test0_alloc; |
3313 | nfit_test->setup = nfit_test0_setup; |
3314 | break; |
3315 | case 1: |
3316 | nfit_test->num_pm = 2; |
3317 | nfit_test->dcr_idx = NUM_DCR; |
3318 | nfit_test->num_dcr = 2; |
3319 | nfit_test->alloc = nfit_test1_alloc; |
3320 | nfit_test->setup = nfit_test1_setup; |
3321 | break; |
3322 | default: |
3323 | rc = -EINVAL; |
3324 | goto err_register; |
3325 | } |
3326 | pdev = &nfit_test->pdev; |
3327 | pdev->name = KBUILD_MODNAME; |
3328 | pdev->id = i; |
3329 | pdev->dev.release = nfit_test_release; |
3330 | rc = platform_device_register(pdev); |
3331 | if (rc) { |
3332 | put_device(dev: &pdev->dev); |
3333 | goto err_register; |
3334 | } |
3335 | get_device(dev: &pdev->dev); |
3336 | |
3337 | rc = dma_coerce_mask_and_coherent(dev: &pdev->dev, DMA_BIT_MASK(64)); |
3338 | if (rc) |
3339 | goto err_register; |
3340 | |
3341 | instances[i] = nfit_test; |
3342 | INIT_WORK(&nfit_test->work, uc_error_notify); |
3343 | } |
3344 | |
3345 | rc = platform_driver_register(&nfit_test_driver); |
3346 | if (rc) |
3347 | goto err_register; |
3348 | return 0; |
3349 | |
3350 | err_register: |
3351 | if (nfit_pool) |
3352 | gen_pool_destroy(nfit_pool); |
3353 | |
3354 | destroy_workqueue(wq: nfit_wq); |
3355 | for (i = 0; i < NUM_NFITS; i++) |
3356 | if (instances[i]) |
3357 | platform_device_unregister(&instances[i]->pdev); |
3358 | nfit_test_teardown(); |
3359 | for (i = 0; i < NUM_NFITS; i++) |
3360 | if (instances[i]) |
3361 | put_device(dev: &instances[i]->pdev.dev); |
3362 | |
3363 | return rc; |
3364 | } |
3365 | |
3366 | static __exit void nfit_test_exit(void) |
3367 | { |
3368 | int i; |
3369 | |
3370 | destroy_workqueue(wq: nfit_wq); |
3371 | for (i = 0; i < NUM_NFITS; i++) |
3372 | platform_device_unregister(&instances[i]->pdev); |
3373 | platform_driver_unregister(&nfit_test_driver); |
3374 | nfit_test_teardown(); |
3375 | |
3376 | gen_pool_destroy(nfit_pool); |
3377 | |
3378 | for (i = 0; i < NUM_NFITS; i++) |
3379 | put_device(dev: &instances[i]->pdev.dev); |
3380 | class_unregister(class: &nfit_test_dimm); |
3381 | } |
3382 | |
3383 | module_init(nfit_test_init); |
3384 | module_exit(nfit_test_exit); |
3385 | MODULE_LICENSE("GPL v2" ); |
3386 | MODULE_AUTHOR("Intel Corporation" ); |
3387 | |