code:
/*	$OpenBSD: macepcibridge.c,v 1.47 2014/05/19 21:18:42 miod Exp $ */

/*
 * Copyright (c) 2009 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 2001-2004 Opsycon AB (www.opsycon.se)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

/*
 * Machine dependent PCI BUS Bridge driver on Mace (O2).
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/ioctl.h>
#include <sys/extent.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/vmparam.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/ppbreg.h>
#include <dev/pci/pcidevs.h>

#include <dev/cardbus/rbus.h>

#include <mips64/archtype.h>
#include <sgi/localbus/crimebus.h>
#include <sgi/localbus/macebus.h>
#include <sgi/localbus/macebusvar.h>
#include <sgi/pci/macepcibrvar.h>

#include "cardbus.h"

int	 mace_pcibrmatch(struct device *, void *, void *);
void	 mace_pcibrattach(struct device *, struct device *, void *);

void	 mace_pcibr_attach_hook(struct device *, struct device *,
	    struct pcibus_attach_args *);
int	 mace_pcibr_bus_maxdevs(void *, int);
pcitag_t mace_pcibr_make_tag(void *, int, int, int);
void	 mace_pcibr_decompose_tag(void *, pcitag_t, int *, int *, int *);
int	 mace_pcibr_conf_size(void *, pcitag_t);
pcireg_t mace_pcibr_conf_read(void *, pcitag_t, int);
void	 mace_pcibr_conf_write(void *, pcitag_t, int, pcireg_t);
int	 mace_pcibr_probe_device_hook(void *, struct pci_attach_args *);
int	 mace_pcibr_get_widget(void *);
int	 mace_pcibr_get_dl(void *, pcitag_t, struct sgi_device_location *);
int	 mace_pcibr_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
const char *mace_pcibr_intr_string(void *, pci_intr_handle_t);
void	*mace_pcibr_intr_establish(void *, pci_intr_handle_t, int,
	    int (*)(void *), void *, const char *);
void	 mace_pcibr_intr_disestablish(void *, void *);
int	 mace_pcibr_intr_line(void *, pci_intr_handle_t);
int	 mace_pcibr_ppb_setup(void *, pcitag_t, bus_addr_t *, bus_addr_t *,
	    bus_addr_t *, bus_addr_t *);
void	*mace_pcibr_rbus_parent_io(struct pci_attach_args *);
void	*mace_pcibr_rbus_parent_mem(struct pci_attach_args *);

void	*mace_pcib_space_vaddr(bus_space_tag_t, bus_space_handle_t);
void	 mace_pcib_space_barrier(bus_space_tag_t, bus_space_handle_t,
	    bus_size_t, bus_size_t, int);
bus_addr_t mace_pcibr_pa_to_device(paddr_t);
paddr_t	 mace_pcibr_device_to_pa(bus_addr_t);

int	mace_pcibr_rbus_space_map(bus_space_tag_t, bus_addr_t, bus_size_t,
	    int, bus_space_handle_t *);
void	mace_pcibr_rbus_space_unmap(bus_space_tag_t, bus_space_handle_t,
	    bus_size_t, bus_addr_t *);

void	mace_pcibr_configure(struct mace_pcibr_softc *);
void	mace_pcibr_device_fixup(struct mace_pcibr_softc *, int, int);
int	mace_pcibr_errintr(void *);

struct cfattach macepcibr_ca = {
	sizeof(struct mace_pcibr_softc), mace_pcibrmatch, mace_pcibrattach,
};

struct cfdriver macepcibr_cd = {
	NULL, "macepcibr", DV_DULL,
};

bus_space_t mace_pcibbus_mem_tag = {
	PHYS_TO_XKPHYS(MACE_PCI_MEM_BASE, CCA_NC),
	NULL,
	mace_pcib_read_1, mace_pcib_write_1,
	mace_pcib_read_2, mace_pcib_write_2,
	mace_pcib_read_4, mace_pcib_write_4,
	mace_pcib_read_8, mace_pcib_write_8,
	mace_pcib_read_raw_2, mace_pcib_write_raw_2,
	mace_pcib_read_raw_4, mace_pcib_write_raw_4,
	mace_pcib_read_raw_8, mace_pcib_write_raw_8,
	mace_pcib_space_map, mace_pcib_space_unmap, mace_pcib_space_region,
	mace_pcib_space_vaddr, mace_pcib_space_barrier
};

bus_space_t mace_pcibbus_io_tag = {
	PHYS_TO_XKPHYS(MACE_PCI_IO_BASE, CCA_NC),
	NULL,
	mace_pcib_read_1, mace_pcib_write_1,
	mace_pcib_read_2, mace_pcib_write_2,
	mace_pcib_read_4, mace_pcib_write_4,
	mace_pcib_read_8, mace_pcib_write_8,
	mace_pcib_read_raw_2, mace_pcib_write_raw_2,
	mace_pcib_read_raw_4, mace_pcib_write_raw_4,
	mace_pcib_read_raw_8, mace_pcib_write_raw_8,
	mace_pcib_space_map, mace_pcib_space_unmap, mace_pcib_space_region,
	mace_pcib_space_vaddr, mace_pcib_space_barrier
};

static const struct mips_pci_chipset mace_pci_chipset = {
	.pc_attach_hook = mace_pcibr_attach_hook,
	.pc_bus_maxdevs = mace_pcibr_bus_maxdevs,
	.pc_make_tag = mace_pcibr_make_tag,
	.pc_decompose_tag = mace_pcibr_decompose_tag,
	.pc_conf_size = mace_pcibr_conf_size,
	.pc_conf_read = mace_pcibr_conf_read,
	.pc_conf_write = mace_pcibr_conf_write,
	.pc_probe_device_hook = mace_pcibr_probe_device_hook,
	.pc_get_widget = mace_pcibr_get_widget,
	.pc_get_dl = mace_pcibr_get_dl,
	.pc_intr_map = mace_pcibr_intr_map,
	.pc_intr_string = mace_pcibr_intr_string,
	.pc_intr_establish = mace_pcibr_intr_establish,
	.pc_intr_disestablish = mace_pcibr_intr_disestablish,
	.pc_intr_line = mace_pcibr_intr_line,
	.pc_ppb_setup = mace_pcibr_ppb_setup,
#if NCARDBUS > 0
	.pc_rbus_parent_io = mace_pcibr_rbus_parent_io,
	.pc_rbus_parent_mem = mace_pcibr_rbus_parent_mem
#endif
};

/*
 * PCI doesn't have any special needs; just use the generic versions
 * of these functions.
 */
struct machine_bus_dma_tag mace_pci_bus_dma_tag = {
	NULL,                   /* _cookie */
	_dmamap_create,
	_dmamap_destroy,
	_dmamap_load,
	_dmamap_load_mbuf,
	_dmamap_load_uio,
	_dmamap_load_raw,
	_dmamap_load_buffer,
	_dmamap_unload,
	_dmamap_sync,
	_dmamem_alloc,
	_dmamem_free,
	_dmamem_map,
	_dmamem_unmap,
	_dmamem_mmap,
	mace_pcibr_pa_to_device,
	mace_pcibr_device_to_pa,
	CRIME_MEMORY_MASK
};

const struct _perr_map {
	pcireg_t mask;
	pcireg_t flag;
	const char *text;
} perr_map[] = {
  { PERR_MASTER_ABORT,	PERR_MASTER_ABORT_ADDR_VALID,	"master abort" },
  { PERR_TARGET_ABORT,	PERR_TARGET_ABORT_ADDR_VALID,	"target abort" },
  { PERR_DATA_PARITY_ERR,PERR_DATA_PARITY_ADDR_VALID,	"data parity error" },
  { PERR_RETRY_ERR,	PERR_RETRY_ADDR_VALID,	"retry error" },
  { PERR_ILLEGAL_CMD,	0,	"illegal command" },
  { PERR_SYSTEM_ERR,	0,	"system error" },
  { PERR_INTERRUPT_TEST,0,	"interrupt test" },
  { PERR_PARITY_ERR,	0,	"parity error" },
  { PERR_OVERRUN,	0,	"overrun error" },
  { PERR_RSVD,		0,	"reserved ??" },
  { PERR_MEMORY_ADDR,	0,	"memory address" },
  { PERR_CONFIG_ADDR,	0,	"config address" },
  { 0, 0, NULL }
};

static int mace_pcibrprint(void *, const char *pnp);

int
mace_pcibrmatch(struct device *parent, void *match, void *aux)
{
	switch (sys_config.system_type) {
	case SGI_O2:
		return 1;
	default:
		return 0;
	}
}

void
mace_pcibrattach(struct device *parent, struct device *self, void *aux)
{
	struct mace_pcibr_softc *sc = (struct mace_pcibr_softc *)self;
	struct pcibus_attach_args pba;
	struct macebus_attach_args *maa = aux;
	pcireg_t pcireg;

	sc->sc_mem_bus_space = &mace_pcibbus_mem_tag;
	sc->sc_io_bus_space = &mace_pcibbus_io_tag;

	/* Map in PCI control registers */
	sc->sc_memt = maa->maa_memt;
	if (bus_space_map(sc->sc_memt, maa->maa_baseaddr, 4096, 0,
	    &sc->sc_memh)) {
		printf(": can't map PCI control registers\n");
		return;
	}
	pcireg = bus_space_read_4(sc->sc_memt, sc->sc_memh, MACE_PCI_REVISION);

	printf(": mace rev %d\n", pcireg);

	/* Register the PCI ERROR interrupt handler */
	macebus_intr_establish(maa->maa_intr, maa->maa_mace_intr,
	    IST_LEVEL, IPL_HIGH, mace_pcibr_errintr, sc, sc->sc_dev.dv_xname);

	bcopy(&mace_pci_chipset, &sc->sc_pc, sizeof(mace_pci_chipset));
	sc->sc_pc.pc_conf_v = sc;
	sc->sc_pc.pc_intr_v = NULL;

	/*
	 * The O2 firmware sucks.  It makes a mess of I/O BARs and
	 * an even bigger mess for PCI-PCI bridges.
	 */
	mace_pcibr_configure(sc);

	/*
	 *  Configure our PCI devices.
	 */
	bzero(&pba, sizeof(pba));
	pba.pba_busname = "pci";
	pba.pba_iot = sc->sc_io_bus_space;
	pba.pba_memt = sc->sc_mem_bus_space;
	pba.pba_dmat = &mace_pci_bus_dma_tag;
	pba.pba_pc = &sc->sc_pc;
	pba.pba_ioex = extent_create("mace_io", 0, 0xffffffff, M_DEVBUF,
	    NULL, 0, EX_NOWAIT | EX_FILLED);
	if (pba.pba_ioex != NULL) {
		/*
		 * I/O accesses at address zero cause PCI errors, so
		 * make sure the first few bytes are not available.
		 */
		extent_free(pba.pba_ioex, 0x20, (1UL << 32) - 0x20, EX_NOWAIT);
	}
	pba.pba_memex = extent_create("mace_mem", 0, 0xffffffff, M_DEVBUF,
	    NULL, 0, EX_NOWAIT | EX_FILLED);
	if (pba.pba_memex != NULL)
		extent_free(pba.pba_memex, MACE_PCI_MEM_OFFSET,
		    MACE_PCI_MEM_SIZE, EX_NOWAIT);
	pba.pba_domain = pci_ndomains++;
	pba.pba_bus = 0;
	config_found(self, &pba, mace_pcibrprint);

	/* Clear PCI errors and set up error interrupt */
	bus_space_write_4(sc->sc_memt, sc->sc_memh, MACE_PCI_ERROR_FLAGS, 0);
	pcireg = bus_space_read_4(sc->sc_memt, sc->sc_memh, MACE_PCI_CONTROL);
	pcireg |= MACE_PCI_INTCTRL;
	bus_space_write_4(sc->sc_memt, sc->sc_memh, MACE_PCI_CONTROL, pcireg);
}

static int
mace_pcibrprint(void *aux, const char *pnp)
{
	struct pcibus_attach_args *pba = aux;

	if (pnp)
		printf("%s at %s", pba->pba_busname, pnp);
	printf(" bus %d", pba->pba_bus);
	return (UNCONF);
}

void
mace_pcibr_attach_hook(struct device *parent, struct device *self,
    struct pcibus_attach_args *pba)
{
}

int
mace_pcibr_errintr(void *v)
{
	struct mace_pcibr_softc *sc = v;
	bus_space_tag_t memt = sc->sc_memt;
	bus_space_handle_t memh = sc->sc_memh;
	const struct _perr_map *emap = perr_map;
	pcireg_t stat, erraddr;

	/* Check and clear any PCI error, report found */
	stat = bus_space_read_4(memt, memh, MACE_PCI_ERROR_FLAGS);
	erraddr = bus_space_read_4(memt, memh, MACE_PCI_ERROR_ADDRESS);
	while (emap->mask) {
		if (stat & emap->mask) {
			printf("mace: pci err %s", emap->text);
			if (emap->flag && stat & emap->flag)
				printf(" at address 0x%08x", erraddr);
			printf("\n");
		}
		emap++;
	}
	bus_space_write_4(memt, memh, MACE_PCI_ERROR_FLAGS, 0);
	return 1;
}

/*
 *  PCI access drivers
 */

pcitag_t
mace_pcibr_make_tag(void *cpv, int bus, int dev, int fnc)
{
	return (bus << 16) | (dev << 11) | (fnc << 8);
}

void
mace_pcibr_decompose_tag(void *cpv, pcitag_t tag, int *busp, int *devp,
    int *fncp)
{
	if (busp != NULL)
		*busp = (tag >> 16) & 0xff;
	if (devp != NULL)
		*devp = (tag >> 11) & 0x1f;
	if (fncp != NULL)
		*fncp = (tag >> 8) & 0x7;
}

int
mace_pcibr_bus_maxdevs(void *cpv, int busno)
{
	return busno == 0 ? 6 : 32;
}

int
mace_pcibr_conf_size(void *cpv, pcitag_t tag)
{
	return PCI_CONFIG_SPACE_SIZE;
}

pcireg_t
mace_pcibr_conf_read(void *cpv, pcitag_t tag, int offset)
{
	struct mace_pcibr_softc *sc = cpv;
	bus_space_tag_t memt = sc->sc_memt;
	bus_space_handle_t memh = sc->sc_memh;
	pcireg_t data, stat;
	int bus, dev;
	int s;

	s = splhigh();

	bus_space_write_4(memt, memh, MACE_PCI_ERROR_FLAGS, 0);
	data = tag | offset;
	bus_space_write_4(memt, memh, MACE_PCI_CFGADDR, data);
	data = bus_space_read_4(memt, memh, MACE_PCI_CFGDATA);
	bus_space_write_4(memt, memh, MACE_PCI_CFGADDR, 0);

	/*
	 * Onboard ahc on O2 can do Ultra speed despite not
	 * having SEEPROM nor external precision resistors.
	 */
	mace_pcibr_decompose_tag(cpv, tag, &bus, &dev, NULL);
	if (bus == 0 && (dev == 1 || dev == 2) && offset == 0x40)
		data |= 0x1000;	/* REXTVALID */

	/* Check and clear any PCI error, returns -1 if error is found */
	stat = bus_space_read_4(memt, memh, MACE_PCI_ERROR_FLAGS);
	bus_space_write_4(memt, memh, MACE_PCI_ERROR_FLAGS, 0);
	if (stat & (PERR_MASTER_ABORT | PERR_TARGET_ABORT |
		    PERR_DATA_PARITY_ERR | PERR_RETRY_ERR)) {
		data = -1;
	}

	splx(s);
	return(data);
}

void
mace_pcibr_conf_write(void *cpv, pcitag_t tag, int offset, pcireg_t data)
{
	struct mace_pcibr_softc *sc = cpv;
	pcireg_t addr;
	int s;

	s = splhigh();

	addr = tag | offset;
	bus_space_write_4(sc->sc_memt, sc->sc_memh, MACE_PCI_CFGADDR, addr);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, MACE_PCI_CFGDATA, data);
	bus_space_write_4(sc->sc_memt, sc->sc_memh, MACE_PCI_CFGADDR, 0);

	splx(s);
}

int
mace_pcibr_probe_device_hook(void *unused, struct pci_attach_args *notused)
{
	return 0;
}

int
mace_pcibr_get_widget(void *unused)
{
	return 0;
}

int
mace_pcibr_get_dl(void *cpv, pcitag_t tag, struct sgi_device_location *sdl)
{
	int bus, device, fn;

	memset(sdl, 0, sizeof *sdl);
	mace_pcibr_decompose_tag(cpv, tag, &bus, &device, &fn);
	if (bus != 0)
		return 0;
	sdl->device = device;
	sdl->fn = fn;
	return 1;
}

int
mace_pcibr_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
	int bus, dev, pin = pa->pa_rawintrpin;
	static const signed char intrmap[][PCI_INTERRUPT_PIN_MAX] = {
		{ -1, -1, -1, -1 },
		{ 8, -1, -1, -1 },	/* ahc0 */
		{ 9, -1, -1, -1 },	/* ahc1 */
		{ 10, 13, 14, 15 },	/* slot */
		{ 11, 15, 13, 14 },	/* no slots... */
		{ 12, 14, 15, 13 }	/* ... unless you solder them */
	};

	*ihp = -1;

	if (pin == 0) {
		/* No IRQ used. */
		return 1;
	}
#ifdef DIAGNOSTIC
	if (pin > PCI_INTERRUPT_PIN_MAX) {
		printf("mace_pcibr_intr_map: bad interrupt pin %d\n", pin);
		return 1;
	}
#endif

	pci_decompose_tag(pa->pa_pc, pa->pa_tag, &bus, &dev, NULL);

	if (pa->pa_bridgetag) {
		pin = PPB_INTERRUPT_SWIZZLE(pin, dev);
		*ihp = pa->pa_bridgeih[pin - PCI_INTERRUPT_PIN_A];

		return ((*ihp == -1) ? 1 : 0);
	}

	if (dev < nitems(intrmap))
		*ihp = intrmap[dev][pin - PCI_INTERRUPT_PIN_A];

	return ((*ihp == -1) ? 1 : 0);
}

const char *
mace_pcibr_intr_string(void *lcv, pci_intr_handle_t ih)
{
	static char str[16];

	snprintf(str, sizeof(str), "irq %ld", ih);
	return(str);
}

void *
mace_pcibr_intr_establish(void *lcv, pci_intr_handle_t ih, int level,
    int (*func)(void *), void *arg, const char *name)
{
	return macebus_intr_establish(ih, 0, IST_LEVEL, level, func, arg, name);
}

void
mace_pcibr_intr_disestablish(void *lcv, void *ih)
{
	macebus_intr_disestablish(ih);
}

int
mace_pcibr_intr_line(void *lcv, pci_intr_handle_t ih)
{
	return ih;
}

/*
 *  Bus access primitives
 */

u_int8_t
mace_pcib_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	return *(volatile u_int8_t *)(h + (o ^ 3));
}

u_int16_t
mace_pcib_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	return *(volatile u_int16_t *)(h + (o ^ 2));
}

u_int32_t
mace_pcib_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	return *(volatile u_int32_t *)(h + o);
}

u_int64_t
mace_pcib_read_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	return *(volatile u_int64_t *)(h + o);
}

void
mace_pcib_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
    u_int8_t v)
{
	*(volatile u_int8_t *)(h + (o ^ 3)) = v;
}

void
mace_pcib_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
    u_int16_t v)
{
	*(volatile u_int16_t *)(h + (o ^ 2)) = v;
}

void
mace_pcib_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
    u_int32_t v)
{
	*(volatile u_int32_t *)(h + o) = v;
}

void
mace_pcib_write_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
    u_int64_t v)
{
	*(volatile u_int64_t *)(h + o) = v;
}

void
mace_pcib_read_raw_2(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    u_int8_t *buf, bus_size_t len)
{
	volatile u_int16_t *addr = (volatile u_int16_t *)(h + (o ^ 2));
	len >>= 1;
	while (len-- != 0) {
		*(u_int16_t *)buf = letoh16(*addr);
		buf += 2;
	}
}

void
mace_pcib_write_raw_2(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    const u_int8_t *buf, bus_size_t len)
{
	volatile u_int16_t *addr = (volatile u_int16_t *)(h + (o ^ 2));
	len >>= 1;
	while (len-- != 0) {
		*addr = htole16(*(u_int16_t *)buf);
		buf += 2;
	}
}

void
mace_pcib_read_raw_4(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    u_int8_t *buf, bus_size_t len)
{
	volatile u_int32_t *addr = (volatile u_int32_t *)(h + o);
	len >>= 2;
	while (len-- != 0) {
		*(u_int32_t *)buf = letoh32(*addr);
		buf += 4;
	}
}

void
mace_pcib_write_raw_4(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    const u_int8_t *buf, bus_size_t len)
{
	volatile u_int32_t *addr = (volatile u_int32_t *)(h + o);
	len >>= 2;
	while (len-- != 0) {
		*addr = htole32(*(u_int32_t *)buf);
		buf += 4;
	}
}

void
mace_pcib_read_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    u_int8_t *buf, bus_size_t len)
{
	volatile u_int64_t *addr = (volatile u_int64_t *)(h + o);
	len >>= 3;
	while (len-- != 0) {
		*(u_int64_t *)buf = letoh64(*addr);
		buf += 8;
	}
}

void
mace_pcib_write_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
    const u_int8_t *buf, bus_size_t len)
{
	volatile u_int64_t *addr = (volatile u_int64_t *)(h + o);
	len >>= 3;
	while (len-- != 0) {
		*addr = htole64(*(u_int64_t *)buf);
		buf += 8;
	}
}

int
mace_pcib_space_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size,
    int flags, bus_space_handle_t *bshp)
{
#ifdef DIAGNOSTIC
	if (t->bus_base == mace_pcibbus_mem_tag.bus_base) {
		if (offs < MACE_PCI_MEM_OFFSET ||
		    ((offs + size - 1) >> 32) != 0)
			return EINVAL;
	} else {
		if (((offs + size - 1) >> 32) != 0)
			return EINVAL;
	}
#endif

	if (ISSET(flags, BUS_SPACE_MAP_CACHEABLE))
		offs +=
		    PHYS_TO_XKPHYS(0, CCA_CACHED) - PHYS_TO_XKPHYS(0, CCA_NC);
	*bshp = t->bus_base + offs;
	return 0;
}

void
mace_pcib_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh,
    bus_size_t size)
{
}

int
mace_pcib_space_region(bus_space_tag_t t, bus_space_handle_t bsh,
    bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
{
	*nbshp = bsh + offset;
	return (0);
}

void *
mace_pcib_space_vaddr(bus_space_tag_t t, bus_space_handle_t h)
{
	return (void *)h;
}

void
mace_pcib_space_barrier(bus_space_tag_t t, bus_space_handle_t h,
    bus_size_t offs, bus_size_t len, int flags)
{
	mips_sync();
}

/*
 * Mace PCI bus_dma helpers.
 * The PCI bus accesses memory contiguously at 0x00000000 onwards.
 */

bus_addr_t
mace_pcibr_pa_to_device(paddr_t pa)
{
	return (pa & CRIME_MEMORY_MASK);
}

paddr_t
mace_pcibr_device_to_pa(bus_addr_t addr)
{
	paddr_t pa = (paddr_t)addr & CRIME_MEMORY_MASK;

	if (pa >= 256 * 1024 * 1024)
		pa |= CRIME_MEMORY_OFFSET;

	return (pa);
}

/*
 * PCI configuration.
 */

void
mace_pcibr_configure(struct mace_pcibr_softc *sc)
{
	pci_chipset_tag_t pc = &sc->sc_pc;
	pcitag_t tag;
	pcireg_t id, bhlcr;
	int dev, nfuncs;
	uint nppb, npccbb;
	const struct pci_quirkdata *qd;

	nppb = npccbb = 0;
	for (dev = 0; dev < pci_bus_maxdevs(pc, 0); dev++) {
		tag = pci_make_tag(pc, 0, dev, 0);

		id = pci_conf_read(pc, tag, PCI_ID_REG);
		if (PCI_VENDOR(id) == PCI_VENDOR_INVALID ||
		    PCI_VENDOR(id) == 0)
			continue;

		bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
		if (PCI_HDRTYPE_TYPE(bhlcr) == 1)
			nppb++;
		if (PCI_HDRTYPE_TYPE(bhlcr) == 2)
			npccbb++;

		qd = pci_lookup_quirkdata(PCI_VENDOR(id), PCI_PRODUCT(id));
		if (PCI_HDRTYPE_MULTIFN(bhlcr) ||
		    (qd != NULL && (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
			nfuncs = 8;
		else
			nfuncs = 1;

		mace_pcibr_device_fixup(sc, dev, nfuncs);
	}

	/*
	 * Since there is only one working slot, there should be only
	 * up to one bridge (PCI-PCI or PCI-CardBus), which we'll map
	 * after the on-board device resources.
	 */
	if (nppb + npccbb != 1)
		return;

	for (dev = 0; dev < pci_bus_maxdevs(pc, 0); dev++) {
		tag = pci_make_tag(pc, 0, dev, 0);

		id = pci_conf_read(pc, tag, PCI_ID_REG);
		if (PCI_VENDOR(id) == PCI_VENDOR_INVALID ||
		    PCI_VENDOR(id) == 0)
			continue;

		bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
		switch (PCI_HDRTYPE_TYPE(bhlcr)) {
		case 1:
			ppb_initialize(pc, tag, 0, 1, 255);
			break;
		case 2:
			pccbb_initialize(pc, tag, 0, 1, 1);
			break;
		}
	}
}

void
mace_pcibr_device_fixup(struct mace_pcibr_softc *sc, int dev, int nfuncs)
{
	pci_chipset_tag_t pc = &sc->sc_pc;
	pcitag_t tag;
	pcireg_t csr, bhlcr, type;
	int function;
	int reg, reg_start, reg_end;

	for (function = 0; function < nfuncs; function++) {
		tag = pci_make_tag(pc, 0, dev, function);

		bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
		switch (PCI_HDRTYPE_TYPE(bhlcr)) {
		case 0:
			reg_start = PCI_MAPREG_START;
			reg_end = PCI_MAPREG_END;
			break;
		case 1:	/* PCI-PCI bridge */
			reg_start = PCI_MAPREG_START;
			reg_end = PCI_MAPREG_PPB_END;
			break;
		case 2:	/* PCI-CardBus bridge */
			reg_start = PCI_MAPREG_START;
			reg_end = PCI_MAPREG_PCB_END;
			break;
		default:
			continue;
		}

		/*
		 * The firmware will only initialize memory BARs, and only
		 * the lower half of them if they are 64 bit.
		 * So here we disable I/O space and reset the I/O BARs to 0,
		 * and make sure the upper part of 64 bit memory BARs is
		 * correct.
		 * Device drivers will allocate resources themselves and
		 * enable I/O space on an as-needed basis.
		 */
		csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG,
	            csr & ~PCI_COMMAND_IO_ENABLE);

		for (reg = reg_start; reg < reg_end; reg += 4) {
			if (pci_mapreg_probe(pc, tag, reg, &type) == 0)
				continue;

			switch (type) {
			case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
				reg += 4;
				/* FALLTHROUGH */
			case PCI_MAPREG_TYPE_IO:
				pci_conf_write(pc, tag, reg, 0);
				break;
			}
		}
	}
}

int
mace_pcibr_ppb_setup(void *cookie, pcitag_t tag, bus_addr_t *iostart,
    bus_addr_t *ioend, bus_addr_t *memstart, bus_addr_t *memend)
{
	if (*memend != 0) {
		/*
		 * Give all resources to the bridge
		 * (except for the few the on-board ahc(4) will use).
		 */
		*memstart = 0x81000000;
		*memend =   0xffffffff;
	} else {
		*memstart = 0xffffffff;
		*memend = 0;
	}

	if (*ioend != 0) {
		/*
		 * Give all resources to the bridge
		 * (except for the few the on-board ahc(4) will use).
		 */
		*iostart = 0x00010000;
		*ioend =   0xffffffff;
	} else {
		*iostart = 0xffffffff;
		*ioend = 0;
	}

	return 0;
}

#if NCARDBUS > 0

static struct rb_md_fnptr mace_pcibr_rb_md_fn = {
	mace_pcibr_rbus_space_map,
	mace_pcibr_rbus_space_unmap
};

int
mace_pcibr_rbus_space_map(bus_space_tag_t t, bus_addr_t addr, bus_size_t size,
    int flags, bus_space_handle_t *bshp)
{
	return bus_space_map(t, addr, size, flags, bshp);
}

void
mace_pcibr_rbus_space_unmap(bus_space_tag_t t, bus_space_handle_t h,
    bus_size_t size, bus_addr_t *addrp)
{
	bus_space_unmap(t, h, size);
	/* can't simply subtract because of possible cacheability */
	*addrp = XKPHYS_TO_PHYS(h) - XKPHYS_TO_PHYS(t->bus_base);
}

void *
mace_pcibr_rbus_parent_io(struct pci_attach_args *pa)
{
	rbus_tag_t rb;

	rb = rbus_new_root_share(pa->pa_iot, pa->pa_ioex,
	    0x0000, 0xffff);
	if (rb != NULL)
		rb->rb_md = &mace_pcibr_rb_md_fn;

	return rb;
}

void *
mace_pcibr_rbus_parent_mem(struct pci_attach_args *pa)
{
	rbus_tag_t rb;

	rb = rbus_new_root_share(pa->pa_memt, pa->pa_memex,
	    0, 0xffffffff);
	if (rb != NULL)
		rb->rb_md = &mace_pcibr_rb_md_fn;

	return rb;
}

#endif	/* NCARDBUS > 0 */


code:
/*	$OpenBSD: macepcibrvar.h,v 1.6 2009/07/22 20:28:21 miod Exp $ */

/*
 * Copyright (c) 2003-2004 Opsycon AB (www.opsycon.se)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#ifndef _PCIBRVAR_H_
#define _PCIBRVAR_H_

/*
 * Addresses of the PCI I/O and memory spaces.
 *
 * Note that PCI memory space addresses need to have bit 31 set to
 * reach hardware, otherwise they reach physical memory.
 */
#define	MACE_PCI_IO_BASE	0x100000000UL
#define	MACE_PCI_MEM_BASE	0x200000000UL
#define	MACE_PCI_MEM_OFFSET	0x80000000
#define	MACE_PCI_MEM_SIZE	0x80000000

struct mace_pcibr_softc {
	struct device	sc_dev;
	struct mips_bus_space *sc_mem_bus_space;
	struct mips_bus_space *sc_io_bus_space;
	struct mips_pci_chipset sc_pc;
	bus_space_tag_t sc_memt;
	bus_space_handle_t sc_memh;
};

u_int8_t mace_pcib_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
u_int16_t mace_pcib_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
u_int32_t mace_pcib_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
u_int64_t mace_pcib_read_8(bus_space_tag_t, bus_space_handle_t, bus_size_t);

void mace_pcib_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	u_int8_t);
void mace_pcib_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	u_int16_t);
void mace_pcib_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	u_int32_t);
void mace_pcib_write_8(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	u_int64_t);

void mace_pcib_read_raw_2(bus_space_tag_t, bus_space_handle_t,
	    bus_addr_t, u_int8_t *, bus_size_t);
void mace_pcib_write_raw_2(bus_space_tag_t, bus_space_handle_t,
	    bus_addr_t, const u_int8_t *, bus_size_t);
void mace_pcib_read_raw_4(bus_space_tag_t, bus_space_handle_t,
	    bus_addr_t, u_int8_t *, bus_size_t);
void mace_pcib_write_raw_4(bus_space_tag_t, bus_space_handle_t,
	    bus_addr_t, const u_int8_t *, bus_size_t);
void mace_pcib_read_raw_8(bus_space_tag_t, bus_space_handle_t,
	    bus_addr_t, u_int8_t *, bus_size_t);
void mace_pcib_write_raw_8(bus_space_tag_t, bus_space_handle_t,
	    bus_addr_t, const u_int8_t *, bus_size_t);

int mace_pcib_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
	bus_space_handle_t *);
void mace_pcib_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
int mace_pcib_space_region(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	bus_size_t, bus_space_handle_t *);

#endif

license: unknown


code:
/*	$OpenBSD: pci_machdep.c,v 1.5 2009/07/23 19:24:30 miod Exp $	*/

/*
 * Copyright (c) 2009 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/extent.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pccbbreg.h>
#include <dev/pci/ppbreg.h>
#include <dev/pci/pcidevs.h>

void	ppb_device_explore(pci_chipset_tag_t, uint, int, int, struct extent *,
	    struct extent *);
void	ppb_function_explore(pci_chipset_tag_t, pcitag_t, struct extent *,
	    struct extent *);

/*
 * Configure a PCI-PCI bridge.
 */
void
ppb_initialize(pci_chipset_tag_t pc, pcitag_t ppbtag, uint primary,
    uint secondary, uint subordinate)
{
	int dev, nfuncs;
	pcireg_t id, csr, bhlcr;
	pcitag_t tag;
	const struct pci_quirkdata *qd;
	bus_addr_t iostart, ioend, memstart, memend;
	struct extent *ioex, *memex;
	struct extent_region *region;

	/*
	 * In a first pass, enable access to the configuration space,
	 * and figure out what resources the devices behind it will
	 * need.
	 *
	 * Note that, doing this, we do not intend to support any
	 * hotplug capabilities.  This should not be a problem on
	 * sgi.
	 */

	csr = pci_conf_read(pc, ppbtag, PCI_COMMAND_STATUS_REG);
	csr &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
	pci_conf_write(pc, ppbtag, PCI_COMMAND_STATUS_REG, csr);

	pci_conf_write(pc, ppbtag, PPB_REG_BUSINFO,
	    primary | (secondary << 8) | (subordinate << 16));

	ioex = extent_create("ppb_io", 0, 0xffffffff,
	    M_DEVBUF, NULL, 0, EX_NOWAIT);
	memex = extent_create("ppb_mem", 0, 0xffffffff,
	    M_DEVBUF, NULL, 0, EX_NOWAIT);

	for (dev = 0; dev < pci_bus_maxdevs(pc, secondary); dev++) {
		tag = pci_make_tag(pc, secondary, dev, 0);
		id = pci_conf_read(pc, tag, PCI_ID_REG);

		if (PCI_VENDOR(id) == PCI_VENDOR_INVALID ||
		    PCI_VENDOR(id) == 0)
			continue;

		bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
		qd = pci_lookup_quirkdata(PCI_VENDOR(id), PCI_PRODUCT(id));
		if (PCI_HDRTYPE_MULTIFN(bhlcr) ||
		    (qd != NULL && (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
			nfuncs = 8;
		else
			nfuncs = 1;

		ppb_device_explore(pc, secondary, dev, nfuncs, ioex, memex);
	}

	/*
	 * Now figure out the size of the resources we need...
	 */

	iostart = memstart = 0xffffffff;
	ioend = memend = 0;

	if (ioex != NULL) {
		LIST_FOREACH(region, &ioex->ex_regions, er_link) {
			if (region->er_start < iostart)
				iostart = region->er_start;
			if (region->er_end > ioend)
				ioend = region->er_end;
		}
		extent_destroy(ioex);
	}
	if (memex != NULL) {
		LIST_FOREACH(region, &memex->ex_regions, er_link) {
			if (region->er_start < memstart)
				memstart = region->er_start;
			if (region->er_end > memend)
				memend = region->er_end;
		}
		extent_destroy(memex);
	}

	/*
	 * ... and ask the bridge to setup resources for them.
	 */

	if (pc->pc_ppb_setup == NULL || (*pc->pc_ppb_setup)(pc->pc_conf_v,
	    ppbtag, &iostart, &ioend, &memstart, &memend) != 0) {
		iostart = memstart = 0xffffffff;
		ioend = memend = 0;
	}

	pci_conf_write(pc, ppbtag, PPB_REG_MEM,
	    ((memstart & 0xfff00000) >> 16) | (memend & 0xfff00000));
	pci_conf_write(pc, ppbtag, PPB_REG_IOSTATUS,
	    (pci_conf_read(pc, ppbtag, PPB_REG_IOSTATUS) & 0xffff0000) |
	    ((iostart & 0x0000f000) >> 8) | (ioend & 0x0000f000));
	pci_conf_write(pc, ppbtag, PPB_REG_IO_HI,
	    ((iostart & 0xffff0000) >> 16) | (ioend & 0xffff0000));
	pci_conf_write(pc, ppbtag, PPB_REG_PREFMEM, 0x0000fff0);
	pci_conf_write(pc, ppbtag, PPB_REG_PREFBASE_HI32, 0);
	pci_conf_write(pc, ppbtag, PPB_REG_PREFLIM_HI32, 0);

	if (iostart <= ioend)
		csr |= PCI_COMMAND_IO_ENABLE;
	if (memstart <= memend)
		csr |= PCI_COMMAND_MEM_ENABLE;

	pci_conf_write(pc, ppbtag, PCI_COMMAND_STATUS_REG, csr |
	    PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_INVALIDATE_ENABLE |
	    PCI_COMMAND_SERR_ENABLE);
}

/*
 * Figure out what resources a device behind a bridge would need and
 * disable them.
 */
void
ppb_device_explore(pci_chipset_tag_t pc, uint bus, int dev, int nfuncs,
    struct extent *ioex, struct extent *memex)
{
	pcitag_t tag;
	pcireg_t id;
	int function;

	for (function = 0; function < nfuncs; function++) {
		tag = pci_make_tag(pc, bus, dev, function);

		id = pci_conf_read(pc, tag, PCI_ID_REG);
		if (PCI_VENDOR(id) == PCI_VENDOR_INVALID ||
		    PCI_VENDOR(id) == 0)
			continue;

		ppb_function_explore(pc, tag, ioex, memex);
	}
}

/*
 * Figure out what resources a device function would need and
 * disable them.
 */
void
ppb_function_explore(pci_chipset_tag_t pc, pcitag_t tag, struct extent *ioex,
    struct extent *memex)
{
	bus_addr_t base;
	bus_size_t size;
	int reg, reg_start, reg_end, reg_rom;
	pcireg_t csr, bhlcr, type, mask;

	csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
	pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr &
	    ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE));

	bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
	switch (PCI_HDRTYPE_TYPE(bhlcr)) {
	case 0:
		reg_start = PCI_MAPREG_START;
		reg_end = PCI_MAPREG_END;
		reg_rom = PCI_ROM_REG;
		break;
	case 1:	/* PCI-PCI bridge */
		reg_start = PCI_MAPREG_START;
		reg_end = PCI_MAPREG_PPB_END;
		reg_rom = 0;	/* 0x38 */
		break;
	case 2:	/* PCI-Cardbus bridge */
		reg_start = PCI_MAPREG_START;
		reg_end = PCI_MAPREG_PCB_END;
		reg_rom = 0;
		break;
	default:
		return;
	}

	for (reg = reg_start; reg < reg_end; reg += 4) {
		if (pci_mapreg_probe(pc, tag, reg, &type) == 0)
			continue;

		if (pci_mapreg_info(pc, tag, reg, type, &base, &size, NULL))
			continue;

		switch (type) {
		case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
			pci_conf_write(pc, tag, reg + 4, 0);
			/* FALLTHROUGH */
		case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
			if (memex != NULL)
				(void)extent_alloc(memex, size, size, 0, 0, 0,
				    &base);
			break;
		case PCI_MAPREG_TYPE_IO:
			if (ioex != NULL)
				(void)extent_alloc(ioex, size, size, 0, 0, 0,
				    &base);
			break;
		}

		pci_conf_write(pc, tag, reg, 0);

		if (type == (PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT))
			reg += 4;
	}

	if (reg_rom != 0) {
		pci_conf_write(pc, tag, reg_rom, ~PCI_ROM_ENABLE);
		mask = pci_conf_read(pc, tag, reg_rom);
		size = PCI_ROM_SIZE(mask);

		if (size != 0) {
			if (memex != NULL)
				(void)extent_alloc(memex, size, size, 0, 0, 0,
				    &base);
		}

		pci_conf_write(pc, tag, reg_rom, 0);
	}

	/*
	 * Note that we do not try to be recursive and configure PCI-PCI
	 * bridges behind PCI-PCI bridges.
	 */
}

/*
 * Configure a PCI-CardBus bridge.
 */
void
pccbb_initialize(pci_chipset_tag_t pc, pcitag_t tag, uint primary,
    uint secondary, uint subordinate)
{
	pcireg_t csr;

	csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
	csr &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
	pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);

	pci_conf_write(pc, tag, PCI_BUSNUM,
	    primary | (secondary << 8) | (subordinate << 16));

#if 0	/* done by pccbb(4) */
	csr |= PCI_COMMAND_IO_ENABLE;
	csr |= PCI_COMMAND_MEM_ENABLE;

	pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr |
	    PCI_COMMAND_MASTER_ENABLE);
#endif
}


code:
/*	$OpenBSD: pci_machdep.h,v 1.16 2013/11/05 10:12:35 mpi Exp $ */

/*
 * Copyright (c) 2003-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
typedef struct mips_pci_chipset *pci_chipset_tag_t;
typedef u_long pcitag_t;
typedef u_long pci_intr_handle_t;

struct pci_attach_args;
struct sgi_device_location;

/*
 * mips-specific PCI structure and type definitions.
 * NOT TO BE USED DIRECTLY BY MACHINE INDEPENDENT CODE.
 */
struct mips_pci_chipset {
    void	*pc_conf_v;
    void	(*pc_attach_hook)(struct device *,
		    struct device *, struct pcibus_attach_args *);
    int		(*pc_bus_maxdevs)(void *, int);
    pcitag_t	(*pc_make_tag)(void *, int, int, int);
    void	(*pc_decompose_tag)(void *, pcitag_t, int *,
		    int *, int *);
    int		(*pc_conf_size)(void *, pcitag_t);
    pcireg_t	(*pc_conf_read)(void *, pcitag_t, int);
    void	(*pc_conf_write)(void *, pcitag_t, int, pcireg_t);
    int		(*pc_probe_device_hook)(void *, struct pci_attach_args *);

    int		(*pc_get_widget)(void *);
    int		(*pc_get_dl)(void *, pcitag_t, struct sgi_device_location *);

    void	*pc_intr_v;
    int		(*pc_intr_map)(struct pci_attach_args *, pci_intr_handle_t *);
    const char	*(*pc_intr_string)(void *, pci_intr_handle_t);
    void	*(*pc_intr_establish)(void *, pci_intr_handle_t,
		    int, int (*)(void *), void *, const char *);
    void	(*pc_intr_disestablish)(void *, void *);
    int		(*pc_intr_line)(void *, pci_intr_handle_t);

    int		(*pc_ppb_setup)(void *, pcitag_t, bus_addr_t *, bus_addr_t *,
		    bus_addr_t *, bus_addr_t *);

    void	*(*pc_rbus_parent_io)(struct pci_attach_args *);
    void	*(*pc_rbus_parent_mem)(struct pci_attach_args *);
};

/*
 * Functions provided to machine-independent PCI code.
 */
#define	pci_attach_hook(p, s, pba)					\
    (*(pba)->pba_pc->pc_attach_hook)((p), (s), (pba))
#define	pci_bus_maxdevs(c, b)						\
    (*(c)->pc_bus_maxdevs)((c)->pc_conf_v, (b))
#define	pci_make_tag(c, b, d, f)					\
    (*(c)->pc_make_tag)((c)->pc_conf_v, (b), (d), (f))
#define	pci_decompose_tag(c, t, bp, dp, fp)				\
    (*(c)->pc_decompose_tag)((c)->pc_conf_v, (t), (bp), (dp), (fp))
#define	pci_conf_size(c, t)						\
    (*(c)->pc_conf_size)((c)->pc_conf_v, (t))
#define	pci_conf_read(c, t, r)						\
    (*(c)->pc_conf_read)((c)->pc_conf_v, (t), (r))
#define	pci_conf_write(c, t, r, v)					\
    (*(c)->pc_conf_write)((c)->pc_conf_v, (t), (r), (v))
#define	pci_intr_map(c, ihp)						\
    (*(c)->pa_pc->pc_intr_map)((c), (ihp))
#define	pci_intr_map_msi(c, ihp)	(-1)
#define	pci_intr_string(c, ih)						\
    (*(c)->pc_intr_string)((c)->pc_intr_v, (ih))
#define	pci_intr_establish(c, ih, l, h, a, nm)				\
    (*(c)->pc_intr_establish)((c)->pc_intr_v, (ih), (l), (h), (a), (nm))
#define	pci_intr_disestablish(c, iv)					\
    (*(c)->pc_intr_disestablish)((c)->pc_intr_v, (iv))
#define pci_intr_line(c, ih)						\
    (*(c)->pc_intr_line)((c)->pc_intr_v, (ih))
#define	pci_probe_device_hook(c, a)					\
    (*(c)->pc_probe_device_hook)((c)->pc_conf_v, (a))
#define	pci_dev_postattach(a, b)	do { } while (0)

#define	pci_min_powerstate(c, t)	(PCI_PMCSR_STATE_D3)
#define	pci_set_powerstate_md(c, t, s, p)

/*
 * Functions provided to machine-dependent PCI code.
 */
#define	pci_get_widget(c)						\
    (*(c)->pc_get_widget)((c)->pc_conf_v)
#define pci_get_device_location(c,t,l)					\
    (*(c)->pc_get_dl)((c)->pc_conf_v, (t), (l))

/*
 * Functions provided to machine-independent rbus code.
 */
#define	rbus_pccbb_parent_io(dev, pa)					\
    (rbus_tag_t)((*(pa)->pa_pc->pc_rbus_parent_io)(pa))
#define	rbus_pccbb_parent_mem(dev, pa)					\
    (rbus_tag_t)((*(pa)->pa_pc->pc_rbus_parent_mem)(pa))

void	pccbb_initialize(pci_chipset_tag_t, pcitag_t, uint, uint, uint);
void	ppb_initialize(pci_chipset_tag_t, pcitag_t, uint, uint, uint);


code: src/sys/arch/sgi/sgi

path: sys/arch/sgi/sgi/CVS/Repository


code:
/*	$OpenBSD: autoconf.c,v 1.40 2015/01/02 22:38:46 sebastia Exp $	*/
/*
 * Copyright (c) 2009, 2010 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * Copyright (c) 2003-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
/*
 * Copyright (c) 1996 Per Fogelstrom
 * Copyright (c) 1995 Theo de Raadt
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department and Ralph Campbell.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Setup the system to run on the current machine.
 *
 * cpu_configure() is called at boot time.  Available
 * devices are determined (from possibilities mentioned in ioconf.c),
 * and the drivers are initialized.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/reboot.h>
#include <sys/device.h>

#include <machine/autoconf.h>
#include <machine/memconf.h>

#include <mips64/arcbios.h>
#include <mips64/archtype.h>

#include <uvm/uvm_extern.h>

#ifdef TGT_ORIGIN
#include <machine/mnode.h>
#endif
#ifdef TGT_OCTANE
#include <sgi/sgi/ip30.h>
#endif
#include <sgi/xbow/xbow.h>
#include <dev/pci/pcivar.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>

extern void dumpconf(void);

static u_long strtoul(const char *, int, const char **);

/*
 * The following several variables are related to
 * the configuration process, and are used in initializing
 * the machine.
 */
int	cold = 1;			/* if 1, still working on cold-start */
struct device *bootdv = NULL;
int16_t	currentnasid = 0;

char	osloadpartition[256];
char	osloadoptions[129];

/*
 *  Configure all devices found that we know about.
 *  This is done at boot time.
 */
void
cpu_configure(void)
{
	(void)splhigh();	/* Set mask to what we intend. */

	softintr_init();

	if (config_rootfound("mainbus", "mainbus") == 0) {
		panic("no mainbus found");
	}

	splinit();		/* Initialized, fire up interrupt system */
	cold = 0;
}

void
diskconf(void)
{
	if (bootdv == NULL)
		printf("boot device: '%s' unrecognized.\n", osloadpartition);
	else
		printf("boot device: %s\n", bootdv->dv_xname);

	setroot(bootdv, 0, RB_USERREQ);
	dumpconf();
}

/*
 * Register a memory region.
 */
int
memrange_register(uint64_t startpfn, uint64_t endpfn, uint64_t bmask)
{
	struct phys_mem_desc *cur, *m = NULL;
	int i;

#ifdef DEBUG
{
	extern int console_ok;

	if (console_ok)
		printf("%s: memory from 0x%lx to 0x%lx\n",
		    __func__, ptoa(startpfn), ptoa(endpfn));
	else
		bios_printf("%s: memory from 0x%lx to 0x%lx\n",
		     __func__, ptoa(startpfn), ptoa(endpfn));
}
#endif
	physmem += endpfn - startpfn;

#ifdef TGT_OCTANE
	/*
	 * On Octane, the second 16KB page is reserved for the NMI handler.
	 */
	if (sys_config.system_type == SGI_OCTANE &&
	    startpfn < atop(IP30_MEMORY_BASE) + 2) {
		startpfn = atop(IP30_MEMORY_BASE) + 2;
		if (startpfn >= endpfn)
			return 0;
	}
#endif

	/*
	 * Prevent use of memory beyond what pmap can support.
	 * PG_FRAME is the highest supported page number.
	 */
	if (startpfn > atop(pfn_to_pad(PG_FRAME)))
		return 0;
	if (endpfn > atop(pfn_to_pad(PG_FRAME)))
		endpfn = 1 + atop(pfn_to_pad(PG_FRAME));

	for (i = 0, cur = mem_layout; i < MAXMEMSEGS; i++, cur++) {
		if (cur->mem_last_page == 0) {
			if (m == NULL)
				m = cur;	/* first free segment */
			continue;
		}
		/* merge contiguous areas */
		if (cur->mem_first_page == endpfn &&
		    ((cur->mem_last_page ^ startpfn) & bmask) == 0) {
			cur->mem_first_page = startpfn;
			return 0;
		}
		if (cur->mem_last_page == startpfn &&
		    ((cur->mem_first_page ^ endpfn) & bmask) == 0) {
			cur->mem_last_page = endpfn;
			return 0;
		}
	}

	if (m == NULL)
		return ENOMEM;

	m->mem_first_page = startpfn;
	m->mem_last_page = endpfn;
	return 0;
}

void	(*_device_register)(struct device *, void *);

void
device_register(struct device *dev, void *aux)
{
	if (_device_register)
		(*_device_register)(dev, aux);
}

#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2) || \
    defined(TGT_O2) || defined(TGT_OCTANE)

/*
 * ARCS boot path traversal
 */

const char *bootpath_get(int *);
void	bootpath_init(void);
void	bootpath_next(void);

static char bootpath_store[sizeof osloadpartition];
static char *bootpath_curpos;
static char *bootpath_lastpos;
static int bootpath_lastunit;

/*
 * Initialize bootpath analysis.
 */
void
bootpath_init()
{
	strlcpy(bootpath_store, osloadpartition, sizeof bootpath_store);
	bootpath_curpos = bootpath_store;
}

/*
 * Extract a component of the boot path, and return its name and unit
 * value.
 */
const char *
bootpath_get(int *u)
{
	char *c;
	int unit;

	/*
	 * If we don't have a value in cache, compute it.
	 */
	if (bootpath_lastpos == NULL) {
		if (bootpath_curpos == NULL)
			bootpath_init();

		unit = 0;
		c = strchr(bootpath_curpos, '(');
		if (c != NULL) {
			for (*c++ = '\0'; *c >= '0' && *c <= '9'; c++)
				unit = 10 * unit + (*c - '0');
			while (*c != ')' && *c != '\0')
				c++;
			if (*c == ')')
				c++;
		} else {
			c = bootpath_curpos + strlen(bootpath_curpos);
		}

		bootpath_lastpos = bootpath_curpos;
		bootpath_lastunit = unit;
		bootpath_curpos = c;
#ifdef DEBUG
		printf("%s: new component %s unit %d remainder %s\n", __func__,
		    bootpath_lastpos, bootpath_lastunit, bootpath_curpos);
#endif
	}

	*u = bootpath_lastunit;
	return bootpath_lastpos;
}

/*
 * Consume the current component of the bootpath, and switch to the next.
 */
void
bootpath_next()
{
	/* force bootpath_get to go forward */
	bootpath_lastpos = NULL;
#ifdef DEBUG
	printf("%s\n", __func__);
#endif
}

void
arcs_device_register(struct device *dev, void *aux)
{
	static struct device *lastparent = NULL;
#if defined(TGT_O2) || defined(TGT_OCTANE)
	static struct device *pciparent = NULL;
#endif
#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2)
	static struct device *wdscparent = NULL;
#endif
	static int component_pos = 0;

	struct device *parent = dev->dv_parent;
	struct cfdata *cf = dev->dv_cfdata;
	struct cfdriver *cd = cf->cf_driver;

	const char *component;
	int unit;

	if (parent == NULL)
		return;		/* one of the @root devices */

	if (bootdv != NULL)
		return;

	component = bootpath_get(&unit);
	if (*component == '\0')
		return;		/* exhausted path */

	/*
	 * The matching rules are as follows:
	 * xio() matches xbow.
	 * pci() matches any pci controller (macepcibr, xbridge), with the
	 *   unit number being ignored on O2 and the widget number of the
	 *   controller elsewhere.
	 * scsi() matches any pci scsi controller, with the unit number
	 *   being the pci device number (minus one on the O2, grr).
	 * disk() and cdrom() match sd and cd, respectively, with the
	 *   unit number being the target number.
	 *
	 * When a disk is found, we stop the parsing; rdisk() and
	 * partition() components are ignored.
	 */

#ifdef TGT_OCTANE
	if (strcmp(component, "xio") == 0) {
		struct mainbus_attach_args *maa = aux;

		if (strcmp(cd->cd_name, "xbow") == 0 && unit == maa->maa_nasid)
			goto found_advance;
	}
#endif

#if defined(TGT_O2) || defined(TGT_OCTANE)
	if (strcmp(component, "pci") == 0) {
		/*
		 * We'll work in two steps. The controller itself will be
		 * recognized with its parent device and attachment
		 * arguments (if necessary).
		 *
		 * Then we'll only advance the bootpath when matching the
		 * pci device.
		 */
		if (strcmp(cd->cd_name, "pci") == 0 &&
		    parent == lastparent) {
			pciparent = dev;
			goto found_advance;
		}

#ifdef TGT_O2
		if (strcmp(cd->cd_name, "macepcibr") == 0)
			goto found;
#endif
#ifdef TGT_OCTANE
		if (strcmp(cd->cd_name, "xbridge") == 0 &&
		    parent == lastparent) {
			struct xbow_attach_args *xaa = aux;

			if (unit == xaa->xaa_widget)
				goto found;
		}
		if (strcmp(cd->cd_name, "xbpci") == 0 &&
		    parent == lastparent) {
			goto found;
		}
#endif
	}
#endif	/* TGT_O2 || TGT_OCTANE */

	if (strcmp(component, "scsi") == 0) {
		/*
		 * We'll work in two steps. The controller itself will be
		 * recognized with its parent device and pci_attach_args
		 * need to match the scsi() unit number.
		 *
		 * Then we'll only advance the bootpath when matching the
		 * scsibus device.
		 */

		if (strcmp(cd->cd_name, "scsibus") == 0) {
			if (parent == lastparent)
				goto found_advance;

			if (component_pos == 0)
			switch (sys_config.system_type) {
#ifdef TGT_O2
			/*
			 * On O2, the pci(0) component may be omitted from
			 * the bootpath, in which case we fake the missing
			 * pci(0) component.
			 */
			case SGI_O2:
				if (parent->dv_parent != NULL &&
				    strcmp(parent->dv_parent->dv_cfdata->cf_driver->cd_name,
				      "pci") == 0) {
					pciparent = parent->dv_parent;
					goto found_advance;
				}
				break;
#endif
#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2)
			/*
			 * On Ind{igo,y,igo^2} systems, the bootpath
			 * starts at scsi().
			 */
			case SGI_IP20:
			case SGI_IP22:
			case SGI_IP26:
			case SGI_IP28:
				if (strcmp(parent->dv_cfdata->cf_driver->cd_name,
				    "wdsc") == 0 &&
				    parent->dv_parent != NULL &&
				    strcmp(parent->dv_parent->dv_cfdata->cf_driver->cd_name,
				    "hpc") == 0) {
					wdscparent = parent;
					goto found_advance;
				}
				break;
#endif
			default:
				break;
			}
		}

		if (parent == lastparent) {
#if defined(TGT_O2) || defined(TGT_OCTANE)
			if (parent == pciparent) {
				struct pci_attach_args *paa = aux;

				if (unit == paa->pa_device -
				    (sys_config.system_type == SGI_O2 ? 1 : 0))
					goto found;
			} else
#endif
#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2)
			if (parent == wdscparent) {
				/* XXX is there any better information to use
				   XXX than the attachment number? */
				if (unit == parent->dv_unit)
					goto found;
			} else
#endif
			{
				/*
				 * in case scsi() can follow something else then
				 * pci(), write code to handle this here...
				 */
			}
		}
	}

	if ((strcmp(component, "disk") == 0 &&
	     strcmp(cd->cd_name, "sd") == 0) ||
	    (strcmp(component, "cdrom") == 0 &&
	     strcmp(cd->cd_name, "cd") == 0)) {
		if (parent == lastparent) {
			struct scsi_attach_args *saa = aux;

			if (unit == saa->sa_sc_link->target) {
				/*
				 * We found our boot device.
				 * Now get the partition number.
				 */
				bootdv = dev;
#ifdef DEBUG
				printf("%s: boot device is %s\n",
				    __func__, dev->dv_xname);
#endif
				return;
			}
		}
	}

	if (strcmp(component, "bootp") == 0 && cd->cd_class == DV_IFNET) {
#ifdef TGT_OCTANE
		if (strcmp(cd->cd_name, "iec") == 0)
			bootdv = dev;
#endif
#ifdef TGT_O2
		if (strcmp(cd->cd_name, "mec") == 0)
			bootdv = dev;
#endif
#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2)
		if (strcmp(cd->cd_name, "sq") == 0)
			bootdv = dev;
#endif
#ifdef DEBUG
		if (bootdv != NULL)
			printf("%s: boot device is %s\n",
			    __func__, dev->dv_xname);
#endif
		return;
	}

	return;

found_advance:
	bootpath_next();
	component_pos++;
found:
	lastparent = dev;
}

#endif	/* IP20/22/24/26/28/30/32 */

#ifdef TGT_ORIGIN

/*
 * Origin (dksc) boot path analysis
 */

void	dksc_init(void);
int	dksc_scan_board(lboard_t *, void *);
int	dksc_scan_cmp(klinfo_t *, void *);

static struct sgi_device_location dksc_device;
static int dksc_ctrl, dksc_unit;
static const char *dksc_devname;

void
dksc_init()
{
	int val[3], idx;
	char *c = NULL;

	if (strncmp(osloadpartition, "dksc(", 5) == 0) {
		c = osloadpartition + 5;
		dksc_devname = "sd";
	} else if (strncmp(osloadpartition, "cdrom(", 6) == 0) {
		c = osloadpartition + 6;
		dksc_devname = "cd";
	}

	if (c == NULL)
		return;

	val[0] = val[1] = val[2] = 0;
	idx = 0;

	for (; *c != '\0'; c++) {
		if (*c == ')')
			break;
		else if (*c == ',') {
			if (++idx == 3)
				break;
		} else if (*c >= '0' && *c <= '9')
			val[idx] = 10 * val[idx] + (*c - '0');
	}

	dksc_ctrl = val[0];
	dksc_unit = val[1];

	/*
	 * Walk kl configuration and try to match the boot controller
	 * with a component.
	 */
	kl_scan_all_nodes(KLBRD_ANY, dksc_scan_board, NULL);
}

int
dksc_scan_board(lboard_t *brd, void *arg)
{
	kl_scan_board(brd, KLSTRUCT_ANY, dksc_scan_cmp, arg);
	return 0;
}

int
dksc_scan_cmp(klinfo_t *cmp, void *arg)
{
	klscctl_t *scsi2comp;
	klscsi_t *scsicomp;
	int i;

	/* bail out quickly if no controller number */
	if (cmp->virtid < 0)
		return 0;

	switch (cmp->struct_type) {
	case KLSTRUCT_SCSI:
	case KLSTRUCT_FIBERCHANNEL:
	case KLSTRUCT_QLFIBRE:
	case KLSTRUCT_FIREWIRE:
#if 0	/* should not get controller numbers anyway */
	case KLSTRUCT_IDE:
	case KLSTRUCT_IOC4_ATA:
#endif
		if (cmp->virtid == dksc_ctrl) {
			kl_get_location(cmp, &dksc_device);
			return 1;
		}
		break;
	case KLSTRUCT_SCSI2:
		/*
		 * Figure out whether one of the two ports matches our
		 * controller number.
		 */
		scsi2comp = (klscctl_t *)cmp;
		for (i = 0; i < scsi2comp->scsi_buscnt; i++) {
			scsicomp = (klscsi_t *)scsi2comp->scsi_bus[i];
			if (scsicomp == NULL)
				continue;
			if (scsicomp->scsi_info.virtid == dksc_ctrl) {
				kl_get_location(cmp, &dksc_device);
				dksc_device.specific = i;	/* port # */
				return 1;
			}
		}
		break;
	}

	return 0;
}

void
dksc_device_register(struct device *dev, void *aux)
{
	static int dksc_state = 0;
	static struct device *controller = NULL;
	static struct device *scsibus = NULL;

	struct device *parent = dev->dv_parent;
	struct cfdata *cf = dev->dv_cfdata;
	struct cfdriver *cd = cf->cf_driver;

	struct sgi_device_location dl;

	if (dksc_state == 0) {
		dksc_state = 1;
		dksc_init();
	}

	if (parent == NULL)
		return;		/* one of the @root devices */

	if (bootdv != NULL)
		return;

	/*
	 * If we already know our bus, try to match the correct device.
	 */
	if (scsibus != NULL) {
		if (strcmp(cd->cd_name, dksc_devname) == 0) {
			struct scsi_attach_args *saa = aux;
			if (dksc_unit == saa->sa_sc_link->target)
				bootdv = dev;
		}
		return;
	}

	/*
	 * If we already know our controller driver, try to match the
	 * correct scsibus.
	 */
	if (controller != NULL) {
		if (parent == controller &&
		    strcmp(cd->cd_name, "scsibus") == 0) {
			/* only match on the required bus */
			if (dksc_device.specific == 0)
				scsibus = dev;
			else
				dksc_device.specific--;
		}
		return;
	}

	/*
	 * If we are investigating a PCI bus, check whether the current
	 * device may be the controller we are looking for.
	 */

	if (strcmp(parent->dv_cfdata->cf_driver->cd_name, "pci") == 0) {
		struct pci_attach_args *paa = aux;
		if (pci_get_device_location(paa->pa_pc, paa->pa_tag, &dl) &&
		    location_match(&dksc_device, &dl))
			controller = dev;
		return;
	}
}

#endif	/* IP27/35 */

struct nam2blk nam2blk[] = {
	{ "sd",		0 },
	{ "wd",		4 },
	{ "rd",		8 },
	{ "vnd",	2 },
	{ NULL,		-1 }
};

/*
 * Convert "xx:xx:xx:xx:xx:xx" string to Ethernet hardware address.
 */
void
enaddr_aton(const char *s, u_int8_t *a)
{
	int i;

	if (s != NULL) {
		for (i = 0; i < 6; i++) {
			a[i] = strtoul(s, 16, &s);
			if (*s == ':')
				s++;
		}
	}
}

/*
 * Get a numeric environment variable
 */
u_long
bios_getenvint(const char *name)
{
	const char *envvar;
	u_long value;

	envvar = Bios_GetEnvironmentVariable(name);
	if (envvar != NULL) {
		value = strtoul(envvar, 10, &envvar);
		if (*envvar != '\0')
			value = 0;
	} else
		value = 0;

	return value;
}

/*
 * Convert an ASCII string into an integer.
 */
static u_long
strtoul(const char *s, int b, const char **o)
{
	int c;
	unsigned base = b, d;
	int neg = 0;
	u_long val = 0;

	if (s == NULL || *s == 0) {
		if (o != NULL)
			*o = s;
		return 0;
	}

	/* Skip spaces if any. */
	do {
		c = *s++;
	} while (c == ' ' || c == '\t');

	/* Parse sign, allow more than one (compat). */
	while (c == '-') {
		neg = !neg;
		c = *s++;
	}

	/* Parse base specification, if any. */
	if (base == 0 && c == '0') {
		c = *s++;
		switch (c) {
		case 'X':
		case 'x':
			base = 16;
			c = *s++;
			break;
		case 'B':
		case 'b':
			base = 2;
			c = *s++;
			break;
		default:
			base = 8;
		}
	}

	/* Parse number proper. */
	for (;;) {
		if (c >= '0' && c <= '9')
			d = c - '0';
		else if (c >= 'a' && c <= 'z')
			d = c - 'a' + 10;
		else if (c >= 'A' && c <= 'Z')
			d = c - 'A' + 10;
		else
			break;
		if (d >= base)
			break;
		val *= base;
		val += d;
		c = *s++;
	}
	if (neg)
		val = -val;
	if (o != NULL)
		*o = s - 1;
	return val;
}

/*
 * Relaxed comparison of two devices' physical location.
 */
int
location_match(struct sgi_device_location *l1, struct sgi_device_location *l2)
{
	/* must be on the same widget */
	if (l1->nasid != l2->nasid || l1->widget != l2->widget)
		return 0;

	/* must be on the same PCI bus, if applicable */
	if (l1->bus == -1 || l2->bus == -1)
		return 1;
	if (l1->bus != l2->bus)
		return 0;

	/* must be the same PCI device, if applicable */
	if (l1->device == -1 || l2->device == -1)
		return 1;
	if (l1->device != l2->device)
		return 0;

	/* must be the same PCI function, if applicable */
	if (l1->fn == -1 || l2->fn == -1)
		return 1;
	if (l1->fn != l2->fn)
		return 0;

	return 1;
}


code:
/*	$OpenBSD: bus_dma.c,v 1.39 2014/11/16 12:30:58 deraadt Exp $ */

/*
 * Copyright (c) 2003-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
/*-
 * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>

#include <uvm/uvm_extern.h>

#include <mips64/cache.h>
#include <machine/cpu.h>

#include <machine/bus.h>

#if defined(TGT_INDY) || defined(TGT_INDIGO2)
#include <sgi/sgi/ip22.h>
#endif

/*
 * Common function for DMA map creation.  May be called by bus-specific
 * DMA map creation functions.
 */
int
_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
    bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp)
{
	struct machine_bus_dmamap *map;
	void *mapstore;
	size_t mapsize;

	/*
	 * Allocate and initialize the DMA map.  The end of the map
	 * is a variable-sized array of segments, so we allocate enough
	 * room for them in one shot.
	 *
	 * Note we don't preserve the WAITOK or NOWAIT flags.  Preservation
	 * of ALLOCNOW notifies others that we've reserved these resources,
	 * and they are not to be freed.
	 *
	 * The bus_dmamap_t includes one bus_dma_segment_t, hence
	 * the (nsegments - 1).
	 */
	mapsize = sizeof(struct machine_bus_dmamap) +
	    (sizeof(bus_dma_segment_t) * (nsegments - 1));
	if ((mapstore = malloc(mapsize, M_DEVBUF, (flags & BUS_DMA_NOWAIT) ?
	    (M_NOWAIT | M_ZERO) : (M_WAITOK | M_ZERO))) == NULL)
		return (ENOMEM);

	map = (struct machine_bus_dmamap *)mapstore;
	map->_dm_size = size;
	map->_dm_segcnt = nsegments;
	map->_dm_maxsegsz = maxsegsz;
	map->_dm_boundary = boundary;
	map->_dm_flags = flags & ~(BUS_DMA_WAITOK|BUS_DMA_NOWAIT);

	*dmamp = map;
	return (0);
}

/*
 * Common function for DMA map destruction.  May be called by bus-specific
 * DMA map destruction functions.
 */
void
_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
{
	free(map, M_DEVBUF, 0);
}

/*
 * Common function for loading a DMA map with a linear buffer.  May
 * be called by bus-specific DMA map load functions.
 */
int
_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, bus_size_t buflen,
    struct proc *p, int flags)
{
	paddr_t lastaddr;
	int seg, error;

	/*
	 * Make sure that on error condition we return "no valid mappings".
	 */
	map->dm_nsegs = 0;
	map->dm_mapsize = 0;

	if (buflen > map->_dm_size)
		return (EINVAL);

	seg = 0;
	error = (*t->_dmamap_load_buffer)(t, map, buf, buflen, p, flags,
	    &lastaddr, &seg, 1);
	if (error == 0) {
		map->dm_nsegs = seg + 1;
		map->dm_mapsize = buflen;
	}

	return (error);
}

/*
 * Like _bus_dmamap_load(), but for mbufs.
 */
int
_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0, int flags)
{
	paddr_t lastaddr;
	int seg, error, first;
	struct mbuf *m;

	/*
	 * Make sure that on error condition we return "no valid mappings".
	 */
	map->dm_nsegs = 0;
	map->dm_mapsize = 0;

#ifdef DIAGNOSTIC
	if ((m0->m_flags & M_PKTHDR) == 0)
		panic("_dmamap_load_mbuf: no packet header");
#endif

	if (m0->m_pkthdr.len > map->_dm_size)
		return (EINVAL);

	first = 1;
	seg = 0;
	error = 0;
	for (m = m0; m != NULL && error == 0; m = m->m_next) {
		if (m->m_len == 0)
			continue;
		error = (*t->_dmamap_load_buffer)(t, map, m->m_data, m->m_len,
		    NULL, flags, &lastaddr, &seg, first);
		first = 0;
	}
	if (error == 0) {
		map->dm_nsegs = seg + 1;
		map->dm_mapsize = m0->m_pkthdr.len;
	}

	return (error);
}

/*
 * Like _dmamap_load(), but for uios.
 */
int
_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, int flags)
{
	paddr_t lastaddr;
	int seg, i, error, first;
	bus_size_t minlen, resid;
	struct proc *p = NULL;
	struct iovec *iov;
	void *addr;

	/*
	 * Make sure that on error condition we return "no valid mappings".
	 */
	map->dm_nsegs = 0;
	map->dm_mapsize = 0;

	resid = uio->uio_resid;
	iov = uio->uio_iov;

	if (uio->uio_segflg == UIO_USERSPACE) {
		p = uio->uio_procp;
#ifdef DIAGNOSTIC
		if (p == NULL)
			panic("_dmamap_load_uio: USERSPACE but no proc");
#endif
	}

	first = 1;
	seg = 0;
	error = 0;
	for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) {
		/*
		 * Now at the first iovec to load.  Load each iovec
		 * until we have exhausted the residual count.
		 */
		minlen = resid < iov[i].iov_len ? resid : iov[i].iov_len;
		addr = (void *)iov[i].iov_base;

		error = (*t->_dmamap_load_buffer)(t, map, addr, minlen,
		    p, flags, &lastaddr, &seg, first);
		first = 0;

		resid -= minlen;
	}
	if (error == 0) {
		map->dm_nsegs = seg + 1;
		map->dm_mapsize = uio->uio_resid;
	}

	return (error);
}

/*
 * Like _dmamap_load(), but for raw memory allocated with
 * bus_dmamem_alloc().
 */
int
_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs,
    int nsegs, bus_size_t size, int flags)
{
	if (nsegs > map->_dm_segcnt || size > map->_dm_size)
		return (EINVAL);

	/*
	 * Make sure we don't cross any boundaries.
	 */
	if (map->_dm_boundary) {
		bus_addr_t bmask = ~(map->_dm_boundary - 1);
		int i;

		if (t->_dma_mask != 0)
			bmask &= t->_dma_mask;
		for (i = 0; i < nsegs; i++) {
			if (segs[i].ds_len > map->_dm_maxsegsz)
				return (EINVAL);
			if ((segs[i].ds_addr & bmask) !=
			    ((segs[i].ds_addr + segs[i].ds_len - 1) & bmask))
				return (EINVAL);
		}
	}

	bcopy(segs, map->dm_segs, nsegs * sizeof(*segs));
	map->dm_nsegs = nsegs;
	map->dm_mapsize = size;
	return (0);
}

/*
 * Common function for unloading a DMA map.  May be called by
 * bus-specific DMA map unload functions.
 */
void
_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
{
	/*
	 * No resources to free; just mark the mappings as
	 * invalid.
	 */
	map->dm_nsegs = 0;
	map->dm_mapsize = 0;
}

/*
 * Common function for DMA map synchronization.  May be called
 * by bus-specific DMA map synchronization functions.
 */
void
_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t addr,
    bus_size_t size, int op)
{
	int nsegs;
	int curseg;
	int how;
	struct cpu_info *ci;

#ifdef TGT_COHERENT
	/* we only need to writeback here */
	if ((op & BUS_DMASYNC_PREWRITE) == 0)
		return;
	else
		how = CACHE_SYNC_W;
#else
	/*
	 * If only PREWRITE is requested, writeback.
	 * PREWRITE with PREREAD writebacks and invalidates (since noncoherent)
	 * *all* cache levels.
	 * Otherwise, just invalidate (since noncoherent).
	 */
	if (op & BUS_DMASYNC_PREWRITE) {
		if (op & BUS_DMASYNC_PREREAD)
			how = CACHE_SYNC_X;
		else
			how = CACHE_SYNC_W;
	} else {
		if (op & (BUS_DMASYNC_PREREAD | BUS_DMASYNC_POSTREAD))
			how = CACHE_SYNC_R;
		else
			return;
	}
#endif

	ci = curcpu();
	nsegs = map->dm_nsegs;
	curseg = 0;

	while (size && nsegs) {
		paddr_t paddr;
		vaddr_t vaddr;
		bus_size_t ssize;

		ssize = map->dm_segs[curseg].ds_len;
		paddr = map->dm_segs[curseg]._ds_paddr;
		vaddr = map->dm_segs[curseg]._ds_vaddr;

		if (addr != 0) {
			if (addr >= ssize) {
				addr -= ssize;
				ssize = 0;
			} else {
				vaddr += addr;
				paddr += addr;
				ssize -= addr;
				addr = 0;
			}
		}
		if (ssize > size)
			ssize = size;

#ifndef TGT_COHERENT
		if (IS_XKPHYS(vaddr) && XKPHYS_TO_CCA(vaddr) == CCA_NC) {
			size -= ssize;
			ssize = 0;
		}
#endif

		if (ssize != 0) {
			Mips_IOSyncDCache(ci, vaddr, ssize, how);
#if defined(TGT_INDY) || defined(TGT_INDIGO2)
			/*
			 * Also flush external L2 if available - this could
			 * (and used to) be done in Mips_IOSyncDCache, but
			 * as the external L2 is physically addressed, this
			 * would require the physical address to be
			 * recomputed, although we know it here.
			 */
			if (ip22_extsync != NULL)
				(*ip22_extsync)(ci, paddr, ssize, how);
#endif
			size -= ssize;
		}
		curseg++;
		nsegs--;
	}

#ifdef DIAGNOSTIC
	if (size != 0)
		panic("_dmamap_sync: ran off map!");
#endif
}

/*
 * Common function for DMA-safe memory allocation.  May be called
 * by bus-specific DMA memory allocation functions.
 */
int
_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
    bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
    int flags)
{
	return _dmamem_alloc_range(t, size, alignment, boundary,
	    segs, nsegs, rsegs, flags,
	    dma_constraint.ucr_low, dma_constraint.ucr_high);
}

/*
 * Common function for freeing DMA-safe memory.  May be called by
 * bus-specific DMA memory free functions.
 */
void
_dmamem_free(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs)
{
	vm_page_t m;
	bus_addr_t addr;
	struct pglist mlist;
	int curseg;

	/*
	 * Build a list of pages to free back to the VM system.
	 */
	TAILQ_INIT(&mlist);
	for (curseg = 0; curseg < nsegs; curseg++) {
		for (addr = segs[curseg].ds_addr;
		    addr < (segs[curseg].ds_addr + segs[curseg].ds_len);
		    addr += PAGE_SIZE) {
			m = PHYS_TO_VM_PAGE((*t->_device_to_pa)(addr));
			TAILQ_INSERT_TAIL(&mlist, m, pageq);
		}
	}

	uvm_pglistfree(&mlist);
}

/*
 * Common function for mapping DMA-safe memory.  May be called by
 * bus-specific DMA memory map functions.
 */
int
_dmamem_map(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs, size_t size,
    caddr_t *kvap, int flags)
{
	vaddr_t va, sva;
	size_t ssize;
	paddr_t pa;
	bus_addr_t addr;
	int curseg, error, pmap_flags;
	const struct kmem_dyn_mode *kd;

#if defined(TGT_INDIGO2)
	/*
	 * On ECC MC systems, which do not allow uncached writes to memory
	 * during regular operation, fail requests for uncached (coherent)
	 * memory, unless the caller tells us it is aware of this and will
	 * do the right thing, by passing BUS_DMA_BUS1 as well.
	 */
	if ((flags & (BUS_DMA_COHERENT | BUS_DMA_BUS1)) == BUS_DMA_COHERENT &&
	    ip22_ecc)
		return EINVAL;
#endif

#ifdef TGT_COHERENT
	/* coherent mappings do not need to be uncached on these platforms */
	flags &= ~BUS_DMA_COHERENT;
#endif

	if (nsegs == 1) {
		pa = (*t->_device_to_pa)(segs[0].ds_addr);
		if (flags & (BUS_DMA_COHERENT | BUS_DMA_NOCACHE))
			*kvap = (caddr_t)PHYS_TO_XKPHYS(pa, CCA_NC);
		else
			*kvap = (caddr_t)PHYS_TO_XKPHYS(pa, CCA_CACHED);
		return (0);
	}

	size = round_page(size);
	kd = flags & BUS_DMA_NOWAIT ? &kd_trylock : &kd_waitok;
	va = (vaddr_t)km_alloc(size, &kv_any, &kp_none, kd);
	if (va == 0)
		return (ENOMEM);

	*kvap = (caddr_t)va;

	sva = va;
	ssize = size;
	pmap_flags = PMAP_WIRED | PMAP_CANFAIL;
	if (flags & (BUS_DMA_COHERENT | BUS_DMA_NOCACHE))
		pmap_flags |= PMAP_NOCACHE;
	for (curseg = 0; curseg < nsegs; curseg++) {
		for (addr = segs[curseg].ds_addr;
		    addr < (segs[curseg].ds_addr + segs[curseg].ds_len);
		    addr += NBPG, va += NBPG, size -= NBPG) {
#ifdef DIAGNOSTIC
			if (size == 0)
				panic("_dmamem_map: size botch");
#endif
			pa = (*t->_device_to_pa)(addr);
			error = pmap_enter(pmap_kernel(), va, pa,
			    PROT_READ | PROT_WRITE,
			    PROT_READ | PROT_WRITE | pmap_flags);
			if (error) {
				pmap_update(pmap_kernel());
				km_free((void *)sva, ssize, &kv_any, &kp_none);
				return (error);
			}

			/*
			 * This is redundant with what pmap_enter() did
			 * above, but will take care of forcing other
			 * mappings of the same page (if any) to be
			 * uncached.
			 * If there are no multiple mappings of that
			 * page, this amounts to a noop.
			 */
			if (flags & (BUS_DMA_COHERENT | BUS_DMA_NOCACHE))
				pmap_page_cache(PHYS_TO_VM_PAGE(pa),
				    PGF_UNCACHED);
		}
		pmap_update(pmap_kernel());
	}

	return (0);
}

/*
 * Common function for unmapping DMA-safe memory.  May be called by
 * bus-specific DMA memory unmapping functions.
 */
void
_dmamem_unmap(bus_dma_tag_t t, caddr_t kva, size_t size)
{
	if (IS_XKPHYS((vaddr_t)kva))
		return;

	km_free(kva, round_page(size), &kv_any, &kp_none);
}

/*
 * Common function for mmap(2)'ing DMA-safe memory.  May be called by
 * bus-specific DMA mmap(2)'ing functions.
 */
paddr_t
_dmamem_mmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs, off_t off,
    int prot, int flags)
{
	int i;

	for (i = 0; i < nsegs; i++) {
#ifdef DIAGNOSTIC
		if (off & PAGE_MASK)
			panic("_dmamem_mmap: offset unaligned");
		if (segs[i].ds_addr & PAGE_MASK)
			panic("_dmamem_mmap: segment unaligned");
		if (segs[i].ds_len & PAGE_MASK)
			panic("_dmamem_mmap: segment size not multiple"
			    " of page size");
#endif
		if (off >= segs[i].ds_len) {
			off -= segs[i].ds_len;
			continue;
		}

		return ((*t->_device_to_pa)(segs[i].ds_addr) + off);
	}

	/* Page not found. */
	return (-1);
}

/**********************************************************************
 * DMA utility functions
 **********************************************************************/

/*
 * Utility function to load a linear buffer.  lastaddrp holds state
 * between invocations (for multiple-buffer loads).  segp contains
 * the starting segment on entrance, and the ending segment on exit.
 * first indicates if this is the first invocation of this function.
 */
int
_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
    bus_size_t buflen, struct proc *p, int flags, paddr_t *lastaddrp,
    int *segp, int first)
{
	bus_size_t sgsize;
	bus_addr_t lastaddr, baddr, bmask;
	paddr_t curaddr;
	vaddr_t vaddr = (vaddr_t)buf;
	int seg;
	pmap_t pmap;

	if (p != NULL)
		pmap = p->p_vmspace->vm_map.pmap;
	else
		pmap = pmap_kernel();

	lastaddr = *lastaddrp;
	bmask  = ~(map->_dm_boundary - 1);
	if (t->_dma_mask != 0)
		bmask &= t->_dma_mask;

	for (seg = *segp; buflen > 0; ) {
		/*
		 * Get the physical address for this segment.
		 */
		if (pmap_extract(pmap, vaddr, &curaddr) == FALSE)
			panic("_dmapmap_load_buffer: pmap_extract(%p, %p) failed!",
			    pmap, (void *)vaddr);

#ifdef DIAGNOSTIC
		if (curaddr > dma_constraint.ucr_high ||
		    curaddr < dma_constraint.ucr_low)
			panic("Non DMA-reachable buffer at addr %p (raw)",
			    (void *)curaddr);
#endif

		/*
		 * Compute the segment size, and adjust counts.
		 */
		sgsize = NBPG - ((u_long)vaddr & PGOFSET);
		if (buflen < sgsize)
			sgsize = buflen;

		/*
		 * Make sure we don't cross any boundaries.
		 */
		if (map->_dm_boundary > 0) {
			baddr = ((bus_addr_t)curaddr + map->_dm_boundary) &
			    bmask;
			if (sgsize > (baddr - (bus_addr_t)curaddr))
				sgsize = (baddr - (bus_addr_t)curaddr);
		}

		/*
		 * Insert chunk into a segment, coalescing with
		 * previous segment if possible.
		 */
		if (first) {
			map->dm_segs[seg].ds_addr =
			    (*t->_pa_to_device)(curaddr);
			map->dm_segs[seg].ds_len = sgsize;
			map->dm_segs[seg]._ds_paddr = curaddr;
			map->dm_segs[seg]._ds_vaddr = vaddr;
			first = 0;
		} else {
			if ((bus_addr_t)curaddr == lastaddr + 1 &&
			    (map->dm_segs[seg].ds_len + sgsize) <=
			     map->_dm_maxsegsz &&
			     (map->_dm_boundary == 0 ||
			     (map->dm_segs[seg].ds_addr & bmask) ==
			     ((bus_addr_t)curaddr & bmask)))
				map->dm_segs[seg].ds_len += sgsize;
			else {
				if (++seg >= map->_dm_segcnt)
					break;
				map->dm_segs[seg].ds_addr =
				    (*t->_pa_to_device)(curaddr);
				map->dm_segs[seg].ds_len = sgsize;
				map->dm_segs[seg]._ds_paddr = curaddr;
				map->dm_segs[seg]._ds_vaddr = vaddr;
			}
		}

		lastaddr = (bus_addr_t)curaddr + sgsize - 1;
		vaddr += sgsize;
		buflen -= sgsize;
	}

	*segp = seg;
	*lastaddrp = lastaddr;

	/*
	 * Did we fit?
	 */
	if (buflen != 0)
		return (EFBIG);		/* XXX better return value here? */

	return (0);
}

/*
 * Allocate physical memory from the given physical address range.
 * Called by DMA-safe memory allocation methods.
 */
int
_dmamem_alloc_range(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
    bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
    int flags, paddr_t low, paddr_t high)
{
	paddr_t curaddr, lastaddr;
	vm_page_t m;
	struct pglist mlist;
	int curseg, error, plaflag;

	/* Always round the size. */
	size = round_page(size);

	/*
	 * Allocate pages from the VM system.
	 */
	plaflag = flags & BUS_DMA_NOWAIT ? UVM_PLA_NOWAIT : UVM_PLA_WAITOK;
	if (flags & BUS_DMA_ZERO)
		plaflag |= UVM_PLA_ZERO;

	TAILQ_INIT(&mlist);
	error = uvm_pglistalloc(size, low, high, alignment, boundary,
	    &mlist, nsegs, plaflag);
	if (error)
		return (error);

	/*
	 * Compute the location, size, and number of segments actually
	 * returned by the VM code.
	 */
	m = TAILQ_FIRST(&mlist);
	curseg = 0;
	lastaddr = segs[curseg].ds_addr =
	    (*t->_pa_to_device)(VM_PAGE_TO_PHYS(m));
	segs[curseg].ds_len = PAGE_SIZE;
	m = TAILQ_NEXT(m, pageq);

	for (; m != NULL; m = TAILQ_NEXT(m, pageq)) {
		curaddr = VM_PAGE_TO_PHYS(m);
#ifdef DIAGNOSTIC
		if (curaddr < low || curaddr >= high) {
			printf("vm_page_alloc_memory returned non-sensical"
			    " address 0x%lx\n", curaddr);
			panic("_dmamem_alloc_range");
		}
#endif
		curaddr = (*t->_pa_to_device)(curaddr);
		if (curaddr == (lastaddr + PAGE_SIZE))
			segs[curseg].ds_len += PAGE_SIZE;
		else {
			curseg++;
			segs[curseg].ds_addr = curaddr;
			segs[curseg].ds_len = PAGE_SIZE;
		}
		lastaddr = curaddr;
	}

	*rsegs = curseg + 1;

	return (0);
}


code:
/*	$OpenBSD: conf.c,v 1.35 2014/12/11 19:44:17 tedu Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Ralph Campbell.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/ioctl.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/tty.h>

#include <machine/conf.h>

/*
 *	Block devices.
 */

#include "vnd.h"
#include "sd.h"
#include "cd.h"
#include "st.h"
#include "wd.h"
bdev_decl(wd);
#include "rd.h"
#include "hotplug.h"

struct bdevsw	bdevsw[] =
{
	bdev_disk_init(NSD,sd),		/* 0: SCSI disk */
	bdev_swap_init(1,sw),		/* 1: should be here swap pseudo-dev */
	bdev_disk_init(NVND,vnd),	/* 2: vnode disk driver */
	bdev_disk_init(NCD,cd),		/* 3: SCSI CD-ROM */
	bdev_disk_init(NWD,wd),		/* 4: ST506/ESDI/IDE disk */
	bdev_notdef(),			/* 5:  */
	bdev_notdef(),			/* 6: was: concatenated disk driver */
	bdev_notdef(),			/* 7:  */
	bdev_disk_init(NRD,rd),		/* 8: RAM disk (for install) */
	bdev_notdef(),			/* 9:  */
	bdev_tape_init(NST,st),		/* 10: SCSI tape */
	bdev_notdef(),			/* 11:  */
	bdev_notdef(),			/* 12:  */
	bdev_notdef(),			/* 13:  */
	bdev_notdef(),			/* 14:  */
	bdev_notdef(),			/* 15:  */
};

int	nblkdev = nitems(bdevsw);

/*
 *	Character devices.
 */

#define mmread mmrw
#define mmwrite mmrw
dev_type_read(mmrw);
cdev_decl(mm);
#include "bio.h"
#include "pty.h"
cdev_decl(fd);
#include "st.h"
#include "bpfilter.h"
#include "tun.h"
#include "com.h"
cdev_decl(com);
#include "zs.h"
cdev_decl(zs);
#include "lpt.h"
cdev_decl(lpt);
#include "ch.h"
#include "uk.h"
cdev_decl(wd);
#include "audio.h"
#include "video.h"
#include "ksyms.h"

#include "wsdisplay.h"
#include "wskbd.h"
#include "wsmouse.h"
#include "wsmux.h"
#include "pci.h"
cdev_decl(pci);

#include "pf.h"
#include "systrace.h"

#include "usb.h"
#include "uhid.h"
#include "ugen.h"
#include "ulpt.h"
#include "ucom.h"

#include "vscsi.h"
#include "pppx.h"
#include "fuse.h"

struct cdevsw	cdevsw[] =
{
	cdev_cn_init(1,cn),		/* 0: virtual console */
	cdev_notdef(),			/* 1 was /dev/drum */
	cdev_ctty_init(1,ctty),		/* 2: controlling terminal */
	cdev_mm_init(1,mm),		/* 3: /dev/{null,mem,kmem,...} */
	cdev_tty_init(NPTY,pts),	/* 4: pseudo-tty slave */
	cdev_ptc_init(NPTY,ptc),	/* 5: pseudo-tty master */
	cdev_log_init(1,log),		/* 6: /dev/klog */
	cdev_fd_init(1,filedesc),	/* 7: file descriptor pseudo-dev */
	cdev_disk_init(NCD,cd),		/* 8: SCSI CD */
	cdev_disk_init(NSD,sd),		/* 9: SCSI disk */
	cdev_tape_init(NST,st),		/* 10: SCSI tape */
	cdev_disk_init(NVND,vnd),	/* 11: vnode disk */
	cdev_bpf_init(NBPFILTER,bpf),	/* 12: berkeley packet filter */
	cdev_tun_init(NTUN,tun),	/* 13: network tunnel */
	cdev_notdef(),			/* 14 */
	cdev_notdef(),			/* 15: */
	cdev_lpt_init(NLPT,lpt),	/* 16: Parallel printer interface */
	cdev_tty_init(NCOM,com),	/* 17: 16C450 serial interface */
	cdev_disk_init(NWD,wd),		/* 18: ST506/ESDI/IDE disk */
	cdev_tty_init(NZS,zs),		/* 19: Z8530 serial interface */
	cdev_notdef(),			/* 20: */
	cdev_notdef(),			/* 21: */
	cdev_disk_init(NRD,rd),		/* 22: ramdisk device */
	cdev_notdef(),			/* 23: was: concatenated disk driver */
	cdev_notdef(),			/* 24: */
	cdev_wsdisplay_init(NWSDISPLAY, wsdisplay),	/* 25: */
	cdev_mouse_init(NWSKBD, wskbd),	/* 26: */
	cdev_mouse_init(NWSMOUSE, wsmouse),	/* 27: */
	cdev_mouse_init(NWSMUX, wsmux),	/* 28: */
#ifdef USER_PCICONF
	cdev_pci_init(NPCI,pci),	/* 29: PCI user */
#else
	cdev_notdef(),			/* 29 */
#endif
	cdev_notdef(),			/* 30: */
	cdev_pf_init(NPF,pf),		/* 31: packet filter */
	cdev_uk_init(NUK,uk),		/* 32: unknown SCSI */
	cdev_random_init(1,random),	/* 33: random data source */
	cdev_notdef(),			/* 34: */
	cdev_ksyms_init(NKSYMS,ksyms),	/* 35: Kernel symbols device */
	cdev_ch_init(NCH,ch),		/* 36: SCSI autochanger */
	cdev_notdef(),			/* 37: */
	cdev_notdef(),			/* 38: */
	cdev_notdef(),			/* 39: */
	cdev_notdef(),			/* 40: */
	cdev_notdef(),			/* 41: */
	cdev_notdef(),			/* 42: */
	cdev_notdef(),			/* 33: */
	cdev_audio_init(NAUDIO,audio),	/* 44: /dev/audio */
	cdev_video_init(NVIDEO,video),	/* 45: generic video I/O */
	cdev_notdef(),			/* 46: */
	cdev_notdef(),			/* 47: was: /dev/crypto */
	cdev_notdef(),			/* 48: */
	cdev_bio_init(NBIO,bio),	/* 49: ioctl tunnel */
	cdev_systrace_init(NSYSTRACE,systrace),	/* 50: system call tracing */
	cdev_notdef(),			/* 51: */
	cdev_ptm_init(NPTY,ptm),	/* 52: pseudo-tty ptm device */
	cdev_notdef(),			/* 53: */
	cdev_notdef(),			/* 54: */
	cdev_notdef(),			/* 55: */
	cdev_notdef(),			/* 56: */
	cdev_notdef(),			/* 57: */
	cdev_notdef(),			/* 58: */
	cdev_notdef(),			/* 59: */
	cdev_notdef(),			/* 60: */
	cdev_usb_init(NUSB,usb),	/* 61: USB controller */
	cdev_usbdev_init(NUHID,uhid),	/* 62: USB generic HID */
	cdev_usbdev_init(NUGEN,ugen),	/* 63: USB generic driver */
	cdev_ulpt_init(NULPT,ulpt),	/* 64: USB printers */
	cdev_notdef(),			/* 65: was urio */
	cdev_tty_init(NUCOM,ucom),	/* 66: USB tty */
	cdev_hotplug_init(NHOTPLUG,hotplug), /* 67: devices hotplugging */
	cdev_vscsi_init(NVSCSI,vscsi),	/* 68: vscsi */
	cdev_disk_init(1,diskmap),	/* 69: disk mapper */
	cdev_pppx_init(NPPPX,pppx),	/* 70: pppx */
	cdev_notdef(),			/* 71: */
	cdev_notdef(),			/* 72: was USB scanners */
	cdev_fuse_init(NFUSE,fuse),	/* 73: fuse */
};

int	nchrdev = nitems(cdevsw);

/*
 * Swapdev is a fake device implemented
 * in sw.c used only internally to get to swstrategy.
 * It cannot be provided to the users, because the
 * swstrategy routine munches the b_dev and b_blkno entries
 * before calling the appropriate driver.  This would horribly
 * confuse, e.g. the hashing routines. Instead, /dev/drum is
 * provided as a character (raw) device.
 */
dev_t	swapdev = makedev(1, 0);

/*
 * Routine that identifies /dev/mem and /dev/kmem.
 *
 * A minimal stub routine can always return 0.
 */
int
iskmemdev(dev)
	dev_t dev;
{

	if (major(dev) == 3 && (minor(dev) == 0 || minor(dev) == 1))
		return (1);
	return (0);
}

/*
 * Returns true if def is /dev/zero
 */
int
iszerodev(dev)
	dev_t dev;
{
	return (major(dev) == 3 && minor(dev) == 12);
}

dev_t
getnulldev()
{
	return(makedev(3, 2));
}

int chrtoblktbl[] =  {
	/* VCHR         VBLK */
	/* 0 */		NODEV,
	/* 1 */		NODEV,
	/* 2 */		NODEV,
	/* 3 */		NODEV,
	/* 4 */		NODEV,
	/* 5 */		NODEV,
	/* 6 */		NODEV,
	/* 7 */		NODEV,
	/* 8 */		3,		/* cd */
	/* 9 */		0,		/* sd */
	/* 10 */	10,		/* st */
	/* 11 */	2,		/* vnd */
	/* 12 */	NODEV,
	/* 13 */	NODEV,
	/* 14 */	NODEV,
	/* 15 */	NODEV,
	/* 16 */	NODEV,
	/* 17 */	NODEV,
	/* 18 */	4,		/* wd */
	/* 19 */	NODEV,
	/* 20 */	NODEV,
	/* 21 */	NODEV,
	/* 22 */	8		/* rd */
};

int nchrtoblktbl = nitems(chrtoblktbl);

#include <dev/cons.h>

cons_decl(com);
cons_decl(ws);
cons_decl(zs);

struct	consdev constab[] = {
#if NWSDISPLAY > 0
	cons_init(ws),
#endif
#if NCOM > 0
	cons_init(com),
#endif
#if NZS > 0
	cons_init(zs),
#endif
	{ 0 },
};


code:
/*	$OpenBSD: disksubr.c,v 1.30 2014/01/06 21:00:55 miod Exp $	*/

/*
 * Copyright (c) 1999 Michael Shalayeff
 * Copyright (c) 1997 Niklas Hallqvist
 * Copyright (c) 1996 Theo de Raadt
 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/syslog.h>
#include <sys/disk.h>

int	readsgilabel(struct buf *, void (*)(struct buf *),
	    struct disklabel *, daddr_t *, int);

/*
 * Attempt to read a disk label from a device
 * using the indicated strategy routine.
 * The label must be partly set up before this:
 * secpercyl, secsize and anything required for a block i/o read
 * operation in the driver's strategy/start routines
 * must be filled in before calling us.
 */
int
readdisklabel(dev_t dev, void (*strat)(struct buf *),
    struct disklabel *lp, int spoofonly)
{
	struct buf *bp = NULL;
	int error;

	if ((error = initdisklabel(lp)))
		goto done;
	lp->d_flags |= D_VENDOR;

	/* get a buffer and initialize it */
	bp = geteblk((int)lp->d_secsize);
	bp->b_dev = dev;

	error = readsgilabel(bp, strat, lp, NULL, spoofonly);
	if (error == 0)
		goto done;

	error = readdoslabel(bp, strat, lp, NULL, spoofonly);
	if (error == 0)
		goto done;

#if defined(CD9660)
	error = iso_disklabelspoof(dev, strat, lp);
	if (error == 0)
		goto done;
#endif
#if defined(UDF)
	error = udf_disklabelspoof(dev, strat, lp);
	if (error == 0)
		goto done;
#endif

done:
	if (bp) {
		bp->b_flags |= B_INVAL;
		brelse(bp);
	}
	disk_change = 1;
	return (error);
}

static struct {
	int m;
	int b;
} maptab[] = {
    { 0,	FS_BSDFFS},	{ 1,	FS_SWAP},	{ 10,	FS_BSDFFS},
    { 3,	FS_BSDFFS},	{ 4,	FS_BSDFFS},	{ 5,	FS_BSDFFS},
    { 6,	FS_BSDFFS},	{ 7,	FS_BSDFFS},	{ 15,	FS_OTHER},
    { 9,	FS_BSDFFS},	{ 2,	FS_UNUSED},	{ 11,	FS_BSDFFS},
    { 12,	FS_BSDFFS},	{ 13,	FS_BSDFFS},	{ 14,	FS_BSDFFS},
    { 8,	FS_BSDFFS}
};

int
readsgilabel(struct buf *bp, void (*strat)(struct buf *),
    struct disklabel *lp, daddr_t *partoffp, int spoofonly)
{
	struct sgilabel *dlp;
	int i, *p, cs = 0;
	daddr_t fsoffs, fsend;
	int offset;

	bp->b_blkno = 0;
	bp->b_bcount = lp->d_secsize;
	CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
	SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
	(*strat)(bp);

	/* if successful, locate disk label within block and validate */
	if (biowait(bp))
		return (bp->b_error);

	fsoffs = DL_SECTOBLK(lp, DL_GETBSTART(lp));
	fsend = DL_SECTOBLK(lp, DL_GETBEND(lp));

	dlp = (struct sgilabel *)(bp->b_data + LABELOFFSET);
	if (dlp->magic != htobe32(SGILABEL_MAGIC))
		goto finished;

	if (dlp->partitions[0].blocks == 0)
		return (EINVAL);
	fsoffs = (long long)dlp->partitions[0].first;
	fsend = fsoffs + dlp->partitions[0].blocks;

	/* Only came here to find the offset... */
	if (partoffp) {
		*partoffp = fsoffs;
		goto finished;
	}

	p = (int *)dlp;
	i = sizeof(struct sgilabel) / sizeof(int);
	while (i--)
		cs += *p++;
	if (cs != 0)
		return (EINVAL);	/* sgilabel checksum error */

	/* Spoof info from sgi label, in case there is no OpenBSD label. */
	lp->d_npartitions = MAXPARTITIONS;

	for (i = 0; i < 16; i++) {
		int bsd = maptab[i].m;
		int type = maptab[i].b;

		if (spoofonly && type != FS_UNUSED && type != FS_OTHER)
			continue;

		DL_SETPOFFSET(&lp->d_partitions[bsd],
		    dlp->partitions[i].first);
		DL_SETPSIZE(&lp->d_partitions[bsd],
		    dlp->partitions[i].blocks);
		lp->d_partitions[bsd].p_fstype = type;
		if (type == FS_BSDFFS) {
			lp->d_partitions[bsd].p_fragblock =
			    DISKLABELV1_FFS_FRAGBLOCK(1024, 8);
			lp->d_partitions[bsd].p_cpg = 16;
		}
	}

	DL_SETBSTART(lp, DL_BLKTOSEC(lp, fsoffs));
	DL_SETBEND(lp, DL_BLKTOSEC(lp, fsend));
	lp->d_version = 1;
	lp->d_flags = D_VENDOR;
	lp->d_checksum = 0;
	lp->d_checksum = dkcksum(lp);

finished:
	/* record the OpenBSD partition's placement for the caller */
	if (partoffp)
		*partoffp = fsoffs;
	else {
		DL_SETBSTART(lp, DL_BLKTOSEC(lp, fsoffs));
		DL_SETBEND(lp, DL_BLKTOSEC(lp, fsend));
	}

	/* don't read the on-disk label if we are in spoofed-only mode */
	if (spoofonly)
		return (0);

	bp->b_blkno = DL_BLKTOSEC(lp, fsoffs + LABELSECTOR) *
	    DL_BLKSPERSEC(lp);
	offset = DL_BLKOFFSET(lp, fsoffs + LABELSECTOR) + LABELOFFSET;
	bp->b_bcount = lp->d_secsize;
	CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
	SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
	(*strat)(bp);
	if (biowait(bp))
		return (bp->b_error);

	/*
	 * Do OpenBSD disklabel validation/adjustment.
	 *
	 * N.B: No matter what the bits are on the disk, we now have the
	 * OpenBSD disklabel for this sgi disk. DO NOT proceed to
	 * readdoslabel(), iso_spooflabel(), etc.
	 */
	checkdisklabel(bp->b_data + offset, lp, fsoffs, fsend);
	return (0);
}

/*
 * Write disk label back to device after modification.
 */
int
writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp)
{
	daddr_t partoff = -1;
	int error = EIO;
	int offset;
	struct buf *bp = NULL;
	struct disklabel *dlp;

	/* get a buffer and initialize it */
	bp = geteblk((int)lp->d_secsize);
	bp->b_dev = dev;

	if (readsgilabel(bp, strat, lp, &partoff, 1) != 0 &&
	    readdoslabel(bp, strat, lp, &partoff, 1) != 0)
		goto done;

	/* Read it in, slap the new label in, and write it back out */
	bp->b_blkno = DL_BLKTOSEC(lp, partoff + DOS_LABELSECTOR) *
	    DL_BLKSPERSEC(lp);
	offset = DL_BLKOFFSET(lp, partoff + DOS_LABELSECTOR);
	bp->b_bcount = lp->d_secsize;
	CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
	SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
	(*strat)(bp);
	if ((error = biowait(bp)) != 0)
		goto done;

	dlp = (struct disklabel *)(bp->b_data + offset);
	*dlp = *lp;
	CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
	SET(bp->b_flags, B_BUSY | B_WRITE | B_RAW);
	(*strat)(bp);
	error = biowait(bp);

done:
	if (bp) {
		bp->b_flags |= B_INVAL;
		brelse(bp);
	}
	disk_change = 1;
	return (error);
}