MFpseries:

Renovate and improve the AIM Open Firmware support:
- Add RTAS (Run-Time Abstraction Services) support, found on all IBM systems
  and some Apple ones
- Improve support for 32-bit real mode Open Firmware systems
- Pull some more OF bits over from the AIM directory
- Fix memory detection on IBM LPARs and systems with more than one /memory
  node (by andreast@)
This commit is contained in:
Nathan Whitehorn 2011-06-02 14:12:37 +00:00
parent f0cdc18176
commit 1787909001
9 changed files with 898 additions and 187 deletions

View File

@ -87,9 +87,6 @@ GLOBAL(tmpstk)
GLOBAL(esym)
.long 0 /* end of symbol table */
GLOBAL(ofmsr)
.long 0, 0, 0, 0, 0 /* msr/sprg0-3 used in Open Firmware */
#define INTRCNT_COUNT 256 /* max(HROWPIC_IRQMAX,OPENPIC_IRQMAX) */
GLOBAL(intrnames)
.space INTRCNT_COUNT * (MAXCOMLEN + 1) * 2
@ -99,16 +96,6 @@ GLOBAL(intrcnt)
.space INTRCNT_COUNT * 4 * 2
GLOBAL(eintrcnt)
/*
* File-scope for locore.S
*/
idle_u:
.long 0 /* fake uarea during idle after exit */
openfirmware_entry:
.long 0 /* Open Firmware entry point */
srsave:
.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.text
.globl btext
btext:

View File

@ -76,7 +76,6 @@
.set kernbase, KERNBASE
#define TMPSTKSZ 8192 /* 8K temporary stack */
#define OFWSTKSZ 4096 /* 4K Open Firmware stack */
/*
* Globals
@ -85,14 +84,9 @@
.align 4
GLOBAL(tmpstk)
.space TMPSTKSZ
GLOBAL(ofwstk)
.space OFWSTKSZ
GLOBAL(esym)
.llong 0 /* end of symbol table */
GLOBAL(ofmsr)
.llong 0, 0, 0, 0, 0 /* msr/sprg0-3 used in Open Firmware */
#define INTRCNT_COUNT 256 /* max(HROWPIC_IRQMAX,OPENPIC_IRQMAX) */
GLOBAL(intrnames)
.space INTRCNT_COUNT * (MAXCOMLEN + 1) * 2
@ -102,16 +96,6 @@ GLOBAL(intrcnt)
.space INTRCNT_COUNT * 4 * 2
GLOBAL(eintrcnt)
/*
* File-scope for locore.S
*/
idle_u:
.llong 0 /* fake uarea during idle after exit */
openfirmware_entry:
.llong 0 /* Open Firmware entry point */
srsave:
.llong 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.text
.globl btext
btext:
@ -207,122 +191,6 @@ ASENTRY(__start)
tocbase:
.llong .TOC.@tocbase
/*
* Open Firmware Real-mode Entry Point. This is a huge pain.
*/
ASENTRY(ofw_32bit_mode_entry)
mflr %r0
std %r0,16(%r1)
stdu %r1,-208(%r1)
/*
* We need to save the following, because OF's register save/
* restore code assumes that the contents of registers are
* at most 32 bits wide: lr, cr, r2, r13-r31, the old MSR. These
* get placed in that order in the stack.
*/
mfcr %r4
std %r4,48(%r1)
std %r13,56(%r1)
std %r14,64(%r1)
std %r15,72(%r1)
std %r16,80(%r1)
std %r17,88(%r1)
std %r18,96(%r1)
std %r19,104(%r1)
std %r20,112(%r1)
std %r21,120(%r1)
std %r22,128(%r1)
std %r23,136(%r1)
std %r24,144(%r1)
std %r25,152(%r1)
std %r26,160(%r1)
std %r27,168(%r1)
std %r28,176(%r1)
std %r29,184(%r1)
std %r30,192(%r1)
std %r31,200(%r1)
/* Record the old MSR */
mfmsr %r6
/* read client interface handler */
lis %r4,openfirmware_entry@ha
ld %r4,openfirmware_entry@l(%r4)
/*
* Set the MSR to the OF value. This has the side effect of disabling
* exceptions, which is important for the next few steps.
*/
lis %r5,ofmsr@ha
ld %r5,ofmsr@l(%r5)
mtmsrd %r5
isync
/*
* Set up OF stack. This needs to be accessible in real mode and
* use the 32-bit ABI stack frame format. The pointer to the current
* kernel stack is placed at the very top of the stack along with
* the old MSR so we can get them back later.
*/
mr %r5,%r1
lis %r1,(ofwstk+OFWSTKSZ-32)@ha
addi %r1,%r1,(ofwstk+OFWSTKSZ-32)@l
std %r5,8(%r1) /* Save real stack pointer */
std %r2,16(%r1) /* Save old TOC */
std %r6,24(%r1) /* Save old MSR */
li %r5,0
stw %r5,4(%r1)
stw %r5,0(%r1)
/* Finally, branch to OF */
mtctr %r4
bctrl
/* Reload stack pointer and MSR from the OFW stack */
ld %r6,24(%r1)
ld %r2,16(%r1)
ld %r1,8(%r1)
/* Now set the real MSR */
mtmsrd %r6
isync
/* Sign-extend the return value from OF */
extsw %r3,%r3
/* Restore all the non-volatile registers */
ld %r5,48(%r1)
mtcr %r5
ld %r13,56(%r1)
ld %r14,64(%r1)
ld %r15,72(%r1)
ld %r16,80(%r1)
ld %r17,88(%r1)
ld %r18,96(%r1)
ld %r19,104(%r1)
ld %r20,112(%r1)
ld %r21,120(%r1)
ld %r22,128(%r1)
ld %r23,136(%r1)
ld %r24,144(%r1)
ld %r25,152(%r1)
ld %r26,160(%r1)
ld %r27,168(%r1)
ld %r28,176(%r1)
ld %r29,184(%r1)
ld %r30,192(%r1)
ld %r31,200(%r1)
/* Restore the stack and link register */
ld %r1,0(%r1)
ld %r0,16(%r1)
mtlr %r0
blr
/*
* int setfault()
*

View File

@ -0,0 +1,61 @@
/*-
* 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.
*
* $FreeBSD$
*/
#ifndef _MACHINE_RTAS_H_
#define _MACHINE_RTAS_H_
#include <sys/cdefs.h>
#include <sys/types.h>
#include <dev/ofw/openfirm.h>
/*
* RTAS functions are defined by 32-bit integer tokens. These vary from
* system to system, and can be looked up from their standardized names
* using rtas_token_lookup(). If RTAS is not available, rtas_token_lookup()
* and rtas_call_method() return -1; this can be checked in advance using
* rtas_exists(). Otherwise, rtas_call_method() returns one of the RTAS
* status codes from the bottom of this file.
*/
int rtas_exists(void);
int rtas_call_method(cell_t token, int nargs, int nreturns, ...);
cell_t rtas_token_lookup(const char *method);
/* RTAS Status Codes: see CHRP or PAPR specification */
#define RTAS_OK 0
#define RTAS_HW_ERROR -1
#define RTAS_BUSY -2
#define RTAS_PARAM_ERROR -3
#define RTAS_STATE_CHANGE -7
#define RTAS_VENDOR_BEGIN 9000
#define RTAS_EXTENDED_DELAY 9900
#define RTAS_ISOLATION_ERROR -9000
#define RTAS_VENDOR_ERROR_BEGIN -9004
#endif /* _MACHINE_RTAS_H_ */

View File

@ -60,17 +60,15 @@ __FBSDID("$FreeBSD$");
#include <machine/platform.h>
#include <machine/ofw_machdep.h>
#define OFMEM_REGIONS 32
static struct mem_region OFmem[OFMEM_REGIONS + 1], OFavail[OFMEM_REGIONS + 3];
static struct mem_region OFfree[OFMEM_REGIONS + 3];
static int nOFmem;
static struct mem_region OFmem[PHYS_AVAIL_SZ], OFavail[PHYS_AVAIL_SZ];
static struct mem_region OFfree[PHYS_AVAIL_SZ];
extern register_t ofmsr[5];
static int (*ofwcall)(void *);
extern void *openfirmware_entry;
static void *fdt;
int ofw_real_mode;
int ofw_32bit_mode_entry(void *);
int ofwcall(void *);
static void ofw_quiesce(void);
static int openfirmware(void *args);
@ -134,11 +132,32 @@ memr_merge(struct mem_region *from, struct mem_region *to)
to->mr_size = end - to->mr_start;
}
/*
* Quick sort callout for comparing memory regions.
*/
static int mr_cmp(const void *a, const void *b);
static int
mr_cmp(const void *a, const void *b)
{
const struct mem_region *regiona;
const struct mem_region *regionb;
regiona = a;
regionb = b;
if (regiona->mr_start < regionb->mr_start)
return (-1);
else if (regiona->mr_start > regionb->mr_start)
return (1);
else
return (0);
}
static int
parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
{
cell_t address_cells, size_cells;
cell_t OFmem[4*(OFMEM_REGIONS + 1)];
cell_t OFmem[4 * PHYS_AVAIL_SZ];
int sz, i, j;
int apple_hack_mode;
phandle_t phandle;
@ -175,7 +194,7 @@ parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
* Get memory.
*/
if ((node == -1) || (sz = OF_getprop(node, prop,
OFmem, sizeof(OFmem[0]) * 4 * OFMEM_REGIONS)) <= 0)
OFmem, sizeof(OFmem[0]) * 4 * PHYS_AVAIL_SZ)) <= 0)
panic("Physical memory map not found");
i = 0;
@ -225,7 +244,7 @@ parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
#ifdef __powerpc64__
if (apple_hack_mode) {
/* Add in regions above 4 GB to the available list */
struct mem_region himem[OFMEM_REGIONS];
struct mem_region himem[PHYS_AVAIL_SZ];
int hisz;
hisz = parse_ofw_memory(node, "reg", himem);
@ -243,6 +262,81 @@ parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
return (sz);
}
static int
parse_drconf_memory(int *msz, int *asz, struct mem_region *ofmem,
struct mem_region *ofavail)
{
phandle_t phandle;
vm_offset_t base;
int i, idx, len, lasz, lmsz, res;
uint32_t lmb_size[2];
unsigned long *dmem, flags;
lmsz = *msz;
lasz = *asz;
phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
if (phandle == -1)
/* No drconf node, return. */
return (0);
res = OF_getprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size));
if (res == -1)
return (0);
/* Parse the /ibm,dynamic-memory.
The first position gives the # of entries. The next two words
reflect the address of the memory block. The next four words are
the DRC index, reserved, list index and flags.
(see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
#el Addr DRC-idx res list-idx flags
-------------------------------------------------
| 4 | 8 | 4 | 4 | 4 | 4 |....
-------------------------------------------------
*/
len = OF_getproplen(phandle, "ibm,dynamic-memory");
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(phandle, "ibm,dynamic-memory", &arr,
sizeof(arr));
if (res == -1)
return (0);
/* Number of elements */
idx = arr[0];
/* First address. */
dmem = (void*)&arr[1];
for (i = 0; i < idx; i++) {
base = *dmem;
dmem += 2;
flags = *dmem;
/* Use region only if available and not reserved. */
if ((flags & 0x8) && !(flags & 0x80)) {
ofmem[lmsz].mr_start = base;
ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
ofavail[lasz].mr_start = base;
ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
lmsz++;
lasz++;
}
dmem++;
}
}
*msz = lmsz;
*asz = lasz;
return (1);
}
/*
* This is called during powerpc_init, before the system is really initialized.
* It shall provide the total and the available regions of RAM.
@ -255,31 +349,62 @@ ofw_mem_regions(struct mem_region **memp, int *memsz,
struct mem_region **availp, int *availsz)
{
phandle_t phandle;
vm_offset_t maxphysaddr;
int asz, msz, fsz;
int i, j;
int i, j, res;
int still_merging;
char name[31];
asz = msz = 0;
/*
* Get memory.
* Get memory from all the /memory nodes.
*/
phandle = OF_finddevice("/memory");
if (phandle == -1)
phandle = OF_finddevice("/memory@0");
for (phandle = OF_child(OF_peer(0)); phandle != 0;
phandle = OF_peer(phandle)) {
if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0)
continue;
if (strncmp(name, "memory", sizeof(name)) != 0)
continue;
msz = parse_ofw_memory(phandle, "reg", OFmem);
nOFmem = msz / sizeof(struct mem_region);
asz = parse_ofw_memory(phandle, "available", OFavail);
res = parse_ofw_memory(phandle, "reg", &OFmem[msz]);
msz += res/sizeof(struct mem_region);
if (OF_getproplen(phandle, "available") >= 0)
res = parse_ofw_memory(phandle, "available",
&OFavail[asz]);
else
res = parse_ofw_memory(phandle, "reg", &OFavail[asz]);
asz += res/sizeof(struct mem_region);
}
/* Check for memory in ibm,dynamic-reconfiguration-memory */
parse_drconf_memory(&msz, &asz, OFmem, OFavail);
qsort(OFmem, msz, sizeof(*OFmem), mr_cmp);
qsort(OFavail, asz, sizeof(*OFavail), mr_cmp);
*memp = OFmem;
*memsz = nOFmem;
*memsz = msz;
/*
* On some firmwares (SLOF), some memory may be marked available that
* doesn't actually exist. This manifests as an extension of the last
* available segment past the end of physical memory, so truncate that
* one.
*/
maxphysaddr = 0;
for (i = 0; i < msz; i++)
if (OFmem[i].mr_start + OFmem[i].mr_size > maxphysaddr)
maxphysaddr = OFmem[i].mr_start + OFmem[i].mr_size;
if (OFavail[asz - 1].mr_start + OFavail[asz - 1].mr_size > maxphysaddr)
OFavail[asz - 1].mr_size = maxphysaddr -
OFavail[asz - 1].mr_start;
/*
* OFavail may have overlapping regions - collapse these
* and copy out remaining regions to OFfree
*/
asz /= sizeof(struct mem_region);
do {
still_merging = FALSE;
for (i = 0; i < asz; i++) {
@ -318,19 +443,6 @@ OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *))
else
ofw_real_mode = 1;
ofwcall = NULL;
#ifdef __powerpc64__
/*
* For PPC64, we need to use some hand-written
* asm trampolines to get to OF.
*/
if (openfirm != NULL)
ofwcall = ofw_32bit_mode_entry;
#else
ofwcall = openfirm;
#endif
fdt = fdt_ptr;
#ifdef FDT_DTB_STATIC
@ -345,7 +457,7 @@ OF_bootstrap()
{
boolean_t status = FALSE;
if (ofwcall != NULL) {
if (openfirmware_entry != NULL) {
if (ofw_real_mode) {
status = OF_install(OFW_STD_REAL, 0);
} else {
@ -481,12 +593,7 @@ openfirmware(void *args)
int result;
#ifdef SMP
struct ofw_rv_args rv_args;
#endif
if (pmap_bootstrapped && ofw_real_mode)
args = (void *)pmap_kextract((vm_offset_t)args);
#ifdef SMP
rv_args.args = args;
rv_args.in_progress = 1;
smp_rendezvous(smp_no_rendevous_barrier, ofw_rendezvous_dispatch,

View File

@ -205,13 +205,14 @@ ofw_real_bounce_alloc(void *junk)
/*
* Allocate a page of contiguous, wired physical memory that can
* fit into a 32-bit address space.
* fit into a 32-bit address space and accessed from real mode.
*/
mtx_lock(&of_bounce_mtx);
of_bounce_virt = contigmalloc(PAGE_SIZE, M_OFWREAL, 0,
0, BUS_SPACE_MAXADDR_32BIT, PAGE_SIZE, PAGE_SIZE);
of_bounce_virt = contigmalloc(PAGE_SIZE, M_OFWREAL, 0, 0,
ulmin(platform_real_maxaddr(), BUS_SPACE_MAXADDR_32BIT), PAGE_SIZE,
PAGE_SIZE);
of_bounce_phys = vtophys(of_bounce_virt);
of_bounce_size = PAGE_SIZE;

154
sys/powerpc/ofw/ofwcall32.S Normal file
View File

@ -0,0 +1,154 @@
/*-
* Copyright (C) 2009-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 ``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 <sys/syscall.h>
#include <machine/trap.h>
#include <machine/param.h>
#include <machine/spr.h>
#include <machine/asm.h>
#define OFWSTKSZ 4096 /* 4K Open Firmware stack */
/*
* Globals
*/
.data
GLOBAL(ofmsr)
.long 0, 0, 0, 0, 0 /* msr/sprg0-3 used in Open Firmware */
GLOBAL(rtasmsr)
.long 0
GLOBAL(openfirmware_entry)
.long 0 /* Open Firmware entry point */
GLOBAL(rtas_entry)
.long 0 /* RTAS entry point */
.align 4
ofwstk:
.space OFWSTKSZ
rtas_regsave:
.space 4
/*
* Open Firmware Entry Point. May need to enter real mode.
*
* C prototype: int ofwcall(void *callbuffer);
*/
ASENTRY(ofwcall)
mflr %r0
stw %r0,4(%r1)
/* Record the old MSR */
mfmsr %r6
/* read client interface handler */
lis %r4,openfirmware_entry@ha
lwz %r4,openfirmware_entry@l(%r4)
/*
* Set the MSR to the OF value. This has the side effect of disabling
* exceptions, which prevents preemption later.
*/
lis %r5,ofmsr@ha
lwz %r5,ofmsr@l(%r5)
mtmsr %r5
isync
/*
* Set up OF stack. This needs to be potentially accessible in real mode
* The pointer to the current kernel stack is placed at the very
* top of the stack along with the old MSR so we can get them back
* later.
*/
mr %r5,%r1
lis %r1,(ofwstk+OFWSTKSZ-16)@ha
addi %r1,%r1,(ofwstk+OFWSTKSZ-16)@l
stw %r5,8(%r1) /* Save real stack pointer */
stw %r6,12(%r1) /* Save old MSR */
li %r5,0
stw %r5,4(%r1)
stw %r5,0(%r1)
/* Finally, branch to OF */
mtctr %r4
bctrl
/* Reload stack pointer and MSR from the OFW stack */
lwz %r6,12(%r1)
lwz %r1,8(%r1)
/* Now set the real MSR */
mtmsr %r6
isync
/* Return */
lwz %r0,4(%r1)
mtlr %r0
blr
/*
* RTAS Entry Point. Similar to the OF one, but simpler (no separate stack)
*
* C prototype: int rtascall(void *callbuffer, void *rtas_privdat);
*/
ASENTRY(rtascall)
mflr %r0
stw %r0,4(%r1)
/* Record the old MSR to real-mode-accessible area */
mfmsr %r0
lis %r5,rtas_regsave@ha
stw %r0,rtas_regsave@l(%r5)
/* read client interface handler */
lis %r5,rtas_entry@ha
lwz %r5,rtas_entry@l(%r5)
/* Set the MSR to the RTAS value */
lis %r6,rtasmsr@ha
lwz %r6,rtasmsr@l(%r6)
mtmsr %r6
isync
/* Branch to RTAS */
mtctr %r5
bctrl
/* Now set the MSR back */
lis %r6,rtas_regsave@ha
lwz %r6,rtas_regsave@l(%r6)
mtmsr %r6
isync
/* And return */
lwz %r0,4(%r1)
mtlr %r0
blr

290
sys/powerpc/ofw/ofwcall64.S Normal file
View File

@ -0,0 +1,290 @@
/*-
* Copyright (C) 2009-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 ``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 <sys/syscall.h>
#include <machine/trap.h>
#include <machine/param.h>
#include <machine/spr.h>
#include <machine/asm.h>
#define OFWSTKSZ 4096 /* 4K Open Firmware stack */
/*
* Globals
*/
.data
.align 4
ofwstk:
.space OFWSTKSZ
rtas_regsave:
.space 24 /* 3 * sizeof(register_t) */
GLOBAL(ofmsr)
.llong 0, 0, 0, 0, 0 /* msr/sprg0-3 used in Open Firmware */
GLOBAL(rtasmsr)
.llong 0
GLOBAL(openfirmware_entry)
.llong 0 /* Open Firmware entry point */
GLOBAL(rtas_entry)
.llong 0 /* RTAS entry point */
/*
* Open Firmware Real-mode Entry Point. This is a huge pain.
*/
ASENTRY(ofwcall)
mflr %r0
std %r0,16(%r1)
stdu %r1,-208(%r1)
/*
* We need to save the following, because OF's register save/
* restore code assumes that the contents of registers are
* at most 32 bits wide: lr, cr, r2, r13-r31, the old MSR. These
* get placed in that order in the stack.
*/
mfcr %r4
std %r4,48(%r1)
std %r13,56(%r1)
std %r14,64(%r1)
std %r15,72(%r1)
std %r16,80(%r1)
std %r17,88(%r1)
std %r18,96(%r1)
std %r19,104(%r1)
std %r20,112(%r1)
std %r21,120(%r1)
std %r22,128(%r1)
std %r23,136(%r1)
std %r24,144(%r1)
std %r25,152(%r1)
std %r26,160(%r1)
std %r27,168(%r1)
std %r28,176(%r1)
std %r29,184(%r1)
std %r30,192(%r1)
std %r31,200(%r1)
/* Record the old MSR */
mfmsr %r6
/* read client interface handler */
lis %r4,openfirmware_entry@ha
ld %r4,openfirmware_entry@l(%r4)
/*
* Set the MSR to the OF value. This has the side effect of disabling
* exceptions, which is important for the next few steps.
*/
lis %r5,ofmsr@ha
ld %r5,ofmsr@l(%r5)
mtmsrd %r5
isync
/*
* Set up OF stack. This needs to be accessible in real mode and
* use the 32-bit ABI stack frame format. The pointer to the current
* kernel stack is placed at the very top of the stack along with
* the old MSR so we can get them back later.
*/
mr %r5,%r1
lis %r1,(ofwstk+OFWSTKSZ-32)@ha
addi %r1,%r1,(ofwstk+OFWSTKSZ-32)@l
std %r5,8(%r1) /* Save real stack pointer */
std %r2,16(%r1) /* Save old TOC */
std %r6,24(%r1) /* Save old MSR */
li %r5,0
stw %r5,4(%r1)
stw %r5,0(%r1)
/* Finally, branch to OF */
mtctr %r4
bctrl
/* Reload stack pointer and MSR from the OFW stack */
ld %r6,24(%r1)
ld %r2,16(%r1)
ld %r1,8(%r1)
/* Now set the real MSR */
mtmsrd %r6
isync
/* Sign-extend the return value from OF */
extsw %r3,%r3
/* Restore all the non-volatile registers */
ld %r5,48(%r1)
mtcr %r5
ld %r13,56(%r1)
ld %r14,64(%r1)
ld %r15,72(%r1)
ld %r16,80(%r1)
ld %r17,88(%r1)
ld %r18,96(%r1)
ld %r19,104(%r1)
ld %r20,112(%r1)
ld %r21,120(%r1)
ld %r22,128(%r1)
ld %r23,136(%r1)
ld %r24,144(%r1)
ld %r25,152(%r1)
ld %r26,160(%r1)
ld %r27,168(%r1)
ld %r28,176(%r1)
ld %r29,184(%r1)
ld %r30,192(%r1)
ld %r31,200(%r1)
/* Restore the stack and link register */
ld %r1,0(%r1)
ld %r0,16(%r1)
mtlr %r0
blr
/*
* RTAS 32-bit Entry Point. Similar to the OF one, but simpler (no separate
* stack)
*
* C prototype: int rtascall(void *callbuffer, void *rtas_privdat);
*/
ASENTRY(rtascall)
mflr %r0
std %r0,16(%r1)
stdu %r1,-208(%r1)
/*
* We need to save the following, because RTAS's register save/
* restore code assumes that the contents of registers are
* at most 32 bits wide: lr, cr, r2, r13-r31, the old MSR. These
* get placed in that order in the stack.
*/
mfcr %r5
std %r5,48(%r1)
std %r13,56(%r1)
std %r14,64(%r1)
std %r15,72(%r1)
std %r16,80(%r1)
std %r17,88(%r1)
std %r18,96(%r1)
std %r19,104(%r1)
std %r20,112(%r1)
std %r21,120(%r1)
std %r22,128(%r1)
std %r23,136(%r1)
std %r24,144(%r1)
std %r25,152(%r1)
std %r26,160(%r1)
std %r27,168(%r1)
std %r28,176(%r1)
std %r29,184(%r1)
std %r30,192(%r1)
std %r31,200(%r1)
/* Record the old MSR */
mfmsr %r6
/* read client interface handler */
lis %r5,rtas_entry@ha
ld %r5,rtas_entry@l(%r5)
/*
* Set the MSR to the RTAS value. This has the side effect of disabling
* exceptions, which is important for the next few steps.
*/
lis %r7,rtasmsr@ha
ld %r7,rtasmsr@l(%r7)
mtmsrd %r7
isync
/*
* Set up RTAS register save area, so that we can get back all of
* our 64-bit pointers. Save our stack pointer, the TOC, and the MSR.
* Put this in r1, since RTAS is obliged to save it. Kernel globals
* are below 4 GB, so this is safe.
*/
mr %r7,%r1
lis %r1,rtas_regsave@ha
addi %r1,%r1,rtas_regsave@l
std %r7,0(%r1) /* Save 64-bit stack pointer */
std %r2,8(%r1) /* Save TOC */
std %r6,16(%r1) /* Save MSR */
/* Finally, branch to RTAS */
mtctr %r5
bctrl
/*
* Reload stack pointer and MSR from the reg save area in r1. We are
* running in 32-bit mode at this point, so it doesn't matter if r1
* has become sign-extended.
*/
ld %r6,16(%r1)
ld %r2,8(%r1)
ld %r1,0(%r1)
/* Now set the real MSR */
mtmsrd %r6
isync
/* Sign-extend the return value from RTAS */
extsw %r3,%r3
/* Restore all the non-volatile registers */
ld %r5,48(%r1)
mtcr %r5
ld %r13,56(%r1)
ld %r14,64(%r1)
ld %r15,72(%r1)
ld %r16,80(%r1)
ld %r17,88(%r1)
ld %r18,96(%r1)
ld %r19,104(%r1)
ld %r20,112(%r1)
ld %r21,120(%r1)
ld %r22,128(%r1)
ld %r23,136(%r1)
ld %r24,144(%r1)
ld %r25,152(%r1)
ld %r26,160(%r1)
ld %r27,168(%r1)
ld %r28,176(%r1)
ld %r29,184(%r1)
ld %r30,192(%r1)
ld %r31,200(%r1)
/* Restore the stack and link register */
ld %r1,0(%r1)
ld %r0,16(%r1)
mtlr %r0
blr

243
sys/powerpc/ofw/rtas.c Normal file
View File

@ -0,0 +1,243 @@
/*-
* 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/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/systm.h>
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/md_var.h>
#include <machine/pmap.h>
#include <machine/rtas.h>
#include <machine/stdarg.h>
#include <dev/ofw/openfirm.h>
MALLOC_DEFINE(M_RTAS, "rtas", "Run Time Abstraction Service");
static vm_offset_t rtas_bounce_phys;
static caddr_t rtas_bounce_virt;
static off_t rtas_bounce_offset;
static size_t rtas_bounce_size;
static uintptr_t rtas_private_data;
static struct mtx rtas_mtx;
static phandle_t rtas;
/* From ofwcall.S */
int rtascall(vm_offset_t callbuffer, uintptr_t rtas_privdat);
extern uintptr_t rtas_entry;
extern register_t rtasmsr;
/*
* After the VM is up, allocate RTAS memory and instantiate it
*/
static void rtas_setup(void *);
SYSINIT(rtas_setup, SI_SUB_KMEM, SI_ORDER_ANY, rtas_setup, NULL);
static void
rtas_setup(void *junk)
{
ihandle_t rtasi;
cell_t rtas_size = 0, rtas_ptr;
char path[31];
int result;
rtas = OF_finddevice("/rtas");
if (rtas == -1) {
rtas = 0;
return;
}
OF_package_to_path(rtas, path, sizeof(path));
rtasi = OF_open(path);
if (rtasi == 0) {
rtas = 0;
printf("Error initializing RTAS: could not open node\n");
return;
}
mtx_init(&rtas_mtx, "RTAS", MTX_DEF, 0);
/* RTAS must be called with everything turned off in MSR */
rtasmsr = mfmsr();
rtasmsr &= ~(PSL_IR | PSL_DR | PSL_EE | PSL_SE);
#ifdef __powerpc64__
rtasmsr &= ~PSL_SF;
#endif
/*
* Allocate rtas_size + one page of contiguous, wired physical memory
* that can fit into a 32-bit address space and accessed from real mode.
* This is used both to bounce arguments and for RTAS private data.
*
* It must be 4KB-aligned and not cross a 256 MB boundary.
*/
OF_getprop(rtas, "rtas-size", &rtas_size, sizeof(rtas_size));
rtas_size = round_page(rtas_size);
rtas_bounce_virt = contigmalloc(rtas_size + PAGE_SIZE, M_RTAS, 0, 0,
ulmin(platform_real_maxaddr(), BUS_SPACE_MAXADDR_32BIT),
4096, 256*1024*1024);
rtas_private_data = vtophys(rtas_bounce_virt);
rtas_bounce_virt += rtas_size; /* Actual bounce area */
rtas_bounce_phys = vtophys(rtas_bounce_virt);
rtas_bounce_size = PAGE_SIZE;
/*
* Instantiate RTAS. We always use the 32-bit version.
*/
result = OF_call_method("instantiate-rtas", rtasi, 1, 1,
(cell_t)rtas_private_data, &rtas_ptr);
OF_close(rtasi);
if (result != 0) {
rtas = 0;
rtas_ptr = 0;
printf("Error initializing RTAS (%d)\n", result);
return;
}
rtas_entry = (uintptr_t)(rtas_ptr);
}
static cell_t
rtas_real_map(const void *buf, size_t len)
{
cell_t phys;
mtx_assert(&rtas_mtx, MA_OWNED);
/*
* Make sure the bounce page offset satisfies any reasonable
* alignment constraint.
*/
rtas_bounce_offset += sizeof(register_t) -
(rtas_bounce_offset % sizeof(register_t));
if (rtas_bounce_offset + len > rtas_bounce_size) {
panic("Oversize RTAS call!");
return 0;
}
if (buf != NULL)
memcpy(rtas_bounce_virt + rtas_bounce_offset, buf, len);
else
return (0);
phys = rtas_bounce_phys + rtas_bounce_offset;
rtas_bounce_offset += len;
return (phys);
}
static void
rtas_real_unmap(cell_t physaddr, void *buf, size_t len)
{
mtx_assert(&rtas_mtx, MA_OWNED);
if (physaddr == 0)
return;
memcpy(buf, rtas_bounce_virt + (physaddr - rtas_bounce_phys), len);
}
/* Check if we have RTAS */
int
rtas_exists(void)
{
return (rtas != 0);
}
/* Call an RTAS method by token */
int
rtas_call_method(cell_t token, int nargs, int nreturns, ...)
{
vm_offset_t argsptr;
va_list ap;
struct {
cell_t token;
cell_t nargs;
cell_t nreturns;
cell_t args_n_results[12];
} args;
int n, result;
if (!rtas_exists() || nargs + nreturns > 12)
return (-1);
args.token = token;
va_start(ap, nreturns);
mtx_lock(&rtas_mtx);
rtas_bounce_offset = 0;
args.nargs = nargs;
args.nreturns = nreturns;
for (n = 0; n < nargs; n++)
args.args_n_results[n] = va_arg(ap, cell_t);
argsptr = rtas_real_map(&args, sizeof(args));
result = rtascall(argsptr, rtas_private_data);
rtas_real_unmap(argsptr, &args, sizeof(args));
mtx_unlock(&rtas_mtx);
if (result < 0)
return (result);
for (n = nargs; n < nargs + nreturns; n++)
*va_arg(ap, cell_t *) = args.args_n_results[n];
return (result);
}
/* Look up an RTAS token */
cell_t
rtas_token_lookup(const char *method)
{
cell_t token;
if (!rtas_exists())
return (-1);
if (OF_getprop(rtas, method, &token, sizeof(token)) == -1)
return (-1);
return (token);
}