1cbd974b4SConrad Meyer /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3cbd974b4SConrad Meyer * 4cbd974b4SConrad Meyer * Copyright (c) 2019 Dell EMC Isilon 5cbd974b4SConrad Meyer * 6cbd974b4SConrad Meyer * Redistribution and use in source and binary forms, with or without 7cbd974b4SConrad Meyer * modification, are permitted provided that the following conditions 8cbd974b4SConrad Meyer * are met: 9cbd974b4SConrad Meyer * 1. Redistributions of source code must retain the above copyright 10cbd974b4SConrad Meyer * notice, this list of conditions and the following disclaimer. 11cbd974b4SConrad Meyer * 2. Redistributions in binary form must reproduce the above copyright 12cbd974b4SConrad Meyer * notice, this list of conditions and the following disclaimer in the 13cbd974b4SConrad Meyer * documentation and/or other materials provided with the distribution. 14cbd974b4SConrad Meyer * 15cbd974b4SConrad Meyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16cbd974b4SConrad Meyer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17cbd974b4SConrad Meyer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18cbd974b4SConrad Meyer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19cbd974b4SConrad Meyer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20cbd974b4SConrad Meyer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21cbd974b4SConrad Meyer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22cbd974b4SConrad Meyer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23cbd974b4SConrad Meyer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24cbd974b4SConrad Meyer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25cbd974b4SConrad Meyer * SUCH DAMAGE. 26cbd974b4SConrad Meyer */ 27cbd974b4SConrad Meyer 28cbd974b4SConrad Meyer #include <sys/param.h> 29cbd974b4SConrad Meyer #include <sys/bio.h> 30cbd974b4SConrad Meyer #include <sys/bitstring.h> 31cbd974b4SConrad Meyer #include <sys/bus.h> 32cbd974b4SConrad Meyer #include <sys/efi.h> 33cbd974b4SConrad Meyer #include <sys/kernel.h> 34cbd974b4SConrad Meyer #include <sys/linker.h> 35cbd974b4SConrad Meyer #include <sys/lock.h> 36cbd974b4SConrad Meyer #include <sys/malloc.h> 37cbd974b4SConrad Meyer #include <sys/module.h> 38cbd974b4SConrad Meyer #include <sys/sbuf.h> 39cbd974b4SConrad Meyer #include <sys/uuid.h> 40cbd974b4SConrad Meyer 41cbd974b4SConrad Meyer #include <vm/vm_param.h> 42cbd974b4SConrad Meyer 43cbd974b4SConrad Meyer #include <machine/metadata.h> 44cbd974b4SConrad Meyer #include <machine/pc/bios.h> 45cbd974b4SConrad Meyer 46cbd974b4SConrad Meyer #include <contrib/dev/acpica/include/acpi.h> 47cbd974b4SConrad Meyer 48cbd974b4SConrad Meyer #include <dev/nvdimm/nvdimm_var.h> 49cbd974b4SConrad Meyer 50cbd974b4SConrad Meyer struct nvdimm_e820_bus { 51cbd974b4SConrad Meyer SLIST_HEAD(, SPA_mapping) spas; 52cbd974b4SConrad Meyer }; 53cbd974b4SConrad Meyer 54cbd974b4SConrad Meyer #define NVDIMM_E820 "nvdimm_e820" 55cbd974b4SConrad Meyer 56cbd974b4SConrad Meyer static MALLOC_DEFINE(M_NVDIMM_E820, NVDIMM_E820, "NVDIMM e820 bus memory"); 57cbd974b4SConrad Meyer 58cbd974b4SConrad Meyer static const struct bios_smap *smapbase; 59cbd974b4SConrad Meyer static struct { 60cbd974b4SConrad Meyer vm_paddr_t start; 61cbd974b4SConrad Meyer vm_paddr_t size; 62cbd974b4SConrad Meyer } pram_segments[VM_PHYSSEG_MAX]; 63cbd974b4SConrad Meyer static unsigned pram_nreg; 64cbd974b4SConrad Meyer 65cbd974b4SConrad Meyer static void 66cbd974b4SConrad Meyer nvdimm_e820_dump_prams(device_t dev, const char *func, int hintunit) 67cbd974b4SConrad Meyer { 68cbd974b4SConrad Meyer char buffer[256]; 69cbd974b4SConrad Meyer struct sbuf sb; 70cbd974b4SConrad Meyer bool printed = false; 71cbd974b4SConrad Meyer unsigned i; 72cbd974b4SConrad Meyer 73cbd974b4SConrad Meyer sbuf_new(&sb, buffer, sizeof(buffer), SBUF_FIXEDLEN); 74cbd974b4SConrad Meyer sbuf_set_drain(&sb, sbuf_printf_drain, NULL); 75cbd974b4SConrad Meyer 76cbd974b4SConrad Meyer sbuf_printf(&sb, "%s: %s: ", device_get_nameunit(dev), func); 77cbd974b4SConrad Meyer if (hintunit < 0) 78cbd974b4SConrad Meyer sbuf_cat(&sb, "Found BIOS PRAM regions: "); 79cbd974b4SConrad Meyer else 80cbd974b4SConrad Meyer sbuf_printf(&sb, "Remaining unallocated PRAM regions after " 81cbd974b4SConrad Meyer "hint %d: ", hintunit); 82cbd974b4SConrad Meyer 83cbd974b4SConrad Meyer for (i = 0; i < pram_nreg; i++) { 84cbd974b4SConrad Meyer if (pram_segments[i].size == 0) 85cbd974b4SConrad Meyer continue; 86cbd974b4SConrad Meyer if (printed) 87cbd974b4SConrad Meyer sbuf_putc(&sb, ','); 88cbd974b4SConrad Meyer else 89cbd974b4SConrad Meyer printed = true; 90cbd974b4SConrad Meyer sbuf_printf(&sb, "0x%jx-0x%jx", 91cbd974b4SConrad Meyer (uintmax_t)pram_segments[i].start, 92cbd974b4SConrad Meyer (uintmax_t)pram_segments[i].start + pram_segments[i].size 93cbd974b4SConrad Meyer - 1); 94cbd974b4SConrad Meyer } 95cbd974b4SConrad Meyer 96cbd974b4SConrad Meyer if (!printed) 97cbd974b4SConrad Meyer sbuf_cat(&sb, "<none>"); 98cbd974b4SConrad Meyer sbuf_putc(&sb, '\n'); 99cbd974b4SConrad Meyer sbuf_finish(&sb); 100cbd974b4SConrad Meyer sbuf_delete(&sb); 101cbd974b4SConrad Meyer } 102cbd974b4SConrad Meyer 103cbd974b4SConrad Meyer static int 104cbd974b4SConrad Meyer nvdimm_e820_create_spas(device_t dev) 105cbd974b4SConrad Meyer { 106cbd974b4SConrad Meyer static const vm_size_t HINT_ALL = (vm_size_t)-1; 107cbd974b4SConrad Meyer 108cbd974b4SConrad Meyer ACPI_NFIT_SYSTEM_ADDRESS nfit_sa; 109cbd974b4SConrad Meyer struct SPA_mapping *spa_mapping; 110cbd974b4SConrad Meyer enum SPA_mapping_type spa_type; 111cbd974b4SConrad Meyer struct nvdimm_e820_bus *sc; 112cbd974b4SConrad Meyer const char *hinttype; 113cbd974b4SConrad Meyer long hintaddrl, hintsizel; 114cbd974b4SConrad Meyer vm_paddr_t hintaddr; 115cbd974b4SConrad Meyer vm_size_t hintsize; 116cbd974b4SConrad Meyer unsigned i, j; 117cbd974b4SConrad Meyer int error; 118cbd974b4SConrad Meyer 119cbd974b4SConrad Meyer sc = device_get_softc(dev); 120cbd974b4SConrad Meyer error = 0; 121cbd974b4SConrad Meyer nfit_sa = (ACPI_NFIT_SYSTEM_ADDRESS) { 0 }; 122cbd974b4SConrad Meyer 123cbd974b4SConrad Meyer if (bootverbose) 124cbd974b4SConrad Meyer nvdimm_e820_dump_prams(dev, __func__, -1); 125cbd974b4SConrad Meyer 126cbd974b4SConrad Meyer for (i = 0; 127cbd974b4SConrad Meyer resource_long_value("nvdimm_spa", i, "maddr", &hintaddrl) == 0; 128cbd974b4SConrad Meyer i++) { 129cbd974b4SConrad Meyer if (resource_long_value("nvdimm_spa", i, "msize", &hintsizel) 130cbd974b4SConrad Meyer != 0) { 131cbd974b4SConrad Meyer device_printf(dev, "hint.nvdimm_spa.%u missing msize\n", 132cbd974b4SConrad Meyer i); 133cbd974b4SConrad Meyer continue; 134cbd974b4SConrad Meyer } 135cbd974b4SConrad Meyer 136cbd974b4SConrad Meyer hintaddr = (vm_paddr_t)hintaddrl; 137cbd974b4SConrad Meyer hintsize = (vm_size_t)hintsizel; 1387bf55415SConrad Meyer if ((hintaddr & PAGE_MASK) != 0 || 1397bf55415SConrad Meyer ((hintsize & PAGE_MASK) != 0 && hintsize != HINT_ALL)) { 140cbd974b4SConrad Meyer device_printf(dev, "hint.nvdimm_spa.%u addr or size " 141cbd974b4SConrad Meyer "not page aligned\n", i); 142cbd974b4SConrad Meyer continue; 143cbd974b4SConrad Meyer } 144cbd974b4SConrad Meyer 145cbd974b4SConrad Meyer if (resource_string_value("nvdimm_spa", i, "type", &hinttype) 146cbd974b4SConrad Meyer != 0) { 147cbd974b4SConrad Meyer device_printf(dev, "hint.nvdimm_spa.%u missing type\n", 148cbd974b4SConrad Meyer i); 149cbd974b4SConrad Meyer continue; 150cbd974b4SConrad Meyer } 151cbd974b4SConrad Meyer spa_type = nvdimm_spa_type_from_name(hinttype); 152cbd974b4SConrad Meyer if (spa_type == SPA_TYPE_UNKNOWN) { 153cbd974b4SConrad Meyer device_printf(dev, "hint.nvdimm_spa%u.type does not " 154cbd974b4SConrad Meyer "match any known SPA types\n", i); 155cbd974b4SConrad Meyer continue; 156cbd974b4SConrad Meyer } 157cbd974b4SConrad Meyer 158cbd974b4SConrad Meyer for (j = 0; j < pram_nreg; j++) { 159cbd974b4SConrad Meyer if (pram_segments[j].start <= hintaddr && 160cbd974b4SConrad Meyer (hintsize == HINT_ALL || 161cbd974b4SConrad Meyer (pram_segments[j].start + pram_segments[j].size) >= 162cbd974b4SConrad Meyer (hintaddr + hintsize))) 163cbd974b4SConrad Meyer break; 164cbd974b4SConrad Meyer } 165cbd974b4SConrad Meyer 166cbd974b4SConrad Meyer if (j == pram_nreg) { 167cbd974b4SConrad Meyer device_printf(dev, "hint.nvdimm_spa%u hint does not " 168cbd974b4SConrad Meyer "match any region\n", i); 169cbd974b4SConrad Meyer continue; 170cbd974b4SConrad Meyer } 171cbd974b4SConrad Meyer 172cbd974b4SConrad Meyer /* Carve off "SPA" from available regions. */ 173cbd974b4SConrad Meyer if (pram_segments[j].start == hintaddr) { 174cbd974b4SConrad Meyer /* Easy case first: beginning of segment. */ 175cbd974b4SConrad Meyer if (hintsize == HINT_ALL) 176cbd974b4SConrad Meyer hintsize = pram_segments[j].size; 177cbd974b4SConrad Meyer pram_segments[j].start += hintsize; 178cbd974b4SConrad Meyer pram_segments[j].size -= hintsize; 179cbd974b4SConrad Meyer /* We might leave an empty segment; who cares. */ 180cbd974b4SConrad Meyer } else if (hintsize == HINT_ALL || 181cbd974b4SConrad Meyer (pram_segments[j].start + pram_segments[j].size) == 182cbd974b4SConrad Meyer (hintaddr + hintsize)) { 183cbd974b4SConrad Meyer /* 2nd easy case: end of segment. */ 184cbd974b4SConrad Meyer if (hintsize == HINT_ALL) 185cbd974b4SConrad Meyer hintsize = pram_segments[j].size - 186cbd974b4SConrad Meyer (hintaddr - pram_segments[j].start); 187cbd974b4SConrad Meyer pram_segments[j].size -= hintsize; 188cbd974b4SConrad Meyer } else { 189cbd974b4SConrad Meyer /* Hard case: mid segment. */ 190cbd974b4SConrad Meyer if (pram_nreg == nitems(pram_segments)) { 191cbd974b4SConrad Meyer /* Improbable, but handle gracefully. */ 192cbd974b4SConrad Meyer device_printf(dev, "Ran out of %zu segments\n", 193cbd974b4SConrad Meyer nitems(pram_segments)); 194cbd974b4SConrad Meyer error = ENOBUFS; 195cbd974b4SConrad Meyer break; 196cbd974b4SConrad Meyer } 197cbd974b4SConrad Meyer 198cbd974b4SConrad Meyer if (j != pram_nreg - 1) { 199cbd974b4SConrad Meyer memmove(&pram_segments[j + 2], 200cbd974b4SConrad Meyer &pram_segments[j + 1], 201cbd974b4SConrad Meyer (pram_nreg - 1 - j) * 202cbd974b4SConrad Meyer sizeof(pram_segments[0])); 203cbd974b4SConrad Meyer } 204cbd974b4SConrad Meyer pram_nreg++; 205cbd974b4SConrad Meyer 206cbd974b4SConrad Meyer pram_segments[j + 1].start = hintaddr + hintsize; 207cbd974b4SConrad Meyer pram_segments[j + 1].size = 208cbd974b4SConrad Meyer (pram_segments[j].start + pram_segments[j].size) - 209cbd974b4SConrad Meyer (hintaddr + hintsize); 210cbd974b4SConrad Meyer pram_segments[j].size = hintaddr - 211cbd974b4SConrad Meyer pram_segments[j].start; 212cbd974b4SConrad Meyer } 213cbd974b4SConrad Meyer 214cbd974b4SConrad Meyer if (bootverbose) 215cbd974b4SConrad Meyer nvdimm_e820_dump_prams(dev, __func__, (int)i); 216cbd974b4SConrad Meyer 217cbd974b4SConrad Meyer spa_mapping = malloc(sizeof(*spa_mapping), M_NVDIMM_E820, 218cbd974b4SConrad Meyer M_WAITOK | M_ZERO); 219cbd974b4SConrad Meyer 220cbd974b4SConrad Meyer /* Mock up a super primitive table for nvdimm_spa_init(). */ 221cbd974b4SConrad Meyer nfit_sa.RangeIndex = i; 222cbd974b4SConrad Meyer nfit_sa.Flags = 0; 223cbd974b4SConrad Meyer nfit_sa.Address = hintaddr; 224cbd974b4SConrad Meyer nfit_sa.Length = hintsize; 225cbd974b4SConrad Meyer nfit_sa.MemoryMapping = EFI_MD_ATTR_WB | EFI_MD_ATTR_WT | 226cbd974b4SConrad Meyer EFI_MD_ATTR_UC; 227cbd974b4SConrad Meyer 228cbd974b4SConrad Meyer error = nvdimm_spa_init(spa_mapping, &nfit_sa, spa_type); 229cbd974b4SConrad Meyer if (error != 0) { 230cbd974b4SConrad Meyer nvdimm_spa_fini(spa_mapping); 231cbd974b4SConrad Meyer free(spa_mapping, M_NVDIMM_E820); 232cbd974b4SConrad Meyer break; 233cbd974b4SConrad Meyer } 234cbd974b4SConrad Meyer 235cbd974b4SConrad Meyer SLIST_INSERT_HEAD(&sc->spas, spa_mapping, link); 236cbd974b4SConrad Meyer } 237cbd974b4SConrad Meyer return (error); 238cbd974b4SConrad Meyer } 239cbd974b4SConrad Meyer 240cbd974b4SConrad Meyer static int 241cbd974b4SConrad Meyer nvdimm_e820_remove_spas(device_t dev) 242cbd974b4SConrad Meyer { 243cbd974b4SConrad Meyer struct nvdimm_e820_bus *sc; 244cbd974b4SConrad Meyer struct SPA_mapping *spa, *next; 245cbd974b4SConrad Meyer 246cbd974b4SConrad Meyer sc = device_get_softc(dev); 247cbd974b4SConrad Meyer 248cbd974b4SConrad Meyer SLIST_FOREACH_SAFE(spa, &sc->spas, link, next) { 249cbd974b4SConrad Meyer nvdimm_spa_fini(spa); 250cbd974b4SConrad Meyer SLIST_REMOVE_HEAD(&sc->spas, link); 251cbd974b4SConrad Meyer free(spa, M_NVDIMM_E820); 252cbd974b4SConrad Meyer } 253cbd974b4SConrad Meyer return (0); 254cbd974b4SConrad Meyer } 255cbd974b4SConrad Meyer 256cbd974b4SConrad Meyer static void 257216ca4ceSJohn Baldwin nvdimm_e820_identify(driver_t *driver, device_t parent) 258cbd974b4SConrad Meyer { 259cbd974b4SConrad Meyer device_t child; 260cbd974b4SConrad Meyer 261216ca4ceSJohn Baldwin if (resource_disabled(driver->name, 0)) 262cbd974b4SConrad Meyer return; 263cbd974b4SConrad Meyer /* Just create a single instance of the fake bus. */ 264216ca4ceSJohn Baldwin if (device_find_child(parent, driver->name, -1) != NULL) 265cbd974b4SConrad Meyer return; 266cbd974b4SConrad Meyer 267*b72ae900SAhmad Khalifa smapbase = (const void *)preload_search_info(preload_kmdp, 268cbd974b4SConrad Meyer MODINFO_METADATA | MODINFOMD_SMAP); 269cbd974b4SConrad Meyer 270cbd974b4SConrad Meyer /* Only supports BIOS SMAP for now. */ 271cbd974b4SConrad Meyer if (smapbase == NULL) 272cbd974b4SConrad Meyer return; 273cbd974b4SConrad Meyer 274a05a6804SWarner Losh child = BUS_ADD_CHILD(parent, 0, driver->name, DEVICE_UNIT_ANY); 275cbd974b4SConrad Meyer if (child == NULL) 276216ca4ceSJohn Baldwin device_printf(parent, "add %s child failed\n", driver->name); 277cbd974b4SConrad Meyer } 278cbd974b4SConrad Meyer 279cbd974b4SConrad Meyer static int 280cbd974b4SConrad Meyer nvdimm_e820_probe(device_t dev) 281cbd974b4SConrad Meyer { 282cbd974b4SConrad Meyer /* 283cbd974b4SConrad Meyer * nexus panics if a child doesn't have ivars. BUS_ADD_CHILD uses 284cbd974b4SConrad Meyer * nexus_add_child, which creates fuckin ivars. but sometimes if you 285cbd974b4SConrad Meyer * unload and reload nvdimm_e820, the device node stays but the ivars 286cbd974b4SConrad Meyer * are deleted??? avoid trivial panic but this is a kludge. 287cbd974b4SConrad Meyer */ 288cbd974b4SConrad Meyer if (device_get_ivars(dev) == NULL) 289cbd974b4SConrad Meyer return (ENXIO); 290cbd974b4SConrad Meyer 291cbd974b4SConrad Meyer device_quiet(dev); 292cbd974b4SConrad Meyer device_set_desc(dev, "Legacy e820 NVDIMM root device"); 293cbd974b4SConrad Meyer return (BUS_PROBE_NOWILDCARD); 294cbd974b4SConrad Meyer } 295cbd974b4SConrad Meyer 296cbd974b4SConrad Meyer static int 297cbd974b4SConrad Meyer nvdimm_e820_attach(device_t dev) 298cbd974b4SConrad Meyer { 299cbd974b4SConrad Meyer const struct bios_smap *smapend, *smap; 300cbd974b4SConrad Meyer uint32_t smapsize; 301cbd974b4SConrad Meyer unsigned nregions; 302cbd974b4SConrad Meyer int error; 303cbd974b4SConrad Meyer 304cbd974b4SConrad Meyer smapsize = *((const uint32_t *)smapbase - 1); 305cbd974b4SConrad Meyer smapend = (const void *)((const char *)smapbase + smapsize); 306cbd974b4SConrad Meyer 307cbd974b4SConrad Meyer for (nregions = 0, smap = smapbase; smap < smapend; smap++) { 308cbd974b4SConrad Meyer if (smap->type != SMAP_TYPE_PRAM || smap->length == 0) 309cbd974b4SConrad Meyer continue; 310cbd974b4SConrad Meyer pram_segments[nregions].start = smap->base; 311cbd974b4SConrad Meyer pram_segments[nregions].size = smap->length; 312cbd974b4SConrad Meyer 313cbd974b4SConrad Meyer device_printf(dev, "Found PRAM 0x%jx +0x%jx\n", 314cbd974b4SConrad Meyer (uintmax_t)smap->base, (uintmax_t)smap->length); 315cbd974b4SConrad Meyer 316cbd974b4SConrad Meyer nregions++; 317cbd974b4SConrad Meyer } 318cbd974b4SConrad Meyer 319cbd974b4SConrad Meyer if (nregions == 0) { 320cbd974b4SConrad Meyer device_printf(dev, "No e820 PRAM regions detected\n"); 321cbd974b4SConrad Meyer return (ENXIO); 322cbd974b4SConrad Meyer } 323cbd974b4SConrad Meyer pram_nreg = nregions; 324cbd974b4SConrad Meyer 325cbd974b4SConrad Meyer error = nvdimm_e820_create_spas(dev); 326cbd974b4SConrad Meyer return (error); 327cbd974b4SConrad Meyer } 328cbd974b4SConrad Meyer 329cbd974b4SConrad Meyer static int 330cbd974b4SConrad Meyer nvdimm_e820_detach(device_t dev) 331cbd974b4SConrad Meyer { 332cbd974b4SConrad Meyer int error; 333cbd974b4SConrad Meyer 334cbd974b4SConrad Meyer error = nvdimm_e820_remove_spas(dev); 335cbd974b4SConrad Meyer return (error); 336cbd974b4SConrad Meyer } 337cbd974b4SConrad Meyer 338cbd974b4SConrad Meyer static device_method_t nvdimm_e820_methods[] = { 339cbd974b4SConrad Meyer DEVMETHOD(device_identify, nvdimm_e820_identify), 340cbd974b4SConrad Meyer DEVMETHOD(device_probe, nvdimm_e820_probe), 341cbd974b4SConrad Meyer DEVMETHOD(device_attach, nvdimm_e820_attach), 342cbd974b4SConrad Meyer DEVMETHOD(device_detach, nvdimm_e820_detach), 343cbd974b4SConrad Meyer DEVMETHOD_END 344cbd974b4SConrad Meyer }; 345cbd974b4SConrad Meyer 346cbd974b4SConrad Meyer static driver_t nvdimm_e820_driver = { 347cbd974b4SConrad Meyer NVDIMM_E820, 348cbd974b4SConrad Meyer nvdimm_e820_methods, 349cbd974b4SConrad Meyer sizeof(struct nvdimm_e820_bus), 350cbd974b4SConrad Meyer }; 351cbd974b4SConrad Meyer 352cbd974b4SConrad Meyer static int 353cbd974b4SConrad Meyer nvdimm_e820_chainevh(struct module *m, int e, void *arg __unused) 354cbd974b4SConrad Meyer { 355cbd974b4SConrad Meyer devclass_t dc; 356cbd974b4SConrad Meyer device_t dev, parent; 357cbd974b4SConrad Meyer int i, error, maxunit; 358cbd974b4SConrad Meyer 359cbd974b4SConrad Meyer switch (e) { 360cbd974b4SConrad Meyer case MOD_UNLOAD: 36145dc8e3cSJohn Baldwin dc = devclass_find(nvdimm_e820_driver.name); 362cbd974b4SConrad Meyer maxunit = devclass_get_maxunit(dc); 363cbd974b4SConrad Meyer for (i = 0; i < maxunit; i++) { 364cbd974b4SConrad Meyer dev = devclass_get_device(dc, i); 365cbd974b4SConrad Meyer if (dev == NULL) 366cbd974b4SConrad Meyer continue; 367cbd974b4SConrad Meyer parent = device_get_parent(dev); 368cbd974b4SConrad Meyer if (parent == NULL) { 369cbd974b4SConrad Meyer /* Not sure how this would happen. */ 370cbd974b4SConrad Meyer continue; 371cbd974b4SConrad Meyer } 372cbd974b4SConrad Meyer error = device_delete_child(parent, dev); 373cbd974b4SConrad Meyer if (error != 0) 374cbd974b4SConrad Meyer return (error); 375cbd974b4SConrad Meyer } 376cbd974b4SConrad Meyer break; 377cbd974b4SConrad Meyer default: 378cbd974b4SConrad Meyer /* Prevent compiler warning about unhandled cases. */ 379cbd974b4SConrad Meyer break; 380cbd974b4SConrad Meyer } 381cbd974b4SConrad Meyer return (0); 382cbd974b4SConrad Meyer } 383cbd974b4SConrad Meyer 384534c3629SJohn Baldwin DRIVER_MODULE(nvdimm_e820, nexus, nvdimm_e820_driver, 385cbd974b4SConrad Meyer nvdimm_e820_chainevh, NULL); 386