1/*-
2 * Copyright (c) 2017 The FreeBSD Foundation
3 * All rights reserved.
4 * Copyright (c) 2018, 2019 Intel Corporation
5 *
6 * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
7 * under sponsorship from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include "opt_acpi.h"
35#include "opt_ddb.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/bio.h>
40#include <sys/bitstring.h>
41#include <sys/bus.h>
42#include <sys/kernel.h>
43#include <sys/lock.h>
44#include <sys/malloc.h>
45#include <sys/module.h>
46#include <sys/sbuf.h>
47#include <sys/sysctl.h>
48#include <sys/uuid.h>
49#include <contrib/dev/acpica/include/acpi.h>
50#include <contrib/dev/acpica/include/accommon.h>
51#include <contrib/dev/acpica/include/acuuid.h>
52#include <dev/acpica/acpivar.h>
53#include <dev/nvdimm/nvdimm_var.h>
54
55#define _COMPONENT	ACPI_OEM
56ACPI_MODULE_NAME("NVDIMM")
57
58static struct uuid intel_nvdimm_dsm_uuid =
59    {0x4309AC30,0x0D11,0x11E4,0x91,0x91,{0x08,0x00,0x20,0x0C,0x9A,0x66}};
60#define INTEL_NVDIMM_DSM_REV 1
61#define INTEL_NVDIMM_DSM_GET_LABEL_SIZE 4
62#define INTEL_NVDIMM_DSM_GET_LABEL_DATA 5
63
64static devclass_t nvdimm_devclass;
65static devclass_t nvdimm_root_devclass;
66MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory");
67
68static int
69read_label_area_size(struct nvdimm_dev *nv)
70{
71	ACPI_OBJECT *result_buffer;
72	ACPI_HANDLE handle;
73	ACPI_STATUS status;
74	ACPI_BUFFER result;
75	uint32_t *out;
76	int error;
77
78	handle = nvdimm_root_get_acpi_handle(nv->nv_dev);
79	if (handle == NULL)
80		return (ENODEV);
81	result.Length = ACPI_ALLOCATE_BUFFER;
82	result.Pointer = NULL;
83	status = acpi_EvaluateDSM(handle, (uint8_t *)&intel_nvdimm_dsm_uuid,
84	    INTEL_NVDIMM_DSM_REV, INTEL_NVDIMM_DSM_GET_LABEL_SIZE, NULL,
85	    &result);
86	error = ENXIO;
87	if (ACPI_SUCCESS(status) && result.Pointer != NULL &&
88	    result.Length >= sizeof(ACPI_OBJECT)) {
89		result_buffer = result.Pointer;
90		if (result_buffer->Type == ACPI_TYPE_BUFFER &&
91		    result_buffer->Buffer.Length >= 12) {
92			out = (uint32_t *)result_buffer->Buffer.Pointer;
93			nv->label_area_size = out[1];
94			nv->max_label_xfer = out[2];
95			error = 0;
96		}
97	}
98	if (result.Pointer != NULL)
99		AcpiOsFree(result.Pointer);
100	return (error);
101}
102
103static int
104read_label_area(struct nvdimm_dev *nv, uint8_t *dest, off_t offset,
105    off_t length)
106{
107	ACPI_BUFFER result;
108	ACPI_HANDLE handle;
109	ACPI_OBJECT params_pkg, params_buf, *result_buf;
110	ACPI_STATUS status;
111	uint32_t params[2];
112	off_t to_read;
113	int error;
114
115	error = 0;
116	handle = nvdimm_root_get_acpi_handle(nv->nv_dev);
117	if (offset < 0 || length <= 0 ||
118	    offset + length > nv->label_area_size ||
119	    handle == NULL)
120		return (ENODEV);
121	params_pkg.Type = ACPI_TYPE_PACKAGE;
122	params_pkg.Package.Count = 1;
123	params_pkg.Package.Elements = &params_buf;
124	params_buf.Type = ACPI_TYPE_BUFFER;
125	params_buf.Buffer.Length = sizeof(params);
126	params_buf.Buffer.Pointer = (UINT8 *)params;
127	while (length > 0) {
128		to_read = MIN(length, nv->max_label_xfer);
129		params[0] = offset;
130		params[1] = to_read;
131		result.Length = ACPI_ALLOCATE_BUFFER;
132		result.Pointer = NULL;
133		status = acpi_EvaluateDSM(handle,
134		    (uint8_t *)&intel_nvdimm_dsm_uuid, INTEL_NVDIMM_DSM_REV,
135		    INTEL_NVDIMM_DSM_GET_LABEL_DATA, &params_pkg, &result);
136		if (ACPI_FAILURE(status) ||
137		    result.Length < sizeof(ACPI_OBJECT) ||
138		    result.Pointer == NULL) {
139			error = ENXIO;
140			break;
141		}
142		result_buf = (ACPI_OBJECT *)result.Pointer;
143		if (result_buf->Type != ACPI_TYPE_BUFFER ||
144		    result_buf->Buffer.Pointer == NULL ||
145		    result_buf->Buffer.Length != 4 + to_read ||
146		    ((uint16_t *)result_buf->Buffer.Pointer)[0] != 0) {
147			error = ENXIO;
148			break;
149		}
150		bcopy(result_buf->Buffer.Pointer + 4, dest, to_read);
151		dest += to_read;
152		offset += to_read;
153		length -= to_read;
154		if (result.Pointer != NULL) {
155			AcpiOsFree(result.Pointer);
156			result.Pointer = NULL;
157		}
158	}
159	if (result.Pointer != NULL)
160		AcpiOsFree(result.Pointer);
161	return (error);
162}
163
164static uint64_t
165fletcher64(const void *data, size_t length)
166{
167	size_t i;
168	uint32_t a, b;
169	const uint32_t *d;
170
171	a = 0;
172	b = 0;
173	d = (const uint32_t *)data;
174	length = length / sizeof(uint32_t);
175	for (i = 0; i < length; i++) {
176		a += d[i];
177		b += a;
178	}
179	return ((uint64_t)b << 32 | a);
180}
181
182static bool
183label_index_is_valid(struct nvdimm_label_index *index, uint32_t max_labels,
184    size_t size, size_t offset)
185{
186	uint64_t checksum;
187
188	index = (struct nvdimm_label_index *)((uint8_t *)index + offset);
189	if (strcmp(index->signature, NVDIMM_INDEX_BLOCK_SIGNATURE) != 0)
190		return false;
191	checksum = index->checksum;
192	index->checksum = 0;
193	if (checksum != fletcher64(index, size) ||
194	    index->this_offset != size * offset || index->this_size != size ||
195	    index->other_offset != size * (offset == 0 ? 1 : 0) ||
196	    index->seq == 0 || index->seq > 3 || index->slot_cnt > max_labels ||
197	    index->label_size != 1)
198		return false;
199	return true;
200}
201
202static int
203read_label(struct nvdimm_dev *nv, int num)
204{
205	struct nvdimm_label_entry *entry, *i, *next;
206	uint64_t checksum;
207	off_t offset;
208	int error;
209
210	offset = nv->label_index->label_offset +
211	    num * (128 << nv->label_index->label_size);
212	entry = malloc(sizeof(*entry), M_NVDIMM, M_WAITOK);
213	error = read_label_area(nv, (uint8_t *)&entry->label, offset,
214	    sizeof(struct nvdimm_label));
215	if (error != 0) {
216		free(entry, M_NVDIMM);
217		return (error);
218	}
219	checksum = entry->label.checksum;
220	entry->label.checksum = 0;
221	if (checksum != fletcher64(&entry->label, sizeof(entry->label)) ||
222	    entry->label.slot != num) {
223		free(entry, M_NVDIMM);
224		return (ENXIO);
225	}
226
227	/* Insertion ordered by dimm_phys_addr */
228	if (SLIST_EMPTY(&nv->labels) ||
229	    entry->label.dimm_phys_addr <=
230	    SLIST_FIRST(&nv->labels)->label.dimm_phys_addr) {
231		SLIST_INSERT_HEAD(&nv->labels, entry, link);
232		return (0);
233	}
234	SLIST_FOREACH_SAFE(i, &nv->labels, link, next) {
235		if (next == NULL ||
236		    entry->label.dimm_phys_addr <= next->label.dimm_phys_addr) {
237			SLIST_INSERT_AFTER(i, entry, link);
238			return (0);
239		}
240	}
241	__assert_unreachable();
242}
243
244static int
245read_labels(struct nvdimm_dev *nv)
246{
247	struct nvdimm_label_index *indices;
248	size_t bitfield_size, index_size, num_labels;
249	int error, n;
250	bool index_0_valid, index_1_valid;
251
252	for (index_size = 256; ; index_size += 256) {
253		num_labels = 8 * (index_size -
254		    sizeof(struct nvdimm_label_index));
255		if (index_size + num_labels * sizeof(struct nvdimm_label) >=
256		    nv->label_area_size)
257			break;
258	}
259	num_labels = (nv->label_area_size - index_size) /
260	    sizeof(struct nvdimm_label);
261	bitfield_size = roundup2(num_labels, 8) / 8;
262	indices = malloc(2 * index_size, M_NVDIMM, M_WAITOK);
263	error = read_label_area(nv, (void *)indices, 0, 2 * index_size);
264	if (error != 0) {
265		free(indices, M_NVDIMM);
266		return (error);
267	}
268	index_0_valid = label_index_is_valid(indices, num_labels, index_size,
269	    0);
270	index_1_valid = label_index_is_valid(indices, num_labels, index_size,
271	    1);
272	if (!index_0_valid && !index_1_valid) {
273		free(indices, M_NVDIMM);
274		return (ENXIO);
275	}
276	if (index_0_valid && index_1_valid &&
277	    (indices[1].seq > indices[0].seq ||
278	    (indices[1].seq == 1 && indices[0].seq == 3)))
279		index_0_valid = false;
280	nv->label_index = malloc(index_size, M_NVDIMM, M_WAITOK);
281	bcopy(indices + (index_0_valid ? 0 : 1), nv->label_index, index_size);
282	free(indices, M_NVDIMM);
283	for (bit_ffc_at((bitstr_t *)nv->label_index->free, 0, num_labels, &n);
284	     n >= 0;
285	     bit_ffc_at((bitstr_t *)nv->label_index->free, n + 1, num_labels,
286	     &n)) {
287		read_label(nv, n);
288	}
289	return (0);
290}
291
292struct nvdimm_dev *
293nvdimm_find_by_handle(nfit_handle_t nv_handle)
294{
295	struct nvdimm_dev *res;
296	device_t *dimms;
297	int i, error, num_dimms;
298
299	res = NULL;
300	error = devclass_get_devices(nvdimm_devclass, &dimms, &num_dimms);
301	if (error != 0)
302		return (NULL);
303	for (i = 0; i < num_dimms; i++) {
304		if (nvdimm_root_get_device_handle(dimms[i]) == nv_handle) {
305			res = device_get_softc(dimms[i]);
306			break;
307		}
308	}
309	free(dimms, M_TEMP);
310	return (res);
311}
312
313static int
314nvdimm_probe(device_t dev)
315{
316
317	return (BUS_PROBE_NOWILDCARD);
318}
319
320static int
321nvdimm_attach(device_t dev)
322{
323	struct nvdimm_dev *nv;
324	struct sysctl_ctx_list *ctx;
325	struct sysctl_oid *oid;
326	struct sysctl_oid_list *children;
327	struct sbuf *sb;
328	ACPI_TABLE_NFIT *nfitbl;
329	ACPI_HANDLE handle;
330	ACPI_STATUS status;
331	ACPI_NFIT_MEMORY_MAP **maps;
332	int error, i, num_maps;
333	uint16_t flags;
334
335	nv = device_get_softc(dev);
336	ctx = device_get_sysctl_ctx(dev);
337	oid = device_get_sysctl_tree(dev);
338	children = SYSCTL_CHILDREN(oid);
339	handle = nvdimm_root_get_acpi_handle(dev);
340	if (handle == NULL)
341		return (EINVAL);
342	nv->nv_dev = dev;
343	nv->nv_handle = nvdimm_root_get_device_handle(dev);
344
345	status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl);
346	if (ACPI_FAILURE(status)) {
347		if (bootverbose)
348			device_printf(dev, "cannot get NFIT\n");
349		return (ENXIO);
350	}
351	acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr,
352	    &nv->nv_flush_addr_cnt);
353
354	/*
355	 * Each NVDIMM should have at least one memory map associated with it.
356	 * If any of the maps have one of the error flags set, reflect that in
357	 * the overall status.
358	 */
359	acpi_nfit_get_memory_maps_by_dimm(nfitbl, nv->nv_handle, &maps,
360	    &num_maps);
361	if (num_maps == 0) {
362		free(nv->nv_flush_addr, M_NVDIMM);
363		free(maps, M_NVDIMM);
364		device_printf(dev, "cannot find memory map\n");
365		return (ENXIO);
366	}
367	flags = 0;
368	for (i = 0; i < num_maps; i++) {
369		flags |= maps[i]->Flags;
370	}
371	free(maps, M_NVDIMM);
372
373	/* sbuf_new_auto(9) is M_WAITOK; no need to check for NULL. */
374	sb = sbuf_new_auto();
375	(void) sbuf_printf(sb, "0x%b", flags,
376	    "\20"
377	    "\001SAVE_FAILED"
378	    "\002RESTORE_FAILED"
379	    "\003FLUSH_FAILED"
380	    "\004NOT_ARMED"
381	    "\005HEALTH_OBSERVED"
382	    "\006HEALTH_ENABLED"
383	    "\007MAP_FAILED");
384	error = sbuf_finish(sb);
385	if (error != 0) {
386		sbuf_delete(sb);
387		free(nv->nv_flush_addr, M_NVDIMM);
388		device_printf(dev, "cannot convert flags to string\n");
389		return (error);
390	}
391	/* strdup(9) is M_WAITOK; no need to check for NULL. */
392	nv->nv_flags_str = strdup(sbuf_data(sb), M_NVDIMM);
393	sbuf_delete(sb);
394	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "flags",
395	    CTLFLAG_RD | CTLFLAG_MPSAFE, nv->nv_flags_str, 0,
396	    "NVDIMM State Flags");
397	/*
398	 * Anything other than HEALTH_ENABLED indicates a fault condition of
399	 * some kind, so log if that's seen.
400	 */
401	if ((flags & ~ACPI_NFIT_MEM_HEALTH_ENABLED) != 0)
402		device_printf(dev, "flags: %s\n", nv->nv_flags_str);
403
404	AcpiPutTable(&nfitbl->Header);
405	error = read_label_area_size(nv);
406	if (error == 0) {
407		/*
408		 * Ignoring errors reading labels. Not all NVDIMMs
409		 * support labels and namespaces.
410		 */
411		read_labels(nv);
412	}
413	return (0);
414}
415
416static int
417nvdimm_detach(device_t dev)
418{
419	struct nvdimm_dev *nv;
420	struct nvdimm_label_entry *label, *next;
421
422	nv = device_get_softc(dev);
423	free(nv->nv_flags_str, M_NVDIMM);
424	free(nv->nv_flush_addr, M_NVDIMM);
425	free(nv->label_index, M_NVDIMM);
426	SLIST_FOREACH_SAFE(label, &nv->labels, link, next) {
427		SLIST_REMOVE_HEAD(&nv->labels, link);
428		free(label, M_NVDIMM);
429	}
430	return (0);
431}
432
433static int
434nvdimm_suspend(device_t dev)
435{
436
437	return (0);
438}
439
440static int
441nvdimm_resume(device_t dev)
442{
443
444	return (0);
445}
446
447static ACPI_STATUS
448find_dimm(ACPI_HANDLE handle, UINT32 nesting_level, void *context,
449    void **return_value)
450{
451	ACPI_DEVICE_INFO *device_info;
452	ACPI_STATUS status;
453
454	status = AcpiGetObjectInfo(handle, &device_info);
455	if (ACPI_FAILURE(status))
456		return_ACPI_STATUS(AE_ERROR);
457	if (device_info->Address == (uintptr_t)context) {
458		*(ACPI_HANDLE *)return_value = handle;
459		return_ACPI_STATUS(AE_CTRL_TERMINATE);
460	}
461	return_ACPI_STATUS(AE_OK);
462}
463
464static ACPI_HANDLE
465get_dimm_acpi_handle(ACPI_HANDLE root_handle, nfit_handle_t adr)
466{
467	ACPI_HANDLE res;
468	ACPI_STATUS status;
469
470	res = NULL;
471	status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, root_handle, 1, find_dimm,
472	    NULL, (void *)(uintptr_t)adr, &res);
473	if (ACPI_FAILURE(status))
474		res = NULL;
475	return (res);
476}
477
478static int
479nvdimm_root_create_devs(device_t dev, ACPI_TABLE_NFIT *nfitbl)
480{
481	ACPI_HANDLE root_handle, dimm_handle;
482	device_t child;
483	nfit_handle_t *dimm_ids, *dimm;
484	uintptr_t *ivars;
485	int num_dimm_ids;
486
487	root_handle = acpi_get_handle(dev);
488	acpi_nfit_get_dimm_ids(nfitbl, &dimm_ids, &num_dimm_ids);
489	for (dimm = dimm_ids; dimm < dimm_ids + num_dimm_ids; dimm++) {
490		dimm_handle = get_dimm_acpi_handle(root_handle, *dimm);
491		child = BUS_ADD_CHILD(dev, 100, "nvdimm", -1);
492		if (child == NULL) {
493			device_printf(dev, "failed to create nvdimm\n");
494			return (ENXIO);
495		}
496		ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX, sizeof(uintptr_t),
497		    M_NVDIMM, M_ZERO | M_WAITOK);
498		device_set_ivars(child, ivars);
499		nvdimm_root_set_acpi_handle(child, dimm_handle);
500		nvdimm_root_set_device_handle(child, *dimm);
501	}
502	free(dimm_ids, M_NVDIMM);
503	return (0);
504}
505
506static int
507nvdimm_root_create_spas(struct nvdimm_root_dev *dev, ACPI_TABLE_NFIT *nfitbl)
508{
509	ACPI_NFIT_SYSTEM_ADDRESS **spas, **spa;
510	struct SPA_mapping *spa_mapping;
511	enum SPA_mapping_type spa_type;
512	int error, num_spas;
513
514	error = 0;
515	acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas);
516	for (spa = spas; spa < spas + num_spas; spa++) {
517		spa_type = nvdimm_spa_type_from_uuid(
518			(struct uuid *)(*spa)->RangeGuid);
519		if (spa_type == SPA_TYPE_UNKNOWN)
520			continue;
521		spa_mapping = malloc(sizeof(struct SPA_mapping), M_NVDIMM,
522		    M_WAITOK | M_ZERO);
523		error = nvdimm_spa_init(spa_mapping, *spa, spa_type);
524		if (error != 0) {
525			nvdimm_spa_fini(spa_mapping);
526			free(spa_mapping, M_NVDIMM);
527			break;
528		}
529		nvdimm_create_namespaces(spa_mapping, nfitbl);
530		SLIST_INSERT_HEAD(&dev->spas, spa_mapping, link);
531	}
532	free(spas, M_NVDIMM);
533	return (error);
534}
535
536static char *nvdimm_root_id[] = {"ACPI0012", NULL};
537
538static int
539nvdimm_root_probe(device_t dev)
540{
541
542	if (acpi_disabled("nvdimm"))
543		return (ENXIO);
544	if (ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id)
545	    != NULL) {
546		device_set_desc(dev, "ACPI NVDIMM root device");
547		return (BUS_PROBE_DEFAULT);
548	}
549	return (ENXIO);
550}
551
552static int
553nvdimm_root_attach(device_t dev)
554{
555	struct nvdimm_root_dev *root;
556	ACPI_TABLE_NFIT *nfitbl;
557	ACPI_STATUS status;
558	int error;
559
560	status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl);
561	if (ACPI_FAILURE(status)) {
562		device_printf(dev, "cannot get NFIT\n");
563		return (ENXIO);
564	}
565	error = nvdimm_root_create_devs(dev, nfitbl);
566	if (error != 0)
567		return (error);
568	error = bus_generic_attach(dev);
569	if (error != 0)
570		return (error);
571	root = device_get_softc(dev);
572	error = nvdimm_root_create_spas(root, nfitbl);
573	AcpiPutTable(&nfitbl->Header);
574	return (error);
575}
576
577static int
578nvdimm_root_detach(device_t dev)
579{
580	struct nvdimm_root_dev *root;
581	struct SPA_mapping *spa, *next;
582	device_t *children;
583	int i, error, num_children;
584
585	root = device_get_softc(dev);
586	SLIST_FOREACH_SAFE(spa, &root->spas, link, next) {
587		nvdimm_destroy_namespaces(spa);
588		nvdimm_spa_fini(spa);
589		SLIST_REMOVE_HEAD(&root->spas, link);
590		free(spa, M_NVDIMM);
591	}
592	error = bus_generic_detach(dev);
593	if (error != 0)
594		return (error);
595	error = device_get_children(dev, &children, &num_children);
596	if (error != 0)
597		return (error);
598	for (i = 0; i < num_children; i++)
599		free(device_get_ivars(children[i]), M_NVDIMM);
600	free(children, M_TEMP);
601	error = device_delete_children(dev);
602	return (error);
603}
604
605static int
606nvdimm_root_read_ivar(device_t dev, device_t child, int index,
607    uintptr_t *result)
608{
609
610	if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX)
611		return (ENOENT);
612	*result = ((uintptr_t *)device_get_ivars(child))[index];
613	return (0);
614}
615
616static int
617nvdimm_root_write_ivar(device_t dev, device_t child, int index,
618    uintptr_t value)
619{
620
621	if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX)
622		return (ENOENT);
623	((uintptr_t *)device_get_ivars(child))[index] = value;
624	return (0);
625}
626
627static int
628nvdimm_root_child_location_str(device_t dev, device_t child, char *buf,
629    size_t buflen)
630{
631	ACPI_HANDLE handle;
632	int res;
633
634	handle = nvdimm_root_get_acpi_handle(child);
635	if (handle != NULL)
636		res = snprintf(buf, buflen, "handle=%s", acpi_name(handle));
637	else
638		res = snprintf(buf, buflen, "");
639
640	if (res >= buflen)
641		return (EOVERFLOW);
642	return (0);
643}
644
645static device_method_t nvdimm_methods[] = {
646	DEVMETHOD(device_probe, nvdimm_probe),
647	DEVMETHOD(device_attach, nvdimm_attach),
648	DEVMETHOD(device_detach, nvdimm_detach),
649	DEVMETHOD(device_suspend, nvdimm_suspend),
650	DEVMETHOD(device_resume, nvdimm_resume),
651	DEVMETHOD_END
652};
653
654static driver_t	nvdimm_driver = {
655	"nvdimm",
656	nvdimm_methods,
657	sizeof(struct nvdimm_dev),
658};
659
660static device_method_t nvdimm_root_methods[] = {
661	DEVMETHOD(device_probe, nvdimm_root_probe),
662	DEVMETHOD(device_attach, nvdimm_root_attach),
663	DEVMETHOD(device_detach, nvdimm_root_detach),
664	DEVMETHOD(bus_add_child, bus_generic_add_child),
665	DEVMETHOD(bus_read_ivar, nvdimm_root_read_ivar),
666	DEVMETHOD(bus_write_ivar, nvdimm_root_write_ivar),
667	DEVMETHOD(bus_child_location_str, nvdimm_root_child_location_str),
668	DEVMETHOD_END
669};
670
671static driver_t	nvdimm_root_driver = {
672	"nvdimm_root",
673	nvdimm_root_methods,
674	sizeof(struct nvdimm_root_dev),
675};
676
677DRIVER_MODULE(nvdimm_root, acpi, nvdimm_root_driver, nvdimm_root_devclass, NULL,
678    NULL);
679DRIVER_MODULE(nvdimm, nvdimm_root, nvdimm_driver, nvdimm_devclass, NULL, NULL);
680MODULE_DEPEND(nvdimm, acpi, 1, 1, 1);
681