code:
/*	$OpenBSD: pmapae.c,v 1.41 2015/08/04 06:12:16 mlarkin Exp $	*/

/*
 * Copyright (c) 2006-2008 Michael Shalayeff
 * All rights reserved.
 *
 * 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 MIND, 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) 1997 Charles D. Cranor and Washington University.
 * 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.
 *
 * 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.
 *
 *	from OpenBSD: pmap.c,v 1.85 2005/11/18 17:05:04 brad Exp
 */

/*
 * pmap.c: i386 pmap module rewrite
 * Chuck Cranor <chuck@ccrc.wustl.edu>
 * 11-Aug-97
 *
 * history of this pmap module: in addition to my own input, i used
 *    the following references for this rewrite of the i386 pmap:
 *
 * [1] the NetBSD i386 pmap.   this pmap appears to be based on the
 *     BSD hp300 pmap done by Mike Hibler at University of Utah.
 *     it was then ported to the i386 by William Jolitz of UUNET
 *     Technologies, Inc.   Then Charles M. Hannum of the NetBSD
 *     project fixed some bugs and provided some speed ups.
 *
 * [2] the FreeBSD i386 pmap.   this pmap seems to be the
 *     Hibler/Jolitz pmap, as modified for FreeBSD by John S. Dyson
 *     and David Greenman.
 *
 * [3] the Mach pmap.   this pmap, from CMU, seems to have migrated
 *     between several processors.   the VAX version was done by
 *     Avadis Tevanian, Jr., and Michael Wayne Young.    the i386
 *     version was done by Lance Berc, Mike Kupfer, Bob Baron,
 *     David Golub, and Richard Draves.    the alpha version was
 *     done by Alessandro Forin (CMU/Mach) and Chris Demetriou
 *     (NetBSD/alpha).
 */
/*
 * PAE support
 * Michael Shalayeff <mickey@lucifier.net>
 *
 * This module implements PAE mode for i386.
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/user.h>
#include <sys/kernel.h>
#include <sys/mutex.h>

#include <uvm/uvm.h>

#include <machine/cpu.h>
#include <machine/specialreg.h>
#include <machine/gdt.h>

#include <dev/isa/isareg.h>
#include <sys/msgbuf.h>
#include <stand/boot/bootarg.h>

/*
 * this file contains the code for the "pmap module."   the module's
 * job is to manage the hardware's virtual to physical address mappings.
 * note that there are two levels of mapping in the VM system:
 *
 *  [1] the upper layer of the VM system uses vm_map's and vm_map_entry's
 *      to map ranges of virtual address space to objects/files.  for
 *      example, the vm_map may say: "map VA 0x1000 to 0x22000 read-only
 *      to the file /bin/ls starting at offset zero."   note that
 *      the upper layer mapping is not concerned with how individual
 *      vm_pages are mapped.
 *
 *  [2] the lower layer of the VM system (the pmap) maintains the mappings
 *      from virtual addresses.   it is concerned with which vm_page is
 *      mapped where.   for example, when you run /bin/ls and start
 *      at page 0x1000 the fault routine may lookup the correct page
 *      of the /bin/ls file and then ask the pmap layer to establish
 *      a mapping for it.
 *
 * note that information in the lower layer of the VM system can be
 * thrown away since it can easily be reconstructed from the info
 * in the upper layer.
 *
 * data structures we use include:
 *
 *  - struct pmap: describes the address space of one thread
 *  - struct pv_entry: describes one <PMAP,VA> mapping of a PA
 *  - struct pv_head: there is one pv_head per managed page of
 *	physical memory.   the pv_head points to a list of pv_entry
 *	structures which describe all the <PMAP,VA> pairs that this
 *      page is mapped in.    this is critical for page based operations
 *      such as pmap_page_protect() [change protection on _all_ mappings
 *      of a page]
 */
/*
 * i386 PAE hardware Page Tables structure:
 *
 * the i386 PAE Page Table is a three-level PT which maps 4GB of VA.
 * the pagesize is 4K (4096 [0x1000] bytes) or 2MB.
 *
 * the first level table is called "page directory index" and consists
 * of 4 page directory index entries (PDIE) each 64 bits in size.
 *
 * the second level table is called a "page directory" and it contains
 * 512 page directory entries (PDEs).   each PDE is
 * 8 bytes (a long long), so a PD fits in a single 4K page.   this page is
 * the page directory page (PDP).  each PDE in a PDP maps 1GB of space
 * (512 * 2MB = 1GB).   a PDE contains the physical address of the
 * second level table: the page table.   or, if 2MB pages are being used,
 * then the PDE contains the PA of the 2MB page being mapped.
 *
 * a page table consists of 512 page table entries (PTEs).  each PTE is
 * 8 bytes (a long long), so a page table also fits in a single 4K page.
 * a 4K page being used as a page table is called a page table page (PTP).
 * each PTE in a PTP maps one 4K page (512 * 4K = 2MB).   a PTE contains
 * the physical address of the page it maps and some flag bits (described
 * below).
 *
 * the processor has a special register, "cr3", which points to the
 * the PDP which is currently controlling the mappings of the virtual
 * address space.
 *
 * the following picture shows the translation process for a 4K page:
 *
 * %cr3 register [PA of PDPT]
 *  |
 *  |  bits <31-30> of VA
 *  |  index the DPE (0-3)
 *  |        |
 *  v        v
 *  +-----------+
 *  |  PDP Ptr  |
 *  | 4 entries |
 *  +-----------+
 *       |
 *    PA of PDP
 *       |
 *       |
 *       |  bits <29-21> of VA       bits <20-12> of VA   bits <11-0>
 *       |  index the PDP (0 - 512)  index the PTP        are the page offset
 *       |        |                         |                    |
 *       |        v                         |                    |
 *       +-->+---------+                    |                    |
 *           | PD Page |    PA of           v                    |
 *           |         |-----PTP----->+------------+             |
 *           | 512 PDE |              | page table |--PTE--+     |
 *           | entries |              | (aka PTP)  |       |     |
 *           +---------+              |  512 PTE   |       |     |
 *                                    |  entries   |       |     |
 *                                    +------------+       |     |
 *                                                         |     |
 *                                              bits <35-12>   bits <11-0>
 *                                               p h y s i c a l  a d d r
 *
 * the i386 caches PTEs in a TLB.   it is important to flush out old
 * TLB mappings when making a change to a mapping.   writing to the
 * %cr3 will flush the entire TLB.    newer processors also have an
 * instruction that will invalidate the mapping of a single page (which
 * is useful if you are changing a single mapping because it preserves
 * all the cached TLB entries).
 *
 * as shows, bits 31-12 of the PTE contain PA of the page being mapped.
 * the rest of the PTE is defined as follows:
 *   bit#	name	use
 *   63		NX	no-execute bit (0=ITLB, 1=DTLB), optional
 *   11		n/a	available for OS use, hardware ignores it
 *   10		n/a	available for OS use, hardware ignores it
 *   9		n/a	available for OS use, hardware ignores it
 *   8		G	global bit (see discussion below)
 *   7		PS	page size [for PDEs] (0=4k, 1=4M <if supported>)
 *   6		D	dirty (modified) page
 *   5		A	accessed (referenced) page
 *   4		PCD	cache disable
 *   3		PWT	prevent write through (cache)
 *   2		U/S	user/supervisor bit (0=supervisor only, 1=both u&s)
 *   1		R/W	read/write bit (0=read only, 1=read-write)
 *   0		P	present (valid)
 *
 * notes:
 *  - on the i386 the R/W bit is ignored if processor is in supervisor
 *    state (bug!)
 *  - PS is only supported on newer processors
 *  - PTEs with the G bit are global in the sense that they are not
 *    flushed from the TLB when %cr3 is written (to flush, use the
 *    "flush single page" instruction).   this is only supported on
 *    newer processors.    this bit can be used to keep the kernel's
 *    TLB entries around while context switching.   since the kernel
 *    is mapped into all processes at the same place it does not make
 *    sense to flush these entries when switching from one process'
 *    pmap to another.
 */
/*
 * A pmap describes a process' 4GB virtual address space.  This
 * virtual address space can be broken up into 2048 2MB regions which
 * are described by PDEs in the PDP.  The PDEs are defined as follows:
 *
 * Ranges are inclusive -> exclusive, just like vm_map_entry start/end.
 * The following assumes that KERNBASE is 0xd0000000.
 *
 * PDE#s	VA range		Usage
 * 0->1660	0x0 -> 0xcf800000	user address space, note that the
 *					max user address is 0xcfbfe000
 *					the final two pages in the last 4MB
 *					used to be reserved for the UAREA
 *					but now are no longer used.
 * 1660		0xcf800000->		recursive mapping of PDP (used for
 *			0xd0000000	linear mapping of PTPs).
 * 1664->2044	0xd0000000->		kernel address space (constant
 *			0xff800000	across all pmaps/processes).
 * 2044		0xff800000->		"alternate" recursive PDP mapping
 *			<end>		(for other pmaps).
 *
 *
 * Note: A recursive PDP mapping provides a way to map all the PTEs for
 * a 4GB address space into a linear chunk of virtual memory.  In other
 * words, the PTE for page 0 is the first 8b mapped into the 2MB recursive
 * area.  The PTE for page 1 is the second 8b.  The very last 8b in the
 * 2MB range is the PTE that maps VA 0xffffe000 (the last page in a 4GB
 * address).
 *
 * All pmaps' PDs must have the same values in slots 1660->2043 so that
 * the kernel is always mapped in every process.  These values are loaded
 * into the PD at pmap creation time.
 *
 * At any one time only one pmap can be active on a processor.  This is
 * the pmap whose PDP is pointed to by processor register %cr3.  This pmap
 * will have all its PTEs mapped into memory at the recursive mapping
 * point (slots #1660-3 as show above).  When the pmap code wants to find the
 * PTE for a virtual address, all it has to do is the following:
 *
 * Address of PTE = (1660 * 2MB) + (VA / NBPG) * sizeof(pt_entry_t)
 *                = 0xcf800000 + (VA / 4096) * 8
 *
 * What happens if the pmap layer is asked to perform an operation
 * on a pmap that is not the one which is currently active?  In that
 * case we take the PA of the PDP of the non-active pmap and put it in
 * slots 2044-7 of the active pmap.  This causes the non-active pmap's
 * PTEs to get mapped in the final 4MB of the 4GB address space
 * (e.g. starting at 0xffc00000).
 *
 * The following figure shows the effects of the recursive PDP mapping:
 *
 *   PDP (%cr3->PDPTP)
 *   +----+
 *   |   0| -> PTP#0 that maps VA 0x0 -> 0x200000
 *   |    |
 *   |    |
 *   |1660| -> points back to PDP (%cr3) mapping VA 0xcf800000 -> 0xd0000000
 *   |1661|    (PDP is 4 pages)
 *   |1662|
 *   |1663|
 *   |1664| -> first kernel PTP (maps 0xd0000000 -> 0xe0200000)
 *   |    |
 *   |2044| -> points to alternate pmap's PDP (maps 0xff800000 -> end)
 *   |2045|
 *   |2046|
 *   |2047|
 *   +----+
 *
 * Note that the PDE#1660 VA (0xcf8033e0) is defined as "PTE_BASE".
 * Note that the PDE#2044 VA (0xff803fe0) is defined as "APTE_BASE".
 *
 * Starting at VA 0xcf8033e0 the current active PDPs (%cr3) act as a
 * PDPTP and reference four consecutively mapped pages:
 *
 * PTP#1660-3 == PDP(%cr3) => maps VA 0xcf800000 -> 0xd0000000
 *   +----+
 *   |   0| -> maps the contents of PTP#0 at VA 0xcf800000->0xcf801000
 *   |    |
 *   |    |
 *   |1660| -> maps the contents of PTP#1660 (the PDP) at VA 0xcfe7c000
 *   |1661|
 *   |1662|
 *   |1663|
 *   |1664| -> maps the contents of first kernel PTP
 *   |    |
 *   |2047|
 *   +----+
 *
 * Note that mapping of the PDP at PTP#1660's VA (0xcfe7c000) is
 * defined as "PDP_BASE".... within that mapping there are two
 * defines:
 *   "PDP_PDE" (0xcfe7f3e0) is the VA of the PDE in the PDP
 *      which points back to itself.
 *   "APDP_PDE" (0xfff02fe0) is the VA of the PDE in the PDP which
 *      establishes the recursive mapping of the alternate pmap.
 *      To set the alternate PDP, one just has to put the correct
 *	PA info in *APDP_PDE.
 *
 * Note that in the APTE_BASE space, the APDP appears at VA
 * "APDP_BASE" (0xffffc000).
 *
 * unfortunately, we cannot use recursive PDPT from the page tables
 * because cr3 is only 32 bits wide.
 *
 */
#define PG_FRAME	0xffffff000ULL	/* page frame mask */
#define PG_LGFRAME	0xfffe00000ULL	/* large (2M) page frame mask */

/*
 * Redefine the PDSHIFT and NBPD macros for PAE
 */
#undef PDSHIFT
#define PDSHIFT		21		/* page directory address shift */
#undef NBPD
#define NBPD		(1U << PDSHIFT)	/* # bytes mapped by PD (2MB) */

#undef PDSLOT_PTE
#define PDSLOT_PTE	(1660U)	/* 1660: for recursive PDP map */
#undef PDSLOT_KERN
#define PDSLOT_KERN	(1664U)	/* 1664: start of kernel space */
#undef PDSLOT_APTE
#define PDSLOT_APTE	(2044U)	/* 2044: alternative recursive slot */

/*
 * The following defines give the virtual addresses of various MMU
 * data structures:
 * PTE_BASE and APTE_BASE: the base VA of the linear PTE mappings
 * PTD_BASE and APTD_BASE: the base VA of the recursive mapping of the PTD
 * PDP_PDE and APDP_PDE: the VA of the PDE that points back to the PDP/APDP
 */
#define PTE_BASE	((pt_entry_t *) (PDSLOT_PTE * NBPD))
#define APTE_BASE	((pt_entry_t *) (PDSLOT_APTE * NBPD))
#define PDP_BASE ((pd_entry_t *)(((char *)PTE_BASE) + (PDSLOT_PTE * NBPG)))
#define APDP_BASE ((pd_entry_t *)(((char *)APTE_BASE) + (PDSLOT_APTE * NBPG)))
#define PDP_PDE		(PDP_BASE + PDSLOT_PTE)
#define APDP_PDE	(PDP_BASE + PDSLOT_APTE)

/*
 * pdei/ptei: generate index into PDP/PTP from a VA
 */
#define PD_MASK		0xffe00000	/* page directory address bits */
#define PT_MASK		0x001ff000	/* page table address bits */
#define pdei(VA)	(((VA) & PD_MASK) >> PDSHIFT)
#define ptei(VA)	(((VA) & PT_MASK) >> PGSHIFT)

/*
 * Mach derived conversion macros
 */
#define i386_round_pdr(x)	((((unsigned)(x)) + ~PD_MASK) & PD_MASK)

/*
 * various address macros
 *
 *  vtopte: return a pointer to the PTE mapping a VA
 */
#define vtopte(VA)	(PTE_BASE + atop((vaddr_t)VA))

/*
 * PTP macros:
 *   A PTP's index is the PD index of the PDE that points to it.
 *   A PTP's offset is the byte-offset in the PTE space that this PTP is at.
 *   A PTP's VA is the first VA mapped by that PTP.
 *
 * Note that NBPG == number of bytes in a PTP (4096 bytes == 512 entries)
 *           NBPD == number of bytes a PTP can map (2MB)
 */

#define ptp_i2o(I)	((I) * NBPG)	/* index => offset */
#define ptp_o2i(O)	((O) / NBPG)	/* offset => index */
#define ptp_i2v(I)	((I) * NBPD)	/* index => VA */
#define ptp_v2i(V)	((V) / NBPD)	/* VA => index (same as pdei) */

/*
 * Access PD and PT
 */
#define PDE(pm,i)	(((pd_entry_t *)(pm)->pm_pdir)[(i)])

/*
 * here we define the data types for PDEs and PTEs for PAE
 */
typedef u_int64_t pd_entry_t;		/* PDE */
typedef u_int64_t pt_entry_t;		/* PTE */

#define PG_NX	0x8000000000000000ULL	/* execute-disable */

/*
 * Number of PTEs per cache line. 8 byte pte, 64-byte cache line
 * Used to avoid false sharing of cache lines.
 */
#define NPTECL			8

/*
 * other data structures
 */

extern u_int32_t protection_codes[];	/* maps MI prot to i386 prot code */
extern boolean_t pmap_initialized;	/* pmap_init done yet? */

/*
 * MULTIPROCESSOR: special VA's/ PTE's are actually allocated inside a
 * MAXCPUS*NPTECL array of PTE's, to avoid cache line thrashing
 * due to false sharing.
 */

#ifdef MULTIPROCESSOR
#define PTESLEW(pte, id) ((pte)+(id)*NPTECL)
#define VASLEW(va,id) ((va)+(id)*NPTECL*NBPG)
#else
#define PTESLEW(pte, id) (pte)
#define VASLEW(va,id) (va)
#endif

/*
 * special VAs and the PTEs that map them
 */

static pt_entry_t *csrc_pte, *cdst_pte, *zero_pte, *ptp_pte, *flsh_pte;
extern caddr_t pmap_csrcp, pmap_cdstp, pmap_zerop, pmap_ptpp, pmap_flshp;

extern int pmap_pg_g;
extern int pmap_pg_wc;
extern struct pmap_head pmaps;

/*
 * local prototypes
 */
struct vm_page	*pmap_alloc_ptp_pae(struct pmap *, int, pt_entry_t);
struct vm_page	*pmap_get_ptp_pae(struct pmap *, int);
void		 pmap_drop_ptp_pae(struct pmap *, vaddr_t, struct vm_page *,
    pt_entry_t *);
pt_entry_t	*pmap_map_ptes_pae(struct pmap *);
void		 pmap_unmap_ptes_pae(struct pmap *);
void		 pmap_do_remove_pae(struct pmap *, vaddr_t, vaddr_t, int);
void		 pmap_remove_ptes_pae(struct pmap *, struct vm_page *,
		     vaddr_t, vaddr_t, vaddr_t, int, struct pv_entry **);
void		 pmap_sync_flags_pte_pae(struct vm_page *, pt_entry_t);

static __inline u_int
pmap_pte2flags(pt_entry_t pte)
{
	return (((pte & PG_U) ? PG_PMAP_REF : 0) |
	    ((pte & PG_M) ? PG_PMAP_MOD : 0));
}

void
pmap_sync_flags_pte_pae(struct vm_page *pg, pt_entry_t pte)
{
	if (pte & (PG_U|PG_M)) {
		atomic_setbits_int(&pg->pg_flags, pmap_pte2flags(pte));
	}
}

/*
 * pmap_map_ptes: map a pmap's PTEs into KVM and lock them in
 *
 * => we lock enough pmaps to keep things locked in
 * => must be undone with pmap_unmap_ptes before returning
 */

pt_entry_t *
pmap_map_ptes_pae(struct pmap *pmap)
{
	pd_entry_t opde;

	/* the kernel's pmap is always accessible */
	if (pmap == pmap_kernel()) {
		return(PTE_BASE);
	}

	mtx_enter(&pmap->pm_mtx);

	/* if curpmap then we are always mapped */
	if (pmap_is_curpmap(pmap)) {
		return(PTE_BASE);
	}

	mtx_enter(&curcpu()->ci_curpmap->pm_apte_mtx);

	/* need to load a new alternate pt space into curpmap? */
	opde = *APDP_PDE;
#if defined(MULTIPROCESSOR) && defined(DIAGNOSTIC)
	if (pmap_valid_entry(opde))
		panic("pmap_map_ptes_pae: APTE valid");
#endif
	if (!pmap_valid_entry(opde) || (opde & PG_FRAME) != pmap->pm_pdidx[0]) {
		APDP_PDE[0] = pmap->pm_pdidx[0] | PG_RW | PG_V | PG_U | PG_M;
		APDP_PDE[1] = pmap->pm_pdidx[1] | PG_RW | PG_V | PG_U | PG_M;
		APDP_PDE[2] = pmap->pm_pdidx[2] | PG_RW | PG_V | PG_U | PG_M;
		APDP_PDE[3] = pmap->pm_pdidx[3] | PG_RW | PG_V | PG_U | PG_M;
		if (pmap_valid_entry(opde))
			pmap_apte_flush();
	}
	return(APTE_BASE);
}

/*
 * pmap_unmap_ptes: unlock the PTE mapping of "pmap"
 */

void
pmap_unmap_ptes_pae(struct pmap *pmap)
{
	if (pmap == pmap_kernel())
		return;

	if (!pmap_is_curpmap(pmap)) {
#if defined(MULTIPROCESSOR)
		APDP_PDE[0] = 0;
		APDP_PDE[1] = 0;
		APDP_PDE[2] = 0;
		APDP_PDE[3] = 0;
		pmap_apte_flush();
#endif
		mtx_leave(&curcpu()->ci_curpmap->pm_apte_mtx);
	}

	mtx_leave(&pmap->pm_mtx);
}

u_int32_t
pmap_pte_set_pae(vaddr_t va, paddr_t pa, u_int32_t bits)
{
	pt_entry_t pte, *ptep = vtopte(va);

	pa &= PMAP_PA_MASK;

	pte = i386_atomic_testset_uq(ptep, pa | bits);  /* zap! */
	return (pte & ~PG_FRAME);
}

u_int32_t
pmap_pte_setbits_pae(vaddr_t va, u_int32_t set, u_int32_t clr)
{
	pt_entry_t *ptep = vtopte(va);
	pt_entry_t pte = *ptep;

	i386_atomic_testset_uq(ptep, (pte | set) & ~(pt_entry_t)clr);
	return (pte & ~PG_FRAME);
}

u_int32_t
pmap_pte_bits_pae(vaddr_t va)
{
	pt_entry_t *ptep = vtopte(va);

	return (*ptep & ~PG_FRAME);
}

paddr_t
pmap_pte_paddr_pae(vaddr_t va)
{
	pt_entry_t *ptep = vtopte(va);

	return (*ptep & PG_FRAME);
}

/*
 * Switch over to PAE page tables
 */
void
pmap_bootstrap_pae(void)
{
	extern int cpu_pae, nkpde;
	struct pmap *kpm = pmap_kernel();
	struct vm_page *ptp;
	paddr_t ptaddr;
	u_int32_t bits;
	vaddr_t va, eva;

	if ((cpu_feature & CPUID_PAE) == 0 ||
	    (ecpu_feature & CPUID_NXE) == 0)
		return;

	cpu_pae = 1;

	va = (vaddr_t)kpm->pm_pdir;
	kpm->pm_pdidx[0] = (va + 0*NBPG - KERNBASE) | PG_V;
	kpm->pm_pdidx[1] = (va + 1*NBPG - KERNBASE) | PG_V;
	kpm->pm_pdidx[2] = (va + 2*NBPG - KERNBASE) | PG_V;
	kpm->pm_pdidx[3] = (va + 3*NBPG - KERNBASE) | PG_V;
	/* map pde recursively into itself */
	PDE(kpm, PDSLOT_PTE+0) = kpm->pm_pdidx[0] | PG_KW | PG_M | PG_U;
	PDE(kpm, PDSLOT_PTE+1) = kpm->pm_pdidx[1] | PG_KW | PG_M | PG_U;
	PDE(kpm, PDSLOT_PTE+2) = kpm->pm_pdidx[2] | PG_KW | PG_M | PG_U;
	PDE(kpm, PDSLOT_PTE+3) = kpm->pm_pdidx[3] | PG_KW | PG_M | PG_U;

	/* transfer all kernel mappings over into pae tables */
	for (va = KERNBASE, eva = va + (nkpde << 22);
	    va < eva; va += PAGE_SIZE) {
		if (!pmap_valid_entry(PDE(kpm, pdei(va)))) {
			ptp = uvm_pagealloc(&kpm->pm_obj, va, NULL,
			    UVM_PGA_ZERO);
			ptaddr = VM_PAGE_TO_PHYS(ptp);
			PDE(kpm, pdei(va)) = ptaddr | PG_KW | PG_V |
			    PG_U | PG_M;
			pmap_pte_set_86((vaddr_t)vtopte(va),
			    ptaddr, PG_KW | PG_V | PG_U | PG_M);

			/* count PTP as resident */
			kpm->pm_stats.resident_count++;
		}
		bits = pmap_pte_bits_86(va) | pmap_pg_g;
		if (pmap_valid_entry(bits))
			pmap_pte_set_pae(va, pmap_pte_paddr_86(va), bits);
	}

	if (!cpu_paenable(&kpm->pm_pdidx[0])) {
		extern struct user *proc0paddr;

		proc0paddr->u_pcb.pcb_cr3 = kpm->pm_pdirpa =
		    (vaddr_t)kpm - KERNBASE;
		kpm->pm_pdirsize = 4 * NBPG;

		csrc_pte = vtopte(pmap_csrcp);
		cdst_pte = vtopte(pmap_cdstp);
		zero_pte = vtopte(pmap_zerop);
		ptp_pte = vtopte(pmap_ptpp);
		flsh_pte = vtopte(pmap_flshp);

		nkpde *= 2;
		nkptp_max = 2048 - PDSLOT_KERN - 4;

		pmap_pte_set_p = pmap_pte_set_pae;
		pmap_pte_setbits_p = pmap_pte_setbits_pae;
		pmap_pte_bits_p = pmap_pte_bits_pae;
		pmap_pte_paddr_p = pmap_pte_paddr_pae;
		pmap_clear_attrs_p = pmap_clear_attrs_pae;
		pmap_enter_p = pmap_enter_pae;
		pmap_extract_p = pmap_extract_pae;
		pmap_growkernel_p = pmap_growkernel_pae;
		pmap_page_remove_p = pmap_page_remove_pae;
		pmap_do_remove_p = pmap_do_remove_pae;
		pmap_test_attrs_p = pmap_test_attrs_pae;
		pmap_unwire_p = pmap_unwire_pae;
		pmap_write_protect_p = pmap_write_protect_pae;
		pmap_pinit_pd_p = pmap_pinit_pd_pae;
		pmap_zero_phys_p = pmap_zero_phys_pae;
		pmap_zero_page_uncached_p = pmap_zero_page_uncached_pae;
		pmap_copy_page_p = pmap_copy_page_pae;

		bzero((void *)kpm->pm_pdir + 8, (PDSLOT_PTE-1) * 8);
		/* TODO also reclaim old PDPs */
	}
}

/*
 * p t p   f u n c t i o n s
 */

/*
 * pmap_alloc_ptp: allocate a PTP for a PMAP
 *
 * => pmap should already be locked by caller
 * => we use the ptp's wire_count to count the number of active mappings
 *	in the PTP (we start it at one to prevent any chance this PTP
 *	will ever leak onto the active/inactive queues)
 * => we should not be holding any pv_head locks (in case we are forced
 *	to call pmap_steal_ptp())
 * => we may need to lock pv_head's if we have to steal a PTP
 * => just_try: true if we want a PTP, but not enough to steal one
 * 	from another pmap (e.g. during optional functions like pmap_copy)
 */

struct vm_page *
pmap_alloc_ptp_pae(struct pmap *pmap, int pde_index, pt_entry_t pde_flags)
{
	struct vm_page *ptp;

	ptp = uvm_pagealloc(&pmap->pm_obj, ptp_i2o(pde_index), NULL,
			    UVM_PGA_USERESERVE|UVM_PGA_ZERO);
	if (ptp == NULL)
		return (NULL);

	/* got one! */
	atomic_clearbits_int(&ptp->pg_flags, PG_BUSY);
	ptp->wire_count = 1;	/* no mappings yet */
	PDE(pmap, pde_index) = (pd_entry_t)(VM_PAGE_TO_PHYS(ptp) |
	    PG_RW | PG_V | PG_M | PG_U | pde_flags);
	pmap->pm_stats.resident_count++;	/* count PTP as resident */
	pmap->pm_ptphint = ptp;
	return(ptp);
}

/*
 * pmap_get_ptp: get a PTP (if there isn't one, allocate a new one)
 *
 * => pmap should NOT be pmap_kernel()
 * => pmap should be locked
 */

struct vm_page *
pmap_get_ptp_pae(struct pmap *pmap, int pde_index)
{
	struct vm_page *ptp;

	if (pmap_valid_entry(PDE(pmap, pde_index))) {
		/* valid... check hint (saves us a PA->PG lookup) */
		if (pmap->pm_ptphint &&
		    (PDE(pmap, pde_index) & PG_FRAME) ==
		    VM_PAGE_TO_PHYS(pmap->pm_ptphint))
			return(pmap->pm_ptphint);

		ptp = uvm_pagelookup(&pmap->pm_obj, ptp_i2o(pde_index));
#ifdef DIAGNOSTIC
		if (ptp == NULL)
			panic("pmap_get_ptp_pae: unmanaged user PTP");
#endif
		pmap->pm_ptphint = ptp;
		return(ptp);
	}

	/* allocate a new PTP (updates ptphint) */
	return (pmap_alloc_ptp_pae(pmap, pde_index, PG_u));
}

void
pmap_drop_ptp_pae(struct pmap *pm, vaddr_t va, struct vm_page *ptp,
    pt_entry_t *ptes)
{
	i386_atomic_testset_uq(&PDE(pm, pdei(va)), 0);
	pmap_tlb_shootpage(curcpu()->ci_curpmap, ((vaddr_t)ptes) + ptp->offset);
#ifdef MULTIPROCESSOR
	/*
	 * Always shoot down the other pmap's
	 * self-mapping of the PTP.
	 */
	pmap_tlb_shootpage(pm, ((vaddr_t)PTE_BASE) + ptp->offset);
#endif
	pm->pm_stats.resident_count--;
	/* update hint */
	if (pm->pm_ptphint == ptp)
		pm->pm_ptphint = RB_ROOT(&pm->pm_obj.memt);
	ptp->wire_count = 0;
	/* Postpone free to after shootdown. */
	uvm_pagerealloc(ptp, NULL, 0);
}

/*
 * pmap_pinit_pd: given a freshly allocated pmap structure, give it a PD
 */
void
pmap_pinit_pd_pae(struct pmap *pmap)
{
	extern int nkpde;
	vaddr_t va;
	paddr_t pdidx[4];

	/* allocate PDP */
	pmap->pm_pdir = uvm_km_alloc(kernel_map, 4 * NBPG);
	if (pmap->pm_pdir == 0)
		panic("pmap_pinit_pd_pae: kernel_map out of virtual space!");
	/* page index is in the pmap! */
	pmap_extract(pmap_kernel(), (vaddr_t)pmap, &pmap->pm_pdirpa);
	va = (vaddr_t)pmap->pm_pdir;
	pmap_extract(pmap_kernel(), va + 0*NBPG, &pdidx[0]);
	pmap_extract(pmap_kernel(), va + 1*NBPG, &pdidx[1]);
	pmap_extract(pmap_kernel(), va + 2*NBPG, &pdidx[2]);
	pmap_extract(pmap_kernel(), va + 3*NBPG, &pdidx[3]);
	pmap->pm_pdidx[0] = (uint64_t)pdidx[0];
	pmap->pm_pdidx[1] = (uint64_t)pdidx[1];
	pmap->pm_pdidx[2] = (uint64_t)pdidx[2];
	pmap->pm_pdidx[3] = (uint64_t)pdidx[3];
	pmap->pm_pdidx[0] |= PG_V;
	pmap->pm_pdidx[1] |= PG_V;
	pmap->pm_pdidx[2] |= PG_V;
	pmap->pm_pdidx[3] |= PG_V;
	pmap->pm_pdirsize = 4 * NBPG;

	/* init PDP */
	/* zero init area */
	bzero((void *)pmap->pm_pdir, PDSLOT_PTE * sizeof(pd_entry_t));
	/* put in recursive PDE to map the PTEs */
	PDE(pmap, PDSLOT_PTE+0) = pmap->pm_pdidx[0] | PG_KW | PG_U |
	    PG_M | PG_V;
	PDE(pmap, PDSLOT_PTE+1) = pmap->pm_pdidx[1] | PG_KW | PG_U |
	    PG_M | PG_V;
	PDE(pmap, PDSLOT_PTE+2) = pmap->pm_pdidx[2] | PG_KW | PG_U |
	    PG_M | PG_V;
	PDE(pmap, PDSLOT_PTE+3) = pmap->pm_pdidx[3] | PG_KW | PG_U |
	    PG_M | PG_V;

	/*
	 * we need to lock pmaps_lock to prevent nkpde from changing on
	 * us.   note that there is no need to splvm to protect us from
	 * malloc since malloc allocates out of a submap and we should have
	 * already allocated kernel PTPs to cover the range...
	 */
	/* put in kernel VM PDEs */
	bcopy(&PDP_BASE[PDSLOT_KERN], &PDE(pmap, PDSLOT_KERN),
	       nkpde * sizeof(pd_entry_t));
	/* zero the rest */
	bzero(&PDE(pmap, PDSLOT_KERN + nkpde), pmap->pm_pdirsize -
	    ((PDSLOT_KERN + nkpde) * sizeof(pd_entry_t)));
	LIST_INSERT_HEAD(&pmaps, pmap, pm_list);
}

/*
 * some misc. functions
 */

/*
 * pmap_extract: extract a PA for the given VA
 */

boolean_t
pmap_extract_pae(struct pmap *pmap, vaddr_t va, paddr_t *pap)
{
	pt_entry_t *ptes, pte;

	if (pmap_valid_entry(PDE(pmap, pdei(va)))) {
		ptes = pmap_map_ptes_pae(pmap);
		pte = ptes[atop(va)];
		pmap_unmap_ptes_pae(pmap);
		if (!pmap_valid_entry(pte))
			return (FALSE);
		if (pap != NULL)
			*pap = (pte & PG_FRAME) | (va & ~PG_FRAME);
		return (TRUE);
	}
	return (FALSE);
}

extern void (*pagezero)(void *, size_t);

/*
 * pmap_zero_phys: same as pmap_zero_page, but for use before vm_pages are
 * initialized.
 */
void
pmap_zero_phys_pae(paddr_t pa)
{
#ifdef MULTIPROCESSOR
	int id = cpu_number();
#endif
	pt_entry_t *zpte = PTESLEW(zero_pte, id);
	caddr_t zerova = VASLEW(pmap_zerop, id);

#ifdef DIAGNOSTIC
	if (*zpte)
		panic("pmap_zero_phys_pae: lock botch");
#endif

	*zpte = (pa & PG_FRAME) | PG_V | PG_RW;	/* map in */
	pmap_update_pg((vaddr_t)zerova);	/* flush TLB */
	pagezero(zerova, PAGE_SIZE);		/* zero */
	*zpte = 0;
}

/*
 * pmap_zero_page_uncached: the same, except uncached.
 */

boolean_t
pmap_zero_page_uncached_pae(paddr_t pa)
{
#ifdef MULTIPROCESSOR
	int id = cpu_number();
#endif
	pt_entry_t *zpte = PTESLEW(zero_pte, id);
	caddr_t zerova = VASLEW(pmap_zerop, id);

#ifdef DIAGNOSTIC
	if (*zpte)
		panic("pmap_zero_page_uncached_pae: lock botch");
#endif

	*zpte = (pa & PG_FRAME) | PG_V | PG_RW | PG_N;	/* map in */
	pmap_update_pg((vaddr_t)zerova);		/* flush TLB */
	pagezero(zerova, PAGE_SIZE);		/* zero */
	*zpte = 0;

	return (TRUE);
}

/*
 * pmap_copy_page: copy a page
 */

void
pmap_copy_page_pae(struct vm_page *srcpg, struct vm_page *dstpg)
{
	paddr_t srcpa = VM_PAGE_TO_PHYS(srcpg);
	paddr_t dstpa = VM_PAGE_TO_PHYS(dstpg);
#ifdef MULTIPROCESSOR
	int id = cpu_number();
#endif
	pt_entry_t *spte = PTESLEW(csrc_pte, id);
	pt_entry_t *dpte = PTESLEW(cdst_pte, id);
	caddr_t csrcva = VASLEW(pmap_csrcp, id);
	caddr_t cdstva = VASLEW(pmap_cdstp, id);

#ifdef DIAGNOSTIC
	if (*spte || *dpte)
		panic("pmap_copy_page_pae: lock botch");
#endif

	*spte = (srcpa & PG_FRAME) | PG_V | PG_RW;
	*dpte = (dstpa & PG_FRAME) | PG_V | PG_RW;
	pmap_update_2pg((vaddr_t)csrcva, (vaddr_t)cdstva);
	bcopy(csrcva, cdstva, PAGE_SIZE);
	*spte = *dpte = 0;
	pmap_update_2pg((vaddr_t)csrcva, (vaddr_t)cdstva);
}

/*
 * p m a p   r e m o v e   f u n c t i o n s
 *
 * functions that remove mappings
 */

/*
 * pmap_remove_ptes: remove PTEs from a PTP
 *
 * => must have proper locking on pmap_master_lock
 * => caller must hold pmap's lock
 * => PTP must be mapped into KVA
 * => PTP should be null if pmap == pmap_kernel()
*/

void
pmap_remove_ptes_pae(struct pmap *pmap, struct vm_page *ptp, vaddr_t ptpva,
    vaddr_t startva, vaddr_t endva, int flags, struct pv_entry **free_pvs)
{
	struct pv_entry *pve;
	pt_entry_t *pte = (pt_entry_t *) ptpva;
	struct vm_page *pg;
	pt_entry_t opte;

	/*
	 * note that ptpva points to the PTE that maps startva.   this may
	 * or may not be the first PTE in the PTP.
	 *
	 * we loop through the PTP while there are still PTEs to look at
	 * and the wire_count is greater than 1 (because we use the wire_count
	 * to keep track of the number of real PTEs in the PTP).
	 */

	for (/*null*/; startva < endva && (ptp == NULL || ptp->wire_count > 1)
			     ; pte++, startva += NBPG) {
		if (!pmap_valid_entry(*pte))
			continue;			/* VA not mapped */

		if ((flags & PMAP_REMOVE_SKIPWIRED) && (*pte & PG_W))
			continue;

		/* atomically save the old PTE and zero it */
		opte = i386_atomic_testset_uq(pte, 0);

		if (opte & PG_W)
			pmap->pm_stats.wired_count--;
		pmap->pm_stats.resident_count--;

		if (ptp)
			ptp->wire_count--;		/* dropping a PTE */

		/*
		 * Unnecessary work if not PG_PVLIST.
		 */
		pg = PHYS_TO_VM_PAGE(opte & PG_FRAME);

		/*
		 * if we are not on a pv list we are done.
		 */
		if ((opte & PG_PVLIST) == 0) {
#ifdef DIAGNOSTIC
			if (pg != NULL)
				panic("pmap_remove_ptes_pae: managed page "
				     "without PG_PVLIST for 0x%lx", startva);
#endif
			continue;
		}

#ifdef DIAGNOSTIC
		if (pg == NULL)
			panic("pmap_remove_ptes_pae: unmanaged page marked "
			      "PG_PVLIST, va = 0x%lx, pa = 0x%lx",
			      startva, (u_long)(opte & PG_FRAME));
#endif

		/* sync R/M bits */
		pmap_sync_flags_pte_pae(pg, opte);
		pve = pmap_remove_pv(pg, pmap, startva);
		if (pve) {
			pve->pv_next = *free_pvs;
			*free_pvs = pve;
		}

		/* end of "for" loop: time for next pte */
	}
}

/*
 * pmap_remove: top level mapping removal function
 *
 * => caller should not be holding any pmap locks
 */

void
pmap_do_remove_pae(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int flags)
{
	pt_entry_t *ptes;
	paddr_t ptppa;
	vaddr_t blkendva;
	struct vm_page *ptp;
	struct pv_entry *pve;
	struct pv_entry *free_pvs = NULL;
	TAILQ_HEAD(, vm_page) empty_ptps;
	int shootall;
	vaddr_t va;

	TAILQ_INIT(&empty_ptps);

	ptes = pmap_map_ptes_pae(pmap);	/* locks pmap */

	/*
	 * Decide if we want to shoot the whole tlb or just the range.
	 * Right now, we simply shoot everything when we remove more
	 * than 32 pages, but never in the kernel pmap. XXX - tune.
	 */
	if ((eva - sva > 32 * PAGE_SIZE) && pmap != pmap_kernel())
		shootall = 1;
	else
		shootall = 0;

	for (va = sva ; va < eva ; va = blkendva) {
		/* determine range of block */
		blkendva = i386_round_pdr(va + 1);
		if (blkendva > eva)
			blkendva = eva;

		/*
		 * XXXCDC: our PTE mappings should never be removed
		 * with pmap_remove!  if we allow this (and why would
		 * we?) then we end up freeing the pmap's page
		 * directory page (PDP) before we are finished using
		 * it when we hit in in the recursive mapping.  this
		 * is BAD.
		 *
		 * long term solution is to move the PTEs out of user
		 * address space.  and into kernel address space (up
		 * with APTE).  then we can set VM_MAXUSER_ADDRESS to
		 * be VM_MAX_ADDRESS.
		 */

		if (pdei(va) >= PDSLOT_PTE && pdei(va) <= (PDSLOT_PTE + 3))
			/* XXXCDC: ugly hack to avoid freeing PDP here */
			continue;

		if (!pmap_valid_entry(PDE(pmap, pdei(va))))
			/* valid block? */
			continue;

		/* PA of the PTP */
		ptppa = PDE(pmap, pdei(va)) & PG_FRAME;

		/* get PTP if non-kernel mapping */
		if (pmap == pmap_kernel()) {
			/* we never free kernel PTPs */
			ptp = NULL;
		} else {
			if (pmap->pm_ptphint &&
			    VM_PAGE_TO_PHYS(pmap->pm_ptphint) == ptppa) {
				ptp = pmap->pm_ptphint;
			} else {
				ptp = PHYS_TO_VM_PAGE(ptppa);
#ifdef DIAGNOSTIC
				if (ptp == NULL)
					panic("pmap_do_remove_pae: unmanaged "
					      "PTP detected");
#endif
			}
		}

		pmap_remove_ptes_pae(pmap, ptp, (vaddr_t)&ptes[atop(va)],
		    va, blkendva, flags, &free_pvs);

		/* If PTP is no longer being used, free it. */
		if (ptp && ptp->wire_count <= 1) {
			pmap_drop_ptp_pae(pmap, va, ptp, ptes);
			TAILQ_INSERT_TAIL(&empty_ptps, ptp, pageq);
		}

		if (!shootall)
			pmap_tlb_shootrange(pmap, va, blkendva);
	}

	if (shootall)
		pmap_tlb_shoottlb();

	pmap_unmap_ptes_pae(pmap);
	pmap_tlb_shootwait();

	while ((pve = free_pvs) != NULL) {
		free_pvs = pve->pv_next;
		pmap_free_pv(pmap, pve);
	}

	while ((ptp = TAILQ_FIRST(&empty_ptps)) != NULL) {
		TAILQ_REMOVE(&empty_ptps, ptp, pageq);
		uvm_pagefree(ptp);
	}
}

/*
 * pmap_page_remove: remove a managed vm_page from all pmaps that map it
 *
 * => R/M bits are sync'd back to attrs
 */

void
pmap_page_remove_pae(struct vm_page *pg)
{
	struct pv_entry *pve;
	pt_entry_t *ptes, opte;
	TAILQ_HEAD(, vm_page) empty_ptps;
	struct vm_page *ptp;

	if (pg->mdpage.pv_list == NULL)
		return;

	TAILQ_INIT(&empty_ptps);

	mtx_enter(&pg->mdpage.pv_mtx);
	while ((pve = pg->mdpage.pv_list) != NULL) {
		pg->mdpage.pv_list = pve->pv_next;
		pmap_reference(pve->pv_pmap);
		mtx_leave(&pg->mdpage.pv_mtx);

		ptes = pmap_map_ptes_pae(pve->pv_pmap);	/* locks pmap */

#ifdef DIAGNOSTIC
		if (pve->pv_ptp && (PDE(pve->pv_pmap, pdei(pve->pv_va)) &
				    PG_FRAME)
		    != VM_PAGE_TO_PHYS(pve->pv_ptp)) {
			printf("pmap_page_remove_pae: pg=%p: va=%lx, "
				"pv_ptp=%p\n",
				pg, pve->pv_va, pve->pv_ptp);
			printf("pmap_page_remove_pae: PTP's phys addr: "
				"actual=%llx, recorded=%lx\n",
				(PDE(pve->pv_pmap, pdei(pve->pv_va)) &
				PG_FRAME), VM_PAGE_TO_PHYS(pve->pv_ptp));
			panic("pmap_page_remove_pae: mapped managed page has "
				"invalid pv_ptp field");
}
#endif
		opte = i386_atomic_testset_uq(&ptes[atop(pve->pv_va)], 0);

		if (opte & PG_W)
			pve->pv_pmap->pm_stats.wired_count--;
		pve->pv_pmap->pm_stats.resident_count--;

		/* sync R/M bits */
		pmap_sync_flags_pte_pae(pg, opte);

		/* update the PTP reference count.  free if last reference. */
		if (pve->pv_ptp && --pve->pv_ptp->wire_count <= 1) {
			pmap_drop_ptp_pae(pve->pv_pmap, pve->pv_va,
			    pve->pv_ptp, ptes);
			TAILQ_INSERT_TAIL(&empty_ptps, pve->pv_ptp, pageq);
		}

		pmap_tlb_shootpage(pve->pv_pmap, pve->pv_va);

		pmap_unmap_ptes_pae(pve->pv_pmap);	/* unlocks pmap */
		pmap_destroy(pve->pv_pmap);
		pmap_free_pv(NULL, pve);
		mtx_enter(&pg->mdpage.pv_mtx);
	}
	mtx_leave(&pg->mdpage.pv_mtx);

	pmap_tlb_shootwait();

	while ((ptp = TAILQ_FIRST(&empty_ptps)) != NULL) {
		TAILQ_REMOVE(&empty_ptps, ptp, pageq);
		uvm_pagefree(ptp);
	}
}

/*
 * p m a p   a t t r i b u t e  f u n c t i o n s
 * functions that test/change managed page's attributes
 * since a page can be mapped multiple times we must check each PTE that
 * maps it by going down the pv lists.
 */

/*
 * pmap_test_attrs: test a page's attributes
 *
 * => we set pv_head => pmap locking
 */

boolean_t
pmap_test_attrs_pae(struct vm_page *pg, int testbits)
{
	struct pv_entry *pve;
	pt_entry_t *ptes, pte;
	u_long mybits, testflags;
	paddr_t ptppa;

	testflags = pmap_pte2flags(testbits);

	if (pg->pg_flags & testflags)
		return (TRUE);

	mybits = 0;
	mtx_enter(&pg->mdpage.pv_mtx);
	for (pve = pg->mdpage.pv_list; pve != NULL && mybits == 0;
	    pve = pve->pv_next) {
		ptppa = PDE(pve->pv_pmap, pdei(pve->pv_va)) & PG_FRAME;
		ptes = (pt_entry_t *)pmap_tmpmap_pa(ptppa);
		pte = ptes[ptei(pve->pv_va)];
		pmap_tmpunmap_pa();
		mybits |= (pte & testbits);
	}
	mtx_leave(&pg->mdpage.pv_mtx);

	if (mybits == 0)
		return (FALSE);

	atomic_setbits_int(&pg->pg_flags, pmap_pte2flags(mybits));

	return (TRUE);
}

/*
 * pmap_clear_attrs: change a page's attributes
 *
 * => we return TRUE if we cleared one of the bits we were asked to
 */
boolean_t
pmap_clear_attrs_pae(struct vm_page *pg, int clearbits)
{
	struct pv_entry *pve;
	pt_entry_t *ptes, npte, opte;
	u_long clearflags;
	paddr_t ptppa;
	int result;

	clearflags = pmap_pte2flags(clearbits);

	result = pg->pg_flags & clearflags;
	if (result)
		atomic_clearbits_int(&pg->pg_flags, clearflags);

	mtx_enter(&pg->mdpage.pv_mtx);
	for (pve = pg->mdpage.pv_list; pve != NULL; pve = pve->pv_next) {
		ptppa = PDE(pve->pv_pmap, pdei(pve->pv_va)) & PG_FRAME;
		ptes = (pt_entry_t *)pmap_tmpmap_pa(ptppa);
#ifdef DIAGNOSTIC
		if (!pmap_valid_entry(PDE(pve->pv_pmap, pdei(pve->pv_va))))
			panic("pmap_clear_attrs_pae: mapping without PTP "
				"detected");
#endif

		opte = ptes[ptei(pve->pv_va)];
		if (opte & clearbits) {
			result = TRUE;
			npte = opte & ~clearbits;
			opte = i386_atomic_testset_uq(
			   &ptes[ptei(pve->pv_va)], npte);
			pmap_tlb_shootpage(pve->pv_pmap, pve->pv_va);
		}
		pmap_tmpunmap_pa();
	}
	mtx_leave(&pg->mdpage.pv_mtx);

	pmap_tlb_shootwait();

	return (result != 0);
}

/*
 * p m a p   p r o t e c t i o n   f u n c t i o n s
 */

/*
 * pmap_page_protect: change the protection of all recorded mappings
 *	of a managed page
 *
 * => NOTE: this is an inline function in pmap.h
 */

/* see pmap.h */

/*
 * pmap_protect: set the protection in of the pages in a pmap
 *
 * => NOTE: this is an inline function in pmap.h
 */

/* see pmap.h */

/*
 * pmap_write_protect: write-protect pages in a pmap
 */

void
pmap_write_protect_pae(struct pmap *pmap, vaddr_t sva, vaddr_t eva,
    vm_prot_t prot)
{
	pt_entry_t *ptes, *spte, *epte, npte, opte;
	vaddr_t blockend;
	u_int64_t md_prot;
	vaddr_t va;
	int shootall = 0;

	ptes = pmap_map_ptes_pae(pmap);		/* locks pmap */

	/* should be ok, but just in case ... */
	sva &= PG_FRAME;
	eva &= PG_FRAME;

	if ((eva - sva > 32 * PAGE_SIZE) && pmap != pmap_kernel())
		shootall = 1;

	for (va = sva; va < eva; va = blockend) {
		blockend = (va & PD_MASK) + NBPD;
		if (blockend > eva)
			blockend = eva;

		/*
		 * XXXCDC: our PTE mappings should never be write-protected!
		 *
		 * long term solution is to move the PTEs out of user
		 * address space.  and into kernel address space (up
		 * with APTE).  then we can set VM_MAXUSER_ADDRESS to
		 * be VM_MAX_ADDRESS.
		 */

		/* XXXCDC: ugly hack to avoid freeing PDP here */
		if (pdei(va) >= PDSLOT_PTE && pdei(va) <= (PDSLOT_PTE + 3))
			continue;

		/* empty block? */
		if (!pmap_valid_entry(PDE(pmap, pdei(va))))
			continue;

		md_prot = protection_codes[prot];
		if (!(prot & PROT_EXEC))
			md_prot |= PG_NX;
		if (va < VM_MAXUSER_ADDRESS)
			md_prot |= PG_u;
		else if (va < VM_MAX_ADDRESS)
			/* XXX: write-prot our PTES? never! */
			md_prot |= PG_RW;

		spte = &ptes[atop(va)];
		epte = &ptes[atop(blockend)];

		for (/*null */; spte < epte ; spte++, va += PAGE_SIZE) {

			if (!pmap_valid_entry(*spte))	/* no mapping? */
				continue;

			opte = *spte;
			npte = (opte & ~(pt_entry_t)PG_PROT) | md_prot;

			if (npte != opte) {
				pmap_exec_account(pmap, va, *spte, npte);
				i386_atomic_testset_uq(spte, npte);
			}
		}
	}
	if (shootall)
		pmap_tlb_shoottlb();
	else
		pmap_tlb_shootrange(pmap, sva, eva);

	pmap_unmap_ptes_pae(pmap);		/* unlocks pmap */
	pmap_tlb_shootwait();
}

/*
 * end of protection functions
 */

/*
 * pmap_unwire: clear the wired bit in the PTE
 *
 * => mapping should already be in map
 */

void
pmap_unwire_pae(struct pmap *pmap, vaddr_t va)
{
	pt_entry_t *ptes;

	if (pmap_valid_entry(PDE(pmap, pdei(va)))) {
		ptes = pmap_map_ptes_pae(pmap);		/* locks pmap */

#ifdef DIAGNOSTIC
		if (!pmap_valid_entry(ptes[atop(va)]))
			panic("pmap_unwire_pae: invalid (unmapped) va "
			      "0x%lx", va);
#endif
		if ((ptes[atop(va)] & PG_W) != 0) {
			i386_atomic_testset_uq(&ptes[atop(va)],
			    ptes[atop(va)] & ~PG_W);
			pmap->pm_stats.wired_count--;
		}
#ifdef DIAGNOSTIC
		else {
			printf("pmap_unwire_pae: wiring for pmap %p va 0x%lx "
			       "didn't change!\n", pmap, va);
		}
#endif
		pmap_unmap_ptes_pae(pmap);		/* unlocks map */
	}
#ifdef DIAGNOSTIC
	else {
		panic("pmap_unwire_pae: invalid PDE");
	}
#endif
}

/*
 * pmap_copy: copy mappings from one pmap to another
 *
 * => optional function
 * void pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr)
 */

/*
 * defined as macro in pmap.h
 */

/*
 * pmap_enter: enter a mapping into a pmap
 *
 * => must be done "now" ... no lazy-evaluation
 */

int
pmap_enter_pae(struct pmap *pmap, vaddr_t va, paddr_t pa, vm_prot_t prot,
    int flags)
{
	pt_entry_t *ptes, opte, npte;
	struct vm_page *ptp;
	struct pv_entry *pve, *opve = NULL;
	boolean_t wired = (flags & PMAP_WIRED) != 0;
	boolean_t nocache = (pa & PMAP_NOCACHE) != 0;
	boolean_t wc = (pa & PMAP_WC) != 0;
	struct vm_page *pg = NULL;
	int error, wired_count, resident_count, ptp_count;

	KASSERT(!(wc && nocache));
	pa &= PMAP_PA_MASK;	/* nuke flags from pa */

#ifdef DIAGNOSTIC
	/* sanity check: totally out of range? */
	if (va >= VM_MAX_KERNEL_ADDRESS)
		panic("pmap_enter_pae: too big");

	if (va == (vaddr_t) PDP_BASE || va == (vaddr_t) APDP_BASE)
		panic("pmap_enter:_pae trying to map over PDP/APDP!");

	/* sanity check: kernel PTPs should already have been pre-allocated */
	if (va >= VM_MIN_KERNEL_ADDRESS &&
	    !pmap_valid_entry(PDE(pmap, pdei(va))))
		panic("pmap_enter_pae: missing kernel PTP!");
#endif

	if (pmap_initialized)
		pve = pmap_alloc_pv(pmap, ALLOCPV_NEED);
	else
		pve = NULL;
	wired_count = resident_count = ptp_count = 0;

	/*
	 * map in ptes and get a pointer to our PTP (unless we are the kernel)
	 */

	ptes = pmap_map_ptes_pae(pmap);		/* locks pmap */
	if (pmap == pmap_kernel()) {
		ptp = NULL;
	} else {
		ptp = pmap_get_ptp_pae(pmap, pdei(va));
		if (ptp == NULL) {
			if (flags & PMAP_CANFAIL) {
				error = ENOMEM;
				pmap_unmap_ptes_pae(pmap);
				goto out;
			}
			panic("pmap_enter_pae: get ptp failed");
		}
	}
	/*
	 * not allowed to sleep after here!
	 */
	opte = ptes[atop(va)];			/* old PTE */

	/*
	 * is there currently a valid mapping at our VA?
	 */

	if (pmap_valid_entry(opte)) {

		/*
		 * first, calculate pm_stats updates.  resident count will not
		 * change since we are replacing/changing a valid
		 * mapping.  wired count might change...
		 */

		if (wired && (opte & PG_W) == 0)
			wired_count++;
		else if (!wired && (opte & PG_W) != 0)
			wired_count--;

		/*
		 * is the currently mapped PA the same as the one we
		 * want to map?
		 */

		if ((opte & PG_FRAME) == pa) {

			/* if this is on the PVLIST, sync R/M bit */
			if (opte & PG_PVLIST) {
				pg = PHYS_TO_VM_PAGE(pa);
#ifdef DIAGNOSTIC
				if (pg == NULL)
					panic("pmap_enter_pae: same pa "
					     "PG_PVLIST mapping with "
					     "unmanaged page "
					     "pa = 0x%lx (0x%lx)", pa,
					     atop(pa));
#endif
				pmap_sync_flags_pte_pae(pg, opte);
			}
			goto enter_now;
		}

		/*
		 * changing PAs: we must remove the old one first
		 */

		/*
		 * if current mapping is on a pvlist,
		 * remove it (sync R/M bits)
		 */

		if (opte & PG_PVLIST) {
			pg = PHYS_TO_VM_PAGE(opte & PG_FRAME);
#ifdef DIAGNOSTIC
			if (pg == NULL)
				panic("pmap_enter_pae: PG_PVLIST mapping with "
				      "unmanaged page "
				      "pa = 0x%lx (0x%lx)", pa, atop(pa));
#endif
			pmap_sync_flags_pte_pae(pg, opte);
			opve = pmap_remove_pv(pg, pmap, va);
			pg = NULL; /* This is not the page we are looking for */
		}
	} else {	/* opte not valid */
		resident_count++;
		if (wired)
			wired_count++;
		if (ptp)
			ptp_count++;	/* count # of valid entries */
	}

	/*
	 * pve is either NULL or points to a now-free pv_entry structure
	 * (the latter case is if we called pmap_remove_pv above).
	 *
	 * if this entry is to be on a pvlist, enter it now.
	 */

	if (pmap_initialized && pg == NULL)
		pg = PHYS_TO_VM_PAGE(pa);

	if (pg != NULL) {
		if (pve == NULL) {
			pve = opve;
			opve = NULL;
		}
		if (pve == NULL) {
			if (flags & PMAP_CANFAIL) {
				pmap_unmap_ptes_pae(pmap);
				error = ENOMEM;
				goto out;
			}
			panic("pmap_enter_pae: no pv entries available");
		}
		/* lock pg when adding */
		pmap_enter_pv(pg, pve, pmap, va, ptp);
		pve = NULL;
	}

enter_now:
	/*
	 * at this point pg is !NULL if we want the PG_PVLIST bit set
	 */

	npte = pa | protection_codes[prot] | PG_V;
	if (!(prot & PROT_EXEC))
		npte |= PG_NX;
	pmap_exec_account(pmap, va, opte, npte);
	if (wired)
		npte |= PG_W;
	if (nocache)
		npte |= PG_N;
	if (va < VM_MAXUSER_ADDRESS)
		npte |= PG_u;
	else if (va < VM_MAX_ADDRESS)
		npte |= PG_RW;	/* XXXCDC: no longer needed? */
	if (pmap == pmap_kernel())
		npte |= pmap_pg_g;
	if (flags & PROT_READ)
		npte |= PG_U;
	if (flags & PROT_WRITE)
		npte |= PG_M;
	if (pg) {
		npte |= PG_PVLIST;
		if (pg->pg_flags & PG_PMAP_WC) {
			KASSERT(nocache == 0);
			wc = TRUE;
		}
		pmap_sync_flags_pte_pae(pg, npte);
	}
	if (wc)
		npte |= pmap_pg_wc;

	opte = i386_atomic_testset_uq(&ptes[atop(va)], npte);
	if (ptp)
		ptp->wire_count += ptp_count;
	pmap->pm_stats.resident_count += resident_count;
	pmap->pm_stats.wired_count += wired_count;

	if (pmap_valid_entry(opte)) {
		if (nocache && (opte & PG_N) == 0)
			wbinvd(); /* XXX clflush before we enter? */
		pmap_tlb_shootpage(pmap, va);
	}

	pmap_unmap_ptes_pae(pmap);
	pmap_tlb_shootwait();

	error = 0;

out:
	if (pve)
		pmap_free_pv(pmap, pve);
	if (opve)
		pmap_free_pv(pmap, opve);

	return error;
}

/*
 * pmap_growkernel: increase usage of KVM space
 *
 * => we allocate new PTPs for the kernel and install them in all
 *	the pmaps on the system.
 */

vaddr_t
pmap_growkernel_pae(vaddr_t maxkvaddr)
{
	extern int nkpde;
	struct pmap *kpm = pmap_kernel(), *pm;
	int needed_kpde;   /* needed number of kernel PTPs */
	int s;
	paddr_t ptaddr;

	needed_kpde = (int)(maxkvaddr - VM_MIN_KERNEL_ADDRESS + (NBPD-1))
		/ NBPD;
	if (needed_kpde <= nkpde)
		goto out;		/* we are OK */

	/*
	 * whoops!   we need to add kernel PTPs
	 */

	s = splhigh();	/* to be safe */

	for (/*null*/ ; nkpde < needed_kpde ; nkpde++) {

		if (uvm.page_init_done == FALSE) {

			/*
			 * we're growing the kernel pmap early (from
			 * uvm_pageboot_alloc()).  this case must be
			 * handled a little differently.
			 */

			if (uvm_page_physget(&ptaddr) == FALSE)
				panic("pmap_growkernel: out of memory");
			pmap_zero_phys_pae(ptaddr);

			PDE(kpm, PDSLOT_KERN + nkpde) =
				ptaddr | PG_RW | PG_V | PG_U | PG_M;

			/* count PTP as resident */
			kpm->pm_stats.resident_count++;
			continue;
		}

		/*
		 * THIS *MUST* BE CODED SO AS TO WORK IN THE
		 * pmap_initialized == FALSE CASE!  WE MAY BE
		 * INVOKED WHILE pmap_init() IS RUNNING!
		 */

		while (!pmap_alloc_ptp_pae(kpm, PDSLOT_KERN + nkpde, 0))
			uvm_wait("pmap_growkernel");

		/* distribute new kernel PTP to all active pmaps */
		LIST_FOREACH(pm, &pmaps, pm_list) {
			PDE(pm, PDSLOT_KERN + nkpde) =
				PDE(kpm, PDSLOT_KERN + nkpde);
		}
	}

	splx(s);

out:
	return (VM_MIN_KERNEL_ADDRESS + (nkpde * NBPD));
}

/*
 * Pre-allocate PTP 0 for low memory, so that 1:1 mappings for various
 * trampoline code can be entered.
 */
void
pmap_prealloc_lowmem_ptp_pae(void)
{
	pt_entry_t *pte, npte;
	vaddr_t ptpva = (vaddr_t)vtopte(0);

	/* enter pa for pte 0 into recursive map */
	pte = vtopte(ptpva);
	npte = PTP0_PA | PG_RW | PG_V | PG_U | PG_M;

	i386_atomic_testset_uq(pte, npte);

	/* make sure it is clean before using */
	memset((void *)ptpva, 0, NBPG);
}

/*
 * pmap_tmpmap_pa_pae: map a page in for tmp usage
 */

vaddr_t
pmap_tmpmap_pa_pae(paddr_t pa)
{
#ifdef MULTIPROCESSOR
	int id = cpu_number();
#endif
	pt_entry_t *ptpte = PTESLEW(ptp_pte, id);
	caddr_t ptpva = VASLEW(pmap_ptpp, id);
#if defined(DIAGNOSTIC)
	if (*ptpte)
		panic("pmap_tmpmap_pa_pae: ptp_pte in use?");
#endif
	*ptpte = PG_V | PG_RW | pa;	/* always a new mapping */
	return((vaddr_t)ptpva);
}

/*
 * pmap_tmpunmap_pa_pae: unmap a tmp use page (undoes pmap_tmpmap_pa_pae)
 */

void
pmap_tmpunmap_pa_pae()
{
#ifdef MULTIPROCESSOR
	int id = cpu_number();
#endif
	pt_entry_t *ptpte = PTESLEW(ptp_pte, id);
	caddr_t ptpva = VASLEW(pmap_ptpp, id);
#if defined(DIAGNOSTIC)
	if (!pmap_valid_entry(*ptpte))
		panic("pmap_tmpunmap_pa_pae: our pte invalid?");
#endif
	*ptpte = 0;
	pmap_update_pg((vaddr_t)ptpva);
#ifdef MULTIPROCESSOR
	/*
	 * No need for tlb shootdown here, since ptp_pte is per-CPU.
	 */
#endif
}

paddr_t
vtophys_pae(vaddr_t va)
{
	return ((*vtopte(va) & PG_FRAME) | (va & ~PG_FRAME));
}

void
pmap_flush_page_pae(paddr_t pa)
{
#ifdef MULTIPROCESSOR
	int id = cpu_number();
#endif
	pt_entry_t *pte = PTESLEW(flsh_pte, id);
	caddr_t va = VASLEW(pmap_flshp, id);

	KDASSERT(PHYS_TO_VM_PAGE(pa) != NULL);
#ifdef DIAGNOSTIC
	if (*pte)
		panic("pmap_flush_page_pae: lock botch");
#endif

	*pte = (pa & PG_FRAME) | PG_V | PG_RW;
	pmap_update_pg(va);
	pmap_flush_cache((vaddr_t)va, PAGE_SIZE);
	*pte = 0;
	pmap_update_pg(va);
}

#ifdef DEBUG
void		 pmap_dump_pae(struct pmap *, vaddr_t, vaddr_t);
/*
 * pmap_dump: dump all the mappings from a pmap
 *
 * => caller should not be holding any pmap locks
 */

void
pmap_dump_pae(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
{
	pt_entry_t *ptes, *pte;
	vaddr_t blkendva;

	/*
	 * if end is out of range truncate.
	 * if (end == start) update to max.
	 */

	if (eva > VM_MAXUSER_ADDRESS || eva <= sva)
		eva = VM_MAXUSER_ADDRESS;

	ptes = pmap_map_ptes_pae(pmap);	/* locks pmap */

	/*
	 * dumping a range of pages: we dump in PTP sized blocks (4MB)
	 */

	for (/* null */ ; sva < eva ; sva = blkendva) {

		/* determine range of block */
		blkendva = i386_round_pdr(sva+1);
		if (blkendva > eva)
			blkendva = eva;

		/* valid block? */
		if (!pmap_valid_entry(PDE(pmap, pdei(sva))))
			continue;

		pte = &ptes[atop(sva)];
		for (/* null */; sva < blkendva ; sva += NBPG, pte++) {
			if (!pmap_valid_entry(*pte))
				continue;
			printf("va %#lx -> pa %#llx (pte=%#llx)\n",
			       sva, *pte, *pte & PG_FRAME);
		}
	}
	pmap_unmap_ptes_pae(pmap);
}
#endif

license: unknown


code:
/* $OpenBSD: powernow-k7.c,v 1.36 2014/09/14 14:17:23 jsg Exp $ */

/*
 * Copyright (c) 2004 Martin V�giard.
 * Copyright (c) 2004-2005 Bruno Ducrot
 * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
 *
 * 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.
 */

/* AMD POWERNOW K7 driver */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>

#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/bus.h>

#include <dev/isa/isareg.h>
#include <i386/isa/isa_machdep.h>

#include "acpicpu.h"

#if NACPICPU > 0
#include <dev/acpi/acpidev.h>
#include <dev/acpi/acpivar.h>
#endif

#define BIOS_START			0xe0000
#define	BIOS_LEN			0x20000
#define BIOS_STEP			16

/*
 * MSRs and bits used by PowerNow! technology
 */
#define MSR_AMDK7_FIDVID_CTL		0xc0010041
#define MSR_AMDK7_FIDVID_STATUS		0xc0010042
#define AMD_PN_FID_VID			0x06
#define AMD_ERRATA_A0_CPUSIG		0x660

#define PN7_FLAG_ERRATA_A0		0x01
#define PN7_FLAG_DESKTOP_VRM		0x02

/* Bitfields used by K7 */
#define PN7_PSB_VERSION			0x12
#define PN7_CTR_FID(x)			((x) & 0x1f)
#define PN7_CTR_VID(x)			(((x) & 0x1f) << 8)
#define PN7_CTR_FIDC			0x00010000
#define PN7_CTR_VIDC			0x00020000
#define PN7_CTR_FIDCHRATIO		0x00100000
#define PN7_CTR_SGTC(x)			(((uint64_t)(x) & 0x000fffff) << 32)

#define PN7_STA_CFID(x)			((x) & 0x1f)
#define PN7_STA_SFID(x)			(((x) >> 8) & 0x1f)
#define PN7_STA_MFID(x)			(((x) >> 16) & 0x1f)
#define PN7_STA_CVID(x)			(((x) >> 32) & 0x1f)
#define PN7_STA_SVID(x)			(((x) >> 40) & 0x1f)
#define PN7_STA_MVID(x)			(((x) >> 48) & 0x1f)

/*
 * ACPI ctr_val status register to powernow k7 configuration
 */
#define PN7_ACPI_CTRL_TO_FID(x)		((x) & 0x1f)
#define PN7_ACPI_CTRL_TO_VID(x)		(((x) >> 5) & 0x1f)
#define PN7_ACPI_CTRL_TO_SGTC(x)	(((x) >> 10) & 0xffff)

#define WRITE_FIDVID(fid, vid, ctrl)	\
	wrmsr(MSR_AMDK7_FIDVID_CTL,	\
	    (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))

/*
 * Divide each value by 10 to get the processor multiplier.
 * Taken from powernow-k7.c/Linux by Dave Jones
 */
static int k7pnow_fid_to_mult[32] = {
	110, 115, 120, 125, 50, 55, 60, 65,
	70, 75, 80, 85, 90, 95, 100, 105,
	30, 190, 40, 200, 130, 135, 140, 210,
	150, 225, 160, 165, 170, 180, -1, -1
};

#define POWERNOW_MAX_STATES		16

struct k7pnow_state {
	int freq;
	int fid;
	int vid;
};

struct k7pnow_cpu_state {
	unsigned int fsb;
	unsigned int sgtc;
	struct k7pnow_state state_table[POWERNOW_MAX_STATES];
	unsigned int n_states;
	int flags;
};

struct psb_s {
	char signature[10];	/* AMDK7PNOW! */
	uint8_t version;
	uint8_t flags;
	uint16_t ttime;		/* Min Settling time */
	uint8_t reserved;
	uint8_t n_pst;
};

struct pst_s {
	uint32_t signature;
	uint8_t fsb;		/* Front Side Bus frequency (MHz) */
	uint8_t fid;		/* Max Frequency code */
	uint8_t vid;		/* Max Voltage code */
	uint8_t n_states;	/* Number of states */
};

struct k7pnow_cpu_state *k7pnow_current_state;
extern int setperf_prio;

int k7pnow_decode_pst(struct k7pnow_cpu_state *, uint8_t *, int);
int k7pnow_states(struct k7pnow_cpu_state *, uint32_t, unsigned int,
    unsigned int);

#if NACPICPU > 0
int k7pnow_acpi_init(struct k7pnow_cpu_state * cstate, uint64_t status);
int k7pnow_acpi_states(struct k7pnow_cpu_state * cstate,
    struct acpicpu_pss *pss, int nstates, uint64_t status);
void k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss);
#endif

void
k7_powernow_setperf(int level)
{
	unsigned int i;
	int cvid, cfid, vid = 0, fid = 0;
	uint64_t status, ctl;
	struct k7pnow_cpu_state * cstate;

	cstate = k7pnow_current_state;

	i = ((level * cstate->n_states) + 1) / 101;
	if (i >= cstate->n_states)
		i = cstate->n_states - 1;
	fid = cstate->state_table[i].fid;
	vid = cstate->state_table[i].vid;

	if (fid == 0 || vid == 0)
		return;

	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
	cfid = PN7_STA_CFID(status);
	cvid = PN7_STA_CVID(status);

	/*
	 * We're already at the requested level.
	 */
	if (fid == cfid && vid == cvid)
		return;

	ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;

	ctl |= PN7_CTR_FID(fid);
	ctl |= PN7_CTR_VID(vid);
	ctl |= PN7_CTR_SGTC(cstate->sgtc);

	if (cstate->flags & PN7_FLAG_ERRATA_A0)
		disable_intr();

	if (k7pnow_fid_to_mult[fid] < k7pnow_fid_to_mult[cfid]) {
		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
		if (vid != cvid)
			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
	} else {
		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
		if (fid != cfid)
			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
	}

	if (cstate->flags & PN7_FLAG_ERRATA_A0)
		enable_intr();

	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
	cfid = PN7_STA_CFID(status);
	cvid = PN7_STA_CVID(status);
	if (cfid == fid || cvid == vid)
		cpuspeed = cstate->state_table[i].freq;
}

/*
 * Given a set of pair of fid/vid, and number of performance states,
 * compute state_table via an insertion sort.
 */
int
k7pnow_decode_pst(struct k7pnow_cpu_state * cstate, uint8_t *p, int npst)
{
	int i, j, n;
	struct k7pnow_state state;

	for (n = 0, i = 0; i < npst; ++i) {
		state.fid = *p++;
		state.vid = *p++;
		state.freq = k7pnow_fid_to_mult[state.fid]/10 * cstate->fsb;
		if ((cstate->flags & PN7_FLAG_ERRATA_A0) &&
		    (k7pnow_fid_to_mult[state.fid] % 10) == 5)
			continue;

		j = n;
		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
			memcpy(&cstate->state_table[j],
			    &cstate->state_table[j - 1],
			    sizeof(struct k7pnow_state));
			--j;
		}
		memcpy(&cstate->state_table[j], &state,
		    sizeof(struct k7pnow_state));
		++n;
	}
	/*
	 * Fix powernow_max_states, if errata_a0 give us less states
	 * than expected.
	 */
	cstate->n_states = n;
	return 1;
}

int
k7pnow_states(struct k7pnow_cpu_state *cstate, uint32_t cpusig,
    unsigned int fid, unsigned int vid)
{
	int maxpst;
	struct psb_s *psb;
	struct pst_s *pst;
	uint8_t *p;

	/*
	 * Look in the 0xe0000 - 0x100000 physical address
	 * range for the pst tables; 16 byte blocks
	 */
	for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
	    p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p+=
	    BIOS_STEP) {
		if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
			psb = (struct psb_s *)p;
			if (psb->version != PN7_PSB_VERSION)
				return 0;

			cstate->sgtc = psb->ttime * cstate->fsb;
			if (cstate->sgtc < 100 * cstate->fsb)
				cstate->sgtc = 100 * cstate->fsb;
			if (psb->flags & 1)
				cstate->flags |= PN7_FLAG_DESKTOP_VRM;
			p += sizeof(struct psb_s);

			for (maxpst = 0; maxpst < psb->n_pst; maxpst++) {
				pst = (struct pst_s*) p;

				if (cpusig == pst->signature && fid == pst->fid
				    && vid == pst->vid) {

					if (abs(cstate->fsb - pst->fsb) > 5)
						continue;
					cstate->n_states = pst->n_states;
					return (k7pnow_decode_pst(cstate,
					    p + sizeof(struct pst_s),
					    cstate->n_states));
				}
				p += sizeof(struct pst_s) +
				    (2 * pst->n_states);
			}
		}
	}

	return 0;
}

#if NACPICPU > 0

int
k7pnow_acpi_states(struct k7pnow_cpu_state * cstate, struct acpicpu_pss *pss,
    int nstates, uint64_t status)
{
	struct k7pnow_state state;
	int j, k, n;
	uint32_t ctrl;

	k = -1;
	for (n = 0; n < cstate->n_states; n++) {
		if (status == pss[n].pss_status)
			k = n;
		ctrl = pss[n].pss_ctrl;
		state.fid = PN7_ACPI_CTRL_TO_FID(ctrl);
		state.vid = PN7_ACPI_CTRL_TO_VID(ctrl);

		if ((cstate->flags & PN7_FLAG_ERRATA_A0) &&
		    (k7pnow_fid_to_mult[state.fid] % 10) == 5)
			continue;

		state.freq = pss[n].pss_core_freq;
		j = n;
		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
			memcpy(&cstate->state_table[j],
			    &cstate->state_table[j - 1],
			sizeof(struct k7pnow_state));
			--j;
		}
		memcpy(&cstate->state_table[j], &state,
		    sizeof(struct k7pnow_state));
	}
	return k;
}

void
k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss)
{
	int curs;
	struct k7pnow_cpu_state *cstate;
	uint32_t ctrl;
	uint64_t status;

	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
	cstate = k7pnow_current_state;

	curs = k7pnow_acpi_states(cstate, pss, npss, status);
	ctrl = pss[curs].pss_ctrl;
	cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
	cstate->n_states = npss;
}

int
k7pnow_acpi_init(struct k7pnow_cpu_state *cstate, uint64_t status)
{
	int curs;
	uint32_t ctrl;
	struct acpicpu_pss *pss;
	int mfid;

	cstate->n_states = acpicpu_fetch_pss(&pss);
	if (cstate->n_states == 0)
		return 0;

	curs = k7pnow_acpi_states(cstate, pss, cstate->n_states, status);
	/*
	 * XXX: Some BIOS supplied _PSS implementations have the wrong
	 * maximum frequency, if we encounter one of these punt and
	 * hope the legacy tables have correct values.
	 */
	mfid = PN7_STA_MFID(status);
	if (mfid != cstate->state_table[cstate->n_states - 1].fid) {
		return 0;
	}

	acpicpu_set_notify(k7pnow_acpi_pss_changed);
	ctrl = pss[curs].pss_ctrl;
	cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);

	return 1;
}

#endif /* NACPICPU */

void
k7_powernow_init(void)
{
	u_int regs[4];
	uint64_t status;
	u_int maxfid, startvid, currentfid;
	struct k7pnow_cpu_state *cstate;
	struct k7pnow_state *state;
	struct cpu_info *ci;
	char *techname = NULL;
	int i;

	if (setperf_prio > 1)
		return;

	ci = curcpu();

	cpuid(0x80000000, regs);
	if (regs[0] < 0x80000007)
		return;

	cpuid(0x80000007, regs);
	if (!(regs[3] & AMD_PN_FID_VID))
		return;

	/* Extended CPUID signature value */
	cpuid(0x80000001, regs);

	cstate = malloc(sizeof(struct k7pnow_cpu_state), M_DEVBUF, M_NOWAIT);
	if (!cstate)
		return;

	cstate->flags = cstate->n_states = 0;
	if (ci->ci_signature == AMD_ERRATA_A0_CPUSIG)
		cstate->flags |= PN7_FLAG_ERRATA_A0;

	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
	maxfid = PN7_STA_MFID(status);
	startvid = PN7_STA_SVID(status);
	currentfid = PN7_STA_CFID(status);

	cstate->fsb = cpuspeed / (k7pnow_fid_to_mult[currentfid]/10);

	if (!k7pnow_states(cstate, ci->ci_signature, maxfid, startvid))
		if (!k7pnow_states(cstate, regs[0], maxfid, startvid)) {
#if NACPICPU > 0
			/* If we have it try ACPI */
			k7pnow_acpi_init(cstate, status);
#endif
	}

	if (cstate->n_states) {
		if (cstate->flags & PN7_FLAG_DESKTOP_VRM)
			techname = "Cool'n'Quiet K7";
		else
			techname = "PowerNow! K7";
		printf("%s: %s %d MHz: speeds:",
		    ci->ci_dev.dv_xname, techname, cpuspeed);
		for (i = cstate->n_states; i > 0; i--) {
			state = &cstate->state_table[i-1];
			printf(" %d", state->freq);
		}
		printf(" MHz\n");

		k7pnow_current_state = cstate;
		cpu_setperf = k7_powernow_setperf;
		setperf_prio = 1;
		return;
	}
	free(cstate, M_DEVBUF, 0);
}


code:
/*	$OpenBSD: powernow-k8.c,v 1.28 2014/09/14 14:17:23 jsg Exp $ */

/*
 * Copyright (c) 2004 Martin V�giard.
 * Copyright (c) 2004-2005 Bruno Ducrot
 * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
 *
 * 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.
 */
/* AMD POWERNOW K8 driver */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>

#include <dev/isa/isareg.h>
#include <i386/isa/isa_machdep.h>

#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/bus.h>

#include "acpicpu.h"

#if NACPICPU > 0
#include <dev/acpi/acpidev.h>
#include <dev/acpi/acpivar.h>
#endif

#define BIOS_START			0xe0000
#define	BIOS_LEN			0x20000
#define BIOS_STEP			16

/*
 * MSRs and bits used by PowerNow! technology
 */
#define MSR_AMDK7_FIDVID_CTL		0xc0010041
#define MSR_AMDK7_FIDVID_STATUS		0xc0010042
#define AMD_PN_FID_VID			0x06

/* Bitfields used by K8 */

#define PN8_CTR_FID(x)			((x) & 0x3f)
#define PN8_CTR_VID(x)			(((x) & 0x1f) << 8)
#define PN8_CTR_PENDING(x)		(((x) & 1) << 32)

#define PN8_STA_CFID(x)			((x) & 0x3f)
#define PN8_STA_SFID(x)			(((x) >> 8) & 0x3f)
#define PN8_STA_MFID(x)			(((x) >> 16) & 0x3f)
#define PN8_STA_PENDING(x)		(((x) >> 31) & 0x01)
#define PN8_STA_CVID(x)			(((x) >> 32) & 0x1f)
#define PN8_STA_SVID(x)			(((x) >> 40) & 0x1f)
#define PN8_STA_MVID(x)			(((x) >> 48) & 0x1f)

/* Reserved1 to powernow k8 configuration */
#define PN8_PSB_VERSION			0x14
#define PN8_PSB_TO_RVO(x)		((x) & 0x03)
#define PN8_PSB_TO_IRT(x)		(((x) >> 2) & 0x03)
#define PN8_PSB_TO_MVS(x)		(((x) >> 4) & 0x03)
#define PN8_PSB_TO_BATT(x)		(((x) >> 6) & 0x03)

/* ACPI ctr_val status register to powernow k8 configuration */
#define PN8_ACPI_CTRL_TO_FID(x)		((x) & 0x3f)
#define PN8_ACPI_CTRL_TO_VID(x)		(((x) >> 6) & 0x1f)
#define PN8_ACPI_CTRL_TO_VST(x)		(((x) >> 11) & 0x1f)
#define PN8_ACPI_CTRL_TO_MVS(x)		(((x) >> 18) & 0x03)
#define PN8_ACPI_CTRL_TO_PLL(x)		(((x) >> 20) & 0x7f)
#define PN8_ACPI_CTRL_TO_RVO(x)		(((x) >> 28) & 0x03)
#define PN8_ACPI_CTRL_TO_IRT(x)		(((x) >> 30) & 0x03)

#define PN8_PSS_CFID(x)			((x) & 0x3f)
#define PN8_PSS_CVID(x)			(((x) >> 6) & 0x1f)

#define PN8_PLL_LOCK(x)			((x) * 1000/5)
#define WRITE_FIDVID(fid, vid, ctrl)	\
	wrmsr(MSR_AMDK7_FIDVID_CTL,	\
	    (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))

#define COUNT_OFF_IRT(irt)		DELAY(10 * (1 << (irt)))
#define COUNT_OFF_VST(vst)		DELAY(20 * (vst))

#define FID_TO_VCO_FID(fid)		\
	(((fid) < 8) ? (8 + ((fid) << 1)) : (fid))

#define POWERNOW_MAX_STATES		16

struct k8pnow_state {
	int freq;
	uint8_t fid;
	uint8_t vid;
};

struct k8pnow_cpu_state {
	struct k8pnow_state state_table[POWERNOW_MAX_STATES];
	unsigned int n_states;
	unsigned int sgtc;
	unsigned int vst;
	unsigned int mvs;
	unsigned int pll;
	unsigned int rvo;
	unsigned int irt;
	int low;
};

struct psb_s {
	char signature[10];	/* AMDK7PNOW! */
	uint8_t version;
	uint8_t flags;
	uint16_t ttime;		/* Min Settling time */
	uint8_t reserved;
	uint8_t n_pst;
};

struct pst_s {
	uint32_t cpuid;
	uint8_t pll;
	uint8_t fid;
	uint8_t vid;
	uint8_t n_states;
};

struct k8pnow_cpu_state *k8pnow_current_state = NULL;
extern int setperf_prio;
extern int perflevel;

int k8pnow_read_pending_wait(uint64_t *);
int k8pnow_decode_pst(struct k8pnow_cpu_state *, uint8_t *);
int k8pnow_states(struct k8pnow_cpu_state *, uint32_t, unsigned int, unsigned int);
void k8pnow_transition(struct k8pnow_cpu_state *e, int);

#if NACPICPU > 0
int k8pnow_acpi_init(struct k8pnow_cpu_state *, uint64_t);
void k8pnow_acpi_pss_changed(struct acpicpu_pss *, int);
int k8pnow_acpi_states(struct k8pnow_cpu_state *, struct acpicpu_pss *, int,
    uint64_t);
#endif

int
k8pnow_read_pending_wait(uint64_t *status)
{
	unsigned int i = 100000;

	while (i--) {
		*status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
		if (!PN8_STA_PENDING(*status))
			return 0;

	}
	printf("k8pnow_read_pending_wait: change pending stuck.\n");
	return 1;
}

void
k8_powernow_setperf(int level)
{
	unsigned int i;
	struct k8pnow_cpu_state *cstate;

	cstate = k8pnow_current_state;

	i = ((level * cstate->n_states) + 1) / 101;
	if (i >= cstate->n_states)
		i = cstate->n_states - 1;

	k8pnow_transition(cstate, i);
}

void
k8pnow_transition(struct k8pnow_cpu_state *cstate, int level)
{
	uint64_t status;
	int cfid, cvid, fid = 0, vid = 0;
	int rvo;
	u_int val;

	/*
	 * We dont do a k8pnow_read_pending_wait here, need to ensure that the
	 * change pending bit isn't stuck,
	 */
	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
	if (PN8_STA_PENDING(status))
		return;
	cfid = PN8_STA_CFID(status);
	cvid = PN8_STA_CVID(status);

	fid = cstate->state_table[level].fid;
	vid = cstate->state_table[level].vid;

	if (fid == cfid && vid == cvid)
		return;

	/*
	 * Phase 1: Raise core voltage to requested VID if frequency is
	 * going up.
	 */
	while (cvid > vid) {
		val = cvid - (1 << cstate->mvs);
		WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
		if (k8pnow_read_pending_wait(&status))
			return;
		cvid = PN8_STA_CVID(status);
		COUNT_OFF_VST(cstate->vst);
	}

	/* ... then raise to voltage + RVO (if required) */
	for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
		/* XXX It's not clear from spec if we have to do that
		 * in 0.25 step or in MVS.  Therefore do it as it's done
		 * under Linux */
		WRITE_FIDVID(cfid, cvid - 1, 1ULL);
		if (k8pnow_read_pending_wait(&status))
			return;
		cvid = PN8_STA_CVID(status);
		COUNT_OFF_VST(cstate->vst);
	}

	/* Phase 2: change to requested core frequency */
	if (cfid != fid) {
		u_int vco_fid, vco_cfid;

		vco_fid = FID_TO_VCO_FID(fid);
		vco_cfid = FID_TO_VCO_FID(cfid);

		while (abs(vco_fid - vco_cfid) > 2) {
			if (fid > cfid) {
				if (cfid > 6)
					val = cfid + 2;
				else
					val = FID_TO_VCO_FID(cfid) + 2;
			} else
				val = cfid - 2;
			WRITE_FIDVID(val, cvid, (uint64_t)cstate->pll * 1000 / 5);

			if (k8pnow_read_pending_wait(&status))
				return;
			cfid = PN8_STA_CFID(status);
			COUNT_OFF_IRT(cstate->irt);

			vco_cfid = FID_TO_VCO_FID(cfid);
		}

		WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
		if (k8pnow_read_pending_wait(&status))
			return;
		cfid = PN8_STA_CFID(status);
		COUNT_OFF_IRT(cstate->irt);
	}

	/* Phase 3: change to requested voltage */
	if (cvid != vid) {
		WRITE_FIDVID(cfid, vid, 1ULL);
		if (k8pnow_read_pending_wait(&status))
			return;
		cvid = PN8_STA_CVID(status);
		COUNT_OFF_VST(cstate->vst);
	}

	if (cfid == fid || cvid == vid)
		cpuspeed = cstate->state_table[level].freq;
}

/*
 * Given a set of pair of fid/vid, and number of performance states,
 * compute state_table via an insertion sort.
 */
int
k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t *p)
{
	int i, j, n;
	struct k8pnow_state state;

	for (n = 0, i = 0; i < cstate->n_states; i++) {
		state.fid = *p++;
		state.vid = *p++;

		/*
		 * The minimum supported frequency per the data sheet is 800MHz
		 * The maximum supported frequency is 5000MHz.
		 */
		state.freq = 800 + state.fid * 100;
		j = n;
		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
			memcpy(&cstate->state_table[j],
			    &cstate->state_table[j - 1],
			    sizeof(struct k8pnow_state));
			--j;
		}
		memcpy(&cstate->state_table[j], &state,
		    sizeof(struct k8pnow_state));
		n++;
	}
	return 1;
}

int
k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
    unsigned int fid, unsigned int vid)
{
	struct psb_s *psb;
	struct pst_s *pst;
	uint8_t *p;
	int i;

	for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
	    p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p +=
	    BIOS_STEP) {
		if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
			psb = (struct psb_s *)p;
			if (psb->version != PN8_PSB_VERSION)
				return 0;

			cstate->vst = psb->ttime;
			cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
			cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
			cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
			cstate->low = PN8_PSB_TO_BATT(psb->reserved);
			p+= sizeof(struct psb_s);

			for (i = 0; i < psb->n_pst; ++i) {
				pst = (struct pst_s *) p;

				cstate->pll = pst->pll;
				cstate->n_states = pst->n_states;
				if (cpusig == pst->cpuid &&
				    pst->fid == fid && pst->vid == vid) {
					return (k8pnow_decode_pst(cstate,
					    p+= sizeof (struct pst_s)));
				}
				p += sizeof(struct pst_s) + 2
				     * cstate->n_states;
			}
		}
	}

	return 0;

}

#if NACPICPU > 0

int
k8pnow_acpi_states(struct k8pnow_cpu_state * cstate, struct acpicpu_pss * pss,
    int nstates, uint64_t status)
{
	struct k8pnow_state state;
	int j, k, n;
	uint32_t ctrl;

	k = -1;

	for (n = 0; n < cstate->n_states; n++) {
		if ((PN8_STA_CFID(status) == PN8_PSS_CFID(pss[n].pss_status)) &&
		    (PN8_STA_CVID(status) == PN8_PSS_CVID(pss[n].pss_status)))
			k = n;
		ctrl = pss[n].pss_ctrl;
		state.fid = PN8_ACPI_CTRL_TO_FID(ctrl);
		state.vid = PN8_ACPI_CTRL_TO_VID(ctrl);

		state.freq = pss[n].pss_core_freq;
		j = n;
		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
			memcpy(&cstate->state_table[j],
			    &cstate->state_table[j - 1],
			    sizeof(struct k8pnow_state));
			--j;
		}
		memcpy(&cstate->state_table[j], &state,
		    sizeof(struct k8pnow_state));
	}

	return k;
}

void
k8pnow_acpi_pss_changed(struct acpicpu_pss * pss, int npss)
{
	int curs;
	struct k8pnow_cpu_state * cstate;
	uint32_t ctrl;
	uint64_t status;

	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
	cstate = k8pnow_current_state;

	curs = k8pnow_acpi_states(cstate, pss, npss, status);
	ctrl = pss[curs].pss_ctrl;

	cstate->rvo = PN8_ACPI_CTRL_TO_RVO(ctrl);
	cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
	cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
	cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
	cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
	cstate->low = 0;
	cstate->n_states = npss;
}

int
k8pnow_acpi_init(struct k8pnow_cpu_state * cstate, uint64_t status)
{
	int curs;
	uint32_t ctrl;
	struct acpicpu_pss *pss;

	cstate->n_states = acpicpu_fetch_pss(&pss);
	if (cstate->n_states == 0)
		return 0;
	acpicpu_set_notify(k8pnow_acpi_pss_changed);

	curs = k8pnow_acpi_states(cstate, pss, cstate->n_states, status);
	ctrl = pss[curs].pss_ctrl;

	cstate->rvo = PN8_ACPI_CTRL_TO_RVO(ctrl);
	cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
	cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
	cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
	cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
	cstate->low = 0;

	return 1;
}

#endif /* NACPICPU */

void
k8_powernow_init(void)
{
	uint64_t status;
	u_int maxfid, maxvid, i;
	struct k8pnow_cpu_state *cstate;
	struct k8pnow_state *state;
	struct cpu_info * ci;
	char * techname = NULL;
	u_int32_t regs[4];

	ci = curcpu();

	if (setperf_prio > 1)
		return;

	if (k8pnow_current_state)
		return;

	cpuid(0x80000000, regs);
	if (regs[0] < 0x80000007)
		return;

	cpuid(0x80000007, regs);
	if (!(regs[3] & AMD_PN_FID_VID))
		return;

	/* Extended CPUID signature value */
	cpuid(0x80000001, regs);

	cstate = malloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_NOWAIT);
	if (!cstate)
		return;

	cstate->n_states = 0;
	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
	maxfid = PN8_STA_MFID(status);
	maxvid = PN8_STA_MVID(status);

	/*
	* If start FID is different to max FID, then it is a
	* mobile processor.  If not, it is a low powered desktop
	* processor.
	*/
	if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
		techname = "PowerNow! K8";
	else
		techname = "Cool'n'Quiet K8";

#if NACPICPU > 0
	/* If we have acpi check acpi first */
	if (!k8pnow_acpi_init(cstate, status))
#endif
	{
		if (!k8pnow_states(cstate, ci->ci_signature, maxfid, maxvid))
			k8pnow_states(cstate, regs[0], maxfid, maxvid);
	}
	if (cstate->n_states) {
		printf("%s: %s %d MHz: speeds:",
		    ci->ci_dev.dv_xname, techname, cpuspeed);
		for (i = cstate->n_states; i > 0; i--) {
			state = &cstate->state_table[i-1];
			printf(" %d", state->freq);
		}
		printf(" MHz\n");
		k8pnow_current_state = cstate;
		cpu_setperf = k8_powernow_setperf;
		setperf_prio = 1;
		return;
	}
	free(cstate, M_DEVBUF, 0);
}


code:
/*	$OpenBSD: powernow.c,v 1.6 2014/09/14 14:17:23 jsg Exp $	*/
/*
 * Copyright (c) 2004 Ted Unangst
 * All rights reserved.
 *
 * 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.
 */

/*
 * AMD K6 PowerNow driver, see AMD tech doc #23525
 * Numerous hints from Linux cpufreq driver
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>

#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/specialreg.h>
#include <machine/pio.h>

/* table 27 in amd 23535.  entries after 45 taken from linux cpufreq */
struct {
	int mult;	/* not actually used, maybe for cpuspeed */
	int magic;
} k6_multipliers[] = {
	{ 20, 4 },
	{ 30, 5 },
	{ 35, 7 },
	{ 40, 2 },
	{ 45, 0 },
	{ 55, 3 },
	{ 50, 1 },
	{ 60, 6 },
};
#define NUM_K6_ENTRIES (sizeof k6_multipliers / sizeof k6_multipliers[0])

int	k6_maxindex;	/* no setting higher than this */

/* linux says: "it doesn't matter where, as long as it is unused */
#define K6PORT 0xfff0

void
k6_powernow_init(void)
{
	uint64_t msrval;
	uint32_t portval;
	int i;

	/* on */
	msrval = K6PORT | 0x1;
	wrmsr(MSR_K6_EPMR, msrval);
	/* read */
	portval = inl(K6PORT + 8);
	/* off */
	wrmsr(MSR_K6_EPMR, 0LL);

	portval = (portval >> 5) & 7;

	for (i = 0; i < NUM_K6_ENTRIES; i++)
		if (portval == k6_multipliers[i].magic) {
			k6_maxindex = i;
			break;
		}
	if (i == NUM_K6_ENTRIES) {
		printf("bad value for current multiplier\n");
		return;
	}

	cpu_setperf = k6_powernow_setperf;
}

void
k6_powernow_setperf(int level)
{
	uint64_t msrval;
	uint32_t portval;
	int index;

	index = level * k6_maxindex / 100;

	/* on */
	msrval = K6PORT | 0x1;
	wrmsr(MSR_K6_EPMR, msrval);
	/* read and update */
	portval = inl(K6PORT + 8);
	portval &= 0xf;
	portval |= 1 << 12 | 1 << 10 | 1 << 9 |
	    k6_multipliers[index].magic << 5;
	outl(K6PORT + 8, portval);
	/* off */
	wrmsr(MSR_K6_EPMR, 0LL);
}


code:
/*	$OpenBSD: process_machdep.c,v 1.28 2013/05/08 15:36:30 tedu Exp $	*/
/*	$NetBSD: process_machdep.c,v 1.22 1996/05/03 19:42:25 christos Exp $	*/

/*
 * Copyright (c) 1995, 1996 Charles M. Hannum.  All rights reserved.
 * Copyright (c) 1993 The Regents of the University of California.
 * Copyright (c) 1993 Jan-Simon Pendry
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Jan-Simon Pendry.
 *
 * 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.
 *
 * From:
 *	Id: procfs_i386.c,v 4.1 1993/12/17 10:47:45 jsp Rel
 */

/*
 * This file may seem a bit stylized, but that so that it's easier to port.
 * Functions to be implemented here are:
 *
 * process_read_regs(proc, regs)
 *	Get the current user-visible register set from the process
 *	and copy it into the regs structure (<machine/reg.h>).
 *	The process is stopped at the time read_regs is called.
 *
 * process_write_regs(proc, regs)
 *	Update the current register set from the passed in regs
 *	structure.  Take care to avoid clobbering special CPU
 *	registers or privileged bits in the PSL.
 *	The process is stopped at the time write_regs is called.
 *
 * process_sstep(proc)
 *	Arrange for the process to trap after executing a single instruction.
 *
 * process_set_pc(proc)
 *	Set the process's program counter.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <sys/ptrace.h>

#include <machine/psl.h>
#include <machine/reg.h>
#include <machine/segments.h>

#ifdef VM86
#include <machine/vm86.h>
#endif

#include "npx.h"

static __inline struct trapframe *process_frame(struct proc *);
static __inline union savefpu *process_fpframe(struct proc *);
void process_fninit_xmm(struct savexmm *);

static __inline struct trapframe *
process_frame(struct proc *p)
{

	return (p->p_md.md_regs);
}

static __inline union savefpu *
process_fpframe(struct proc *p)
{

	return (&p->p_addr->u_pcb.pcb_savefpu);
}

void
process_xmm_to_s87(const struct savexmm *sxmm, struct save87 *s87)
{
	int i;

	/* FPU control/status */
	s87->sv_env.en_cw = sxmm->sv_env.en_cw;
	s87->sv_env.en_sw = sxmm->sv_env.en_sw;
	/* tag word handled below */
	s87->sv_env.en_fip = sxmm->sv_env.en_fip;
	s87->sv_env.en_fcs = sxmm->sv_env.en_fcs;
	s87->sv_env.en_opcode = sxmm->sv_env.en_opcode;
	s87->sv_env.en_foo = sxmm->sv_env.en_foo;
	s87->sv_env.en_fos = sxmm->sv_env.en_fos;

	/* Tag word and registers. */
	for (i = 0; i < 8; i++) {
		if (sxmm->sv_env.en_tw & (1U << i))
			s87->sv_env.en_tw &= ~(3U << (i * 2));
		else
			s87->sv_env.en_tw |= (3U << (i * 2));

		if (sxmm->sv_ex_tw & (1U << i))
			s87->sv_ex_tw &= ~(3U << (i * 2));
		else
			s87->sv_ex_tw |= (3U << (i * 2));

		memcpy(&s87->sv_ac[i].fp_bytes, &sxmm->sv_ac[i].fp_bytes,
		    sizeof(s87->sv_ac[i].fp_bytes));
	}

	s87->sv_ex_sw = sxmm->sv_ex_sw;
}

void
process_fninit_xmm(struct savexmm *sxmm)
{
	memset(sxmm, 0, sizeof(*sxmm));
	sxmm->sv_env.en_cw = __INITIAL_NPXCW__;
	sxmm->sv_env.en_mxcsr = __INITIAL_MXCSR__;
	sxmm->sv_env.en_mxcsr_mask = fpu_mxcsr_mask;
	sxmm->sv_env.en_sw = 0x0000;
	sxmm->sv_env.en_tw = 0x00;
}

int
process_read_regs(struct proc *p, struct reg *regs)
{
	struct trapframe *tf = process_frame(p);

#ifdef VM86
	if (tf->tf_eflags & PSL_VM) {
		regs->r_gs = tf->tf_vm86_gs & 0xffff;
		regs->r_fs = tf->tf_vm86_fs & 0xffff;
		regs->r_es = tf->tf_vm86_es & 0xffff;
		regs->r_ds = tf->tf_vm86_ds & 0xffff;
		regs->r_eflags = get_vflags(p);
	} else
#endif
	{
		regs->r_gs = tf->tf_gs & 0xffff;
		regs->r_fs = tf->tf_fs & 0xffff;
		regs->r_es = tf->tf_es & 0xffff;
		regs->r_ds = tf->tf_ds & 0xffff;
		regs->r_eflags = tf->tf_eflags;
	}
	regs->r_edi = tf->tf_edi;
	regs->r_esi = tf->tf_esi;
	regs->r_ebp = tf->tf_ebp;
	regs->r_ebx = tf->tf_ebx;
	regs->r_edx = tf->tf_edx;
	regs->r_ecx = tf->tf_ecx;
	regs->r_eax = tf->tf_eax;
	regs->r_eip = tf->tf_eip;
	regs->r_cs = tf->tf_cs & 0xffff;
	regs->r_esp = tf->tf_esp;
	regs->r_ss = tf->tf_ss & 0xffff;

	return (0);
}

int
process_read_fpregs(struct proc *p, struct fpreg *regs)
{
	union savefpu *frame = process_fpframe(p);

	if (p->p_md.md_flags & MDP_USEDFPU) {
#if NNPX > 0
		npxsave_proc(p, 1);
#endif
	} else {
		/* Fake a FNINIT. */
		if (i386_use_fxsave) {
			process_fninit_xmm(&frame->sv_xmm);
		} else {
			memset(&frame->sv_87, 0, sizeof(frame->sv_87));
			frame->sv_87.sv_env.en_cw = __INITIAL_NPXCW__;
			frame->sv_87.sv_env.en_sw = 0x0000;
			frame->sv_87.sv_env.en_tw = 0xffff;
		}
		p->p_md.md_flags |= MDP_USEDFPU;
	}

	if (i386_use_fxsave) {
		struct save87 s87;

		/* XXX Yuck */
		process_xmm_to_s87(&frame->sv_xmm, &s87);
		memcpy(regs, &s87, sizeof(*regs));
	} else
		memcpy(regs, &frame->sv_87, sizeof(*regs));

	return (0);
}

#ifdef PTRACE

void
process_s87_to_xmm(const struct save87 *s87, struct savexmm *sxmm)
{
	int i;

	/* FPU control/status */
	sxmm->sv_env.en_cw = s87->sv_env.en_cw;
	sxmm->sv_env.en_sw = s87->sv_env.en_sw;
	/* tag word handled below */
	sxmm->sv_env.en_fip = s87->sv_env.en_fip;
	sxmm->sv_env.en_fcs = s87->sv_env.en_fcs;
	sxmm->sv_env.en_opcode = s87->sv_env.en_opcode;
	sxmm->sv_env.en_foo = s87->sv_env.en_foo;
	sxmm->sv_env.en_fos = s87->sv_env.en_fos;

	/* Tag word and registers. */
	for (i = 0; i < 8; i++) {
		if (((s87->sv_env.en_tw >> (i * 2)) & 3) == 3)
			sxmm->sv_env.en_tw &= ~(1U << i);
		else
			sxmm->sv_env.en_tw |= (1U << i);

		if (((s87->sv_ex_tw >> (i * 2)) & 3) == 3)
			sxmm->sv_ex_tw &= ~(1U << i);
		else
			sxmm->sv_ex_tw |= (1U << i);

		memcpy(&sxmm->sv_ac[i].fp_bytes, &s87->sv_ac[i].fp_bytes,
		    sizeof(sxmm->sv_ac[i].fp_bytes));
	}

	sxmm->sv_ex_sw = s87->sv_ex_sw;
}

int
process_write_regs(struct proc *p, struct reg *regs)
{
	struct trapframe *tf = process_frame(p);

#ifdef VM86
	if (tf->tf_eflags & PSL_VM) {
		tf->tf_vm86_gs = regs->r_gs & 0xffff;
		tf->tf_vm86_fs = regs->r_fs & 0xffff;
		tf->tf_vm86_es = regs->r_es & 0xffff;
		tf->tf_vm86_ds = regs->r_ds & 0xffff;
		set_vflags(p, regs->r_eflags);
	} else
#endif
	{
		/*
		 * Check for security violations.
		 */
		if (((regs->r_eflags ^ tf->tf_eflags) & PSL_USERSTATIC) != 0 ||
		    !USERMODE(regs->r_cs, regs->r_eflags))
			return (EINVAL);

		tf->tf_gs = regs->r_gs & 0xffff;
		tf->tf_fs = regs->r_fs & 0xffff;
		tf->tf_es = regs->r_es & 0xffff;
		tf->tf_ds = regs->r_ds & 0xffff;
		tf->tf_eflags = regs->r_eflags;
	}
	tf->tf_edi = regs->r_edi;
	tf->tf_esi = regs->r_esi;
	tf->tf_ebp = regs->r_ebp;
	tf->tf_ebx = regs->r_ebx;
	tf->tf_edx = regs->r_edx;
	tf->tf_ecx = regs->r_ecx;
	tf->tf_eax = regs->r_eax;
	tf->tf_eip = regs->r_eip;
	tf->tf_cs = regs->r_cs & 0xffff;
	tf->tf_esp = regs->r_esp;
	tf->tf_ss = regs->r_ss & 0xffff;

	return (0);
}

int
process_write_fpregs(struct proc *p, struct fpreg *regs)
{
	union savefpu *frame = process_fpframe(p);

	if (p->p_md.md_flags & MDP_USEDFPU) {
#if NNPX > 0
		npxsave_proc(p, 0);
#endif
	} else {
		/*
		 * Make sure MXCSR and the XMM registers are
		 * initialized to sane defaults.
		 */
		if (i386_use_fxsave)
			process_fninit_xmm(&frame->sv_xmm);
		p->p_md.md_flags |= MDP_USEDFPU;
	}

	if (i386_use_fxsave) {
		struct save87 s87;

		/* XXX Yuck. */
		memcpy(&s87, regs, sizeof(*regs));
		process_s87_to_xmm(&s87, &frame->sv_xmm);
	} else
		memcpy(&frame->sv_87, regs, sizeof(*regs));

	return (0);
}

int
process_read_xmmregs(struct proc *p, struct xmmregs *regs)
{
	union savefpu *frame = process_fpframe(p);

	if (!i386_use_fxsave)
		return (EINVAL);

	if (p->p_md.md_flags & MDP_USEDFPU) {
#if NNPX > 0
		npxsave_proc(p, 1);
#endif
	} else {
		/* Fake a FNINIT. */
		process_fninit_xmm(&frame->sv_xmm);
		p->p_md.md_flags |= MDP_USEDFPU;
	}

	memcpy(regs, &frame->sv_xmm, sizeof(*regs));
	return (0);
}

int
process_write_xmmregs(struct proc *p, const struct xmmregs *regs)
{
	union savefpu *frame = process_fpframe(p);

	if (!i386_use_fxsave)
		return (EINVAL);

	if (p->p_md.md_flags & MDP_USEDFPU) {
#if NNPX > 0
		npxsave_proc(p, 0);
#endif
	} else
		p->p_md.md_flags |= MDP_USEDFPU;

	memcpy(&frame->sv_xmm, regs, sizeof(*regs));
	frame->sv_xmm.sv_env.en_mxcsr &= fpu_mxcsr_mask;
	return (0);
}

int
process_sstep(struct proc *p, int sstep)
{
	struct trapframe *tf = process_frame(p);

	if (sstep)
		tf->tf_eflags |= PSL_T;
	else
		tf->tf_eflags &= ~PSL_T;

	return (0);
}

int
process_set_pc(struct proc *p, caddr_t addr)
{
	struct trapframe *tf = process_frame(p);

	tf->tf_eip = (int)addr;

	return (0);
}

#endif	/* PTRACE */


code:
/*	$OpenBSD: rbus_machdep.c,v 1.27 2010/09/22 02:28:37 jsg Exp $ */
/*	$NetBSD: rbus_machdep.c,v 1.2 1999/10/15 06:43:06 haya Exp $	*/

/*
 * Copyright (c) 1999
 *     HAYAKAWA Koichi.  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.
 *
 * 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.
 */

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

#include <uvm/uvm_extern.h>

#include <machine/bus.h>
#include <dev/cardbus/rbus.h>

#include <dev/pci/pcivar.h>

#ifndef RBUS_MEM_START
/* Avoid the ISA hole and everything below it. */
#define RBUS_MEM_START	0x00100000
#endif

#ifndef RBUS_IO_START
/* Try to avoid onboard legacy devices; just a guess. */
#define RBUS_IO_START	0xa000
#endif

struct rbustag rbus_null;

rbus_tag_t
rbus_pccbb_parent_mem(struct device *self, struct pci_attach_args *pa)
{
	struct extent *ex = pa->pa_memex;
	bus_addr_t start;
	bus_size_t size;

	if (ex == NULL)
		return &rbus_null;

	start = RBUS_MEM_START;
	size = ex->ex_end - start;

	return (rbus_new_root_share(pa->pa_memt, ex, start, size));
}

rbus_tag_t
rbus_pccbb_parent_io(struct device *self, struct pci_attach_args *pa)
{
	struct extent *ex = pa->pa_ioex;
	bus_addr_t start;
	bus_size_t size;

	if (ex == NULL)
		return &rbus_null;

	start = ex->ex_start;
	if (ex == pciio_ex) {
		/*
		 * We're on the root bus, or behind a subtractive
		 * decode PCI-PCI bridge.  To avoid conflicts with
		 * onboard legacy devices, we only make a subregion
		 * available.
		 */
		start = max(start, RBUS_IO_START);
	}
	size = ex->ex_end - start;

	return (rbus_new_root_share(pa->pa_iot, ex, start, size));
}

void
pccbb_attach_hook(struct device *parent, struct device *self,
    struct pci_attach_args *pa)
{
}


code:
/*	$OpenBSD: softintr.c,v 1.6 2015/07/16 05:10:14 guenther Exp $	*/
/*	$NetBSD: softintr.c,v 1.1 2003/02/26 21:26:12 fvdl Exp $	*/

/*-
 * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe.
 *
 * 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.
 */

/*
 * Generic soft interrupt implementation for NetBSD/x86.
 */

#include <sys/param.h>
#include <sys/malloc.h>

#include <machine/intr.h>

#include <uvm/uvm_extern.h>

struct i386_soft_intr i386_soft_intrs[I386_NSOFTINTR];

const int i386_soft_intr_to_ssir[I386_NSOFTINTR] = {
	SIR_CLOCK,
	SIR_NET,
	SIR_TTY,
};

/*
 * softintr_init:
 *
 *	Initialize the software interrupt system.
 */
void
softintr_init(void)
{
	struct i386_soft_intr *si;
	int i;

	for (i = 0; i < I386_NSOFTINTR; i++) {
		si = &i386_soft_intrs[i];
		TAILQ_INIT(&si->softintr_q);
		mtx_init(&si->softintr_lock, IPL_HIGH);
		si->softintr_ssir = i386_soft_intr_to_ssir[i];
	}
}

/*
 * softintr_dispatch:
 *
 *	Process pending software interrupts.
 */
void
softintr_dispatch(int which)
{
	struct i386_soft_intr *si = &i386_soft_intrs[which];
	struct i386_soft_intrhand *sih;

	KERNEL_LOCK();
	for (;;) {
		mtx_enter(&si->softintr_lock);
		sih = TAILQ_FIRST(&si->softintr_q);
		if (sih == NULL) {
			mtx_leave(&si->softintr_lock);
			break;
		}
		TAILQ_REMOVE(&si->softintr_q, sih, sih_q);
		sih->sih_pending = 0;

		uvmexp.softs++;

		mtx_leave(&si->softintr_lock);

		(*sih->sih_fn)(sih->sih_arg);
	}
	KERNEL_UNLOCK();
}

/*
 * softintr_establish:		[interface]
 *
 *	Register a software interrupt handler.
 */
void *
softintr_establish(int ipl, void (*func)(void *), void *arg)
{
	struct i386_soft_intr *si;
	struct i386_soft_intrhand *sih;
	int which;

	switch (ipl) {
	case IPL_SOFTCLOCK:
		which = I386_SOFTINTR_SOFTCLOCK;
		break;

	case IPL_SOFTNET:
		which = I386_SOFTINTR_SOFTNET;
		break;

	case IPL_TTY:
	case IPL_SOFTTTY:
		which = I386_SOFTINTR_SOFTTTY;
		break;

	default:
		panic("softintr_establish");
	}

	si = &i386_soft_intrs[which];

	sih = malloc(sizeof(*sih), M_DEVBUF, M_NOWAIT);
	if (__predict_true(sih != NULL)) {
		sih->sih_intrhead = si;
		sih->sih_fn = func;
		sih->sih_arg = arg;
		sih->sih_pending = 0;
	}
	return (sih);
}

/*
 * softintr_disestablish:	[interface]
 *
 *	Unregister a software interrupt handler.
 */
void
softintr_disestablish(void *arg)
{
	struct i386_soft_intrhand *sih = arg;
	struct i386_soft_intr *si = sih->sih_intrhead;

	mtx_enter(&si->softintr_lock);
	if (sih->sih_pending) {
		TAILQ_REMOVE(&si->softintr_q, sih, sih_q);
		sih->sih_pending = 0;
	}
	mtx_leave(&si->softintr_lock);

	free(sih, M_DEVBUF, 0);
}