2 * Copyright 1998 Massachusetts Institute of Technology
3 * Copyright (c) 2008 The DragonFly Project.
5 * Permission to use, copy, modify, and distribute this software and
6 * its documentation for any purpose and without fee is hereby
7 * granted, provided that both the above copyright notice and this
8 * permission notice appear in all copies, that both the above
9 * copyright notice and this permission notice appear in all
10 * supporting documentation, and that the name of M.I.T. not be used
11 * in advertising or publicity pertaining to distribution of the
12 * software without specific, written prior permission. M.I.T. makes
13 * no representations about the suitability of this software for any
14 * purpose. It is provided "as is" without express or implied
17 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
18 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
21 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * $FreeBSD: src/sys/i386/i386/nexus.c,v 1.26.2.10 2003/02/22 13:16:45 imp Exp $
34 * This code implements a `root nexus' for Intel Architecture
35 * machines. The function of the root nexus is to serve as an
36 * attachment point for both processors and buses, and to manage
37 * resources which are common to all of them. In particular,
38 * this code implements the core resource managers for interrupt
39 * requests, DMA requests (which rightfully should be a part of the
40 * ISA code but it's easier to do it here for now), I/O port addresses,
41 * and I/O memory address space.
46 #include <sys/param.h>
47 #include <sys/systm.h>
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
51 #include <sys/module.h>
53 #include <sys/interrupt.h>
54 #include <sys/machintr.h>
55 #include <sys/linker.h>
57 #include <machine/vmparam.h>
60 #include <machine/pmap.h>
62 #include <machine/nexusvar.h>
63 #include <machine/smp.h>
64 #include <machine/intr_machdep.h>
65 #include <machine_base/apic/lapic.h>
66 #include <machine_base/apic/ioapic.h>
67 #include <machine/pc/bios.h>
68 #include <machine/metadata.h>
74 #define I386_BUS_SPACE_IO 0 /* space is i/o space */
75 #define I386_BUS_SPACE_MEM 1 /* space is mem space */
77 #define ELF_KERN_STR ("elf"__XSTRING(__ELF_WORD_SIZE)" kernel")
79 static MALLOC_DEFINE(M_NEXUSDEV
, "nexusdev", "Nexus device");
81 struct resource_list nx_resources
;
85 #define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev))
87 static struct rman irq_rman
[MAXCPU
], drq_rman
, port_rman
, mem_rman
;
89 static int nexus_probe(device_t
);
90 static int nexus_attach(device_t
);
91 static int nexus_print_all_resources(device_t dev
);
92 static int nexus_print_child(device_t
, device_t
);
93 static device_t
nexus_add_child(device_t bus
, device_t parent
, int order
,
94 const char *name
, int unit
);
95 static struct resource
*nexus_alloc_resource(device_t
, device_t
, int, int *,
96 u_long
, u_long
, u_long
, u_int
, int);
97 static int nexus_read_ivar(device_t
, device_t
, int, uintptr_t *);
98 static int nexus_write_ivar(device_t
, device_t
, int, uintptr_t);
99 static int nexus_activate_resource(device_t
, device_t
, int, int,
101 static int nexus_deactivate_resource(device_t
, device_t
, int, int,
103 static int nexus_release_resource(device_t
, device_t
, int, int,
105 static int nexus_config_intr(device_t
, device_t
, int, enum intr_trigger
,
107 static int nexus_setup_intr(device_t
, device_t
, struct resource
*, int flags
,
108 void (*)(void *), void *, void **, lwkt_serialize_t
,
110 static int nexus_teardown_intr(device_t
, device_t
, struct resource
*,
112 static int nexus_set_resource(device_t
, device_t
, int, int, u_long
, u_long
,
114 static int nexus_get_resource(device_t
, device_t
, int, int, u_long
*, u_long
*);
115 static void nexus_delete_resource(device_t
, device_t
, int, int);
118 static int nexus_alloc_msi(device_t
, device_t
, int, int, int *, int);
119 static int nexus_release_msi(device_t
, device_t
, int, int *, int);
120 static int nexus_map_msi(device_t
, device_t
, int, uint64_t *, uint32_t *, int);
121 static int nexus_alloc_msix(device_t
, device_t
, int *, int);
122 static int nexus_release_msix(device_t
, device_t
, int, int);
126 * The device_identify method will cause nexus to automatically associate
127 * and attach to the root bus.
129 static device_method_t nexus_methods
[] = {
130 /* Device interface */
131 DEVMETHOD(device_identify
, bus_generic_identify
),
132 DEVMETHOD(device_probe
, nexus_probe
),
133 DEVMETHOD(device_attach
, nexus_attach
),
134 DEVMETHOD(device_detach
, bus_generic_detach
),
135 DEVMETHOD(device_shutdown
, bus_generic_shutdown
),
136 DEVMETHOD(device_suspend
, bus_generic_suspend
),
137 DEVMETHOD(device_resume
, bus_generic_resume
),
140 DEVMETHOD(bus_print_child
, nexus_print_child
),
141 DEVMETHOD(bus_add_child
, nexus_add_child
),
142 DEVMETHOD(bus_read_ivar
, nexus_read_ivar
),
143 DEVMETHOD(bus_write_ivar
, nexus_write_ivar
),
144 DEVMETHOD(bus_alloc_resource
, nexus_alloc_resource
),
145 DEVMETHOD(bus_release_resource
, nexus_release_resource
),
146 DEVMETHOD(bus_activate_resource
, nexus_activate_resource
),
147 DEVMETHOD(bus_deactivate_resource
, nexus_deactivate_resource
),
148 DEVMETHOD(bus_config_intr
, nexus_config_intr
),
149 DEVMETHOD(bus_setup_intr
, nexus_setup_intr
),
150 DEVMETHOD(bus_teardown_intr
, nexus_teardown_intr
),
151 DEVMETHOD(bus_set_resource
, nexus_set_resource
),
152 DEVMETHOD(bus_get_resource
, nexus_get_resource
),
153 DEVMETHOD(bus_delete_resource
, nexus_delete_resource
),
156 DEVMETHOD(pcib_alloc_msi
, nexus_alloc_msi
),
157 DEVMETHOD(pcib_release_msi
, nexus_release_msi
),
158 DEVMETHOD(pcib_map_msi
, nexus_map_msi
),
159 DEVMETHOD(pcib_alloc_msix
, nexus_alloc_msix
),
160 DEVMETHOD(pcib_release_msix
, nexus_release_msix
),
166 static driver_t nexus_driver
= {
171 static devclass_t nexus_devclass
;
173 DRIVER_MODULE(nexus
, root
, nexus_driver
, nexus_devclass
, NULL
, NULL
);
176 nexus_probe(device_t dev
)
180 device_quiet(dev
); /* suppress attach message for neatness */
182 for (cpuid
= 0; cpuid
< ncpus
; ++cpuid
) {
183 struct rman
*rm
= &irq_rman
[cpuid
];
186 rm
->rm_end
= IDT_HWI_VECTORS
- 1;
187 rm
->rm_type
= RMAN_ARRAY
;
188 rm
->rm_descr
= "Interrupt request lines";
190 if (rman_init(rm
, cpuid
))
191 panic("%s rman_init", __func__
);
192 MachIntrABI
.rman_setup(rm
);
196 * ISA DMA on PCI systems is implemented in the ISA part of each
197 * PCI->ISA bridge and the channels can be duplicated if there are
198 * multiple bridges. (eg: laptops with docking stations)
200 drq_rman
.rm_start
= 0;
202 drq_rman
.rm_type
= RMAN_ARRAY
;
203 drq_rman
.rm_descr
= "DMA request lines";
204 /* XXX drq 0 not available on some machines */
205 if (rman_init(&drq_rman
, -1)
206 || rman_manage_region(&drq_rman
,
207 drq_rman
.rm_start
, drq_rman
.rm_end
))
208 panic("%s drq_rman", __func__
);
211 * However, IO ports and Memory truely are global at this level,
212 * as are APIC interrupts (however many IO APICS there turn out
213 * to be on large systems..)
215 port_rman
.rm_start
= 0;
216 port_rman
.rm_end
= 0xffff;
217 port_rman
.rm_type
= RMAN_ARRAY
;
218 port_rman
.rm_descr
= "I/O ports";
219 if (rman_init(&port_rman
, -1)
220 || rman_manage_region(&port_rman
, 0, 0xffff))
221 panic("%s port_rman", __func__
);
223 mem_rman
.rm_start
= 0;
224 mem_rman
.rm_end
= ~0u;
225 mem_rman
.rm_type
= RMAN_ARRAY
;
226 mem_rman
.rm_descr
= "I/O memory addresses";
227 if (rman_init(&mem_rman
, -1)
228 || rman_manage_region(&mem_rman
, 0, ~0))
229 panic("%s mem_rman", __func__
);
231 return bus_generic_probe(dev
);
235 nexus_attach(device_t dev
)
240 * First, let our child driver's identify any child devices that
241 * they can find. Once that is done attach any devices that we
245 bus_generic_probe(dev
);
247 bus_generic_attach(dev
);
250 * And if we didn't see ISA on a pci bridge, create a
251 * connection point now so it shows up "on motherboard".
253 if (!devclass_get_device(devclass_find("isa"), 0)) {
254 child
= BUS_ADD_CHILD(dev
, dev
, 0, "isa", 0);
256 panic("%s isa", __func__
);
257 device_probe_and_attach(child
);
264 nexus_print_all_resources(device_t dev
)
266 struct nexus_device
*ndev
= DEVTONX(dev
);
267 struct resource_list
*rl
= &ndev
->nx_resources
;
270 if (SLIST_FIRST(rl
) || ndev
->nx_pcibus
!= -1)
271 retval
+= kprintf(" at");
273 retval
+= resource_list_print_type(rl
, "port", SYS_RES_IOPORT
, "%#lx");
274 retval
+= resource_list_print_type(rl
, "iomem", SYS_RES_MEMORY
, "%#lx");
275 retval
+= resource_list_print_type(rl
, "irq", SYS_RES_IRQ
, "%ld");
281 nexus_print_child(device_t bus
, device_t child
)
283 struct nexus_device
*ndev
= DEVTONX(child
);
286 retval
+= bus_print_child_header(bus
, child
);
287 retval
+= nexus_print_all_resources(child
);
288 if (ndev
->nx_pcibus
!= -1)
289 retval
+= kprintf(" pcibus %d", ndev
->nx_pcibus
);
290 retval
+= kprintf(" on motherboard\n");
296 nexus_add_child(device_t bus
, device_t parent
, int order
,
297 const char *name
, int unit
)
300 struct nexus_device
*ndev
;
302 ndev
= kmalloc(sizeof(struct nexus_device
), M_NEXUSDEV
, M_INTWAIT
|M_ZERO
);
303 resource_list_init(&ndev
->nx_resources
);
304 ndev
->nx_pcibus
= -1;
306 child
= device_add_child_ordered(parent
, order
, name
, unit
);
308 /* should we free this in nexus_child_detached? */
309 device_set_ivars(child
, ndev
);
315 nexus_read_ivar(device_t dev
, device_t child
, int which
, uintptr_t *result
)
317 struct nexus_device
*ndev
= DEVTONX(child
);
320 case NEXUS_IVAR_PCIBUS
:
321 *result
= ndev
->nx_pcibus
;
330 nexus_write_ivar(device_t dev
, device_t child
, int which
, uintptr_t value
)
332 struct nexus_device
*ndev
= DEVTONX(child
);
335 case NEXUS_IVAR_PCIBUS
:
336 ndev
->nx_pcibus
= value
;
345 * Allocate a resource on behalf of child. NB: child is usually going to be a
346 * child of one of our descendants, not a direct child of nexus0.
347 * (Exceptions include npx.)
349 static struct resource
*
350 nexus_alloc_resource(device_t bus
, device_t child
, int type
, int *rid
,
351 u_long start
, u_long end
, u_long count
, u_int flags
, int cpuid
)
353 struct nexus_device
*ndev
= DEVTONX(child
);
355 struct resource_list_entry
*rle
;
357 int needactivate
= flags
& RF_ACTIVE
;
360 * If this is an allocation of the "default" range for a given RID, and
361 * we know what the resources for this device are (ie. they aren't maintained
362 * by a child bus), then work out the start/end values.
364 if ((start
== 0UL) && (end
== ~0UL) && (count
== 1)) {
367 rle
= resource_list_find(&ndev
->nx_resources
, type
, *rid
);
380 KASSERT(cpuid
>= 0 && cpuid
< ncpus
,
381 ("nexus invalid cpuid: %d", cpuid
));
382 rm
= &irq_rman
[cpuid
];
401 rv
= rman_reserve_resource(rm
, start
, end
, count
, flags
, child
);
404 rman_set_rid(rv
, *rid
);
406 if (type
== SYS_RES_MEMORY
) {
407 rman_set_bustag(rv
, I386_BUS_SPACE_MEM
);
408 } else if (type
== SYS_RES_IOPORT
) {
409 rman_set_bustag(rv
, I386_BUS_SPACE_IO
);
410 rman_set_bushandle(rv
, rv
->r_start
);
414 if (bus_activate_resource(child
, type
, *rid
, rv
)) {
415 rman_release_resource(rv
);
424 nexus_activate_resource(device_t bus
, device_t child
, int type
, int rid
,
428 * If this is a memory resource, map it into the kernel.
430 if (rman_get_bustag(r
) == I386_BUS_SPACE_MEM
) {
433 if (rman_get_end(r
) < 1024 * 1024) {
435 * The first 1Mb is mapped at KERNBASE.
437 vaddr
= (caddr_t
)(uintptr_t)(KERNBASE
+ rman_get_start(r
));
443 paddr
= rman_get_start(r
);
444 psize
= rman_get_size(r
);
446 poffs
= paddr
- trunc_page(paddr
);
447 vaddr
= (caddr_t
) pmap_mapdev(paddr
-poffs
, psize
+poffs
) + poffs
;
449 rman_set_virtual(r
, vaddr
);
450 /* IBM-PC: the type of bus_space_handle_t is u_int */
451 rman_set_bushandle(r
, (bus_space_handle_t
) vaddr
);
453 return (rman_activate_resource(r
));
457 nexus_deactivate_resource(device_t bus
, device_t child
, int type
, int rid
,
461 * If this is a memory resource, unmap it.
463 if ((rman_get_bustag(r
) == I386_BUS_SPACE_MEM
) &&
464 (rman_get_end(r
) >= 1024 * 1024)) {
467 psize
= rman_get_size(r
);
468 pmap_unmapdev((vm_offset_t
)rman_get_virtual(r
), psize
);
471 return (rman_deactivate_resource(r
));
475 nexus_release_resource(device_t bus
, device_t child
, int type
, int rid
,
478 if (rman_get_flags(r
) & RF_ACTIVE
) {
479 int error
= bus_deactivate_resource(child
, type
, rid
, r
);
483 return (rman_release_resource(r
));
487 nexus_config_intr(device_t bus
, device_t chile
, int irq
,
488 enum intr_trigger trig
, enum intr_polarity pola
)
490 machintr_legacy_intr_config(irq
, trig
, pola
);
495 * Currently this uses the really grody interface from kern/kern_intr.c
496 * (which really doesn't belong in kern/anything.c). Eventually, all of
497 * the code in kern_intr.c and machdep_intr.c should get moved here, since
498 * this is going to be the official interface.
501 nexus_setup_intr(device_t bus
, device_t child
, struct resource
*irq
,
502 int flags
, void (*ihand
)(void *), void *arg
, void **cookiep
,
503 lwkt_serialize_t serializer
, const char *desc
)
507 /* somebody tried to setup an irq that failed to allocate! */
509 panic("%s: NULL irq resource!", __func__
);
513 if ((irq
->r_flags
& RF_SHAREABLE
) == 0)
514 icflags
|= INTR_EXCL
;
517 * We depend here on rman_activate_resource() being idempotent.
519 error
= rman_activate_resource(irq
);
523 /* Use device name, if description is not specified */
525 desc
= device_get_nameunit(child
);
528 * XXX cast the interrupt handler function to an inthand2_t. The
529 * difference is that an additional frame argument is passed which
530 * we do not currently want to expose the BUS subsystem to.
532 *cookiep
= register_int(irq
->r_start
, (inthand2_t
*)ihand
, arg
,
533 desc
, serializer
, icflags
, rman_get_cpuid(irq
));
534 if (*cookiep
== NULL
)
540 nexus_teardown_intr(device_t dev
, device_t child
, struct resource
*r
, void *ih
)
543 unregister_int(ih
, rman_get_cpuid(r
));
550 nexus_set_resource(device_t dev
, device_t child
, int type
, int rid
,
551 u_long start
, u_long count
, int cpuid
)
553 struct nexus_device
*ndev
= DEVTONX(child
);
554 struct resource_list
*rl
= &ndev
->nx_resources
;
556 /* XXX this should return a success/failure indicator */
557 resource_list_add(rl
, type
, rid
, start
, start
+ count
- 1, count
,
563 nexus_get_resource(device_t dev
, device_t child
, int type
, int rid
, u_long
*startp
, u_long
*countp
)
565 struct nexus_device
*ndev
= DEVTONX(child
);
566 struct resource_list
*rl
= &ndev
->nx_resources
;
567 struct resource_list_entry
*rle
;
569 rle
= resource_list_find(rl
, type
, rid
);
570 device_printf(child
, "type %d rid %d startp %p countp %p - got %p\n",
571 type
, rid
, startp
, countp
, rle
);
575 *startp
= rle
->start
;
577 *countp
= rle
->count
;
582 nexus_delete_resource(device_t dev
, device_t child
, int type
, int rid
)
584 struct nexus_device
*ndev
= DEVTONX(child
);
585 struct resource_list
*rl
= &ndev
->nx_resources
;
587 resource_list_delete(rl
, type
, rid
);
592 nexus_alloc_msi(device_t dev
, device_t child
, int count
, int maxcount
,
593 int *irqs
, int cpuid
)
598 return MachIntrABI
.msi_alloc(irqs
, count
, cpuid
);
602 nexus_release_msi(device_t dev
, device_t child
, int count
, int *irqs
, int cpuid
)
604 KKASSERT(lapic_enable
);
605 MachIntrABI
.msi_release(irqs
, count
, cpuid
);
610 nexus_map_msi(device_t dev
, device_t child
, int irq
, uint64_t *addr
,
611 uint32_t *data
, int cpuid
)
613 KKASSERT(lapic_enable
);
614 MachIntrABI
.msi_map(irq
, addr
, data
, cpuid
);
619 nexus_alloc_msix(device_t dev
, device_t child
, int *irq
, int cpuid
)
624 return MachIntrABI
.msix_alloc(irq
, cpuid
);
628 nexus_release_msix(device_t dev
, device_t child
, int irq
, int cpuid
)
630 KKASSERT(lapic_enable
);
631 MachIntrABI
.msix_release(irq
, cpuid
);
636 /* Placeholder for system RAM. */
638 ram_identify(driver_t
*driver
, device_t parent
)
640 if (resource_disabled("ram", 0))
642 if (BUS_ADD_CHILD(parent
, parent
, 0, "ram", 0) == NULL
)
643 panic("%s", __func__
);
647 ram_probe(device_t dev
)
650 device_set_desc(dev
, "System RAM");
655 ram_attach(device_t dev
)
657 struct bios_smap
*smapbase
, *smap
, *smapend
;
658 struct resource
*res
;
665 device_set_desc(dev
, "System RAM");
667 /* Retrieve the system memory map from the loader. */
668 kmdp
= preload_search_by_type("elf kernel");
670 kmdp
= preload_search_by_type(ELF_KERN_STR
);
672 smapbase
= (struct bios_smap
*)preload_search_info(kmdp
,
673 MODINFO_METADATA
| MODINFOMD_SMAP
);
676 if (smapbase
!= NULL
) {
677 smapsize
= *((u_int32_t
*)smapbase
- 1);
678 smapend
= (struct bios_smap
*)((uintptr_t)smapbase
+ smapsize
);
681 for (smap
= smapbase
; smap
< smapend
; smap
++) {
682 if (smap
->type
!= SMAP_TYPE_MEMORY
||
685 error
= bus_set_resource(dev
, SYS_RES_MEMORY
, rid
,
686 smap
->base
, smap
->length
, -1);
688 panic("%s: resource %d failed set with %d",
689 __func__
, rid
, error
);
690 res
= bus_alloc_resource_any(dev
, SYS_RES_MEMORY
, &rid
,
693 panic("%s: resource %d failed to "
694 "attach 0x%016jx/%jd",
696 (intmax_t)smap
->base
,
697 (intmax_t)smap
->length
);
705 * If the system map is not available, fall back to using
706 * dump_avail[]. We use the dump_avail[] array rather than
707 * phys_avail[] for the memory map as phys_avail[] contains
708 * holes for kernel memory, page 0, the message buffer, and
709 * the dcons buffer. We test the end address in the loop
710 * instead of the start since the start address for the first
713 for (rid
= 0, p
= &dump_avail
[0]; p
->phys_end
; ++rid
, ++p
) {
714 error
= bus_set_resource(dev
, SYS_RES_MEMORY
, rid
,
716 p
->phys_end
- p
->phys_beg
,
719 panic("%s: resource %d failed set with %d", __func__
,
721 res
= bus_alloc_resource_any(dev
, SYS_RES_MEMORY
, &rid
, 0);
723 panic("%s: resource %d failed to attach", __func__
,
728 static device_method_t ram_methods
[] = {
729 /* Device interface */
730 DEVMETHOD(device_identify
, ram_identify
),
731 DEVMETHOD(device_probe
, ram_probe
),
732 DEVMETHOD(device_attach
, ram_attach
),
736 static driver_t ram_driver
= {
742 static devclass_t ram_devclass
;
743 DRIVER_MODULE(ram
, nexus
, ram_driver
, ram_devclass
, NULL
, NULL
);