Merge in support for PAPR-compliant (Power Architecture Platform
Requirements) systems from the projects/pseries branch. This in principle includes all IBM POWER hardware released in the last 15 years with the exception of POWER3-based systems when run in 64-bit mode. The main development target, however, has been the PAPR logical partition support that is the default target in KVM on POWER and QEMU -- mileage may vary on actual hardware at present. Much of the heavy lifting here was done by Andreas Tobler. Approved by: re (kib)
This commit is contained in:
parent
982b134610
commit
7a8d25c037
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=255643
@ -225,6 +225,15 @@ powerpc/ps3/ps3disk.c optional ps3
|
||||
powerpc/ps3/ps3pic.c optional ps3
|
||||
powerpc/ps3/ps3_syscons.c optional ps3 sc
|
||||
powerpc/ps3/ps3-hvcall.S optional ps3 sc
|
||||
powerpc/pseries/phyp-hvcall.S optional pseries powerpc64
|
||||
powerpc/pseries/mmu_phyp.c optional pseries powerpc64
|
||||
powerpc/pseries/phyp_console.c optional pseries powerpc64
|
||||
powerpc/pseries/platform_chrp.c optional pseries
|
||||
powerpc/pseries/plpar_iommu.c optional pseries powerpc64
|
||||
powerpc/pseries/rtas_dev.c optional pseries
|
||||
powerpc/pseries/rtas_pci.c optional pseries pci
|
||||
powerpc/pseries/vdevice.c optional pseries powerpc64
|
||||
powerpc/pseries/xics.c optional pseries powerpc64
|
||||
powerpc/psim/iobus.c optional psim
|
||||
powerpc/psim/ata_iobus.c optional ata psim
|
||||
powerpc/psim/openpic_iobus.c optional psim
|
||||
|
@ -22,6 +22,7 @@ MPC85XX opt_platform.h
|
||||
POWERMAC opt_platform.h
|
||||
PS3 opt_platform.h
|
||||
MAMBO
|
||||
PSERIES
|
||||
PSIM
|
||||
WII opt_platform.h
|
||||
|
||||
|
@ -9,6 +9,7 @@ device mem # Memory and kernel memory devices
|
||||
# UART chips on this platform
|
||||
device uart_ns8250
|
||||
|
||||
options GEOM_PART_BSD
|
||||
options GEOM_PART_MBR
|
||||
|
||||
options NEW_PCIB
|
||||
|
@ -30,6 +30,7 @@ makeoptions WITH_CTF=1
|
||||
options POWERMAC #NewWorld Apple PowerMacs
|
||||
options PSIM #GDB PSIM ppc simulator
|
||||
options MAMBO #IBM Mambo Full System Simulator
|
||||
options PSERIES #PAPR-compliant systems
|
||||
|
||||
options SCHED_ULE #ULE scheduler
|
||||
options PREEMPTION #Enable kernel thread preemption
|
||||
|
@ -30,6 +30,7 @@ makeoptions WITH_CTF=1
|
||||
options POWERMAC #NewWorld Apple PowerMacs
|
||||
options PS3 #Sony Playstation 3
|
||||
options MAMBO #IBM Mambo Full System Simulator
|
||||
options PSERIES #PAPR-compliant systems (e.g. IBM p)
|
||||
|
||||
options SCHED_ULE #ULE scheduler
|
||||
options PREEMPTION #Enable kernel thread preemption
|
||||
@ -131,6 +132,9 @@ device uart
|
||||
device uart_z8530
|
||||
|
||||
# Ethernet hardware
|
||||
device em # Intel PRO/1000 Gigabit Ethernet Family
|
||||
device igb # Intel PRO/1000 PCIE Server Gigabit Family
|
||||
device ixgbe # Intel PRO/10GbE PCIE Ethernet Family
|
||||
device glc # Sony Playstation 3 Ethernet
|
||||
|
||||
# PCI Ethernet NICs that use the common MII bus controller code.
|
||||
@ -139,6 +143,8 @@ device bge # Broadcom BCM570xx Gigabit Ethernet
|
||||
device gem # Sun GEM/Sun ERI/Apple GMAC
|
||||
device dc # DEC/Intel 21143 and various workalikes
|
||||
device fxp # Intel EtherExpress PRO/100B (82557, 82558)
|
||||
device re # RealTek 8139C+/8169/8169S/8110S
|
||||
device rl # RealTek 8129/8139
|
||||
|
||||
# Pseudo devices.
|
||||
device loop # Network loopback
|
||||
|
420
sys/powerpc/pseries/mmu_phyp.c
Normal file
420
sys/powerpc/pseries/mmu_phyp.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Andreas Tobler
|
||||
* 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 TOOLS GMBH 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/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/ktr.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/msgbuf.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/vmmeter.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <machine/ofw_machdep.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/vm_kern.h>
|
||||
#include <vm/vm_page.h>
|
||||
#include <vm/vm_map.h>
|
||||
#include <vm/vm_object.h>
|
||||
#include <vm/vm_extern.h>
|
||||
#include <vm/vm_pageout.h>
|
||||
#include <vm/uma.h>
|
||||
|
||||
#include <powerpc/aim/mmu_oea64.h>
|
||||
|
||||
#include "mmu_if.h"
|
||||
#include "moea64_if.h"
|
||||
|
||||
#include "phyp-hvcall.h"
|
||||
|
||||
extern int n_slbs;
|
||||
|
||||
/*
|
||||
* Kernel MMU interface
|
||||
*/
|
||||
|
||||
static void mphyp_bootstrap(mmu_t mmup, vm_offset_t kernelstart,
|
||||
vm_offset_t kernelend);
|
||||
static void mphyp_cpu_bootstrap(mmu_t mmup, int ap);
|
||||
static void mphyp_pte_synch(mmu_t, uintptr_t pt, struct lpte *pvo_pt);
|
||||
static void mphyp_pte_clear(mmu_t, uintptr_t pt, struct lpte *pvo_pt,
|
||||
uint64_t vpn, u_int64_t ptebit);
|
||||
static void mphyp_pte_unset(mmu_t, uintptr_t pt, struct lpte *pvo_pt,
|
||||
uint64_t vpn);
|
||||
static void mphyp_pte_change(mmu_t, uintptr_t pt, struct lpte *pvo_pt,
|
||||
uint64_t vpn);
|
||||
static int mphyp_pte_insert(mmu_t, u_int ptegidx, struct lpte *pvo_pt);
|
||||
static uintptr_t mphyp_pvo_to_pte(mmu_t, const struct pvo_entry *pvo);
|
||||
|
||||
#define VSID_HASH_MASK 0x0000007fffffffffULL
|
||||
|
||||
|
||||
static mmu_method_t mphyp_methods[] = {
|
||||
MMUMETHOD(mmu_bootstrap, mphyp_bootstrap),
|
||||
MMUMETHOD(mmu_cpu_bootstrap, mphyp_cpu_bootstrap),
|
||||
|
||||
MMUMETHOD(moea64_pte_synch, mphyp_pte_synch),
|
||||
MMUMETHOD(moea64_pte_clear, mphyp_pte_clear),
|
||||
MMUMETHOD(moea64_pte_unset, mphyp_pte_unset),
|
||||
MMUMETHOD(moea64_pte_change, mphyp_pte_change),
|
||||
MMUMETHOD(moea64_pte_insert, mphyp_pte_insert),
|
||||
MMUMETHOD(moea64_pvo_to_pte, mphyp_pvo_to_pte),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
MMU_DEF_INHERIT(pseries_mmu, "mmu_phyp", mphyp_methods, 0, oea64_mmu);
|
||||
|
||||
static void
|
||||
mphyp_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend)
|
||||
{
|
||||
uint64_t final_pteg_count = 0;
|
||||
char buf[8];
|
||||
uint32_t prop[2];
|
||||
uint32_t nptlp, shift = 0, slb_encoding = 0;
|
||||
phandle_t dev, node, root;
|
||||
int idx, len, res;
|
||||
|
||||
moea64_early_bootstrap(mmup, kernelstart, kernelend);
|
||||
|
||||
root = OF_peer(0);
|
||||
|
||||
dev = OF_child(root);
|
||||
while (dev != 0) {
|
||||
res = OF_getprop(dev, "name", buf, sizeof(buf));
|
||||
if (res > 0 && strcmp(buf, "cpus") == 0)
|
||||
break;
|
||||
dev = OF_peer(dev);
|
||||
}
|
||||
|
||||
node = OF_child(dev);
|
||||
|
||||
while (node != 0) {
|
||||
res = OF_getprop(node, "device_type", buf, sizeof(buf));
|
||||
if (res > 0 && strcmp(buf, "cpu") == 0)
|
||||
break;
|
||||
node = OF_peer(node);
|
||||
}
|
||||
|
||||
res = OF_getprop(node, "ibm,pft-size", prop, sizeof(prop));
|
||||
if (res <= 0)
|
||||
panic("mmu_phyp: unknown PFT size");
|
||||
final_pteg_count = 1 << prop[1];
|
||||
res = OF_getprop(node, "ibm,slb-size", prop, sizeof(prop[0]));
|
||||
if (res > 0)
|
||||
n_slbs = prop[0];
|
||||
|
||||
moea64_pteg_count = final_pteg_count / sizeof(struct lpteg);
|
||||
|
||||
/*
|
||||
* Scan the large page size property for PAPR compatible machines.
|
||||
* See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties'
|
||||
* for the encoding of the property.
|
||||
*/
|
||||
|
||||
len = OF_getproplen(node, "ibm,segment-page-sizes");
|
||||
if (len > 0) {
|
||||
/*
|
||||
* We have to use a variable length array on the stack
|
||||
* since we have very limited stack space.
|
||||
*/
|
||||
cell_t arr[len/sizeof(cell_t)];
|
||||
res = OF_getprop(node, "ibm,segment-page-sizes", &arr,
|
||||
sizeof(arr));
|
||||
len /= 4;
|
||||
idx = 0;
|
||||
while (len > 0) {
|
||||
shift = arr[idx];
|
||||
slb_encoding = arr[idx + 1];
|
||||
nptlp = arr[idx + 2];
|
||||
idx += 3;
|
||||
len -= 3;
|
||||
while (len > 0 && nptlp) {
|
||||
idx += 2;
|
||||
len -= 2;
|
||||
nptlp--;
|
||||
}
|
||||
}
|
||||
moea64_large_page_shift = shift;
|
||||
moea64_large_page_size = 1 << shift;
|
||||
}
|
||||
|
||||
moea64_mid_bootstrap(mmup, kernelstart, kernelend);
|
||||
moea64_late_bootstrap(mmup, kernelstart, kernelend);
|
||||
}
|
||||
|
||||
static void
|
||||
mphyp_cpu_bootstrap(mmu_t mmup, int ap)
|
||||
{
|
||||
struct slb *slb = PCPU_GET(slb);
|
||||
register_t seg0;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Install kernel SLB entries
|
||||
*/
|
||||
|
||||
__asm __volatile ("slbia");
|
||||
__asm __volatile ("slbmfee %0,%1; slbie %0;" : "=r"(seg0) : "r"(0));
|
||||
for (i = 0; i < 64; i++) {
|
||||
if (!(slb[i].slbe & SLBE_VALID))
|
||||
continue;
|
||||
|
||||
__asm __volatile ("slbmte %0, %1" ::
|
||||
"r"(slb[i].slbv), "r"(slb[i].slbe));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mphyp_pte_synch(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt)
|
||||
{
|
||||
struct lpte pte;
|
||||
uint64_t junk;
|
||||
|
||||
phyp_pft_hcall(H_READ, 0, slot, 0, 0, &pte.pte_hi, &pte.pte_lo,
|
||||
&junk);
|
||||
|
||||
pvo_pt->pte_lo |= pte.pte_lo & (LPTE_CHG | LPTE_REF);
|
||||
}
|
||||
|
||||
static void
|
||||
mphyp_pte_clear(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn,
|
||||
u_int64_t ptebit)
|
||||
{
|
||||
|
||||
if (ptebit & LPTE_CHG)
|
||||
phyp_hcall(H_CLEAR_MOD, 0, slot);
|
||||
if (ptebit & LPTE_REF)
|
||||
phyp_hcall(H_CLEAR_REF, 0, slot);
|
||||
}
|
||||
|
||||
static void
|
||||
mphyp_pte_unset(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn)
|
||||
{
|
||||
|
||||
/* XXX: last argument can check the VPN -- set flag to enable */
|
||||
phyp_hcall(H_REMOVE, 0, slot, vpn);
|
||||
}
|
||||
|
||||
static void
|
||||
mphyp_pte_change(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn)
|
||||
{
|
||||
struct lpte evicted;
|
||||
uint64_t index, junk;
|
||||
int64_t result;
|
||||
|
||||
/*
|
||||
* NB: this is protected by the global table lock, so this two-step
|
||||
* is safe, except for the scratch-page case. No CPUs on which we run
|
||||
* this code should be using scratch pages.
|
||||
*/
|
||||
KASSERT(!(pvo_pt->pte_hi & LPTE_LOCKED),
|
||||
("Locked pages not supported on PHYP"));
|
||||
|
||||
/* XXX: optimization using H_PROTECT for common case? */
|
||||
result = phyp_hcall(H_REMOVE, 0, slot, vpn);
|
||||
if (result != H_SUCCESS)
|
||||
panic("mphyp_pte_change() invalidation failure: %ld\n", result);
|
||||
result = phyp_pft_hcall(H_ENTER, H_EXACT, slot, pvo_pt->pte_hi,
|
||||
pvo_pt->pte_lo, &index, &evicted.pte_lo, &junk);
|
||||
if (result != H_SUCCESS)
|
||||
panic("mphyp_pte_change() insertion failure: %ld\n", result);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
mphyp_pte_spillable_ident(u_int ptegidx, struct lpte *to_evict)
|
||||
{
|
||||
uint64_t slot, junk, k;
|
||||
struct lpte pt;
|
||||
int i, j;
|
||||
|
||||
/* Start at a random slot */
|
||||
i = mftb() % 8;
|
||||
k = -1;
|
||||
for (j = 0; j < 8; j++) {
|
||||
slot = (ptegidx << 3) + (i + j) % 8;
|
||||
phyp_pft_hcall(H_READ, 0, slot, 0, 0, &pt.pte_hi, &pt.pte_lo,
|
||||
&junk);
|
||||
|
||||
if (pt.pte_hi & LPTE_SWBITS)
|
||||
continue;
|
||||
|
||||
/* This is a candidate, so remember it */
|
||||
k = slot;
|
||||
|
||||
/* Try to get a page that has not been used lately */
|
||||
if (!(pt.pte_lo & LPTE_REF)) {
|
||||
memcpy(to_evict, &pt, sizeof(struct lpte));
|
||||
return (k);
|
||||
}
|
||||
}
|
||||
|
||||
phyp_pft_hcall(H_READ, 0, slot, 0, 0, &to_evict->pte_hi,
|
||||
&to_evict->pte_lo, &junk);
|
||||
return (k);
|
||||
}
|
||||
|
||||
static int
|
||||
mphyp_pte_insert(mmu_t mmu, u_int ptegidx, struct lpte *pvo_pt)
|
||||
{
|
||||
int64_t result;
|
||||
struct lpte evicted;
|
||||
struct pvo_entry *pvo;
|
||||
uint64_t index, junk;
|
||||
u_int pteg_bktidx;
|
||||
|
||||
/* Check for locked pages, which we can't support on this system */
|
||||
KASSERT(!(pvo_pt->pte_hi & LPTE_LOCKED),
|
||||
("Locked pages not supported on PHYP"));
|
||||
|
||||
/* Initialize PTE */
|
||||
pvo_pt->pte_hi |= LPTE_VALID;
|
||||
pvo_pt->pte_hi &= ~LPTE_HID;
|
||||
evicted.pte_hi = 0;
|
||||
|
||||
/*
|
||||
* First try primary hash.
|
||||
*/
|
||||
pteg_bktidx = ptegidx;
|
||||
result = phyp_pft_hcall(H_ENTER, 0, pteg_bktidx << 3, pvo_pt->pte_hi,
|
||||
pvo_pt->pte_lo, &index, &evicted.pte_lo, &junk);
|
||||
if (result == H_SUCCESS)
|
||||
return (index & 0x07);
|
||||
KASSERT(result == H_PTEG_FULL, ("Page insertion error: %ld "
|
||||
"(ptegidx: %#x/%#x, PTE %#lx/%#lx", result, ptegidx,
|
||||
moea64_pteg_count, pvo_pt->pte_hi, pvo_pt->pte_lo));
|
||||
|
||||
/*
|
||||
* Next try secondary hash.
|
||||
*/
|
||||
pteg_bktidx ^= moea64_pteg_mask;
|
||||
pvo_pt->pte_hi |= LPTE_HID;
|
||||
result = phyp_pft_hcall(H_ENTER, 0, pteg_bktidx << 3,
|
||||
pvo_pt->pte_hi, pvo_pt->pte_lo, &index, &evicted.pte_lo, &junk);
|
||||
if (result == H_SUCCESS)
|
||||
return (index & 0x07);
|
||||
KASSERT(result == H_PTEG_FULL, ("Secondary page insertion error: %ld",
|
||||
result));
|
||||
|
||||
/*
|
||||
* Out of luck. Find a PTE to sacrifice.
|
||||
*/
|
||||
pteg_bktidx = ptegidx;
|
||||
index = mphyp_pte_spillable_ident(pteg_bktidx, &evicted);
|
||||
if (index == -1L) {
|
||||
pteg_bktidx ^= moea64_pteg_mask;
|
||||
index = mphyp_pte_spillable_ident(pteg_bktidx, &evicted);
|
||||
}
|
||||
|
||||
if (index == -1L) {
|
||||
/* No freeable slots in either PTEG? We're hosed. */
|
||||
panic("mphyp_pte_insert: overflow");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (pteg_bktidx == ptegidx)
|
||||
pvo_pt->pte_hi &= ~LPTE_HID;
|
||||
else
|
||||
pvo_pt->pte_hi |= LPTE_HID;
|
||||
|
||||
/*
|
||||
* Synchronize the sacrifice PTE with its PVO, then mark both
|
||||
* invalid. The PVO will be reused when/if the VM system comes
|
||||
* here after a fault.
|
||||
*/
|
||||
|
||||
if (evicted.pte_hi & LPTE_HID)
|
||||
pteg_bktidx ^= moea64_pteg_mask; /* PTEs indexed by primary */
|
||||
|
||||
LIST_FOREACH(pvo, &moea64_pvo_table[pteg_bktidx], pvo_olink) {
|
||||
if (pvo->pvo_pte.lpte.pte_hi == evicted.pte_hi) {
|
||||
KASSERT(pvo->pvo_pte.lpte.pte_hi & LPTE_VALID,
|
||||
("Invalid PVO for valid PTE!"));
|
||||
phyp_hcall(H_REMOVE, 0, index, 0);
|
||||
PVO_PTEGIDX_CLR(pvo);
|
||||
moea64_pte_overflow++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
KASSERT(pvo->pvo_pte.lpte.pte_hi == evicted.pte_hi,
|
||||
("Unable to find PVO for spilled PTE"));
|
||||
|
||||
/*
|
||||
* Set the new PTE.
|
||||
*/
|
||||
result = phyp_pft_hcall(H_ENTER, H_EXACT, index, pvo_pt->pte_hi,
|
||||
pvo_pt->pte_lo, &index, &evicted.pte_lo, &junk);
|
||||
if (result == H_SUCCESS)
|
||||
return (index & 0x07);
|
||||
|
||||
panic("Page replacement error: %ld", result);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static __inline u_int
|
||||
va_to_pteg(uint64_t vsid, vm_offset_t addr, int large)
|
||||
{
|
||||
uint64_t hash;
|
||||
int shift;
|
||||
|
||||
shift = large ? moea64_large_page_shift : ADDR_PIDX_SHFT;
|
||||
hash = (vsid & VSID_HASH_MASK) ^ (((uint64_t)addr & ADDR_PIDX) >>
|
||||
shift);
|
||||
return (hash & moea64_pteg_mask);
|
||||
}
|
||||
|
||||
static uintptr_t
|
||||
mphyp_pvo_to_pte(mmu_t mmu, const struct pvo_entry *pvo)
|
||||
{
|
||||
uint64_t vsid;
|
||||
u_int ptegidx;
|
||||
|
||||
/* If the PTEG index is not set, then there is no page table entry */
|
||||
if (!PVO_PTEGIDX_ISSET(pvo))
|
||||
return (-1);
|
||||
|
||||
vsid = PVO_VSID(pvo);
|
||||
ptegidx = va_to_pteg(vsid, PVO_VADDR(pvo), pvo->pvo_vaddr & PVO_LARGE);
|
||||
|
||||
/*
|
||||
* We can find the actual pte entry without searching by grabbing
|
||||
* the PTEG index from 3 unused bits in pvo_vaddr and by
|
||||
* noticing the HID bit.
|
||||
*/
|
||||
if (pvo->pvo_pte.lpte.pte_hi & LPTE_HID)
|
||||
ptegidx ^= moea64_pteg_mask;
|
||||
|
||||
return ((ptegidx << 3) | PVO_PTEGIDX_GET(pvo));
|
||||
}
|
||||
|
68
sys/powerpc/pseries/phyp-hvcall.S
Normal file
68
sys/powerpc/pseries/phyp-hvcall.S
Normal file
@ -0,0 +1,68 @@
|
||||
/*-
|
||||
* Copyright (C) 2010 Andreas Tobler
|
||||
* 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 TOOLS GMBH 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <machine/asm.h>
|
||||
|
||||
/* Hypervisor entry call. */
|
||||
#define hc .long 0x44000022
|
||||
|
||||
/*
|
||||
* Simple HV calls take the same arguments, with the same ABI, as this
|
||||
* C function
|
||||
*/
|
||||
ASENTRY(phyp_hcall)
|
||||
mflr %r0
|
||||
std %r0,16(%r1)
|
||||
hc /* invoke the hypervisor */
|
||||
ld %r0,16(%r1)
|
||||
mtlr %r0
|
||||
blr /* return r3 = status */
|
||||
|
||||
/*
|
||||
* PFT HV calls take a special ABI (see PAPR 14.5.4.1)
|
||||
*
|
||||
* r3-r7 arguments passed unchanged, r8-r10 are addresses of return values
|
||||
* HV takes the same r3-r7, but returns values in r3, r4-r6
|
||||
*/
|
||||
ASENTRY(phyp_pft_hcall)
|
||||
mflr %r0
|
||||
std %r0,16(%r1)
|
||||
stdu %r1,-80(%r1)
|
||||
std %r8,48(%r1) /* save arguments */
|
||||
std %r9,56(%r1)
|
||||
std %r10,64(%r1)
|
||||
hc /* invoke the hypervisor */
|
||||
ld %r11,48(%r1) /* store results */
|
||||
std %r4,0(%r11)
|
||||
ld %r11,56(%r1)
|
||||
std %r5,0(%r11)
|
||||
ld %r11,64(%r1)
|
||||
std %r6,0(%r11)
|
||||
ld %r1,0(%r1) /* exit */
|
||||
ld %r0,16(%r1)
|
||||
mtlr %r0
|
||||
blr /* return r3 = status */
|
||||
|
305
sys/powerpc/pseries/phyp-hvcall.h
Normal file
305
sys/powerpc/pseries/phyp-hvcall.h
Normal file
@ -0,0 +1,305 @@
|
||||
/*-
|
||||
* Copyright (C) 2010 Andreas Tobler
|
||||
* 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 TOOLS GMBH 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _PSERIES_PHYP_HVCALL_H_
|
||||
#define _PSERIES_PHYP_HVCALL_H_
|
||||
|
||||
/* Information taken from: Power.org PAPR, Version 2.4 (December 7, 2009). */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/* Return codes. */
|
||||
|
||||
#define H_SUCCESS 0
|
||||
#define H_BUSY 1 /* Hardware Busy -- Retry Later. */
|
||||
#define H_CLOSED 2 /* Virtual I/O connection is closed. */
|
||||
#define H_NOT_AVAILABLE 3
|
||||
#define H_CONSTRAINED 4 /* The request called for resources in excess of
|
||||
the maximum allowed. The resultant allocation
|
||||
was constrained to maximum allowed. */
|
||||
#define H_PARTIAL 5 /* The request completed only partially successful.
|
||||
Parameters were valid but some specific hcall
|
||||
function condition prevented fully completing the
|
||||
architected function, see the specific hcall
|
||||
definition for possible reasons. */
|
||||
#define H_IN_PROGRESS 14
|
||||
#define H_PAGE_REGISTERED 15
|
||||
#define H_PARTIAL_STORE 16
|
||||
#define H_PENDING 17
|
||||
#define H_CONTINUE 18
|
||||
|
||||
#define H_LONG_BUSY_ORDER_1_MS 9900 /* This return code is identical to
|
||||
H_BUSY, but with the added bonus of a
|
||||
hint to the partition OS. If the
|
||||
partition OS can delay for 1
|
||||
millisecond, the hcall will likely
|
||||
succeed on a new hcall with no further
|
||||
busy return codes. If the partition OS
|
||||
cannot handle a delay, they are
|
||||
certainly free to immediately turn
|
||||
around and try again. */
|
||||
#define H_LONG_BUSY_ORDER_10_MS 9901 /* Similar to H_LONG_BUSY_ORDER_1_MS, but
|
||||
the hint is 10mSec wait this time. */
|
||||
|
||||
#define H_LONG_BUSY_ORDER_100_MS 9902 /* Similar to H_LONG_BUSY_ORDER_1_MS, but
|
||||
the hint is 100mSec wait this time. */
|
||||
|
||||
#define H_LONG_BUSY_ORDER_1_S 9903 /* Similar to H_LONG_BUSY_ORDER_1_MS, but
|
||||
the hint is 1Sec wait this time. */
|
||||
#define H_LONG_BUSY_ORDER_10_S 9904 /* Similar to H_LONG_BUSY_ORDER_1_MS, but
|
||||
the hint is 10Sec wait this time. */
|
||||
#define H_LONG_BUSY_ORDER_100_S 9905 /* Similar to H_LONG_BUSY_ORDER_1_MS, but
|
||||
the hint is 100Sec wait this time. */
|
||||
|
||||
#define H_HARDWARE -1 /* Error. */
|
||||
#define H_FUNCTION -2 /* Not supported. */
|
||||
#define H_PRIVILEGE -3 /* Caller not in privileged mode. */
|
||||
#define H_PARAMETER -4 /* Outside valid range for partition or conflicting. */
|
||||
#define H_BAD_MODE -5 /* Illegal MSR value. */
|
||||
#define H_PTEG_FULL -6 /* The requested pteg was full. */
|
||||
#define H_NOT_FOUND -7 /* The requested entitiy was not found. */
|
||||
#define H_RESERVED_DABR -8 /* The requested address is reserved by the
|
||||
hypervisor on this processor. */
|
||||
#define H_NOMEM -9
|
||||
#define H_AUTHORITY -10 /* The caller did not have authority to perform the
|
||||
function. */
|
||||
#define H_PERMISSION -11 /* The mapping specified by the request does not
|
||||
allow for the requested transfer. */
|
||||
#define H_DROPPED -12 /* One or more packets could not be delivered to
|
||||
their requested destinations. */
|
||||
#define H_S_PARM -13 /* The source parameter is illegal. */
|
||||
#define H_D_PARM -14 /* The destination parameter is illegal. */
|
||||
#define H_R_PARM -15 /* The remote TCE mapping is illegal. */
|
||||
#define H_RESOURCE -16 /* One or more required resources are in use. */
|
||||
#define H_ADAPTER_PARM -17 /* Invalid adapter. */
|
||||
#define H_RH_PARM -18 /* Resource not valid or logical partition
|
||||
conflicting. */
|
||||
#define H_RCQ_PARM -19 /* RCQ not valid or logical partition conflicting. */
|
||||
#define H_SCQ_PARM -20 /* SCQ not valid or logical partition conflicting. */
|
||||
#define H_EQ_PARM -21 /* EQ not valid or logical partition conflicting. */
|
||||
#define H_RT_PARM -22 /* Invalid resource type. */
|
||||
#define H_ST_PARM -23 /* Invalid service type. */
|
||||
#define H_SIGT_PARM -24 /* Invalid signalling type. */
|
||||
#define H_TOKEN_PARM -25 /* Invalid token. */
|
||||
#define H_MLENGTH_PARM -27 /* Invalid memory length. */
|
||||
#define H_MEM_PARM -28 /* Invalid memory I/O virtual address. */
|
||||
#define H_MEM_ACCESS_PARM -29 /* Invalid memory access control. */
|
||||
#define H_ATTR_PARM -30 /* Invalid attribute value. */
|
||||
#define H_PORT_PARM -31 /* Invalid port number. */
|
||||
#define H_MCG_PARM -32 /* Invalid multicast group. */
|
||||
#define H_VL_PARM -33 /* Invalid virtual lane. */
|
||||
#define H_TSIZE_PARM -34 /* Invalid trace size. */
|
||||
#define H_TRACE_PARM -35 /* Invalid trace buffer. */
|
||||
#define H_MASK_PARM -37 /* Invalid mask value. */
|
||||
#define H_MCG_FULL -38 /* Multicast attachments exceeded. */
|
||||
#define H_ALIAS_EXIST -39 /* Alias QP already defined. */
|
||||
#define H_P_COUNTER -40 /* Invalid counter specification. */
|
||||
#define H_TABLE_FULL -41 /* Resource page table full. */
|
||||
#define H_ALT_TABLE -42 /* Alternate table already exists / alternate page
|
||||
table not available. */
|
||||
#define H_MR_CONDITION -43 /* Invalid memory region condition. */
|
||||
#define H_NOT_ENOUGH_RESOURCES -44 /* Insufficient resources. */
|
||||
#define H_R_STATE -45 /* Invalid resource state condition or sequencing
|
||||
error. */
|
||||
#define H_RESCINDED -46
|
||||
#define H_ABORTED -54
|
||||
#define H_P2 -55
|
||||
#define H_P3 -56
|
||||
#define H_P4 -57
|
||||
#define H_P5 -58
|
||||
#define H_P6 -59
|
||||
#define H_P7 -60
|
||||
#define H_P8 -61
|
||||
#define H_P9 -62
|
||||
#define H_NOOP -63
|
||||
#define H_TOO_BIG -64
|
||||
|
||||
#define H_UNSUPPORTED -67 /* Parameter value outside of the range supported
|
||||
by this implementation. */
|
||||
|
||||
/* Flags. */
|
||||
/* Table 168. Page Frame Table Access flags field definition. */
|
||||
#define H_EXACT (1UL<<(63-24))
|
||||
#define H_R_XLATE (1UL<<(63-25))
|
||||
#define H_READ_4 (1UL<<(63-26))
|
||||
|
||||
/* Table 178. CMO Page Usage State flags Definition. */
|
||||
#define H_PAGE_STATE_CHANGE (1UL<<(63-28))
|
||||
#define H_PAGE_UNUSED ((1UL<<(63-29)) | (1UL<<(63-30)))
|
||||
#define H_PAGE_SET_UNUSED (H_PAGE_STATE_CHANGE | H_PAGE_UNUSED)
|
||||
#define H_PAGE_SET_LOANED (H_PAGE_SET_UNUSED | (1UL<<(63-31)))
|
||||
#define H_PAGE_SET_ACTIVE H_PAGE_STATE_CHANGE
|
||||
|
||||
/* Table 168. Page Frame Table Access flags field definition. */
|
||||
#define H_AVPN (1UL<<(63-32))
|
||||
#define H_ANDCOND (1UL<<(63-33))
|
||||
|
||||
#define H_ICACHE_INVALIDATE (1UL<<(63-40))
|
||||
#define H_ICACHE_SYNCHRONIZE (1UL<<(63-41))
|
||||
|
||||
#define H_ZERO_PAGE (1UL<<(63-48))
|
||||
#define H_COPY_PAGE (1UL<<(63-49))
|
||||
|
||||
#define H_N (1UL<<(63-61))
|
||||
#define H_PP1 (1UL<<(63-62))
|
||||
#define H_PP2 (1UL<<(63-63))
|
||||
|
||||
/* pSeries hypervisor opcodes. */
|
||||
#define H_REMOVE 0x04
|
||||
#define H_ENTER 0x08
|
||||
#define H_READ 0x0c
|
||||
#define H_CLEAR_MOD 0x10
|
||||
#define H_CLEAR_REF 0x14
|
||||
#define H_PROTECT 0x18
|
||||
#define H_GET_TCE 0x1c
|
||||
#define H_PUT_TCE 0x20
|
||||
#define H_SET_SPRG0 0x24
|
||||
#define H_SET_DABR 0x28
|
||||
#define H_PAGE_INIT 0x2c
|
||||
#define H_SET_ASR 0x30
|
||||
#define H_ASR_ON 0x34
|
||||
#define H_ASR_OFF 0x38
|
||||
#define H_LOGICAL_CI_LOAD 0x3c
|
||||
#define H_LOGICAL_CI_STORE 0x40
|
||||
#define H_LOGICAL_CACHE_LOAD 0x44
|
||||
#define H_LOGICAL_CACHE_STORE 0x48
|
||||
#define H_LOGICAL_ICBI 0x4c
|
||||
#define H_LOGICAL_DCBF 0x50
|
||||
#define H_GET_TERM_CHAR 0x54
|
||||
#define H_PUT_TERM_CHAR 0x58
|
||||
#define H_REAL_TO_LOGICAL 0x5c
|
||||
#define H_HYPERVISOR_DATA 0x60
|
||||
#define H_EOI 0x64
|
||||
#define H_CPPR 0x68
|
||||
#define H_IPI 0x6c
|
||||
#define H_IPOLL 0x70
|
||||
#define H_XIRR 0x74
|
||||
#define H_MIGRATE_DMA 0x78
|
||||
#define H_PERFMON 0x7c
|
||||
#define H_REGISTER_VPA 0xdc
|
||||
#define H_CEDE 0xe0
|
||||
#define H_CONFER 0xe4
|
||||
#define H_PROD 0xe8
|
||||
#define H_GET_PPP 0xec
|
||||
#define H_SET_PPP 0xf0
|
||||
#define H_PURR 0xf4
|
||||
#define H_PIC 0xf8
|
||||
#define H_REG_CRQ 0xfc
|
||||
#define H_FREE_CRQ 0x100
|
||||
#define H_VIO_SIGNAL 0x104
|
||||
#define H_SEND_CRQ 0x108
|
||||
#define H_PUT_RTCE 0x10c
|
||||
#define H_COPY_RDMA 0x110
|
||||
#define H_REGISTER_LOGICAL_LAN 0x114
|
||||
#define H_FREE_LOGICAL_LAN 0x118
|
||||
#define H_ADD_LOGICAL_LAN_BUFFER 0x11c
|
||||
#define H_SEND_LOGICAL_LAN 0x120
|
||||
#define H_BULK_REMOVE 0x124
|
||||
#define H_WRITE_RDMA 0x128
|
||||
#define H_READ_RDMA 0x12c
|
||||
#define H_MULTICAST_CTRL 0x130
|
||||
#define H_SET_XDABR 0x134
|
||||
#define H_STUFF_TCE 0x138
|
||||
#define H_PUT_TCE_INDIRECT 0x13c
|
||||
#define H_PUT_RTCE_INDIRECT 0x140
|
||||
#define H_CHANGE_LOGICAL_LAN_MAC 0x14c
|
||||
#define H_VTERM_PARTNER_INFO 0x150
|
||||
#define H_REGISTER_VTERM 0x154
|
||||
#define H_FREE_VTERM 0x158
|
||||
/* Reserved ....
|
||||
#define H_RESET_EVENTS 0x15c
|
||||
#define H_ALLOC_RESOURCE 0x160
|
||||
#define H_FREE_RESOURCE 0x164
|
||||
#define H_MODIFY_QP 0x168
|
||||
#define H_QUERY_QP 0x16c
|
||||
#define H_REREGISTER_PMR 0x170
|
||||
#define H_REGISTER_SMR 0x174
|
||||
#define H_QUERY_MR 0x178
|
||||
#define H_QUERY_MW 0x17c
|
||||
#define H_QUERY_HCA 0x180
|
||||
#define H_QUERY_PORT 0x184
|
||||
#define H_MODIFY_PORT 0x188
|
||||
#define H_DEFINE_AQP1 0x18c
|
||||
#define H_GET_TRACE_BUFFER 0x190
|
||||
#define H_DEFINE_AQP0 0x194
|
||||
#define H_RESIZE_MR 0x198
|
||||
#define H_ATTACH_MCQP 0x19c
|
||||
#define H_DETACH_MCQP 0x1a0
|
||||
#define H_CREATE_RPT 0x1a4
|
||||
#define H_REMOVE_RPT 0x1a8
|
||||
#define H_REGISTER_RPAGES 0x1ac
|
||||
#define H_DISABLE_AND_GETC 0x1b0
|
||||
#define H_ERROR_DATA 0x1b4
|
||||
#define H_GET_HCA_INFO 0x1b8
|
||||
#define H_GET_PERF_COUNT 0x1bc
|
||||
#define H_MANAGE_TRACE 0x1c0
|
||||
.... */
|
||||
#define H_FREE_LOGICAL_LAN_BUFFER 0x1d4
|
||||
#define H_POLL_PENDING 0x1d8
|
||||
/* Reserved ....
|
||||
#define H_QUERY_INT_STATE 0x1e4
|
||||
.... */
|
||||
#define H_LIOBN_ATTRIBUTES 0x240
|
||||
#define H_ILLAN_ATTRIBUTES 0x244
|
||||
#define H_REMOVE_RTCE 0x24c
|
||||
/* Reserved ...
|
||||
#define H_MODIFY_HEA_QP 0x250
|
||||
#define H_QUERY_HEA_QP 0x254
|
||||
#define H_QUERY_HEA 0x258
|
||||
#define H_QUERY_HEA_PORT 0x25c
|
||||
#define H_MODIFY_HEA_PORT 0x260
|
||||
#define H_REG_BCMC 0x264
|
||||
#define H_DEREG_BCMC 0x268
|
||||
#define H_REGISTER_HEA_RPAGES 0x26c
|
||||
#define H_DISABLE_AND_GET_HEA 0x270
|
||||
#define H_GET_HEA_INFO 0x274
|
||||
#define H_ALLOC_HEA_RESOURCE 0x278
|
||||
#define H_ADD_CONN 0x284
|
||||
#define H_DEL_CONN 0x288
|
||||
... */
|
||||
#define H_JOIN 0x298
|
||||
#define H_DONOR_OPERATION 0x29c
|
||||
#define H_VASI_SIGNAL 0x2a0
|
||||
#define H_VASI_STATE 0x2a4
|
||||
#define H_VIOCTL 0x2a8
|
||||
#define H_VRMASD 0x2ac
|
||||
#define H_ENABLE_CRQ 0x2b0
|
||||
/* Reserved ...
|
||||
#define H_GET_EM_PARMS 0x2b8
|
||||
... */
|
||||
#define H_VPM_STAT 0x2bc
|
||||
#define H_SET_MPP 0x2d0
|
||||
#define H_GET_MPP 0x2d4
|
||||
#define MAX_HCALL_OPCODE H_GET_MPP
|
||||
|
||||
int64_t phyp_hcall(uint64_t opcode, ...);
|
||||
int64_t phyp_pft_hcall(uint64_t opcode, uint64_t flags, uint64_t pteidx,
|
||||
uint64_t pte_hi, uint64_t pte_lo, uint64_t *pteidx_out, uint64_t *ptelo_out,
|
||||
uint64_t *r6);
|
||||
|
||||
#endif /* _PSERIES_PHYP_HVCALL_H_ */
|
||||
|
420
sys/powerpc/pseries/phyp_console.c
Normal file
420
sys/powerpc/pseries/phyp_console.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*-
|
||||
* Copyright (C) 2011 by Nathan Whitehorn. 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 TOOLS GMBH 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/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kdb.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/priv.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/cons.h>
|
||||
#include <sys/tty.h>
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
#include <dev/uart/uart.h>
|
||||
#include <dev/uart/uart_cpu.h>
|
||||
#include <dev/uart/uart_bus.h>
|
||||
|
||||
#include "phyp-hvcall.h"
|
||||
#include "uart_if.h"
|
||||
|
||||
struct uart_phyp_softc {
|
||||
device_t dev;
|
||||
phandle_t node;
|
||||
int vtermid;
|
||||
|
||||
struct tty *tp;
|
||||
struct resource *irqres;
|
||||
int irqrid;
|
||||
struct callout callout;
|
||||
void *sc_icookie;
|
||||
int polltime;
|
||||
|
||||
struct mtx sc_mtx;
|
||||
int protocol;
|
||||
|
||||
union {
|
||||
uint64_t u64[2];
|
||||
char str[16];
|
||||
} phyp_inbuf;
|
||||
uint64_t inbuflen;
|
||||
uint8_t outseqno;
|
||||
};
|
||||
|
||||
static struct uart_phyp_softc *console_sc = NULL;
|
||||
#if defined(KDB)
|
||||
static int alt_break_state;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
HVTERM1, HVTERMPROT
|
||||
};
|
||||
|
||||
#define VS_DATA_PACKET_HEADER 0xff
|
||||
#define VS_CONTROL_PACKET_HEADER 0xfe
|
||||
#define VSV_SET_MODEM_CTL 0x01
|
||||
#define VSV_MODEM_CTL_UPDATE 0x02
|
||||
#define VSV_RENEGOTIATE_CONNECTION 0x03
|
||||
#define VS_QUERY_PACKET_HEADER 0xfd
|
||||
#define VSV_SEND_VERSION_NUMBER 0x01
|
||||
#define VSV_SEND_MODEM_CTL_STATUS 0x02
|
||||
#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc
|
||||
|
||||
static int uart_phyp_probe(device_t dev);
|
||||
static int uart_phyp_attach(device_t dev);
|
||||
static void uart_phyp_intr(void *v);
|
||||
|
||||
static device_method_t uart_phyp_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, uart_phyp_probe),
|
||||
DEVMETHOD(device_attach, uart_phyp_attach),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t uart_phyp_driver = {
|
||||
"uart",
|
||||
uart_phyp_methods,
|
||||
sizeof(struct uart_phyp_softc),
|
||||
};
|
||||
|
||||
DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, uart_devclass, 0, 0);
|
||||
|
||||
static cn_probe_t uart_phyp_cnprobe;
|
||||
static cn_init_t uart_phyp_cninit;
|
||||
static cn_term_t uart_phyp_cnterm;
|
||||
static cn_getc_t uart_phyp_cngetc;
|
||||
static cn_putc_t uart_phyp_cnputc;
|
||||
static cn_grab_t uart_phyp_cngrab;
|
||||
static cn_ungrab_t uart_phyp_cnungrab;
|
||||
|
||||
CONSOLE_DRIVER(uart_phyp);
|
||||
|
||||
static void uart_phyp_ttyoutwakeup(struct tty *tp);
|
||||
|
||||
static struct ttydevsw uart_phyp_tty_class = {
|
||||
.tsw_flags = TF_INITLOCK|TF_CALLOUT,
|
||||
.tsw_outwakeup = uart_phyp_ttyoutwakeup,
|
||||
};
|
||||
|
||||
static int
|
||||
uart_phyp_probe_node(struct uart_phyp_softc *sc)
|
||||
{
|
||||
phandle_t node = sc->node;
|
||||
uint32_t reg;
|
||||
char buf[64];
|
||||
|
||||
sc->inbuflen = 0;
|
||||
sc->outseqno = 0;
|
||||
|
||||
if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
|
||||
return (ENXIO);
|
||||
if (strcmp(buf, "vty") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0)
|
||||
return (ENXIO);
|
||||
if (strcmp(buf, "serial") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
reg = -1;
|
||||
OF_getprop(node, "reg", ®, sizeof(reg));
|
||||
if (reg == -1)
|
||||
return (ENXIO);
|
||||
sc->node = node;
|
||||
|
||||
if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0)
|
||||
return (ENXIO);
|
||||
if (strcmp(buf, "hvterm1") == 0) {
|
||||
sc->protocol = HVTERM1;
|
||||
return (0);
|
||||
} else if (strcmp(buf, "hvterm-protocol") == 0) {
|
||||
sc->protocol = HVTERMPROT;
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
uart_phyp_probe(device_t dev)
|
||||
{
|
||||
const char *name;
|
||||
struct uart_phyp_softc sc;
|
||||
int err;
|
||||
|
||||
name = ofw_bus_get_name(dev);
|
||||
if (name == NULL || strcmp(name, "vty") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
sc.node = ofw_bus_get_node(dev);
|
||||
err = uart_phyp_probe_node(&sc);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
|
||||
device_set_desc(dev, "POWER Hypervisor Virtual Serial Port");
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static void
|
||||
uart_phyp_cnprobe(struct consdev *cp)
|
||||
{
|
||||
char buf[64];
|
||||
ihandle_t stdout;
|
||||
phandle_t input, opts, chosen;
|
||||
static struct uart_phyp_softc sc;
|
||||
|
||||
if ((opts = OF_finddevice("/options")) == -1)
|
||||
goto fail;
|
||||
if ((chosen = OF_finddevice("/chosen")) == -1)
|
||||
goto fail;
|
||||
|
||||
/* Check if OF has an active stdin/stdout */
|
||||
input = -1;
|
||||
if (OF_getprop(chosen, "stdout", &stdout,
|
||||
sizeof(stdout)) == sizeof(stdout) && stdout != 0)
|
||||
input = OF_instance_to_package(stdout);
|
||||
if (input == -1)
|
||||
goto fail;
|
||||
|
||||
if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1)
|
||||
goto fail;
|
||||
if (strcmp(buf, "serial") != 0)
|
||||
goto fail;
|
||||
|
||||
sc.node = input;
|
||||
if (uart_phyp_probe_node(&sc) != 0)
|
||||
goto fail;
|
||||
mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET |
|
||||
MTX_NOWITNESS);
|
||||
|
||||
cp->cn_pri = CN_NORMAL;
|
||||
console_sc = ≻
|
||||
return;
|
||||
|
||||
fail:
|
||||
cp->cn_pri = CN_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
uart_phyp_attach(device_t dev)
|
||||
{
|
||||
struct uart_phyp_softc *sc;
|
||||
int unit;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
sc->node = ofw_bus_get_node(dev);
|
||||
uart_phyp_probe_node(sc);
|
||||
|
||||
unit = device_get_unit(dev);
|
||||
sc->tp = tty_alloc(&uart_phyp_tty_class, sc);
|
||||
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL,
|
||||
MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);
|
||||
|
||||
if (console_sc != NULL && console_sc->vtermid == sc->vtermid) {
|
||||
sc->outseqno = console_sc->outseqno;
|
||||
console_sc = sc;
|
||||
sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit);
|
||||
tty_init_console(sc->tp, 0);
|
||||
}
|
||||
|
||||
sc->irqrid = 0;
|
||||
#ifdef NOTYET
|
||||
sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid,
|
||||
RF_ACTIVE | RF_SHAREABLE);
|
||||
#else
|
||||
sc->irqres = NULL;
|
||||
#endif
|
||||
if (sc->irqres != NULL) {
|
||||
bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE,
|
||||
NULL, uart_phyp_intr, sc, &sc->sc_icookie);
|
||||
} else {
|
||||
callout_init(&sc->callout, CALLOUT_MPSAFE);
|
||||
sc->polltime = hz / 20;
|
||||
if (sc->polltime < 1)
|
||||
sc->polltime = 1;
|
||||
callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
|
||||
}
|
||||
|
||||
tty_makedev(sc->tp, NULL, "u%r", unit);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
uart_phyp_cninit(struct consdev *cp)
|
||||
{
|
||||
|
||||
strcpy(cp->cn_name, "phypcons");
|
||||
}
|
||||
|
||||
static void
|
||||
uart_phyp_cnterm(struct consdev *cp)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
|
||||
{
|
||||
int err;
|
||||
|
||||
uart_lock(&sc->sc_mtx);
|
||||
if (sc->inbuflen == 0) {
|
||||
err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid,
|
||||
0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0],
|
||||
&sc->phyp_inbuf.u64[1]);
|
||||
if (err != H_SUCCESS) {
|
||||
uart_unlock(&sc->sc_mtx);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->inbuflen == 0) {
|
||||
uart_unlock(&sc->sc_mtx);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (bufsize > sc->inbuflen)
|
||||
bufsize = sc->inbuflen;
|
||||
memcpy(buffer, sc->phyp_inbuf.str, bufsize);
|
||||
sc->inbuflen -= bufsize;
|
||||
if (sc->inbuflen > 0)
|
||||
memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize],
|
||||
sc->inbuflen);
|
||||
|
||||
uart_unlock(&sc->sc_mtx);
|
||||
return (bufsize);
|
||||
}
|
||||
|
||||
static int
|
||||
uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
|
||||
{
|
||||
uint16_t seqno;
|
||||
uint64_t len = 0;
|
||||
union {
|
||||
uint64_t u64;
|
||||
char bytes[8];
|
||||
} cbuf;
|
||||
|
||||
uart_lock(&sc->sc_mtx);
|
||||
switch (sc->protocol) {
|
||||
case HVTERM1:
|
||||
if (bufsize > 8)
|
||||
bufsize = 8;
|
||||
memcpy(&cbuf, buffer, bufsize);
|
||||
len = bufsize;
|
||||
break;
|
||||
case HVTERMPROT:
|
||||
if (bufsize > 4)
|
||||
bufsize = 4;
|
||||
seqno = sc->outseqno++;
|
||||
cbuf.bytes[0] = VS_DATA_PACKET_HEADER;
|
||||
cbuf.bytes[1] = 4 + bufsize; /* total length */
|
||||
cbuf.bytes[2] = (seqno >> 8) & 0xff;
|
||||
cbuf.bytes[3] = seqno & 0xff;
|
||||
memcpy(&cbuf.bytes[4], buffer, bufsize);
|
||||
len = 4 + bufsize;
|
||||
break;
|
||||
}
|
||||
phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, cbuf.u64, 0);
|
||||
uart_unlock(&sc->sc_mtx);
|
||||
|
||||
return (bufsize);
|
||||
}
|
||||
|
||||
static int
|
||||
uart_phyp_cngetc(struct consdev *cp)
|
||||
{
|
||||
unsigned char c;
|
||||
int retval;
|
||||
|
||||
retval = uart_phyp_get(console_sc, &c, 1);
|
||||
if (retval != 1)
|
||||
return (-1);
|
||||
#if defined(KDB)
|
||||
kdb_alt_break(c, &alt_break_state);
|
||||
#endif
|
||||
|
||||
return (c);
|
||||
}
|
||||
|
||||
static void
|
||||
uart_phyp_cnputc(struct consdev *cp, int c)
|
||||
{
|
||||
unsigned char ch = c;
|
||||
uart_phyp_put(console_sc, &ch, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
uart_phyp_cngrab(struct consdev *cp)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
uart_phyp_cnungrab(struct consdev *cp)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
uart_phyp_ttyoutwakeup(struct tty *tp)
|
||||
{
|
||||
struct uart_phyp_softc *sc;
|
||||
char buffer[8];
|
||||
int len;
|
||||
|
||||
sc = tty_softc(tp);
|
||||
|
||||
while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0)
|
||||
uart_phyp_put(sc, buffer, len);
|
||||
}
|
||||
|
||||
static void
|
||||
uart_phyp_intr(void *v)
|
||||
{
|
||||
struct uart_phyp_softc *sc = v;
|
||||
struct tty *tp = sc->tp;
|
||||
unsigned char c;
|
||||
int len;
|
||||
|
||||
tty_lock(tp);
|
||||
while ((len = uart_phyp_get(sc, &c, 1)) > 0)
|
||||
ttydisc_rint(tp, c, 0);
|
||||
ttydisc_rint_done(tp);
|
||||
tty_unlock(tp);
|
||||
|
||||
if (sc->irqres == NULL)
|
||||
callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
|
||||
}
|
||||
|
411
sys/powerpc/pseries/platform_chrp.c
Normal file
411
sys/powerpc/pseries/platform_chrp.c
Normal file
@ -0,0 +1,411 @@
|
||||
/*-
|
||||
* Copyright (c) 2008 Marcel Moolenaar
|
||||
* Copyright (c) 2009 Nathan Whitehorn
|
||||
* 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/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/pcpu.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/smp.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/cpu.h>
|
||||
#include <machine/hid.h>
|
||||
#include <machine/platformvar.h>
|
||||
#include <machine/pmap.h>
|
||||
#include <machine/rtas.h>
|
||||
#include <machine/smp.h>
|
||||
#include <machine/spr.h>
|
||||
#include <machine/trap_aim.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <machine/ofw_machdep.h>
|
||||
|
||||
#include "platform_if.h"
|
||||
|
||||
#ifdef SMP
|
||||
extern void *ap_pcpu;
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc64__
|
||||
static uint8_t splpar_vpa[640] __aligned(64);
|
||||
#endif
|
||||
|
||||
static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
|
||||
|
||||
static int chrp_probe(platform_t);
|
||||
static int chrp_attach(platform_t);
|
||||
void chrp_mem_regions(platform_t, struct mem_region **phys, int *physsz,
|
||||
struct mem_region **avail, int *availsz);
|
||||
static vm_offset_t chrp_real_maxaddr(platform_t);
|
||||
static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
|
||||
static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
|
||||
static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
|
||||
static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
|
||||
static void chrp_smp_ap_init(platform_t);
|
||||
#ifdef SMP
|
||||
static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
|
||||
static struct cpu_group *chrp_smp_topo(platform_t plat);
|
||||
#endif
|
||||
static void chrp_reset(platform_t);
|
||||
#ifdef __powerpc64__
|
||||
#include "phyp-hvcall.h"
|
||||
static void phyp_cpu_idle(sbintime_t sbt);
|
||||
#endif
|
||||
|
||||
static platform_method_t chrp_methods[] = {
|
||||
PLATFORMMETHOD(platform_probe, chrp_probe),
|
||||
PLATFORMMETHOD(platform_attach, chrp_attach),
|
||||
PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
|
||||
PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
|
||||
PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
|
||||
|
||||
PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
|
||||
PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
|
||||
PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
|
||||
PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
|
||||
#ifdef SMP
|
||||
PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
|
||||
PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
|
||||
#endif
|
||||
|
||||
PLATFORMMETHOD(platform_reset, chrp_reset),
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static platform_def_t chrp_platform = {
|
||||
"chrp",
|
||||
chrp_methods,
|
||||
0
|
||||
};
|
||||
|
||||
PLATFORM_DEF(chrp_platform);
|
||||
|
||||
static int
|
||||
chrp_probe(platform_t plat)
|
||||
{
|
||||
if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
|
||||
return (BUS_PROBE_GENERIC);
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
chrp_attach(platform_t plat)
|
||||
{
|
||||
#ifdef __powerpc64__
|
||||
/* XXX: check for /rtas/ibm,hypertas-functions? */
|
||||
if (!(mfmsr() & PSL_HV)) {
|
||||
struct mem_region *phys, *avail;
|
||||
int nphys, navail;
|
||||
mem_regions(&phys, &nphys, &avail, &navail);
|
||||
realmaxaddr = phys[0].mr_size;
|
||||
|
||||
pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
|
||||
cpu_idle_hook = phyp_cpu_idle;
|
||||
|
||||
/* Set up important VPA fields */
|
||||
bzero(splpar_vpa, sizeof(splpar_vpa));
|
||||
splpar_vpa[4] = (uint8_t)((sizeof(splpar_vpa) >> 8) & 0xff);
|
||||
splpar_vpa[5] = (uint8_t)(sizeof(splpar_vpa) & 0xff);
|
||||
splpar_vpa[0xba] = 1; /* Maintain FPRs */
|
||||
splpar_vpa[0xbb] = 1; /* Maintain PMCs */
|
||||
splpar_vpa[0xfc] = 0xff; /* Maintain full SLB */
|
||||
splpar_vpa[0xfd] = 0xff;
|
||||
splpar_vpa[0xff] = 1; /* Maintain Altivec */
|
||||
mb();
|
||||
|
||||
/* Set up hypervisor CPU stuff */
|
||||
chrp_smp_ap_init(plat);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
chrp_mem_regions(platform_t plat, struct mem_region **phys, int *physsz,
|
||||
struct mem_region **avail, int *availsz)
|
||||
{
|
||||
ofw_mem_regions(phys,physsz,avail,availsz);
|
||||
}
|
||||
|
||||
static vm_offset_t
|
||||
chrp_real_maxaddr(platform_t plat)
|
||||
{
|
||||
return (realmaxaddr);
|
||||
}
|
||||
|
||||
static u_long
|
||||
chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
|
||||
{
|
||||
phandle_t phandle;
|
||||
int32_t ticks = -1;
|
||||
|
||||
phandle = cpuref->cr_hwref;
|
||||
|
||||
OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
|
||||
|
||||
if (ticks <= 0)
|
||||
panic("Unable to determine timebase frequency!");
|
||||
|
||||
return (ticks);
|
||||
}
|
||||
|
||||
static int
|
||||
chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
|
||||
{
|
||||
char buf[8];
|
||||
phandle_t cpu, dev, root;
|
||||
int res, cpuid;
|
||||
|
||||
root = OF_peer(0);
|
||||
|
||||
dev = OF_child(root);
|
||||
while (dev != 0) {
|
||||
res = OF_getprop(dev, "name", buf, sizeof(buf));
|
||||
if (res > 0 && strcmp(buf, "cpus") == 0)
|
||||
break;
|
||||
dev = OF_peer(dev);
|
||||
}
|
||||
if (dev == 0) {
|
||||
/*
|
||||
* psim doesn't have a name property on the /cpus node,
|
||||
* but it can be found directly
|
||||
*/
|
||||
dev = OF_finddevice("/cpus");
|
||||
if (dev == 0)
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
cpu = OF_child(dev);
|
||||
|
||||
while (cpu != 0) {
|
||||
res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
|
||||
if (res > 0 && strcmp(buf, "cpu") == 0)
|
||||
break;
|
||||
cpu = OF_peer(cpu);
|
||||
}
|
||||
if (cpu == 0)
|
||||
return (ENOENT);
|
||||
|
||||
cpuref->cr_hwref = cpu;
|
||||
res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
|
||||
sizeof(cpuid));
|
||||
if (res <= 0)
|
||||
res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
|
||||
if (res <= 0)
|
||||
cpuid = 0;
|
||||
cpuref->cr_cpuid = cpuid;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
|
||||
{
|
||||
char buf[8];
|
||||
phandle_t cpu;
|
||||
int i, res, cpuid;
|
||||
|
||||
/* Check for whether it should be the next thread */
|
||||
res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s");
|
||||
if (res > 0) {
|
||||
cell_t interrupt_servers[res/sizeof(cell_t)];
|
||||
OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s",
|
||||
interrupt_servers, res);
|
||||
for (i = 0; i < res/sizeof(cell_t) - 1; i++) {
|
||||
if (interrupt_servers[i] == cpuref->cr_cpuid) {
|
||||
cpuref->cr_cpuid = interrupt_servers[i+1];
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Next CPU core/package */
|
||||
cpu = OF_peer(cpuref->cr_hwref);
|
||||
while (cpu != 0) {
|
||||
res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
|
||||
if (res > 0 && strcmp(buf, "cpu") == 0)
|
||||
break;
|
||||
cpu = OF_peer(cpu);
|
||||
}
|
||||
if (cpu == 0)
|
||||
return (ENOENT);
|
||||
|
||||
cpuref->cr_hwref = cpu;
|
||||
res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
|
||||
sizeof(cpuid));
|
||||
if (res <= 0)
|
||||
res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
|
||||
if (res <= 0)
|
||||
cpuid = 0;
|
||||
cpuref->cr_cpuid = cpuid;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
|
||||
{
|
||||
ihandle_t inst;
|
||||
phandle_t bsp, chosen;
|
||||
int res, cpuid;
|
||||
|
||||
chosen = OF_finddevice("/chosen");
|
||||
if (chosen == 0)
|
||||
return (ENXIO);
|
||||
|
||||
res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
|
||||
if (res < 0)
|
||||
return (ENXIO);
|
||||
|
||||
bsp = OF_instance_to_package(inst);
|
||||
|
||||
/* Pick the primary thread. Can it be any other? */
|
||||
cpuref->cr_hwref = bsp;
|
||||
res = OF_getprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid,
|
||||
sizeof(cpuid));
|
||||
if (res <= 0)
|
||||
res = OF_getprop(bsp, "reg", &cpuid, sizeof(cpuid));
|
||||
if (res <= 0)
|
||||
cpuid = 0;
|
||||
cpuref->cr_cpuid = cpuid;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef SMP
|
||||
static int
|
||||
chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
|
||||
{
|
||||
cell_t start_cpu;
|
||||
int result, err, timeout;
|
||||
|
||||
if (!rtas_exists()) {
|
||||
printf("RTAS uninitialized: unable to start AP %d\n",
|
||||
pc->pc_cpuid);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
start_cpu = rtas_token_lookup("start-cpu");
|
||||
if (start_cpu == -1) {
|
||||
printf("RTAS unknown method: unable to start AP %d\n",
|
||||
pc->pc_cpuid);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
ap_pcpu = pc;
|
||||
powerpc_sync();
|
||||
|
||||
result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc,
|
||||
&err);
|
||||
if (result < 0 || err != 0) {
|
||||
printf("RTAS error (%d/%d): unable to start AP %d\n",
|
||||
result, err, pc->pc_cpuid);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
timeout = 10000;
|
||||
while (!pc->pc_awake && timeout--)
|
||||
DELAY(100);
|
||||
|
||||
return ((pc->pc_awake) ? 0 : EBUSY);
|
||||
}
|
||||
|
||||
static struct cpu_group *
|
||||
chrp_smp_topo(platform_t plat)
|
||||
{
|
||||
struct pcpu *pc, *last_pc;
|
||||
int i, ncores, ncpus;
|
||||
|
||||
ncores = ncpus = 0;
|
||||
last_pc = NULL;
|
||||
for (i = 0; i <= mp_maxid; i++) {
|
||||
pc = pcpu_find(i);
|
||||
if (pc == NULL)
|
||||
continue;
|
||||
if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
|
||||
ncores++;
|
||||
last_pc = pc;
|
||||
ncpus++;
|
||||
}
|
||||
|
||||
if (ncpus % ncores != 0) {
|
||||
printf("WARNING: Irregular SMP topology. Performance may be "
|
||||
"suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
|
||||
return (smp_topo_none());
|
||||
}
|
||||
|
||||
/* Don't do anything fancier for non-threaded SMP */
|
||||
if (ncpus == ncores)
|
||||
return (smp_topo_none());
|
||||
|
||||
return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
chrp_reset(platform_t platform)
|
||||
{
|
||||
OF_reboot();
|
||||
}
|
||||
|
||||
#ifdef __powerpc64__
|
||||
static void
|
||||
phyp_cpu_idle(sbintime_t sbt)
|
||||
{
|
||||
#ifdef NOTYET /* Causes hangs on QEMU */
|
||||
phyp_hcall(H_CEDE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
chrp_smp_ap_init(platform_t platform)
|
||||
{
|
||||
if (!(mfmsr() & PSL_HV)) {
|
||||
/* Set interrupt priority */
|
||||
phyp_hcall(H_CPPR, 0xff);
|
||||
|
||||
/* Register VPA */
|
||||
phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid), splpar_vpa);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void
|
||||
chrp_smp_ap_init(platform_t platform)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
239
sys/powerpc/pseries/plpar_iommu.c
Normal file
239
sys/powerpc/pseries/plpar_iommu.c
Normal file
@ -0,0 +1,239 @@
|
||||
/*-
|
||||
* Copyright (c) 2013, Nathan Whitehorn <nwhitehorn@FreeBSD.org>
|
||||
* 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 unmodified, 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/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/libkern.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/vmem.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
#include <dev/ofw/openfirm.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <powerpc/pseries/phyp-hvcall.h>
|
||||
#include <powerpc/pseries/plpar_iommu.h>
|
||||
|
||||
MALLOC_DEFINE(M_PHYPIOMMU, "iommu", "IOMMU data for PAPR LPARs");
|
||||
|
||||
struct papr_iommu_map {
|
||||
uint32_t iobn;
|
||||
vmem_t *vmem;
|
||||
struct papr_iommu_map *next;
|
||||
};
|
||||
|
||||
static SLIST_HEAD(iommu_maps, iommu_map) iommu_map_head =
|
||||
SLIST_HEAD_INITIALIZER(iommu_map_head);
|
||||
static int papr_supports_stuff_tce = -1;
|
||||
|
||||
struct iommu_map {
|
||||
uint32_t iobn;
|
||||
vmem_t *vmem;
|
||||
|
||||
SLIST_ENTRY(iommu_map) entries;
|
||||
};
|
||||
|
||||
struct dma_window {
|
||||
struct iommu_map *map;
|
||||
bus_addr_t start;
|
||||
bus_addr_t end;
|
||||
};
|
||||
|
||||
int
|
||||
phyp_iommu_set_dma_tag(device_t dev, device_t child, bus_dma_tag_t tag)
|
||||
{
|
||||
device_t p;
|
||||
phandle_t node;
|
||||
cell_t dma_acells, dma_scells, dmawindow[5];
|
||||
struct iommu_map *i;
|
||||
|
||||
for (p = child; p != NULL; p = device_get_parent(p)) {
|
||||
if (ofw_bus_has_prop(p, "ibm,my-dma-window"))
|
||||
break;
|
||||
if (ofw_bus_has_prop(p, "ibm,dma-window"))
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
node = ofw_bus_get_node(p);
|
||||
if (OF_getprop(node, "ibm,#dma-size-cells", &dma_scells,
|
||||
sizeof(cell_t)) <= 0)
|
||||
OF_searchprop(node, "#size-cells", &dma_scells, sizeof(cell_t));
|
||||
if (OF_getprop(node, "ibm,#dma-address-cells", &dma_acells,
|
||||
sizeof(cell_t)) <= 0)
|
||||
OF_searchprop(node, "#address-cells", &dma_acells,
|
||||
sizeof(cell_t));
|
||||
|
||||
if (ofw_bus_has_prop(p, "ibm,my-dma-window"))
|
||||
OF_getprop(node, "ibm,my-dma-window", dmawindow,
|
||||
sizeof(cell_t)*(dma_scells + dma_acells + 1));
|
||||
else
|
||||
OF_getprop(node, "ibm,dma-window", dmawindow,
|
||||
sizeof(cell_t)*(dma_scells + dma_acells + 1));
|
||||
|
||||
struct dma_window *window = malloc(sizeof(struct dma_window),
|
||||
M_PHYPIOMMU, M_WAITOK);
|
||||
if (dma_acells == 1)
|
||||
window->start = dmawindow[1];
|
||||
else
|
||||
window->start = ((uint64_t)(dmawindow[1]) << 32) | dmawindow[2];
|
||||
if (dma_scells == 1)
|
||||
window->end = window->start + dmawindow[dma_acells + 1];
|
||||
else
|
||||
window->end = window->start +
|
||||
(((uint64_t)(dmawindow[dma_acells + 1]) << 32) |
|
||||
dmawindow[dma_acells + 2]);
|
||||
|
||||
window->map = NULL;
|
||||
SLIST_FOREACH(i, &iommu_map_head, entries) {
|
||||
if (i->iobn == dmawindow[0]) {
|
||||
window->map = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window->map == NULL) {
|
||||
window->map = malloc(sizeof(struct iommu_map), M_PHYPIOMMU,
|
||||
M_WAITOK);
|
||||
window->map->iobn = dmawindow[0];
|
||||
/*
|
||||
* Allocate IOMMU range beginning at PAGE_SIZE. Some drivers
|
||||
* (em(4), for example) do not like getting mappings at 0.
|
||||
*/
|
||||
window->map->vmem = vmem_create("IOMMU mappings", PAGE_SIZE,
|
||||
trunc_page(VMEM_ADDR_MAX) - PAGE_SIZE, PAGE_SIZE, 0,
|
||||
M_BESTFIT | M_NOWAIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check experimentally whether we can use H_STUFF_TCE. It is required
|
||||
* by the spec but some firmware (e.g. QEMU) does not actually support
|
||||
* it
|
||||
*/
|
||||
if (papr_supports_stuff_tce == -1)
|
||||
papr_supports_stuff_tce = !(phyp_hcall(H_STUFF_TCE,
|
||||
window->map->iobn, 0, 0, 0) == H_FUNCTION);
|
||||
|
||||
bus_dma_tag_set_iommu(tag, dev, window);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs,
|
||||
bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary,
|
||||
void *cookie)
|
||||
{
|
||||
struct dma_window *window = cookie;
|
||||
bus_addr_t minaddr, maxaddr;
|
||||
bus_addr_t alloced;
|
||||
bus_size_t allocsize;
|
||||
int error, i, j;
|
||||
uint64_t tce;
|
||||
minaddr = window->start;
|
||||
maxaddr = window->end;
|
||||
|
||||
/* XXX: handle exclusion range in a more useful way */
|
||||
if (min < maxaddr)
|
||||
maxaddr = min;
|
||||
|
||||
/* XXX: consolidate segs? */
|
||||
for (i = 0; i < *nsegs; i++) {
|
||||
allocsize = round_page(segs[i].ds_len +
|
||||
(segs[i].ds_addr & PAGE_MASK));
|
||||
error = vmem_xalloc(window->map->vmem, allocsize,
|
||||
(alignment < PAGE_SIZE) ? PAGE_SIZE : alignment, 0,
|
||||
boundary, minaddr, maxaddr, M_BESTFIT | M_NOWAIT, &alloced);
|
||||
if (error != 0) {
|
||||
panic("VMEM failure: %d\n", error);
|
||||
return (error);
|
||||
}
|
||||
KASSERT(alloced % PAGE_SIZE == 0, ("Alloc not page aligned"));
|
||||
KASSERT((alloced + (segs[i].ds_addr & PAGE_MASK)) %
|
||||
alignment == 0,
|
||||
("Allocated segment does not match alignment constraint"));
|
||||
|
||||
tce = trunc_page(segs[i].ds_addr);
|
||||
tce |= 0x3; /* read/write */
|
||||
if (papr_supports_stuff_tce) {
|
||||
error = phyp_hcall(H_STUFF_TCE, window->map->iobn,
|
||||
alloced, tce, allocsize/PAGE_SIZE);
|
||||
} else {
|
||||
for (j = 0; j < allocsize; j += PAGE_SIZE)
|
||||
error = phyp_hcall(H_PUT_TCE, window->map->iobn,
|
||||
alloced + j, tce + j);
|
||||
}
|
||||
|
||||
segs[i].ds_addr = alloced + (segs[i].ds_addr & PAGE_MASK);
|
||||
KASSERT(segs[i].ds_addr > 0, ("Address needs to be positive"));
|
||||
KASSERT(segs[i].ds_addr + segs[i].ds_len < maxaddr,
|
||||
("Address not in range"));
|
||||
if (error < 0) {
|
||||
panic("IOMMU mapping error: %d\n", error);
|
||||
return (ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie)
|
||||
{
|
||||
struct dma_window *window = cookie;
|
||||
bus_addr_t pageround;
|
||||
bus_size_t roundedsize;
|
||||
int i;
|
||||
bus_addr_t j;
|
||||
|
||||
for (i = 0; i < nsegs; i++) {
|
||||
pageround = trunc_page(segs[i].ds_addr);
|
||||
roundedsize = round_page(segs[i].ds_len +
|
||||
(segs[i].ds_addr & PAGE_MASK));
|
||||
|
||||
if (papr_supports_stuff_tce) {
|
||||
phyp_hcall(H_STUFF_TCE, window->map->iobn, pageround, 0,
|
||||
roundedsize/PAGE_SIZE);
|
||||
} else {
|
||||
for (j = 0; j < roundedsize; j += PAGE_SIZE)
|
||||
phyp_hcall(H_PUT_TCE, window->map->iobn,
|
||||
pageround + j, 0);
|
||||
}
|
||||
|
||||
vmem_xfree(window->map->vmem, pageround, roundedsize);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
43
sys/powerpc/pseries/plpar_iommu.h
Normal file
43
sys/powerpc/pseries/plpar_iommu.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*-
|
||||
* Copyright (c) 2013, Nathan Whitehorn <nwhitehorn@FreeBSD.org>
|
||||
* 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 unmodified, 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _PSERIES_PHYP_IOMMU_H_
|
||||
#define _PSERIES_PHYP_IOMMU_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/bus_dma.h>
|
||||
|
||||
int phyp_iommu_set_dma_tag(device_t dev, device_t child, bus_dma_tag_t tag);
|
||||
int phyp_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs,
|
||||
bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary,
|
||||
void *cookie);
|
||||
int phyp_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs,
|
||||
void *cookie);
|
||||
|
||||
#endif
|
||||
|
173
sys/powerpc/pseries/rtas_dev.c
Normal file
173
sys/powerpc/pseries/rtas_dev.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*-
|
||||
* Copyright (c) 2011 Nathan Whitehorn
|
||||
* 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 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/clock.h>
|
||||
#include <sys/cpu.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/openfirm.h>
|
||||
|
||||
#include <machine/rtas.h>
|
||||
|
||||
#include "clock_if.h"
|
||||
|
||||
static int rtasdev_probe(device_t);
|
||||
static int rtasdev_attach(device_t);
|
||||
/* clock interface */
|
||||
static int rtas_gettime(device_t dev, struct timespec *ts);
|
||||
static int rtas_settime(device_t dev, struct timespec *ts);
|
||||
|
||||
static void rtas_shutdown(void *arg, int howto);
|
||||
|
||||
static device_method_t rtasdev_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, rtasdev_probe),
|
||||
DEVMETHOD(device_attach, rtasdev_attach),
|
||||
|
||||
/* clock interface */
|
||||
DEVMETHOD(clock_gettime, rtas_gettime),
|
||||
DEVMETHOD(clock_settime, rtas_settime),
|
||||
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static driver_t rtasdev_driver = {
|
||||
"rtas",
|
||||
rtasdev_methods,
|
||||
0
|
||||
};
|
||||
|
||||
static devclass_t rtasdev_devclass;
|
||||
|
||||
DRIVER_MODULE(rtasdev, nexus, rtasdev_driver, rtasdev_devclass, 0, 0);
|
||||
|
||||
static int
|
||||
rtasdev_probe(device_t dev)
|
||||
{
|
||||
const char *name = ofw_bus_get_name(dev);
|
||||
|
||||
if (strcmp(name, "rtas") != 0)
|
||||
return (ENXIO);
|
||||
if (!rtas_exists())
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "Run-Time Abstraction Services");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
rtasdev_attach(device_t dev)
|
||||
{
|
||||
if (rtas_token_lookup("get-time-of-day") != -1)
|
||||
clock_register(dev, 2000);
|
||||
|
||||
EVENTHANDLER_REGISTER(shutdown_final, rtas_shutdown, NULL,
|
||||
SHUTDOWN_PRI_LAST);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
rtas_gettime(device_t dev, struct timespec *ts) {
|
||||
struct clocktime ct;
|
||||
cell_t tod[8];
|
||||
cell_t token;
|
||||
int error;
|
||||
|
||||
token = rtas_token_lookup("get-time-of-day");
|
||||
if (token == -1)
|
||||
return (ENXIO);
|
||||
error = rtas_call_method(token, 0, 8, &tod[0], &tod[1], &tod[2],
|
||||
&tod[3], &tod[4], &tod[5], &tod[6], &tod[7]);
|
||||
if (error < 0)
|
||||
return (ENXIO);
|
||||
if (tod[0] != 0)
|
||||
return ((tod[0] == -1) ? ENXIO : EAGAIN);
|
||||
|
||||
ct.year = tod[1];
|
||||
ct.mon = tod[2];
|
||||
ct.day = tod[3];
|
||||
ct.hour = tod[4];
|
||||
ct.min = tod[5];
|
||||
ct.sec = tod[6];
|
||||
ct.nsec = tod[7];
|
||||
|
||||
return (clock_ct_to_ts(&ct, ts));
|
||||
}
|
||||
|
||||
static int
|
||||
rtas_settime(device_t dev, struct timespec *ts)
|
||||
{
|
||||
struct clocktime ct;
|
||||
cell_t token, status;
|
||||
int error;
|
||||
|
||||
token = rtas_token_lookup("set-time-of-day");
|
||||
if (token == -1)
|
||||
return (ENXIO);
|
||||
|
||||
clock_ts_to_ct(ts, &ct);
|
||||
error = rtas_call_method(token, 7, 1, ct.year, ct.mon, ct.day, ct.hour,
|
||||
ct.min, ct.sec, ct.nsec, &status);
|
||||
if (error < 0)
|
||||
return (ENXIO);
|
||||
if (status != 0)
|
||||
return (((int)status < 0) ? ENXIO : EAGAIN);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
rtas_shutdown(void *arg, int howto)
|
||||
{
|
||||
cell_t token, status;
|
||||
|
||||
if (howto & RB_HALT) {
|
||||
token = rtas_token_lookup("power-off");
|
||||
if (token == -1)
|
||||
return;
|
||||
|
||||
rtas_call_method(token, 2, 1, 0, 0, &status);
|
||||
} else {
|
||||
token = rtas_token_lookup("system-reboot");
|
||||
if (token == -1)
|
||||
return;
|
||||
|
||||
rtas_call_method(token, 0, 1, &status);
|
||||
}
|
||||
}
|
||||
|
236
sys/powerpc/pseries/rtas_pci.c
Normal file
236
sys/powerpc/pseries/rtas_pci.c
Normal file
@ -0,0 +1,236 @@
|
||||
/*-
|
||||
* Copyright (c) 2011 Nathan Whitehorn
|
||||
* 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 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_pci.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/pio.h>
|
||||
#include <machine/resource.h>
|
||||
#include <machine/rtas.h>
|
||||
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <powerpc/ofw/ofw_pci.h>
|
||||
#include <powerpc/pseries/plpar_iommu.h>
|
||||
|
||||
#include "pcib_if.h"
|
||||
#include "iommu_if.h"
|
||||
|
||||
/*
|
||||
* Device interface.
|
||||
*/
|
||||
static int rtaspci_probe(device_t);
|
||||
static int rtaspci_attach(device_t);
|
||||
|
||||
/*
|
||||
* pcib interface.
|
||||
*/
|
||||
static u_int32_t rtaspci_read_config(device_t, u_int, u_int, u_int,
|
||||
u_int, int);
|
||||
static void rtaspci_write_config(device_t, u_int, u_int, u_int,
|
||||
u_int, u_int32_t, int);
|
||||
|
||||
/*
|
||||
* IOMMU LPAR interface
|
||||
*/
|
||||
static bus_dma_tag_t rtaspci_get_dma_tag(device_t dev, device_t child);
|
||||
|
||||
/*
|
||||
* Driver methods.
|
||||
*/
|
||||
static device_method_t rtaspci_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, rtaspci_probe),
|
||||
DEVMETHOD(device_attach, rtaspci_attach),
|
||||
|
||||
/* pcib interface */
|
||||
DEVMETHOD(pcib_read_config, rtaspci_read_config),
|
||||
DEVMETHOD(pcib_write_config, rtaspci_write_config),
|
||||
|
||||
/* IOMMU functions */
|
||||
DEVMETHOD(bus_get_dma_tag, rtaspci_get_dma_tag),
|
||||
#ifdef __powerpc64__
|
||||
DEVMETHOD(iommu_map, phyp_iommu_map),
|
||||
DEVMETHOD(iommu_unmap, phyp_iommu_unmap),
|
||||
#endif
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
struct rtaspci_softc {
|
||||
struct ofw_pci_softc pci_sc;
|
||||
bus_dma_tag_t dma_tag;
|
||||
|
||||
cell_t read_pci_config, write_pci_config;
|
||||
cell_t ex_read_pci_config, ex_write_pci_config;
|
||||
int sc_extended_config;
|
||||
};
|
||||
|
||||
static devclass_t rtaspci_devclass;
|
||||
DEFINE_CLASS_1(pcib, rtaspci_driver, rtaspci_methods,
|
||||
sizeof(struct rtaspci_softc), ofw_pci_driver);
|
||||
DRIVER_MODULE(rtaspci, nexus, rtaspci_driver, rtaspci_devclass, 0, 0);
|
||||
|
||||
static int
|
||||
rtaspci_probe(device_t dev)
|
||||
{
|
||||
const char *type;
|
||||
|
||||
if (!rtas_exists())
|
||||
return (ENXIO);
|
||||
|
||||
type = ofw_bus_get_type(dev);
|
||||
|
||||
if (OF_getproplen(ofw_bus_get_node(dev), "used-by-rtas") < 0)
|
||||
return (ENXIO);
|
||||
if (type == NULL || strcmp(type, "pci") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "RTAS Host-PCI bridge");
|
||||
return (BUS_PROBE_GENERIC);
|
||||
}
|
||||
|
||||
static int
|
||||
rtaspci_attach(device_t dev)
|
||||
{
|
||||
struct rtaspci_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
sc->read_pci_config = rtas_token_lookup("read-pci-config");
|
||||
sc->write_pci_config = rtas_token_lookup("write-pci-config");
|
||||
sc->ex_read_pci_config = rtas_token_lookup("ibm,read-pci-config");
|
||||
sc->ex_write_pci_config = rtas_token_lookup("ibm,write-pci-config");
|
||||
|
||||
sc->sc_extended_config = 0;
|
||||
OF_getprop(ofw_bus_get_node(dev), "ibm,pci-config-space-type",
|
||||
&sc->sc_extended_config, sizeof(sc->sc_extended_config));
|
||||
|
||||
bus_dma_tag_create(bus_get_dma_tag(dev),
|
||||
1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
|
||||
NULL, NULL, BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED,
|
||||
BUS_SPACE_MAXSIZE, 0, NULL, NULL, &sc->dma_tag);
|
||||
#ifdef __powerpc64__
|
||||
if (!(mfmsr() & PSL_HV))
|
||||
phyp_iommu_set_dma_tag(dev, dev, sc->dma_tag);
|
||||
#endif
|
||||
|
||||
return (ofw_pci_attach(dev));
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
rtaspci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
|
||||
int width)
|
||||
{
|
||||
struct rtaspci_softc *sc;
|
||||
uint32_t retval = 0xffffffff;
|
||||
uint32_t config_addr;
|
||||
int error, pcierror;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) |
|
||||
((func & 0x7) << 8) | (reg & 0xff);
|
||||
if (sc->sc_extended_config)
|
||||
config_addr |= (reg & 0xf00) << 16;
|
||||
|
||||
if (sc->ex_read_pci_config != -1)
|
||||
error = rtas_call_method(sc->ex_read_pci_config, 4, 2,
|
||||
config_addr, sc->pci_sc.sc_pcir.phys_hi,
|
||||
sc->pci_sc.sc_pcir.phys_mid, width, &pcierror, &retval);
|
||||
else
|
||||
error = rtas_call_method(sc->read_pci_config, 2, 2,
|
||||
config_addr, width, &pcierror, &retval);
|
||||
|
||||
/* Sign-extend output */
|
||||
switch (width) {
|
||||
case 1:
|
||||
retval = (int32_t)(int8_t)(retval);
|
||||
break;
|
||||
case 2:
|
||||
retval = (int32_t)(int16_t)(retval);
|
||||
break;
|
||||
}
|
||||
|
||||
if (error < 0 || pcierror != 0)
|
||||
retval = 0xffffffff;
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static void
|
||||
rtaspci_write_config(device_t dev, u_int bus, u_int slot, u_int func,
|
||||
u_int reg, uint32_t val, int width)
|
||||
{
|
||||
struct rtaspci_softc *sc;
|
||||
uint32_t config_addr;
|
||||
int pcierror;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
config_addr = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) |
|
||||
((func & 0x7) << 8) | (reg & 0xff);
|
||||
if (sc->sc_extended_config)
|
||||
config_addr |= (reg & 0xf00) << 16;
|
||||
|
||||
if (sc->ex_write_pci_config != -1)
|
||||
rtas_call_method(sc->ex_write_pci_config, 5, 1, config_addr,
|
||||
sc->pci_sc.sc_pcir.phys_hi, sc->pci_sc.sc_pcir.phys_mid,
|
||||
width, val, &pcierror);
|
||||
else
|
||||
rtas_call_method(sc->write_pci_config, 3, 1, config_addr,
|
||||
width, val, &pcierror);
|
||||
}
|
||||
|
||||
static bus_dma_tag_t
|
||||
rtaspci_get_dma_tag(device_t dev, device_t child)
|
||||
{
|
||||
struct rtaspci_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
return (sc->dma_tag);
|
||||
}
|
||||
|
202
sys/powerpc/pseries/vdevice.c
Normal file
202
sys/powerpc/pseries/vdevice.c
Normal file
@ -0,0 +1,202 @@
|
||||
/*-
|
||||
* Copyright (c) 2011 Nathan Whitehorn
|
||||
* 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 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 AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/cpu.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
static int vdevice_probe(device_t);
|
||||
static int vdevice_attach(device_t);
|
||||
static const struct ofw_bus_devinfo *vdevice_get_devinfo(device_t dev,
|
||||
device_t child);
|
||||
static int vdevice_print_child(device_t dev, device_t child);
|
||||
static struct resource_list *vdevice_get_resource_list (device_t, device_t);
|
||||
|
||||
/*
|
||||
* VDevice devinfo
|
||||
*/
|
||||
struct vdevice_devinfo {
|
||||
struct ofw_bus_devinfo mdi_obdinfo;
|
||||
struct resource_list mdi_resources;
|
||||
};
|
||||
|
||||
static device_method_t vdevice_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, vdevice_probe),
|
||||
DEVMETHOD(device_attach, vdevice_attach),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_add_child, bus_generic_add_child),
|
||||
DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str),
|
||||
DEVMETHOD(bus_print_child, vdevice_print_child),
|
||||
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
|
||||
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
|
||||
DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource),
|
||||
DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
|
||||
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
|
||||
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
|
||||
DEVMETHOD(bus_get_resource_list, vdevice_get_resource_list),
|
||||
|
||||
/* ofw_bus interface */
|
||||
DEVMETHOD(ofw_bus_get_devinfo, vdevice_get_devinfo),
|
||||
DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
|
||||
DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
|
||||
DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
|
||||
DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
|
||||
DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t vdevice_driver = {
|
||||
"vdevice",
|
||||
vdevice_methods,
|
||||
0
|
||||
};
|
||||
|
||||
static devclass_t vdevice_devclass;
|
||||
|
||||
DRIVER_MODULE(vdevice, nexus, vdevice_driver, vdevice_devclass, 0, 0);
|
||||
|
||||
static int
|
||||
vdevice_probe(device_t dev)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
name = ofw_bus_get_name(dev);
|
||||
|
||||
if (name == NULL || strcmp(name, "vdevice") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
if (!ofw_bus_is_compatible(dev, "IBM,vdevice"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "POWER Hypervisor Virtual Device Root");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
vdevice_attach(device_t dev)
|
||||
{
|
||||
phandle_t root, child;
|
||||
device_t cdev;
|
||||
int icells, i, nintr, *intr;
|
||||
phandle_t iparent;
|
||||
struct vdevice_devinfo *dinfo;
|
||||
|
||||
root = ofw_bus_get_node(dev);
|
||||
|
||||
for (child = OF_child(root); child != 0; child = OF_peer(child)) {
|
||||
dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
|
||||
if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo,
|
||||
child) != 0) {
|
||||
free(dinfo, M_DEVBUF);
|
||||
continue;
|
||||
}
|
||||
resource_list_init(&dinfo->mdi_resources);
|
||||
|
||||
if (OF_searchprop(child, "#interrupt-cells", &icells,
|
||||
sizeof(icells)) <= 0)
|
||||
icells = 2;
|
||||
if (OF_getprop(child, "interrupt-parent", &iparent,
|
||||
sizeof(iparent)) <= 0)
|
||||
iparent = -1;
|
||||
nintr = OF_getprop_alloc(child, "interrupts", sizeof(*intr),
|
||||
(void **)&intr);
|
||||
if (nintr > 0) {
|
||||
for (i = 0; i < nintr; i += icells) {
|
||||
u_int irq = intr[i];
|
||||
if (iparent != -1)
|
||||
irq = MAP_IRQ(iparent, intr[i]);
|
||||
|
||||
resource_list_add(&dinfo->mdi_resources,
|
||||
SYS_RES_IRQ, i, irq, irq, i);
|
||||
}
|
||||
}
|
||||
|
||||
cdev = device_add_child(dev, NULL, -1);
|
||||
if (cdev == NULL) {
|
||||
device_printf(dev, "<%s>: device_add_child failed\n",
|
||||
dinfo->mdi_obdinfo.obd_name);
|
||||
ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo);
|
||||
free(dinfo, M_DEVBUF);
|
||||
continue;
|
||||
}
|
||||
device_set_ivars(cdev, dinfo);
|
||||
}
|
||||
|
||||
return (bus_generic_attach(dev));
|
||||
}
|
||||
|
||||
static const struct ofw_bus_devinfo *
|
||||
vdevice_get_devinfo(device_t dev, device_t child)
|
||||
{
|
||||
return (device_get_ivars(child));
|
||||
}
|
||||
|
||||
static int
|
||||
vdevice_print_child(device_t dev, device_t child)
|
||||
{
|
||||
struct vdevice_devinfo *dinfo;
|
||||
struct resource_list *rl;
|
||||
int retval = 0;
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
rl = &dinfo->mdi_resources;
|
||||
|
||||
retval += bus_print_child_header(dev, child);
|
||||
|
||||
retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
|
||||
|
||||
retval += bus_print_child_footer(dev, child);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static struct resource_list *
|
||||
vdevice_get_resource_list (device_t dev, device_t child)
|
||||
{
|
||||
struct vdevice_devinfo *dinfo;
|
||||
|
||||
dinfo = device_get_ivars(child);
|
||||
return (&dinfo->mdi_resources);
|
||||
}
|
||||
|
323
sys/powerpc/pseries/xics.c
Normal file
323
sys/powerpc/pseries/xics.c
Normal file
@ -0,0 +1,323 @@
|
||||
/*-
|
||||
* Copyright 2011 Nathan Whitehorn
|
||||
*
|
||||
* 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/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/smp.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/rtas.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include "phyp-hvcall.h"
|
||||
#include "pic_if.h"
|
||||
|
||||
#define XICP_PRIORITY 5 /* Random non-zero number */
|
||||
#define XICP_IPI 2
|
||||
#define MAX_XICP_IRQS (1<<24) /* 24-bit XIRR field */
|
||||
|
||||
static int xicp_probe(device_t);
|
||||
static int xicp_attach(device_t);
|
||||
static int xics_probe(device_t);
|
||||
static int xics_attach(device_t);
|
||||
|
||||
static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask);
|
||||
static void xicp_dispatch(device_t, struct trapframe *);
|
||||
static void xicp_enable(device_t, u_int, u_int);
|
||||
static void xicp_eoi(device_t, u_int);
|
||||
static void xicp_ipi(device_t, u_int);
|
||||
static void xicp_mask(device_t, u_int);
|
||||
static void xicp_unmask(device_t, u_int);
|
||||
|
||||
static device_method_t xicp_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, xicp_probe),
|
||||
DEVMETHOD(device_attach, xicp_attach),
|
||||
|
||||
/* PIC interface */
|
||||
DEVMETHOD(pic_bind, xicp_bind),
|
||||
DEVMETHOD(pic_dispatch, xicp_dispatch),
|
||||
DEVMETHOD(pic_enable, xicp_enable),
|
||||
DEVMETHOD(pic_eoi, xicp_eoi),
|
||||
DEVMETHOD(pic_ipi, xicp_ipi),
|
||||
DEVMETHOD(pic_mask, xicp_mask),
|
||||
DEVMETHOD(pic_unmask, xicp_unmask),
|
||||
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static device_method_t xics_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, xics_probe),
|
||||
DEVMETHOD(device_attach, xics_attach),
|
||||
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
struct xicp_softc {
|
||||
struct mtx sc_mtx;
|
||||
|
||||
int ibm_int_on;
|
||||
int ibm_int_off;
|
||||
int ibm_get_xive;
|
||||
int ibm_set_xive;
|
||||
|
||||
/* XXX: inefficient -- hash table? tree? */
|
||||
struct {
|
||||
int irq;
|
||||
int vector;
|
||||
} intvecs[256];
|
||||
int nintvecs;
|
||||
};
|
||||
|
||||
static driver_t xicp_driver = {
|
||||
"xicp",
|
||||
xicp_methods,
|
||||
sizeof(struct xicp_softc)
|
||||
};
|
||||
|
||||
static driver_t xics_driver = {
|
||||
"xics",
|
||||
xics_methods,
|
||||
0
|
||||
};
|
||||
|
||||
static devclass_t xicp_devclass;
|
||||
static devclass_t xics_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(xicp, nexus, xicp_driver, xicp_devclass, 0, 0,
|
||||
BUS_PASS_INTERRUPT-1);
|
||||
EARLY_DRIVER_MODULE(xics, nexus, xics_driver, xics_devclass, 0, 0,
|
||||
BUS_PASS_INTERRUPT);
|
||||
|
||||
static int
|
||||
xicp_probe(device_t dev)
|
||||
{
|
||||
if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev),
|
||||
"interrupt-controller") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
if (!ofw_bus_is_compatible(dev, "ibm,ppc-xicp"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "PAPR virtual interrupt controller");
|
||||
return (BUS_PROBE_GENERIC);
|
||||
}
|
||||
|
||||
static int
|
||||
xics_probe(device_t dev)
|
||||
{
|
||||
if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev),
|
||||
"interrupt-controller") != 0)
|
||||
return (ENXIO);
|
||||
|
||||
if (!ofw_bus_is_compatible(dev, "ibm,ppc-xics"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "PAPR virtual interrupt source");
|
||||
return (BUS_PROBE_GENERIC);
|
||||
}
|
||||
|
||||
static int
|
||||
xicp_attach(device_t dev)
|
||||
{
|
||||
struct xicp_softc *sc = device_get_softc(dev);
|
||||
phandle_t phandle = ofw_bus_get_node(dev);
|
||||
|
||||
mtx_init(&sc->sc_mtx, "XICP", NULL, MTX_DEF);
|
||||
sc->nintvecs = 0;
|
||||
|
||||
sc->ibm_int_on = rtas_token_lookup("ibm,int-on");
|
||||
sc->ibm_int_off = rtas_token_lookup("ibm,int-off");
|
||||
sc->ibm_set_xive = rtas_token_lookup("ibm,set-xive");
|
||||
sc->ibm_get_xive = rtas_token_lookup("ibm,get-xive");
|
||||
|
||||
if (OF_getproplen(phandle, "ibm,phandle") > 0)
|
||||
OF_getprop(phandle, "ibm,phandle", &phandle, sizeof(phandle));
|
||||
|
||||
powerpc_register_pic(dev, phandle, MAX_XICP_IRQS,
|
||||
1 /* Number of IPIs */, FALSE);
|
||||
root_pic = dev;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
xics_attach(device_t dev)
|
||||
{
|
||||
phandle_t phandle = ofw_bus_get_node(dev);
|
||||
|
||||
if (OF_getproplen(phandle, "ibm,phandle") > 0)
|
||||
OF_getprop(phandle, "ibm,phandle", &phandle, sizeof(phandle));
|
||||
|
||||
/* The XICP (root PIC) will handle all our interrupts */
|
||||
powerpc_register_pic(root_pic, phandle, MAX_XICP_IRQS,
|
||||
1 /* Number of IPIs */, FALSE);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* PIC I/F methods.
|
||||
*/
|
||||
|
||||
static void
|
||||
xicp_bind(device_t dev, u_int irq, cpuset_t cpumask)
|
||||
{
|
||||
struct xicp_softc *sc = device_get_softc(dev);
|
||||
cell_t status, cpu;
|
||||
|
||||
/*
|
||||
* This doesn't appear to actually support affinity groups, so just
|
||||
* use the first CPU.
|
||||
*/
|
||||
CPU_FOREACH(cpu)
|
||||
if (CPU_ISSET(cpu, &cpumask)) break;
|
||||
|
||||
rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY,
|
||||
&status);
|
||||
}
|
||||
|
||||
static void
|
||||
xicp_dispatch(device_t dev, struct trapframe *tf)
|
||||
{
|
||||
struct xicp_softc *sc;
|
||||
uint64_t xirr, junk;
|
||||
int i;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
for (;;) {
|
||||
/* Return value in R4, use the PFT call */
|
||||
phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk);
|
||||
xirr &= 0x00ffffff;
|
||||
|
||||
if (xirr == 0) { /* No more pending interrupts? */
|
||||
phyp_hcall(H_CPPR, (uint64_t)0xff);
|
||||
break;
|
||||
}
|
||||
if (xirr == XICP_IPI) { /* Magic number for IPIs */
|
||||
xirr = MAX_XICP_IRQS; /* Map to FreeBSD magic */
|
||||
phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(cpuid)),
|
||||
0xff); /* Clear IPI */
|
||||
}
|
||||
|
||||
/* XXX: super inefficient */
|
||||
for (i = 0; i < sc->nintvecs; i++) {
|
||||
if (sc->intvecs[i].irq == xirr)
|
||||
break;
|
||||
}
|
||||
|
||||
KASSERT(i < sc->nintvecs, ("Unmapped XIRR"));
|
||||
powerpc_dispatch_intr(sc->intvecs[i].vector, tf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xicp_enable(device_t dev, u_int irq, u_int vector)
|
||||
{
|
||||
struct xicp_softc *sc;
|
||||
cell_t status, cpu;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
KASSERT(sc->nintvecs + 1 < sizeof(sc->intvecs)/sizeof(sc->intvecs[0]),
|
||||
("Too many XICP interrupts"));
|
||||
|
||||
mtx_lock(&sc->sc_mtx);
|
||||
sc->intvecs[sc->nintvecs].irq = irq;
|
||||
sc->intvecs[sc->nintvecs].vector = vector;
|
||||
mb();
|
||||
sc->nintvecs++;
|
||||
mtx_unlock(&sc->sc_mtx);
|
||||
|
||||
/* IPIs are also enabled */
|
||||
if (irq == MAX_XICP_IRQS)
|
||||
return;
|
||||
|
||||
/* Bind to this CPU to start: distrib. ID is last entry in gserver# */
|
||||
cpu = PCPU_GET(cpuid);
|
||||
rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY,
|
||||
&status);
|
||||
xicp_unmask(dev, irq);
|
||||
}
|
||||
|
||||
static void
|
||||
xicp_eoi(device_t dev, u_int irq)
|
||||
{
|
||||
uint64_t xirr;
|
||||
|
||||
if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */
|
||||
irq = XICP_IPI;
|
||||
xirr = irq | (XICP_PRIORITY << 24);
|
||||
|
||||
phyp_hcall(H_EOI, xirr);
|
||||
}
|
||||
|
||||
static void
|
||||
xicp_ipi(device_t dev, u_int cpu)
|
||||
{
|
||||
|
||||
phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY);
|
||||
}
|
||||
|
||||
static void
|
||||
xicp_mask(device_t dev, u_int irq)
|
||||
{
|
||||
struct xicp_softc *sc = device_get_softc(dev);
|
||||
cell_t status;
|
||||
|
||||
if (irq == MAX_XICP_IRQS)
|
||||
return;
|
||||
|
||||
rtas_call_method(sc->ibm_int_off, 1, 1, irq, &status);
|
||||
}
|
||||
|
||||
static void
|
||||
xicp_unmask(device_t dev, u_int irq)
|
||||
{
|
||||
struct xicp_softc *sc = device_get_softc(dev);
|
||||
cell_t status;
|
||||
|
||||
if (irq == MAX_XICP_IRQS)
|
||||
return;
|
||||
|
||||
rtas_call_method(sc->ibm_int_on, 1, 1, irq, &status);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user