summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/boot/powerpc/Makefile2
-rw-r--r--sys/boot/powerpc/ps3/Makefile122
-rw-r--r--sys/boot/powerpc/ps3/conf.c119
-rw-r--r--sys/boot/powerpc/ps3/devicename.c238
-rw-r--r--sys/boot/powerpc/ps3/help.ps31
-rw-r--r--sys/boot/powerpc/ps3/ldscript.powerpc112
-rw-r--r--sys/boot/powerpc/ps3/lv1call.S256
-rw-r--r--sys/boot/powerpc/ps3/lv1call.h72
-rw-r--r--sys/boot/powerpc/ps3/main.c211
-rw-r--r--sys/boot/powerpc/ps3/metadata.c355
-rw-r--r--sys/boot/powerpc/ps3/ppc64_elf_freebsd.c98
-rw-r--r--sys/boot/powerpc/ps3/ps3.h35
-rw-r--r--sys/boot/powerpc/ps3/ps3cons.c173
-rw-r--r--sys/boot/powerpc/ps3/ps3mmu.c120
-rw-r--r--sys/boot/powerpc/ps3/ps3net.c278
-rw-r--r--sys/boot/powerpc/ps3/start.S167
-rw-r--r--sys/boot/powerpc/ps3/version6
-rw-r--r--sys/conf/files.powerpc9
-rw-r--r--sys/conf/options.powerpc2
-rw-r--r--sys/powerpc/conf/GENERIC644
-rw-r--r--sys/powerpc/conf/NOTES2
-rw-r--r--sys/powerpc/include/pte.h1
-rw-r--r--sys/powerpc/ps3/ehci_ps3.c173
-rw-r--r--sys/powerpc/ps3/if_glc.c938
-rw-r--r--sys/powerpc/ps3/if_glcreg.h160
-rw-r--r--sys/powerpc/ps3/mmu_ps3.c311
-rw-r--r--sys/powerpc/ps3/platform_ps3.c262
-rw-r--r--sys/powerpc/ps3/ps3-hv-asm.awk52
-rw-r--r--sys/powerpc/ps3/ps3-hv-header.awk42
-rw-r--r--sys/powerpc/ps3/ps3-hvcall.S1254
-rw-r--r--sys/powerpc/ps3/ps3-hvcall.h110
-rw-r--r--sys/powerpc/ps3/ps3-hvcall.master111
-rw-r--r--sys/powerpc/ps3/ps3_syscons.c757
-rw-r--r--sys/powerpc/ps3/ps3bus.c561
-rw-r--r--sys/powerpc/ps3/ps3bus.h65
-rw-r--r--sys/powerpc/ps3/ps3pic.c254
36 files changed, 7432 insertions, 1 deletions
diff --git a/sys/boot/powerpc/Makefile b/sys/boot/powerpc/Makefile
index 80a68a6..d3652c4 100644
--- a/sys/boot/powerpc/Makefile
+++ b/sys/boot/powerpc/Makefile
@@ -1,5 +1,5 @@
# $FreeBSD$
-SUBDIR= boot1.chrp ofw uboot
+SUBDIR= boot1.chrp ofw ps3 uboot
.include <bsd.subdir.mk>
diff --git a/sys/boot/powerpc/ps3/Makefile b/sys/boot/powerpc/ps3/Makefile
new file mode 100644
index 0000000..b3a37be
--- /dev/null
+++ b/sys/boot/powerpc/ps3/Makefile
@@ -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>
diff --git a/sys/boot/powerpc/ps3/conf.c b/sys/boot/powerpc/ps3/conf.c
new file mode 100644
index 0000000..7749a15
--- /dev/null
+++ b/sys/boot/powerpc/ps3/conf.c
@@ -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;
diff --git a/sys/boot/powerpc/ps3/devicename.c b/sys/boot/powerpc/ps3/devicename.c
new file mode 100644
index 0000000..b652d9b
--- /dev/null
+++ b/sys/boot/powerpc/ps3/devicename.c
@@ -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);
+}
diff --git a/sys/boot/powerpc/ps3/help.ps3 b/sys/boot/powerpc/ps3/help.ps3
new file mode 100644
index 0000000..5873eb0
--- /dev/null
+++ b/sys/boot/powerpc/ps3/help.ps3
@@ -0,0 +1 @@
+$FreeBSD$
diff --git a/sys/boot/powerpc/ps3/ldscript.powerpc b/sys/boot/powerpc/ps3/ldscript.powerpc
new file mode 100644
index 0000000..3400a85
--- /dev/null
+++ b/sys/boot/powerpc/ps3/ldscript.powerpc
@@ -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 = .);
+}
+
diff --git a/sys/boot/powerpc/ps3/lv1call.S b/sys/boot/powerpc/ps3/lv1call.S
new file mode 100644
index 0000000..a5dafdd
--- /dev/null
+++ b/sys/boot/powerpc/ps3/lv1call.S
@@ -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
+
diff --git a/sys/boot/powerpc/ps3/lv1call.h b/sys/boot/powerpc/ps3/lv1call.h
new file mode 100644
index 0000000..26e6e14
--- /dev/null
+++ b/sys/boot/powerpc/ps3/lv1call.h
@@ -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
+
diff --git a/sys/boot/powerpc/ps3/main.c b/sys/boot/powerpc/ps3/main.c
new file mode 100644
index 0000000..22c51fa
--- /dev/null
+++ b/sys/boot/powerpc/ps3/main.c
@@ -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);
+}
+
diff --git a/sys/boot/powerpc/ps3/metadata.c b/sys/boot/powerpc/ps3/metadata.c
new file mode 100644
index 0000000..0698cd1
--- /dev/null
+++ b/sys/boot/powerpc/ps3/metadata.c
@@ -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));
+}
+
diff --git a/sys/boot/powerpc/ps3/ppc64_elf_freebsd.c b/sys/boot/powerpc/ps3/ppc64_elf_freebsd.c
new file mode 100644
index 0000000..2892448
--- /dev/null
+++ b/sys/boot/powerpc/ps3/ppc64_elf_freebsd.c
@@ -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
+};
diff --git a/sys/boot/powerpc/ps3/ps3.h b/sys/boot/powerpc/ps3/ps3.h
new file mode 100644
index 0000000..1a77002
--- /dev/null
+++ b/sys/boot/powerpc/ps3/ps3.h
@@ -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
diff --git a/sys/boot/powerpc/ps3/ps3cons.c b/sys/boot/powerpc/ps3/ps3cons.c
new file mode 100644
index 0000000..fa9ef32
--- /dev/null
+++ b/sys/boot/powerpc/ps3/ps3cons.c
@@ -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);
+}
+
diff --git a/sys/boot/powerpc/ps3/ps3mmu.c b/sys/boot/powerpc/ps3/ps3mmu.c
new file mode 100644
index 0000000..a7005fb
--- /dev/null
+++ b/sys/boot/powerpc/ps3/ps3mmu.c
@@ -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);
+}
+
diff --git a/sys/boot/powerpc/ps3/ps3net.c b/sys/boot/powerpc/ps3/ps3net.c
new file mode 100644
index 0000000..142eab8
--- /dev/null
+++ b/sys/boot/powerpc/ps3/ps3net.c
@@ -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);
+}
+
diff --git a/sys/boot/powerpc/ps3/start.S b/sys/boot/powerpc/ps3/start.S
new file mode 100644
index 0000000..92b02f0
--- /dev/null
+++ b/sys/boot/powerpc/ps3/start.S
@@ -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
+
diff --git a/sys/boot/powerpc/ps3/version b/sys/boot/powerpc/ps3/version
new file mode 100644
index 0000000..fa0b185
--- /dev/null
+++ b/sys/boot/powerpc/ps3/version
@@ -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.
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc
index 4856715..b6d7e6e 100644
--- a/sys/conf/files.powerpc
+++ b/sys/conf/files.powerpc
@@ -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
diff --git a/sys/conf/options.powerpc b/sys/conf/options.powerpc
index b7b3dbb..7e3358e 100644
--- a/sys/conf/options.powerpc
+++ b/sys/conf/options.powerpc
@@ -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
diff --git a/sys/powerpc/conf/GENERIC64 b/sys/powerpc/conf/GENERIC64
index b18dee9..b423702 100644
--- a/sys/powerpc/conf/GENERIC64
+++ b/sys/powerpc/conf/GENERIC64
@@ -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
diff --git a/sys/powerpc/conf/NOTES b/sys/powerpc/conf/NOTES
index a9a620a..9cd156a 100644
--- a/sys/powerpc/conf/NOTES
+++ b/sys/powerpc/conf/NOTES
@@ -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
diff --git a/sys/powerpc/include/pte.h b/sys/powerpc/include/pte.h
index 8b9dd4e..67cd5fd 100644
--- a/sys/powerpc/include/pte.h
+++ b/sys/powerpc/include/pte.h
@@ -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
diff --git a/sys/powerpc/ps3/ehci_ps3.c b/sys/powerpc/ps3/ehci_ps3.c
new file mode 100644
index 0000000..7808709
--- /dev/null
+++ b/sys/powerpc/ps3/ehci_ps3.c
@@ -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);
+
diff --git a/sys/powerpc/ps3/if_glc.c b/sys/powerpc/ps3/if_glc.c
new file mode 100644
index 0000000..6901f44
--- /dev/null
+++ b/sys/powerpc/ps3/if_glc.c
@@ -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);
+}
+
diff --git a/sys/powerpc/ps3/if_glcreg.h b/sys/powerpc/ps3/if_glcreg.h
new file mode 100644
index 0000000..c56363c
--- /dev/null
+++ b/sys/powerpc/ps3/if_glcreg.h
@@ -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 */
+
diff --git a/sys/powerpc/ps3/mmu_ps3.c b/sys/powerpc/ps3/mmu_ps3.c
new file mode 100644
index 0000000..5a7fe93
--- /dev/null
+++ b/sys/powerpc/ps3/mmu_ps3.c
@@ -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));
+}
+
diff --git a/sys/powerpc/ps3/platform_ps3.c b/sys/powerpc/ps3/platform_ps3.c
new file mode 100644
index 0000000..cb22cc3
--- /dev/null
+++ b/sys/powerpc/ps3/platform_ps3.c
@@ -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;
+}
+
diff --git a/sys/powerpc/ps3/ps3-hv-asm.awk b/sys/powerpc/ps3/ps3-hv-asm.awk
new file mode 100644
index 0000000..21241a8
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hv-asm.awk
@@ -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");
+}
diff --git a/sys/powerpc/ps3/ps3-hv-header.awk b/sys/powerpc/ps3/ps3-hv-header.awk
new file mode 100644
index 0000000..d00571a
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hv-header.awk
@@ -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");
+}
+
diff --git a/sys/powerpc/ps3/ps3-hvcall.S b/sys/powerpc/ps3/ps3-hvcall.S
new file mode 100644
index 0000000..135c601
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hvcall.S
@@ -0,0 +1,1254 @@
+/* $FreeBSD$ */
+
+#include <machine/asm.h>
+
+#define hc .long 0x44000022
+
+ASENTRY(lv1_allocate_memory)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r7,48(%r1)
+ std %r8,56(%r1)
+ li %r11,0
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_write_htab_entry)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,1
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_construct_virtual_address_space)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r6,48(%r1)
+ std %r7,56(%r1)
+ li %r11,2
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_virtual_address_space_id_of_ppe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r4,48(%r1)
+ li %r11,4
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_query_logical_partition_address_region_info)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-88(%r1)
+ std %r4,48(%r1)
+ std %r5,56(%r1)
+ std %r6,64(%r1)
+ std %r7,72(%r1)
+ std %r8,80(%r1)
+ li %r11,6
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r11,64(%r1)
+ std %r6,0(%r11)
+ ld %r11,72(%r1)
+ std %r7,0(%r11)
+ ld %r11,80(%r1)
+ std %r8,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_select_virtual_address_space)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,7
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_pause)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,9
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_destruct_virtual_address_space)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,10
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_configure_irq_state_bitmap)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,11
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_connect_irq_plug_ext)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,12
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_release_memory)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,13
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_put_iopte)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,15
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_disconnect_irq_plug_ext)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,17
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_construct_event_receive_port)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r3,48(%r1)
+ li %r11,18
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_destruct_event_receive_port)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,19
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_send_event_locally)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,24
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_end_of_interrupt)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,27
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_connect_irq_plug)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,28
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_disconnect_irq_plus)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,29
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_end_of_interrupt_ext)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,30
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_did_update_interrupt_mask)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,31
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_shutdown_logical_partition)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,44
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_destruct_logical_spe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,54
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_construct_logical_spe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-96(%r1)
+ std %r10,48(%r1)
+ ld %r11,208(%r1)
+ std %r11,56(%r1)
+ ld %r11,216(%r1)
+ std %r11,64(%r1)
+ ld %r11,224(%r1)
+ std %r11,72(%r1)
+ ld %r11,232(%r1)
+ std %r11,80(%r1)
+ ld %r11,240(%r1)
+ std %r11,88(%r1)
+ li %r11,57
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r11,64(%r1)
+ std %r6,0(%r11)
+ ld %r11,72(%r1)
+ std %r7,0(%r11)
+ ld %r11,80(%r1)
+ std %r8,0(%r11)
+ ld %r11,88(%r1)
+ std %r9,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_set_spe_interrupt_mask)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,61
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_disable_logical_spe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,65
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_clear_spe_interrupt_status)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,66
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_spe_interrupt_status)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r5,48(%r1)
+ li %r11,67
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_logical_ppe_id)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r3,48(%r1)
+ li %r11,69
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_logical_partition_id)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r3,48(%r1)
+ li %r11,74
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_spe_irq_outlet)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r5,48(%r1)
+ li %r11,78
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_set_spe_privilege_state_area_1_register)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,79
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_repository_node_value)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r8,48(%r1)
+ std %r9,56(%r1)
+ li %r11,91
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_read_htab_entries)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-88(%r1)
+ std %r5,48(%r1)
+ std %r6,56(%r1)
+ std %r7,64(%r1)
+ std %r8,72(%r1)
+ std %r9,80(%r1)
+ li %r11,95
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r11,64(%r1)
+ std %r6,0(%r11)
+ ld %r11,72(%r1)
+ std %r7,0(%r11)
+ ld %r11,80(%r1)
+ std %r8,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_set_dabr)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,96
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_allocate_io_segment)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r6,48(%r1)
+ li %r11,116
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_release_io_segment)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,117
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_construct_io_irq_outlet)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r4,48(%r1)
+ li %r11,120
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_destruct_io_irq_outlet)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,121
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_map_htab)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r4,48(%r1)
+ li %r11,122
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_unmap_htab)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,123
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_version_info)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r3,48(%r1)
+ li %r11,127
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_insert_htab_entry)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-72(%r1)
+ std %r9,48(%r1)
+ std %r10,56(%r1)
+ ld %r11,184(%r1)
+ std %r11,64(%r1)
+ li %r11,158
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r11,64(%r1)
+ std %r6,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_read_virtual_uart)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r6,48(%r1)
+ li %r11,162
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_write_virtual_uart)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r6,48(%r1)
+ li %r11,163
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_set_virtual_uart_param)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,164
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_virtual_uart_param)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r5,48(%r1)
+ li %r11,165
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_configure_virtual_uart)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r4,48(%r1)
+ li %r11,166
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_open_device)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,170
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_close_device)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,171
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_map_device_mmio_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r8,48(%r1)
+ li %r11,172
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_unmap_device_mmio_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,173
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_allocate_device_dma_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r8,48(%r1)
+ li %r11,174
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_free_device_dma_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,175
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_map_device_dma_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,176
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_unmap_device_dma_region)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,177
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_read_pci_config)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r9,48(%r1)
+ li %r11,178
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_write_pci_config)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,179
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_add_multicast_address)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,185
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_remove_multicast_address)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,186
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_start_tx_dma)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,187
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_stop_tx_dma)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,188
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_start_rx_dma)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,189
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_stop_rx_dma)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,190
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_set_interrupt_status_indicator)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,191
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_set_interrupt_mask)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,193
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_net_control)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r9,48(%r1)
+ std %r10,56(%r1)
+ li %r11,194
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_connect_interrupt_event_receive_port)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,197
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_disconnect_interrupt_event_receive_port)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,198
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_deconfigure_virtual_uart_irq)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,202
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_enable_logical_spe)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,207
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_open)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,210
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_close)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,211
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_device_map)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r4,48(%r1)
+ std %r5,56(%r1)
+ li %r11,212
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_device_unmap)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,213
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_memory_allocate)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r8,48(%r1)
+ std %r9,56(%r1)
+ li %r11,214
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_memory_free)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,216
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_context_allocate)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r5,48(%r1)
+ li %r11,217
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_context_free)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,218
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_context_iomap)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,221
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_context_attribute)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,225
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_gpu_context_intr)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r4,48(%r1)
+ li %r11,227
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_get_rtc)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r3,48(%r1)
+ std %r4,56(%r1)
+ li %r11,232
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_storage_read)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r9,48(%r1)
+ li %r11,245
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_storage_write)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r9,48(%r1)
+ li %r11,246
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_storage_send_device_command)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r9,48(%r1)
+ li %r11,248
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_storage_get_async_status)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r4,48(%r1)
+ std %r5,56(%r1)
+ li %r11,249
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r11,56(%r1)
+ std %r5,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_storage_check_async_status)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-56(%r1)
+ std %r5,48(%r1)
+ li %r11,254
+ hc
+ extsw %r3,%r3
+ ld %r11,48(%r1)
+ std %r4,0(%r11)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
+ASENTRY(lv1_panic)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-48(%r1)
+ li %r11,255
+ hc
+ extsw %r3,%r3
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr
+
diff --git a/sys/powerpc/ps3/ps3-hvcall.h b/sys/powerpc/ps3/ps3-hvcall.h
new file mode 100644
index 0000000..912158b
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hvcall.h
@@ -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);
diff --git a/sys/powerpc/ps3/ps3-hvcall.master b/sys/powerpc/ps3/ps3-hvcall.master
new file mode 100644
index 0000000..d53a32e
--- /dev/null
+++ b/sys/powerpc/ps3/ps3-hvcall.master
@@ -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
diff --git a/sys/powerpc/ps3/ps3_syscons.c b/sys/powerpc/ps3/ps3_syscons.c
new file mode 100644
index 0000000..e0e9cc8
--- /dev/null
+++ b/sys/powerpc/ps3/ps3_syscons.c
@@ -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);
+
diff --git a/sys/powerpc/ps3/ps3bus.c b/sys/powerpc/ps3/ps3bus.c
new file mode 100644
index 0000000..11dbba5
--- /dev/null
+++ b/sys/powerpc/ps3/ps3bus.c
@@ -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);
+}
+
diff --git a/sys/powerpc/ps3/ps3bus.h b/sys/powerpc/ps3/ps3bus.h
new file mode 100644
index 0000000..b11ff8a
--- /dev/null
+++ b/sys/powerpc/ps3/ps3bus.h
@@ -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 */
diff --git a/sys/powerpc/ps3/ps3pic.c b/sys/powerpc/ps3/ps3pic.c
new file mode 100644
index 0000000..5d56d3e
--- /dev/null
+++ b/sys/powerpc/ps3/ps3pic.c
@@ -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);
+}
+
OpenPOWER on IntegriCloud