sys/platform/pc64: port the ram device from FreeBSD's nexus.c
authorMarkus Pfeiffer <profmakx@dragonflybsd.org>
Sun, 17 Aug 2014 15:48:16 +0000 (16:48 +0100)
committerMarkus Pfeiffer <markus.pfeiffer@morphism.de>
Wed, 20 Aug 2014 22:49:03 +0000 (22:49 +0000)
Our kernel didn't reserve RAM in the IO manager, which lead to
mmaped IO from devices being mapped in the same address space,
most notably this lead to problems with the i915 driver, bug

sys/platform/pc64/x86_64/nexus.c

index bd974af..a0e308b 100644 (file)
@@ -52,6 +52,7 @@
 #include <sys/rman.h>
 #include <sys/interrupt.h>
 #include <sys/machintr.h>
+#include <sys/linker.h>
 
 #include <machine/vmparam.h>
 #include <vm/vm.h>
@@ -63,6 +64,8 @@
 #include <machine/intr_machdep.h>
 #include <machine_base/apic/lapic.h>
 #include <machine_base/apic/ioapic.h>
+#include <machine/pc/bios.h>
+#include <machine/metadata.h>
 
 #if NPCI > 0
 #include "pcib_if.h"
@@ -71,6 +74,8 @@
 #define I386_BUS_SPACE_IO       0       /* space is i/o space */
 #define I386_BUS_SPACE_MEM      1       /* space is mem space */
 
+#define ELF_KERN_STR    ("elf"__XSTRING(__ELF_WORD_SIZE)" kernel")
+
 static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device");
 struct nexus_device {
        struct resource_list    nx_resources;
@@ -627,3 +632,107 @@ nexus_release_msix(device_t dev, device_t child, int irq, int cpuid)
        return 0;
 }
 #endif
+
+/* Placeholder for system RAM. */
+static void
+ram_identify(driver_t *driver, device_t parent)
+{
+
+        if (resource_disabled("ram", 0))
+                return;
+        if (BUS_ADD_CHILD(parent, parent, 0, "ram", 0) == NULL)
+                panic("ram_identify");
+}
+
+static int
+ram_probe(device_t dev)
+{
+
+        device_quiet(dev);
+        device_set_desc(dev, "System RAM");
+        return (0);
+}
+
+static int
+ram_attach(device_t dev)
+{
+        struct bios_smap *smapbase, *smap, *smapend;
+        struct resource *res;
+        vm_paddr_t *p;
+        caddr_t kmdp;
+        uint32_t smapsize;
+        int error, rid;
+
+        /* Retrieve the system memory map from the loader. */
+        kmdp = preload_search_by_type("elf kernel");
+        if (kmdp == NULL)
+                kmdp = preload_search_by_type(ELF_KERN_STR);
+        if (kmdp != NULL)
+                smapbase = (struct bios_smap *)preload_search_info(kmdp,
+                    MODINFO_METADATA | MODINFOMD_SMAP);
+        else
+                smapbase = NULL;
+        if (smapbase != NULL) {
+                smapsize = *((u_int32_t *)smapbase - 1);
+                smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize);
+
+                rid = 0;
+                for (smap = smapbase; smap < smapend; smap++) {
+                        if (smap->type != SMAP_TYPE_MEMORY ||
+                            smap->length == 0)
+                                continue;
+                        error = bus_set_resource(dev, SYS_RES_MEMORY, rid,
+                            smap->base, smap->length, -1);
+                        if (error)
+                                panic(
+                                    "ram_attach: resource %d failed set with %d",
+                                    rid, error);
+                        res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+                            0);
+                        if (res == NULL)
+                                panic("ram_attach: resource %d failed to attach",
+                                    rid);
+                        rid++;
+                }
+                return (0);
+        }
+
+        /*
+         * If the system map is not available, fall back to using
+         * dump_avail[].  We use the dump_avail[] array rather than
+         * phys_avail[] for the memory map as phys_avail[] contains
+         * holes for kernel memory, page 0, the message buffer, and
+         * the dcons buffer.  We test the end address in the loop
+         * instead of the start since the start address for the first
+         * segment is 0.
+         */
+        for (rid = 0, p = dump_avail; p[1] != 0; rid++, p += 2) {
+                error = bus_set_resource(dev, SYS_RES_MEMORY, rid, p[0],
+                    p[1] - p[0], -1);
+                if (error)
+                        panic("ram_attach: resource %d failed set with %d", rid,
+                            error);
+                res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 0);
+                if (res == NULL)
+                        panic("ram_attach: resource %d failed to attach", rid);
+        }
+        return (0);
+}
+static device_method_t ram_methods[] = {
+        /* Device interface */
+        DEVMETHOD(device_identify,      ram_identify),
+        DEVMETHOD(device_probe,         ram_probe),
+        DEVMETHOD(device_attach,        ram_attach),
+        { 0, 0 }
+};
+
+static driver_t ram_driver = {
+        "ram",
+        ram_methods,
+        1,              /* no softc */
+};
+
+static devclass_t ram_devclass;
+
+DRIVER_MODULE(ram, nexus, ram_driver, ram_devclass, 0, 0);
+