diff options
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"), ®_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); +} + |