Import support for the Sony Playstation 3 using the OtherOS feature

available on firmwares 3.15 and earlier.

Caveats: Support for the internal SATA controller is currently missing,
as is support for framebuffer resolutions other than 720x480. These
deficiencies will be remedied soon.

Special thanks to Peter Grehan for providing the hardware that made this
port possible, and thanks to Geoff Levand of Sony Computer Entertainment
for advice on the LV1 hypervisor.
This commit is contained in:
nwhitehorn 2011-01-06 04:12:29 +00:00
parent 0bfe906525
commit c2aa4fc0eb
36 changed files with 7432 additions and 1 deletions

View File

@ -1,5 +1,5 @@
# $FreeBSD$
SUBDIR= boot1.chrp ofw uboot
SUBDIR= boot1.chrp ofw ps3 uboot
.include <bsd.subdir.mk>

View File

@ -0,0 +1,122 @@
# $FreeBSD$
.include <bsd.own.mk>
MK_SSP= no
PROG= loader.ps3
NEWVERSWHAT= "Playstation 3 loader" ${MACHINE_ARCH}
BINDIR?= /boot
INSTALLFLAGS= -b
# Architecture-specific loader code
SRCS= start.S conf.c metadata.c vers.c main.c devicename.c ppc64_elf_freebsd.c
SRCS+= lv1call.S ps3cons.c font.h ps3mmu.c ps3net.c
SRCS+= ucmpdi2.c
LOADER_DISK_SUPPORT?= yes
LOADER_UFS_SUPPORT?= yes
LOADER_CD9660_SUPPORT?= yes
LOADER_EXT2FS_SUPPORT?= no
LOADER_NET_SUPPORT?= yes
LOADER_NFS_SUPPORT?= yes
LOADER_TFTP_SUPPORT?= no
LOADER_GZIP_SUPPORT?= yes
LOADER_FDT_SUPPORT?= no
LOADER_BZIP2_SUPPORT?= no
.if ${LOADER_DISK_SUPPORT} == "yes"
CFLAGS+= -DLOADER_DISK_SUPPORT
.endif
.if ${LOADER_UFS_SUPPORT} == "yes"
CFLAGS+= -DLOADER_UFS_SUPPORT
.endif
.if ${LOADER_CD9660_SUPPORT} == "yes"
CFLAGS+= -DLOADER_CD9660_SUPPORT
.endif
.if ${LOADER_EXT2FS_SUPPORT} == "yes"
CFLAGS+= -DLOADER_EXT2FS_SUPPORT
.endif
.if ${LOADER_GZIP_SUPPORT} == "yes"
CFLAGS+= -DLOADER_GZIP_SUPPORT
.endif
.if ${LOADER_BZIP2_SUPPORT} == "yes"
CFLAGS+= -DLOADER_BZIP2_SUPPORT
.endif
.if ${LOADER_NET_SUPPORT} == "yes"
CFLAGS+= -DLOADER_NET_SUPPORT
.endif
.if ${LOADER_NFS_SUPPORT} == "yes"
CFLAGS+= -DLOADER_NFS_SUPPORT
.endif
.if ${LOADER_TFTP_SUPPORT} == "yes"
CFLAGS+= -DLOADER_TFTP_SUPPORT
.endif
.if ${LOADER_FDT_SUPPORT} == "yes"
CFLAGS+= -I${.CURDIR}/../../fdt
CFLAGS+= -I${.OBJDIR}/../../fdt
CFLAGS+= -DLOADER_FDT_SUPPORT
LIBFDT= ${.OBJDIR}/../../fdt/libfdt.a
.endif
.if ${MK_FORTH} != "no"
# Enable BootForth
BOOT_FORTH= yes
CFLAGS+= -DBOOT_FORTH -I${.CURDIR}/../../ficl -I${.CURDIR}/../../ficl/powerpc
LIBFICL= ${.OBJDIR}/../../ficl/libficl.a
.endif
# Avoid the open-close-dance for every file access as some firmwares perform
# an auto-negotiation on every open of the network interface and thus causes
# netbooting to take horribly long.
CFLAGS+= -DNETIF_OPEN_CLOSE_ONCE -mcpu=powerpc64
# Always add MI sources
.PATH: ${.CURDIR}/../../common ${.CURDIR}/../../../libkern
.include "${.CURDIR}/../../common/Makefile.inc"
CFLAGS+= -I${.CURDIR}/../../common -I${.CURDIR}/../../..
CFLAGS+= -I.
CLEANFILES+= vers.c loader.help
CFLAGS+= -Wall -ffreestanding -msoft-float -DAIM
# load address. set in linker script
RELOC?= 0x0
CFLAGS+= -DRELOC=${RELOC}
LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc
# 64-bit bridge extensions
CFLAGS+= -Wa,-mppc64bridge
# Pull in common loader code
#.PATH: ${.CURDIR}/../../ofw/common
#.include "${.CURDIR}/../../ofw/common/Makefile.inc"
# where to get libstand from
CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/
DPADD= ${LIBFICL} ${LIBOFW} ${LIBSTAND}
LDADD= ${LIBFICL} ${LIBOFW} -lstand
SC_DFLT_FONT=cp437
font.h:
uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x16.fnt && file2c 'u_char dflt_font_16[16*256] = {' '};' < ${SC_DFLT_FONT}-8x16 > font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x14.fnt && file2c 'u_char dflt_font_14[14*256] = {' '};' < ${SC_DFLT_FONT}-8x14 >> font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x8.fnt && file2c 'u_char dflt_font_8[8*256] = {' '};' < ${SC_DFLT_FONT}-8x8 >> font.h
vers.c: ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version
sh ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version ${NEWVERSWHAT}
loader.help: help.common help.ps3
cat ${.ALLSRC} | \
awk -f ${.CURDIR}/../../common/merge_help.awk > ${.TARGET}
.PATH: ${.CURDIR}/../../forth
FILES= loader.help loader.4th support.4th loader.conf
FILESDIR_loader.conf= /boot/defaults
.if !exists(${DESTDIR}/boot/loader.rc)
FILES+= loader.rc
.endif
.include <bsd.prog.mk>

119
sys/boot/powerpc/ps3/conf.c Normal file
View File

@ -0,0 +1,119 @@
/*-
* Copyright (c) 1999 Michael Smith <msmith@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, 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 <stand.h>
#include "bootstrap.h"
#if defined(LOADER_NET_SUPPORT)
#include "dev_net.h"
#endif
/*
* We could use linker sets for some or all of these, but
* then we would have to control what ended up linked into
* the bootstrap. So it's easier to conditionalise things
* here.
*
* XXX rename these arrays to be consistent and less namespace-hostile
*/
/* Exported for libstand */
struct devsw *devsw[] = {
#if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT)
#ifdef NOTYET
&ps3disk,
#endif
#endif
#if defined(LOADER_NET_SUPPORT)
&netdev,
#endif
NULL
};
struct fs_ops *file_system[] = {
#if defined(LOADER_UFS_SUPPORT)
&ufs_fsops,
#endif
#if defined(LOADER_CD9660_SUPPORT)
&cd9660_fsops,
#endif
#if defined(LOADER_EXT2FS_SUPPORT)
&ext2fs_fsops,
#endif
#if defined(LOADER_NFS_SUPPORT)
&nfs_fsops,
#endif
#if defined(LOADER_TFTP_SUPPORT)
&tftp_fsops,
#endif
#if defined(LOADER_GZIP_SUPPORT)
&gzipfs_fsops,
#endif
#if defined(LOADER_BZIP2_SUPPORT)
&bzipfs_fsops,
#endif
NULL
};
extern struct netif_driver ps3net;
struct netif_driver *netif_drivers[] = {
#if defined(LOADER_NET_SUPPORT)
&ps3net,
#endif
NULL,
};
/* Exported for PowerPC only */
/*
* Sort formats so that those that can detect based on arguments
* rather than reading the file go first.
*/
extern struct file_format ppc_elf64;
struct file_format *file_formats[] = {
&ppc_elf64,
NULL
};
/*
* Consoles
*/
extern struct console ps3console;
struct console *consoles[] = {
&ps3console,
NULL
};
/*
* reloc - our load address
*/
vm_offset_t reloc = RELOC;

View File

@ -0,0 +1,238 @@
/*-
* Copyright (c) 1998 Michael Smith <msmith@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, 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/disklabel.h>
#include <stand.h>
#include <string.h>
#include "bootstrap.h"
static int ps3_parsedev(struct devdesc **dev, const char *devspec,
const char **path);
/*
* Point (dev) at an allocated device specifier for the device matching the
* path in (devspec). If it contains an explicit device specification,
* use that. If not, use the default device.
*/
int
ps3_getdev(void **vdev, const char *devspec, const char **path)
{
struct devdesc **dev = (struct devdesc **)vdev;
int rv = 0;
/*
* If it looks like this is just a path and no
* device, go with the current device.
*/
if ((devspec == NULL) || (devspec[0] == '/') ||
(strchr(devspec, ':') == NULL)) {
rv = ps3_parsedev(dev, getenv("currdev"), NULL);
if (rv == 0 && path != NULL)
*path = devspec;
return(rv);
}
/*
* Try to parse the device name off the beginning of the devspec.
*/
return (ps3_parsedev(dev, devspec, path));
}
/*
* Point (dev) at an allocated device specifier matching the string version
* at the beginning of (devspec). Return a pointer to the remaining
* text in (path).
*
* In all cases, the beginning of (devspec) is compared to the names
* of known devices in the device switch, and then any following text
* is parsed according to the rules applied to the device type.
*
* For disk-type devices, the syntax is:
*
* disk<unit>[<partition>]:
*
*/
static int
ps3_parsedev(struct devdesc **dev, const char *devspec, const char **path)
{
struct devdesc *idev;
struct devsw *dv;
char *cp;
const char *np;
int i, unit, pnum, ptype, err;
/* minimum length check */
if (strlen(devspec) < 2)
return(EINVAL);
/* look for a device that matches */
for (i = 0, dv = NULL; devsw[i] != NULL; i++) {
if (!strncmp(devspec, devsw[i]->dv_name,
strlen(devsw[i]->dv_name))) {
dv = devsw[i];
break;
}
}
if (dv == NULL)
return(ENOENT);
idev = malloc(sizeof(struct devdesc));
err = 0;
np = (devspec + strlen(dv->dv_name));
switch(dv->dv_type) {
case DEVT_NONE:
break;
#ifdef NOTYET
case DEVT_DISK:
unit = -1;
pnum = -1;
ptype = -1;
if (*np && (*np != ':')) {
/* next comes the unit number */
unit = strtol(np, &cp, 10);
if (cp == np) {
err = EUNIT;
goto fail;
}
if (*cp && (*cp != ':')) {
/* get partition */
if (*cp == 'p' && *(cp + 1) &&
*(cp + 1) != ':') {
pnum = strtol(cp + 1, &cp, 10);
ptype = PTYPE_GPT;
} else {
pnum = *cp - 'a';
ptype = PTYPE_BSDLABEL;
if ((pnum < 0) ||
(pnum >= MAXPARTITIONS)) {
err = EPART;
goto fail;
}
cp++;
}
}
}
if (*cp && (*cp != ':')) {
err = EINVAL;
goto fail;
}
idev->d_unit = unit;
idev->d_disk.pnum = pnum;
idev->d_disk.ptype = ptype;
idev->d_disk.data = NULL;
if (path != NULL)
*path = (*cp == 0) ? cp : cp + 1;
break;
#endif
case DEVT_NET:
/*
* PS3 only has one network interface (well, two, but
* netbooting over wireless is not something I'm going
* to worry about.
*/
idev->d_unit = 0;
break;
default:
err = EINVAL;
goto fail;
}
idev->d_dev = dv;
idev->d_type = dv->dv_type;
if (dev == NULL) {
free(idev);
} else {
*dev = idev;
}
return (0);
fail:
free(idev);
return (err);
}
char *
ps3_fmtdev(void *vdev)
{
struct devdesc *dev = (struct devdesc *)vdev;
char *cp;
static char buf[128];
switch(dev->d_type) {
case DEVT_NONE:
strcpy(buf, "(no device)");
break;
#ifdef NOTYET
case DEVT_DISK:
cp = buf;
cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_unit);
if (dev->d_kind.disk.pnum >= 0) {
if (dev->d_kind.disk.ptype == PTYPE_BSDLABEL)
cp += sprintf(cp, "%c",
dev->d_kind.disk.pnum + 'a');
else if (dev->d_kind.disk.ptype == PTYPE_GPT)
cp += sprintf(cp, "p%i",
dev->d_kind.disk.pnum);
}
strcat(cp, ":");
break;
#endif
case DEVT_NET:
sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
break;
}
return(buf);
}
/*
* Set currdev to suit the value being supplied in (value).
*/
int
ps3_setcurrdev(struct env_var *ev, int flags, const void *value)
{
struct devdesc *ncurr;
int rv;
if ((rv = ps3_parsedev(&ncurr, value, NULL)) != 0)
return (rv);
free(ncurr);
env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
return (0);
}

View File

@ -0,0 +1 @@
$FreeBSD$

View File

@ -0,0 +1,112 @@
/* $FreeBSD$ */
OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
OUTPUT_ARCH(powerpc:common)
ENTRY(_start)
SEARCH_DIR(/usr/lib);
/* Do we need any of these for elf?
__DYNAMIC = 0; */
PROVIDE (__stack = 0);
SECTIONS
{
/* Read-only sections, merged into text segment: */
. = 0x0;
.text :
{
*(.text)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.gnu.linkonce.t*)
} =0
_etext = .;
.interp : { *(.interp) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rela.text :
{ *(.rela.text) *(.rela.gnu.linkonce.t*) }
.rela.data :
{ *(.rela.data) *(.rela.gnu.linkonce.d*) }
.rela.rodata :
{ *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
.rela.got : { *(.rela.got) }
.rela.got1 : { *(.rela.got1) }
.rela.got2 : { *(.rela.got2) }
.rela.ctors : { *(.rela.ctors) }
.rela.dtors : { *(.rela.dtors) }
.rela.init : { *(.rela.init) }
.rela.fini : { *(.rela.fini) }
.rela.bss : { *(.rela.bss) }
.rela.plt : { *(.rela.plt) }
.rela.sbss : { *(.rela.sbss) }
.rela.sbss2 : { *(.rela.sbss2) }
.text :
{
*(.text)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.gnu.linkonce.t*)
} =0
_etext = .;
PROVIDE (etext = .);
.init : { *(.init) } =0
.fini : { *(.fini) } =0
.rodata : { *(.rodata) *(.gnu.linkonce.r*) }
.rodata1 : { *(.rodata1) }
.sbss2 : { *(.sbss2) }
/* Adjust the address for the data segment to the next page up. */
. = ((. + 0x1000) & ~(0x1000 - 1));
.data :
{
*(.data)
*(.gnu.linkonce.d*)
CONSTRUCTORS
}
.data1 : { *(.data1) }
.got1 : { *(.got1) }
.dynamic : { *(.dynamic) }
/* Put .ctors and .dtors next to the .got2 section, so that the pointers
get relocated with -mrelocatable. Also put in the .fixup pointers.
The current compiler no longer needs this, but keep it around for 2.7.2 */
PROVIDE (_GOT2_START_ = .);
.got2 : { *(.got2) }
PROVIDE (__CTOR_LIST__ = .);
.ctors : { *(.ctors) }
PROVIDE (__CTOR_END__ = .);
PROVIDE (__DTOR_LIST__ = .);
.dtors : { *(.dtors) }
PROVIDE (__DTOR_END__ = .);
PROVIDE (_FIXUP_START_ = .);
.fixup : { *(.fixup) }
PROVIDE (_FIXUP_END_ = .);
PROVIDE (_GOT2_END_ = .);
PROVIDE (_GOT_START_ = .);
.got : { *(.got) }
.got.plt : { *(.got.plt) }
PROVIDE (_GOT_END_ = .);
_edata = .;
PROVIDE (edata = .);
.sbss :
{
PROVIDE (__sbss_start = .);
*(.sbss)
*(.scommon)
*(.dynsbss)
PROVIDE (__sbss_end = .);
}
.plt : { *(.plt) }
.bss :
{
PROVIDE (__bss_start = .);
*(.dynbss)
*(.bss)
*(COMMON)
}
. = ALIGN(4096);
_end = . ;
PROVIDE (end = .);
}

View File

@ -0,0 +1,256 @@
/*-
* Copyright (C) 2010 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$
*/
/* Hypercall stubs. Note: this is all a hack and should die. */
#define hc .long 0x44000022
#define LD64_IM(r, highest, higher, high, low) \
lis r,highest; \
addi r,r,higher; \
sldi r,r,32; \
addis r,r,high; \
addi r,r,low;
#define SIMPLE_HVCALL(x, c) \
.global x; \
x: \
mflr %r0; \
stw %r0,4(%r1); \
clrldi %r3,%r3,32; \
clrldi %r4,%r4,32; \
clrldi %r5,%r5,32; \
clrldi %r6,%r6,32; \
clrldi %r7,%r7,32; \
clrldi %r8,%r8,32; \
clrldi %r9,%r9,32; \
clrldi %r10,%r10,32; \
li %r11,c; \
hc; \
extsw %r3,%r3; \
lwz %r0,4(%r1); \
mtlr %r0; \
blr
SIMPLE_HVCALL(lv1_open_device, 170)
SIMPLE_HVCALL(lv1_close_device, 171)
SIMPLE_HVCALL(lv1_gpu_open, 210)
SIMPLE_HVCALL(lv1_gpu_context_attribute, 225)
SIMPLE_HVCALL(lv1_panic, 255)
SIMPLE_HVCALL(lv1_net_start_tx_dma, 187)
SIMPLE_HVCALL(lv1_net_stop_tx_dma, 188)
SIMPLE_HVCALL(lv1_net_start_rx_dma, 189)
SIMPLE_HVCALL(lv1_net_stop_rx_dma, 190)
.global lv1_get_physmem
lv1_get_physmem:
mflr %r0
stw %r0,4(%r1)
stw %r3,-8(%r1) /* Address for maxmem */
li %r11,69 /* Get PU ID */
hc
std %r4,-16(%r1)
li %r11,74 /* Get LPAR ID */
hc
std %r4,-24(%r1)
ld %r3,-24(%r1)
LD64_IM(%r4,0x0000,0x0000,0x6269,0x0000 /* "bi" */)
LD64_IM(%r5,0x7075,0x0000,0x0000,0x0000 /* "pu" */)
ld %r6,-16(%r1)
LD64_IM(%r7,0x726d,0x5f73,0x697a,0x6500 /* "rm_size" */)
li %r11,91
hc
extsw %r3,%r3
lwz %r5,-8(%r1)
std %r4,0(%r5)
lwz %r0,4(%r1)
mtlr %r0
blr
.global lv1_setup_address_space
lv1_setup_address_space:
mflr %r0
stw %r0,4(%r1)
stw %r3,-4(%r1)
stw %r4,-8(%r1)
li %r3,18 /* PT size: log2(256 KB) */
li %r4,2 /* Two page sizes */
li %r5,24 /* Page sizes: (24 << 56) | (16 << 48) */
sldi %r5,%r5,24
li %r6,16
sldi %r6,%r6,16
or %r5,%r5,%r6
sldi %r5,%r5,32
li %r11,2 /* lv1_construct_virtual_address_space */
hc
lwz %r6,-4(%r1)
lwz %r7,-8(%r1)
std %r4,0(%r6)
std %r5,0(%r7)
/* AS_ID in r4 */
mr %r3,%r4
li %r11,7 /* lv1_select_virtual_address_space */
hc
extsw %r3,%r3
lwz %r0,4(%r1)
mtlr %r0
blr
.global lv1_insert_pte
lv1_insert_pte:
mflr %r0
stw %r0,4(%r1)
mr %r11,%r4 /* Save R4 */
clrldi %r3,%r3,32
clrldi %r7,%r5,32
sldi %r4,%r3,3 /* Convert ptegidx into base PTE slot */
li %r3,0 /* Current address space */
ld %r5,0(%r11)
ld %r6,8(%r11)
li %r8,0 /* No other flags */
li %r11,158
hc
extsw %r3,%r3
lwz %r0,4(%r1)
mtlr %r0
blr
.global lv1_gpu_context_allocate
lv1_gpu_context_allocate:
mflr %r0
stw %r0,4(%r1)
stw %r7,-4(%r1)
sldi %r3,%r3,32
clrldi %r4,%r4,32
ori %r3,%r3,%r4
clrldi %r4,%r5,32
clrldi %r5,%r6,32
li %r11,217
hc
extsw %r3,%r3
lwz %r7,-4(%r1)
std %r4,0(%r7)
lwz %r0,4(%r1)
mtlr %r0
blr
.global lv1_gpu_memory_allocate
lv1_gpu_memory_allocate:
mflr %r0
stw %r0,4(%r1)
stw %r8,-4(%r1)
stw %r9,-8(%r1)
li %r11,214
hc
extsw %r3,%r3
lwz %r8,-4(%r1)
lwz %r9,-8(%r1)
std %r4,0(%r8)
std %r5,0(%r9)
lwz %r0,4(%r1)
mtlr %r0
blr
.global lv1_net_control
lv1_net_control:
mflr %r0
stw %r0,4(%r1)
stw %r9,-4(%r1)
li %r11,194
hc
extsw %r3,%r3
lwz %r8,-4(%r1)
std %r4,0(%r8)
lwz %r0,4(%r1)
mtlr %r0
blr
.global lv1_setup_dma
lv1_setup_dma:
mflr %r0
stw %r0,4(%r1)
stw %r3,-4(%r1)
stw %r4,-8(%r1)
stw %r5,-12(%r1)
lwz %r3,-4(%r1)
lwz %r4,-8(%r1)
lis %r5,0x0800 /* 128 MB */
li %r6,24 /* log2(IO_PAGESIZE) */
li %r7,0 /* flags */
li %r11,174 /* lv1_allocate_device_dma_region */
hc
extsw %r3,%r3
cmpdi %r3,0
bne 1f
std %r4,-24(%r1)
lwz %r3,-4(%r1)
lwz %r4,-8(%r1)
li %r5,0
ld %r6,-24(%r1)
lis %r7,0x0800 /* 128 MB */
lis %r8,0xf800 /* flags */
sldi %r8,%r8,32
li %r11,176 /* lv1_map_device_dma_region */
hc
extsw %r3,%r3
lwz %r9,-12(%r1)
ld %r6,-24(%r1)
std %r6,0(%r9)
1: lwz %r0,4(%r1)
mtlr %r0
blr

View File

@ -0,0 +1,72 @@
/*-
* Copyright (C) 2010 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$
*/
#ifndef _PS3_LV1CALL_H
#define _PS3_LV1CALL_H
#include <machine/pte.h>
int lv1_get_physmem(uint64_t *maxmem);
int lv1_setup_address_space(uint64_t *as_id, uint64_t *ptsize);
int lv1_insert_pte(u_int ptegidx, struct lpte *pte, int lockflags);
int lv1_panic(int reboot);
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET 0x0100
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x0101
#define L1GPU_DISPLAY_SYNC_HSYNC 1
#define L1GPU_DISPLAY_SYNC_VSYNC 2
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x0102
int lv1_gpu_open(int);
int lv1_gpu_context_attribute(int context, int op, int, int, int, int);
int lv1_gpu_memory_allocate(int size, int, int, int, int, uint64_t *handle,
uint64_t *paddr);
int lv1_gpu_context_allocate(uint64_t handle, int, uint64_t *context);
int lv1_open_device(int, int, int /* 0 */);
int lv1_close_device(int, int);
int lv1_setup_dma(int, int, uint64_t *dmabase);
#define GELIC_GET_MAC_ADDRESS 0x0001
#define GELIC_GET_LINK_STATUS 0x0002
#define GELIC_LINK_UP 0x0001
#define GELIC_FULL_DUPLEX 0x0002
#define GELIC_AUTO_NEG 0x0004
#define GELIC_SPEED_10 0x0010
#define GELIC_SPEED_100 0x0020
#define GELIC_SPEED_1000 0x0040
#define GELIC_GET_VLAN_ID 0x0004
int lv1_net_init(int bus, int dev);
int lv1_net_control(int bus, int dev, int, int, int, int, uint64_t *);
int lv1_net_start_tx_dma(int bus, int dev, uint32_t addr, int);
int lv1_net_start_rx_dma(int bus, int dev, uint32_t addr, int);
int lv1_net_stop_tx_dma(int bus, int dev, int);
int lv1_net_stop_rx_dma(int bus, int dev, int);
#endif

211
sys/boot/powerpc/ps3/main.c Normal file
View File

@ -0,0 +1,211 @@
/*-
* Copyright (C) 2010 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 <stand.h>
#include <sys/param.h>
#define _KERNEL
#include <machine/cpufunc.h>
#include "bootstrap.h"
#include "lv1call.h"
#include "ps3.h"
struct arch_switch archsw;
extern void *_end;
extern char bootprog_name[];
extern char bootprog_rev[];
extern char bootprog_date[];
extern char bootprog_maker[];
int ps3_getdev(void **vdev, const char *devspec, const char **path);
ssize_t ps3_copyin(const void *src, vm_offset_t dest, const size_t len);
ssize_t ps3_copyout(vm_offset_t src, void *dest, const size_t len);
ssize_t ps3_readin(const int fd, vm_offset_t dest, const size_t len);
int ps3_autoload(void);
int ps3_setcurrdev(struct env_var *ev, int flags, const void *value);
static uint64_t basetb;
int
main(void)
{
uint64_t maxmem = 0;
void *heapbase;
int i;
lv1_get_physmem(&maxmem);
ps3mmu_init(maxmem);
/*
* Set up console.
*/
cons_probe();
/*
* Set the heap to one page after the end of the loader.
*/
heapbase = (void *)(maxmem - 0x80000);
setheap(heapbase, maxmem);
/*
* March through the device switch probing for things.
*/
for (i = 0; devsw[i] != NULL; i++)
if (devsw[i]->dv_init != NULL)
(devsw[i]->dv_init)();
/*
* Get timebase at boot.
*/
basetb = mftb();
archsw.arch_getdev = ps3_getdev;
archsw.arch_copyin = ps3_copyin;
archsw.arch_copyout = ps3_copyout;
archsw.arch_readin = ps3_readin;
archsw.arch_autoload = ps3_autoload;
printf("\n");
printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
printf("(%s, %s)\n", bootprog_maker, bootprog_date);
printf("Memory: %lldKB\n", maxmem / 1024);
env_setenv("currdev", EV_VOLATILE, "net", ps3_setcurrdev, env_nounset);
env_setenv("loaddev", EV_VOLATILE, "net", env_noset, env_nounset);
setenv("LINES", "24", 1);
setenv("hw.platform", "ps3", 1);
interact(); /* doesn't return */
return (0);
}
void
ppc_exception(int code, vm_offset_t where, register_t msr)
{
mtmsr(PSL_IR | PSL_DR | PSL_RI);
printf("Exception %x at %#lx!\n", code, where);
printf("Rebooting in 5 seconds...\n");
delay(10000000);
lv1_panic(1);
}
const u_int ns_per_tick = 12;
void
exit(int code)
{
lv1_panic(code);
}
void
delay(int usecs)
{
uint64_t tb,ttb;
tb = mftb();
ttb = tb + (usecs * 1000 + ns_per_tick - 1) / ns_per_tick;
while (tb < ttb)
tb = mftb();
}
int
getsecs()
{
return ((mftb() - basetb)*ns_per_tick/1000000000);
}
time_t
time(time_t *tloc)
{
time_t rv;
rv = getsecs();
if (tloc != NULL)
*tloc = rv;
return (rv);
}
ssize_t
ps3_copyin(const void *src, vm_offset_t dest, const size_t len)
{
bcopy(src, (void *)dest, len);
return (len);
}
ssize_t
ps3_copyout(vm_offset_t src, void *dest, const size_t len)
{
bcopy((void *)src, dest, len);
return (len);
}
ssize_t
ps3_readin(const int fd, vm_offset_t dest, const size_t len)
{
void *buf;
size_t resid, chunk, get;
ssize_t got;
vm_offset_t p;
p = dest;
chunk = min(PAGE_SIZE, len);
buf = malloc(chunk);
if (buf == NULL) {
printf("ps3_readin: buf malloc failed\n");
return(0);
}
for (resid = len; resid > 0; resid -= got, p += got) {
get = min(chunk, resid);
got = read(fd, buf, get);
if (got <= 0) {
if (got < 0)
printf("ps3_readin: read failed\n");
break;
}
bcopy(buf, (void *)p, got);
}
free(buf);
return (len - resid);
}
int
ps3_autoload(void)
{
return (0);
}

View File

@ -0,0 +1,355 @@
/*-
* Copyright (c) 1998 Michael Smith <msmith@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, 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.
*
* from: FreeBSD: src/sys/boot/sparc64/loader/metadata.c,v 1.6
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <stand.h>
#include <sys/param.h>
#include <sys/reboot.h>
#include <sys/linker.h>
#include <machine/metadata.h>
#include "bootstrap.h"
/*
* Return a 'boothowto' value corresponding to the kernel arguments in
* (kargs) and any relevant environment variables.
*/
static struct
{
const char *ev;
int mask;
} howto_names[] = {
{"boot_askname", RB_ASKNAME},
{"boot_cdrom", RB_CDROM},
{"boot_ddb", RB_KDB},
{"boot_dfltroot", RB_DFLTROOT},
{"boot_gdb", RB_GDB},
{"boot_multicons", RB_MULTIPLE},
{"boot_mute", RB_MUTE},
{"boot_pause", RB_PAUSE},
{"boot_serial", RB_SERIAL},
{"boot_single", RB_SINGLE},
{"boot_verbose", RB_VERBOSE},
{NULL, 0}
};
int
md_getboothowto(char *kargs)
{
char *cp;
int howto;
int active;
int i;
/* Parse kargs */
howto = 0;
if (kargs != NULL) {
cp = kargs;
active = 0;
while (*cp != 0) {
if (!active && (*cp == '-')) {
active = 1;
} else if (active)
switch (*cp) {
case 'a':
howto |= RB_ASKNAME;
break;
case 'C':
howto |= RB_CDROM;
break;
case 'd':
howto |= RB_KDB;
break;
case 'D':
howto |= RB_MULTIPLE;
break;
case 'm':
howto |= RB_MUTE;
break;
case 'g':
howto |= RB_GDB;
break;
case 'h':
howto |= RB_SERIAL;
break;
case 'p':
howto |= RB_PAUSE;
break;
case 'r':
howto |= RB_DFLTROOT;
break;
case 's':
howto |= RB_SINGLE;
break;
case 'v':
howto |= RB_VERBOSE;
break;
default:
active = 0;
break;
}
cp++;
}
}
/* get equivalents from the environment */
for (i = 0; howto_names[i].ev != NULL; i++)
if (getenv(howto_names[i].ev) != NULL)
howto |= howto_names[i].mask;
if (!strcmp(getenv("console"), "comconsole"))
howto |= RB_SERIAL;
if (!strcmp(getenv("console"), "nullconsole"))
howto |= RB_MUTE;
return(howto);
}
/*
* Copy the environment into the load area starting at (addr).
* Each variable is formatted as <name>=<value>, with a single nul
* separating each variable, and a double nul terminating the environment.
*/
vm_offset_t
md_copyenv(vm_offset_t addr)
{
struct env_var *ep;
/* traverse the environment */
for (ep = environ; ep != NULL; ep = ep->ev_next) {
archsw.arch_copyin(ep->ev_name, addr, strlen(ep->ev_name));
addr += strlen(ep->ev_name);
archsw.arch_copyin("=", addr, 1);
addr++;
if (ep->ev_value != NULL) {
archsw.arch_copyin(ep->ev_value, addr, strlen(ep->ev_value));
addr += strlen(ep->ev_value);
}
archsw.arch_copyin("", addr, 1);
addr++;
}
archsw.arch_copyin("", addr, 1);
addr++;
return(addr);
}
/*
* Copy module-related data into the load area, where it can be
* used as a directory for loaded modules.
*
* Module data is presented in a self-describing format. Each datum
* is preceded by a 32-bit identifier and a 32-bit size field.
*
* Currently, the following data are saved:
*
* MOD_NAME (variable) module name (string)
* MOD_TYPE (variable) module type (string)
* MOD_ARGS (variable) module parameters (string)
* MOD_ADDR sizeof(vm_offset_t) module load address
* MOD_SIZE sizeof(size_t) module size
* MOD_METADATA (variable) type-specific metadata
*/
static int align;
#define COPY32(v, a, c) { \
u_int32_t x = (v); \
if (c) \
archsw.arch_copyin(&x, a, sizeof(x)); \
a += sizeof(x); \
}
#define MOD_STR(t, a, s, c) { \
COPY32(t, a, c); \
COPY32(strlen(s) + 1, a, c) \
if (c) \
archsw.arch_copyin(s, a, strlen(s) + 1);\
a += roundup(strlen(s) + 1, align); \
}
#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
#define MOD_VAR(t, a, s, c) { \
COPY32(t, a, c); \
COPY32(sizeof(s), a, c); \
if (c) \
archsw.arch_copyin(&s, a, sizeof(s)); \
a += roundup(sizeof(s), align); \
}
#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
#define MOD_METADATA(a, mm, c) { \
COPY32(MODINFO_METADATA | mm->md_type, a, c);\
COPY32(mm->md_size, a, c); \
if (c) \
archsw.arch_copyin(mm->md_data, a, mm->md_size);\
a += roundup(mm->md_size, align); \
}
#define MOD_END(a, c) { \
COPY32(MODINFO_END, a, c); \
COPY32(0, a, c); \
}
vm_offset_t
md_copymodules(vm_offset_t addr, int kern64)
{
struct preloaded_file *fp;
struct file_metadata *md;
uint64_t scratch64;
int c;
c = addr != 0;
/* start with the first module on the list, should be the kernel */
for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
MOD_NAME(addr, fp->f_name, c); /* this field must come first */
MOD_TYPE(addr, fp->f_type, c);
if (fp->f_args)
MOD_ARGS(addr, fp->f_args, c);
if (kern64) {
scratch64 = fp->f_addr;
MOD_ADDR(addr, scratch64, c);
scratch64 = fp->f_size;
MOD_SIZE(addr, scratch64, c);
} else {
MOD_ADDR(addr, fp->f_addr, c);
MOD_SIZE(addr, fp->f_size, c);
}
for (md = fp->f_metadata; md != NULL; md = md->md_next) {
if (!(md->md_type & MODINFOMD_NOCOPY)) {
MOD_METADATA(addr, md, c);
}
}
}
MOD_END(addr, c);
return(addr);
}
/*
* Load the information expected by a powerpc kernel.
*
* - The 'boothowto' argument is constructed
* - The 'bootdev' argument is constructed
* - The kernel environment is copied into kernel space.
* - Module metadata are formatted and placed in kernel space.
*/
int
md_load_dual(char *args, vm_offset_t *modulep, int kern64)
{
struct preloaded_file *kfp;
struct preloaded_file *xp;
struct file_metadata *md;
vm_offset_t kernend;
vm_offset_t addr;
vm_offset_t envp;
vm_offset_t size;
uint64_t scratch64;
char *rootdevname;
int howto;
align = kern64 ? 8 : 4;
howto = md_getboothowto(args);
/*
* Allow the environment variable 'rootdev' to override the supplied device
* This should perhaps go to MI code and/or have $rootdev tested/set by
* MI code before launching the kernel.
*/
rootdevname = getenv("rootdev");
if (rootdevname == NULL)
rootdevname = getenv("currdev");
/* Try reading the /etc/fstab file to select the root device */
getrootmount(rootdevname);
/* find the last module in the chain */
addr = 0;
for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
if (addr < (xp->f_addr + xp->f_size))
addr = xp->f_addr + xp->f_size;
}
/* pad to a page boundary */
addr = roundup(addr, PAGE_SIZE);
/* copy our environment */
envp = addr;
addr = md_copyenv(addr);
/* pad to a page boundary */
addr = roundup(addr, PAGE_SIZE);
kernend = 0;
kfp = file_findfile(NULL, kern64 ? "elf64 kernel" : "elf32 kernel");
if (kfp == NULL)
kfp = file_findfile(NULL, "elf kernel");
if (kfp == NULL)
panic("can't find kernel file");
file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
if (kern64) {
scratch64 = envp;
file_addmetadata(kfp, MODINFOMD_ENVP, sizeof scratch64, &scratch64);
scratch64 = kernend;
file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof scratch64, &scratch64);
} else {
file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
}
*modulep = addr;
size = md_copymodules(0, kern64);
kernend = roundup(addr + size, PAGE_SIZE);
md = file_findmetadata(kfp, MODINFOMD_KERNEND);
if (kern64) {
scratch64 = kernend;
bcopy(&scratch64, md->md_data, sizeof scratch64);
} else {
bcopy(&kernend, md->md_data, sizeof kernend);
}
(void)md_copymodules(addr, kern64);
return(0);
}
int
md_load(char *args, vm_offset_t *modulep)
{
return (md_load_dual(args, modulep, 0));
}
int
md_load64(char *args, vm_offset_t *modulep)
{
return (md_load_dual(args, modulep, 1));
}

View File

@ -0,0 +1,98 @@
/*-
* Copyright (c) 2001 Benno Rice <benno@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, 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$");
#define __ELF_WORD_SIZE 64
#include <sys/param.h>
#include <sys/linker.h>
#include <machine/metadata.h>
#include <machine/elf.h>
#include <stand.h>
#include "bootstrap.h"
extern char end[];
extern vm_offset_t reloc; /* From <arch>/conf.c */
int
ppc64_elf_loadfile(char *filename, u_int64_t dest,
struct preloaded_file **result)
{
int r;
r = __elfN(loadfile)(filename, dest, result);
if (r != 0)
return (r);
/*
* No need to sync the icache for modules: this will
* be done by the kernel after relocation.
*/
if (!strcmp((*result)->f_type, "elf kernel"))
__syncicache((void *) (*result)->f_addr, (*result)->f_size);
return (0);
}
int
ppc64_elf_exec(struct preloaded_file *fp)
{
struct file_metadata *fmp;
vm_offset_t mdp;
Elf_Ehdr *e;
int error;
int (*entry)(u_long, u_long, u_long, void *, u_long);
if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
return(EFTYPE);
}
e = (Elf_Ehdr *)&fmp->md_data;
/* Handle function descriptor */
entry = (void *)(uintptr_t)(*(uint64_t *)e->e_entry);
if ((error = md_load64(fp->f_args, &mdp)) != 0)
return (error);
printf("Kernel entry at %p ...\n", entry);
dev_cleanup();
entry(0 /* FDT */, 0 /* Phys. mem offset */, 0 /* OF entry */,
(void *)mdp, sizeof(mdp));
panic("exec returned");
}
struct file_format ppc_elf64 =
{
ppc64_elf_loadfile,
ppc64_elf_exec
};

View File

@ -0,0 +1,35 @@
/*-
* Copyright (C) 2010 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$
*/
#ifndef _PS3_H
#define _PS3_H
int ps3mmu_init(int maxmem);
int ps3mmu_map(uint64_t va, uint64_t pa);
void *ps3mmu_mapdev(uint64_t pa, size_t length);
#endif

View File

@ -0,0 +1,173 @@
/*-
* Copyright (C) 2010 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 <stand.h>
#include "bootstrap.h"
#include "font.h"
#include "lv1call.h"
#include "ps3.h"
#define FONT_SIZE 14
#define FONT dflt_font_14
#define XMARGIN 40
#define YMARGIN 30
#define BG_COLOR 0x00000000
#define FG_COLOR 0xffffffff
#define FB_SIZE (16*1024*1024)
uint64_t fb_paddr = 0;
uint32_t *fb_vaddr;
int fb_width, fb_height;
int x, y;
static void ps3cons_probe(struct console *cp);
static int ps3cons_init(int arg);
static void ps3cons_putchar(int c);
static int ps3cons_getchar();
static int ps3cons_poll();
struct console ps3console = {
"ps3",
"Playstation 3 Framebuffer",
0,
ps3cons_probe,
ps3cons_init,
ps3cons_putchar,
ps3cons_getchar,
ps3cons_poll,
};
static void
ps3cons_probe(struct console *cp)
{
/* XXX: Get from HV */
fb_width = 720;
fb_height = 480;
cp->c_flags |= C_PRESENTIN|C_PRESENTOUT;
}
static int
ps3cons_init(int arg)
{
uint64_t fbhandle, fbcontext;
int i;
lv1_gpu_open(0);
lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET,
0,0,0,0);
lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET,
0,0,1,0);
lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
0,L1GPU_DISPLAY_SYNC_VSYNC,0,0);
lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
1,L1GPU_DISPLAY_SYNC_VSYNC,0,0);
lv1_gpu_memory_allocate(FB_SIZE, 0, 0, 0, 0, &fbhandle, &fb_paddr);
lv1_gpu_context_allocate(fbhandle, 0, &fbcontext);
lv1_gpu_context_attribute(fbcontext,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, 0, 0, 0);
lv1_gpu_context_attribute(fbcontext,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, 0, 0, 0);
fb_vaddr = ps3mmu_mapdev(fb_paddr, FB_SIZE);
x = y = 0;
/* Blank console */
for (i = 0; i < fb_width*fb_height; i++)
fb_vaddr[i] = BG_COLOR;
return (0);
}
static void
ps3cons_putchar(int c)
{
uint32_t fg, bg;
uint32_t *addr;
int i, j, k;
u_char *p;
fg = FG_COLOR;
bg = BG_COLOR;
switch (c) {
case '\0':
break;
case '\r':
x = 0;
break;
case '\n':
y += FONT_SIZE;
break;
case '\b':
x = max(0, x - 8);
break;
default:
/* Wrap long lines */
if (x + XMARGIN + FONT_SIZE > fb_width - XMARGIN) {
y += FONT_SIZE;
x = 0;
}
if (y + YMARGIN + FONT_SIZE > fb_height - YMARGIN)
y = 0;
addr = fb_vaddr + (y + YMARGIN)*fb_width + (x + XMARGIN);
p = FONT + c*FONT_SIZE;
for (i = 0; i < FONT_SIZE; i++) {
for (j = 0, k = 7; j < 8; j++, k--) {
if ((p[i] & (1 << k)) == 0)
*(addr + j) = bg;
else
*(addr + j) = fg;
}
addr += fb_width;
}
x += 8;
break;
}
}
static int
ps3cons_getchar()
{
return (-1);
}
static int
ps3cons_poll()
{
return (0);
}

View File

@ -0,0 +1,120 @@
/*-
* Copyright (C) 2010 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 <stand.h>
#include <stdint.h>
#define _KERNEL
#include <machine/cpufunc.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <machine/slb.h>
#include <machine/param.h>
#include "bootstrap.h"
#include "lv1call.h"
#include "ps3.h"
register_t pteg_count, pteg_mask;
uint64_t as_id;
uint64_t virtual_avail;
int
ps3mmu_map(uint64_t va, uint64_t pa)
{
struct lpte pt;
int shift;
uint64_t vsid, ptegidx;
if (pa < 0x8000000) { /* Phys mem? */
pt.pte_hi = LPTE_BIG;
pt.pte_lo = LPTE_M;
shift = 24;
vsid = 0;
} else {
pt.pte_hi = 0;
pt.pte_lo = LPTE_I | LPTE_G | LPTE_M | LPTE_NOEXEC;
shift = ADDR_PIDX_SHFT;
vsid = 1;
}
pt.pte_hi |= (vsid << LPTE_VSID_SHIFT) |
(((uint64_t)(va & ADDR_PIDX) >> ADDR_API_SHFT64) & LPTE_API);
pt.pte_lo |= pa;
ptegidx = vsid ^ (((uint64_t)va & ADDR_PIDX) >> shift);
pt.pte_hi |= LPTE_LOCKED | LPTE_VALID;
ptegidx &= pteg_mask;
return (lv1_insert_pte(ptegidx, &pt, LPTE_LOCKED));
}
void *
ps3mmu_mapdev(uint64_t pa, size_t length)
{
uint64_t spa;
void *mapstart;
int err;
mapstart = (void *)(uintptr_t)virtual_avail;
for (spa = pa; spa < pa + length; spa += PAGE_SIZE) {
err = ps3mmu_map(virtual_avail, spa);
virtual_avail += PAGE_SIZE;
if (err != 0)
return (NULL);
}
return (mapstart);
}
int
ps3mmu_init(int maxmem)
{
uint64_t ptsize;
int i;
i = lv1_setup_address_space(&as_id, &ptsize);
pteg_count = ptsize / sizeof(struct lpteg);
pteg_mask = pteg_count - 1;
for (i = 0; i < maxmem; i += 16*1024*1024)
ps3mmu_map(i,i);
virtual_avail = 0x10000000;
__asm __volatile ("slbia; slbmte %0, %1; slbmte %2,%3" ::
"r"((0 << SLBV_VSID_SHIFT) | SLBV_L), "r"(0 | SLBE_VALID),
"r"(1 << SLBV_VSID_SHIFT),
"r"((1 << SLBE_ESID_SHIFT) | SLBE_VALID | 1));
mtmsr(mfmsr() | PSL_IR | PSL_DR | PSL_RI | PSL_ME);
return (0);
}

View File

@ -0,0 +1,278 @@
/*-
* Copyright (C) 2010 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/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#define _KERNEL
#include <machine/cpufunc.h>
#include <stand.h>
#include <net.h>
#include <netif.h>
#include "bootstrap.h"
#include "lv1call.h"
#include "ps3.h"
#define GELIC_DESCR_OWNED 0xa0000000
#define GELIC_CMDSTAT_NOIPSEC 0x00080000
#define GELIC_CMDSTAT_LAST 0x00040000
#define GELIC_RXERRORS 0x7def8000
#define GELIC_POLL_PERIOD 100 /* microseconds */
static int ps3net_probe(struct netif *, void *);
static int ps3net_match(struct netif *, void *);
static void ps3net_init(struct iodesc *, void *);
static int ps3net_get(struct iodesc *, void *, size_t, time_t);
static int ps3net_put(struct iodesc *, void *, size_t);
static void ps3net_end(struct netif *);
struct netif_stats ps3net_stats[1];
struct netif_dif ps3net_ifs[] = {{0, 1, ps3net_stats, 0}};
/* XXX: Get from firmware, not hardcoding */
static int busid = 1;
static int devid = 0;
static int vlan;
static uint64_t dma_base;
struct gelic_dmadesc {
uint32_t paddr;
uint32_t len;
uint32_t next;
uint32_t cmd_stat;
uint32_t result_size;
uint32_t valid_size;
uint32_t data_stat;
uint32_t rxerror;
};
struct netif_driver ps3net = {
"net",
ps3net_match,
ps3net_probe,
ps3net_init,
ps3net_get,
ps3net_put,
ps3net_end,
ps3net_ifs, 1
};
static int
ps3net_match(struct netif *nif, void *machdep_hint)
{
return (1);
}
static int
ps3net_probe(struct netif *nif, void *machdep_hint)
{
return (0);
}
static int
ps3net_put(struct iodesc *desc, void *pkt, size_t len)
{
volatile static struct gelic_dmadesc txdesc __aligned(32);
volatile static char txbuf[1536] __aligned(128);
size_t sendlen;
int err;
#if defined(NETIF_DEBUG)
struct ether_header *eh;
printf("net_put: desc %p, pkt %p, len %d\n", desc, pkt, len);
eh = pkt;
printf("dst: %s ", ether_sprintf(eh->ether_dhost));
printf("src: %s ", ether_sprintf(eh->ether_shost));
printf("type: 0x%x\n", eh->ether_type & 0xffff);
#endif
while (txdesc.cmd_stat & GELIC_DESCR_OWNED) {
printf("Stalled XMIT!\n");
delay(10);
}
/*
* We must add 4 extra bytes to this packet to store the destination
* VLAN.
*/
memcpy(txbuf, pkt, 12);
sendlen = 12;
if (vlan >= 0) {
sendlen += 4;
((uint8_t *)txbuf)[12] = 0x81;
((uint8_t *)txbuf)[13] = 0x00;
((uint8_t *)txbuf)[14] = vlan >> 8;
((uint8_t *)txbuf)[15] = vlan & 0xff;
}
memcpy((void *)txbuf + sendlen, pkt + 12, len - 12);
sendlen += len - 12;
bzero(&txdesc, sizeof(txdesc));
txdesc.paddr = dma_base + (uint32_t)txbuf;
txdesc.len = sendlen;
txdesc.cmd_stat = GELIC_CMDSTAT_NOIPSEC | GELIC_CMDSTAT_LAST |
GELIC_DESCR_OWNED;
powerpc_sync();
do {
err = lv1_net_start_tx_dma(busid, devid,
dma_base + (uint32_t)&txdesc, 0);
delay(1);
if (err != 0)
printf("TX Error: %d\n",err);
} while (err != 0);
return (len);
}
static int
ps3net_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout)
{
volatile static struct gelic_dmadesc rxdesc __aligned(32);
volatile static char rxbuf[1536] __aligned(128);
int err = 0;
if (len == 0)
goto restartdma;
timeout *= 1000000; /* convert to microseconds */
while (rxdesc.cmd_stat & GELIC_DESCR_OWNED) {
if (timeout < GELIC_POLL_PERIOD)
return (ETIMEDOUT);
delay(GELIC_POLL_PERIOD);
timeout -= GELIC_POLL_PERIOD;
}
delay(200);
if (rxdesc.rxerror & GELIC_RXERRORS) {
err = -1;
goto restartdma;
}
/*
* Copy the packet to the receive buffer, leaving out the
* 2 byte VLAN header.
*/
len = min(len, rxdesc.valid_size - 2);
memcpy(pkt, (u_char *)rxbuf + 2, len);
err = len;
#if defined(NETIF_DEBUG)
{
struct ether_header *eh;
printf("net_get: desc %p, pkt %p, len %d\n", desc, pkt, len);
eh = pkt;
printf("dst: %s ", ether_sprintf(eh->ether_dhost));
printf("src: %s ", ether_sprintf(eh->ether_shost));
printf("type: 0x%x\n", eh->ether_type & 0xffff);
}
#endif
restartdma:
lv1_net_stop_rx_dma(busid, devid, 0);
powerpc_sync();
bzero(&rxdesc, sizeof(rxdesc));
rxdesc.paddr = dma_base + (uint32_t)rxbuf;
rxdesc.len = sizeof(rxbuf);
rxdesc.next = 0;
rxdesc.cmd_stat = GELIC_DESCR_OWNED;
powerpc_sync();
lv1_net_start_rx_dma(busid, devid, dma_base + (uint32_t)&rxdesc, 0);
return (err);
}
static void
ps3net_init(struct iodesc *desc, void *machdep_hint)
{
uint64_t mac, val;
int i,err;
err = lv1_open_device(busid, devid, 0);
lv1_net_stop_tx_dma(busid, devid, 0);
lv1_net_stop_rx_dma(busid, devid, 0);
/*
* Wait for link to come up
*/
for (i = 0; i < 1000; i++) {
lv1_net_control(busid, devid, GELIC_GET_LINK_STATUS, 2, 0,
0, &val);
if (val & GELIC_LINK_UP)
break;
delay(500);
}
/*
* Set up DMA IOMMU entries
*/
err = lv1_setup_dma(busid, devid, &dma_base);
/*
* Get MAC address and VLAN IDs
*/
lv1_net_control(busid, devid, GELIC_GET_MAC_ADDRESS, 0, 0, 0, &mac);
bcopy(&((uint8_t *)&mac)[2], desc->myea, sizeof(desc->myea));
vlan = -1;
err = lv1_net_control(busid, devid, GELIC_GET_VLAN_ID, 2, 0,
0, &val);
if (err == 0)
vlan = val;
/*
* Start RX DMA engine
*/
ps3net_get(NULL, NULL, 0, 0);
}
static void
ps3net_end(struct netif *nif)
{
lv1_close_device(busid, devid);
}

View File

@ -0,0 +1,167 @@
/*-
* Copyright (C) 2010 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 <machine/trap_aim.h>
/*
* KBoot and simulators will start this program from the _start symbol, with
* r3 pointing to a flattened device tree (kexec), r4 the physical address
* at which we were loaded, and r5 0 (kexec) or a pointer to Open Firmware
* (simulator). If r4 is non-zero, the first order of business is relocating
* ourselves to 0. In the kboot case, the PPE secondary thread will enter
* at 0x60.
*
* If started directly by the LV1 hypervisor, we are loaded to address 0
* and execution on both threads begins at 0x100 (EXC_RST).
*/
#define CACHELINE_SIZE 128
#define SPR_CTRL 136
/* KBoot thread 0 entry -- do relocation, then jump to main */
.global _start
_start:
mfmsr %r31
clrldi %r31,%r31,1
mtmsrd %r31
isync
cmpwi %r4,0
bne relocate_self
relocated_start:
lis %r1,0x100
bl main
. = 0x40
.global secondary_spin_sem
secondary_spin_sem:
.long 0
. = 0x60
thread1_start_kboot:
mfmsr %r31
clrldi %r31,%r31,1
mtmsrd %r31
isync
ba thread1_start /* kboot copies the first 256 bytes to
* address 0, so we are safe to jump
* (and stay) there */
thread1_start:
li %r3,secondary_spin_sem@l
1: lwz %r1,0(%r3) /* Spin on SECONDARY_SPIN_SEM_ADDRESS */
cmpwi %r1,0
beq 1b /* If the semaphore is still zero, spin again */
/* We have been woken up by thread 0 */
li %r0,0x100 /* Invalidate reset vector cache line */
icbi 0,%r0
isync
sync
ba 0x100 /* Jump to the reset vector */
. = EXC_RST
exc_rst:
mfmsr %r31
clrldi %r31,%r31,1
mtmsrd %r31
isync
mfspr %r3,SPR_CTRL
/* The first two bits of r0 are 01 (thread 1) or 10 (thread 0) */
cntlzw %r3,%r3 /* Now 0 for thread 0, 1 for thread 1 */
cmpwi %r3,0
bne thread1_start /* Send thread 1 to wait */
b relocated_start /* Main entry point for thread 0 */
#define EXCEPTION_HANDLER(exc) \
. = exc; \
li %r3, exc; \
mfsrr0 %r4; \
mfmsr %r5; \
clrldi %r6,%r5,1; \
mtmsrd %r6; \
isync; \
lis %r1,0x100; \
bl ppc_exception
EXCEPTION_HANDLER(EXC_MCHK)
EXCEPTION_HANDLER(EXC_DSI)
EXCEPTION_HANDLER(EXC_DSE)
EXCEPTION_HANDLER(EXC_ISI)
EXCEPTION_HANDLER(EXC_ISE)
EXCEPTION_HANDLER(EXC_EXI)
EXCEPTION_HANDLER(EXC_ALI)
EXCEPTION_HANDLER(EXC_PGM)
EXCEPTION_HANDLER(EXC_FPU)
EXCEPTION_HANDLER(EXC_DECR)
EXCEPTION_HANDLER(EXC_SC)
relocate_self:
/* We enter this with r4 the physical offset for our relocation */
lis %r8,_end@ha /* r8: copy length */
addi %r8,%r8,_end@l
li %r5,0x100 /* r5: dest address */
1: add %r6,%r4,%r5 /* r6: source address */
ld %r7,0(%r6)
std %r7,0(%r5)
addi %r5,%r5,8
cmpw %r5,%r8
blt 1b
/*
* Now invalidate the cacheline with the second half of relocate_self,
* and do an absolute branch there in case we overwrote part of
* ourselves.
*/
lis %r9,relocate_self_cache@ha
addi %r9,%r9,relocate_self_cache@l
dcbst 0,%r9
sync
icbi 0,%r9
sync
isync
ba relocate_self_cache
relocate_self_cache:
/* Now invalidate the icache */
li %r5,0x100
2: dcbst 0,%r5
sync
icbi 0,%r5
sync
isync
cmpw %r5,%r8
addi %r5,%r5,CACHELINE_SIZE
blt 2b
/* All done: absolute jump to relocated entry point */
ba relocated_start

View File

@ -0,0 +1,6 @@
$FreeBSD$
NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
file is important. Make sure the current version number is on line 6.
0.1: Initial PS3/PowerPC version.

View File

@ -197,6 +197,15 @@ powerpc/powerpc/suswintr.c standard
powerpc/powerpc/syncicache.c standard
powerpc/powerpc/sys_machdep.c standard
powerpc/powerpc/uio_machdep.c standard
powerpc/ps3/ehci_ps3.c optional ps3 ehci
powerpc/ps3/if_glc.c optional ps3 glc
powerpc/ps3/mmu_ps3.c optional ps3
powerpc/ps3/platform_ps3.c optional ps3
powerpc/ps3/ps3ata.c optional ps3 ps3ata
powerpc/ps3/ps3bus.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/psim/iobus.c optional psim
powerpc/psim/ata_iobus.c optional ata psim
powerpc/psim/openpic_iobus.c optional psim

View File

@ -3,6 +3,7 @@
AIM opt_global.h
E500 opt_global.h
CELL
POWERPC
POWERPC64
@ -17,6 +18,7 @@ GFB_NO_MODE_CHANGE opt_gfb.h
MPC85XX opt_platform.h
POWERMAC opt_platform.h
PS3 opt_platform.h
MAMBO
PSIM

View File

@ -27,6 +27,7 @@ makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
# Platform support
options POWERMAC #NewWorld Apple PowerMacs
options PS3 #Sony Playstation 3
options MAMBO #IBM Mambo Full System Simulator
options SCHED_ULE #ULE scheduler
@ -120,6 +121,9 @@ makeoptions SC_DFLT_FONT=cp437
device scc
device uart
# Ethernet hardware
device glc # Sony Playstation 3 Ethernet
# PCI Ethernet NICs that use the common MII bus controller code.
device miibus # MII bus support
device bge # Broadcom BCM570xx Gigabit Ethernet

View File

@ -20,6 +20,7 @@ options FPU_EMU
#options MPC85XX
options POWERMAC #NewWorld Apple PowerMacs
options PS3 #Sony Playstation 3
options PSIM #GDB PSIM ppc simulator
options MAMBO #IBM Mambo Full System Simulator
@ -33,6 +34,7 @@ device pci
device agp
device bm # Apple BMAC (Big Mac Ethernet)
device glc # Sony Playstation 3 Ethernet
device kiic # Apple Keywest I2C Controller
device ofwd # Open Firmware disks
device adb # Apple Desktop Bus

View File

@ -94,6 +94,7 @@ struct lpteg {
/* High quadword: */
#define LPTE_VSID_SHIFT 12
#define LPTE_AVPN_MASK 0xFFFFFFFFFFFFFF80ULL
#define LPTE_API 0x0000000000000F80ULL
#define LPTE_LOCKED 0x0000000000000040ULL
#define LPTE_WIRED 0x0000000000000008ULL

173
sys/powerpc/ps3/ehci_ps3.c Normal file
View File

@ -0,0 +1,173 @@
/*-
* Copyright (C) 2010 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/linker_set.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <sys/rman.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_util.h>
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/controller/ehci.h>
#include <dev/usb/controller/ehcireg.h>
#include "ps3bus.h"
struct ps3_ehci_softc {
ehci_softc_t base;
struct bus_space tag;
};
static int
ehci_ps3_probe(device_t dev)
{
if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS ||
ps3bus_get_devtype(dev) != PS3_DEVTYPE_USB)
return (ENXIO);
device_set_desc(dev, "Playstation 3 USB 2.0 controller");
return (BUS_PROBE_SPECIFIC);
}
static int
ehci_ps3_attach(device_t dev)
{
ehci_softc_t *sc = device_get_softc(dev);
int rid, err;
sc->sc_bus.parent = dev;
sc->sc_bus.devices = sc->sc_devices;
sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
if (usb_bus_mem_alloc_all(&sc->sc_bus,
USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc))
return (ENOMEM);
rid = 1;
sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&rid, RF_ACTIVE);
if (!sc->sc_io_res) {
device_printf(dev, "Could not map memory\n");
goto error;
}
sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
sc->sc_io_size = rman_get_size(sc->sc_io_res);
rid = 1;
sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE | RF_ACTIVE);
if (sc->sc_irq_res == NULL) {
device_printf(dev, "Could not allocate irq\n");
return (ENXIO);
}
sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
if (!sc->sc_bus.bdev) {
device_printf(dev, "Could not add USB device\n");
return (ENXIO);
}
device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
sprintf(sc->sc_vendor, "Sony");
err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
if (err) {
device_printf(dev, "Could not setup error irq, %d\n", err);
goto error;
}
sc->sc_flags |= EHCI_SCFLG_BIGEMMIO;
err = ehci_init(sc);
if (err) {
device_printf(dev, "USB init failed err=%d\n", err);
goto error;
}
err = device_probe_and_attach(sc->sc_bus.bdev);
if (err == 0)
return (0);
error:
return (ENXIO);
}
static device_method_t ehci_ps3_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ehci_ps3_probe),
DEVMETHOD(device_attach, ehci_ps3_attach),
/* Bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
{0, 0}
};
static driver_t ehci_ps3_driver = {
"ehci",
ehci_ps3_methods,
sizeof(ehci_softc_t),
};
static devclass_t ehci_ps3_devclass;
DRIVER_MODULE(ehci_ps3, ps3bus, ehci_ps3_driver, ehci_ps3_devclass, 0, 0);
MODULE_DEPEND(ehci_ps3, usb, 1, 1, 1);

938
sys/powerpc/ps3/if_glc.c Normal file
View File

@ -0,0 +1,938 @@
/*-
* Copyright (C) 2010 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/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_vlan_var.h>
#include <machine/pio.h>
#include <machine/bus.h>
#include <machine/platform.h>
#include <machine/pmap.h>
#include <machine/resource.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include "ps3bus.h"
#include "ps3-hvcall.h"
#include "if_glcreg.h"
static int glc_probe(device_t);
static int glc_attach(device_t);
static void glc_init(void *xsc);
static void glc_start(struct ifnet *ifp);
static int glc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
static void glc_set_multicast(struct glc_softc *sc);
static int glc_add_rxbuf(struct glc_softc *sc, int idx);
static int glc_add_rxbuf_dma(struct glc_softc *sc, int idx);
static int glc_encap(struct glc_softc *sc, struct mbuf **m_head,
bus_addr_t *pktdesc);
static int glc_intr_filter(void *xsc);
static void glc_intr(void *xsc);
static void glc_tick(void *xsc);
static void glc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr);
static int glc_media_change(struct ifnet *ifp);
static MALLOC_DEFINE(M_GLC, "gelic", "PS3 GELIC ethernet");
static device_method_t glc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, glc_probe),
DEVMETHOD(device_attach, glc_attach),
{ 0, 0 }
};
static driver_t glc_driver = {
"glc",
glc_methods,
sizeof(struct glc_softc)
};
static devclass_t glc_devclass;
DRIVER_MODULE(glc, ps3bus, glc_driver, glc_devclass, 0, 0);
static int
glc_probe(device_t dev)
{
if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS ||
ps3bus_get_devtype(dev) != PS3_DEVTYPE_GELIC)
return (ENXIO);
device_set_desc(dev, "Playstation 3 GELIC Network Controller");
return (BUS_PROBE_SPECIFIC);
}
static void
glc_getphys(void *xaddr, bus_dma_segment_t *segs, int nsegs, int error)
{
if (error != 0)
return;
*(bus_addr_t *)xaddr = segs[0].ds_addr;
}
static int
glc_attach(device_t dev)
{
struct glc_softc *sc;
struct glc_txsoft *txs;
uint64_t mac64, val, junk;
int i, err;
sc = device_get_softc(dev);
sc->sc_bus = ps3bus_get_bus(dev);
sc->sc_dev = ps3bus_get_device(dev);
sc->sc_self = dev;
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
MTX_DEF);
callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
sc->next_txdma_slot = 0;
sc->bsy_txdma_slots = 0;
sc->first_used_txdma_slot = -1;
/*
* Shut down existing tasks.
*/
lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
sc->sc_ifp = if_alloc(IFT_ETHER);
sc->sc_ifp->if_softc = sc;
/*
* Get MAC address and VLAN id
*/
lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_MAC_ADDRESS,
0, 0, 0, &mac64, &junk);
memcpy(sc->sc_enaddr, &((uint8_t *)&mac64)[2], sizeof(sc->sc_enaddr));
sc->sc_tx_vlan = sc->sc_rx_vlan = -1;
err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID,
GELIC_VLAN_TX_ETHERNET, 0, 0, &val, &junk);
if (err == 0)
sc->sc_tx_vlan = val;
err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID,
GELIC_VLAN_RX_ETHERNET, 0, 0, &val, &junk);
if (err == 0)
sc->sc_rx_vlan = val;
/*
* Set up interrupt handler
*/
sc->sc_irqid = 0;
sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid,
RF_ACTIVE);
if (sc->sc_irq == NULL) {
device_printf(dev, "Could not allocate IRQ!\n");
mtx_destroy(&sc->sc_mtx);
return (ENXIO);
}
bus_setup_intr(dev, sc->sc_irq,
INTR_TYPE_MISC | INTR_MPSAFE | INTR_ENTROPY,
glc_intr_filter, glc_intr, sc, &sc->sc_irqctx);
sc->sc_hwirq_status = (uint64_t *)contigmalloc(8, M_GLC, M_ZERO, 0,
BUS_SPACE_MAXADDR_32BIT, 8, PAGE_SIZE);
lv1_net_set_interrupt_status_indicator(sc->sc_bus, sc->sc_dev,
vtophys(sc->sc_hwirq_status), 0);
lv1_net_set_interrupt_mask(sc->sc_bus, sc->sc_dev,
GELIC_INT_RXDONE | GELIC_INT_RXFRAME | GELIC_INT_PHY |
GELIC_INT_TX_CHAIN_END, 0);
/*
* Set up DMA.
*/
err = bus_dma_tag_create(bus_get_dma_tag(dev), 32, 0,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
129*sizeof(struct glc_dmadesc), 1, 128*sizeof(struct glc_dmadesc),
0, NULL,NULL, &sc->sc_dmadesc_tag);
err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_txdmadesc,
BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
&sc->sc_txdmadesc_map);
err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map,
sc->sc_txdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys,
&sc->sc_txdmadesc_phys, 0);
err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_rxdmadesc,
BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
&sc->sc_rxdmadesc_map);
err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
sc->sc_rxdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys,
&sc->sc_rxdmadesc_phys, 0);
err = bus_dma_tag_create(bus_get_dma_tag(dev), 128, 0,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL,
&sc->sc_rxdma_tag);
err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
BUS_SPACE_MAXSIZE_32BIT, 16, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL,
&sc->sc_txdma_tag);
/* init transmit descriptors */
STAILQ_INIT(&sc->sc_txfreeq);
STAILQ_INIT(&sc->sc_txdirtyq);
/* create TX DMA maps */
err = ENOMEM;
for (i = 0; i < GLC_MAX_TX_PACKETS; i++) {
txs = &sc->sc_txsoft[i];
txs->txs_mbuf = NULL;
err = bus_dmamap_create(sc->sc_txdma_tag, 0, &txs->txs_dmamap);
if (err) {
device_printf(dev,
"unable to create TX DMA map %d, error = %d\n",
i, err);
}
STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
}
/* Create the receive buffer DMA maps. */
for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
err = bus_dmamap_create(sc->sc_rxdma_tag, 0,
&sc->sc_rxsoft[i].rxs_dmamap);
if (err) {
device_printf(dev,
"unable to create RX DMA map %d, error = %d\n",
i, err);
}
sc->sc_rxsoft[i].rxs_mbuf = NULL;
}
/*
* Attach to network stack
*/
if_initname(sc->sc_ifp, device_get_name(dev), device_get_unit(dev));
sc->sc_ifp->if_mtu = ETHERMTU;
sc->sc_ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
sc->sc_ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
sc->sc_ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_RXCSUM;
sc->sc_ifp->if_capenable = IFCAP_HWCSUM | IFCAP_RXCSUM;
sc->sc_ifp->if_start = glc_start;
sc->sc_ifp->if_ioctl = glc_ioctl;
sc->sc_ifp->if_init = glc_init;
ifmedia_init(&sc->sc_media, IFM_IMASK, glc_media_change,
glc_media_status);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T, 0, NULL);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX, 0, NULL);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
IFQ_SET_MAXLEN(&sc->sc_ifp->if_snd, GLC_MAX_TX_PACKETS);
sc->sc_ifp->if_snd.ifq_drv_maxlen = GLC_MAX_TX_PACKETS;
IFQ_SET_READY(&sc->sc_ifp->if_snd);
ether_ifattach(sc->sc_ifp, sc->sc_enaddr);
sc->sc_ifp->if_hwassist = 0;
return (0);
mtx_destroy(&sc->sc_mtx);
if_free(sc->sc_ifp);
return (ENXIO);
}
static void
glc_init_locked(struct glc_softc *sc)
{
int i, error;
struct glc_rxsoft *rxs;
struct glc_txsoft *txs;
mtx_assert(&sc->sc_mtx, MA_OWNED);
lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
glc_set_multicast(sc);
for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
rxs = &sc->sc_rxsoft[i];
rxs->rxs_desc_slot = i;
if (rxs->rxs_mbuf == NULL) {
glc_add_rxbuf(sc, i);
if (rxs->rxs_mbuf == NULL) {
rxs->rxs_desc_slot = -1;
break;
}
}
glc_add_rxbuf_dma(sc, i);
bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
BUS_DMASYNC_PREREAD);
}
/* Clear TX dirty queue */
while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
if (txs->txs_mbuf != NULL) {
m_freem(txs->txs_mbuf);
txs->txs_mbuf = NULL;
}
STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
}
sc->first_used_txdma_slot = -1;
sc->bsy_txdma_slots = 0;
error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
sc->sc_rxsoft[0].rxs_desc, 0);
if (error != 0)
device_printf(sc->sc_self,
"lv1_net_start_rx_dma error: %d\n", error);
sc->sc_ifp->if_drv_flags |= IFF_DRV_RUNNING;
sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
sc->sc_ifpflags = sc->sc_ifp->if_flags;
sc->sc_wdog_timer = 0;
callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
}
static void
glc_stop(void *xsc)
{
struct glc_softc *sc = xsc;
mtx_assert(&sc->sc_mtx, MA_OWNED);
lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
}
static void
glc_init(void *xsc)
{
struct glc_softc *sc = xsc;
mtx_lock(&sc->sc_mtx);
glc_init_locked(sc);
mtx_unlock(&sc->sc_mtx);
}
static void
glc_tick(void *xsc)
{
struct glc_softc *sc = xsc;
mtx_assert(&sc->sc_mtx, MA_OWNED);
if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) {
callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
return;
}
/* Problems */
device_printf(sc->sc_self, "device timeout\n");
glc_init_locked(sc);
}
static void
glc_start_locked(struct ifnet *ifp)
{
struct glc_softc *sc = ifp->if_softc;
bus_addr_t first, pktdesc;
int kickstart = 0;
int error;
struct mbuf *mb_head;
mtx_assert(&sc->sc_mtx, MA_OWNED);
first = 0;
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING)
return;
if (STAILQ_EMPTY(&sc->sc_txdirtyq))
kickstart = 1;
while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
IFQ_DRV_DEQUEUE(&ifp->if_snd, mb_head);
if (mb_head == NULL)
break;
/* Check if the ring buffer is full */
if (sc->bsy_txdma_slots > 125) {
/* Put the packet back and stop */
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
IFQ_DRV_PREPEND(&ifp->if_snd, mb_head);
break;
}
BPF_MTAP(ifp, mb_head);
if (sc->sc_tx_vlan >= 0)
mb_head = ether_vlanencap(mb_head, sc->sc_tx_vlan);
if (glc_encap(sc, &mb_head, &pktdesc)) {
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
break;
}
if (first == 0)
first = pktdesc;
}
if (kickstart && first != 0) {
error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev, first, 0);
if (error != 0)
device_printf(sc->sc_self,
"lv1_net_start_tx_dma error: %d\n", error);
sc->sc_wdog_timer = 5;
}
}
static void
glc_start(struct ifnet *ifp)
{
struct glc_softc *sc = ifp->if_softc;
mtx_lock(&sc->sc_mtx);
glc_start_locked(ifp);
mtx_unlock(&sc->sc_mtx);
}
static int
glc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct glc_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int err = 0;
switch (cmd) {
case SIOCSIFFLAGS:
mtx_lock(&sc->sc_mtx);
if ((ifp->if_flags & IFF_UP) != 0) {
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 &&
((ifp->if_flags ^ sc->sc_ifpflags) &
(IFF_ALLMULTI | IFF_PROMISC)) != 0)
glc_set_multicast(sc);
else
glc_init_locked(sc);
}
else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
glc_stop(sc);
sc->sc_ifpflags = ifp->if_flags;
mtx_unlock(&sc->sc_mtx);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
mtx_lock(&sc->sc_mtx);
glc_set_multicast(sc);
mtx_unlock(&sc->sc_mtx);
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
break;
default:
err = ether_ioctl(ifp, cmd, data);
break;
}
return (err);
}
static void
glc_set_multicast(struct glc_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
struct ifmultiaddr *inm;
uint64_t addr;
int naddrs;
/* Clear multicast filter */
lv1_net_remove_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
/* Add broadcast */
lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev,
0xffffffffffffL, 0);
if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
} else {
if_maddr_rlock(ifp);
naddrs = 1; /* Include broadcast */
TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) {
if (inm->ifma_addr->sa_family != AF_LINK)
continue;
addr = 0;
memcpy(&((uint8_t *)(&addr))[2],
LLADDR((struct sockaddr_dl *)inm->ifma_addr),
ETHER_ADDR_LEN);
lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev,
addr, 0);
/*
* Filter can only hold 32 addresses, so fall back to
* the IFF_ALLMULTI case if we have too many.
*/
if (++naddrs >= 32) {
lv1_net_add_multicast_address(sc->sc_bus,
sc->sc_dev, 0, 1);
break;
}
}
if_maddr_runlock(ifp);
}
}
static int
glc_add_rxbuf(struct glc_softc *sc, int idx)
{
struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx];
struct mbuf *m;
bus_dma_segment_t segs[1];
int error, nsegs;
m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
if (m == NULL)
return (ENOBUFS);
m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
if (rxs->rxs_mbuf != NULL) {
bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap,
BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_rxdma_tag, rxs->rxs_dmamap);
}
error = bus_dmamap_load_mbuf_sg(sc->sc_rxdma_tag, rxs->rxs_dmamap, m,
segs, &nsegs, BUS_DMA_NOWAIT);
if (error != 0) {
device_printf(sc->sc_self,
"cannot load RS DMA map %d, error = %d\n", idx, error);
m_freem(m);
return (error);
}
/* If nsegs is wrong then the stack is corrupt. */
KASSERT(nsegs == 1,
("%s: too many DMA segments (%d)", __func__, nsegs));
rxs->rxs_mbuf = m;
rxs->segment = segs[0];
bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap, BUS_DMASYNC_PREREAD);
return (0);
}
static int
glc_add_rxbuf_dma(struct glc_softc *sc, int idx)
{
struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx];
bzero(&sc->sc_rxdmadesc[idx], sizeof(sc->sc_rxdmadesc[idx]));
sc->sc_rxdmadesc[idx].paddr = rxs->segment.ds_addr;
sc->sc_rxdmadesc[idx].len = rxs->segment.ds_len;
sc->sc_rxdmadesc[idx].next = sc->sc_rxdmadesc_phys +
((idx + 1) % GLC_MAX_RX_PACKETS)*sizeof(sc->sc_rxdmadesc[idx]);
sc->sc_rxdmadesc[idx].cmd_stat = GELIC_DESCR_OWNED;
rxs->rxs_desc_slot = idx;
rxs->rxs_desc = sc->sc_rxdmadesc_phys + idx*sizeof(struct glc_dmadesc);
return (0);
}
static int
glc_encap(struct glc_softc *sc, struct mbuf **m_head, bus_addr_t *pktdesc)
{
bus_dma_segment_t segs[16];
struct glc_txsoft *txs;
struct mbuf *m;
bus_addr_t firstslotphys;
int i, idx, nsegs, nsegs_max;
int err = 0;
/* Max number of segments is the number of free DMA slots */
nsegs_max = 128 - sc->bsy_txdma_slots;
if (nsegs_max > 16 || sc->first_used_txdma_slot < 0)
nsegs_max = 16;
/* Get a work queue entry. */
if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) {
/* Ran out of descriptors. */
return (ENOBUFS);
}
nsegs = 0;
for (m = *m_head; m != NULL; m = m->m_next)
nsegs++;
if (nsegs > nsegs_max) {
m = m_collapse(*m_head, M_DONTWAIT, nsegs_max);
if (m == NULL) {
m_freem(*m_head);
*m_head = NULL;
return (ENOBUFS);
}
*m_head = m;
}
err = bus_dmamap_load_mbuf_sg(sc->sc_txdma_tag, txs->txs_dmamap,
*m_head, segs, &nsegs, BUS_DMA_NOWAIT);
if (err != 0) {
m_freem(*m_head);
*m_head = NULL;
return (err);
}
KASSERT(nsegs <= 128 - sc->bsy_txdma_slots,
("GLC: Mapped too many (%d) DMA segments with %d available",
nsegs, 128 - sc->bsy_txdma_slots));
if (nsegs == 0) {
m_freem(*m_head);
*m_head = NULL;
return (EIO);
}
txs->txs_ndescs = nsegs;
txs->txs_firstdesc = sc->next_txdma_slot;
idx = txs->txs_firstdesc;
firstslotphys = sc->sc_txdmadesc_phys +
txs->txs_firstdesc*sizeof(struct glc_dmadesc);
for (i = 0; i < nsegs; i++) {
bzero(&sc->sc_txdmadesc[idx], sizeof(sc->sc_txdmadesc[idx]));
sc->sc_txdmadesc[idx].paddr = segs[i].ds_addr;
sc->sc_txdmadesc[idx].len = segs[i].ds_len;
sc->sc_txdmadesc[idx].next = sc->sc_txdmadesc_phys +
((idx + 1) % GLC_MAX_TX_PACKETS)*sizeof(struct glc_dmadesc);
sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_NOIPSEC;
if (i+1 == nsegs) {
txs->txs_lastdesc = idx;
sc->sc_txdmadesc[idx].next = 0;
sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_LAST;
}
if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_TCP;
if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_UDP;
sc->sc_txdmadesc[idx].cmd_stat |= GELIC_DESCR_OWNED;
idx = (idx + 1) % GLC_MAX_TX_PACKETS;
}
sc->next_txdma_slot = idx;
sc->bsy_txdma_slots += nsegs;
if (txs->txs_firstdesc != 0)
idx = txs->txs_firstdesc - 1;
else
idx = GLC_MAX_TX_PACKETS - 1;
if (sc->first_used_txdma_slot < 0)
sc->first_used_txdma_slot = txs->txs_firstdesc;
bus_dmamap_sync(sc->sc_txdma_tag, txs->txs_dmamap,
BUS_DMASYNC_PREWRITE);
sc->sc_txdmadesc[idx].next = firstslotphys;
STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q);
STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q);
txs->txs_mbuf = *m_head;
*pktdesc = firstslotphys;
return (0);
}
static void
glc_rxintr(struct glc_softc *sc)
{
int i, restart_rxdma, error;
struct mbuf *m;
struct ifnet *ifp = sc->sc_ifp;
bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
BUS_DMASYNC_PREWRITE);
restart_rxdma = 0;
while ((sc->sc_rxdmadesc[sc->sc_next_rxdma_slot].cmd_stat &
GELIC_DESCR_OWNED) == 0) {
i = sc->sc_next_rxdma_slot;
if (sc->sc_rxdmadesc[i].rxerror & GELIC_RXERRORS) {
ifp->if_ierrors++;
goto requeue;
}
m = sc->sc_rxsoft[i].rxs_mbuf;
if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_IPCSUM) {
m->m_pkthdr.csum_flags |=
CSUM_IP_CHECKED | CSUM_IP_VALID;
}
if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_TCPUDPCSUM) {
m->m_pkthdr.csum_flags |=
CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
m->m_pkthdr.csum_data = 0xffff;
}
if (glc_add_rxbuf(sc, i)) {
ifp->if_ierrors++;
goto requeue;
}
ifp->if_ipackets++;
m->m_pkthdr.rcvif = ifp;
m->m_len = sc->sc_rxdmadesc[i].valid_size;
m->m_pkthdr.len = m->m_len;
sc->sc_next_rxdma_slot++;
if (sc->sc_next_rxdma_slot >= GLC_MAX_RX_PACKETS)
sc->sc_next_rxdma_slot = 0;
if (sc->sc_rx_vlan >= 0)
m_adj(m, 2);
mtx_unlock(&sc->sc_mtx);
(*ifp->if_input)(ifp, m);
mtx_lock(&sc->sc_mtx);
requeue:
if (sc->sc_rxdmadesc[i].cmd_stat & GELIC_CMDSTAT_CHAIN_END)
restart_rxdma = 1;
glc_add_rxbuf_dma(sc, i);
if (restart_rxdma) {
error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
sc->sc_rxsoft[i].rxs_desc, 0);
if (error != 0)
device_printf(sc->sc_self,
"lv1_net_start_rx_dma error: %d\n", error);
}
}
}
static void
glc_txintr(struct glc_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
struct glc_txsoft *txs;
int progress = 0, kickstart = 0, error;
while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat
& GELIC_DESCR_OWNED)
break;
STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
sc->bsy_txdma_slots -= txs->txs_ndescs;
if (txs->txs_mbuf != NULL) {
m_freem(txs->txs_mbuf);
txs->txs_mbuf = NULL;
}
if ((sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat & 0xf0000000)
!= 0) {
lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
kickstart = 1;
ifp->if_oerrors++;
}
if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat &
GELIC_CMDSTAT_CHAIN_END)
kickstart = 1;
STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
ifp->if_opackets++;
progress = 1;
}
if (txs != NULL)
sc->first_used_txdma_slot = txs->txs_firstdesc;
else
sc->first_used_txdma_slot = -1;
if (kickstart && txs != NULL) {
error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev,
sc->sc_txdmadesc_phys +
txs->txs_firstdesc*sizeof(struct glc_dmadesc), 0);
if (error != 0)
device_printf(sc->sc_self,
"lv1_net_start_tx_dma error: %d\n", error);
}
if (progress) {
/*
* We freed some descriptors, so reset IFF_DRV_OACTIVE
* and restart.
*/
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5;
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) &&
!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
glc_start_locked(ifp);
}
}
static int
glc_intr_filter(void *xsc)
{
struct glc_softc *sc = xsc;
powerpc_sync();
atomic_set_64(&sc->sc_interrupt_status, *sc->sc_hwirq_status);
return (FILTER_SCHEDULE_THREAD);
}
static void
glc_intr(void *xsc)
{
struct glc_softc *sc = xsc;
uint64_t status, linkstat, junk;
mtx_lock(&sc->sc_mtx);
status = atomic_readandclear_64(&sc->sc_interrupt_status);
if (status == 0) {
mtx_unlock(&sc->sc_mtx);
return;
}
if (status & (GELIC_INT_RXDONE | GELIC_INT_RXFRAME))
glc_rxintr(sc);
if (status & (GELIC_INT_TXDONE | GELIC_INT_TX_CHAIN_END))
glc_txintr(sc);
if (status & GELIC_INT_PHY) {
lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
GELIC_VLAN_TX_ETHERNET, 0, 0, &linkstat, &junk);
linkstat = (linkstat & GELIC_LINK_UP) ?
LINK_STATE_UP : LINK_STATE_DOWN;
if (linkstat != sc->sc_ifp->if_link_state)
if_link_state_change(sc->sc_ifp, linkstat);
}
mtx_unlock(&sc->sc_mtx);
}
static void
glc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct glc_softc *sc = ifp->if_softc;
uint64_t status, junk;
ifmr->ifm_status = IFM_AVALID;
ifmr->ifm_active = IFM_ETHER;
lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
GELIC_VLAN_TX_ETHERNET, 0, 0, &status, &junk);
if (status & GELIC_LINK_UP)
ifmr->ifm_status |= IFM_ACTIVE;
if (status & GELIC_SPEED_10)
ifmr->ifm_active |= IFM_10_T;
else if (status & GELIC_SPEED_100)
ifmr->ifm_active |= IFM_100_TX;
else if (status & GELIC_SPEED_1000)
ifmr->ifm_active |= IFM_1000_T;
if (status & GELIC_FULL_DUPLEX)
ifmr->ifm_active |= IFM_FDX;
else
ifmr->ifm_active |= IFM_HDX;
}
static int
glc_media_change(struct ifnet *ifp)
{
struct glc_softc *sc = ifp->if_softc;
uint64_t mode, junk;
int result;
if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_ETHER)
return (EINVAL);
switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) {
case IFM_AUTO:
mode = GELIC_AUTO_NEG;
break;
case IFM_10_T:
mode = GELIC_SPEED_10;
break;
case IFM_100_TX:
mode = GELIC_SPEED_100;
break;
case IFM_1000_T:
mode = GELIC_SPEED_1000 | GELIC_FULL_DUPLEX;
break;
default:
return (EINVAL);
}
if (IFM_OPTIONS(sc->sc_media.ifm_media) & IFM_FDX)
mode |= GELIC_FULL_DUPLEX;
result = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_SET_LINK_MODE,
GELIC_VLAN_TX_ETHERNET, mode, 0, &junk, &junk);
return (result ? EIO : 0);
}

160
sys/powerpc/ps3/if_glcreg.h Normal file
View File

@ -0,0 +1,160 @@
/*-
* Copyright (C) 2010 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$
*/
#ifndef _POWERPC_PS3_IF_GLCREG_H
#define _POWERPC_PS3_IF_GLCREG_H
#define GLC_MAX_TX_PACKETS 128
#define GLC_MAX_RX_PACKETS 128
struct glc_dmadesc;
/*
* software state for transmit job mbufs (may be elements of mbuf chains)
*/
struct glc_txsoft {
struct mbuf *txs_mbuf; /* head of our mbuf chain */
bus_dmamap_t txs_dmamap; /* our DMA map */
int txs_firstdesc; /* first descriptor in packet */
int txs_lastdesc; /* last descriptor in packet */
int txs_ndescs; /* number of descriptors */
STAILQ_ENTRY(glc_txsoft) txs_q;
};
STAILQ_HEAD(glc_txsq, glc_txsoft);
/*
* software state for receive jobs
*/
struct glc_rxsoft {
struct mbuf *rxs_mbuf; /* head of our mbuf chain */
bus_dmamap_t rxs_dmamap; /* our DMA map */
int rxs_desc_slot; /* DMA descriptor for this packet */
bus_addr_t rxs_desc;
bus_dma_segment_t segment;
};
struct glc_softc {
struct ifnet *sc_ifp;
device_t sc_self;
struct mtx sc_mtx;
u_char sc_enaddr[ETHER_ADDR_LEN];
int sc_tx_vlan, sc_rx_vlan;
int sc_ifpflags;
uint64_t sc_dma_base[5];
bus_dma_tag_t sc_dmadesc_tag;
int sc_irqid;
struct resource *sc_irq;
void *sc_irqctx;
uint64_t *sc_hwirq_status;
volatile uint64_t sc_interrupt_status;
struct ifmedia sc_media;
/* Transmission */
bus_dma_tag_t sc_txdma_tag;
struct glc_txsoft sc_txsoft[GLC_MAX_TX_PACKETS];
struct glc_dmadesc *sc_txdmadesc;
int next_txdma_slot, first_used_txdma_slot, bsy_txdma_slots;
bus_dmamap_t sc_txdmadesc_map;
bus_addr_t sc_txdmadesc_phys;
struct glc_txsq sc_txfreeq;
struct glc_txsq sc_txdirtyq;
/* Reception */
bus_dma_tag_t sc_rxdma_tag;
struct glc_rxsoft sc_rxsoft[GLC_MAX_RX_PACKETS];
struct glc_dmadesc *sc_rxdmadesc;
int sc_next_rxdma_slot;
bus_dmamap_t sc_rxdmadesc_map;
bus_addr_t sc_rxdmadesc_phys;
int sc_bus, sc_dev;
int sc_wdog_timer;
struct callout sc_tick_ch;
};
#define GELIC_GET_MAC_ADDRESS 0x0001
#define GELIC_GET_LINK_STATUS 0x0002
#define GELIC_SET_LINK_MODE 0x0003
#define GELIC_LINK_UP 0x0001
#define GELIC_FULL_DUPLEX 0x0002
#define GELIC_AUTO_NEG 0x0004
#define GELIC_SPEED_10 0x0010
#define GELIC_SPEED_100 0x0020
#define GELIC_SPEED_1000 0x0040
#define GELIC_GET_VLAN_ID 0x0004
#define GELIC_VLAN_TX_ETHERNET 0x0002
#define GELIC_VLAN_RX_ETHERNET 0x0012
#define GELIC_VLAN_TX_WIRELESS 0x0003
#define GELIC_VLAN_RX_WIRELESS 0x0013
/* Command status code */
#define GELIC_DESCR_OWNED 0xa0000000
#define GELIC_CMDSTAT_DMA_DONE 0x00000000
#define GELIC_CMDSTAT_CHAIN_END 0x00000002
#define GELIC_CMDSTAT_CSUM_TCP 0x00020000
#define GELIC_CMDSTAT_CSUM_UDP 0x00030000
#define GELIC_CMDSTAT_NOIPSEC 0x00080000
#define GELIC_CMDSTAT_LAST 0x00040000
#define GELIC_RXERRORS 0x7def8000
/* RX Data Status codes */
#define GELIC_RX_IPCSUM 0x20000000
#define GELIC_RX_TCPUDPCSUM 0x10000000
/* Interrupt options */
#define GELIC_INT_RXDONE 0x0000000000004000UL
#define GELIC_INT_RXFRAME 0x1000000000000000UL
#define GELIC_INT_TXDONE 0x0080000000000000UL
#define GELIC_INT_TX_CHAIN_END 0x0100000000000000UL
#define GELIC_INT_PHY 0x0000000020000000UL
/* Hardware DMA descriptor. Must be 32-byte aligned */
struct glc_dmadesc {
uint32_t paddr; /* Must be 128 byte aligned for receive */
uint32_t len;
uint32_t next;
uint32_t cmd_stat;
uint32_t result_size;
uint32_t valid_size;
uint32_t data_stat;
uint32_t rxerror;
};
#endif /* _POWERPC_PS3_IF_GLCREG_H */

311
sys/powerpc/ps3/mmu_ps3.c Normal file
View File

@ -0,0 +1,311 @@
/*-
* Copyright (C) 2010 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/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 <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/vm_pager.h>
#include <vm/uma.h>
#include <powerpc/aim/mmu_oea64.h>
#include "mmu_if.h"
#include "moea64_if.h"
#include "ps3-hvcall.h"
#define VSID_HASH_MASK 0x0000007fffffffffUL
#define PTESYNC() __asm __volatile("ptesync")
extern int ps3fb_remap(void);
static uint64_t mps3_vas_id;
/*
* Kernel MMU interface
*/
static void mps3_bootstrap(mmu_t mmup, vm_offset_t kernelstart,
vm_offset_t kernelend);
static void mps3_cpu_bootstrap(mmu_t mmup, int ap);
static void mps3_pte_synch(mmu_t, uintptr_t pt, struct lpte *pvo_pt);
static void mps3_pte_clear(mmu_t, uintptr_t pt, struct lpte *pvo_pt,
uint64_t vpn, uint64_t ptebit);
static void mps3_pte_unset(mmu_t, uintptr_t pt, struct lpte *pvo_pt,
uint64_t vpn);
static void mps3_pte_change(mmu_t, uintptr_t pt, struct lpte *pvo_pt,
uint64_t vpn);
static int mps3_pte_insert(mmu_t, u_int ptegidx, struct lpte *pvo_pt);
static uintptr_t mps3_pvo_to_pte(mmu_t, const struct pvo_entry *pvo);
static mmu_method_t mps3_methods[] = {
MMUMETHOD(mmu_bootstrap, mps3_bootstrap),
MMUMETHOD(mmu_cpu_bootstrap, mps3_cpu_bootstrap),
MMUMETHOD(moea64_pte_synch, mps3_pte_synch),
MMUMETHOD(moea64_pte_clear, mps3_pte_clear),
MMUMETHOD(moea64_pte_unset, mps3_pte_unset),
MMUMETHOD(moea64_pte_change, mps3_pte_change),
MMUMETHOD(moea64_pte_insert, mps3_pte_insert),
MMUMETHOD(moea64_pvo_to_pte, mps3_pvo_to_pte),
{ 0, 0 }
};
MMU_DEF_INHERIT(ps3_mmu, "mmu_ps3", mps3_methods, 0, oea64_mmu);
static void
mps3_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend)
{
uint64_t final_pteg_count;
moea64_early_bootstrap(mmup, kernelstart, kernelend);
lv1_construct_virtual_address_space(
20 /* log_2(moea64_pteg_count) */, 2 /* n page sizes */,
(24UL << 56) | (16UL << 48) /* page sizes 16 MB + 64 KB */,
&mps3_vas_id, &final_pteg_count
);
moea64_pteg_count = final_pteg_count / sizeof(struct lpteg);
moea64_mid_bootstrap(mmup, kernelstart, kernelend);
moea64_late_bootstrap(mmup, kernelstart, kernelend);
}
static void
mps3_cpu_bootstrap(mmu_t mmup, int ap)
{
struct slb *slb = PCPU_GET(slb);
register_t seg0;
int i;
mtmsr(mfmsr() & ~PSL_DR & ~PSL_IR);
/*
* Destroy the loader's address space if we are coming up for
* the first time, and redo the FB mapping so we can continue
* having a console.
*/
if (!ap)
lv1_destruct_virtual_address_space(0);
lv1_select_virtual_address_space(mps3_vas_id);
if (!ap)
ps3fb_remap();
/*
* 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
mps3_pte_synch(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt)
{
uint64_t halfbucket[4], rcbits;
PTESYNC();
lv1_read_htab_entries(mps3_vas_id, slot & ~0x3UL, &halfbucket[0],
&halfbucket[1], &halfbucket[2], &halfbucket[3], &rcbits);
/*
* rcbits contains the low 12 bits of each PTEs 2nd part,
* spaced at 16-bit intervals
*/
KASSERT((halfbucket[slot & 0x3] & LPTE_AVPN_MASK) ==
(pvo_pt->pte_hi & LPTE_AVPN_MASK),
("PTE upper word %#lx != %#lx\n",
halfbucket[slot & 0x3], pvo_pt->pte_hi));
pvo_pt->pte_lo |= (rcbits >> ((3 - (slot & 0x3))*16)) &
(LPTE_CHG | LPTE_REF);
}
static void
mps3_pte_clear(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn,
u_int64_t ptebit)
{
lv1_write_htab_entry(mps3_vas_id, slot, pvo_pt->pte_hi,
pvo_pt->pte_lo & ~ptebit);
}
static void
mps3_pte_unset(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn)
{
mps3_pte_synch(mmu, slot, pvo_pt);
pvo_pt->pte_hi &= ~LPTE_VALID;
lv1_write_htab_entry(mps3_vas_id, slot, 0, 0);
moea64_pte_valid--;
}
static void
mps3_pte_change(mmu_t mmu, uintptr_t slot, struct lpte *pvo_pt, uint64_t vpn)
{
mps3_pte_synch(mmu, slot, pvo_pt);
lv1_write_htab_entry(mps3_vas_id, slot, pvo_pt->pte_hi,
pvo_pt->pte_lo);
}
static int
mps3_pte_insert(mmu_t mmu, u_int ptegidx, struct lpte *pvo_pt)
{
int result;
struct lpte evicted;
struct pvo_entry *pvo;
uint64_t index;
pvo_pt->pte_hi |= LPTE_VALID;
pvo_pt->pte_hi &= ~LPTE_HID;
evicted.pte_hi = 0;
PTESYNC();
result = lv1_insert_htab_entry(mps3_vas_id, ptegidx << 3,
pvo_pt->pte_hi, pvo_pt->pte_lo, LPTE_LOCKED | LPTE_WIRED, 0,
&index, &evicted.pte_hi, &evicted.pte_lo);
if (result != 0) {
/* No freeable slots in either PTEG? We're hosed. */
panic("mps3_pte_insert: overflow (%d)", result);
return (-1);
}
/*
* See where we ended up.
*/
if (index >> 3 != ptegidx)
pvo_pt->pte_hi |= LPTE_HID;
moea64_pte_valid++;
if (!evicted.pte_hi)
return (index & 0x7);
/*
* 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.
*/
ptegidx = index >> 3; /* Where the sacrifice PTE was found */
if (evicted.pte_hi & LPTE_HID)
ptegidx ^= moea64_pteg_mask; /* PTEs indexed by primary */
KASSERT((evicted.pte_hi & (LPTE_WIRED | LPTE_LOCKED)) == 0,
("Evicted a wired PTE"));
result = 0;
LIST_FOREACH(pvo, &moea64_pvo_table[ptegidx], pvo_olink) {
if (!PVO_PTEGIDX_ISSET(pvo))
continue;
if (pvo->pvo_pte.lpte.pte_hi == (evicted.pte_hi | LPTE_VALID)) {
KASSERT(pvo->pvo_pte.lpte.pte_hi & LPTE_VALID,
("Invalid PVO for valid PTE!"));
pvo->pvo_pte.lpte.pte_hi &= ~LPTE_VALID;
pvo->pvo_pte.lpte.pte_lo |=
evicted.pte_lo & (LPTE_REF | LPTE_CHG);
PVO_PTEGIDX_CLR(pvo);
moea64_pte_valid--;
moea64_pte_overflow++;
result = 1;
break;
}
}
KASSERT(result == 1, ("PVO for sacrifice PTE not found"));
return (index & 0x7);
}
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);
}
uintptr_t
mps3_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));
}

View File

@ -0,0 +1,262 @@
/*-
* Copyright (c) 2010 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/reboot.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/platform.h>
#include <machine/platformvar.h>
#include <machine/pmap.h>
#include <machine/smp.h>
#include <machine/spr.h>
#include <machine/vmparam.h>
#include "platform_if.h"
#include "ps3-hvcall.h"
#ifdef SMP
extern void *ap_pcpu;
#endif
static int ps3_probe(platform_t);
static int ps3_attach(platform_t);
static void ps3_mem_regions(platform_t, struct mem_region **phys, int *physsz,
struct mem_region **avail, int *availsz);
static vm_offset_t ps3_real_maxaddr(platform_t);
static u_long ps3_timebase_freq(platform_t, struct cpuref *cpuref);
#ifdef SMP
static int ps3_smp_first_cpu(platform_t, struct cpuref *cpuref);
static int ps3_smp_next_cpu(platform_t, struct cpuref *cpuref);
static int ps3_smp_get_bsp(platform_t, struct cpuref *cpuref);
static int ps3_smp_start_cpu(platform_t, struct pcpu *cpu);
static struct cpu_group *ps3_smp_topo(platform_t);
#endif
static void ps3_reset(platform_t);
static void ps3_cpu_idle(void);
static platform_method_t ps3_methods[] = {
PLATFORMMETHOD(platform_probe, ps3_probe),
PLATFORMMETHOD(platform_attach, ps3_attach),
PLATFORMMETHOD(platform_mem_regions, ps3_mem_regions),
PLATFORMMETHOD(platform_real_maxaddr, ps3_real_maxaddr),
PLATFORMMETHOD(platform_timebase_freq, ps3_timebase_freq),
#ifdef SMP
PLATFORMMETHOD(platform_smp_first_cpu, ps3_smp_first_cpu),
PLATFORMMETHOD(platform_smp_next_cpu, ps3_smp_next_cpu),
PLATFORMMETHOD(platform_smp_get_bsp, ps3_smp_get_bsp),
PLATFORMMETHOD(platform_smp_start_cpu, ps3_smp_start_cpu),
PLATFORMMETHOD(platform_smp_topo, ps3_smp_topo),
#endif
PLATFORMMETHOD(platform_reset, ps3_reset),
{ 0, 0 }
};
static platform_def_t ps3_platform = {
"ps3",
ps3_methods,
0
};
PLATFORM_DEF(ps3_platform);
static int
ps3_probe(platform_t plat)
{
return (BUS_PROBE_NOWILDCARD);
}
#define MEM_REGIONS 2
static struct mem_region avail_regions[MEM_REGIONS];
static int
ps3_attach(platform_t plat)
{
uint64_t lpar_id, junk, ppe_id;
/* Get real mode memory region */
avail_regions[0].mr_start = 0;
lv1_get_logical_partition_id(&lpar_id);
lv1_get_logical_ppe_id(&ppe_id);
lv1_get_repository_node_value(lpar_id,
lv1_repository_string("bi") >> 32, lv1_repository_string("pu"),
ppe_id, lv1_repository_string("rm_size"),
&avail_regions[0].mr_size, &junk);
/* Now get extended memory region */
lv1_get_repository_node_value(lpar_id,
lv1_repository_string("bi") >> 32,
lv1_repository_string("rgntotal"), 0, 0,
&avail_regions[1].mr_size, &junk);
/* Convert to maximum amount we can allocate in 16 MB pages */
avail_regions[1].mr_size -= avail_regions[0].mr_size;
avail_regions[1].mr_size -= avail_regions[1].mr_size % (16*1024*1024);
lv1_allocate_memory(avail_regions[1].mr_size, 24 /* 16 MB pages */,
0, 0x04 /* any address */, &avail_regions[1].mr_start, &junk);
pmap_mmu_install("mmu_ps3", BUS_PROBE_SPECIFIC);
cpu_idle_hook = ps3_cpu_idle;
/* Set a breakpoint to make NULL an invalid address */
lv1_set_dabr(0x7 /* read and write, MMU on */, 2 /* kernel accesses */);
return (0);
}
void
ps3_mem_regions(platform_t plat, struct mem_region **phys, int *physsz,
struct mem_region **avail, int *availsz)
{
*phys = *avail = avail_regions;
*physsz = *availsz = MEM_REGIONS;
}
static u_long
ps3_timebase_freq(platform_t plat, struct cpuref *cpuref)
{
uint64_t ticks, node_id, junk;
lv1_get_repository_node_value(PS3_LPAR_ID_PME,
lv1_repository_string("be") >> 32, 0, 0, 0, &node_id, &junk);
lv1_get_repository_node_value(PS3_LPAR_ID_PME,
lv1_repository_string("be") >> 32, node_id,
lv1_repository_string("clock"), 0, &ticks, &junk);
return (ticks);
}
#ifdef SMP
static int
ps3_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
{
cpuref->cr_cpuid = 0;
cpuref->cr_hwref = cpuref->cr_cpuid;
return (0);
}
static int
ps3_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
{
if (cpuref->cr_cpuid >= 1)
return (ENOENT);
cpuref->cr_cpuid++;
cpuref->cr_hwref = cpuref->cr_cpuid;
return (0);
}
static int
ps3_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
{
cpuref->cr_cpuid = 0;
cpuref->cr_hwref = cpuref->cr_cpuid;
return (0);
}
static int
ps3_smp_start_cpu(platform_t plat, struct pcpu *pc)
{
/* loader(8) is spinning on 0x40 == 0 right now */
uint32_t *secondary_spin_sem = (uint32_t *)(0x40);
int timeout;
if (pc->pc_hwref != 1)
return (ENXIO);
ap_pcpu = pc;
*secondary_spin_sem = 1;
powerpc_sync();
DELAY(1);
timeout = 10000;
while (!pc->pc_awake && timeout--)
DELAY(100);
return ((pc->pc_awake) ? 0 : EBUSY);
}
static struct cpu_group *
ps3_smp_topo(platform_t plat)
{
return (smp_topo_1level(CG_SHARE_L1, 2, CG_FLAG_SMT));
}
#endif
static void
ps3_reset(platform_t plat)
{
lv1_panic(1);
}
static vm_offset_t
ps3_real_maxaddr(platform_t plat)
{
return (avail_regions[0].mr_start + avail_regions[0].mr_size);
}
static void
ps3_cpu_idle(void)
{
static volatile int pausing = 0;
/*
* XXX: It appears that the PS3 can livelock if both threads
* call lv1_pause(0) simultaneously.
*/
if (!atomic_cmpset_int(&pausing, 0, 1))
return;
lv1_pause(0);
pausing = 0;
}

View File

@ -0,0 +1,52 @@
# This script generates the PS3 hypervisor call stubs from an HV
# interface definition file. The PS3 HV calling convention is very
# similar to the PAPR one, except that the function token is passed in
# r11 instead of r3.
#
# Invoke like so: awk -f ps3-hv-asm.awk < ps3-hvcall.master > ps3-hvcall.S
#
# $FreeBSD$
BEGIN {
printf("#include <machine/asm.h>\n\n");
printf("#define hc .long 0x44000022\n\n");
}
/HVCALL.*/ {
code = $2;
ins = split($4, a, ",")
outs = split($5, a, ",")
printf("ASENTRY(%s)\n",$3);
printf("\tmflr %%r0\n");
printf("\tstd %%r0,16(%%r1)\n");
printf("\tstdu %%r1,-%d(%%r1)\n", 48+8*outs);
if ($4 == "UNUSED")
ins = 0
# Save output reg addresses to the stack
for (i = 0; i < outs; i++) {
if (ins+i >= 8) {
printf("\tld %%r11,%d(%%r1)\n", 48+8*outs + 48 + 8*(i+ins));
printf("\tstd %%r11,%d(%%r1)\n", 48+8*i);
} else {
printf("\tstd %%r%d,%d(%%r1)\n", 3+ins+i, 48+8*i);
}
}
printf("\tli %%r11,%d\n", code);
printf("\thc\n");
printf("\textsw %%r3,%%r3\n");
for (i = 0; i < outs; i++) {
printf("\tld %%r11,%d(%%r1)\n", 48+8*i);
printf("\tstd %%r%d,0(%%r11)\n", 4+i);
}
printf("\tld %%r1,0(%%r1)\n");
printf("\tld %%r0,16(%%r1)\n");
printf("\tmtlr %%r0\n");
printf("\tblr\n\n");
}

View File

@ -0,0 +1,42 @@
# This script generates the PS3 hypervisor call header from a hypervisor
# interface definition file. All lines that do not begin with HVCALL
# or a bare # for comments are copied to the output header so that
# enums, constant, C comments and the like can be passed through into the
# header.
#
# Invoke like so: awk -f ps3-hv-header.awk < ps3-hvcall.master > ps3-hv.h
#
# $FreeBSD$
!/HVCALL.*/ && (!/#.*/ || /#define.*/ || /#include.*/) {
print($0);
}
/HVCALL.*/ {
split($5, outs, ",")
if ($4 == "UNUSED")
split("", ins, ",")
else
split($4, ins, ",")
printf("int %s(",$3);
for (i = 1; i <= length(ins); i++) {
printf("uint64_t %s", ins[i]);
if (i < length(ins)) printf(", ");
}
if (length(outs) > 0 && length(ins) > 0)
printf(", ");
for (i = 1; i <= length(outs); i++) {
printf("uint64_t *%s", outs[i]);
if (i < length(outs)) printf(", ");
}
if (length(outs) == 0 && length(ins) == 0)
printf("void");
printf(");\n");
}

1254
sys/powerpc/ps3/ps3-hvcall.S Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,110 @@
/*
* Playstation 3 LV1 hypercall interface
*
* $FreeBSD$
*/
#include <sys/types.h>
enum lpar_id {
PS3_LPAR_ID_CURRENT = 0x00,
PS3_LPAR_ID_PME = 0x01,
};
static inline uint64_t
lv1_repository_string(const char *str)
{
uint64_t ret = 0;
strncpy((char *)&ret, str, sizeof(ret));
return (ret);
}
int lv1_allocate_memory(uint64_t size, uint64_t log_page_size, uint64_t zero, uint64_t flags, uint64_t *base_addr, uint64_t *muid);
int lv1_write_htab_entry(uint64_t vas_id, uint64_t slot, uint64_t pte_hi, uint64_t pte_lo);
int lv1_construct_virtual_address_space(uint64_t log_pteg_count, uint64_t n_sizes, uint64_t page_sizes, uint64_t *vas_id, uint64_t *hv_pteg_count);
int lv1_get_virtual_address_space_id_of_ppe(uint64_t ppe_id, uint64_t *vas_id);
int lv1_query_logical_partition_address_region_info(uint64_t lpar_id, uint64_t *base_addr, uint64_t *size, uint64_t *access_right, uint64_t *max_page_size, uint64_t *flags);
int lv1_select_virtual_address_space(uint64_t vas_id);
int lv1_pause(uint64_t mode);
int lv1_destruct_virtual_address_space(uint64_t vas_id);
int lv1_configure_irq_state_bitmap(uint64_t ppe_id, uint64_t cpu_id, uint64_t bitmap_addr);
int lv1_connect_irq_plug_ext(uint64_t ppe_id, uint64_t cpu_id, uint64_t virq, uint64_t outlet, uint64_t zero);
int lv1_release_memory(uint64_t base_addr);
int lv1_put_iopte(uint64_t ioas_id, uint64_t ioif_addr, uint64_t lpar_addr, uint64_t io_id, uint64_t flags);
int lv1_disconnect_irq_plug_ext(uint64_t ppe_id, uint64_t cpu_id, uint64_t virq);
int lv1_construct_event_receive_port(uint64_t *outlet);
int lv1_destruct_event_receive_port(uint64_t outlet);
int lv1_send_event_locally(uint64_t outlet);
int lv1_end_of_interrupt(uint64_t irq);
int lv1_connect_irq_plug(uint64_t virq, uint64_t irq);
int lv1_disconnect_irq_plus(uint64_t virq);
int lv1_end_of_interrupt_ext(uint64_t ppe_id, uint64_t cpu_id, uint64_t virq);
int lv1_did_update_interrupt_mask(uint64_t ppe_id, uint64_t cpu_id);
int lv1_shutdown_logical_partition(uint64_t cmd);
int lv1_destruct_logical_spe(uint64_t spe_id);
int lv1_construct_logical_spe(uint64_t pshift1, uint64_t pshift2, uint64_t pshift3, uint64_t pshift4, uint64_t pshift5, uint64_t vas_id, uint64_t spe_type, uint64_t *priv2_addr, uint64_t *problem_phys, uint64_t *local_store_phys, uint64_t *unused, uint64_t *shadow_addr, uint64_t *spe_id);
int lv1_set_spe_interrupt_mask(uint64_t spe_id, uint64_t class, uint64_t mask);
int lv1_disable_logical_spe(uint64_t spe_id, uint64_t zero);
int lv1_clear_spe_interrupt_status(uint64_t spe_id, uint64_t class, uint64_t stat, uint64_t zero);
int lv1_get_spe_interrupt_status(uint64_t spe_id, uint64_t class, uint64_t *stat);
int lv1_get_logical_ppe_id(uint64_t *ppe_id);
int lv1_get_logical_partition_id(uint64_t *lpar_id);
int lv1_get_spe_irq_outlet(uint64_t spe_id, uint64_t class, uint64_t *outlet);
int lv1_set_spe_privilege_state_area_1_register(uint64_t spe_id, uint64_t offset, uint64_t value);
int lv1_get_repository_node_value(uint64_t lpar_id, uint64_t n1, uint64_t n2, uint64_t n3, uint64_t n4, uint64_t *v1, uint64_t *v2);
int lv1_read_htab_entries(uint64_t vas_id, uint64_t slot, uint64_t *hi1, uint64_t *hi2, uint64_t *hi3, uint64_t *hi4, uint64_t *rcbits);
int lv1_set_dabr(uint64_t dabr, uint64_t flags);
int lv1_allocate_io_segment(uint64_t ioas_id, uint64_t seg_size, uint64_t io_pagesize, uint64_t *ioif_addr);
int lv1_release_io_segment(uint64_t ioas_id, uint64_t ioif_addr);
int lv1_construct_io_irq_outlet(uint64_t interrupt_id, uint64_t *outlet);
int lv1_destruct_io_irq_outlet(uint64_t outlet);
int lv1_map_htab(uint64_t lpar_id, uint64_t *htab_addr);
int lv1_unmap_htab(uint64_t htab_addr);
int lv1_get_version_info(uint64_t *firm_vers);
int lv1_insert_htab_entry(uint64_t vas_id, uint64_t pteg, uint64_t pte_hi, uint64_t pte_lo, uint64_t lockflags, uint64_t flags, uint64_t *index, uint64_t *evicted_hi, uint64_t *evicted_lo);
int lv1_read_virtual_uart(uint64_t port, uint64_t buffer, uint64_t bytes, uint64_t *bytes_read);
int lv1_write_virtual_uart(uint64_t port, uint64_t buffer, uint64_t bytes, uint64_t *bytes_written);
int lv1_set_virtual_uart_param(uint64_t port, uint64_t param, uint64_t value);
int lv1_get_virtual_uart_param(uint64_t port, uint64_t param, uint64_t *value);
int lv1_configure_virtual_uart(uint64_t lpar_addr, uint64_t *outlet);
int lv1_open_device(uint64_t bus, uint64_t dev, uint64_t zero);
int lv1_close_device(uint64_t bus, uint64_t dev);
int lv1_map_device_mmio_region(uint64_t bus, uint64_t dev, uint64_t bus_addr, uint64_t size, uint64_t page_size, uint64_t *lpar_addr);
int lv1_unmap_device_mmio_region(uint64_t bus, uint64_t dev, uint64_t lpar_addr);
int lv1_allocate_device_dma_region(uint64_t bus, uint64_t dev, uint64_t io_size, uint64_t io_pagesize, uint64_t flag, uint64_t *dma_region);
int lv1_free_device_dma_region(uint64_t bus, uint64_t dev, uint64_t dma_region);
int lv1_map_device_dma_region(uint64_t bus, uint64_t dev, uint64_t lpar_addr, uint64_t dma_region, uint64_t size, uint64_t flags);
int lv1_unmap_device_dma_region(uint64_t bus, uint64_t dev, uint64_t dma_region, uint64_t size);
int lv1_read_pci_config(uint64_t ps3bus, uint64_t bus, uint64_t dev, uint64_t func, uint64_t offset, uint64_t size, uint64_t *result);
int lv1_write_pci_config(uint64_t ps3bus, uint64_t bus, uint64_t dev, uint64_t func, uint64_t offset, uint64_t size, uint64_t data);
int lv1_net_add_multicast_address(uint64_t bus, uint64_t dev, uint64_t addr, uint64_t flags);
int lv1_net_remove_multicast_address(uint64_t bus, uint64_t dev, uint64_t zero, uint64_t one);
int lv1_net_start_tx_dma(uint64_t bus, uint64_t dev, uint64_t bus_addr, uint64_t zero);
int lv1_net_stop_tx_dma(uint64_t bus, uint64_t dev, uint64_t zero);
int lv1_net_start_rx_dma(uint64_t bus, uint64_t dev, uint64_t bus_addr, uint64_t zero);
int lv1_net_stop_rx_dma(uint64_t bus, uint64_t dev, uint64_t zero);
int lv1_net_set_interrupt_status_indicator(uint64_t bus, uint64_t dev, uint64_t irq_status_addr, uint64_t zero);
int lv1_net_set_interrupt_mask(uint64_t bus, uint64_t dev, uint64_t mask, uint64_t zero);
int lv1_net_control(uint64_t bus, uint64_t dev, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t *v1, uint64_t *v2);
int lv1_connect_interrupt_event_receive_port(uint64_t bus, uint64_t dev, uint64_t outlet, uint64_t irq);
int lv1_disconnect_interrupt_event_receive_port(uint64_t bus, uint64_t dev, uint64_t outlet, uint64_t irq);
int lv1_deconfigure_virtual_uart_irq(void);
int lv1_enable_logical_spe(uint64_t spe_id, uint64_t resource_id);
int lv1_gpu_open(uint64_t zero);
int lv1_gpu_close(void);
int lv1_gpu_device_map(uint64_t dev, uint64_t *lpar_addr, uint64_t *lpar_size);
int lv1_gpu_device_unmap(uint64_t dev);
int lv1_gpu_memory_allocate(uint64_t ddr_size, uint64_t zero1, uint64_t zero2, uint64_t zero3, uint64_t zero4, uint64_t *handle, uint64_t *ddr_lpar);
int lv1_gpu_memory_free(uint64_t handle);
int lv1_gpu_context_allocate(uint64_t handle, uint64_t , uint64_t *zero);
int lv1_gpu_context_free(uint64_t chandle);
int lv1_gpu_context_iomap(uint64_t changle, uint64_t gpu_ioif, uint64_t xdr_lpar, uint64_t fbsize, uint64_t ioflags);
int lv1_gpu_context_attribute(uint64_t chandle, uint64_t op, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4);
int lv1_gpu_context_intr(uint64_t chandle, uint64_t *v1);
int lv1_get_rtc(uint64_t *rtc_val, uint64_t *timebase);
int lv1_storage_read(uint64_t dev, uint64_t region, uint64_t sector, uint64_t nsectors, uint64_t flags, uint64_t buf, uint64_t *dma_tag);
int lv1_storage_write(uint64_t dev, uint64_t region, uint64_t sector, uint64_t nsectors, uint64_t flags, uint64_t buf, uint64_t *dma_tag);
int lv1_storage_send_device_command(uint64_t dev, uint64_t cmd_id, uint64_t cmd_block, uint64_t cmd_size, uint64_t data_buf, uint64_t blocks, uint64_t *dma_tag);
int lv1_storage_get_async_status(uint64_t dev, uint64_t *dma_tag, uint64_t *status);
int lv1_storage_check_async_status(uint64_t dev, uint64_t dma_tag, uint64_t *status);
int lv1_panic(uint64_t howto);

View File

@ -0,0 +1,111 @@
/*
* Playstation 3 LV1 hypercall interface
*
* $FreeBSD$
*/
#include <sys/types.h>
enum lpar_id {
PS3_LPAR_ID_CURRENT = 0x00,
PS3_LPAR_ID_PME = 0x01,
};
static inline uint64_t
lv1_repository_string(const char *str)
{
uint64_t ret = 0;
strncpy((char *)&ret, str, sizeof(ret));
return (ret);
}
# Code Name Inputs Outputs
HVCALL 0 lv1_allocate_memory size,log_page_size,zero,flags base_addr,muid
HVCALL 1 lv1_write_htab_entry vas_id,slot,pte_hi,pte_lo
HVCALL 2 lv1_construct_virtual_address_space log_pteg_count,n_sizes,page_sizes vas_id,hv_pteg_count
HVCALL 4 lv1_get_virtual_address_space_id_of_ppe ppe_id vas_id
HVCALL 6 lv1_query_logical_partition_address_region_info lpar_id base_addr,size,access_right,max_page_size,flags
HVCALL 7 lv1_select_virtual_address_space vas_id
HVCALL 9 lv1_pause mode
HVCALL 10 lv1_destruct_virtual_address_space vas_id
HVCALL 11 lv1_configure_irq_state_bitmap ppe_id,cpu_id,bitmap_addr
HVCALL 12 lv1_connect_irq_plug_ext ppe_id,cpu_id,virq,outlet,zero
HVCALL 13 lv1_release_memory base_addr
HVCALL 15 lv1_put_iopte ioas_id,ioif_addr,lpar_addr,io_id,flags
HVCALL 17 lv1_disconnect_irq_plug_ext ppe_id,cpu_id,virq
HVCALL 18 lv1_construct_event_receive_port UNUSED outlet
HVCALL 19 lv1_destruct_event_receive_port outlet
HVCALL 24 lv1_send_event_locally outlet
HVCALL 27 lv1_end_of_interrupt irq
HVCALL 28 lv1_connect_irq_plug virq,irq
HVCALL 29 lv1_disconnect_irq_plus virq
HVCALL 30 lv1_end_of_interrupt_ext ppe_id,cpu_id,virq
HVCALL 31 lv1_did_update_interrupt_mask ppe_id,cpu_id
HVCALL 44 lv1_shutdown_logical_partition cmd
HVCALL 54 lv1_destruct_logical_spe spe_id
HVCALL 57 lv1_construct_logical_spe pshift1,pshift2,pshift3,pshift4,pshift5,vas_id,spe_type priv2_addr,problem_phys,local_store_phys,unused,shadow_addr,spe_id
HVCALL 61 lv1_set_spe_interrupt_mask spe_id,class,mask
HVCALL 65 lv1_disable_logical_spe spe_id,zero
HVCALL 66 lv1_clear_spe_interrupt_status spe_id,class,stat,zero
HVCALL 67 lv1_get_spe_interrupt_status spe_id,class stat
HVCALL 69 lv1_get_logical_ppe_id UNUSED ppe_id
HVCALL 74 lv1_get_logical_partition_id UNUSED lpar_id
HVCALL 78 lv1_get_spe_irq_outlet spe_id,class outlet
HVCALL 79 lv1_set_spe_privilege_state_area_1_register spe_id,offset,value
HVCALL 91 lv1_get_repository_node_value lpar_id,n1,n2,n3,n4 v1,v2
HVCALL 95 lv1_read_htab_entries vas_id,slot hi1,hi2,hi3,hi4,rcbits
HVCALL 96 lv1_set_dabr dabr,flags
HVCALL 116 lv1_allocate_io_segment ioas_id,seg_size,io_pagesize ioif_addr
HVCALL 117 lv1_release_io_segment ioas_id,ioif_addr
HVCALL 120 lv1_construct_io_irq_outlet interrupt_id outlet
HVCALL 121 lv1_destruct_io_irq_outlet outlet
HVCALL 122 lv1_map_htab lpar_id htab_addr
HVCALL 123 lv1_unmap_htab htab_addr
HVCALL 127 lv1_get_version_info UNUSED firm_vers
HVCALL 158 lv1_insert_htab_entry vas_id,pteg,pte_hi,pte_lo,lockflags,flags index,evicted_hi,evicted_lo
HVCALL 162 lv1_read_virtual_uart port,buffer,bytes bytes_read
HVCALL 163 lv1_write_virtual_uart port,buffer,bytes bytes_written
HVCALL 164 lv1_set_virtual_uart_param port,param,value
HVCALL 165 lv1_get_virtual_uart_param port,param value
HVCALL 166 lv1_configure_virtual_uart lpar_addr outlet
HVCALL 170 lv1_open_device bus,dev,zero
HVCALL 171 lv1_close_device bus,dev
HVCALL 172 lv1_map_device_mmio_region bus,dev,bus_addr,size,page_size lpar_addr
HVCALL 173 lv1_unmap_device_mmio_region bus,dev,lpar_addr
HVCALL 174 lv1_allocate_device_dma_region bus,dev,io_size,io_pagesize,flag dma_region
HVCALL 175 lv1_free_device_dma_region bus,dev,dma_region
HVCALL 176 lv1_map_device_dma_region bus,dev,lpar_addr,dma_region,size,flags
HVCALL 177 lv1_unmap_device_dma_region bus,dev,dma_region,size
HVCALL 178 lv1_read_pci_config ps3bus,bus,dev,func,offset,size result
HVCALL 179 lv1_write_pci_config ps3bus,bus,dev,func,offset,size,data
HVCALL 185 lv1_net_add_multicast_address bus,dev,addr,flags
HVCALL 186 lv1_net_remove_multicast_address bus,dev,zero,one
HVCALL 187 lv1_net_start_tx_dma bus,dev,bus_addr,zero
HVCALL 188 lv1_net_stop_tx_dma bus,dev,zero
HVCALL 189 lv1_net_start_rx_dma bus,dev,bus_addr,zero
HVCALL 190 lv1_net_stop_rx_dma bus,dev,zero
HVCALL 191 lv1_net_set_interrupt_status_indicator bus,dev,irq_status_addr,zero
HVCALL 193 lv1_net_set_interrupt_mask bus,dev,mask,zero
HVCALL 194 lv1_net_control bus,dev,p1,p2,p3,p4 v1,v2
HVCALL 197 lv1_connect_interrupt_event_receive_port bus,dev,outlet,irq
HVCALL 198 lv1_disconnect_interrupt_event_receive_port bus,dev,outlet,irq
HVCALL 202 lv1_deconfigure_virtual_uart_irq
HVCALL 207 lv1_enable_logical_spe spe_id,resource_id
HVCALL 210 lv1_gpu_open zero
HVCALL 211 lv1_gpu_close
HVCALL 212 lv1_gpu_device_map dev lpar_addr,lpar_size
HVCALL 213 lv1_gpu_device_unmap dev
HVCALL 214 lv1_gpu_memory_allocate ddr_size,zero1,zero2,zero3,zero4 handle,ddr_lpar
HVCALL 216 lv1_gpu_memory_free handle
HVCALL 217 lv1_gpu_context_allocate handle, zero chandle,lpar_dma_control,lpar_driver_info,lpar_reports,lpar_reports_size
HVCALL 218 lv1_gpu_context_free chandle
HVCALL 221 lv1_gpu_context_iomap changle,gpu_ioif,xdr_lpar,fbsize,ioflags
HVCALL 225 lv1_gpu_context_attribute chandle,op,p1,p2,p3,p4
HVCALL 227 lv1_gpu_context_intr chandle v1
HVCALL 232 lv1_get_rtc UNUSED rtc_val,timebase
HVCALL 245 lv1_storage_read dev,region,sector,nsectors,flags,buf dma_tag
HVCALL 246 lv1_storage_write dev,region,sector,nsectors,flags,buf dma_tag
HVCALL 248 lv1_storage_send_device_command dev,cmd_id,cmd_block,cmd_size,data_buf,blocks dma_tag
HVCALL 249 lv1_storage_get_async_status dev dma_tag,status
HVCALL 254 lv1_storage_check_async_status dev,dma_tag status
HVCALL 255 lv1_panic howto

View File

@ -0,0 +1,757 @@
/*-
* Copyright (c) 2003 Peter Grehan
* 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/kernel.h>
#include <sys/sysctl.h>
#include <sys/limits.h>
#include <sys/conf.h>
#include <sys/cons.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/fbio.h>
#include <sys/consio.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/sc_machdep.h>
#include <machine/platform.h>
#include <machine/pmap.h>
#include <sys/rman.h>
#include <dev/fb/fbreg.h>
#include <dev/syscons/syscons.h>
#include "ps3-hvcall.h"
#define PS3FB_SIZE (4*1024*1024)
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET 0x0100
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x0101
#define L1GPU_DISPLAY_SYNC_HSYNC 1
#define L1GPU_DISPLAY_SYNC_VSYNC 2
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x0102
extern u_char dflt_font_16[];
extern u_char dflt_font_14[];
extern u_char dflt_font_8[];
static int ps3fb_configure(int flags);
void ps3fb_remap(void);
static vi_probe_t ps3fb_probe;
static vi_init_t ps3fb_init;
static vi_get_info_t ps3fb_get_info;
static vi_query_mode_t ps3fb_query_mode;
static vi_set_mode_t ps3fb_set_mode;
static vi_save_font_t ps3fb_save_font;
static vi_load_font_t ps3fb_load_font;
static vi_show_font_t ps3fb_show_font;
static vi_save_palette_t ps3fb_save_palette;
static vi_load_palette_t ps3fb_load_palette;
static vi_set_border_t ps3fb_set_border;
static vi_save_state_t ps3fb_save_state;
static vi_load_state_t ps3fb_load_state;
static vi_set_win_org_t ps3fb_set_win_org;
static vi_read_hw_cursor_t ps3fb_read_hw_cursor;
static vi_set_hw_cursor_t ps3fb_set_hw_cursor;
static vi_set_hw_cursor_shape_t ps3fb_set_hw_cursor_shape;
static vi_blank_display_t ps3fb_blank_display;
static vi_mmap_t ps3fb_mmap;
static vi_ioctl_t ps3fb_ioctl;
static vi_clear_t ps3fb_clear;
static vi_fill_rect_t ps3fb_fill_rect;
static vi_bitblt_t ps3fb_bitblt;
static vi_diag_t ps3fb_diag;
static vi_save_cursor_palette_t ps3fb_save_cursor_palette;
static vi_load_cursor_palette_t ps3fb_load_cursor_palette;
static vi_copy_t ps3fb_copy;
static vi_putp_t ps3fb_putp;
static vi_putc_t ps3fb_putc;
static vi_puts_t ps3fb_puts;
static vi_putm_t ps3fb_putm;
struct ps3fb_softc {
video_adapter_t sc_va;
struct cdev *sc_si;
int sc_console;
intptr_t sc_addr;
int sc_height;
int sc_width;
int sc_stride;
int sc_ncol;
int sc_nrow;
int sc_xmargin;
int sc_ymargin;
u_char *sc_font;
int sc_font_height;
};
static video_switch_t ps3fbvidsw = {
.probe = ps3fb_probe,
.init = ps3fb_init,
.get_info = ps3fb_get_info,
.query_mode = ps3fb_query_mode,
.set_mode = ps3fb_set_mode,
.save_font = ps3fb_save_font,
.load_font = ps3fb_load_font,
.show_font = ps3fb_show_font,
.save_palette = ps3fb_save_palette,
.load_palette = ps3fb_load_palette,
.set_border = ps3fb_set_border,
.save_state = ps3fb_save_state,
.load_state = ps3fb_load_state,
.set_win_org = ps3fb_set_win_org,
.read_hw_cursor = ps3fb_read_hw_cursor,
.set_hw_cursor = ps3fb_set_hw_cursor,
.set_hw_cursor_shape = ps3fb_set_hw_cursor_shape,
.blank_display = ps3fb_blank_display,
.mmap = ps3fb_mmap,
.ioctl = ps3fb_ioctl,
.clear = ps3fb_clear,
.fill_rect = ps3fb_fill_rect,
.bitblt = ps3fb_bitblt,
.diag = ps3fb_diag,
.save_cursor_palette = ps3fb_save_cursor_palette,
.load_cursor_palette = ps3fb_load_cursor_palette,
.copy = ps3fb_copy,
.putp = ps3fb_putp,
.putc = ps3fb_putc,
.puts = ps3fb_puts,
.putm = ps3fb_putm,
};
VIDEO_DRIVER(ps3fb, ps3fbvidsw, ps3fb_configure);
extern sc_rndr_sw_t txtrndrsw;
RENDERER(ps3fb, 0, txtrndrsw, gfb_set);
RENDERER_MODULE(ps3fb, gfb_set);
/*
* Define the iso6429-1983 colormap
*/
static struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} ps3fb_cmap[16] = { /* # R G B Color */
/* - - - - ----- */
{ 0x00, 0x00, 0x00 }, /* 0 0 0 0 Black */
{ 0x00, 0x00, 0xaa }, /* 1 0 0 2/3 Blue */
{ 0x00, 0xaa, 0x00 }, /* 2 0 2/3 0 Green */
{ 0x00, 0xaa, 0xaa }, /* 3 0 2/3 2/3 Cyan */
{ 0xaa, 0x00, 0x00 }, /* 4 2/3 0 0 Red */
{ 0xaa, 0x00, 0xaa }, /* 5 2/3 0 2/3 Magenta */
{ 0xaa, 0x55, 0x00 }, /* 6 2/3 1/3 0 Brown */
{ 0xaa, 0xaa, 0xaa }, /* 7 2/3 2/3 2/3 White */
{ 0x55, 0x55, 0x55 }, /* 8 1/3 1/3 1/3 Gray */
{ 0x55, 0x55, 0xff }, /* 9 1/3 1/3 1 Bright Blue */
{ 0x55, 0xff, 0x55 }, /* 10 1/3 1 1/3 Bright Green */
{ 0x55, 0xff, 0xff }, /* 11 1/3 1 1 Bright Cyan */
{ 0xff, 0x55, 0x55 }, /* 12 1 1/3 1/3 Bright Red */
{ 0xff, 0x55, 0xff }, /* 13 1 1/3 1 Bright Magenta */
{ 0xff, 0xff, 0x80 }, /* 14 1 1 1/3 Bright Yellow */
{ 0xff, 0xff, 0xff } /* 15 1 1 1 Bright White */
};
#define TODO printf("%s: unimplemented\n", __func__)
static u_int16_t ps3fb_static_window[ROW*COL];
static struct ps3fb_softc ps3fb_softc;
static __inline int
ps3fb_background(uint8_t attr)
{
return (attr >> 4);
}
static __inline int
ps3fb_foreground(uint8_t attr)
{
return (attr & 0x0f);
}
static u_int
ps3fb_pix32(int attr)
{
u_int retval;
retval = (ps3fb_cmap[attr].red << 16) |
(ps3fb_cmap[attr].green << 8) |
ps3fb_cmap[attr].blue;
return (retval);
}
static int
ps3fb_configure(int flags)
{
struct ps3fb_softc *sc;
int disable;
char compatible[64];
#if 0
phandle_t root;
#endif
static int done = 0;
disable = 0;
TUNABLE_INT_FETCH("hw.syscons.disable", &disable);
if (disable != 0)
return (0);
if (done != 0)
return (0);
done = 1;
sc = &ps3fb_softc;
#if 0
root = OF_finddevice("/");
if (OF_getprop(root, "compatible", compatible, sizeof(compatible)) <= 0)
return (0);
if (strncmp(compatible, "sony,ps3", sizeof(compatible)) != 0)
return (0);
#else
TUNABLE_STR_FETCH("hw.platform", compatible, sizeof(compatible));
if (strcmp(compatible, "ps3") != 0)
return (0);
#endif
sc->sc_console = 1;
/* XXX: get from HV repository */
sc->sc_height = 480;
sc->sc_width = 720;
sc->sc_stride = sc->sc_width*4;
/*
* The loader puts the FB at 0x10000000, so use that for now.
*/
sc->sc_addr = 0x10000000;
ps3fb_init(0, &sc->sc_va, 0);
return (0);
}
void
ps3fb_remap(void)
{
vm_offset_t va, fb_paddr;
uint64_t fbhandle, fbcontext;
lv1_gpu_close();
lv1_gpu_open(0);
lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET,
0,0,0,0);
lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_MODE_SET,
0,0,1,0);
lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
0,L1GPU_DISPLAY_SYNC_VSYNC,0,0);
lv1_gpu_context_attribute(0, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
1,L1GPU_DISPLAY_SYNC_VSYNC,0,0);
lv1_gpu_memory_allocate(PS3FB_SIZE, 0, 0, 0, 0, &fbhandle, &fb_paddr);
lv1_gpu_context_allocate(fbhandle, 0, &fbcontext);
lv1_gpu_context_attribute(fbcontext,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, 0, 0, 0);
lv1_gpu_context_attribute(fbcontext,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, 0, 0, 0);
for (va = 0; va < PS3FB_SIZE; va += PAGE_SIZE)
pmap_kenter_attr(0x10000000 + va, fb_paddr + va,
VM_MEMATTR_WRITE_COMBINING);
}
static int
ps3fb_probe(int unit, video_adapter_t **adp, void *arg, int flags)
{
TODO;
return (0);
}
static int
ps3fb_init(int unit, video_adapter_t *adp, int flags)
{
struct ps3fb_softc *sc;
video_info_t *vi;
int cxborder, cyborder;
int font_height;
sc = (struct ps3fb_softc *)adp;
vi = &adp->va_info;
vid_init_struct(adp, "ps3fb", -1, unit);
/* The default font size can be overridden by loader */
font_height = 16;
TUNABLE_INT_FETCH("hw.syscons.fsize", &font_height);
if (font_height == 8) {
sc->sc_font = dflt_font_8;
sc->sc_font_height = 8;
} else if (font_height == 14) {
sc->sc_font = dflt_font_14;
sc->sc_font_height = 14;
} else {
/* default is 8x16 */
sc->sc_font = dflt_font_16;
sc->sc_font_height = 16;
}
/* The user can set a border in chars - default is 1 char width */
cxborder = 8;
cyborder = 2;
TUNABLE_INT_FETCH("hw.syscons.xborder", &cxborder);
TUNABLE_INT_FETCH("hw.syscons.yborder", &cyborder);
vi->vi_cheight = sc->sc_font_height;
vi->vi_width = sc->sc_width/8 - 2*cxborder;
vi->vi_height = sc->sc_height/sc->sc_font_height - 2*cyborder;
vi->vi_cwidth = 8;
/*
* Clamp width/height to syscons maximums
*/
if (vi->vi_width > COL)
vi->vi_width = COL;
if (vi->vi_height > ROW)
vi->vi_height = ROW;
sc->sc_xmargin = (sc->sc_width - (vi->vi_width * vi->vi_cwidth)) / 2;
sc->sc_ymargin = (sc->sc_height - (vi->vi_height * vi->vi_cheight))/2;
/*
* Avoid huge amounts of conditional code in syscons by
* defining a dummy h/w text display buffer.
*/
adp->va_window = (vm_offset_t) ps3fb_static_window;
/*
* Enable future font-loading and flag color support, as well as
* adding V_ADP_MODECHANGE so that we ps3fb_set_mode() gets called
* when the X server shuts down. This enables us to get the console
* back when X disappears.
*/
adp->va_flags |= V_ADP_FONT | V_ADP_COLOR | V_ADP_MODECHANGE;
ps3fb_set_mode(&sc->sc_va, 0);
vid_register(&sc->sc_va);
return (0);
}
static int
ps3fb_get_info(video_adapter_t *adp, int mode, video_info_t *info)
{
bcopy(&adp->va_info, info, sizeof(*info));
return (0);
}
static int
ps3fb_query_mode(video_adapter_t *adp, video_info_t *info)
{
TODO;
return (0);
}
static int
ps3fb_set_mode(video_adapter_t *adp, int mode)
{
struct ps3fb_softc *sc;
sc = (struct ps3fb_softc *)adp;
/* XXX: no real mode setting at the moment */
ps3fb_blank_display(&sc->sc_va, V_DISPLAY_ON);
return (0);
}
static int
ps3fb_save_font(video_adapter_t *adp, int page, int size, int width,
u_char *data, int c, int count)
{
TODO;
return (0);
}
static int
ps3fb_load_font(video_adapter_t *adp, int page, int size, int width,
u_char *data, int c, int count)
{
struct ps3fb_softc *sc;
sc = (struct ps3fb_softc *)adp;
/*
* syscons code has already determined that current width/height
* are unchanged for this new font
*/
sc->sc_font = data;
return (0);
}
static int
ps3fb_show_font(video_adapter_t *adp, int page)
{
return (0);
}
static int
ps3fb_save_palette(video_adapter_t *adp, u_char *palette)
{
/* TODO; */
return (0);
}
static int
ps3fb_load_palette(video_adapter_t *adp, u_char *palette)
{
/* TODO; */
return (0);
}
static int
ps3fb_set_border(video_adapter_t *adp, int border)
{
/* XXX Be lazy for now and blank entire screen */
return (ps3fb_blank_display(adp, border));
}
static int
ps3fb_save_state(video_adapter_t *adp, void *p, size_t size)
{
TODO;
return (0);
}
static int
ps3fb_load_state(video_adapter_t *adp, void *p)
{
TODO;
return (0);
}
static int
ps3fb_set_win_org(video_adapter_t *adp, off_t offset)
{
TODO;
return (0);
}
static int
ps3fb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
{
*col = 0;
*row = 0;
return (0);
}
static int
ps3fb_set_hw_cursor(video_adapter_t *adp, int col, int row)
{
return (0);
}
static int
ps3fb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
int celsize, int blink)
{
return (0);
}
static int
ps3fb_blank_display(video_adapter_t *adp, int mode)
{
struct ps3fb_softc *sc;
int i;
uint32_t *addr;
sc = (struct ps3fb_softc *)adp;
addr = (uint32_t *) sc->sc_addr;
for (i = 0; i < (sc->sc_stride/4)*sc->sc_height; i++)
*(addr + i) = ps3fb_pix32(ps3fb_background(SC_NORM_ATTR));
return (0);
}
static int
ps3fb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr,
int prot, vm_memattr_t *memattr)
{
struct ps3fb_softc *sc;
sc = (struct ps3fb_softc *)adp;
/*
* This might be a legacy VGA mem request: if so, just point it at the
* framebuffer, since it shouldn't be touched
*/
if (offset < sc->sc_stride*sc->sc_height) {
*paddr = sc->sc_addr + offset;
return (0);
}
return (EINVAL);
}
static int
ps3fb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t data)
{
return (0);
}
static int
ps3fb_clear(video_adapter_t *adp)
{
TODO;
return (0);
}
static int
ps3fb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
{
TODO;
return (0);
}
static int
ps3fb_bitblt(video_adapter_t *adp, ...)
{
TODO;
return (0);
}
static int
ps3fb_diag(video_adapter_t *adp, int level)
{
TODO;
return (0);
}
static int
ps3fb_save_cursor_palette(video_adapter_t *adp, u_char *palette)
{
TODO;
return (0);
}
static int
ps3fb_load_cursor_palette(video_adapter_t *adp, u_char *palette)
{
TODO;
return (0);
}
static int
ps3fb_copy(video_adapter_t *adp, vm_offset_t src, vm_offset_t dst, int n)
{
TODO;
return (0);
}
static int
ps3fb_putp(video_adapter_t *adp, vm_offset_t off, uint32_t p, uint32_t a,
int size, int bpp, int bit_ltor, int byte_ltor)
{
TODO;
return (0);
}
static int
ps3fb_putc(video_adapter_t *adp, vm_offset_t off, uint8_t c, uint8_t a)
{
struct ps3fb_softc *sc;
int row;
int col;
int i, j, k;
uint32_t *addr;
u_char *p;
sc = (struct ps3fb_softc *)adp;
row = (off / adp->va_info.vi_width) * adp->va_info.vi_cheight;
col = (off % adp->va_info.vi_width) * adp->va_info.vi_cwidth;
p = sc->sc_font + c*sc->sc_font_height;
addr = (uint32_t *)sc->sc_addr
+ (row + sc->sc_ymargin)*(sc->sc_stride/4)
+ col + sc->sc_xmargin;
for (i = 0; i < sc->sc_font_height; i++) {
for (j = 0, k = 7; j < 8; j++, k--) {
if ((p[i] & (1 << k)) == 0)
*(addr + j) = ps3fb_pix32(ps3fb_background(a));
else
*(addr + j) = ps3fb_pix32(ps3fb_foreground(a));
}
addr += (sc->sc_stride/4);
}
return (0);
}
static int
ps3fb_puts(video_adapter_t *adp, vm_offset_t off, u_int16_t *s, int len)
{
int i;
for (i = 0; i < len; i++) {
ps3fb_putc(adp, off + i, s[i] & 0xff, (s[i] & 0xff00) >> 8);
}
return (0);
}
static int
ps3fb_putm(video_adapter_t *adp, int x, int y, uint8_t *pixel_image,
uint32_t pixel_mask, int size, int width)
{
struct ps3fb_softc *sc;
int i, j, k;
uint32_t fg, bg;
uint32_t *addr;
sc = (struct ps3fb_softc *)adp;
addr = (uint32_t *)sc->sc_addr
+ (y + sc->sc_ymargin)*(sc->sc_stride/4)
+ x + sc->sc_xmargin;
fg = ps3fb_pix32(ps3fb_foreground(SC_NORM_ATTR));
bg = ps3fb_pix32(ps3fb_background(SC_NORM_ATTR));
for (i = 0; i < size && i+y < sc->sc_height - 2*sc->sc_ymargin; i++) {
for (j = 0, k = width; j < 8; j++, k--) {
if (x + j >= sc->sc_width - 2*sc->sc_xmargin)
continue;
if (pixel_image[i] & (1 << k))
*(addr + j) = (*(addr + j) == fg) ? bg : fg;
}
addr += (sc->sc_stride/4);
}
return (0);
}
/*
* Define the syscons nexus device attachment
*/
static void
ps3fb_scidentify(driver_t *driver, device_t parent)
{
device_t child;
/*
* Add with a priority guaranteed to make it last on
* the device list
*/
if (strcmp(installed_platform(), "ps3") == 0)
child = BUS_ADD_CHILD(parent, INT_MAX, SC_DRIVER_NAME, 0);
}
static int
ps3fb_scprobe(device_t dev)
{
int error;
if (strcmp(installed_platform(), "ps3") != 0)
return (ENXIO);
device_set_desc(dev, "System console");
error = sc_probe_unit(device_get_unit(dev),
device_get_flags(dev) | SC_AUTODETECT_KBD);
if (error != 0)
return (error);
/* This is a fake device, so make sure we added it ourselves */
return (BUS_PROBE_NOWILDCARD);
}
static int
ps3fb_scattach(device_t dev)
{
return (sc_attach_unit(device_get_unit(dev),
device_get_flags(dev) | SC_AUTODETECT_KBD));
}
static device_method_t ps3fb_sc_methods[] = {
DEVMETHOD(device_identify, ps3fb_scidentify),
DEVMETHOD(device_probe, ps3fb_scprobe),
DEVMETHOD(device_attach, ps3fb_scattach),
{ 0, 0 }
};
static driver_t ps3fb_sc_driver = {
SC_DRIVER_NAME,
ps3fb_sc_methods,
sizeof(sc_softc_t),
};
static devclass_t sc_devclass;
DRIVER_MODULE(sc, nexus, ps3fb_sc_driver, sc_devclass, 0, 0);
/*
* Define a stub keyboard driver in case one hasn't been
* compiled into the kernel
*/
#include <sys/kbio.h>
#include <dev/kbd/kbdreg.h>
static int dummy_kbd_configure(int flags);
keyboard_switch_t ps3dummysw;
static int
dummy_kbd_configure(int flags)
{
return (0);
}
KEYBOARD_DRIVER(ps3dummy, ps3dummysw, dummy_kbd_configure);

561
sys/powerpc/ps3/ps3bus.c Normal file
View File

@ -0,0 +1,561 @@
/*-
* Copyright (C) 2010 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/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 <sys/resource.h>
#include <sys/rman.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/platform.h>
#include <machine/pmap.h>
#include <machine/resource.h>
#include "ps3bus.h"
#include "ps3-hvcall.h"
#include "iommu_if.h"
static void ps3bus_identify(driver_t *, device_t);
static int ps3bus_probe(device_t);
static int ps3bus_attach(device_t);
static int ps3bus_print_child(device_t dev, device_t child);
static int ps3bus_read_ivar(device_t bus, device_t child, int which,
uintptr_t *result);
static struct resource *ps3bus_alloc_resource(device_t bus, device_t child,
int type, int *rid, u_long start, u_long end,
u_long count, u_int flags);
static int ps3bus_activate_resource(device_t bus, device_t child, int type,
int rid, struct resource *res);
static bus_dma_tag_t ps3bus_get_dma_tag(device_t dev, device_t child);
static int ps3_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_size_t boundary, void *cookie);
static int ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs,
int nsegs, void *cookie);
struct ps3bus_devinfo {
int bus;
int dev;
uint64_t bustype;
uint64_t devtype;
struct resource_list resources;
bus_dma_tag_t dma_tag;
struct mtx iommu_mtx;
bus_addr_t dma_base[4];
};
static MALLOC_DEFINE(M_PS3BUS, "ps3bus", "PS3 system bus device information");
enum ps3bus_irq_type {
SB_IRQ = 2,
OHCI_IRQ = 3,
EHCI_IRQ = 4,
};
static device_method_t ps3bus_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, ps3bus_identify),
DEVMETHOD(device_probe, ps3bus_probe),
DEVMETHOD(device_attach, ps3bus_attach),
/* Bus interface */
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
DEVMETHOD(bus_add_child, bus_generic_add_child),
DEVMETHOD(bus_get_dma_tag, ps3bus_get_dma_tag),
DEVMETHOD(bus_print_child, ps3bus_print_child),
DEVMETHOD(bus_read_ivar, ps3bus_read_ivar),
DEVMETHOD(bus_alloc_resource, ps3bus_alloc_resource),
DEVMETHOD(bus_activate_resource, ps3bus_activate_resource),
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
/* IOMMU interface */
DEVMETHOD(iommu_map, ps3_iommu_map),
DEVMETHOD(iommu_unmap, ps3_iommu_unmap),
{ 0, 0 }
};
struct ps3bus_softc {
struct rman sc_mem_rman;
struct mem_region *regions;
int rcount;
};
static driver_t ps3bus_driver = {
"ps3bus",
ps3bus_methods,
sizeof(struct ps3bus_softc)
};
static devclass_t ps3bus_devclass;
DRIVER_MODULE(ps3bus, nexus, ps3bus_driver, ps3bus_devclass, 0, 0);
static void
ps3bus_identify(driver_t *driver, device_t parent)
{
if (strcmp(installed_platform(), "ps3") != 0)
return;
if (device_find_child(parent, "ps3bus", -1) == NULL)
BUS_ADD_CHILD(parent, 0, "ps3bus", 0);
}
static int
ps3bus_probe(device_t dev)
{
/* Do not attach to any OF nodes that may be present */
device_set_desc(dev, "Playstation 3 System Bus");
return (BUS_PROBE_NOWILDCARD);
}
static void
ps3bus_resources_init(struct rman *rm, int bus_index, int dev_index,
struct ps3bus_devinfo *dinfo)
{
uint64_t irq_type, irq, outlet;
uint64_t reg_type, paddr, len;
uint64_t ppe, junk;
int i, result;
int thread;
resource_list_init(&dinfo->resources);
lv1_get_logical_ppe_id(&ppe);
thread = 32 - fls(mfctrl());
/* Scan for interrupts */
for (i = 0; i < 10; i++) {
result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
(lv1_repository_string("bus") >> 32) | bus_index,
lv1_repository_string("dev") | dev_index,
lv1_repository_string("intr") | i, 0, &irq_type, &irq);
if (result != 0)
break;
switch (irq_type) {
case SB_IRQ:
lv1_construct_event_receive_port(&outlet);
lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet,
0);
lv1_connect_interrupt_event_receive_port(dinfo->bus,
dinfo->dev, outlet, irq);
break;
case OHCI_IRQ:
case EHCI_IRQ:
lv1_construct_io_irq_outlet(irq, &outlet);
lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet,
0);
break;
default:
printf("Unknown IRQ type %ld for device %d.%d\n",
irq_type, dinfo->bus, dinfo->dev);
break;
}
resource_list_add(&dinfo->resources, SYS_RES_IRQ, i,
outlet, outlet, 1);
}
/* Scan for registers */
for (i = 0; i < 10; i++) {
result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
(lv1_repository_string("bus") >> 32) | bus_index,
lv1_repository_string("dev") | dev_index,
lv1_repository_string("reg") | i,
lv1_repository_string("type"), &reg_type, &junk);
if (result != 0)
break;
result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
(lv1_repository_string("bus") >> 32) | bus_index,
lv1_repository_string("dev") | dev_index,
lv1_repository_string("reg") | i,
lv1_repository_string("data"), &paddr, &len);
result = lv1_map_device_mmio_region(dinfo->bus, dinfo->dev,
paddr, len, 12 /* log_2(4 KB) */, &paddr);
if (result != 0) {
printf("Mapping registers failed for device "
"%d.%d (%ld.%ld): %d\n", dinfo->bus, dinfo->dev,
dinfo->bustype, dinfo->devtype, result);
continue;
}
rman_manage_region(rm, paddr, paddr + len - 1);
resource_list_add(&dinfo->resources, SYS_RES_MEMORY, i,
paddr, paddr + len, len);
}
}
static int
ps3bus_attach(device_t self)
{
struct ps3bus_softc *sc;
struct ps3bus_devinfo *dinfo;
int bus_index, dev_index, result;
uint64_t bustype, bus, devs;
uint64_t dev, devtype;
uint64_t junk;
device_t cdev;
sc = device_get_softc(self);
sc->sc_mem_rman.rm_type = RMAN_ARRAY;
sc->sc_mem_rman.rm_descr = "PS3Bus Memory Mapped I/O";
rman_init(&sc->sc_mem_rman);
/* Get memory regions for DMA */
mem_regions(&sc->regions, &sc->rcount, &sc->regions, &sc->rcount);
/*
* Probe all the PS3's buses.
*/
for (bus_index = 0; bus_index < 5; bus_index++) {
result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
(lv1_repository_string("bus") >> 32) | bus_index,
lv1_repository_string("type"), 0, 0, &bustype, &junk);
if (result != 0)
continue;
result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
(lv1_repository_string("bus") >> 32) | bus_index,
lv1_repository_string("id"), 0, 0, &bus, &junk);
if (result != 0)
continue;
result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
(lv1_repository_string("bus") >> 32) | bus_index,
lv1_repository_string("num_dev"), 0, 0, &devs, &junk);
for (dev_index = 0; dev_index < devs; dev_index++) {
result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
(lv1_repository_string("bus") >> 32) | bus_index,
lv1_repository_string("dev") | dev_index,
lv1_repository_string("type"), 0, &devtype, &junk);
if (result != 0)
continue;
result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
(lv1_repository_string("bus") >> 32) | bus_index,
lv1_repository_string("dev") | dev_index,
lv1_repository_string("id"), 0, &dev, &junk);
if (result != 0)
continue;
dinfo = malloc(sizeof(*dinfo), M_PS3BUS,
M_WAITOK | M_ZERO);
dinfo->bus = bus;
dinfo->dev = dev;
dinfo->bustype = bustype;
dinfo->devtype = devtype;
if (dinfo->bustype == PS3_BUSTYPE_SYSBUS)
lv1_open_device(bus, dev, 0);
ps3bus_resources_init(&sc->sc_mem_rman, bus_index,
dev_index, dinfo);
cdev = device_add_child(self, NULL, -1);
if (cdev == NULL) {
device_printf(self,
"device_add_child failed\n");
free(dinfo, M_PS3BUS);
continue;
}
mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF);
device_set_ivars(cdev, dinfo);
}
}
return (bus_generic_attach(self));
}
static int
ps3bus_print_child(device_t dev, device_t child)
{
struct ps3bus_devinfo *dinfo = device_get_ivars(child);
int retval = 0;
retval += bus_print_child_header(dev, child);
retval += resource_list_print_type(&dinfo->resources, "mem",
SYS_RES_MEMORY, "%#lx");
retval += resource_list_print_type(&dinfo->resources, "irq",
SYS_RES_IRQ, "%ld");
retval += bus_print_child_footer(dev, child);
return (retval);
}
static int
ps3bus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
{
struct ps3bus_devinfo *dinfo = device_get_ivars(child);
switch (which) {
case PS3BUS_IVAR_BUS:
*result = dinfo->bus;
break;
case PS3BUS_IVAR_DEVICE:
*result = dinfo->dev;
break;
case PS3BUS_IVAR_BUSTYPE:
*result = dinfo->bustype;
break;
case PS3BUS_IVAR_DEVTYPE:
*result = dinfo->devtype;
break;
default:
return (EINVAL);
}
return (0);
}
static struct resource *
ps3bus_alloc_resource(device_t bus, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
struct ps3bus_devinfo *dinfo;
struct ps3bus_softc *sc;
int needactivate;
struct resource *rv;
struct rman *rm;
u_long adjstart, adjend, adjcount;
struct resource_list_entry *rle;
sc = device_get_softc(bus);
dinfo = device_get_ivars(child);
needactivate = flags & RF_ACTIVE;
flags &= ~RF_ACTIVE;
switch (type) {
case SYS_RES_MEMORY:
rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
*rid);
if (rle == NULL) {
device_printf(bus, "no rle for %s memory %d\n",
device_get_nameunit(child), *rid);
return (NULL);
}
if (start < rle->start)
adjstart = rle->start;
else if (start > rle->end)
adjstart = rle->end;
else
adjstart = start;
if (end < rle->start)
adjend = rle->start;
else if (end > rle->end)
adjend = rle->end;
else
adjend = end;
adjcount = adjend - adjstart;
rm = &sc->sc_mem_rman;
break;
case SYS_RES_IRQ:
return (resource_list_alloc(&dinfo->resources, bus, child,
type, rid, start, end, count, flags));
default:
device_printf(bus, "unknown resource request from %s\n",
device_get_nameunit(child));
return (NULL);
}
rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags,
child);
if (rv == NULL) {
device_printf(bus,
"failed to reserve resource %#lx - %#lx (%#lx)"
" for %s\n", adjstart, adjend, adjcount,
device_get_nameunit(child));
return (NULL);
}
rman_set_rid(rv, *rid);
if (needactivate) {
if (bus_activate_resource(child, type, *rid, rv) != 0) {
device_printf(bus,
"failed to activate resource for %s\n",
device_get_nameunit(child));
rman_release_resource(rv);
return (NULL);
}
}
return (rv);
}
static int
ps3bus_activate_resource(device_t bus, device_t child, int type, int rid,
struct resource *res)
{
void *p;
if (type == SYS_RES_IRQ)
return (bus_activate_resource(bus, type, rid, res));
if (type == SYS_RES_MEMORY) {
vm_offset_t start;
start = (vm_offset_t) rman_get_start(res);
if (bootverbose)
printf("ps3 mapdev: start %zx, len %ld\n", start,
rman_get_size(res));
p = pmap_mapdev(start, (vm_size_t) rman_get_size(res));
if (p == NULL)
return (ENOMEM);
rman_set_virtual(res, p);
rman_set_bustag(res, &bs_be_tag);
rman_set_bushandle(res, (u_long)p);
}
return (rman_activate_resource(res));
}
static bus_dma_tag_t
ps3bus_get_dma_tag(device_t dev, device_t child)
{
struct ps3bus_devinfo *dinfo = device_get_ivars(child);
struct ps3bus_softc *sc = device_get_softc(dev);
int i, err, flags;
if (dinfo->bustype != PS3_BUSTYPE_SYSBUS)
return (bus_get_dma_tag(dev));
mtx_lock(&dinfo->iommu_mtx);
if (dinfo->dma_tag != NULL) {
mtx_unlock(&dinfo->iommu_mtx);
return (dinfo->dma_tag);
}
flags = 0; /* 32-bit mode */
if (dinfo->bustype == PS3_BUSTYPE_SYSBUS &&
dinfo->devtype == PS3_DEVTYPE_USB)
flags = 2; /* 8-bit mode */
for (i = 0; i < sc->rcount; i++) {
err = lv1_allocate_device_dma_region(dinfo->bus, dinfo->dev,
sc->regions[i].mr_size, 24 /* log_2(16 MB) */, flags,
&dinfo->dma_base[i]);
if (err != 0) {
device_printf(child,
"could not allocate DMA region %d: %d\n", i, err);
goto fail;
}
err = lv1_map_device_dma_region(dinfo->bus, dinfo->dev,
sc->regions[i].mr_start, dinfo->dma_base[i],
sc->regions[i].mr_size,
0xf800000000000800UL /* Cell Handbook Figure 7.3.4.1 */);
if (err != 0) {
device_printf(child,
"could not map DMA region %d: %d\n", i, err);
goto fail;
}
}
err = bus_dma_tag_create(bus_get_dma_tag(dev),
1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
NULL, NULL, BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE,
0, NULL, NULL, &dinfo->dma_tag);
bus_dma_tag_set_iommu(dinfo->dma_tag, dev, dinfo);
fail:
mtx_unlock(&dinfo->iommu_mtx);
if (err)
return (NULL);
return (dinfo->dma_tag);
}
static int
ps3_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_size_t boundary,
void *cookie)
{
struct ps3bus_devinfo *dinfo = cookie;
struct ps3bus_softc *sc = device_get_softc(dev);
int i, j;
for (i = 0; i < *nsegs; i++) {
for (j = 0; j < sc->rcount; j++) {
if (segs[i].ds_addr >= sc->regions[j].mr_start &&
segs[i].ds_addr < sc->regions[j].mr_start +
sc->regions[j].mr_size)
break;
}
KASSERT(j < sc->rcount,
("Trying to map address %#lx not in physical memory",
segs[i].ds_addr));
segs[i].ds_addr = dinfo->dma_base[j] +
(segs[i].ds_addr - sc->regions[j].mr_start);
}
return (0);
}
static int
ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie)
{
return (0);
}

65
sys/powerpc/ps3/ps3bus.h Normal file
View File

@ -0,0 +1,65 @@
/*-
* Copyright (C) 2010 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$
*/
#ifndef _POWERPC_PS3_PS3BUS_H
#define _POWERPC_PS3_PS3BUS_H
enum {
PS3BUS_IVAR_BUS,
PS3BUS_IVAR_DEVICE,
PS3BUS_IVAR_BUSTYPE,
PS3BUS_IVAR_DEVTYPE
};
#define PS3BUS_ACCESSOR(A, B, T) \
__BUS_ACCESSOR(ps3bus, A, PS3BUS, B, T)
PS3BUS_ACCESSOR(bus, BUS, int)
PS3BUS_ACCESSOR(device, DEVICE, int)
PS3BUS_ACCESSOR(bustype, BUSTYPE, uint64_t)
PS3BUS_ACCESSOR(devtype, DEVTYPE, uint64_t)
/* Bus types */
enum {
PS3_BUSTYPE_SYSBUS = 4,
PS3_BUSTYPE_STORAGE = 5,
};
/* Device types */
enum {
/* System bus devices */
PS3_DEVTYPE_GELIC = 3,
PS3_DEVTYPE_USB = 4,
PS3_DEVTYPE_GPIO = 6,
/* Storage bus devices */
PS3_DEVTYPE_DISK = 0,
PS3_DEVTYPE_CDROM = 5,
PS3_DEVTYPE_FLASH = 14,
};
#endif /* _POWERPC_PS3_PS3BUS_H */

254
sys/powerpc/ps3/ps3pic.c Normal file
View File

@ -0,0 +1,254 @@
/*-
* Copyright 2010 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.
*
* $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 <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/intr_machdep.h>
#include <machine/md_var.h>
#include <machine/platform.h>
#include "ps3-hvcall.h"
#include "pic_if.h"
static void ps3pic_identify(driver_t *driver, device_t parent);
static int ps3pic_probe(device_t);
static int ps3pic_attach(device_t);
static void ps3pic_dispatch(device_t, struct trapframe *);
static void ps3pic_enable(device_t, u_int, u_int);
static void ps3pic_eoi(device_t, u_int);
static void ps3pic_ipi(device_t, u_int);
static void ps3pic_mask(device_t, u_int);
static void ps3pic_unmask(device_t, u_int);
static uint32_t ps3pic_id(device_t dev);
struct ps3pic_softc {
uint64_t *bitmap_thread0;
uint64_t *mask_thread0;
uint64_t *bitmap_thread1;
uint64_t *mask_thread1;
uint64_t sc_ipi_outlet[2];
int sc_vector[64];
};
static device_method_t ps3pic_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, ps3pic_identify),
DEVMETHOD(device_probe, ps3pic_probe),
DEVMETHOD(device_attach, ps3pic_attach),
/* PIC interface */
DEVMETHOD(pic_dispatch, ps3pic_dispatch),
DEVMETHOD(pic_enable, ps3pic_enable),
DEVMETHOD(pic_eoi, ps3pic_eoi),
DEVMETHOD(pic_id, ps3pic_id),
DEVMETHOD(pic_ipi, ps3pic_ipi),
DEVMETHOD(pic_mask, ps3pic_mask),
DEVMETHOD(pic_unmask, ps3pic_unmask),
{ 0, 0 },
};
static driver_t ps3pic_driver = {
"ps3pic",
ps3pic_methods,
sizeof(struct ps3pic_softc)
};
static devclass_t ps3pic_devclass;
DRIVER_MODULE(ps3pic, nexus, ps3pic_driver, ps3pic_devclass, 0, 0);
static MALLOC_DEFINE(M_PS3PIC, "ps3pic", "PS3 PIC");
static void
ps3pic_identify(driver_t *driver, device_t parent)
{
if (strcmp(installed_platform(), "ps3") != 0)
return;
if (device_find_child(parent, "ps3pic", -1) == NULL)
BUS_ADD_CHILD(parent, 0, "ps3pic", 0);
}
static int
ps3pic_probe(device_t dev)
{
device_set_desc(dev, "Playstation 3 interrupt controller");
return (BUS_PROBE_NOWILDCARD);
}
static int
ps3pic_attach(device_t dev)
{
struct ps3pic_softc *sc;
uint64_t ppe;
int thread;
sc = device_get_softc(dev);
sc->bitmap_thread0 = contigmalloc(128 /* 512 bits * 2 */, M_PS3PIC,
M_NOWAIT | M_ZERO, 0, BUS_SPACE_MAXADDR, 64 /* alignment */,
PAGE_SIZE /* boundary */);
sc->mask_thread0 = sc->bitmap_thread0 + 4;
sc->bitmap_thread1 = sc->bitmap_thread0 + 8;
sc->mask_thread1 = sc->bitmap_thread0 + 12;
lv1_get_logical_ppe_id(&ppe);
thread = 32 - fls(mfctrl());
lv1_configure_irq_state_bitmap(ppe, thread,
vtophys(sc->bitmap_thread0));
#ifdef SMP
lv1_configure_irq_state_bitmap(ppe, !thread,
vtophys(sc->bitmap_thread1));
/* Map both IPIs to the same VIRQ to avoid changes in intr_machdep */
lv1_construct_event_receive_port(&sc->sc_ipi_outlet[0]);
lv1_connect_irq_plug_ext(ppe, thread, sc->sc_ipi_outlet[0],
sc->sc_ipi_outlet[0], 0);
lv1_construct_event_receive_port(&sc->sc_ipi_outlet[1]);
lv1_connect_irq_plug_ext(ppe, !thread, sc->sc_ipi_outlet[0],
sc->sc_ipi_outlet[1], 0);
#endif
powerpc_register_pic(dev, sc->sc_ipi_outlet[0]);
root_pic = dev; /* PS3s have only one PIC */
return (0);
}
/*
* PIC I/F methods.
*/
static void
ps3pic_dispatch(device_t dev, struct trapframe *tf)
{
uint64_t bitmap, mask;
int irq;
struct ps3pic_softc *sc;
sc = device_get_softc(dev);
if (PCPU_GET(cpuid) == 0) {
bitmap = sc->bitmap_thread0[0];
mask = sc->mask_thread0[0];
} else {
bitmap = sc->bitmap_thread1[0];
mask = sc->mask_thread1[0];
}
while ((irq = ffsl(bitmap & mask) - 1) != -1) {
bitmap &= ~(1UL << irq);
powerpc_dispatch_intr(sc->sc_vector[63 - irq], tf);
}
}
static void
ps3pic_enable(device_t dev, u_int irq, u_int vector)
{
struct ps3pic_softc *sc;
sc = device_get_softc(dev);
sc->sc_vector[irq] = vector;
ps3pic_unmask(dev, irq);
}
static void
ps3pic_eoi(device_t dev, u_int irq)
{
uint64_t ppe;
int thread;
lv1_get_logical_ppe_id(&ppe);
thread = 32 - fls(mfctrl());
lv1_end_of_interrupt_ext(ppe, thread, irq);
}
static void
ps3pic_ipi(device_t dev, u_int cpu)
{
struct ps3pic_softc *sc;
sc = device_get_softc(dev);
lv1_send_event_locally(sc->sc_ipi_outlet[cpu]);
}
static void
ps3pic_mask(device_t dev, u_int irq)
{
struct ps3pic_softc *sc;
uint64_t ppe;
sc = device_get_softc(dev);
/* Do not mask IPIs! */
if (irq == sc->sc_ipi_outlet[0])
return;
sc->mask_thread0[0] &= ~(1UL << (63 - irq));
sc->mask_thread1[0] &= ~(1UL << (63 - irq));
lv1_get_logical_ppe_id(&ppe);
lv1_did_update_interrupt_mask(ppe, 0);
lv1_did_update_interrupt_mask(ppe, 1);
}
static void
ps3pic_unmask(device_t dev, u_int irq)
{
struct ps3pic_softc *sc;
uint64_t ppe;
sc = device_get_softc(dev);
sc->mask_thread0[0] |= (1UL << (63 - irq));
sc->mask_thread1[0] |= (1UL << (63 - irq));
lv1_get_logical_ppe_id(&ppe);
lv1_did_update_interrupt_mask(ppe, 0);
lv1_did_update_interrupt_mask(ppe, 1);
}
static uint32_t
ps3pic_id(device_t dev)
{
return (0);
}