aboutsummaryrefslogtreecommitdiffstats
path: root/roms/SLOF/lib
diff options
context:
space:
mode:
Diffstat (limited to 'roms/SLOF/lib')
-rw-r--r--roms/SLOF/lib/Makefile35
-rw-r--r--roms/SLOF/lib/libbases/Makefile40
-rw-r--r--roms/SLOF/lib/libbases/libbases.code43
-rw-r--r--roms/SLOF/lib/libbases/libbases.in17
-rw-r--r--roms/SLOF/lib/libbcm/Makefile53
-rw-r--r--roms/SLOF/lib/libbcm/bcm.code57
-rw-r--r--roms/SLOF/lib/libbcm/bcm.in20
-rw-r--r--roms/SLOF/lib/libbcm/bcm57xx.c3461
-rw-r--r--roms/SLOF/lib/libbcm/bcm57xx.h323
-rw-r--r--roms/SLOF/lib/libbootmenu/Makefile49
-rw-r--r--roms/SLOF/lib/libbootmenu/bootmenu.c187
-rw-r--r--roms/SLOF/lib/libbootmenu/bootmenu.code20
-rw-r--r--roms/SLOF/lib/libbootmenu/bootmenu.h15
-rw-r--r--roms/SLOF/lib/libbootmenu/bootmenu.in15
-rw-r--r--roms/SLOF/lib/libbootmsg/Makefile75
-rw-r--r--roms/SLOF/lib/libbootmsg/bootmsg.code61
-rw-r--r--roms/SLOF/lib/libbootmsg/bootmsg.in19
-rw-r--r--roms/SLOF/lib/libbootmsg/bootmsg_lvl.S202
-rw-r--r--roms/SLOF/lib/libbootmsg/libbootmsg.h21
-rw-r--r--roms/SLOF/lib/libc/Makefile61
-rw-r--r--roms/SLOF/lib/libc/README.txt49
-rw-r--r--roms/SLOF/lib/libc/ctype/Makefile.inc20
-rw-r--r--roms/SLOF/lib/libc/ctype/isdigit.c25
-rw-r--r--roms/SLOF/lib/libc/ctype/isprint.c18
-rw-r--r--roms/SLOF/lib/libc/ctype/isspace.c29
-rw-r--r--roms/SLOF/lib/libc/ctype/isxdigit.c21
-rw-r--r--roms/SLOF/lib/libc/ctype/tolower.c18
-rw-r--r--roms/SLOF/lib/libc/ctype/toupper.c21
-rw-r--r--roms/SLOF/lib/libc/getopt/Makefile.inc17
-rw-r--r--roms/SLOF/lib/libc/getopt/getopt.c470
-rw-r--r--roms/SLOF/lib/libc/include/assert.h36
-rw-r--r--roms/SLOF/lib/libc/include/ctype.h24
-rw-r--r--roms/SLOF/lib/libc/include/errno.h34
-rw-r--r--roms/SLOF/lib/libc/include/getopt.h37
-rw-r--r--roms/SLOF/lib/libc/include/limits.h32
-rw-r--r--roms/SLOF/lib/libc/include/stdarg.h22
-rw-r--r--roms/SLOF/lib/libc/include/stdbool.h20
-rw-r--r--roms/SLOF/lib/libc/include/stddef.h24
-rw-r--r--roms/SLOF/lib/libc/include/stdint.h28
-rw-r--r--roms/SLOF/lib/libc/include/stdio.h64
-rw-r--r--roms/SLOF/lib/libc/include/stdlib.h34
-rw-r--r--roms/SLOF/lib/libc/include/string.h37
-rw-r--r--roms/SLOF/lib/libc/include/sys/socket.h53
-rw-r--r--roms/SLOF/lib/libc/include/unistd.h28
-rw-r--r--roms/SLOF/lib/libc/stdio/Makefile.inc23
-rw-r--r--roms/SLOF/lib/libc/stdio/fileno.c19
-rw-r--r--roms/SLOF/lib/libc/stdio/fprintf.c26
-rw-r--r--roms/SLOF/lib/libc/stdio/fscanf.c26
-rw-r--r--roms/SLOF/lib/libc/stdio/printf.c27
-rw-r--r--roms/SLOF/lib/libc/stdio/putc.c25
-rw-r--r--roms/SLOF/lib/libc/stdio/putchar.c21
-rw-r--r--roms/SLOF/lib/libc/stdio/puts.c28
-rw-r--r--roms/SLOF/lib/libc/stdio/scanf.c26
-rw-r--r--roms/SLOF/lib/libc/stdio/setvbuf.c28
-rw-r--r--roms/SLOF/lib/libc/stdio/snprintf.c28
-rw-r--r--roms/SLOF/lib/libc/stdio/sprintf.c30
-rw-r--r--roms/SLOF/lib/libc/stdio/stdchnls.c23
-rw-r--r--roms/SLOF/lib/libc/stdio/vfprintf.c27
-rw-r--r--roms/SLOF/lib/libc/stdio/vfscanf.c266
-rw-r--r--roms/SLOF/lib/libc/stdio/vsnprintf.c299
-rw-r--r--roms/SLOF/lib/libc/stdio/vsprintf.c19
-rw-r--r--roms/SLOF/lib/libc/stdio/vsscanf.c131
-rw-r--r--roms/SLOF/lib/libc/stdlib/Makefile.inc22
-rw-r--r--roms/SLOF/lib/libc/stdlib/atoi.c18
-rw-r--r--roms/SLOF/lib/libc/stdlib/atol.c18
-rw-r--r--roms/SLOF/lib/libc/stdlib/error.c15
-rw-r--r--roms/SLOF/lib/libc/stdlib/free.c28
-rw-r--r--roms/SLOF/lib/libc/stdlib/malloc.c157
-rw-r--r--roms/SLOF/lib/libc/stdlib/malloc_defs.h16
-rw-r--r--roms/SLOF/lib/libc/stdlib/memalign.c26
-rw-r--r--roms/SLOF/lib/libc/stdlib/rand.c29
-rw-r--r--roms/SLOF/lib/libc/stdlib/realloc.c40
-rw-r--r--roms/SLOF/lib/libc/stdlib/strtol.c115
-rw-r--r--roms/SLOF/lib/libc/stdlib/strtoul.c105
-rw-r--r--roms/SLOF/lib/libc/string/Makefile.inc22
-rw-r--r--roms/SLOF/lib/libc/string/memchr.c29
-rw-r--r--roms/SLOF/lib/libc/string/memcmp.c30
-rw-r--r--roms/SLOF/lib/libc/string/memcpy.c27
-rw-r--r--roms/SLOF/lib/libc/string/memmove.c42
-rw-r--r--roms/SLOF/lib/libc/string/memset.c25
-rw-r--r--roms/SLOF/lib/libc/string/strcasecmp.c28
-rw-r--r--roms/SLOF/lib/libc/string/strcat.c24
-rw-r--r--roms/SLOF/lib/libc/string/strchr.c28
-rw-r--r--roms/SLOF/lib/libc/string/strcmp.c28
-rw-r--r--roms/SLOF/lib/libc/string/strcpy.c25
-rw-r--r--roms/SLOF/lib/libc/string/strlen.c27
-rw-r--r--roms/SLOF/lib/libc/string/strncasecmp.c32
-rw-r--r--roms/SLOF/lib/libc/string/strncmp.c31
-rw-r--r--roms/SLOF/lib/libc/string/strncpy.c33
-rw-r--r--roms/SLOF/lib/libc/string/strrchr.c28
-rw-r--r--roms/SLOF/lib/libc/string/strstr.c37
-rw-r--r--roms/SLOF/lib/libc/string/strtok.c45
-rw-r--r--roms/SLOF/lib/libe1k/Makefile51
-rw-r--r--roms/SLOF/lib/libe1k/e1k.c1000
-rw-r--r--roms/SLOF/lib/libe1k/e1k.code75
-rw-r--r--roms/SLOF/lib/libe1k/e1k.h108
-rw-r--r--roms/SLOF/lib/libe1k/e1k.in21
-rw-r--r--roms/SLOF/lib/libelf/Makefile47
-rw-r--r--roms/SLOF/lib/libelf/elf.c224
-rw-r--r--roms/SLOF/lib/libelf/elf32.c262
-rw-r--r--roms/SLOF/lib/libelf/elf64.c531
-rw-r--r--roms/SLOF/lib/libelf/elf_claim.c28
-rw-r--r--roms/SLOF/lib/libelf/libelf.code43
-rw-r--r--roms/SLOF/lib/libelf/libelf.in18
-rw-r--r--roms/SLOF/lib/libhvcall/Makefile57
-rw-r--r--roms/SLOF/lib/libhvcall/brokensc1.c162
-rw-r--r--roms/SLOF/lib/libhvcall/hvcall.S155
-rw-r--r--roms/SLOF/lib/libhvcall/hvcall.code144
-rw-r--r--roms/SLOF/lib/libhvcall/hvcall.in35
-rw-r--r--roms/SLOF/lib/libhvcall/libhvcall.h112
-rw-r--r--roms/SLOF/lib/libhvcall/rfill.c38
-rw-r--r--roms/SLOF/lib/libipmi/Makefile28
-rw-r--r--roms/SLOF/lib/libipmi/libipmi.code120
-rw-r--r--roms/SLOF/lib/libipmi/libipmi.h33
-rw-r--r--roms/SLOF/lib/libipmi/libipmi.in24
-rw-r--r--roms/SLOF/lib/libipmi/libipmi.ocobin0 -> 104462 bytes
-rw-r--r--roms/SLOF/lib/libnativeio/nativeio.code25
-rw-r--r--roms/SLOF/lib/libnativeio/nativeio.in22
-rw-r--r--roms/SLOF/lib/libnet/Makefile50
-rw-r--r--roms/SLOF/lib/libnet/args.c179
-rw-r--r--roms/SLOF/lib/libnet/args.h23
-rw-r--r--roms/SLOF/lib/libnet/bootp.c255
-rw-r--r--roms/SLOF/lib/libnet/dhcp.c991
-rw-r--r--roms/SLOF/lib/libnet/dhcp.h49
-rw-r--r--roms/SLOF/lib/libnet/dhcpv6.c211
-rw-r--r--roms/SLOF/lib/libnet/dhcpv6.h154
-rw-r--r--roms/SLOF/lib/libnet/dns.c526
-rw-r--r--roms/SLOF/lib/libnet/dns.h28
-rw-r--r--roms/SLOF/lib/libnet/ethernet.c189
-rw-r--r--roms/SLOF/lib/libnet/ethernet.h50
-rw-r--r--roms/SLOF/lib/libnet/icmpv6.c407
-rw-r--r--roms/SLOF/lib/libnet/icmpv6.h135
-rw-r--r--roms/SLOF/lib/libnet/ipv4.c898
-rw-r--r--roms/SLOF/lib/libnet/ipv4.h95
-rw-r--r--roms/SLOF/lib/libnet/ipv6.c768
-rw-r--r--roms/SLOF/lib/libnet/ipv6.h186
-rw-r--r--roms/SLOF/lib/libnet/libnet.code16
-rw-r--r--roms/SLOF/lib/libnet/libnet.in3
-rw-r--r--roms/SLOF/lib/libnet/ndp.c184
-rw-r--r--roms/SLOF/lib/libnet/ndp.h72
-rw-r--r--roms/SLOF/lib/libnet/netapps.h26
-rw-r--r--roms/SLOF/lib/libnet/netload.c804
-rw-r--r--roms/SLOF/lib/libnet/ping.c225
-rw-r--r--roms/SLOF/lib/libnet/pxelinux.c252
-rw-r--r--roms/SLOF/lib/libnet/pxelinux.h33
-rw-r--r--roms/SLOF/lib/libnet/tcp.c46
-rw-r--r--roms/SLOF/lib/libnet/tcp.h27
-rw-r--r--roms/SLOF/lib/libnet/tftp.c793
-rw-r--r--roms/SLOF/lib/libnet/tftp.h54
-rw-r--r--roms/SLOF/lib/libnet/time.h6
-rw-r--r--roms/SLOF/lib/libnet/udp.c115
-rw-r--r--roms/SLOF/lib/libnet/udp.h53
-rw-r--r--roms/SLOF/lib/libnvram/Makefile53
-rw-r--r--roms/SLOF/lib/libnvram/envvar.c242
-rw-r--r--roms/SLOF/lib/libnvram/libnvram.code274
-rw-r--r--roms/SLOF/lib/libnvram/libnvram.in42
-rw-r--r--roms/SLOF/lib/libnvram/nvram.c660
-rw-r--r--roms/SLOF/lib/libnvram/nvram.h75
-rw-r--r--roms/SLOF/lib/libtpm/Makefile51
-rw-r--r--roms/SLOF/lib/libtpm/Readme57
-rw-r--r--roms/SLOF/lib/libtpm/sha.c232
-rw-r--r--roms/SLOF/lib/libtpm/sha.h23
-rw-r--r--roms/SLOF/lib/libtpm/sha256.c246
-rw-r--r--roms/SLOF/lib/libtpm/sha512.c285
-rw-r--r--roms/SLOF/lib/libtpm/sha_test.h59
-rw-r--r--roms/SLOF/lib/libtpm/tcgbios.c1477
-rw-r--r--roms/SLOF/lib/libtpm/tcgbios.h45
-rw-r--r--roms/SLOF/lib/libtpm/tcgbios_int.h317
-rwxr-xr-xroms/SLOF/lib/libtpm/test.sh31
-rw-r--r--roms/SLOF/lib/libtpm/tpm.code208
-rw-r--r--roms/SLOF/lib/libtpm/tpm.in32
-rw-r--r--roms/SLOF/lib/libtpm/tpm_drivers.c436
-rw-r--r--roms/SLOF/lib/libtpm/tpm_drivers.h82
-rw-r--r--roms/SLOF/lib/libusb/Makefile52
-rw-r--r--roms/SLOF/lib/libusb/tools.h77
-rw-r--r--roms/SLOF/lib/libusb/usb-core.c559
-rw-r--r--roms/SLOF/lib/libusb/usb-core.h283
-rw-r--r--roms/SLOF/lib/libusb/usb-ehci.c612
-rw-r--r--roms/SLOF/lib/libusb/usb-ehci.h155
-rw-r--r--roms/SLOF/lib/libusb/usb-hid.c461
-rw-r--r--roms/SLOF/lib/libusb/usb-hub.c220
-rw-r--r--roms/SLOF/lib/libusb/usb-key.c446
-rw-r--r--roms/SLOF/lib/libusb/usb-key.h42
-rw-r--r--roms/SLOF/lib/libusb/usb-ohci.c1055
-rw-r--r--roms/SLOF/lib/libusb/usb-ohci.h217
-rw-r--r--roms/SLOF/lib/libusb/usb-slof.c93
-rw-r--r--roms/SLOF/lib/libusb/usb-xhci.c1553
-rw-r--r--roms/SLOF/lib/libusb/usb-xhci.h378
-rw-r--r--roms/SLOF/lib/libusb/usb.code162
-rw-r--r--roms/SLOF/lib/libusb/usb.h77
-rw-r--r--roms/SLOF/lib/libusb/usb.in29
-rw-r--r--roms/SLOF/lib/libveth/Makefile52
-rw-r--r--roms/SLOF/lib/libveth/veth.c277
-rw-r--r--roms/SLOF/lib/libveth/veth.code61
-rw-r--r--roms/SLOF/lib/libveth/veth.h24
-rw-r--r--roms/SLOF/lib/libveth/veth.in20
-rw-r--r--roms/SLOF/lib/libvirtio/Makefile55
-rw-r--r--roms/SLOF/lib/libvirtio/p9.c575
-rw-r--r--roms/SLOF/lib/libvirtio/p9.h68
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-9p.c340
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-9p.h32
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-blk.c209
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-blk.h61
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-internal.h48
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-net.c384
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-net.h40
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-scsi.c152
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-scsi.h69
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-serial.c202
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-serial.h27
-rw-r--r--roms/SLOF/lib/libvirtio/virtio.c645
-rw-r--r--roms/SLOF/lib/libvirtio/virtio.code203
-rw-r--r--roms/SLOF/lib/libvirtio/virtio.h132
-rw-r--r--roms/SLOF/lib/libvirtio/virtio.in41
214 files changed, 34518 insertions, 0 deletions
diff --git a/roms/SLOF/lib/Makefile b/roms/SLOF/lib/Makefile
new file mode 100644
index 000000000..736989495
--- /dev/null
+++ b/roms/SLOF/lib/Makefile
@@ -0,0 +1,35 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+SUBDIRS = libc libipmi libbootmsg libbases libnvram libelf libhvcall libvirtio \
+ libusb libveth libe1k libbcm libnet libbootmenu libtpm
+
+all: subdirs
+
+.PHONY : subdirs $(SUBDIRS) clean distclean
+
+
+subdirs: $(SUBDIRS)
+
+$(SUBDIRS):
+ $(MAKE) -C $@ FLAG=$(FLAG) $(MAKEARG)
+
+# Rules for making clean:
+clean:
+ @for dir in $(SUBDIRS); do \
+ $(MAKE) -C $$dir clean || exit 1; \
+ done
+
+distclean:
+ @for dir in $(SUBDIRS); do \
+ $(MAKE) -C $$dir distclean || exit 1; \
+ done
diff --git a/roms/SLOF/lib/libbases/Makefile b/roms/SLOF/lib/libbases/Makefile
new file mode 100644
index 000000000..add4ed18c
--- /dev/null
+++ b/roms/SLOF/lib/libbases/Makefile
@@ -0,0 +1,40 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) -I. -I../../include
+LDFLAGS = -nostdlib
+
+all:
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+
+
+# Include dependency file if available:
+-include Makefile.dep
+
diff --git a/roms/SLOF/lib/libbases/libbases.code b/roms/SLOF/lib/libbases/libbases.code
new file mode 100644
index 000000000..128b94ab2
--- /dev/null
+++ b/roms/SLOF/lib/libbases/libbases.code
@@ -0,0 +1,43 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+#include <southbridge.h>
+#include <nvram.h>
+
+// : get-nvram-base ( -- base )
+PRIM(get_X2d_nvram_X2d_base)
+ PUSH;
+ TOS.u = SB_NVRAM_adr;
+MIRP
+
+// : get-nvram-size ( -- size )
+PRIM(get_X2d_nvram_X2d_size)
+ PUSH;
+ TOS.u = get_nvram_size();
+MIRP
+
+// : get-flash-base ( -- base )
+PRIM(get_X2d_flash_X2d_base)
+ PUSH;
+ TOS.u = SB_FLASH_adr;
+MIRP
+
+// : get-flash-size ( -- size )
+PRIM(get_X2d_flash_X2d_size)
+ PUSH;
+ TOS.u = FLASH_LENGTH;
+MIRP
+
+// : get-mbx-base ( -- base )
+PRIM(get_X2d_mbx_X2d_base)
+ PUSH;
+ TOS.u = SB_MAILBOX_adr;
+MIRP
diff --git a/roms/SLOF/lib/libbases/libbases.in b/roms/SLOF/lib/libbases/libbases.in
new file mode 100644
index 000000000..844a55de1
--- /dev/null
+++ b/roms/SLOF/lib/libbases/libbases.in
@@ -0,0 +1,17 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+cod(get-nvram-base)
+cod(get-nvram-size)
+cod(get-flash-base)
+cod(get-flash-size)
+cod(get-mbx-base)
diff --git a/roms/SLOF/lib/libbcm/Makefile b/roms/SLOF/lib/libbcm/Makefile
new file mode 100644
index 000000000..1aead4c5e
--- /dev/null
+++ b/roms/SLOF/lib/libbcm/Makefile
@@ -0,0 +1,53 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008, 2013 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
+
+#CFLAGS += -O2 -I. -I../common -I$(TOP)/clients/net-snk/include -I$(TOP)/lib/libc/include -fno-builtin -ffreestanding -msoft-float -Wall -nostdinc
+
+LDFLAGS = -nostdlib
+
+TARGET = ../libbcm.a
+
+
+all: $(TARGET) Makefile.dep
+
+SRCS = bcm57xx.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libbcm/bcm.code b/roms/SLOF/lib/libbcm/bcm.code
new file mode 100644
index 000000000..308ebbaa2
--- /dev/null
+++ b/roms/SLOF/lib/libbcm/bcm.code
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * libbcm Forth wrapper
+ */
+
+#include <bcm57xx.h>
+
+// : bcm57xx-open ( -- false | [ driver true ] )
+PRIM(BCM57XX_X2d_OPEN)
+{
+ net_driver_t *net_driver = bcm57xx_open();
+ if (net_driver) {
+ PUSH;
+ TOS.u = (unsigned long)net_driver; PUSH;
+ TOS.n = -1;
+ } else {
+ PUSH;
+ TOS.n = 0;
+ }
+}
+MIRP
+
+// : bcm57xx-close ( driver -- )
+PRIM(BCM57XX_X2d_CLOSE)
+{
+ net_driver_t *driver = TOS.a; POP;
+ bcm57xx_close(driver);
+}
+MIRP
+
+
+// : bcm57xx-read ( addr len -- actual )
+PRIM(BCM57XX_X2d_READ)
+{
+ int len = TOS.u; POP;
+ TOS.n = bcm57xx_read(TOS.a, len);
+}
+MIRP
+
+// : bcm57xx-write ( addr len -- actual )
+PRIM(BCM57XX_X2d_WRITE)
+{
+ int len = TOS.u; POP;
+ TOS.n = bcm57xx_write(TOS.a, len);
+}
+MIRP
diff --git a/roms/SLOF/lib/libbcm/bcm.in b/roms/SLOF/lib/libbcm/bcm.in
new file mode 100644
index 000000000..ee50a6e65
--- /dev/null
+++ b/roms/SLOF/lib/libbcm/bcm.in
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * libbcm bindings for Forth - definitions
+ */
+
+cod(BCM57XX-OPEN)
+cod(BCM57XX-CLOSE)
+cod(BCM57XX-READ)
+cod(BCM57XX-WRITE)
diff --git a/roms/SLOF/lib/libbcm/bcm57xx.c b/roms/SLOF/lib/libbcm/bcm57xx.c
new file mode 100644
index 000000000..2ecb517f1
--- /dev/null
+++ b/roms/SLOF/lib/libbcm/bcm57xx.c
@@ -0,0 +1,3461 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ *
+ ******************************************************************************
+ * reference:
+ * Broadcom 57xx
+ * Host Programmer Interface Specification for the
+ * NetXtreme Family of Highly-Integrated Media Access Controlers
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <byteorder.h>
+#include <helpers.h>
+#include <netdriver.h>
+#include "bcm57xx.h"
+
+/*
+ * local defines
+ ******************************************************************************
+ */
+
+
+// #define BCM_VLAN_TAG ( (uint32_t) 0x1 )
+
+// number of tx/rx rings
+// NOTE: 5714 only uses 1 rx/tx ring, but memory
+// for the other rings is cleaned anyways for
+// sanity & future use
+#define BCM_MAX_TX_RING 16
+#define BCM_MAX_RXRET_RING 16
+#define BCM_MAX_RXPROD_RCB 3
+
+// bd descriptions
+#define BCM_RXPROD_RING_SIZE 512 // don't change
+#define BCM_RXRET_RING_SIZE 512 // don't change
+#define BCM_TX_RING_SIZE 512 // don't change
+#define BCM_BUF_SIZE 1536 // don't change
+#define BCM_MTU_MAX_LEN 1522
+#define BCM_MAX_RX_BUF 64
+#define BCM_MAX_TX_BUF 16
+
+// number of MAC addresses in NIC
+#define BCM_NUM_MAC_ADDR 4
+#define BCM_NUM_MAC5704_ADDR 12
+// offset of mac address field(s) in bcm register space
+#define MAC5704_ADDR_OFFS ( (uint16_t) 0x0530 )
+
+// offset of NIC memory start address from base address
+#define BCM_MEMORY_OFFS ( (uint64_t) 0x8000 )
+
+// offset of statistics block in NIC memory
+#define BCM_STATISTIC_OFFS ( (uint64_t) 0x0300 )
+// size of statistic block in NIC memory
+#define BCM_STATISTIC_SIZE 0x800
+
+// offsets of NIC rx/tx rings in NIC memory
+#define BCM_NIC_TX_OFFS ( (uint16_t) 0x4000 )
+#define BCM_NIC_RX_OFFS ( (uint16_t) 0x6000 )
+#define BCM_NIC_TX_SIZE ( (uint16_t) ( ( BCM_TX_RING_SIZE * BCM_RCB_SIZE_u16 ) / 4 ) )
+
+// device mailboxes
+#define BCM_FW_MBX ( (uint16_t) 0x0b50 )
+#define BCM_FW_MBX_CMD ( (uint16_t) 0x0b78 )
+#define BCM_FW_MBX_LEN ( (uint16_t) 0x0b7c )
+#define BCM_FW_MBX_DATA ( (uint16_t) 0x0b80 )
+#define BCM_NICDRV_STATE_MBX ( (uint16_t) 0x0c04 )
+
+// device mailbox commands
+#define BCM_NICDRV_ALIVE ( (uint32_t) 0x00000001 )
+#define BCM_NICDRV_PAUSE_FW ( (uint32_t) 0x00000002 )
+
+// device values
+#define BCM_MAGIC_NUMBER ( (uint32_t) 0x4b657654 )
+
+// device states
+#define NIC_FWDRV_STATE_START ( (uint32_t) 0x00000001 )
+#define NIC_FWDRV_STATE_START_DONE ( (uint32_t) 0x80000001 )
+#define NIC_FWDRV_STATE_UNLOAD ( (uint32_t) 0x00000002 )
+#define NIC_FWDRV_STATE_UNLOAD_DONE ( (uint32_t) 0x80000002 )
+#define NIC_FWDRV_STATE_SUSPEND ( (uint32_t) 0x00000004 )
+
+// timer prescaler value
+#define BCM_TMR_PRESCALE ( (uint32_t) 0x41 )
+
+// offset of transmit rcb's in NIC memory
+#define BCM_TX_RCB_OFFS ( (uint16_t) 0x0100 )
+// offset of receive return rcb's in NIC memory
+#define BCM_RXRET_RCB_OFFS ( (uint16_t) 0x0200 )
+
+// register offsets for ring indices
+#define TX_PROD_IND ( (uint16_t) 0x0304 )
+#define TX_CONS_IND ( (uint16_t) 0x3cc0 )
+#define RXPROD_PROD_IND ( (uint16_t) 0x026c )
+#define RXPROD_CONS_IND ( (uint16_t) 0x3c54 )
+#define RXRET_PROD_IND ( (uint16_t) 0x3c80 )
+#define RXRET_CONS_IND ( (uint16_t) 0x0284 )
+// NIC producer index only needed for initialization
+#define TX_NIC_PROD_IND ( (uint16_t) 0x0384 )
+
+/*
+ * predefined register values used during initialization
+ * may be adapted by user
+ */
+#define DMA_RW_CTRL_VAL_5714 ( (uint32_t) 0x76144000 )
+#define DMA_RW_CTRL_VAL ( (uint32_t) 0x760F0000 )
+#define TX_MAC_LEN_VAL ( (uint32_t) 0x00002620 )
+
+#define RX_LST_PLC_CFG_VAL ( (uint32_t) 0x00000109 )
+#define RX_LST_PLC_STAT_EN_VAL ( (uint32_t) 0x007e000f )
+#define NVM_ADDR_MSK ( (uint32_t) 0x000fffff )
+
+// Number of Receive Rules /w or /wo SOL enabled
+#define RX_RULE_CFG_VAL ( (uint32_t) 0x00000008 )
+#define NUM_RX_RULE ( (uint32_t) 16 )
+#define NUM_RX_RULE_ASF ( (uint32_t) ( NUM_RX_RULE - 4 ) )
+
+// RCB register offsets
+#define BCM_RXPROD_RCB_JUM ( (uint16_t) 0x2440 )
+#define BCM_RXPROD_RCB_STD ( (uint16_t) 0x2450 )
+#define BCM_RXPROD_RCB_MIN ( (uint16_t) 0x2460 )
+
+// macros needed for new addressing method
+#define BCM_RCB_HOSTADDR_HI_u16( rcb ) ( (uint16_t) rcb + 0x00 )
+#define BCM_RCB_HOSTADDR_LOW_u16( rcb ) ( (uint16_t) rcb + 0x04 )
+#define BCM_RCB_LENFLAG_u16( rcb ) ( (uint16_t) rcb + 0x08 )
+#define BCM_RCB_NICADDR_u16( rcb ) ( (uint16_t) rcb + 0x0c )
+#define BCM_RCB_SIZE_u16 ( (uint16_t) 0x0010 )
+
+// RCB flags
+#define RCB_FLAG_RING_DISABLED BIT32( 1 )
+
+// BCM device ID masks
+#define BCM_DEV_5714 ( (uint64_t) 0x1 )
+#define BCM_DEV_5704 ( (uint64_t) 0x2 )
+#define BCM_DEV_5703 ( (uint64_t) 0x4 )
+#define BCM_DEV_SERDES ( (uint64_t) 0x80000000 )
+#define BCM_DEV_COPPER ( (uint64_t) 0x40000000 )
+
+#define IS_5714 ( ( bcm_device_u64 & BCM_DEV_5714 ) != 0 )
+#define IS_5704 ( ( bcm_device_u64 & BCM_DEV_5704 ) != 0 )
+#define IS_5703 ( ( bcm_device_u64 & BCM_DEV_5703 ) != 0 )
+#define IS_SERDES ( ( bcm_device_u64 & BCM_DEV_SERDES ) != 0 )
+#define IS_COPPER_PHY ( ( bcm_device_u64 & BCM_DEV_COPPER ) != 0 )
+
+#define BUFFERED_FLASH_PAGE_POS 9
+#define BUFFERED_FLASH_BYTE_ADDR_MASK ((<<BUFFERED_FLASH_PAGE_POS) - 1)
+#define BUFFERED_FLASH_PAGE_SIZE 264
+#define BUFFERED_FLASH_PHY_SIZE 512
+#define MANUFACTURING_INFO_SIZE 140
+#define CRC32_POLYNOMIAL 0xEDB88320
+
+/*
+ * local types
+ ******************************************************************************
+ */
+typedef struct {
+ uint32_t m_dev_u32;
+ uint64_t m_devmsk_u64;
+} bcm_dev_t;
+
+/*
+ * BCM common data structures
+ * BCM57xx Programmer's Guide: Section 5
+ */
+
+/*
+ * 64bit host address in a way the NIC is able to understand it
+ */
+typedef struct {
+ uint32_t m_hi_u32;
+ uint32_t m_lo_u32;
+} bcm_addr64_t;
+/*
+ * ring control block
+ */
+typedef struct {
+ bcm_addr64_t m_hostaddr_st;
+ uint32_t m_lenflags_u32; // upper 16b: len, lower 16b: flags
+ uint32_t m_nicaddr_u32;
+} bcm_rcb_t;
+
+/*
+ * tx buffer descriptor
+ */
+typedef struct {
+ bcm_addr64_t m_hostaddr_st;
+ uint32_t m_lenflags_u32; // upper 16b: len, lower 16b: flags
+ uint32_t m_VLANtag_u32; // lower 16b: vtag
+} bcm_txbd_t;
+
+/*
+ * rx buffer descriptor
+ */
+typedef struct {
+ bcm_addr64_t m_hostaddr_st;
+ uint32_t m_idxlen_u32; // upper 16b: idx, lower 16b: len
+ uint32_t m_typeflags_u32; // upper 16b: type, lower 16b: flags
+ uint32_t m_chksum_u32; // upper 16b: ip, lower 16b: tcp/udp
+ uint32_t m_errvlan_u32; // upper 16b: err, lower 16b: vlan tag
+ uint32_t m_reserved_u32;
+ uint32_t m_opaque_u32;
+} bcm_rxbd_t;
+
+/*
+ * bcm status block
+ * NOTE: in fact the status block is not used and configured
+ * so that it is not updated by the NIC. Still it has to be
+ * set up so the NIC is satisfied
+ */
+typedef struct {
+ uint32_t m_st_word_u32;
+ uint32_t m_st_tag_u32;
+ uint16_t m_rxprod_cons_u16;
+ uint16_t m_unused_u16;
+ uint32_t m_unused_u32;
+ uint16_t m_tx_cons_u16;
+ uint16_t m_rxret_prod_u16;
+} bcm_status_t;
+
+/*
+ * local constants
+ ******************************************************************************
+ */
+static const bcm_dev_t bcm_dev[] = {
+ { 0x166b, BCM_DEV_5714 },
+ { 0x1668, BCM_DEV_5714 },
+ { 0x1669, BCM_DEV_5714 },
+ { 0x166a, BCM_DEV_5714 },
+ { 0x1648, BCM_DEV_5704 },
+ { 0x1649, BCM_DEV_5704 | BCM_DEV_SERDES },
+ { 0x16a8, BCM_DEV_5704 | BCM_DEV_SERDES },
+ { 0x16a7, BCM_DEV_5703 | BCM_DEV_SERDES },
+ { 0x16c7, BCM_DEV_5703 | BCM_DEV_SERDES },
+ { 0 , 0 }
+};
+
+/*
+ * local variables
+ ******************************************************************************
+ */
+static uint64_t bcm_device_u64;
+static uint32_t bcm_rxret_ring_sz;
+static uint64_t bcm_baseaddr_u64;
+static uint64_t bcm_memaddr_u64;
+
+/*
+ * rings & their buffers
+ */
+// the rings made of buffer descriptors
+static bcm_txbd_t bcm_tx_ring[BCM_TX_RING_SIZE];
+static bcm_rxbd_t bcm_rxprod_ring[BCM_RXPROD_RING_SIZE];
+static bcm_rxbd_t bcm_rxret_ring[BCM_RXRET_RING_SIZE*2];
+
+// the buffers used in the rings
+static uint8_t bcm_tx_buffer_pu08[BCM_MAX_TX_BUF][BCM_BUF_SIZE];
+static uint8_t bcm_rx_buffer_pu08[BCM_MAX_RX_BUF][BCM_BUF_SIZE];
+
+// tx ring index of first/last bd
+static uint32_t bcm_tx_start_u32;
+static uint32_t bcm_tx_stop_u32;
+static uint32_t bcm_tx_bufavail_u32;
+
+/*
+ * status block
+ */
+static bcm_status_t bcm_status;
+
+/*
+ * implementation
+ ******************************************************************************
+ */
+
+
+/*
+ * global functions
+ ******************************************************************************
+ */
+
+
+/*
+ * local helper functions
+ ******************************************************************************
+ */
+#if 0
+static char *
+memcpy( char *dest, const char *src, size_t n )
+{
+ char *ret = dest;
+ while( n-- ) {
+ *dest++ = *src++;
+ }
+
+ return( ret );
+}
+#endif
+
+static char *
+memset_ci( char *dest, int c, size_t n )
+{
+ char *ret = dest;
+
+ while( n-- ) {
+ wr08( dest, c );
+ dest++;
+ }
+
+ return( ret );
+}
+
+#if 0
+static char *
+memset( char *dest, int c, size_t n )
+{
+ char *ret = dest;
+ while( n-- ) {
+ *dest++ = (char) c;
+ }
+
+ return( ret );
+}
+#endif
+
+static uint32_t
+bcm_nvram_logical_to_physical_address(uint32_t address)
+{
+ uint32_t page_no = address / BUFFERED_FLASH_PAGE_SIZE;
+ uint32_t page_addr = address % BUFFERED_FLASH_PAGE_SIZE;
+
+ return (page_no << BUFFERED_FLASH_PAGE_POS) + page_addr;
+}
+
+/*
+ * read/write functions to access NIC registers & memory
+ * NOTE: all functions are executed with cache inhibitation (dead slow :-) )
+ */
+static uint32_t
+bcm_read_mem32( uint16_t f_offs_u16 )
+{ // caution: shall only be used after initialization!
+ return rd32( bcm_memaddr_u64 + (uint64_t) f_offs_u16 );
+}
+
+/* not used so far
+static uint16_t
+bcm_read_mem16( uint16_t f_offs_u16 )
+{ // caution: shall only be used after initialization!
+ return rd16( bcm_memaddr_u64 + (uint64_t) f_offs_u16 );
+}*/
+/* not used so far
+static uint8_t
+bcm_read_mem08( uint16_t f_offs_u16 )
+{ // caution: shall only be used after initialization!
+ return rd08( bcm_memaddr_u64 + (uint64_t) f_offs_u16 );
+}*/
+
+static uint32_t
+bcm_read_reg32_indirect( uint16_t f_offs_u16 )
+{ // caution: shall only be used after initialization!
+ SLOF_pci_config_write32(REG_BASE_ADDR_REG, f_offs_u16);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ REG_BASE_ADDR_REG,
+ f_offs_u16 );*/
+ return bswap_32(SLOF_pci_config_read32(REG_DATA_REG));
+ /*return (uint32_t) bswap_32( snk_kernel_interface->pci_config_read( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ REG_DATA_REG ) ) ;*/
+}
+
+static uint32_t
+bcm_read_reg32( uint16_t f_offs_u16 )
+{ // caution: shall only be used after initialization!
+ if(f_offs_u16 >= 0x200 && f_offs_u16 <0x400)
+ return bcm_read_reg32_indirect( f_offs_u16 + 0x5600 );
+ return rd32( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 );
+}
+
+static uint16_t
+bcm_read_reg16( uint16_t f_offs_u16 )
+{ // caution: shall only be used after initialization!
+ return rd16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 );
+}
+/* not used so far
+static uint8_t
+bcm_read_reg08( uint16_t f_offs_u16 )
+{ // caution: shall only be used after initialization!
+ return rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 );
+}*/
+
+static void
+bcm_write_mem32_indirect( uint16_t f_offs_u16, uint32_t f_val_u32 )
+{ // caution: shall only be used after initialization!
+ SLOF_pci_config_write32(MEM_BASE_ADDR_REG, f_offs_u16);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ MEM_BASE_ADDR_REG,
+ f_offs_u16 );*/
+ SLOF_pci_config_write32(MEM_DATA_REG, bswap_32(f_val_u32));
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ MEM_DATA_REG,
+ bswap_32 ( f_val_u32 ) );*/
+}
+
+static void
+bcm_write_mem32( uint16_t f_offs_u16, uint32_t f_val_u32 )
+{ // caution: shall only be used after initialization!
+ if(f_offs_u16 >= BCM_RXRET_RCB_OFFS &&
+ f_offs_u16 < BCM_RXRET_RCB_OFFS + (BCM_MAX_RXRET_RING*BCM_RCB_SIZE_u16))
+ bcm_write_mem32_indirect( f_offs_u16, f_val_u32 );
+ else if(f_offs_u16 >= BCM_TX_RCB_OFFS &&
+ f_offs_u16 < BCM_TX_RCB_OFFS + (BCM_MAX_TX_RING*BCM_RCB_SIZE_u16))
+ bcm_write_mem32_indirect( f_offs_u16, f_val_u32 );
+ else
+ wr32( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u32 );
+}
+/* not used so far
+static void
+bcm_write_mem16( uint16_t f_offs_u16, uint16_t f_val_u16 )
+{ // caution: shall only be used after initialization!
+ wr16( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u16 );
+}*/
+/* not used so far
+static void
+bcm_write_mem08( uint16_t f_offs_u16, uint8_t f_val_u08 )
+{ // caution: shall only be used after initialization!
+ wr08( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u08 );
+}*/
+
+static void
+bcm_write_reg32_indirect( uint16_t f_offs_u16, uint32_t f_val_u32 )
+{ // caution: shall only be used after initialization!
+ SLOF_pci_config_write32(REG_BASE_ADDR_REG, f_offs_u16);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ REG_BASE_ADDR_REG,
+ f_offs_u16 );*/
+ SLOF_pci_config_write32(REG_DATA_REG, bswap_32(f_val_u32));
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ REG_DATA_REG,
+ bswap_32 ( f_val_u32 ) );*/
+}
+
+static void
+bcm_write_reg32( uint16_t f_offs_u16, uint32_t f_val_u32 )
+{ // caution: shall only be used after initialization!
+ if(f_offs_u16 >= 0x200 && f_offs_u16 <0x400)
+ bcm_write_reg32_indirect( f_offs_u16 + 0x5600, f_val_u32 );
+ else
+ wr32( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u32 );
+}
+
+static void
+bcm_write_reg16( uint16_t f_offs_u16, uint16_t f_val_u16 )
+{ // caution: shall only be used after initialization!
+ wr16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u16 );
+}
+/* not used so far
+static void
+bcm_write_reg08( uint16_t f_offs_u16, uint8_t f_val_u08 )
+{ // caution: shall only be used after initialization!
+ wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u08 );
+}*/
+
+static void
+bcm_setb_reg32( uint16_t f_offs_u16, uint32_t f_mask_u32 )
+{
+ uint32_t v;
+
+ v = bcm_read_reg32( f_offs_u16 );
+ v |= f_mask_u32;
+ bcm_write_reg32( f_offs_u16, v );
+}
+/* not used so far
+static void
+bcm_setb_reg16( uint16_t f_offs_u16, uint16_t f_mask_u16 )
+{
+ uint16_t v;
+ v = rd16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 );
+ v |= f_mask_u16;
+ wr16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v );
+}*/
+/* not used so far
+static void
+bcm_setb_reg08( uint16_t f_offs_u16, uint8_t f_mask_u08 )
+{
+ uint8_t v;
+ v = rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 );
+ v |= f_mask_u08;
+ wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v );
+}*/
+
+static void
+bcm_clrb_reg32( uint16_t f_offs_u16, uint32_t f_mask_u32 )
+{
+ uint32_t v;
+
+ v = bcm_read_reg32( f_offs_u16 );
+ v &= ~f_mask_u32;
+ bcm_write_reg32( f_offs_u16, v );
+}
+
+static void
+bcm_clrb_reg16( uint16_t f_offs_u16, uint16_t f_mask_u16 )
+{
+ uint16_t v;
+
+ v = bcm_read_reg16( f_offs_u16 );
+ v &= ~f_mask_u16;
+ bcm_write_reg16( f_offs_u16, v );
+}
+/* not used so far
+static void
+bcm_clrb_reg08( uint16_t f_offs_u16, uint8_t f_mask_u08 )
+{
+ uint8_t v;
+ v = rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 );
+ v &= ~f_mask_u32;
+ wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v );
+}*/
+
+static void
+bcm_clr_wait_bit32( uint16_t r, uint32_t b )
+{
+ uint32_t i;
+
+ bcm_clrb_reg32( r, b );
+
+ i = 1000;
+ while( --i ) {
+
+ if( ( bcm_read_reg32( r ) & b ) == 0 ) {
+ break;
+ }
+
+ SLOF_usleep( 10 );
+ }
+#ifdef BCM_DEBUG
+ if( ( bcm_read_reg32( r ) & b ) != 0 ) {
+ printf( "bcm57xx: bcm_clear_wait_bit32 failed (0x%04X)!\n", r );
+ }
+#endif
+}
+
+/*
+ * (g)mii bus access
+ */
+#if 0
+// not used so far
+static int32_t
+bcm_mii_write16( uint32_t f_reg_u32, uint16_t f_value_u16 )
+{
+ static const uint32_t WR_VAL = ( ( ((uint32_t) 0x1) << 21 ) | BIT32( 29 ) | BIT32( 26 ) );
+ int32_t l_autopoll_i32 = 0;
+ uint32_t l_wrval_u32;
+ uint32_t i;
+
+ /*
+ * only 0x00-0x1f are valid registers
+ */
+ if( f_reg_u32 > (uint32_t) 0x1f ) {
+ return -1;
+ }
+
+ /*
+ * disable auto polling if enabled
+ */
+ if( ( bcm_read_reg32( MI_MODE_R ) & BIT32( 4 ) ) != 0 ) {
+ l_autopoll_i32 = (int32_t) !0;
+ bcm_clrb_reg32( MI_MODE_R, BIT32( 4 ) );
+ SLOF_usleep( 40 );
+ }
+
+ /*
+ * construct & write mi com register value
+ */
+ l_wrval_u32 = ( WR_VAL | ( f_reg_u32 << 16 ) | (uint32_t) f_value_u16 );
+ bcm_write_reg32( MI_COM_R, l_wrval_u32 );
+
+ /*
+ * wait for transaction to complete
+ */
+ i = 25;
+ while( ( --i ) &&
+ ( ( bcm_read_reg32( MI_COM_R ) & BIT32( 29 ) ) != 0 ) ) {
+ SLOF_usleep( 10 );
+ }
+
+ /*
+ * re-enable auto polling if necessary
+ */
+ if( l_autopoll_i32 ) {
+ bcm_setb_reg32( MI_MODE_R, BIT32( 4 ) );
+ }
+
+ // return on error
+ if( i == 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+static int32_t
+bcm_mii_read16( uint32_t f_reg_u32, uint16_t *f_value_pu16 )
+{
+ static const uint32_t RD_VAL = ( ( ((uint32_t) 0x1) << 21 ) | BIT32( 29 ) | BIT32( 27 ) );
+ int32_t l_autopoll_i32 = 0;
+ uint32_t l_rdval_u32;
+ uint32_t i;
+ uint16_t first_not_busy;
+
+ /*
+ * only 0x00-0x1f are valid registers
+ */
+ if( f_reg_u32 > (uint32_t) 0x1f ) {
+ return -1;
+ }
+
+ /*
+ * disable auto polling if enabled
+ */
+ if( ( bcm_read_reg32( MI_MODE_R ) & BIT32( 4 ) ) != 0 ) {
+ l_autopoll_i32 = ( int32_t ) !0;
+ bcm_clrb_reg32( MI_MODE_R, BIT32( 4 ) );
+ SLOF_usleep( 40 );
+ }
+
+ /*
+ * construct & write mi com register value
+ */
+ l_rdval_u32 = ( RD_VAL | ( f_reg_u32 << 16 ) );
+ bcm_write_reg32( MI_COM_R, l_rdval_u32 );
+
+ /*
+ * wait for transaction to complete
+ * ERRATA workaround: must read two "not busy" states to indicate transaction complete
+ */
+ i = 25;
+ first_not_busy = 0;
+ l_rdval_u32 = bcm_read_reg32( MI_COM_R );
+ while( ( --i ) &&
+ ( (first_not_busy == 0) || ( ( l_rdval_u32 & BIT32( 29 ) ) != 0 ) ) ) {
+ /* Is this the first clear BUSY state? */
+ if ( ( l_rdval_u32 & BIT32( 29 ) ) == 0 )
+ first_not_busy++;
+ SLOF_usleep( 10 );
+ l_rdval_u32 = bcm_read_reg32( MI_COM_R );
+ }
+
+ /*
+ * re-enable autopolling if necessary
+ */
+ if( l_autopoll_i32 ) {
+ bcm_setb_reg32( MI_MODE_R, BIT32( 4 ) );
+ }
+
+ /*
+ * return on read transaction error
+ * (check read failed bit)
+ */
+ if( ( i == 0 ) ||
+ ( ( l_rdval_u32 & BIT32( 28 ) ) != 0 ) ) {
+ return -1;
+ }
+
+ /*
+ * return read value
+ */
+ *f_value_pu16 = (uint16_t) ( l_rdval_u32 & (uint32_t) 0xffff );
+
+ return 0;
+}
+
+/*
+ * ht2000 dump (not complete)
+ */
+#if 0
+static void
+bcm_dump( void )
+{
+ uint32_t i, j;
+
+ printf( "*** DUMP ***********************************************************************\n\n" );
+
+ printf( "* PCI Configuration Registers:\n" );
+ for( i = 0, j = 0; i < 0x40; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* Private PCI Configuration Registers:\n" );
+ for( i = 0x68, j = 0; i < 0x88; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* VPD Config:\n" );
+ printf( "%04X: %08X \n", 0x94, bcm_read_reg32( 0x94 ) );
+
+ printf( "\n* Dual MAC Control Registers:\n" );
+ for( i = 0xb8, j = 0; i < 0xd0; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* Ethernet MAC Control Registers:\n" );
+ for( i = 0x400, j = 0; i < 0x590; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* Send Data Initiator Control:\n" );
+ for( i = 0xc00, j = 0; i < 0xc10; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* Send Data Completion Control:\n" );
+ printf( "%04X: %08X ", 0x1000, bcm_read_reg32( 0x1000 ) );
+ printf( "%04X: %08X \n", 0x1008, bcm_read_reg32( 0x1008 ) );
+
+ printf( "\n* Send BD Ring Selector Control:\n" );
+ printf( "%04X: %08X ", 0x1400, bcm_read_reg32( 0x1400 ) );
+ printf( "%04X: %08X ", 0x1404, bcm_read_reg32( 0x1404 ) );
+ printf( "%04X: %08X \n", 0x1408, bcm_read_reg32( 0x1408 ) );
+
+ printf( "\n* Send BD Initiator Control:\n" );
+ printf( "%04X: %08X ", 0x1800, bcm_read_reg32( 0x1800 ) );
+ printf( "%04X: %08X \n", 0x1804, bcm_read_reg32( 0x1804 ) );
+
+ printf( "\n* Send BD Completion Control:\n" );
+ printf( "%04X: %08X ", 0x1c00, bcm_read_reg32( 0x1c00 ) );
+
+ printf( "\n* Receive List Placement Control:\n" );
+ for( i = 0x2000, j = 0; i < 0x2020; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* Receive Data & Receive BD Initiator Control:\n" );
+ printf( "%04X: %08X ", 0x2400, bcm_read_reg32( 0x2400 ) );
+ printf( "%04X: %08X \n", 0x2404, bcm_read_reg32( 0x2404 ) );
+
+ printf( "\n* Jumbo Receive BD Ring RCB:\n" );
+ for( i = 0x2440, j = 0; i < 0x2450; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* Standard Receive BD Ring RCB:\n" );
+ for( i = 0x2450, j = 0; i < 0x2460; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* Mini Receive BD Ring RCB:\n" );
+ for( i = 0x2460, j = 0; i < 0x2470; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\nRDI Timer Mode Register:\n" );
+ printf( "%04X: %08X \n", 0x24f0, bcm_read_reg32( 0x24f0 ) );
+
+ printf( "\n* Receive BD Initiator Control:\n" );
+ for( i = 0x2c00, j = 0; i < 0x2c20; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+
+ printf( "\n* Receive BD Completion Control:\n" );
+ for( i = 0x3000, j = 0; i < 0x3014; i += 4 ) {
+
+ printf( "%04X: %08X ", i, bcm_read_reg32( i ) );
+
+ if( ( ++j & 0x3 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+}
+#endif
+
+
+
+/*
+ * NVRAM access
+ */
+
+static int
+bcm_nvram_lock( void )
+{
+ int i;
+
+ /*
+ * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON)
+ */
+// bcm_setb_reg32( SW_ARB_R, BIT32( 0 ) );
+ bcm_setb_reg32( SW_ARB_R, BIT32( 1 ) );
+
+ i = 2000;
+ while( ( --i ) &&
+// ( bcm_read_reg32( SW_ARB_R ) & BIT32( 8 ) ) == 0 ) {
+ ( bcm_read_reg32( SW_ARB_R ) & BIT32( 9 ) ) == 0 ) {
+ SLOF_msleep( 1 );
+ }
+
+ // return on error
+ if( i == 0 ) {
+#ifdef BCM_DEBUG
+ printf("bcm57xx: failed to lock nvram");
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+bcm_nvram_unlock( void )
+{
+ /*
+ * release NVRam lock (CLR0)
+ */
+// bcm_setb_reg32( SW_ARB_R, BIT32( 4 ) );
+ bcm_setb_reg32( SW_ARB_R, BIT32( 5 ) );
+}
+
+static void
+bcm_nvram_init( void )
+{
+ /*
+ * enable access to NVRAM registers
+ */
+ if(IS_5714) {
+ bcm_setb_reg32( NVM_ACC_R, BIT32( 1 ) | BIT32( 0 ) );
+ }
+
+ /*
+ * disable bit-bang method 19& disable interface bypass
+ */
+ bcm_clrb_reg32( NVM_CFG1_R, BIT32( 31 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 14 ) | BIT32( 16 ) );
+ bcm_setb_reg32( NVM_CFG1_R, BIT32 ( 13 ) | BIT32 ( 17 ));
+
+ /*
+ * enable Auto SEEPROM Access
+ */
+ bcm_setb_reg32( MISC_LOCAL_CTRL_R, BIT32 ( 24 ) );
+
+ /*
+ * NVRAM write enable
+ */
+ bcm_setb_reg32( MODE_CTRL_R, BIT32 ( 21 ) );
+}
+
+static int32_t
+bcm_nvram_read( uint32_t f_addr_u32, uint32_t *f_val_pu32, uint32_t lock )
+{
+ uint32_t i;
+
+ /*
+ * parameter check
+ */
+ if( f_addr_u32 > NVM_ADDR_MSK ) {
+ return -1;
+ }
+
+ /*
+ * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON)
+ */
+ if( lock && (bcm_nvram_lock() == -1) ) {
+ return -1;
+ }
+
+ /*
+ * setup address to read
+ */
+ bcm_write_reg32( NVM_ADDR_R,
+ bcm_nvram_logical_to_physical_address(f_addr_u32) );
+// bcm_write_reg32( NVM_ADDR_R, f_addr_u32 );
+
+ /*
+ * get the command going
+ */
+ bcm_write_reg32( NVM_COM_R, BIT32( 8 ) | BIT32( 7 ) |
+ BIT32( 4 ) | BIT32( 3 ) );
+
+ /*
+ * wait for command completion
+ */
+ i = 2000;
+ while( ( --i ) &&
+ ( ( bcm_read_reg32( NVM_COM_R ) & BIT32( 3 ) ) == 0 ) ) {
+ SLOF_msleep( 1 );
+ }
+
+ /*
+ * read back data if no error
+ */
+ if( i != 0 ) {
+ /*
+ * read back data
+ */
+ *f_val_pu32 = bcm_read_reg32( NVM_READ_R );
+ }
+
+ if(lock)
+ bcm_nvram_unlock();
+
+ // error
+ if( i == 0 ) {
+#ifdef BCM_DEBUG
+ printf("bcm57xx: reading from NVRAM failed\n");
+#endif
+ return -1;
+ }
+
+ // success
+ return 0;
+}
+
+static int32_t
+bcm_nvram_write( uint32_t f_addr_u32, uint32_t f_value_u32, uint32_t lock )
+{
+ uint32_t i;
+
+ /*
+ * parameter check
+ */
+ if( f_addr_u32 > NVM_ADDR_MSK ) {
+ return -1;
+ }
+
+ /*
+ * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON)
+ */
+ if( lock && (bcm_nvram_lock() == -1) ) {
+ return -1;
+ }
+
+ /*
+ * setup address to write
+ */
+ bcm_write_reg32( NVM_ADDR_R, bcm_nvram_logical_to_physical_address( f_addr_u32 ) );
+
+ /*
+ * setup write data
+ */
+ bcm_write_reg32( NVM_WRITE_R, f_value_u32 );
+
+ /*
+ * get the command going
+ */
+ bcm_write_reg32( NVM_COM_R, BIT32( 8 ) | BIT32( 7 ) |
+ BIT32( 5 ) | BIT32( 4 ) | BIT32( 3 ) );
+
+ /*
+ * wait for command completion
+ */
+ i = 2000;
+ while( ( --i ) &&
+ ( ( bcm_read_reg32( NVM_COM_R ) & BIT32( 3 ) ) == 0 ) ) {
+ SLOF_msleep( 1 );
+ }
+
+ /*
+ * release NVRam lock (CLR0)
+ */
+ if(lock)
+ bcm_nvram_unlock();
+
+ // error
+ if( i == 0 ) {
+#ifdef BCM_DEBUG
+ printf("bcm57xx: writing to NVRAM failed\n");
+#endif
+ return -1;
+ }
+
+ // success
+ return 0;
+}
+
+/*
+ * PHY initialization
+ */
+static int32_t
+bcm_mii_phy_init( void )
+{
+ static const uint32_t PHY_STAT_R = (uint32_t) 0x01;
+ static const uint32_t AUX_STAT_R = (uint32_t) 0x19;
+ static const uint32_t MODE_GMII = BIT32( 3 );
+ static const uint32_t MODE_MII = BIT32( 2 );
+ static const uint32_t NEG_POLARITY = BIT32( 10 );
+ static const uint32_t MII_MSK = ( MODE_GMII | MODE_MII );
+ static const uint16_t GIGA_ETH = ( BIT16( 10 ) | BIT16( 9 ) );
+ int32_t i;
+ uint16_t v;
+
+ /*
+ * enable MDI communication
+ */
+ bcm_write_reg32( MDI_CTRL_R, (uint32_t) 0x0 );
+
+ /*
+ * check link up
+ */
+ i = 2500;
+ do {
+ SLOF_msleep( 1 );
+ // register needs to be read twice!
+ bcm_mii_read16( PHY_STAT_R, &v );
+ bcm_mii_read16( PHY_STAT_R, &v );
+ } while( ( --i ) &&
+ ( ( v & BIT16( 2 ) ) == 0 ) );
+
+ if( i == 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: link is down\n" );
+#endif
+ return -1;
+ }
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: link is up\n" );
+#endif
+ if( !IS_COPPER_PHY ) {
+ return 0;
+ }
+
+ /*
+ * setup GMII or MII interface
+ */
+ i = bcm_read_reg32( ETH_MAC_MODE_R );
+ /*
+ * read status register twice, since the first
+ * read fails once between here and the moon...
+ */
+ bcm_mii_read16( AUX_STAT_R, &v );
+ bcm_mii_read16( AUX_STAT_R, &v );
+
+ if( ( v & GIGA_ETH ) == GIGA_ETH ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: running PHY in GMII mode (1000BaseT)\n" );
+#endif
+ // GMII device
+ if( ( i & MII_MSK ) != MODE_GMII ) {
+ i &= ~MODE_MII;
+ i |= MODE_GMII;
+ }
+
+ } else {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: running PHY in MII mode (10/100BaseT)\n" );
+#endif
+ // MII device
+ if( ( i & MII_MSK ) != MODE_MII ) {
+ i &= ~MODE_GMII;
+ i |= MODE_MII;
+ }
+
+ }
+
+ if( IS_5704 && !IS_SERDES ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: set the link ready signal for 5704C to negative polarity\n" );
+#endif
+ i |= NEG_POLARITY; // set the link ready signal for 5704C to negative polarity
+ }
+
+ bcm_write_reg32( ETH_MAC_MODE_R, i );
+
+ return 0;
+}
+
+static int32_t
+bcm_tbi_phy_init( void )
+{
+ int32_t i;
+#if 0
+ /*
+ * set TBI mode full duplex
+ */
+ bcm_clrb_reg32( ETH_MAC_MODE_R, BIT32( 1 ) );
+ bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 2 ) | BIT32( 3 ) );
+
+ /*
+ * enable MDI communication
+ */
+ bcm_write_reg32( MDI_CTRL_R, (uint32_t) 0x0 );
+
+ /* Disable link change interrupt. */
+ bcm_write_reg32( ETH_MAC_EVT_EN_R, 0 );
+
+ /*
+ * set link polarity
+ */
+ bcm_clrb_reg32( ETH_MAC_MODE_R, BIT32( 10 ) );
+
+ /*
+ * wait for sync/config changes
+ */
+ for( i = 0; i < 100; i++ ) {
+ bcm_write_reg32( ETH_MAC_STAT_R,
+ BIT32( 3 ) | BIT32( 4 ) );
+
+ SLOF_usleep( 20 );
+
+ if( ( bcm_read_reg32( ETH_MAC_STAT_R ) &
+ ( BIT32( 3 ) | BIT32( 4 ) ) ) == 0 ) {
+ break;
+ }
+
+ }
+#endif
+ /*
+ * wait for sync to come up
+ */
+ for( i = 0; i < 100; i++ ) {
+
+ if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) != 0 ) {
+ break;
+ }
+
+ SLOF_usleep( 20 );
+ }
+
+ if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) == 0) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: link is down\n" );
+#endif
+ return -1;
+ }
+#if 0
+ /*
+ * clear all attentions
+ */
+ bcm_write_reg32( ETH_MAC_STAT_R, (uint32_t) ~0 );
+#endif
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: link is up\n" );
+#endif
+ return 0;
+}
+
+static int32_t
+bcm_phy_init( void )
+{
+ static const uint16_t SRAM_HW_CFG = (uint16_t) 0x0b58;
+ uint32_t l_val_u32;
+ int32_t l_ret_i32 = 0;
+
+ /*
+ * get HW configuration from SRAM
+ */
+ l_val_u32 = bcm_read_mem32( SRAM_HW_CFG );
+ l_val_u32 &= ( BIT32( 5 ) | BIT32( 4 ) );
+
+ switch( l_val_u32 ) {
+ case 0x10: {
+ #ifdef BCM_DEBUG
+ printf( "bcm57xx: copper PHY detected\n" );
+ #endif
+
+ bcm_device_u64 |= BCM_DEV_COPPER;
+ l_ret_i32 = bcm_mii_phy_init();
+ } break;
+
+ case 0x20: {
+ #ifdef BCM_DEBUG
+ printf( "bcm57xx: fiber PHY detected\n" );
+ #endif
+
+ if( !IS_SERDES ) {
+ #ifdef BCM_DEBUG
+ printf( "bcm57xx: running PHY in gmii/mii mode\n" );
+ #endif
+ l_ret_i32 = bcm_mii_phy_init();
+ } else {
+ #ifdef BCM_DEBUG
+ printf( "bcm57xx: running PHY in tbi mode\n" );
+ #endif
+ l_ret_i32 = bcm_tbi_phy_init();
+ }
+
+ } break;
+
+ default: {
+ #ifdef BCM_DEBUG
+ printf( "bcm57xx: unknown PHY type detected, terminating\n" );
+ #endif
+ l_ret_i32 = -1;
+ }
+
+ }
+
+ return l_ret_i32;
+}
+
+/*
+ * ring initialization
+ */
+static void
+bcm_init_rxprod_ring( void )
+{
+ uint32_t v;
+ uint32_t i;
+
+ /*
+ * clear out the whole rx prod ring for sanity
+ */
+ memset( (void *) &bcm_rxprod_ring,
+ 0,
+ BCM_RXPROD_RING_SIZE * sizeof( bcm_rxbd_t ) );
+ mb();
+
+ /*
+ * assign buffers & indices to the ring members
+ */
+ for( i = 0; i < BCM_MAX_RX_BUF; i++ ) {
+ bcm_rxprod_ring[i].m_hostaddr_st.m_hi_u32 =
+ (uint32_t) ( (uint64_t) &bcm_rx_buffer_pu08[i] >> 32 );
+ bcm_rxprod_ring[i].m_hostaddr_st.m_lo_u32 =
+ (uint32_t) ( (uint64_t) &bcm_rx_buffer_pu08[i] &
+ (uint64_t) 0xffffffff );
+ bcm_rxprod_ring[i].m_idxlen_u32 = ( i << 16 );
+ bcm_rxprod_ring[i].m_idxlen_u32 += BCM_BUF_SIZE;
+ }
+
+ /*
+ * clear rcb registers & disable rings
+ * NOTE: mini & jumbo rings are not supported,
+ * still rcb's are cleaned out for sanity
+ */
+ bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_JUM ), RCB_FLAG_RING_DISABLED );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_JUM ), 0 );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_JUM ), 0 );
+ bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_JUM ), 0 );
+
+ bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), RCB_FLAG_RING_DISABLED );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ), 0 );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ), 0 );
+ bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ), 0 );
+
+ bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_MIN ), RCB_FLAG_RING_DISABLED );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_MIN ), 0 );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_MIN ), 0 );
+ bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_MIN ), 0 );
+
+ /*
+ * clear rx producer index of std producer ring
+ */
+ bcm_write_reg32( RXPROD_PROD_IND, 0 );
+
+ /*
+ * setup rx standard rcb using recommended NIC addr (hard coded)
+ */
+ bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ),
+ (uint32_t) ( (uint64_t) &bcm_rxprod_ring >> 32 ) );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ),
+ (uint32_t) ( (uint64_t) &bcm_rxprod_ring & (uint64_t) 0xffffffff ) );
+ bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ),
+ (uint32_t) BCM_NIC_RX_OFFS );
+
+ if( IS_5704 || IS_5703 ) {
+ // 5704: length field = max buffer len
+ v = (uint32_t) BCM_BUF_SIZE << 16;
+ } else {
+ // 5714: length field = number of ring entries
+ v = (uint32_t) BCM_RXPROD_RING_SIZE << 16;
+ }
+
+ v &= (uint32_t) ~RCB_FLAG_RING_DISABLED;
+ bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), v );
+}
+
+static void
+bcm_init_rxret_ring( void )
+{
+ uint32_t i;
+ uint16_t v;
+
+ /*
+ * clear out the whole rx ret ring for sanity
+ */
+ memset( (void *) &bcm_rxret_ring,
+ 0,
+ 2 * BCM_RXRET_RING_SIZE * sizeof( bcm_rxbd_t ) );
+ mb();
+
+ /*
+ * setup return ring size dependent on installed device
+ */
+ bcm_rxret_ring_sz = BCM_RXRET_RING_SIZE;
+ if( IS_5704 || IS_5703 ) {
+ bcm_rxret_ring_sz *= 2;
+ }
+
+ /*
+ * clear rcb memory & disable rings
+ * NOTE: 5714 only supports one return ring,
+ * still all possible rcb's are cleaned out for sanity
+ */
+ v = BCM_RXRET_RCB_OFFS;
+ for( i = 0; i < BCM_MAX_RXRET_RING; i++ ) {
+ bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 );
+ bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 );
+
+ v += BCM_RCB_SIZE_u16;
+ }
+
+ /*
+ * clear rx consumer index of return ring
+ */
+ bcm_write_reg32( RXRET_CONS_IND, 0 );
+
+ /*
+ * setup rx ret rcb
+ * NOTE: NIC address not aplicable in return rings
+ */
+ bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXRET_RCB_OFFS ),
+ (uint32_t) ( (uint64_t) &bcm_rxret_ring >> 32 ) );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXRET_RCB_OFFS ),
+ (uint32_t) ( (uint64_t) &bcm_rxret_ring &
+ (uint64_t) 0xffffffff ) );
+ bcm_write_mem32( BCM_RCB_NICADDR_u16( BCM_RXRET_RCB_OFFS ), 0 );
+
+ i = bcm_rxret_ring_sz;
+ i <<= 16;
+ i &= (uint32_t) ~RCB_FLAG_RING_DISABLED;
+ bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXRET_RCB_OFFS ), i );
+}
+
+static void
+bcm_init_tx_ring( void )
+{
+ uint32_t i;
+ uint16_t v;
+
+ /*
+ * clear out the whole tx ring for sanity
+ */
+ memset( (void *) &bcm_tx_ring,
+ 0,
+ BCM_TX_RING_SIZE * sizeof( bcm_txbd_t ) );
+ mb();
+
+ /*
+ * assign buffers to the ring members & setup invariant flags
+ */
+ for( i = 0; i < BCM_MAX_TX_BUF; i++ ) {
+ bcm_tx_ring[i].m_hostaddr_st.m_hi_u32 =
+ (uint32_t) ( (uint64_t) &bcm_tx_buffer_pu08[i] >> 32 );
+ bcm_tx_ring[i].m_hostaddr_st.m_lo_u32 =
+ (uint32_t) ( (uint64_t) &bcm_tx_buffer_pu08[i] &
+ (uint64_t) 0xffffffff );
+ // flags: indicate last packet & coal now
+ // -last packet is always true (only one send packet supported)
+ // -coal now needed to always get the consumed bd's (since
+ // only a few bd's are set up which permanently are recycled)
+ bcm_tx_ring[i].m_lenflags_u32 = ( BIT32( 2 ) | BIT32( 7 ) );
+ bcm_tx_ring[i].m_VLANtag_u32 = (uint32_t) 0; // not used
+ }
+
+ /*
+ * clear rcb memory & disable rings
+ * NOTE: 5714 only supports one send ring,
+ * still all possible rcb's are cleaned out for sanity
+ */
+ v = BCM_TX_RCB_OFFS;
+ for( i = 0; i < BCM_MAX_TX_RING; i++ ) {
+ bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 );
+ bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 );
+
+ v += BCM_RCB_SIZE_u16;
+ }
+
+ /*
+ * clear host/nic producer indices
+ */
+ bcm_write_reg32( TX_NIC_PROD_IND, 0 );
+ bcm_write_reg32( TX_PROD_IND, 0 );
+
+ /*
+ * setup tx rcb using recommended NIC addr (hard coded)
+ */
+ bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( BCM_TX_RCB_OFFS ),
+ (uint32_t) ( (uint64_t) &bcm_tx_ring >> 32 ) );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( BCM_TX_RCB_OFFS ),
+ (uint32_t) ( (uint64_t) &bcm_tx_ring &
+ (uint64_t) 0xffffffff ) );
+ bcm_write_mem32( BCM_RCB_NICADDR_u16( BCM_TX_RCB_OFFS ),
+ (uint32_t) BCM_NIC_TX_OFFS );
+
+ if( IS_5704 || IS_5703 ) {
+ // 5704: length field = max buffer len
+ i = (uint32_t) BCM_BUF_SIZE << 16;
+ } else {
+ // 5714: length field = number of ring entries
+ i = (uint32_t) BCM_TX_RING_SIZE << 16;
+ }
+
+ i &= ( uint32_t ) ~RCB_FLAG_RING_DISABLED;
+ bcm_write_mem32( BCM_RCB_LENFLAG_u16( BCM_TX_RCB_OFFS ), i );
+
+ /*
+ * remember the next bd index to be used
+ * & number of available buffers
+ */
+ bcm_tx_stop_u32 = BCM_MAX_TX_BUF;
+ bcm_tx_bufavail_u32 = BCM_MAX_TX_BUF;
+}
+
+static int32_t
+bcm_mac_init( uint8_t *f_mac_pu08 )
+{
+ static const uint16_t MEM_MAC_LO = (uint16_t) 0x0c18;
+ static const uint16_t MEM_MAC_HI = (uint16_t) 0x0c14;
+
+ uint32_t NVR_MAC_LO = (uint16_t) 0x80;
+ uint32_t NVR_MAC_HI = (uint16_t) 0x7c;
+
+ bcm_addr64_t l_mac_st;
+ uint32_t i;
+ uint32_t v;
+
+ /*
+ * Use MAC address from device tree if possible
+ */
+ for( i = 0, v = 0; i < 6; i++ ) {
+ v += (uint32_t) f_mac_pu08[i];
+ }
+
+ if( v != 0 ) {
+ l_mac_st.m_hi_u32 = ( ( (uint32_t) f_mac_pu08[0]) << 8 );
+ l_mac_st.m_hi_u32 |= ( ( (uint32_t) f_mac_pu08[1]) << 0 );
+ l_mac_st.m_lo_u32 = ( ( (uint32_t) f_mac_pu08[2]) << 24 );
+ l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[3]) << 16 );
+ l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[4]) << 8 );
+ l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[5]) << 0 );
+ } else {
+ /*
+ * try to read MAC address from MAC mailbox
+ */
+ l_mac_st.m_hi_u32 = bcm_read_mem32( MEM_MAC_HI );
+
+ if( ( l_mac_st.m_hi_u32 >> 16 ) == (uint32_t) 0x484b ) {
+ l_mac_st.m_hi_u32 &= (uint32_t) 0xffff;
+ l_mac_st.m_lo_u32 = bcm_read_mem32( MEM_MAC_LO );
+ } else {
+ int32_t l_err_i32;
+
+ /*
+ * otherwise retrieve MAC address from NVRam
+ */
+ if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) != 0 ) {
+ // secondary MAC is in use, address in NVRAM changes
+ NVR_MAC_LO += 0x50;
+ NVR_MAC_HI += 0x50;
+ }
+
+ l_err_i32 = bcm_nvram_read( NVR_MAC_LO, &l_mac_st.m_lo_u32, 1 );
+ l_err_i32 += bcm_nvram_read( NVR_MAC_HI, &l_mac_st.m_hi_u32, 1 );
+
+ // return on read error
+ if( l_err_i32 < 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: failed to retrieve MAC address\n" );
+#endif
+ return -1;
+ }
+ }
+ }
+
+ /*
+ * write the mac addr into the NIC's register area
+ */
+ bcm_write_reg32( MAC_ADDR_OFFS_HI(0), l_mac_st.m_hi_u32 );
+ bcm_write_reg32( MAC_ADDR_OFFS_LO(0), l_mac_st.m_lo_u32 );
+ for( i = 1; i < BCM_NUM_MAC_ADDR; i++ ) {
+ bcm_write_reg32( MAC_ADDR_OFFS_HI(i), 0 );
+ bcm_write_reg32( MAC_ADDR_OFFS_LO(i), 0 );
+ }
+
+ /*
+ * WY 26.01.07
+ * not needed anymore, s.a.
+ if( IS_5704 != 0 ) {
+
+ v = MAC5704_ADDR_OFFS;
+ for( i = 0; i < BCM_NUM_MAC5704_ADDR; i++ ) {
+ bcm_write_reg32( v, l_mac_st.m_hi_u32 );
+ v += sizeof( uint32_t );
+ bcm_write_reg32( v, l_mac_st.m_lo_u32 );
+ v += sizeof( uint32_t );
+ }
+
+ }
+ */
+
+ /*
+ * return MAC address as string
+ */
+ f_mac_pu08[0] = (uint8_t) ( ( l_mac_st.m_hi_u32 >> 8 ) & (uint32_t) 0xff );
+ f_mac_pu08[1] = (uint8_t) ( ( l_mac_st.m_hi_u32 ) & (uint32_t) 0xff );
+ f_mac_pu08[2] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 24 ) & (uint32_t) 0xff );
+ f_mac_pu08[3] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 16 ) & (uint32_t) 0xff );
+ f_mac_pu08[4] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 8 ) & (uint32_t) 0xff );
+ f_mac_pu08[5] = (uint8_t) ( ( l_mac_st.m_lo_u32 ) & (uint32_t) 0xff );
+
+#ifdef BCM_DEBUG
+ do {
+ int32_t i;
+ printf( "bcm57xx: retrieved MAC address " );
+
+ for( i = 0; i < 6; i++ ) {
+ printf( "%02X", f_mac_pu08[i] );
+
+ if( i != 5 ) {
+ printf( ":" );
+ }
+
+ }
+
+ printf( "\n" );
+ } while( 0 );
+#endif
+
+ return 0;
+}
+
+
+/*
+ ******************************************************************************
+ * ASF Firmware
+ ******************************************************************************
+ */
+
+
+#ifdef BCM_DEBUG
+#ifdef BCM_SHOW_ASF_REGS
+static void
+bcm_asf_check_register( void )
+{
+ uint32_t i;
+
+ i = bcm_read_reg32( ASF_CTRL_R );
+ printf( "bcm57xx: ASF control : %x\n", i );
+
+ i = bcm_read_reg32( ASF_WATCHDOG_TIMER_R );
+ printf( "bcm57xx: ASF Watchdog Timer : %x\n", i );
+
+ i = bcm_read_reg32( ASF_HEARTBEAT_TIMER_R );
+ printf( "bcm57xx: ASF Heartbeat Timer : %x\n", i );
+
+ i = bcm_read_reg32( ASF_POLL_TIMER_R );
+ printf( "bcm57xx: ASF Poll Timer : %x\n", i );
+
+ i = bcm_read_reg32( POLL_LEGACY_TIMER_R );
+ printf( "bcm57xx: Poll Legacy Timer : %x\n", i );
+
+ i = bcm_read_reg32( RETRANSMISSION_TIMER_R );
+ printf( "bcm57xx: Retransmission Timer : %x\n", i );
+
+ i = bcm_read_reg32( TIME_STAMP_COUNTER_R );
+ printf( "bcm57xx: Time Stamp Counter : %x\n", i );
+
+ i = bcm_read_reg32( RX_CPU_MODE_R );
+ printf( "bcm57xx: RX RISC Mode : %x\n", i );
+
+ i = bcm_read_reg32( RX_CPU_STATE_R );
+ printf( "bcm57xx: RX RISC State : %x\n", i );
+
+ i = bcm_read_reg32( RX_CPU_PC_R );
+ printf( "bcm57xx: RX RISC Prg. Counter : %x\n", i );
+}
+#endif
+#endif
+
+static int
+bcm_fw_halt( void )
+{
+ int i;
+
+ bcm_write_mem32( BCM_FW_MBX_CMD, BCM_NICDRV_PAUSE_FW );
+ bcm_setb_reg32( RX_CPU_EVENT_R, BIT32( 14 ) );
+
+ /* Wait for RX cpu to ACK the event. */
+ for (i = 0; i < 100; i++) {
+ if(bcm_read_reg32( RX_CPU_EVENT_R ) & BIT32( 14 ))
+ break;
+ SLOF_msleep(1);
+ }
+ if( i>= 100)
+ return -1;
+ return 0;
+}
+
+
+#ifdef BCM_SW_AUTONEG
+static void
+bcm_sw_autoneg( void ) {
+ uint32_t i, j, k;
+ uint32_t SerDesCfg;
+ uint32_t SgDigControl;
+ uint32_t SgDigStatus;
+ uint32_t ExpectedSgDigControl;
+ int AutoNegJustInitiated = 0;
+
+ // step 1: init TX 1000BX Autoneg. Register to zero
+ bcm_write_reg32(TX_1000BX_AUTONEG_R, 0);
+
+ // step 2&3: set TBI mode
+ bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 2 ) | BIT32( 3 ) );
+ SLOF_usleep(10);
+
+ // step 4: enable link attention
+ bcm_setb_reg32( ETH_MAC_EVT_EN_R, BIT32( 12 ) );
+
+ // step 5: preserve voltage regulator bits
+ SerDesCfg = bcm_read_reg32(SERDES_CTRL_R) & ( BIT32( 20 ) | BIT32( 21 )
+ | BIT32( 22 ) | BIT32( 23 ) );
+
+ // step 6: preserve voltage regulator bits
+ SgDigControl = bcm_read_reg32(HW_AUTONEG_CTRL_R);
+
+ // step 7: if device is NOT set-up for auto negotiation, then go to step 26
+ // goto bcm_setup_phy_step26;
+
+ // We want to use auto negotiation
+
+ // step 8: we don't want to use flow control
+ ExpectedSgDigControl = 0x81388400; // no flow control
+
+ // step 9: compare SgDigControl with 0x81388400
+ if(SgDigControl == ExpectedSgDigControl) {
+ goto bcm_setup_phy_step17;
+ }
+#ifdef BCM_DEBUG
+ printf("bcm57xx: SgDigControl = %08X\n", SgDigControl);
+#endif
+ // step 10
+ bcm_write_reg32(SERDES_CTRL_R, SerDesCfg | 0xC011880);
+
+ // step 11: restart auto negotiation
+ bcm_write_reg32(HW_AUTONEG_CTRL_R, ExpectedSgDigControl | BIT32( 30 ) );
+
+ // step 12: read back HW_AUTONEG_CTRL_R
+ bcm_read_reg32(HW_AUTONEG_CTRL_R);
+
+ // step 13
+ SLOF_usleep( 5 );
+
+ // step 14,15,16: same as step 11, but don't restart auto neg.
+ bcm_write_reg32(HW_AUTONEG_CTRL_R, ExpectedSgDigControl);
+ AutoNegJustInitiated = 1;
+ goto bcm_setup_phy_step30;
+
+ // step 17:
+ bcm_setup_phy_step17:
+ if( ( bcm_read_reg32(ETH_MAC_STAT_R) & ( BIT32( 1 ) | BIT32( 0 ) ) ) == 0 ) {
+ goto bcm_setup_phy_step30;
+ }
+
+ // step 18: Get HW Autoneg. Status
+ SgDigStatus = bcm_read_reg32(HW_AUTONEG_STAT_R);
+
+ // step 19:
+ if( ( SgDigStatus & BIT32(1) )
+ && ( bcm_read_reg32(ETH_MAC_STAT_R) & BIT32(0) ) ) {
+ // resolve the current flow control?
+ AutoNegJustInitiated = 0;
+ goto bcm_setup_phy_step30;
+ }
+
+ // step 20
+ if( SgDigStatus & BIT32(1) ) {
+ goto bcm_setup_phy_step30;
+ }
+ if( AutoNegJustInitiated != 0) {
+ AutoNegJustInitiated = 0;
+ goto bcm_setup_phy_step29;
+ }
+
+ // step 21, 22, 23, 24: fallback to 1000Mbps-FullDuplex forced mode
+ if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) == 0 ) {
+ // port 0
+ bcm_write_reg32( SERDES_CTRL_R, 0xC010880 );
+ }
+ else { // port 1
+ bcm_write_reg32( SERDES_CTRL_R, 0x4010880 );
+ }
+ // set to 1000Mbps-FullDuplex
+ bcm_write_reg32(HW_AUTONEG_CTRL_R, 0x1388400);
+ // read back
+ bcm_read_reg32(HW_AUTONEG_CTRL_R);
+ SLOF_usleep( 40 );
+
+ // step 25: a little bit reduces...
+ goto bcm_setup_phy_step30;
+
+ // step 26: check if auto negotiation bit is NOT set
+// bcm_setup_phy_step26:
+ if( ( SgDigControl & BIT32(31) )== 0 ) {
+ printf("No autoneg.\n");
+ goto bcm_setup_phy_step29;
+ }
+
+ // step 27:
+ if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) == 0 ) {
+ // port 0
+ bcm_write_reg32( SERDES_CTRL_R, 0xC010880 );
+ }
+ else { // port 1
+ bcm_write_reg32( SERDES_CTRL_R, 0x4010880 );
+ }
+
+ // step 28: disable auto neg. and force 1000FD mode
+ bcm_write_reg32(HW_AUTONEG_CTRL_R, 0x1388400);
+
+ // step 29-31: omitted for 5704S
+ bcm_setup_phy_step29:
+ bcm_setup_phy_step30:
+
+ // step 32: clear link attentions
+ i = bcm_read_reg32( ETH_MAC_STAT_R ) | BIT32( 3 ) | BIT32( 4 );
+ k = 100;
+ do {
+ bcm_write_reg32( ETH_MAC_STAT_R, i );
+ j = bcm_read_reg32( ETH_MAC_STAT_R );
+ if( ( j & BIT32( 3 ) ) != 0 )
+ i = i & ~(BIT32( 3 ));
+ if( ( j & BIT32( 4 ) ) != 0 )
+ i = i & ~(BIT32( 4 ));
+ --k;
+ } while( i & k);
+
+ // step 33
+ if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) == 0 ) {
+ goto bcm_setup_phy_step35;
+ }
+
+ // step 34
+ i = bcm_read_reg32( ETH_MAC_MODE_R );
+ i|= BIT32( 17 );
+ bcm_write_reg32( ETH_MAC_MODE_R, i );
+
+ SLOF_usleep( 1 );
+
+ i = bcm_read_reg32( ETH_MAC_STAT_R );
+ i&= ~BIT32( 17 );
+ bcm_write_reg32( ETH_MAC_STAT_R, i );
+
+ // step 35 & 36: done
+ bcm_setup_phy_step35:
+#ifdef BCM_DEBUG
+ printf("bcm57xx: SetupPhy\n");
+#endif
+ return;
+}
+#endif
+
+static int
+bcm_handle_events( void ) {
+#ifdef BCM_DEBUG
+#ifdef BCM_SHOW_ASF_REGS
+ // ASF REGISTER CHECK
+ // ------------------
+ // check if watchdog timer expired
+ if( bcm_read_reg32( ASF_WATCHDOG_TIMER_R ) == 0 ) {
+ // Show ASF registers
+ bcm_asf_check_register();
+
+ // rearm watchdog timer
+ bcm_write_reg32( ASF_WATCHDOG_TIMER_R, 5 );
+ }
+#endif
+#endif
+
+#ifdef BCM_SW_AUTONEG
+ // AUTO NEGOTIATION
+ // ----------------
+
+ // Check event for Auto Negotiation
+ if( ( bcm_read_reg32( ETH_MAC_STAT_R ) &
+ ( BIT32( 12 ) | BIT32( 3 ) | BIT32( 0 ) ) ) != 0 ) {
+ // link timer procedure
+ bcm_sw_autoneg();
+ }
+#endif
+
+ // ASF FW HEARTBEAT
+ // ----------------
+
+ // check if heartsbeat timer expired
+ if( bcm_read_reg32( ASF_HEARTBEAT_TIMER_R ) <= 2) {
+ int i;
+
+ // Send heartbeat event
+ bcm_write_mem32( BCM_FW_MBX_CMD, BCM_NICDRV_ALIVE );
+ bcm_write_mem32( BCM_FW_MBX_LEN, 4 );
+ bcm_write_mem32( BCM_FW_MBX_DATA, 5 );
+ bcm_setb_reg32( RX_CPU_EVENT_R, BIT32( 14 ) );
+
+ // Wait for RX cpu to ACK the event.
+ for (i = 100; i > 0; i--) {
+ if(bcm_read_reg32( RX_CPU_EVENT_R ) & BIT32( 14 ))
+ break;
+ SLOF_msleep(1);
+ }
+ if( i == 0) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: RX cpu did not acknowledge heartbeat event\n" );
+#endif
+ return -1;
+ }
+
+ // rearm heartbeat timer
+ bcm_write_reg32( ASF_HEARTBEAT_TIMER_R, 5 );
+ }
+ return 0;
+}
+
+/*
+ * interface
+ ******************************************************************************
+ */
+
+/*
+ * bcm_receive
+ */
+static int
+bcm_receive( char *f_buffer_pc, int f_len_i )
+{
+ uint32_t l_rxret_prod_u32 = bcm_read_reg32( RXRET_PROD_IND );
+ uint32_t l_rxret_cons_u32 = bcm_read_reg32( RXRET_CONS_IND );
+ uint32_t l_rxprod_prod_u32 = bcm_read_reg32( RXPROD_PROD_IND );
+ int l_ret_i;
+#ifdef BCM_DEBUG
+#ifdef BCM_SHOW_RCV_DATA
+ int i, j;
+#endif
+#endif
+
+ /*
+ * NOTE: dummy read to ensure data has already been DMA'd is
+ * done by the indice reads
+ */
+
+ bcm_handle_events();
+
+ /*
+ * if producer index == consumer index then nothing was received
+ */
+ if( l_rxret_prod_u32 == l_rxret_cons_u32 ) {
+ return 0;
+ }
+
+ /*
+ * discard erroneous packets
+ */
+ if( ( bcm_rxret_ring[l_rxret_cons_u32].m_typeflags_u32 & BIT32( 10 ) ) != 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: erroneous frame received\n" );
+ printf( " : frame discarded\n" );
+#endif
+ l_ret_i = 0;
+ } else {
+ /*
+ * get packet length, throw away checksum (last 4 bytes)
+ */
+ l_ret_i = (int) ( bcm_rxret_ring[l_rxret_cons_u32].m_idxlen_u32 &
+ (uint32_t) 0xffff ) - (int) 4;
+
+ /*
+ * discard oversized packets
+ */
+ if( l_ret_i > f_len_i ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: receive packet length error:\n" );
+ printf( " : incoming 0x%X bytes, available buffer 0x%X bytes\n", l_ret_i, f_len_i );
+ printf( " : frame discarded\n" );
+#endif
+ l_ret_i = 0;
+ }
+
+ }
+
+ /*
+ * copy & update data & indices
+ */
+ if( l_ret_i != 0 ) {
+ uint64_t l_cpyaddr_u64;
+
+ l_cpyaddr_u64 =
+ ( (uint64_t) bcm_rxret_ring[l_rxret_cons_u32].m_hostaddr_st.m_hi_u32 << 32 );
+ l_cpyaddr_u64 +=
+ ( (uint64_t) bcm_rxret_ring[l_rxret_cons_u32].m_hostaddr_st.m_lo_u32 );
+
+// FIXME:
+ if(l_cpyaddr_u64 == 0) {
+#ifdef BCM_DEBUG
+ printf("bcm57xx: NULL address\n");
+#endif
+ return 0;
+ }
+//
+ memcpy( (void *) f_buffer_pc,
+ (void *) l_cpyaddr_u64,
+ (size_t) l_ret_i );
+
+ }
+
+ /*
+ * replenish bd to producer ring
+ */
+ bcm_rxprod_ring[l_rxprod_prod_u32] =
+ bcm_rxret_ring[l_rxret_cons_u32];
+ bcm_rxprod_ring[l_rxprod_prod_u32].m_idxlen_u32 =
+ ( l_rxprod_prod_u32 << 16 );
+ bcm_rxprod_ring[l_rxprod_prod_u32].m_idxlen_u32 +=
+ (uint32_t) BCM_BUF_SIZE;
+
+ /*
+ * update producer ring's producer index
+ */
+ l_rxprod_prod_u32 = ( l_rxprod_prod_u32 + 1 ) & ( BCM_RXPROD_RING_SIZE - 1 );
+
+ /*
+ * move to the next bd in return ring
+ */
+ l_rxret_cons_u32 = ( l_rxret_cons_u32 + 1 ) & ( bcm_rxret_ring_sz - 1 );
+
+ /*
+ * synchronize before new indices are send to NIC
+ */
+ mb();
+
+ /*
+ * write back new indices
+ */
+ bcm_write_reg32( RXRET_CONS_IND, l_rxret_cons_u32 );
+ bcm_write_reg32( RXPROD_PROD_IND, l_rxprod_prod_u32 );
+
+#ifdef BCM_DEBUG
+#ifdef BCM_SHOW_RCV
+ if( l_ret_i != 0 ) {
+ printf( "bcm57xx: received bytes: %d\n", l_ret_i );
+ }
+#ifdef BCM_SHOW_RCV_DATA
+ for( i = 0, j = 0; i < l_ret_i; i++ ) {
+ printf( "%02X ", ( uint32_t ) f_buffer_pc[i] );
+
+ if( ( ++j % 0x18 ) == 0 ) {
+ printf( "\n" );
+ }
+ }
+
+ if( ( i % 0x18 ) != 0 ) {
+ printf( "\n" );
+ }
+#endif
+#endif
+#endif
+
+ /*
+ * return packet length
+ */
+ return l_ret_i;
+}
+
+static int
+bcm_xmit( char *f_buffer_pc, int f_len_i )
+{
+ uint32_t l_tx_cons_u32 = bcm_read_reg32( TX_CONS_IND );
+ uint32_t l_tx_prod_u32 = bcm_read_reg32( TX_PROD_IND );
+ uint64_t l_cpyaddr_u64;
+
+#ifdef BCM_DEBUG
+#ifdef BCM_SHOW_XMIT_DATA
+ int i, j;
+#endif
+#ifdef BCM_SHOW_IDX
+ printf( "\n" );
+ printf( "bcm57xx: TX_PROD_IND : 0x%03X\n", l_tx_prod_u32 );
+ printf( "bcm57xx: TX_CONS_IND : 0x%03X\n", l_tx_cons_u32 );
+ printf( "bcm57xx: RXPROD_PROD_IND: 0x%03X\n", bcm_read_reg32( RXPROD_PROD_IND ) );
+ printf( "bcm57xx: RXPROD_CONS_IND: 0x%03X\n", bcm_read_reg32( RXPROD_CONS_IND ) );
+ printf( "bcm57xx: RXRET_PROD_IND : 0x%03X\n", bcm_read_reg32( RXRET_PROD_IND ) );
+ printf( "bcm57xx: RXRET_CONS_IND : 0x%03X\n", bcm_read_reg32( RXRET_CONS_IND ) );
+ printf( "bcm57xx: available txb : 0x%03X\n", bcm_tx_bufavail_u32 );
+#endif
+#ifdef BCM_SHOW_STATS
+ printf( "bcm57xx: bcm_status.m_st_word_u32: %08X\n", bcm_status.m_st_word_u32 );
+ printf( "bcm57xx: bcm_status.m_st_tag_u32 : %08X\n", bcm_status.m_st_tag_u32 );
+ printf( "bcm57xx: bcm_status.m_rxprod_cons_u16: %04X\n", ( uint32_t ) bcm_status.m_rxprod_cons_u16 );
+ printf( "bcm57xx: bcm_status.m_unused_u16: %04X\n", ( uint32_t ) bcm_status.m_unused_u16 );
+ printf( "bcm57xx: bcm_status.m_unused_u32: %08X\n", bcm_status.m_unused_u32 );
+ printf( "bcm57xx: bcm_status.m_tx_cons_u16: %04X\n", ( uint32_t ) bcm_status.m_tx_cons_u16 );
+ printf( "bcm57xx: bcm_status.m_rxret_prod_u16: %04X\n", ( uint32_t ) bcm_status.m_rxret_prod_u16 );
+#endif
+#endif
+
+ bcm_handle_events();
+
+ /*
+ * make all consumed bd's available in the ring again
+ * this way only a few buffers are needed instead of
+ * having 512 buffers allocated
+ */
+ while( bcm_tx_start_u32 != l_tx_cons_u32 ) {
+ bcm_tx_ring[bcm_tx_stop_u32] = bcm_tx_ring[bcm_tx_start_u32];
+ bcm_tx_stop_u32 = ( bcm_tx_stop_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 );
+ bcm_tx_start_u32 = ( bcm_tx_start_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 );
+ bcm_tx_bufavail_u32++;
+ }
+
+ /*
+ * check for tx buffer availability
+ */
+ if( bcm_tx_bufavail_u32 == 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: no more transmit buffers available\n" );
+#endif
+ return 0;
+ }
+
+ /*
+ * setup next available bd in tx ring
+ */
+ bcm_tx_ring[l_tx_prod_u32].m_lenflags_u32 = ( BIT32( 2 ) | BIT32( 7 ) /*| BIT32( 6 )*/ );
+ bcm_tx_ring[l_tx_prod_u32].m_lenflags_u32 += ( (uint32_t) f_len_i << 16 );
+// bcm_tx_ring[l_tx_prod_u32].m_VLANtag_u32 = BCM_VLAN_TAG;
+
+ l_cpyaddr_u64 = ( (uint64_t) bcm_tx_ring[l_tx_prod_u32].m_hostaddr_st.m_hi_u32 << 32 );
+ l_cpyaddr_u64 += ( (uint64_t) bcm_tx_ring[l_tx_prod_u32].m_hostaddr_st.m_lo_u32 );
+
+#ifdef BCM_DEBUG
+#ifdef BCM_SHOW_XMIT_STATS
+ printf("bcm57xx: xmit: l_cpyaddr_u64: 0x%lx\n", l_cpyaddr_u64 );
+ printf(" f_buffer_pc : 0x%lx\n", f_buffer_pc );
+ printf(" f_len_i : %d\n", f_len_i );
+#endif
+#endif
+ memcpy( (void *) l_cpyaddr_u64, (void *) f_buffer_pc, (size_t) f_len_i );
+
+ /*
+ * update tx producer index & available buffers
+ */
+ l_tx_prod_u32 = ( l_tx_prod_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 );
+ bcm_tx_bufavail_u32--;
+
+ /*
+ * synchronize before new index is send to NIC
+ */
+ mb();
+
+ bcm_write_reg32( TX_PROD_IND, l_tx_prod_u32 );
+
+#ifdef BCM_DEBUG
+#ifdef BCM_SHOW_XMIT
+ printf( "bcm57xx: sent bytes: %d\n", f_len_i );
+#ifdef BCM_SHOW_XMIT_DATA
+ for( i = 0, j = 0; i < f_len_i; i++ ) {
+ printf( "%02X ", ( uint32_t ) f_buffer_pc[i] );
+
+ if( ( ++j % 0x18 ) == 0 ) {
+ printf( "\n" );
+ }
+
+ }
+ if( ( i % 0x18 ) != 0 ) {
+ printf( "\n" );
+ }
+#endif
+#endif
+
+#ifdef BCM_SHOW_STATS
+ // coalesce status block now
+ bcm_setb_reg32( HOST_COAL_MODE_R, BIT32( 3 ) | BIT32( 1 ) );
+#endif
+
+#endif
+ return f_len_i;
+}
+
+static int
+check_driver( uint16_t vendor_id, uint16_t device_id )
+{
+ uint64_t i;
+
+ /*
+ * checks whether the driver is handling this device
+ * by verifying vendor & device id
+ * vendor id 0x14e4 == Broadcom
+ */
+ if( vendor_id != 0x14e4 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: netdevice not supported, illegal vendor id\n" );
+#endif
+ return -1;
+ }
+
+ for( i = 0; bcm_dev[i].m_dev_u32 != 0; i++ ) {
+ if( bcm_dev[i].m_dev_u32 == (uint32_t) device_id ) {
+ // success
+ break;
+ }
+ }
+
+ if(bcm_dev[i].m_dev_u32 == 0) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: netdevice not supported, illegal device ID\n" );
+#endif
+ return -1;
+ }
+
+ /*
+ * initialize static variables
+ */
+ bcm_device_u64 = bcm_dev[i].m_devmsk_u64;
+ bcm_rxret_ring_sz = 0;
+ bcm_baseaddr_u64 = 0;
+ bcm_memaddr_u64 = 0;
+
+ bcm_tx_start_u32 = 0;
+ bcm_tx_stop_u32 = 0;
+ bcm_tx_bufavail_u32 = 0;
+
+ return 0;
+}
+
+static void
+bcm_wol_activate(void)
+{
+#ifdef BCM_DEBUG
+ uint16_t reg_pwr_cap;
+#endif
+ uint16_t reg_pwr_crtl;
+ uint32_t wol_mode;
+
+ wol_mode = bcm_read_reg32( WOL_MODE_R );
+ bcm_write_reg32( WOL_MODE_R, wol_mode | BIT32(0) );
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: WOL activating..." );
+#endif
+
+// bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_WOL );
+// SLOF_msleep( 100 );
+
+#ifdef BCM_DEBUG
+ reg_pwr_cap = SLOF_pci_config_read16(0x4a);
+ /*reg_pwr_cap = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ 0x4a );*/
+ printf( "bcm57xx: PM Capability Register: %04X\n", reg_pwr_cap );
+#endif
+ /* get curretn power control register */
+ reg_pwr_crtl = SLOF_pci_config_read16(0x4c);
+ /*reg_pwr_crtl = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ 0x4c );*/
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: PM Control/Status Register: %04X\n", reg_pwr_crtl );
+#endif
+
+ /* switch to power state D0 */
+ reg_pwr_crtl |= 0x8000;
+ reg_pwr_crtl &= ~(0x0003);
+ SLOF_pci_config_write16(0x4c, reg_pwr_crtl);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ 0x4c,
+ reg_pwr_crtl );*/
+ SLOF_msleep(10);
+
+/*
+ bcm_write_mem32( BCM_NICDRV_WOL_MBX, BCM_WOL_MAGIC_NUMBER |
+ NIC_WOLDRV_STATE_SHUTDOWN |
+ NIC_WOLDRV_WOL |
+ NIC_WOLDRV_SET_MAGIC_PKT );
+*/
+
+ /* switch to power state D3hot */
+/*
+ reg_pwr_crtl |= 0x0103;
+ SLOF_pci_config_write16(0x4c, reg_pwr_crtl);
+ snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ 0x4c,
+ reg_pwr_crtl );
+ SLOF_msleep(10);
+*/
+
+#ifdef BCM_DEBUG
+ reg_pwr_crtl = SLOF_pci_config_read16(0x4c);
+ /*reg_pwr_crtl = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ 0x4c );*/
+
+ printf( "bcm57xx: PM Control/Status Register: %04X\n", reg_pwr_crtl );
+#endif
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: WOL activated" );
+#endif
+}
+
+static int
+bcm_init( net_driver_t *driver )
+{
+ static const uint32_t lc_Maxwait_u32 = (uint32_t) 1000;
+ uint32_t l_baseaddrL_u32;
+ uint32_t l_baseaddrH_u32;
+ uint32_t i;
+ uint8_t *mac_addr = driver->mac_addr;
+
+ if(driver->running != 0) {
+ return 0;
+ }
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: detected device " );
+ if( IS_5703 ) {
+ printf( "5703S\n" );
+ } else if( IS_5704 ) {
+ printf( "5704" );
+
+ if( IS_SERDES ) {
+ printf( "S\n" );
+ } else {
+ printf( "C\n" );
+ }
+
+ } else if( IS_5714 ) {
+ printf( "5714\n" );
+ }
+#endif
+ /*
+ * setup register & memory base addresses of NIC
+ */
+ l_baseaddrL_u32 = (uint32_t) ~0xf &
+ (uint32_t) SLOF_pci_config_read32(PCI_BAR1_R);
+ /*l_baseaddrL_u32 = ( (uint32_t) ~0xf &
+ (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_BAR1_R ) );*/
+
+ l_baseaddrH_u32 = (uint32_t) SLOF_pci_config_read32(PCI_BAR2_R);
+ /*l_baseaddrH_u32 =
+ (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_BAR2_R );*/
+ bcm_baseaddr_u64 = (uint64_t) l_baseaddrH_u32;
+ bcm_baseaddr_u64 <<= 32;
+ bcm_baseaddr_u64 += (uint64_t) l_baseaddrL_u32;
+ bcm_baseaddr_u64 =
+ (uint64_t) SLOF_translate_my_address((void *)bcm_baseaddr_u64);
+ /*snk_kernel_interface->translate_addr(((void *)&(bcm_baseaddr_u64)));*/
+ bcm_memaddr_u64 = bcm_baseaddr_u64 + BCM_MEMORY_OFFS;
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: device's register base high address = 0x%08X\n", l_baseaddrH_u32 );
+ printf( "bcm57xx: device's register base low address = 0x%08X\n", l_baseaddrL_u32 );
+ printf( "bcm57xx: device's register address = 0x%llx\n", bcm_baseaddr_u64 );
+#endif
+
+ /*
+ * 57xx hardware initialization
+ * BCM57xx Programmer's Guide: Section 8, "Initialization"
+ * steps 1 through 101
+ */
+
+ // step 1: enable bus master & memory space in command reg
+ i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) );
+ SLOF_pci_config_write16(PCI_COM_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_COM_R,
+ ( int ) i );*/
+ // step 2: disable & mask interrupts & enable pci byte/word swapping & enable indirect addressing mode
+ i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) );
+
+ SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_MISC_HCTRL_R,
+ ( int ) i );*/
+
+ /*
+ * from now on access may be made through the local
+ * read/write functions
+ */
+
+ // step 3: Save ahche line size register
+ // omitted, because register is not used for 5704
+
+ // step 4: acquire the nvram lock
+ if( bcm_nvram_lock() != 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: locking NVRAM failed\n" );
+#endif
+ return -1;
+ }
+
+ // step 5: prepare the chip for writing TG3_MAGIC_NUMBER
+ bcm_setb_reg32( MEMARB_MODE_R, BIT32( 1 ) );
+ i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) );
+ SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_MISC_HCTRL_R,
+ ( int ) i );*/
+ bcm_write_reg32( MODE_CTRL_R, BIT32( 23 ) | BIT32( 20 ) |
+ BIT32( 17 ) | BIT32( 16 ) |
+ BIT32( 14 ) | BIT32( 13 ) |
+ BIT32( 5 ) | BIT32( 4 ) |
+ BIT32( 2 ) | BIT32( 1 ) );
+
+ // step 6: write TG3_MAGIC_NUMBER
+ bcm_write_mem32( BCM_FW_MBX, BCM_MAGIC_NUMBER );
+
+ // step 7: reset core clocks
+
+ if( IS_5714 ) {
+ bcm_setb_reg32( MISC_CFG_R, BIT32( 26 ) | BIT32( 0 ) );
+ } else {
+ bcm_setb_reg32( MISC_CFG_R, BIT32( 0 ) );
+ }
+ // step 8
+ SLOF_msleep( 20 );
+
+ // step 9: disable & mask interrupts & enable indirect addressing mode &
+ // enable pci byte/word swapping initialize the misc host control register
+ i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) );
+ SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_MISC_HCTRL_R,
+ ( int ) i );*/
+
+ // step 10: set but master et cetera
+ i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) );
+ SLOF_pci_config_write16(PCI_COM_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_COM_R,
+ ( int ) i );*/
+
+ // step 11: disable PCI-X relaxed ordering
+ bcm_clrb_reg16( PCI_X_COM_R, BIT16( 1 ) );
+
+ // step 12: enable the MAC memory arbiter
+ bcm_setb_reg32( MEMARB_MODE_R, BIT32( 1 ) );
+
+ // step 13: omitted, only for BCM5700
+ // step 14: s. step 10
+ i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) );
+ SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_MISC_HCTRL_R,
+ ( int ) i );*/
+ // step 15: set byte swapping (incl. step 27/28/29/30)
+ // included prohibition of tx/rx interrupts
+ bcm_write_reg32( MODE_CTRL_R, BIT32( 23 ) | BIT32( 20 ) |
+ BIT32( 17 ) | BIT32( 16 ) |
+ BIT32( 14 ) | BIT32( 13 ) |
+ BIT32( 5 ) | BIT32( 4 ) |
+ BIT32( 2 ) | BIT32( 1 ) );
+ // step 16: omitted
+ i = 1000;
+ while( ( --i ) &&
+ ( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) ) {
+#ifdef BCM_DEBUG
+ printf( "." );
+#endif
+ SLOF_msleep( 1 );
+ }
+
+ // return on error
+ if( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) {
+ printf( "bootcode not loaded: %x\n", bcm_read_mem32( BCM_FW_MBX ) );
+#ifdef BCM_DEBUG
+ printf( "failed\n" );
+#endif
+ return -1;
+ }
+
+
+ // if ASF Firmware enabled
+ bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START );
+ SLOF_msleep( 10 );
+
+ // step 17: write ethernet mac mode register
+ /*
+ * WY 07.02.07
+ * omitted for correct SOL function
+ */
+ /*
+ if( IS_SERDES ) {
+ bcm_write_reg32( ETH_MAC_MODE_R, (uint32_t) 0xc );
+ } else {
+ bcm_write_reg32( ETH_MAC_MODE_R, (uint32_t) 0x0 );
+ }
+ */
+
+ // step 18/19: omitted
+ // step 20: enable hw bugfix for 5704
+ if( IS_5704 || IS_5703 ) {
+ bcm_setb_reg32( MSG_DATA_R, BIT32( 26 ) |
+ BIT32( 28 ) |
+ BIT32( 29 ) );
+ }
+
+ // step 21: omitted
+ // step 22: omitted
+ // step 23: 5704 clear statistics block
+ if( IS_5703 || IS_5704 ) {
+ memset_ci( (void *) ( bcm_memaddr_u64 + BCM_STATISTIC_OFFS ),
+ 0,
+ BCM_STATISTIC_SIZE );
+ }
+
+ // step 24/25: omitted
+ // step 26: set DMA Read/Write Control register
+ // NOTE: recommended values from the spec are used here
+ if( IS_5714 ) {
+ bcm_write_reg32( DMA_RW_CTRL_R, DMA_RW_CTRL_VAL_5714 );
+ } else {
+ uint32_t l_PCIState_u32 = bcm_read_reg32( PCI_STATE_R );
+ uint32_t l_DMAVal_u32 = DMA_RW_CTRL_VAL;
+
+ if( ( l_PCIState_u32 & BIT32( 2 ) ) != 0 ) { // PCI
+ l_DMAVal_u32 |= (uint32_t) 0x300000;
+ } else { // PCI-X
+ l_DMAVal_u32 |= (uint32_t) 0x900000;
+
+ if( ( bcm_read_reg32( PCI_CLK_CTRL_R ) & (uint32_t) 0x1f )
+ >= (uint32_t) 6 ) {
+ l_DMAVal_u32 |= (uint32_t) 0x4000;
+ }
+
+ }
+
+ bcm_write_reg32( DMA_RW_CTRL_R, l_DMAVal_u32 );
+ }
+
+ // step 27/28/29: s. step 14
+
+ // step 30: Configure TCP/UDP pseudo header checksum offloading
+ // already done in step 14: offloading disabled
+
+ // step 31: setup timer prescaler
+ i = bcm_read_reg32( MISC_CFG_R );
+ i &= (uint32_t) ~0xfe; // clear bits 7-1 first
+ i |= ( BCM_TMR_PRESCALE << 1 );
+ bcm_write_reg32( MISC_CFG_R, i );
+
+ // step 32: 5703/4 configure Mbuf pool address/length
+ // step 33: 5703/4 configure MAC DMA resource pool
+ // step 34: configure MAC memory pool watermarks
+ // step 35: 5703/4 configure DMA resource watermarks
+ // using recommended settings (hard coded)
+ if( IS_5703 || IS_5704 ) {
+
+ if( IS_5703 ) {
+ bcm_write_reg32( MBUF_POOL_ADDR_R, (uint32_t) 0x8000 );
+ bcm_write_reg32( MBUF_POOL_LEN_R, (uint32_t) 0x18000 );
+ } else {
+ bcm_write_reg32( MBUF_POOL_ADDR_R, (uint32_t) 0x10000 );
+ bcm_write_reg32( MBUF_POOL_LEN_R, (uint32_t) 0x10000 );
+ }
+
+ bcm_write_reg32( DMA_DESC_POOL_ADDR_R, (uint32_t) 0x2000 );
+ bcm_write_reg32( DMA_DESC_POOL_LEN_R, (uint32_t) 0x2000 );
+
+ bcm_write_reg32( DMA_RMBUF_LOW_WMARK_R, (uint32_t) 0x50 );
+ bcm_write_reg32( MAC_RXMBUF_LOW_WMARK_R, (uint32_t) 0x20 );
+ bcm_write_reg32( MBUF_HIGH_WMARK_R, (uint32_t) 0x60 );
+
+ bcm_write_reg32( DMA_DESC_LOW_WM_R, (uint32_t) 5 );
+ bcm_write_reg32( DMA_DESC_HIGH_WM_R, (uint32_t) 10 );
+ } else {
+ bcm_write_reg32( DMA_RMBUF_LOW_WMARK_R, (uint32_t) 0x00 );
+ bcm_write_reg32( MAC_RXMBUF_LOW_WMARK_R, (uint32_t) 0x10 );
+ bcm_write_reg32( MBUF_HIGH_WMARK_R, (uint32_t) 0x60 );
+ }
+
+ // step 35: omitted
+ // step 36: Configure flow control behaviour
+ // using recommended settings (hard coded)
+ bcm_write_reg32( LOW_WMARK_MAX_RXFRAM_R, (uint32_t) 0x02 );
+
+ // step 37/38: enable buffer manager & wait for successful start
+ bcm_setb_reg32( BUF_MAN_MODE_R, BIT32( 2 ) | BIT32( 1 ) );
+
+ i = lc_Maxwait_u32;
+ while( ( --i ) &&
+ ( ( bcm_read_reg32( BUF_MAN_MODE_R ) & BIT32( 1 ) ) == 0 ) ) {
+ SLOF_usleep( 10 );
+ }
+
+ // return on error
+ if( i == 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: init step 38: enable buffer manager failed\n" );
+#endif
+ return -1;
+ }
+
+ // step 39: enable internal hardware queues
+ bcm_write_reg32( FTQ_RES_R, (uint32_t) ~0 );
+ bcm_write_reg32( FTQ_RES_R, (uint32_t) 0 );
+
+ // step 40/41/42: initialize rx producer ring
+ bcm_init_rxprod_ring();
+
+ // step 43: set rx producer ring replenish threshold
+ // using recommended setting of maximum allocated BD's/8
+ bcm_write_reg32( STD_RXPR_REP_THR_R, (uint32_t) BCM_MAX_RX_BUF / 8 );
+
+ // step 44/45/46: initialize send rings
+ bcm_init_tx_ring();
+ bcm_init_rxret_ring();
+
+ // steps 47-50 done in ring init functions
+ // step 51: configure MAC unicast address
+ bcm_nvram_init();
+ if( bcm_mac_init( (uint8_t *) mac_addr ) < 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: init step 51: configure MAC unicast address failed\n" );
+#endif
+ return -1;
+ }
+ memcpy(driver->mac_addr, mac_addr, 6);
+
+ // step 52: configure backoff random seed for transmit
+ // using recommended algorithm
+ i = (uint32_t) mac_addr[0] + (uint32_t) mac_addr[1] +
+ (uint32_t) mac_addr[2] + (uint32_t) mac_addr[3] +
+ (uint32_t) mac_addr[4] + (uint32_t) mac_addr[5];
+ i &= (uint32_t) 0x03ff;
+ bcm_write_reg32( ETH_TX_RND_BO_R, i );
+
+ // step 53: configure message transfer unit MTU size
+ bcm_write_reg32( RX_MTU_SIZE_R, (uint32_t) BCM_MTU_MAX_LEN );
+
+ // step 54: configure IPG for transmit
+ // using recommended value (through #define)
+ bcm_write_reg32( TX_MAC_LEN_R, TX_MAC_LEN_VAL );
+
+ // step 55: configure receive rules
+
+ // set RX rule default class
+ bcm_write_reg32( RX_RULE_CFG_R, RX_RULE_CFG_VAL );
+
+ // step 56: configure the number of receive lists
+ bcm_write_reg32( RX_LST_PLACE_CFG_R, RX_LST_PLC_CFG_VAL );
+ bcm_write_reg32( RX_LST_PLACE_STAT_EN_R, RX_LST_PLC_STAT_EN_VAL );
+
+/*
+ // rule 1: accept frames for our MAC address
+ bcm_write_reg32( RX_RULE_CTRL_R ( 0 ),
+ BIT32( 31 ) | // enable rule
+ BIT32( 30 ) | // and with next
+ BIT32( 26 ) | // split value register
+ BIT32( 8 ) ); // class 1
+ bcm_write_reg32( RX_RULE_VAL_R ( 0 ),
+ (uint32_t) 0xffff0000 |
+ ( bcm_read_reg32( MAC_ADDR_OFFS_HI(0) ) &
+ (uint32_t) 0xffff ) );
+
+ bcm_write_reg32( RX_RULE_CTRL_R ( 1 ),
+ BIT32( 31 ) | // enable rule
+ BIT32( 8 ) | // class 1
+ BIT32( 1 ) ); // offset 2
+ bcm_write_reg32( RX_RULE_VAL_R ( 1 ),
+ bcm_read_reg32( MAC_ADDR_OFFS_LO(0) ) );
+
+ // rule 2: accept broadcast frames
+ bcm_write_reg32( RX_RULE_CTRL_R ( 2 ),
+ BIT32( 31 ) | // enable rule
+ BIT32( 30 ) | // and with next
+ BIT32( 26 ) | // split value register
+ BIT32( 8 ) ); // class 1
+ bcm_write_reg32( RX_RULE_VAL_R ( 2 ),
+ (uint32_t) ~0 );
+
+ bcm_write_reg32( RX_RULE_CTRL_R ( 3 ),
+ BIT32( 31 ) | // enable rule
+ BIT32( 8 ) | // class 1
+ BIT32( 1 ) ); // offset 2
+ bcm_write_reg32( RX_RULE_VAL_R ( 3 ),
+ (uint32_t) ~0 );
+*/
+ for( i=0; i<NUM_RX_RULE_ASF; ++i) {
+ bcm_write_reg32( RX_RULE_CTRL_R ( i ), 0 );
+ bcm_write_reg32( RX_RULE_VAL_R ( i ), 0 );
+ }
+
+ // step 57-60: enable rx/tx statistics
+ // omitted, no need for statistics (so far)
+
+ // step 61/62: disable host coalescing engine/wait 20ms
+ bcm_write_reg32( HOST_COAL_MODE_R, (uint32_t) 0 );
+
+ i = lc_Maxwait_u32 * 2;
+ while( ( --i ) &&
+ ( bcm_read_reg32( HOST_COAL_MODE_R ) != 0 ) ) {
+ SLOF_usleep( 10 );
+ }
+
+ // return on error
+ if( i == 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: init step 62: disable host coal. engine failed\n" );
+#endif
+ return -1;
+ }
+
+ // step 63-66: initialize coalescing engine
+ // NOTE: status block is unused in this driver,
+ // therefore the coal. engine status block
+ // automatic update is disabled (by writing
+ // 0 to every counter
+ bcm_write_reg32( RX_COAL_TICKS_R, 0 );
+ bcm_write_reg32( TX_COAL_TICKS_R, 0 );
+ bcm_write_reg32( RX_COAL_MAX_BD_R, 0 );
+ bcm_write_reg32( TX_COAL_MAX_BD_R, 0 );
+ bcm_write_reg32( RX_COAL_TICKS_INT_R, 0 );
+ bcm_write_reg32( TX_COAL_TICKS_INT_R, 0 );
+ bcm_write_reg32( RX_COAL_MAX_BD_INT_R, 0 );
+ bcm_write_reg32( TX_COAL_MAX_BD_INT_R, 0 );
+
+ // step 67: initialize host status block address
+ // NOTE: status block is not needed in this driver,
+ // still it needs to be set up
+ i = (uint32_t) ( (uint64_t) &bcm_status >> 32 );
+ bcm_write_reg32( STB_HOST_ADDR_HI_R, i );
+ i = (uint32_t) ( (uint64_t) &bcm_status & (uint64_t) 0xffffffff );
+ bcm_write_reg32( STB_HOST_ADDR_LO_R, i );
+
+ // 5704/3 adaption
+ if( IS_5703 || IS_5704 ) {
+ // step 68: 5704, for now omitted
+ // step 69: 5704 set the statistics coalescing tick counter
+ bcm_write_reg32( STAT_TICK_CNT_R, 0 );
+ // step 70: 5704 configure statistics block address in NIC memory
+ // using recommended values (hard coded)
+ bcm_write_reg32( STAT_NIC_ADDR_R, (uint32_t) 0x300 );
+ // step 71: 5704 configure status block address in NIC memory
+ // using recommended values (hard coded)
+ bcm_write_reg32( STB_NIC_ADDR_R, (uint32_t) 0xb00 );
+ }
+
+ // step 72: enable host coalescing engine
+ bcm_setb_reg32( HOST_COAL_MODE_R, BIT32( 12 ) | BIT32( 11 ) | BIT32( 1 ) );
+
+ // step 73: enable rx bd completion functional block
+ bcm_write_reg32( RX_BD_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) );
+
+ // step 74: enable rx list placement functional block
+ bcm_write_reg32( RX_LST_PLACE_MODE_R, BIT32( 1 ) );
+ // 5704/3 adaption
+ if( IS_5703 || IS_5704 ) {
+ // step 75: 5704/3 enable receive list selector func block
+ bcm_write_reg32( RX_LST_SEL_MODE_R, BIT32( 1 ) | BIT32( 2 ) );
+ }
+
+ // step 76: enable DMA engines
+ bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 23 ) | BIT32( 22 ) | BIT32( 21 ) );
+ /*
+ * WY 26.10.07 This is wrong for 5714, better leave it alone
+ if( IS_5714 ) {
+ bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 20 ) );
+ }
+ */
+
+ // step 77: omitted, statistics are not used
+ // step 78: Configure the General Misc Local Control register
+ // NOTE: as known so far nothing needs to be done here,
+ // default values should work fine
+ //bcm_setb_reg32( MISC_LOCAL_CTRL_R, 0 );
+
+ // step 79: clear interrupts in INT_MBX0_R low word
+ bcm_write_reg32( INT_MBX0_R, 0 );
+ // 5704/3 adaption
+ // step 80: 5704/3 enable DMA completion functional block
+ if( IS_5703 || IS_5704 ) {
+ bcm_write_reg32( DMA_COMPL_MODE_R, BIT32( 1 ) );
+ }
+
+ // step 81/82: configure write/read DMA mode registers
+ // disable MSI
+ bcm_write_reg32( RD_DMA_MODE_R, BIT32( 10 ) | BIT32( 9 ) | BIT32( 8 ) |
+ BIT32( 7 ) | BIT32( 6 ) | BIT32( 5 ) |
+ BIT32( 4 ) | BIT32( 3 ) | BIT32( 2 ) |
+ BIT32( 1 ) );
+ bcm_write_reg32( WR_DMA_MODE_R, BIT32( 9 ) | BIT32( 8 ) | BIT32( 7 ) |
+ BIT32( 6 ) | BIT32( 5 ) | BIT32( 4 ) |
+ BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) );
+ bcm_clrb_reg32( MSI_MODE_R, BIT32( 1 ) );
+ SLOF_usleep( 100 );
+
+ // step 83-91: enable all these functional blocks...
+ bcm_write_reg32( RX_DAT_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) );
+
+ if( IS_5703 || IS_5704 ) {
+ bcm_write_reg32( MBUF_CLSTR_FREE_MODE_R, BIT32( 1 ) );
+ }
+
+ bcm_write_reg32( TX_DAT_COMPL_MODE_R, BIT32( 1 ) );
+ bcm_write_reg32( TX_BD_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) );
+ bcm_write_reg32( RX_BD_INIT_MODE_R, BIT32( 1 ) | BIT32( 2 ) );
+ bcm_write_reg32( RX_DAT_BD_INIT_MODE_R, BIT32( 1 ) );
+ bcm_write_reg32( TX_DAT_INIT_MODE_R, BIT32( 1 ) | BIT32( 3 ) );
+ bcm_write_reg32( TX_BD_INIT_MODE_R, BIT32( 1 ) | BIT32( 2 ) );
+ bcm_write_reg32( TX_BD_RING_SEL_MODE_R, BIT32( 1 ) | BIT32( 2 ) );
+
+ // step 92: omitted
+ // step 93/94: Enable Tx/Rx MAC
+ bcm_setb_reg32( TX_MAC_MODE_R, BIT32( 1 ) );
+// bcm_setb_reg32( RX_MAC_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); // set BIT32( 8 ) for promiscious mode!
+ bcm_setb_reg32( RX_MAC_MODE_R, BIT32( 1 ) ); // set BIT32( 8 ) for promiscious mode!
+ // set BIT32( 10) for VLAN
+
+ // step 95: disable auto polling:
+ // bcm_phy_init takes care of this
+ // step 96: omitted
+ // step 97: omitted, may change though, but is not important
+ // step 98: activate link & enable MAC functional block
+ // NOTE autopolling is enabled so bit 0 needs not to be set
+ //bcm_setb_reg32( MI_STATUS_R, BIT32( 0 ) );
+
+ // step 99: setup PHY
+ // return if link is down
+ if( bcm_phy_init() < 0 ) {
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: init step 99: PHY initialization failed\n" );
+#endif
+ return -1;
+ }
+
+ // step 100: setup multicast filters
+ bcm_write_reg32( MAC_HASH0_R, (uint32_t) 0 );
+ bcm_write_reg32( MAC_HASH1_R, (uint32_t) 0 );
+ bcm_write_reg32( MAC_HASH2_R, (uint32_t) 0 );
+ bcm_write_reg32( MAC_HASH3_R, (uint32_t) 0 );
+/*
+ // accept all multicast frames
+ bcm_write_reg32( MAC_HASH0_R, (uint32_t) 0xffffffff );
+ bcm_write_reg32( MAC_HASH1_R, (uint32_t) 0xffffffff );
+ bcm_write_reg32( MAC_HASH2_R, (uint32_t) 0xffffffff );
+ bcm_write_reg32( MAC_HASH3_R, (uint32_t) 0xffffffff );
+*/
+ // step 101: omitted, no interrupts used
+
+ // make initial receive buffers available for NIC
+ // this step has to be done here after RX DMA engine has started (step 94)
+ bcm_write_reg32( RXPROD_PROD_IND, BCM_MAX_RX_BUF );
+
+ // if ASF Firmware enabled
+ bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START_DONE );
+ SLOF_msleep( 10 );
+
+ // enable heartbeat timer
+
+ bcm_write_reg32( ASF_HEARTBEAT_TIMER_R, 0x5 );
+
+ driver->running = 1;
+ // off we go..
+ return 0;
+}
+
+static int
+bcm_reset( void )
+{
+ uint32_t i;
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: resetting controller.." );
+#endif
+
+ bcm_write_mem32( BCM_FW_MBX, BCM_MAGIC_NUMBER );
+
+ if( IS_5714 ) {
+ bcm_setb_reg32( MISC_CFG_R, BIT32( 26 ) | BIT32( 0 ) );
+ } else {
+ bcm_setb_reg32( MISC_CFG_R, BIT32( 0 ) );
+ }
+
+ SLOF_msleep( 20 );
+
+ /*
+ * after reset local read/write functions cannot be used annymore
+ * until bus master & stuff is set up again
+ */
+
+ i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) );
+ SLOF_pci_config_write16(PCI_COM_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_COM_R,
+ ( int ) i );*/
+
+ // step 9 & 13: disable & mask interrupts & enable indirect addressing mode &
+ // enable pci byte/word swapping initialize the misc host control register
+ i = ( BIT32( 7 ) | BIT32( 5 ) | BIT32( 4 ) |
+ BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) );
+ SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_MISC_HCTRL_R,
+ ( int ) i );*/
+
+ // step 16: poll for bootcode completion by waiting for the one's
+ // complement of the magic number previously written
+ i = 1000;
+ while( ( --i ) &&
+ ( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) ) {
+#ifdef BCM_DEBUG
+ printf( "." );
+#else
+ SLOF_msleep( 1 );
+#endif
+ }
+
+ // return on error
+ if( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) {
+#ifdef BCM_DEBUG
+ printf( "failed\n" );
+#endif
+ return -1;
+ }
+
+#ifdef BCM_DEBUG
+ printf( "done\n" );
+#endif
+ return 0;
+}
+
+static int
+bcm_term( void )
+{
+ uint32_t i;
+ uint16_t v;
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: driver shutdown.." );
+#endif
+
+ /*
+ * halt ASF firmware
+ */
+ bcm_fw_halt();
+
+ /*
+ * unload ASF firmware
+ */
+ bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD );
+
+ /*
+ * disable RX producer rings
+ */
+ bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_JUM ), RCB_FLAG_RING_DISABLED );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_JUM ), 0 );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_JUM ), 0 );
+ bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_JUM ), 0 );
+
+ bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), RCB_FLAG_RING_DISABLED );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ), 0 );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ), 0 );
+ bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ), 0 );
+
+ bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_MIN ), RCB_FLAG_RING_DISABLED );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_MIN ), 0 );
+ bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_MIN ), 0 );
+ bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_MIN ), 0 );
+
+ /*
+ * disable RX return rings
+ */
+ v = BCM_RXRET_RCB_OFFS;
+ for( i = 0; i < BCM_MAX_RXRET_RING; i++ ) {
+ bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 );
+ bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 );
+
+ v += BCM_RCB_SIZE_u16;
+ }
+
+ /*
+ * disable TX rings
+ */
+ v = BCM_TX_RCB_OFFS;
+ for( i = 0; i < BCM_MAX_TX_RING; i++ ) {
+ bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 );
+ bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 );
+ bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 );
+
+ v += BCM_RCB_SIZE_u16;
+ }
+
+ /*
+ * remove receive rules
+ */
+ bcm_write_reg32( RX_RULE_CTRL_R ( 0 ), 0 );
+ bcm_write_reg32( RX_RULE_VAL_R ( 0 ), 0 );
+ bcm_write_reg32( RX_RULE_CTRL_R ( 1 ), 0 );
+ bcm_write_reg32( RX_RULE_VAL_R ( 1 ), 0 );
+
+ /*
+ * shutdown sequence
+ * BCM57xx Programmer's Guide: Section 8, "Shutdown"
+ * the enable bit of every state machine of the 57xx
+ * has to be reset.
+ */
+
+ /*
+ * receive path shutdown sequence
+ */
+ bcm_clr_wait_bit32( RX_MAC_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( RX_LST_PLACE_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( RX_BD_INIT_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( RX_DAT_BD_INIT_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( RX_DAT_COMPL_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( RX_BD_COMPL_MODE_R, BIT32( 1 ) );
+
+ if( IS_5704 || IS_5703 ) {
+ bcm_clr_wait_bit32( RX_LST_SEL_MODE_R, BIT32( 1 ) );
+ }
+
+ /*
+ * transmit path & memory shutdown sequence
+ */
+ bcm_clr_wait_bit32( TX_BD_RING_SEL_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( TX_BD_INIT_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( TX_DAT_INIT_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( RD_DMA_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( TX_DAT_COMPL_MODE_R, BIT32( 1 ) );
+
+ if( IS_5704 ) {
+ bcm_clr_wait_bit32( DMA_COMPL_MODE_R, BIT32( 1 ) );
+ }
+
+ bcm_clr_wait_bit32( TX_BD_COMPL_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( ETH_MAC_MODE_R, BIT32( 21 ) );
+ bcm_clr_wait_bit32( TX_MAC_MODE_R, BIT32( 1 ) );
+
+ bcm_clr_wait_bit32( HOST_COAL_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( WR_DMA_MODE_R, BIT32( 1 ) );
+
+ if( IS_5704 || IS_5703 ) {
+ bcm_clr_wait_bit32( MBUF_CLSTR_FREE_MODE_R, BIT32( 1 ) );
+ }
+
+ bcm_write_reg32( FTQ_RES_R, (uint32_t) ~0 );
+ bcm_write_reg32( FTQ_RES_R, (uint32_t) 0 );
+
+ if( IS_5704 || IS_5703 ) {
+ bcm_clr_wait_bit32( BUF_MAN_MODE_R, BIT32( 1 ) );
+ bcm_clr_wait_bit32( MEMARB_MODE_R, BIT32( 1 ) );
+ }
+
+#ifdef BCM_DEBUG
+ printf( "done.\n" );
+#endif
+ /*
+ * controller reset
+ */
+ if( bcm_reset() != 0 ) {
+ return -1;
+ }
+
+ /*
+ * restart ASF firmware
+ */
+ bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD );
+ SLOF_msleep( 10 );
+ bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD_DONE );
+ SLOF_msleep( 100 );
+ bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START );
+ SLOF_msleep( 10 );
+ bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START_DONE );
+
+ /*
+ * activate Wake-on-LAN
+ */
+ bcm_wol_activate();
+
+ /*
+ * PCI shutdown
+ */
+ bcm_clrb_reg32( PCI_MISC_HCTRL_R, BIT32( 3 ) | BIT32( 2 ) );
+
+ /*
+ * from now on local rw functions cannot be used anymore
+ */
+
+// bcm_clrb_reg32( PCI_COM_R, BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) );
+
+ SLOF_pci_config_write32(PCI_COM_R, BIT32(8) | BIT32(6));
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_COM_R,
+ BIT32(8) | BIT32(6) );*/
+
+ // no more networking...
+ return 0;
+}
+
+static int
+bcm_getmac(uint32_t addr, char mac[6])
+{
+ uint32_t t1, t2;
+ uint64_t t3;
+
+ if (bcm_nvram_read(addr, &t1, 1) != 0)
+ return -1;
+ if (bcm_nvram_read(addr+4, &t2, 1) != 0)
+ return -1;
+ t3 = ((uint64_t)t1 << 32) + t2;
+
+ mac[0] = (t3 >> 40) & 0xFF;
+ mac[1] = (t3 >> 32) & 0xFF;
+ mac[2] = (t3 >> 24) & 0xFF;
+ mac[3] = (t3 >> 16) & 0xFF;
+ mac[4] = (t3 >> 8) & 0xFF;
+ mac[5] = (t3 >> 0) & 0xFF;
+
+ return 0;
+}
+
+static char*
+print_itoa(char *text, uint32_t value)
+{
+ if(value >= 10)
+ text = print_itoa(text, value / 10);
+ *text = '0' + (value % 10);
+ ++text;
+ return text;
+}
+
+static int
+bcm_get_version(char *text)
+{
+ uint32_t t1;
+
+ if (bcm_nvram_read(0x94, &t1, 1) != 0)
+ return -1;
+
+ text = print_itoa(text, (t1 >> 8) & 0xFF);
+ text[0] = '.';
+ text = print_itoa(&text[1], t1 & 0xFF);
+ text[0] = '\n';
+ return 0;
+}
+
+static uint32_t
+util_gen_crc( char *pcDatabuf, uint32_t ulDatalen, uint32_t ulCrc_in)
+{
+ unsigned char data;
+ uint32_t idx, bit, crc = ulCrc_in;
+
+ for(idx = 0; idx < ulDatalen; idx++) {
+ data = *pcDatabuf++;
+ for(bit = 0; bit < 8; bit++, data >>= 1) {
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ?
+ CRC32_POLYNOMIAL : 0);
+ }
+ }
+ return bswap_32(~crc);
+}
+
+static int
+bcm_setmac(char mac_addr1[6], char mac_addr2[6])
+{
+ uint64_t mac1 = 0, mac2 = 0;
+ uint32_t manu[MANUFACTURING_INFO_SIZE/4];
+ int addr, i;
+ uint32_t crc, val1, val2, val3, val4;
+
+#ifdef BCM_DEBUG
+ printf("Flashing MAC 1: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ ((unsigned int) mac_addr1[0]) & 0xFF,
+ ((unsigned int) mac_addr1[1]) & 0xFF,
+ ((unsigned int) mac_addr1[2]) & 0xFF,
+ ((unsigned int) mac_addr1[3]) & 0xFF,
+ ((unsigned int) mac_addr1[4]) & 0xFF,
+ ((unsigned int) mac_addr1[5]) & 0xFF);
+
+ printf("Flashing MAC 2: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ ((unsigned int) mac_addr2[0]) & 0xFF,
+ ((unsigned int) mac_addr2[1]) & 0xFF,
+ ((unsigned int) mac_addr2[2]) & 0xFF,
+ ((unsigned int) mac_addr2[3]) & 0xFF,
+ ((unsigned int) mac_addr2[4]) & 0xFF,
+ ((unsigned int) mac_addr2[5]) & 0xFF);
+#endif
+
+ mac1 |= ((uint64_t) mac_addr1[0]) & 0xFF; mac1 = mac1 << 8;
+ mac1 |= ((uint64_t) mac_addr1[1]) & 0xFF; mac1 = mac1 << 8;
+ mac1 |= ((uint64_t) mac_addr1[2]) & 0xFF; mac1 = mac1 << 8;
+ mac1 |= ((uint64_t) mac_addr1[3]) & 0xFF; mac1 = mac1 << 8;
+ mac1 |= ((uint64_t) mac_addr1[4]) & 0xFF; mac1 = mac1 << 8;
+ mac1 |= ((uint64_t) mac_addr1[5]) & 0xFF;
+
+ mac2 |= ((uint64_t) mac_addr2[0]) & 0xFF; mac2 = mac2 << 8;
+ mac2 |= ((uint64_t) mac_addr2[1]) & 0xFF; mac2 = mac2 << 8;
+ mac2 |= ((uint64_t) mac_addr2[2]) & 0xFF; mac2 = mac2 << 8;
+ mac2 |= ((uint64_t) mac_addr2[3]) & 0xFF; mac2 = mac2 << 8;
+ mac2 |= ((uint64_t) mac_addr2[4]) & 0xFF; mac2 = mac2 << 8;
+ mac2 |= ((uint64_t) mac_addr2[5]) & 0xFF;
+
+ /* Extract the manufacturing data, starts at 0x74 */
+ if(bcm_nvram_lock() == -1) {
+ return -1;
+ }
+
+ addr = 0x74;
+ for (i = 0; i < (MANUFACTURING_INFO_SIZE/4); i++) {
+ if (bcm_nvram_read(addr, &manu[i], 0) != 0) {
+ printf("\nREAD FAILED\n");
+ bcm_nvram_unlock();
+ return -1;
+ }
+ addr+=4;
+ }
+ bcm_nvram_unlock();
+
+ /* Store the new MAC address in the manufacturing data */
+ val1 = mac1 >> 32;
+ val2 = mac1 & 0xFFFFFFFF;
+ val3 = mac2 >> 32;
+ val4 = mac2 & 0xFFFFFFFF;
+ manu[(0x7C-0x74)/4] = val1;
+ manu[(0x80-0x74)/4] = val2;
+ manu[(0xCC-0x74)/4] = val3;
+ manu[(0xD0-0x74)/4] = val4;
+
+ /* Calculate the new manufacturing datas CRC */
+ crc = util_gen_crc(((char *)manu),
+ MANUFACTURING_INFO_SIZE - 4, 0xFFFFFFFF);
+
+ /* Now write the new MAC addresses and CRC */
+ if ((bcm_nvram_write(0x7C, val1, 1) != 0) ||
+ (bcm_nvram_write(0x80, val2, 1) != 0) ||
+ (bcm_nvram_write(0xCC, val3, 1) != 0) ||
+ (bcm_nvram_write(0xD0, val4, 1) != 0) ||
+ (bcm_nvram_write(0xFC, crc, 1) != 0) )
+ {
+ /* Disastor ! */
+#ifdef BCM_DEBUG
+ printf("failed to write MAC address\n");
+#endif
+ return -1;
+ }
+
+ /* Success !!!! */
+ return 0;
+}
+
+static int
+bcm_ioctl( int request, void* data )
+{
+ uint32_t l_baseaddrL_u32;
+ uint32_t l_baseaddrH_u32;
+ uint32_t i;
+ int ret_val = 0;
+ char mac_addr[6];
+ ioctl_net_data_t *ioctl_data = (ioctl_net_data_t*) data;
+
+ if(request != SIOCETHTOOL) {
+ return -1;
+ }
+
+#ifdef BCM_DEBUG
+ printf( "bcm57xx: detected device " );
+ if( IS_5703 ) {
+ printf( "5703S" );
+ } else if( IS_5704 ) {
+ printf( "5704" );
+ if( IS_SERDES ) {
+ printf( "S\n" );
+ } else {
+ printf( "C\n" );
+ }
+ } else if( IS_5714 ) {
+ printf( "5714\n" );
+ }
+#endif
+ /*
+ * setup register & memory base addresses of NIC
+ */
+ l_baseaddrL_u32 = (uint32_t) ~0xf &
+ SLOF_pci_config_read32(PCI_BAR1_R);
+ /*l_baseaddrL_u32 = ( (uint32_t) ~0xf &
+ (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_BAR1_R ) );*/
+
+ l_baseaddrH_u32 = SLOF_pci_config_read32(PCI_BAR2_R);
+ /*l_baseaddrH_u32 =
+ (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_BAR2_R );*/
+
+ bcm_baseaddr_u64 = (uint64_t) l_baseaddrH_u32;
+ bcm_baseaddr_u64 <<= 32;
+ bcm_baseaddr_u64 += (uint64_t) l_baseaddrL_u32;
+ bcm_baseaddr_u64 =
+ (uint64_t)SLOF_translate_my_address((void *)bcm_baseaddr_u64);
+ /*snk_kernel_interface->translate_addr(((void *)&(bcm_baseaddr_u64)));*/
+ bcm_memaddr_u64 = bcm_baseaddr_u64 + BCM_MEMORY_OFFS;
+
+ /*
+ * 57xx hardware initialization
+ * BCM57xx Programmer's Guide: Section 8, "Initialization"
+ * steps 1 through 101
+ */
+
+ // step 1: enable bus master & memory space in command reg
+ i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) );
+ SLOF_pci_config_write16(PCI_COM_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 2,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_COM_R,
+ ( int ) i );*/
+
+ // step 2: disable & mask interrupts & enable pci byte/word swapping & enable indirect addressing mode
+ i = ( BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) );
+ SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i);
+ /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid,
+ 4,
+ bcm_pcicfg_bus,
+ bcm_pcicfg_devfn,
+ PCI_MISC_HCTRL_R,
+ ( int ) i );*/
+
+ bcm_nvram_init();
+
+ switch(ioctl_data->subcmd) {
+ case ETHTOOL_GMAC:
+ switch(ioctl_data->data.mac.idx) {
+ case 0:
+ ret_val = bcm_getmac(0x7C, ioctl_data->data.mac.address);
+ break;
+ case 1:
+ ret_val = bcm_getmac(0xCC, ioctl_data->data.mac.address);
+ break;
+ default:
+ ret_val = -1;
+ break;
+ }
+ break;
+ case ETHTOOL_SMAC:
+ switch(ioctl_data->data.mac.idx) {
+ case 0:
+ ret_val = bcm_getmac(0xCC, mac_addr);
+ if(ret_val == 0)
+ ret_val = bcm_setmac(ioctl_data->data.mac.address, mac_addr);
+ break;
+ case 1:
+ ret_val = bcm_getmac(0x7C, mac_addr);
+ if(ret_val == 0)
+ ret_val = bcm_setmac(mac_addr, ioctl_data->data.mac.address);
+ break;
+ default:
+ ret_val = -1;
+ break;
+ }
+ break;
+ case ETHTOOL_VERSION: {
+ char *text = ioctl_data->data.version.text;
+ memcpy(text, " BCM57xx Boot code level: ", 27);
+ ret_val = bcm_get_version(&text[27]);
+ break;
+ }
+ default:
+ ret_val = -1;
+ break;
+ }
+
+ bcm_term();
+ return ret_val;
+}
+
+net_driver_t *bcm57xx_open(void)
+{
+ net_driver_t *driver;
+ uint16_t vendor_id, device_id;
+
+ vendor_id = SLOF_pci_config_read16(0);
+ device_id = SLOF_pci_config_read16(2);
+ if (check_driver(vendor_id, device_id))
+ return NULL;
+
+ driver = SLOF_alloc_mem(sizeof(*driver));
+ if (!driver) {
+ printf("Unable to allocate virtio-net driver\n");
+ return NULL;
+ }
+ memset(driver, 0, sizeof(*driver));
+
+ if (bcm_init(driver))
+ goto FAIL;
+
+ return driver;
+
+FAIL: SLOF_free_mem(driver, sizeof(*driver));
+ return NULL;
+
+ return 0;
+}
+
+void bcm57xx_close(net_driver_t *driver)
+{
+ if (driver->running == 0)
+ return;
+
+ bcm_term();
+ driver->running = 0;
+ SLOF_free_mem(driver, sizeof(*driver));
+}
+
+int bcm57xx_read(char *buf, int len)
+{
+ if (buf)
+ return bcm_receive(buf, len);
+ return -1;
+}
+
+int bcm57xx_write(char *buf, int len)
+{
+ if (buf)
+ return bcm_xmit(buf, len);
+ return -1;
+}
diff --git a/roms/SLOF/lib/libbcm/bcm57xx.h b/roms/SLOF/lib/libbcm/bcm57xx.h
new file mode 100644
index 000000000..efaba60c6
--- /dev/null
+++ b/roms/SLOF/lib/libbcm/bcm57xx.h
@@ -0,0 +1,323 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <cache.h>
+#include <netdriver.h>
+
+// Debug switches
+//#define BCM_DEBUG // main debug switch, w/o it the other ones don't work
+//#define BCM_SHOW_RCV
+//#define BCM_SHOW_RCV_DATA
+//#define BCM_SHOW_XMIT
+//#define BCM_SHOW_XMIT_DATA
+//#define BCM_SHOW_XMIT_STATS
+//#define BCM_SHOW_IDX
+//#define BCM_SHOW_STATS
+//#define BCM_SHOW_ASF_REGS
+
+// Switch to enable SW AUTO-NEG
+// don't try, it's still incomplete
+//#define BCM_SW_AUTONEG
+
+/*
+ * used register offsets
+ */
+// PCI command register
+#define PCI_COM_R ( (uint16_t) 0x0004 )
+// PCI Cache Line Size register
+#define PCI_CACHELS_R ( (uint16_t) 0x000c )
+// PCI bar1 register
+#define PCI_BAR1_R ( (uint16_t) 0x0010 )
+// PCI bar2 register
+#define PCI_BAR2_R ( (uint16_t) 0x0014 )
+// PCI bar1 register
+#define PCI_SUBID_R ( (uint16_t) 0x002e )
+// PCI-X Comand register
+#define PCI_X_COM_R ( (uint16_t) 0x0042 )
+// Message Data Register
+#define MSG_DATA_R ( (uint16_t) 0x0064 )
+// PCI misc host contrl register
+#define PCI_MISC_HCTRL_R ( (uint16_t) 0x0068 )
+// DMA Read/Write Control register
+#define DMA_RW_CTRL_R ( (uint16_t) 0x006c )
+// PCI State register
+#define PCI_STATE_R ( (uint16_t) 0x0070 )
+// PCI_Clock Control register
+#define PCI_CLK_CTRL_R ( (uint16_t) 0x0074 )
+// Register Base Address Register
+#define REG_BASE_ADDR_REG ( (uint16_t) 0x0078 )
+// Memory Window Base Address Register
+#define MEM_BASE_ADDR_REG ( (uint16_t) 0x007c )
+// Register Data Register
+#define REG_DATA_REG ( (uint16_t) 0x0080 )
+// Memory Window Data Register
+#define MEM_DATA_REG ( (uint16_t) 0x0084 )
+// MAC Function register
+#define MAC_FUNC_R ( (uint16_t) 0x00b8 )
+// Interrupt Mailbox 0 register
+#define INT_MBX0_R ( (uint16_t) 0x0204 )
+// Ethernet MAC Mode register
+#define ETH_MAC_MODE_R ( (uint16_t) 0x0400 )
+// Ethernet MAC Addresses registers
+#define MAC_ADDR_OFFS_HI( idx ) ( (uint16_t) ( (idx*2 + 0)*sizeof( uint32_t ) + 0x0410 ) )
+#define MAC_ADDR_OFFS_LO( idx ) ( (uint16_t) ( (idx*2 + 1)*sizeof( uint32_t ) + 0x0410 ) )
+// Ethernet MAC Status register
+#define ETH_MAC_STAT_R ( (uint16_t) 0x0404 )
+// Ethernet MAC Event Enable register
+#define ETH_MAC_EVT_EN_R ( (uint16_t) 0x0408 )
+// Ethernet Transmit Random Backoff register
+#define ETH_TX_RND_BO_R ( (uint16_t) 0x0438 )
+// Receive MTU Size register
+#define RX_MTU_SIZE_R ( (uint16_t) 0x043c )
+// Transmit 1000BASE-X Auto Negotiation register
+#define TX_1000BX_AUTONEG_R ( (uint16_t) 0x0444 )
+// Receive 1000BASE-X Auto Negotiation register
+#define RX_1000BX_AUTONEG_R ( (uint16_t) 0x0448 )
+// MI Communication register
+#define MI_COM_R ( (uint16_t) 0x044c )
+// MI Status Register
+#define MI_STATUS_R ( (uint16_t) 0x0450 )
+// MI Mode register
+#define MI_MODE_R ( (uint16_t) 0x0454 )
+// Transmit MAC Mode register
+#define TX_MAC_MODE_R ( (uint16_t) 0x045c )
+// Transmit MAC Length register
+#define TX_MAC_LEN_R ( (uint16_t) 0x0464 )
+// Receive MAC Mode register
+#define RX_MAC_MODE_R ( (uint16_t) 0x0468 )
+// MAC Hash 0 register* VPD Config:
+#define MAC_HASH0_R ( (uint16_t) 0x0470 )
+// MAC Hash 1 register
+#define MAC_HASH1_R ( (uint16_t) 0x0474 )
+// MAC Hash 2 register
+#define MAC_HASH2_R ( (uint16_t) 0x0478 )
+// MAC Hash 3 register
+#define MAC_HASH3_R ( (uint16_t) 0x047c )
+// Receive Rules Control register
+#define RX_RULE_CTRL_R( idx ) ( (uint16_t) ( idx*8 + 0x0480 ) )
+// Receive Rules Value register
+#define RX_RULE_VAL_R( idx ) ( (uint16_t) ( idx*8 + 0x0484 ) )
+// Receive Rules Configuration register
+#define RX_RULE_CFG_R ( (uint16_t) 0x0500 )
+// Low Watermark Max Receive Frames register
+#define LOW_WMARK_MAX_RXFRAM_R ( (uint16_t) 0x0504 )
+// SerDes Control Register
+#define SERDES_CTRL_R ( (uint16_t) 0x0590 )
+// Hardware Auto Negotiation Control Register
+#define HW_AUTONEG_CTRL_R ( (uint16_t) 0x05B0 )
+// Hardware Auto Negotiation Status Register
+#define HW_AUTONEG_STAT_R ( (uint16_t) 0x05B4 )
+// Send Data Initiator Mode register
+#define TX_DAT_INIT_MODE_R ( (uint16_t) 0x0c00 )
+// Send Data Completion Mode register
+#define TX_DAT_COMPL_MODE_R ( (uint16_t) 0x1000 )
+// Send BD Ring Selector Mode register
+#define TX_BD_RING_SEL_MODE_R ( (uint16_t) 0x1400 )
+// Send BD Initiator Mode register
+#define TX_BD_INIT_MODE_R ( (uint16_t) 0x1800 )
+// Send BD Completion Mode register
+#define TX_BD_COMPL_MODE_R ( (uint16_t) 0x1c00 )
+// Receive List Placement Mode register
+#define RX_LST_PLACE_MODE_R ( (uint16_t) 0x2000 )
+// Receive List Placement Configuration register
+#define RX_LST_PLACE_CFG_R ( (uint16_t) 0x2010 )
+// Receive List Placement Statistics Enable Mask register
+#define RX_LST_PLACE_STAT_EN_R ( (uint16_t) 0x2018 )
+// Receive Data & Receive BD Initiator Mode register
+#define RX_DAT_BD_INIT_MODE_R ( (uint16_t) 0x2400 )
+// Receive Data Completion Mode register
+#define RX_DAT_COMPL_MODE_R ( (uint16_t) 0x2800 )
+// Receive BD Initiator Mode register
+#define RX_BD_INIT_MODE_R ( (uint16_t) 0x2c00 )
+// Standard Receive Producer Ring Replenish Threshold register
+#define STD_RXPR_REP_THR_R ( (uint16_t) 0x2c18 )
+// Receive BD Completion Mode register
+#define RX_BD_COMPL_MODE_R ( (uint16_t) 0x3000 )
+// Receive List Selector Mode register
+#define RX_LST_SEL_MODE_R ( (uint16_t) 0x3400 )
+// MBUF Cluster Free Mode register
+#define MBUF_CLSTR_FREE_MODE_R ( (uint16_t) 0x3800 )
+// Host Coalescing Mode register
+#define HOST_COAL_MODE_R ( (uint16_t) 0x3c00 )
+// Receive Coalescing Ticks register
+#define RX_COAL_TICKS_R ( (uint16_t) 0x3c08 )
+// Send Coalescing Ticks register
+#define TX_COAL_TICKS_R ( (uint16_t) 0x3c0c )
+// Receive Max Coalesced BD Count register
+#define RX_COAL_MAX_BD_R ( (uint16_t) 0x3c10 )
+// Send Max Coalesced BD Count register
+#define TX_COAL_MAX_BD_R ( (uint16_t) 0x3c14 )
+// Receive Coalescing Ticks During Int register
+#define RX_COAL_TICKS_INT_R ( (uint16_t) 0x3c18 )
+// Send Coalescing Ticks During Int register
+#define TX_COAL_TICKS_INT_R ( (uint16_t) 0x3c1c )
+// Receive Max Coalesced BD Count During Int register
+#define RX_COAL_MAX_BD_INT_R ( (uint16_t) 0x3c18 )
+// Send Max Coalesced BD Count During Int register
+#define TX_COAL_MAX_BD_INT_R ( (uint16_t) 0x3c1c )
+// Statistics Ticks Counter register
+#define STAT_TICK_CNT_R ( (uint16_t) 0x3c28 )
+// Status Block Host Address Low register
+#define STB_HOST_ADDR_HI_R ( (uint16_t) 0x3c38 )
+// Status Block Host Address High register
+#define STB_HOST_ADDR_LO_R ( (uint16_t) 0x3c3c )
+// Statistics Base Address register
+#define STAT_NIC_ADDR_R ( (uint16_t) 0x3c40 )
+// Status Block Base Address register
+#define STB_NIC_ADDR_R ( (uint16_t) 0x3c44 )
+// Memory Arbiter Mode register
+#define MEMARB_MODE_R ( (uint16_t) 0x4000 )
+// Buffer Manager Mode register
+#define BUF_MAN_MODE_R ( (uint16_t) 0x4400 )
+// MBuf Pool Address register
+#define MBUF_POOL_ADDR_R ( (uint16_t) 0x4408 )
+// MBuf Pool Length register
+#define MBUF_POOL_LEN_R ( (uint16_t) 0x440c )
+// Read DMA Mbuf Low Watermark register
+#define DMA_RMBUF_LOW_WMARK_R ( (uint16_t) 0x4410 )
+// MAC Rx Mbuf Low Watermark register
+#define MAC_RXMBUF_LOW_WMARK_R ( (uint16_t) 0x4414 )
+// Mbuf High Watermark register
+#define MBUF_HIGH_WMARK_R ( (uint16_t) 0x4418 )
+// DMA Descriptor Pool Address register
+#define DMA_DESC_POOL_ADDR_R ( (uint16_t) 0x442c )
+// DMA Descriptor Pool Length register
+#define DMA_DESC_POOL_LEN_R ( (uint16_t) 0x4430 )
+// DMA Descriptor Low Watermark register
+#define DMA_DESC_LOW_WM_R ( (uint16_t) 0x4434 )
+// DMA Descriptor HIGH Watermark register
+#define DMA_DESC_HIGH_WM_R ( (uint16_t) 0x4438 )
+// Read DMA Mode register
+#define RD_DMA_MODE_R ( (uint16_t) 0x4800 )
+// Write DMA Mode register
+#define WR_DMA_MODE_R ( (uint16_t) 0x4c00 )
+// FTQ Reset register
+#define FTQ_RES_R ( (uint16_t) 0x5c00 )
+// MSI Mode register
+#define MSI_MODE_R ( (uint16_t) 0x6000 )
+// DMA completion Mode register
+#define DMA_COMPL_MODE_R ( (uint16_t) 0x6400 )
+// Mode Control register
+#define MODE_CTRL_R ( (uint16_t) 0x6800 )
+// Misc Configuration register
+#define MISC_CFG_R ( (uint16_t) 0x6804 )
+// Misc Local Control register
+#define MISC_LOCAL_CTRL_R ( (uint16_t) 0x6808 )
+// RX-Risc Mode Register
+#define RX_CPU_MODE_R ( (uint16_t) 0x5000 )
+// RX-Risc State Register
+#define RX_CPU_STATE_R ( (uint16_t) 0x5004 )
+// RX-Risc Program Counter
+#define RX_CPU_PC_R ( (uint16_t) 0x501c )
+// RX-Risc Event Register
+#define RX_CPU_EVENT_R ( (uint16_t) 0x6810 )
+// MDI Control register
+#define MDI_CTRL_R ( (uint16_t) 0x6844 )
+// WOL Mode register
+#define WOL_MODE_R ( (uint16_t) 0x6880 )
+// WOL Config register
+#define WOL_CFG_R ( (uint16_t) 0x6884 )
+// WOL Status register
+#define WOL_STATUS_R ( (uint16_t) 0x6888 )
+
+// ASF Control register
+#define ASF_CTRL_R ( (uint16_t) 0x6c00 )
+// ASF Watchdog Timer register
+#define ASF_WATCHDOG_TIMER_R ( (uint16_t) 0x6c0c )
+// ASF Heartbeat Timer register
+#define ASF_HEARTBEAT_TIMER_R ( (uint16_t) 0x6c10 )
+// Poll ASF Timer register
+#define ASF_POLL_TIMER_R ( (uint16_t) 0x6c14 )
+// Poll Legacy Timer register
+#define POLL_LEGACY_TIMER_R ( (uint16_t) 0x6c18 )
+// Retransmission Timer register
+#define RETRANSMISSION_TIMER_R ( (uint16_t) 0x6c1c )
+// Time Stamp Counter register
+#define TIME_STAMP_COUNTER_R ( (uint16_t) 0x6c20 )
+
+// NVM Command register
+#define NVM_COM_R ( (uint16_t) 0x7000 )
+// NVM Write register
+#define NVM_WRITE_R ( (uint16_t) 0x7008 )
+// NVM Address register
+#define NVM_ADDR_R ( (uint16_t) 0x700c )
+// NVM Read registertg3_phy_copper_begin
+#define NVM_READ_R ( (uint16_t) 0x7010 )
+// NVM Access register
+#define NVM_ACC_R ( (uint16_t) 0x7024 )
+// NVM Config 1 register
+#define NVM_CFG1_R ( (uint16_t) 0x7014 )
+// Software arbitration register
+#define SW_ARB_R ( (uint16_t) 0x7020 )
+
+/*
+ * useful def's
+ */
+#define rd08(a) ci_read_8((uint8_t *)(a))
+#define rd16(a) ci_read_16((uint16_t *)(a))
+#define rd32(a) ci_read_32((uint32_t *)(a))
+#define wr08(a,v) ci_write_8((uint8_t *)(a), (v))
+#define wr16(a,v) ci_write_16((uint16_t *)(a), (v))
+#define wr32(a,v) ci_write_32((uint32_t *)(a), (v))
+
+#define BIT08( bit ) ( (uint8_t) 0x1 << (bit) )
+#define BIT16( bit ) ( (uint16_t) 0x1 << (bit) )
+#define BIT32( bit ) ( (uint32_t) 0x1 << (bit) )
+
+/*
+ * type definition
+ */
+
+/*
+ * Constants for different kinds of IOCTL requests
+ */
+
+#define SIOCETHTOOL 0x1000
+
+/*
+ * special structure and constants for IOCTL requests of type ETHTOOL
+ */
+
+#define ETHTOOL_GMAC 0x03
+#define ETHTOOL_SMAC 0x04
+#define ETHTOOL_VERSION 0x05
+
+typedef struct {
+ int idx;
+ char address[6];
+} ioctl_ethtool_mac_t;
+
+typedef struct {
+ unsigned int length;
+ char *text;
+} ioctl_ethtool_version_t;
+
+
+/*
+ * default structure and constants for IOCTL requests
+ */
+
+#define IF_NAME_SIZE 0xFF
+
+typedef struct {
+ char if_name[IF_NAME_SIZE];
+ int subcmd;
+ union {
+ ioctl_ethtool_mac_t mac;
+ ioctl_ethtool_version_t version;
+ } data;
+} ioctl_net_data_t;
+
+extern net_driver_t *bcm57xx_open(void);
+extern void bcm57xx_close(net_driver_t *driver);
+extern int bcm57xx_read(char *buf, int len);
+extern int bcm57xx_write(char *buf, int len);
diff --git a/roms/SLOF/lib/libbootmenu/Makefile b/roms/SLOF/lib/libbootmenu/Makefile
new file mode 100644
index 000000000..156bc8bad
--- /dev/null
+++ b/roms/SLOF/lib/libbootmenu/Makefile
@@ -0,0 +1,49 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+ifndef TOP
+ TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd)
+ export TOP
+endif
+include $(TOP)/make.rules
+
+CFLAGS += -I. -I.. -I../libc/include -I$(SLOFCMNDIR) -I$(INCLCMNDIR)
+
+SRCS = bootmenu.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+TARGET = ../libbootmenu.a
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libbootmenu/bootmenu.c b/roms/SLOF/lib/libbootmenu/bootmenu.c
new file mode 100644
index 000000000..e5cb4b225
--- /dev/null
+++ b/roms/SLOF/lib/libbootmenu/bootmenu.c
@@ -0,0 +1,187 @@
+/*****************************************************************************
+ * Boot menu: Displays boot devices and waits for user to select one
+ *
+ * Copyright 2017 Red Hat, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth, Red Hat Inc. - initial implementation
+ *****************************************************************************/
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <paflof.h>
+#include <helpers.h>
+#include "bootmenu.h"
+
+#define MAX_DEVS 36 /* Enough for 10 digits + 26 letters */
+#define MAX_ALIAS_LEN 8 /* Maximum length of alias names */
+
+struct bootdev {
+ char alias[MAX_ALIAS_LEN];
+ char *path;
+};
+
+static int nr_devs;
+static struct bootdev bootdevs[MAX_DEVS];
+
+/**
+ * Look up an alias name.
+ * @return The NUL-terminated device tree path (should be released with free()
+ * when it's not required anymore), or NULL if it can't be found.
+ */
+static char *find_alias(char *alias)
+{
+ char *path;
+ long len;
+
+ forth_push((unsigned long)alias);
+ forth_push(strlen(alias));
+ forth_eval("find-alias");
+
+ len = forth_pop();
+ if (!len)
+ return NULL;
+
+ path = malloc(len + 1);
+ if (!path) {
+ puts("Out of memory in find_alias");
+ return NULL;
+ }
+ memcpy(path, (void *)forth_pop(), len);
+ path[len] = '\0';
+
+ return path;
+}
+
+static void bootmenu_populate_devs_alias(const char *alias)
+{
+ int idx;
+
+ for (idx = 0; idx <= 9 && nr_devs < MAX_DEVS; idx++, nr_devs++) {
+ char *cur_alias = bootdevs[nr_devs].alias;
+ if (idx == 0)
+ strcpy(cur_alias, alias);
+ else
+ sprintf(cur_alias, "%s%i", alias, idx);
+ bootdevs[nr_devs].path = find_alias(cur_alias);
+ if (!bootdevs[nr_devs].path)
+ break;
+ }
+}
+
+static void bootmenu_populate_devs(void)
+{
+ bootmenu_populate_devs_alias("cdrom");
+ bootmenu_populate_devs_alias("disk");
+ bootmenu_populate_devs_alias("net");
+}
+
+static void bootmenu_free_devs(void)
+{
+ while (nr_devs-- > 0) {
+ free(bootdevs[nr_devs].path);
+ bootdevs[nr_devs].path = NULL;
+ }
+}
+
+static void bootmenu_show_devs(void)
+{
+ int i;
+
+ for (i = 0; i < nr_devs; i++) {
+ printf("%c) %6s : %s\n", i < 9 ? '1' + i : 'a' + i - 9,
+ bootdevs[i].alias, bootdevs[i].path);
+ }
+}
+
+static bool has_key(void)
+{
+ forth_eval("key?");
+ return forth_pop();
+}
+
+static char get_key(void)
+{
+ forth_eval("key");
+ return forth_pop();
+}
+
+/* Flush pending key presses */
+static void flush_keys(void)
+{
+ uint32_t start;
+
+ start = SLOF_GetTimer();
+ while (SLOF_GetTimer() - start < 10) {
+ if (has_key()) {
+ get_key();
+ start = SLOF_GetTimer();
+ }
+ }
+}
+
+static int bootmenu_get_selection(void)
+{
+ char key = 0;
+ int sel;
+
+ do {
+ sel = -1;
+ if (!has_key())
+ continue;
+ key = get_key();
+ switch (key) {
+ case '0':
+ return -1;
+ case '1' ... '9':
+ sel = key - '1';
+ break;
+ case 'a' ... 'z':
+ sel = key - 'a' + 9;
+ break;
+ case 'A' ... 'Z':
+ sel = key - 'A' + 9;
+ break;
+ default:
+ /* Might be another escape code (F12) ... skip it */
+ flush_keys();
+ break;
+ }
+ } while (sel < 0 || sel >= nr_devs);
+
+ return sel;
+}
+
+void bootmenu(void)
+{
+ int sel;
+
+ bootmenu_populate_devs();
+ if (!nr_devs) {
+ puts("No available boot devices!");
+ return;
+ }
+
+ puts("\nSelect boot device (or press '0' to abort):");
+ bootmenu_show_devs();
+
+ if (has_key()) /* In case the user hammered on F12 */
+ flush_keys();
+
+ sel = bootmenu_get_selection();
+ if (sel < 0) {
+ forth_push(0);
+ } else {
+ forth_push((unsigned long)bootdevs[sel].alias);
+ forth_push(strlen(bootdevs[sel].alias));
+ }
+
+ bootmenu_free_devs();
+}
diff --git a/roms/SLOF/lib/libbootmenu/bootmenu.code b/roms/SLOF/lib/libbootmenu/bootmenu.code
new file mode 100644
index 000000000..2a55c09aa
--- /dev/null
+++ b/roms/SLOF/lib/libbootmenu/bootmenu.code
@@ -0,0 +1,20 @@
+/*****************************************************************************
+ * Boot menu: Glue code to Forth
+ *
+ * Copyright 2017 Red Hat, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth, Red Hat Inc. - initial implementation
+ *****************************************************************************/
+
+#include "bootmenu.h"
+
+// ( -- [str] len|0 )
+PRIM(boot_X2d_menu)
+ bootmenu();
+MIRP
diff --git a/roms/SLOF/lib/libbootmenu/bootmenu.h b/roms/SLOF/lib/libbootmenu/bootmenu.h
new file mode 100644
index 000000000..6cef23717
--- /dev/null
+++ b/roms/SLOF/lib/libbootmenu/bootmenu.h
@@ -0,0 +1,15 @@
+/*****************************************************************************
+ * Boot menu definitions
+ *
+ * Copyright 2017 Red Hat, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth, Red Hat Inc. - initial implementation
+ *****************************************************************************/
+
+extern void bootmenu(void);
diff --git a/roms/SLOF/lib/libbootmenu/bootmenu.in b/roms/SLOF/lib/libbootmenu/bootmenu.in
new file mode 100644
index 000000000..5cb120ee5
--- /dev/null
+++ b/roms/SLOF/lib/libbootmenu/bootmenu.in
@@ -0,0 +1,15 @@
+/*****************************************************************************
+ * Boot menu: Definitions for Forth
+ *
+ * Copyright 2017 Red Hat, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth, Red Hat Inc. - initial implementation
+ *****************************************************************************/
+
+cod(boot-menu)
diff --git a/roms/SLOF/lib/libbootmsg/Makefile b/roms/SLOF/lib/libbootmsg/Makefile
new file mode 100644
index 000000000..642c970ac
--- /dev/null
+++ b/roms/SLOF/lib/libbootmsg/Makefile
@@ -0,0 +1,75 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) -I. -I../../include
+LDFLAGS = -nostdlib
+
+TARGET = ../libbootmsg.a
+
+
+all: $(TARGET)
+
+ifeq ($(CPUARCH),cbea)
+SRCS =
+SRCSS = bootmsg_lvl.S
+else
+ifeq ($(CPUARCH),ppc970)
+SRCS =
+SRCSS = bootmsg_lvl.S
+else
+ifeq ($(CPUARCH),p5)
+SRCS =
+SRCSS = bootmsg_lvl.S
+else
+ifeq ($(CPUARCH),ppcp7)
+SRCS =
+SRCSS = bootmsg_lvl.S
+else
+SRCS = bootmsg.c
+SRCSS =
+endif
+endif
+endif
+endif
+
+OBJS = $(SRCS:%.c=%.o) $(SRCSS:%.S=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+%.o: %.S
+ $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
+
diff --git a/roms/SLOF/lib/libbootmsg/bootmsg.code b/roms/SLOF/lib/libbootmsg/bootmsg.code
new file mode 100644
index 000000000..ae370af08
--- /dev/null
+++ b/roms/SLOF/lib/libbootmsg/bootmsg.code
@@ -0,0 +1,61 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+#include <libbootmsg.h>
+
+// : cp ( cp-id -- )
+PRIM(bootmsg_X2d_cp)
+ int cpid = TOS.n; POP;
+ bootmsg_cp(cpid);
+MIRP
+
+// : bootmsg-warning ( cp-id lvl pstr -- )
+PRIM(bootmsg_X2d_warning)
+ char* str = TOS.a; POP;
+ short lvl = TOS.n; POP;
+ short cpid = TOS.n; POP;
+ bootmsg_warning(cpid, (const char*)str, lvl);
+MIRP
+
+// : bootmsg-error ( cp-id pstr -- )
+PRIM(bootmsg_X2d_error)
+ char* str = TOS.a; POP;
+ short cpid = TOS.n; POP;
+ bootmsg_error(cpid, (const char*)str);
+MIRP
+
+// : bootmsg-debugcp ( cp-id lvl pstr -- )
+PRIM(bootmsg_X2d_debugcp)
+ char* str = TOS.a; POP;
+ short lvl = TOS.n; POP;
+ short cpid = TOS.n; POP;
+ bootmsg_debugcp(cpid, (const char*)str, lvl);
+MIRP
+
+// : bootmsg-setlevel ( area lvl -- )
+PRIM(bootmsg_X2d_setlevel)
+ char lvl = TOS.n; POP;
+ short area = TOS.n; POP;
+ bootmsg_setlevel(area, lvl);
+MIRP
+
+// : bootmsg-checklevel ( area lvl -- [true|false] )
+PRIM(bootmsg_X2d_checklevel)
+ char lvl = TOS.n; POP;
+ short area = TOS.n; POP;
+ PUSH;
+ TOS.n = (bootmsg_checklevel(area, lvl)) ? -1 : 0;
+MIRP
+
+// : bootmsg-nvupdate ( -- )
+PRIM(bootmsg_X2d_nvupdate)
+ bootmsg_nvupdate();
+MIRP
diff --git a/roms/SLOF/lib/libbootmsg/bootmsg.in b/roms/SLOF/lib/libbootmsg/bootmsg.in
new file mode 100644
index 000000000..73e01e3d5
--- /dev/null
+++ b/roms/SLOF/lib/libbootmsg/bootmsg.in
@@ -0,0 +1,19 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+cod(bootmsg-cp)
+cod(bootmsg-warning)
+cod(bootmsg-error)
+cod(bootmsg-debugcp)
+cod(bootmsg-setlevel)
+cod(bootmsg-nvupdate)
+cod(bootmsg-checklevel)
diff --git a/roms/SLOF/lib/libbootmsg/bootmsg_lvl.S b/roms/SLOF/lib/libbootmsg/bootmsg_lvl.S
new file mode 100644
index 000000000..14ce4bfe7
--- /dev/null
+++ b/roms/SLOF/lib/libbootmsg/bootmsg_lvl.S
@@ -0,0 +1,202 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+#define _ASM_
+#include "macros.h"
+#include "southbridge.h"
+#include "nvramlog.h"
+
+#define bootmsg_area_size 128
+
+ .text
+ .align 3
+
+// Declare the warning level for all 128 possibilities of AC/pCKG kombinations
+ WRNG_LVL:
+ .rept bootmsg_area_size
+ .byte 0x0
+ .endr
+
+
+//*****************************************************************************
+// Check UserWarningLevel against SystemWarningLevel
+// input : r3=cp-id, r5=level
+// change: r6,r7
+// output: CR0 ( compared user vs system level )
+// example:
+// bl GET_WRNG_LVL
+// ble print_warning
+// bgt do_not_print_warning
+ENTRY(GET_WRNG_LVL)
+ mflr r7 // save linkage register
+ bl 0f // get current
+0: mflr r6 // Instruction Address
+ mtlr r7 // restore linkage register
+ addi r6,r6,WRNG_LVL-0b // calc addr of WRNG_LVL array
+ rldic r7,r3,56,57 // calc index into array
+ lbzux r7,r6,r7 // read the warning level
+ cmpw r5,r7 // and compare it
+ blr
+
+//*****************************************************************************
+// Print CheckPoint
+// input : r3=cp-id
+// change: r3, r4, r5, r6, r7, r11
+// output: none
+ENTRY(bootmsg_cp)
+ mflr r11
+ mr r9, r3 // save checkpoint ID
+ li r3, 'C'
+ bl io_putchar // print character
+ mr r3, r9
+ bl io_printhex16 // print checkpoint ID
+ li r3,'\r'
+ bl io_putchar // go back
+ mtlr r11
+ blr
+
+//*****************************************************************************
+// Print a general BootMessage
+// input : r3=cp-id, r4=string, r5=char (type C,W,E)
+// change: r3,r4,r5,r6,r7,r9,r10,r11,r12
+// output: none
+ENTRY(print_msg)
+ mflr r11 // Save linkage register
+ mr r9, r3 // Save ID
+ mr r10, r4 // Save ptr to string
+ mr r12, r5 // Save type (char [CWE])
+ li r3, '\n' // make it a new line
+ bl io_putchar
+ li r3, '\r'
+ bl io_putchar
+ mr r3, r12 // restore type
+ bl io_putchar // print character
+ mr r3, r9 // restore ID
+ bl io_printhex16 // print checkpoint ID
+ li r3, ' ' // print a space
+ bl io_putchar
+ mr r3, r10 // restore ptr to string
+ bl io_print // print message
+ li r3, '\n' // add a new line
+ bl io_putchar
+ li r3, '\r'
+ bl io_putchar
+ mtlr r11 // restore linkage register
+ blr
+
+//*****************************************************************************
+// Print an Error Boot Message
+// input : r3=cp-id, r4=string-ptr
+// change : r3,r4,r5,r6,r7,r9,r10,r11,r12
+// output : none
+ENTRY(bootmsg_error)
+ li r5, 'E' // E is for Error
+ b print_msg // and print this message
+
+//*****************************************************************************
+// Print a Warning Boot Message
+// input : r3=cp-id, r4=string-ptr, r5=level
+// change : r3,r4,r5,r6,r7,r9,r10,r11,r12
+// output : none
+ENTRY(bootmsg_warning)
+ mflr r11 // save linkage register
+ bl GET_WRNG_LVL // check UserLevel against SystemLevel
+ mtlr r11 // restore linkage register
+ li r5, 'W' // 'W' is for Warning
+ ble print_msg // if UserLevel<=SystemLevel print and return
+ blr // else return
+
+//*****************************************************************************
+// Print a Debug Checkpoint
+// input : r3=cp-id, r4=string-ptr, r5=level
+// change : r3,r4,r5,r6,r7,r9,r10,r11,r12
+// output : none
+// r3=cp-id, r4=string, r5=level
+ENTRY(bootmsg_debugcp)
+ mflr r11 // save linkage register
+ addi r5,r5,0x20 // add checkpoint offset
+ bl GET_WRNG_LVL // check UserLevel against SystemLevel
+ mtlr r11 // restore linkage register
+ li r5, 'D' // 'D' is for Debug CheckPoint
+ ble print_msg // if UserLevel<=SystemLevel print and return
+ blr // else return
+
+//*****************************************************************************
+// Check warning level
+// input : r3=cp-id, r4=level
+// change : r3,r4,r5,r6,r7,r9,r10,r11
+// output : r3 (true, false)
+// r3=cp-id, r4=level
+ENTRY(bootmsg_checklevel)
+ mflr r11
+ mr r5, r4
+ slwi r3, r3, 8
+ bl GET_WRNG_LVL // check UserLevel against SystemLevel
+ li r3, 0 // return 0
+ bgt 0f // IF ( UserLevel < SystemLevel )
+ li r3, 1 // | return 1
+0: mtlr r11 // FI
+ blr
+
+// r3=area|pkg, r4=level
+ENTRY(bootmsg_setlevel)
+ mflr r5
+ bl WarningMsg // calc current IA
+ WarningMsg:
+ mflr r6 // get current IA
+ addi r6,r6,WRNG_LVL-WarningMsg
+ andi. r3, r3, 0x7F
+ add r6,r3,r6 // address |
+ stb r4,0(r6) // store level |_ stwbrx r4,r3,r6
+
+#if !defined(DISABLE_NVRAM) && !defined(RTAS_NVRAM)
+ LOAD64(r6, SB_NVRAM_FWONLY_adr + 8 )
+ add r6,r6,r3
+ stb r4,0(r6)
+#endif
+ mtlr r5
+ blr
+
+ENTRY(bootmsg_nvupdate)
+#if !defined(DISABLE_NVRAM) && !defined(RTAS_NVRAM)
+ mflr r10
+ LOAD64(r3, SB_NVRAM_FWONLY_adr)
+ lwz r4, 0(r3)
+ cmpwi r4, 0x424E // find bootmsg area header
+ bne 0f
+
+ LOAD64(r5, bootmsg_area_size/8)
+ mtctr r5
+ bl WngMsg
+ WngMsg:
+ mflr r5
+ addi r5,r5,WRNG_LVL-WngMsg-8
+
+1:
+ ldu r4, 8(r3)
+ stdu r4, 8(r5)
+ bdnz+ 1b
+ b 2f
+
+0:
+ LOAD64(r5, bootmsg_area_size)
+ mtctr r5
+ li r4, 0x424E // clear bootmsg log area
+ stw r4, 0(r3)
+ li r4, 0
+
+1: stdu r4, 8(r3)
+ bdnz+ 1b
+
+2: // the end
+ mtlr r10
+#endif
+ blr
diff --git a/roms/SLOF/lib/libbootmsg/libbootmsg.h b/roms/SLOF/lib/libbootmsg/libbootmsg.h
new file mode 100644
index 000000000..9d0bd157d
--- /dev/null
+++ b/roms/SLOF/lib/libbootmsg/libbootmsg.h
@@ -0,0 +1,21 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+#ifndef _LIBBOOTMSG_H
+#define _LIBBOOTMSG_H
+void bootmsg_cp(short p);
+void bootmsg_error(short p, const char *str);
+void bootmsg_warning(short p, const char *str, short lvl);
+void bootmsg_debugcp(short p, const char *str, short lvl);
+void bootmsg_setlevel(short p, short level);
+int bootmsg_checklevel(short p, short level);
+void *bootmsg_nvupdate(void);
+#endif /* _LIBBOOTMSG_H */
diff --git a/roms/SLOF/lib/libc/Makefile b/roms/SLOF/lib/libc/Makefile
new file mode 100644
index 000000000..0c762ec8b
--- /dev/null
+++ b/roms/SLOF/lib/libc/Makefile
@@ -0,0 +1,61 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+LIBCCMNDIR = $(shell pwd)
+STRINGCMNDIR = $(LIBCCMNDIR)/string
+CTYPECMNDIR = $(LIBCCMNDIR)/ctype
+STDLIBCMNDIR = $(LIBCCMNDIR)/stdlib
+STDIOCMNDIR = $(LIBCCMNDIR)/stdio
+GETOPTCMNDIR = $(LIBCCMNDIR)/getopt
+
+include $(TOPCMNDIR)/make.rules
+
+
+CPPFLAGS = -I$(LIBCCMNDIR)/include
+LDFLAGS= -nostdlib
+
+TARGET = ../libc.a
+
+
+all: $(TARGET)
+
+# Use the following target to build a native version of the lib
+# (for example for debugging purposes):
+native:
+ $(MAKE) CROSS="" CC=$(HOSTCC) NATIVEBUILD=1
+
+
+include $(STRINGCMNDIR)/Makefile.inc
+include $(CTYPECMNDIR)/Makefile.inc
+include $(STDLIBCMNDIR)/Makefile.inc
+include $(STDIOCMNDIR)/Makefile.inc
+include $(GETOPTCMNDIR)/Makefile.inc
+
+OBJS = $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) $(GETOPT_OBJS)
+
+ifneq ($(NATIVEBUILD),1)
+# These parts of the libc use assembler, so they can only be compiled when
+# we are _not_ building a native version.
+endif
+
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
diff --git a/roms/SLOF/lib/libc/README.txt b/roms/SLOF/lib/libc/README.txt
new file mode 100644
index 000000000..eaafdf4af
--- /dev/null
+++ b/roms/SLOF/lib/libc/README.txt
@@ -0,0 +1,49 @@
+
+ Standard C library for the SLOF firmware project
+ ================================================
+
+To use this library, link your target against the "libc.a" archive.
+
+However, there are some prerequisites before you can use certain parts of the
+library:
+
+1) If you want to use malloc() and the like, you have to supply an implemen-
+ tation of sbrk() in your own code. malloc() uses sbrk() to get new, free
+ memory regions.
+
+ Prototype: void *sbrk(int incr);
+ Description: sbrk() increments the available data space by incr bytes and
+ returns a pointer to the start of the new area.
+
+ See the man-page of sbrk for details about this function.
+
+2) Before you can use the stdio output functions like printf(), puts() and the
+ like, you have to provide a standard write() function in your code.
+ printf() and the like use write() to print out the strings to the standard
+ output.
+
+ Prototype: ssize_t write(int fd, const void *buf, size_t cnt);
+ Description: Write cnt byte from the buffer buf to the stream associated
+ with the file descriptor fd.
+
+ The stdio functions will print their output to the stdout channel which is
+ assigned with the file descriptor 1 by default. Note that the stdio
+ functions will not use open() before calling write(), so if the stdout
+ cannel needs to be opened first, you should do that in your start-up code
+ before using the libc functions for the first time.
+
+3) Before you can use the stdio input functions like scanf() and the
+ like, you have to provide a standard read() function in your code.
+ scanf() and the like use read() to get the characters from the standard
+ input.
+
+ Prototype: ssize_t read(int fd, void *buf, size_t cnt);
+ Description: Read cnt byte from the stream associated with the file
+ descriptor fd and put them into the buffer buf.
+
+ The stdio functions will get their input from the stdin channel which is
+ assigned with the file descriptor 0 by default. Note that the stdio
+ functions will not use open() before calling read(), so if the stdin
+ cannel needs to be opened first, you should do that in your start-up code
+ before using the libc functions for the first time.
+
diff --git a/roms/SLOF/lib/libc/ctype/Makefile.inc b/roms/SLOF/lib/libc/ctype/Makefile.inc
new file mode 100644
index 000000000..25513a9a7
--- /dev/null
+++ b/roms/SLOF/lib/libc/ctype/Makefile.inc
@@ -0,0 +1,20 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+
+CTYPE_SRC_C = isdigit.c isprint.c isspace.c isxdigit.c tolower.c toupper.c
+CTYPE_SRC_ASM =
+CTYPE_SRCS = $(CTYPE_SRC_C:%=$(CTYPECMNDIR)/%) $(CTYPE_SRC_ASM:%=$(CTYPECMNDIR)/%)
+CTYPE_OBJS = $(CTYPE_SRC_C:%.c=%.o) $(CTYPE_SRC_ASM:%.S=%.o)
+
+%.o : $(CTYPECMNDIR)/%.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
diff --git a/roms/SLOF/lib/libc/ctype/isdigit.c b/roms/SLOF/lib/libc/ctype/isdigit.c
new file mode 100644
index 000000000..62d08a1cf
--- /dev/null
+++ b/roms/SLOF/lib/libc/ctype/isdigit.c
@@ -0,0 +1,25 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <ctype.h>
+
+int isdigit(int ch)
+{
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return 1;
+
+ default:
+ return 0;
+ }
+}
diff --git a/roms/SLOF/lib/libc/ctype/isprint.c b/roms/SLOF/lib/libc/ctype/isprint.c
new file mode 100644
index 000000000..c74880f1c
--- /dev/null
+++ b/roms/SLOF/lib/libc/ctype/isprint.c
@@ -0,0 +1,18 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <ctype.h>
+
+int isprint(int ch)
+{
+ return (ch >= 32 && ch < 127);
+}
diff --git a/roms/SLOF/lib/libc/ctype/isspace.c b/roms/SLOF/lib/libc/ctype/isspace.c
new file mode 100644
index 000000000..51230192f
--- /dev/null
+++ b/roms/SLOF/lib/libc/ctype/isspace.c
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <ctype.h>
+
+int isspace(int ch)
+{
+ switch (ch) {
+ case ' ':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ return 1;
+
+ default:
+ return 0;
+ }
+}
diff --git a/roms/SLOF/lib/libc/ctype/isxdigit.c b/roms/SLOF/lib/libc/ctype/isxdigit.c
new file mode 100644
index 000000000..9d323f3c0
--- /dev/null
+++ b/roms/SLOF/lib/libc/ctype/isxdigit.c
@@ -0,0 +1,21 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <ctype.h>
+
+int isxdigit(int ch)
+{
+ return (
+ (ch >= '0' && ch <= '9') |
+ (ch >= 'A' && ch <= 'F') |
+ (ch >= 'a' && ch <= 'f') );
+}
diff --git a/roms/SLOF/lib/libc/ctype/tolower.c b/roms/SLOF/lib/libc/ctype/tolower.c
new file mode 100644
index 000000000..f775e9096
--- /dev/null
+++ b/roms/SLOF/lib/libc/ctype/tolower.c
@@ -0,0 +1,18 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <ctype.h>
+
+int tolower(int c)
+{
+ return (((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 'a' ) : c);
+}
diff --git a/roms/SLOF/lib/libc/ctype/toupper.c b/roms/SLOF/lib/libc/ctype/toupper.c
new file mode 100644
index 000000000..9bcee523d
--- /dev/null
+++ b/roms/SLOF/lib/libc/ctype/toupper.c
@@ -0,0 +1,21 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include "ctype.h"
+
+int toupper (int cha)
+{
+ if((cha >= 'a') && (cha <= 'z'))
+ return(cha - 'a' + 'A');
+ return(cha);
+}
diff --git a/roms/SLOF/lib/libc/getopt/Makefile.inc b/roms/SLOF/lib/libc/getopt/Makefile.inc
new file mode 100644
index 000000000..8a2e32f00
--- /dev/null
+++ b/roms/SLOF/lib/libc/getopt/Makefile.inc
@@ -0,0 +1,17 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+GETOPT_SRC_C = getopt.c
+GETOPT_OBJS = $(GETOPT_SRC_C:%.c=%.o)
+
+%.o : $(GETOPTCMNDIR)/%.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
diff --git a/roms/SLOF/lib/libc/getopt/getopt.c b/roms/SLOF/lib/libc/getopt/getopt.c
new file mode 100644
index 000000000..be626ddc2
--- /dev/null
+++ b/roms/SLOF/lib/libc/getopt/getopt.c
@@ -0,0 +1,470 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * includes
+ *******************************************************************************
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+/*
+ * global variables, types & constants
+ * may be removed if already defined
+ *******************************************************************************
+ */
+int opterr = 1;
+int optopt = 0;
+int optind = 1;
+char *optarg = NULL;
+
+/*
+ * internal values needed by getopt
+ * DO NOT CHANGE or REMOVE
+ */
+enum {
+ OPTIONAL_ARG = 0,
+ MANDATORY_ARG = 1,
+ NO_ARG = 2
+};
+
+/*
+ * variables needed by getopt & getopt_long!
+ * DO NOT REMOVE
+ */
+static char *optstart = NULL;
+
+int
+getopt(int argc, char **argv, const char *options)
+{
+ char *optptr;
+ char *argptr;
+ int optman;
+ int idx;
+ int ret = 0;
+ int argpresent;
+
+ /*
+ * reset used global values
+ */
+ optopt = 0;
+ optarg = NULL;
+
+ /*
+ * reset getopt if a new argv pointer is passed
+ */
+ if (optstart != argv[0]) {
+ optopt = 0;
+ optind = 1;
+ optarg = NULL;
+ optstart = argv[0];
+ }
+
+ /*
+ * return if no more arguments are available
+ */
+ if (optind >= argc) {
+ return -1;
+ }
+
+ /*
+ * start parsing argv[optind]
+ */
+ idx = 0;
+
+ /*
+ * return if the option does not begin with a '-' or has more than 2 characters
+ */
+ if (argv[optind][idx] != '-') {
+
+ if (opterr != 0) {
+ printf("unknown option \'%s\', expecting \'-\'\n",
+ argv[optind]);
+ }
+
+ optopt = (int) argv[optind][idx];
+ optind++;
+
+ return '?';
+ }
+
+ /*
+ * continue to the next character in argv[optind]
+ */
+ idx++;
+
+ /*
+ * identify the option
+ * make sure if an option contains a ':' to invalidate the option
+ */
+ optptr = strchr(argv[optind], ':');
+
+ if (optptr == NULL) {
+ optptr = strchr(options, (int) argv[optind][idx]);
+ } else {
+ optptr = NULL;
+ }
+
+ /*
+ * check whether the option is present
+ */
+ if (optptr == NULL) {
+ /*
+ * unknown option detected
+ */
+ if (opterr != 0) {
+ printf("unknown option \'%s\'\n", argv[optind]);
+ }
+
+ optopt = (int) argv[optind][idx];
+ optind++;
+
+ return '?';
+ }
+
+ /*
+ * the option is present in the option string
+ * setup return value
+ */
+ ret = (int) *optptr;
+
+ /*
+ * get option argument if needed
+ */
+ optptr++;
+
+ /*
+ * determine between mandatory and optional argument
+ */
+ optman = NO_ARG;
+
+ if (*optptr == ':') {
+ optman--; // now set to MANDATORY_ARG
+ }
+
+ if (optman == MANDATORY_ARG) {
+ optptr++;
+
+ if (*optptr == ':') {
+ optman--; // now set to OPTIONAL_ARG
+ }
+
+ }
+
+ /*
+ * if strlen( argv[optind ) is greater than 2,
+ * the argument is in the same argv
+ */
+ if (strlen(argv[optind]) > 2) {
+ argptr = &argv[optind][2];
+
+ /*
+ * do not allow '-' in an argument
+ */
+ if (strchr(argptr, '-') != NULL) {
+
+ if (opterr != 0) {
+ printf
+ ("illegal argument value \'%s\' for option \'-%c\'\n",
+ argptr, ret);
+ }
+
+ optopt = ret;
+
+ return '?';
+ }
+
+ } else {
+ /*
+ * move on to the next argv
+ * it now either contains an argument or the next option
+ */
+ optind++;
+
+ /*
+ * make sure not to overflow
+ */
+ if (optind < argc) {
+ argptr = argv[optind];
+ } else {
+ argptr = NULL;
+ }
+
+ }
+
+ /*
+ * do the needed actions for the argument state
+ */
+ switch (optman) {
+ case OPTIONAL_ARG:
+
+ if (argptr == NULL) {
+ break;
+ }
+
+ if (*argptr != '-') {
+ /*
+ * argument present
+ */
+ optarg = argptr;
+ optind++;
+
+ }
+
+
+ break;
+
+ case MANDATORY_ARG:
+ argpresent = (argptr != NULL);
+
+ if (argpresent) {
+ argpresent = (*argptr != '-');
+ }
+
+ if (argpresent) {
+ /*
+ * argument present
+ */
+ optarg = argptr;
+ optind++;
+ } else {
+ /*
+ * mandatory argument missing
+ */
+ if (opterr != 0) {
+ printf
+ ("missing argument for option \'-%c\'\n",
+ ret);
+ }
+
+ optopt = ret;
+
+ /*
+ * if the first character of options is a ':'
+ * return a ':' instead of a '?' in case of
+ * a missing argument
+ */
+ if (*options == ':') {
+ ret = ':';
+ } else {
+ ret = '?';
+ }
+
+ }
+
+
+ break;
+
+ case NO_ARG:
+
+ if (strlen(argv[optind - 1]) > 2) {
+
+ if (opterr != 0) {
+ printf
+ ("too many arguments for option \'-%c\'\n",
+ ret);
+ }
+
+ optopt = ret;
+ ret = '?';
+ }
+
+
+ break;
+
+ }
+
+ return ret;
+}
+
+int
+getopt_long(int argc, char **argv, const char *shortopts,
+ const struct option *longopts, int *indexptr)
+{
+ struct option *optptr = (struct option *) longopts;
+ int optidx = 0;
+ int idx;
+ int ret = 0;
+ int argpresent;
+
+ /*
+ * reset used global values
+ */
+ optopt = 0;
+ optarg = NULL;
+
+ /*
+ * reset indexptr
+ */
+ *indexptr = -1;
+
+ /*
+ * reset getopt if a new argv pointer is passed
+ */
+ if (optstart != argv[0]) {
+ optopt = 0;
+ optind = 1;
+ optarg = NULL;
+ optstart = argv[0];
+ }
+
+ /*
+ * return if no more arguments are available
+ */
+ if (optind >= argc) {
+ return -1;
+ }
+
+ /*
+ * start parsing argv[optind]
+ */
+ idx = 0;
+
+ /*
+ * return if the option does not begin with a '-'
+ */
+ if (argv[optind][idx] != '-') {
+ printf("unknown option \'%s\', expecting \'-\'\n",
+ argv[optind]);
+
+ optind++;
+
+ return '?';
+ }
+
+ /*
+ * move on to the next character in argv[optind]
+ */
+ idx++;
+
+ /*
+ * return getopt() in case of a short option
+ */
+ if (argv[optind][idx] != '-') {
+ return getopt(argc, argv, shortopts);
+ }
+
+ /*
+ * handle a long option
+ */
+ idx++;
+
+ while (optptr->name != NULL) {
+
+ if (strcmp(&argv[optind][idx], optptr->name) == 0) {
+ break;
+ }
+
+ optptr++;
+ optidx++;
+ }
+
+ /*
+ * no matching option found
+ */
+ if (optptr->name == NULL) {
+ printf("unknown option \'%s\'\n", argv[optind]);
+
+ optind++;
+
+ return '?';
+ }
+
+ /*
+ * option was found, set up index pointer
+ */
+ *indexptr = optidx;
+
+ /*
+ * get argument
+ */
+ optind++;
+
+ switch (optptr->has_arg) {
+ case no_argument:
+ /*
+ * nothing to do
+ */
+
+ break;
+
+ case required_argument:
+ argpresent = (optind != argc);
+
+ if (argpresent) {
+ argpresent = (argv[optind][0] != '-');
+ }
+
+ if (argpresent) {
+ /*
+ * argument present
+ */
+ optarg = argv[optind];
+ optind++;
+ } else {
+ /*
+ * mandatory argument missing
+ */
+ printf("missing argument for option \'%s\'\n",
+ argv[optind - 1]);
+
+ ret = '?';
+ }
+
+
+ break;
+
+ case optional_argument:
+
+ if (optind == argc) {
+ break;
+ }
+
+ if (argv[optind][0] != '-') {
+ /*
+ * argument present
+ */
+ optarg = argv[optind];
+ optind++;
+ }
+
+
+ break;
+
+ default:
+ printf("unknown argument option for option \'%s\'\n",
+ argv[optind - 1]);
+
+ ret = '?';
+
+ break;
+
+ }
+
+ /*
+ * setup return values
+ */
+ if (ret != '?') {
+
+ if (optptr->flag == NULL) {
+ ret = optptr->val;
+ } else {
+ *optptr->flag = optptr->val;
+ ret = 0;
+ }
+
+ }
+
+ return ret;
+}
diff --git a/roms/SLOF/lib/libc/include/assert.h b/roms/SLOF/lib/libc/include/assert.h
new file mode 100644
index 000000000..01434da1a
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/assert.h
@@ -0,0 +1,36 @@
+/*****************************************************************************
+ * assert() macro definition
+ *
+ * Copyright 2018 Red Hat, Inc.
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the BSD License which accompanies this distribution, and
+ * is available at http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth, Red Hat Inc. - initial implementation
+ *****************************************************************************/
+
+#ifndef SLIMLINE_ASSERT_H
+#define SLIMLINE_ASSERT_H
+
+#ifdef NDEBUG
+
+#define assert(cond) (void)
+
+#else
+
+#define assert(cond) \
+ do { \
+ if (!(cond)) { \
+ fprintf(stderr, \
+ "ERROR: Assertion '" #cond "' failed!\n" \
+ "(function %s, file " __FILE__ ", line %i)\n", \
+ __func__, __LINE__); \
+ while (1) {} \
+ } \
+ } while (0)
+
+#endif
+
+#endif /* SLIMLINE_ASSERT_H */
diff --git a/roms/SLOF/lib/libc/include/ctype.h b/roms/SLOF/lib/libc/include/ctype.h
new file mode 100644
index 000000000..9051a7563
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/ctype.h
@@ -0,0 +1,24 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _CTYPE_H
+#define _CTYPE_H
+
+int isdigit(int c);
+int isxdigit(int c);
+int isprint(int c);
+int isspace(int c);
+
+int tolower(int c);
+int toupper(int c);
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/errno.h b/roms/SLOF/lib/libc/include/errno.h
new file mode 100644
index 000000000..d5859347e
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/errno.h
@@ -0,0 +1,34 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _ERRNO_H
+#define _ERRNO_H
+
+extern int errno;
+
+/*
+ * Error number definitions
+ */
+#define EPERM 1 /* not permitted */
+#define ENOENT 2 /* file or directory not found */
+#define EIO 5 /* input/output error */
+#define ENOMEM 12 /* not enough space */
+#define EACCES 13 /* permission denied */
+#define EFAULT 14 /* bad address */
+#define EBUSY 16 /* resource busy */
+#define EEXIST 17 /* file already exists */
+#define ENODEV 19 /* device not found */
+#define EINVAL 22 /* invalid argument */
+#define EDOM 33 /* math argument out of domain of func */
+#define ERANGE 34 /* math result not representable */
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/getopt.h b/roms/SLOF/lib/libc/include/getopt.h
new file mode 100644
index 000000000..5956986a5
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/getopt.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef GETOPT_H
+#define GETOPT_H
+
+extern char *optarg;
+extern int optind;
+extern int opterr;
+extern int optopt;
+
+struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+enum {
+ no_argument = 0,
+ required_argument,
+ optional_argument
+};
+
+int getopt(int argc, char **, const char *);
+int getopt_long(int argc, char **, const char *, const struct option *, int *);
+
+#endif /* GETOPT_H */
diff --git a/roms/SLOF/lib/libc/include/limits.h b/roms/SLOF/lib/libc/include/limits.h
new file mode 100644
index 000000000..4726835c2
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/limits.h
@@ -0,0 +1,32 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _LIMITS_H
+#define _LIMITS_H
+
+#define UCHAR_MAX 255
+#define SCHAR_MAX 127
+#define SCHAR_MIN (-128)
+
+#define USHRT_MAX 65535
+#define SHRT_MAX 32767
+#define SHRT_MIN (-32768)
+
+#define UINT_MAX (4294967295U)
+#define INT_MAX 2147483647
+#define INT_MIN (-2147483648)
+
+#define ULONG_MAX ((unsigned long)-1L)
+#define LONG_MAX (ULONG_MAX/2)
+#define LONG_MIN ((-LONG_MAX)-1)
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/stdarg.h b/roms/SLOF/lib/libc/include/stdarg.h
new file mode 100644
index 000000000..d3d12f778
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/stdarg.h
@@ -0,0 +1,22 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _STDARG_H
+#define _STDARG_H
+
+typedef __builtin_va_list va_list;
+
+#define va_start(v,l) __builtin_va_start(v,l)
+#define va_arg(v,l) __builtin_va_arg(v,l)
+#define va_end(v) __builtin_va_end(v)
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/stdbool.h b/roms/SLOF/lib/libc/include/stdbool.h
new file mode 100644
index 000000000..5b7d36a7e
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/stdbool.h
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _STDBOOL_H
+#define _STDBOOL_H
+
+#ifndef __cplusplus
+typedef enum { false = 0, true } bool;
+#endif
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/stddef.h b/roms/SLOF/lib/libc/include/stddef.h
new file mode 100644
index 000000000..e240106a4
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/stddef.h
@@ -0,0 +1,24 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _STDDEF_H
+#define _STDDEF_H
+
+
+#define NULL ((void *)0)
+
+typedef unsigned long size_t;
+
+
+#endif
+
+
diff --git a/roms/SLOF/lib/libc/include/stdint.h b/roms/SLOF/lib/libc/include/stdint.h
new file mode 100644
index 000000000..518a72305
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/stdint.h
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _STDINT_H
+#define _STDINT_H
+
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+
+typedef unsigned short uint16_t;
+typedef signed short int16_t;
+
+typedef unsigned int uint32_t;
+typedef signed int int32_t;
+
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/stdio.h b/roms/SLOF/lib/libc/include/stdio.h
new file mode 100644
index 000000000..1cd5faf26
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/stdio.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _STDIO_H
+#define _STDIO_H
+
+#include <stdarg.h>
+#include "stddef.h"
+
+#define EOF (-1)
+
+#define _IONBF 0
+#define _IOLBF 1
+#define _IOFBF 2
+#define BUFSIZ 80
+
+typedef struct {
+ int fd;
+ int mode;
+ int pos;
+ char *buf;
+ int bufsiz;
+} FILE;
+
+extern FILE stdin_data;
+extern FILE stdout_data;
+extern FILE stderr_data;
+
+#define stdin (&stdin_data)
+#define stdout (&stdout_data)
+#define stderr (&stderr_data)
+
+int fileno(FILE *stream);
+int printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
+int fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3)));
+int sprintf(char *str, const char *format, ...) __attribute__((format (printf, 2, 3)));
+int snprintf(char *str, size_t size, const char *format, ...) __attribute__((format (printf, 3, 4)));
+int vfprintf(FILE *stream, const char *format, va_list);
+int vsprintf(char *str, const char *format, va_list);
+int vsnprintf(char *str, size_t size, const char *format, va_list);
+void setbuf(FILE *stream, char *buf);
+int setvbuf(FILE *stream, char *buf, int mode , size_t size);
+
+int putc(int ch, FILE *stream);
+int putchar(int ch);
+int puts(const char *str);
+
+int scanf(const char *format, ...) __attribute__((format (scanf, 1, 2)));
+int fscanf(FILE *stream, const char *format, ...) __attribute__((format (scanf, 2, 3)));
+int vfscanf(FILE *stream, const char *format, va_list);
+int vsscanf(const char *str, const char *format, va_list);
+int getc(FILE *stream);
+int getchar(void);
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/stdlib.h b/roms/SLOF/lib/libc/include/stdlib.h
new file mode 100644
index 000000000..5e0eda9ff
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/stdlib.h
@@ -0,0 +1,34 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _STDLIB_H
+#define _STDLIB_H
+
+#include "stddef.h"
+
+#define RAND_MAX 32767
+
+
+void *malloc(size_t size);
+void *realloc(void *ptr, size_t size);
+void free(void *ptr);
+void *memalign(size_t boundary, size_t size);
+
+int atoi(const char *str);
+long atol(const char *str);
+unsigned long int strtoul(const char *nptr, char **endptr, int base);
+long int strtol(const char *nptr, char **endptr, int base);
+
+int rand(void);
+void srand(unsigned int seed);
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/string.h b/roms/SLOF/lib/libc/include/string.h
new file mode 100644
index 000000000..0163c9a67
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/string.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _STRING_H
+#define _STRING_H
+
+#include "stddef.h"
+
+char *strcpy(char *dest, const char *src);
+char *strncpy(char *dest, const char *src, size_t n);
+char *strcat(char *dest, const char *src);
+int strcmp(const char *s1, const char *s2);
+int strncmp(const char *s1, const char *s2, size_t n);
+int strcasecmp(const char *s1, const char *s2);
+int strncasecmp(const char *s1, const char *s2, size_t n);
+char *strchr(const char *s, int c);
+char *strrchr(const char *s, int c);
+size_t strlen(const char *s);
+char *strstr(const char *hay, const char *needle);
+char *strtok(char *src, const char *pattern);
+
+void *memset(void *s, int c, size_t n);
+void *memchr(const void *s, int c, size_t n);
+void *memcpy(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
+
+#endif
diff --git a/roms/SLOF/lib/libc/include/sys/socket.h b/roms/SLOF/lib/libc/include/sys/socket.h
new file mode 100644
index 000000000..e9175be37
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/sys/socket.h
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#ifndef _SOCKET_H
+#define _SOCKET_H
+#include <stdint.h>
+
+#define AF_PACKET 0
+#define AF_INET 1
+#define AF_INET6 2
+
+#define SOCK_RAW 0
+#define SOCK_PACKET 1
+#define SOCK_DGRAM 2
+#define SOCK_STREAM 3
+
+#define INADDR_ANY 0xFFFFFFFF
+
+#define IPPROTO_UDP 1
+
+#define ETH_ALEN 6 /**< HW address length */
+
+struct sockaddr {
+ uint16_t tra_port;
+
+ uint16_t ipv4_proto;
+ uint32_t ipv4_addr;
+
+ // protocol field is only used by "connect"-handler
+ uint16_t llc_proto;
+ uint8_t mac_addr[ETH_ALEN];
+};
+
+int socket(int, int, int, char *);
+int sendto(int, const void *, int, int, const void *, int);
+int send(int, const void *, int, int);
+int recv(int, void *, int, int);
+
+#define htonl(x) x
+#define htons(x) x
+
+#endif
+
diff --git a/roms/SLOF/lib/libc/include/unistd.h b/roms/SLOF/lib/libc/include/unistd.h
new file mode 100644
index 000000000..07210d69a
--- /dev/null
+++ b/roms/SLOF/lib/libc/include/unistd.h
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _UNISTD_H
+#define _UNISTD_H
+
+#include <stddef.h>
+
+typedef long ssize_t;
+
+extern int open(const char *name, int flags);
+extern int close(int fd);
+extern ssize_t read(int fd, void *buf, size_t count);
+extern ssize_t write(int fd, const void *buf, size_t count);
+extern ssize_t lseek(int fd, long offset, int whence);
+
+extern void *sbrk(int increment);
+
+#endif
diff --git a/roms/SLOF/lib/libc/stdio/Makefile.inc b/roms/SLOF/lib/libc/stdio/Makefile.inc
new file mode 100644
index 000000000..6688317b0
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/Makefile.inc
@@ -0,0 +1,23 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+
+STDIO_SRC_C = fscanf.c sprintf.c vfprintf.c vsnprintf.c vsprintf.c fprintf.c \
+ printf.c setvbuf.c putc.c puts.c putchar.c scanf.c stdchnls.c \
+ vfscanf.c vsscanf.c fileno.c snprintf.c
+
+STDIO_SRC_ASM =
+STDIO_SRCS = $(STDIO_SRC_C:%=$(STDIOCMNDIR)/%) $(STDIO_SRC_ASM:%=$(STDIOCMNDIR)/%)
+STDIO_OBJS = $(STDIO_SRC_C:%.c=%.o) $(STDIO_SRC_ASM:%.S=%.o)
+
+%.o : $(STDIOCMNDIR)/%.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
diff --git a/roms/SLOF/lib/libc/stdio/fileno.c b/roms/SLOF/lib/libc/stdio/fileno.c
new file mode 100644
index 000000000..6e239511d
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/fileno.c
@@ -0,0 +1,19 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+
+int
+fileno(FILE *stream)
+{
+ return stream->fd;
+}
diff --git a/roms/SLOF/lib/libc/stdio/fprintf.c b/roms/SLOF/lib/libc/stdio/fprintf.c
new file mode 100644
index 000000000..866df3934
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/fprintf.c
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "stdio.h"
+
+
+int fprintf(FILE *stream, const char* fmt, ...)
+{
+ int count;
+ va_list ap;
+
+ va_start(ap, fmt);
+ count = vfprintf(stream, fmt, ap);
+ va_end(ap);
+
+ return count;
+}
diff --git a/roms/SLOF/lib/libc/stdio/fscanf.c b/roms/SLOF/lib/libc/stdio/fscanf.c
new file mode 100644
index 000000000..321b1630a
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/fscanf.c
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+
+int
+fscanf(FILE *stream, const char *fmt, ...)
+{
+ int count;
+ va_list ap;
+
+ va_start(ap, fmt);
+ count = vfscanf(stream, fmt, ap);
+ va_end(ap);
+
+ return count;
+}
diff --git a/roms/SLOF/lib/libc/stdio/printf.c b/roms/SLOF/lib/libc/stdio/printf.c
new file mode 100644
index 000000000..01f4592dd
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/printf.c
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "stdio.h"
+
+
+int printf(const char* fmt, ...)
+{
+ int count;
+ va_list ap;
+
+ va_start(ap, fmt);
+ count = vfprintf(stdout, fmt, ap);
+ va_end(ap);
+
+ return count;
+}
+
diff --git a/roms/SLOF/lib/libc/stdio/putc.c b/roms/SLOF/lib/libc/stdio/putc.c
new file mode 100644
index 000000000..230e9d196
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/putc.c
@@ -0,0 +1,25 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "stdio.h"
+#include "unistd.h"
+
+int
+putc(int ch, FILE *stream)
+{
+ unsigned char outchar = ch;
+
+ if (write(stream->fd, &outchar, 1) == 1)
+ return outchar;
+ else
+ return EOF;
+}
diff --git a/roms/SLOF/lib/libc/stdio/putchar.c b/roms/SLOF/lib/libc/stdio/putchar.c
new file mode 100644
index 000000000..5c750d90a
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/putchar.c
@@ -0,0 +1,21 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include "stdio.h"
+
+
+int
+putchar(int ch)
+{
+ return putc(ch, stdout);
+}
diff --git a/roms/SLOF/lib/libc/stdio/puts.c b/roms/SLOF/lib/libc/stdio/puts.c
new file mode 100644
index 000000000..9a93008d2
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/puts.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include "stdio.h"
+#include "string.h"
+#include "unistd.h"
+
+
+int
+puts(const char *str)
+{
+ int ret;
+
+ ret = write(stdout->fd, str, strlen(str));
+ write(stdout->fd, "\r\n", 2);
+
+ return ret;
+}
diff --git a/roms/SLOF/lib/libc/stdio/scanf.c b/roms/SLOF/lib/libc/stdio/scanf.c
new file mode 100644
index 000000000..96b639980
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/scanf.c
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+
+int
+scanf(const char *fmt, ...)
+{
+ int count;
+ va_list ap;
+
+ va_start(ap, fmt);
+ count = vfscanf(stdin, fmt, ap);
+ va_end(ap);
+
+ return count;
+}
diff --git a/roms/SLOF/lib/libc/stdio/setvbuf.c b/roms/SLOF/lib/libc/stdio/setvbuf.c
new file mode 100644
index 000000000..9b62dd8ff
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/setvbuf.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+
+int setvbuf(FILE *stream, char *buf, int mode , size_t size)
+{
+ if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF)
+ return -1;
+ stream->buf = buf;
+ stream->mode = mode;
+ stream->bufsiz = size;
+ return 0;
+}
+
+void setbuf(FILE *stream, char *buf)
+{
+ setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
+}
diff --git a/roms/SLOF/lib/libc/stdio/snprintf.c b/roms/SLOF/lib/libc/stdio/snprintf.c
new file mode 100644
index 000000000..e3cfa6e6b
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/snprintf.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+
+int snprintf(char *buff, size_t size, const char *format, ...)
+{
+ va_list ar;
+ int count;
+
+ if (!buff || !format)
+ return -1;
+
+ va_start(ar, format);
+ count = vsnprintf(buff, size, format, ar);
+ va_end(ar);
+
+ return count;
+}
diff --git a/roms/SLOF/lib/libc/stdio/sprintf.c b/roms/SLOF/lib/libc/stdio/sprintf.c
new file mode 100644
index 000000000..9c4540e2e
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/sprintf.c
@@ -0,0 +1,30 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+
+
+int sprintf(char *buff, const char *format, ...)
+{
+ va_list ar;
+ int count;
+
+ if ((buff==NULL) || (format==NULL))
+ return(-1);
+
+ va_start(ar, format);
+ count = vsprintf(buff, format, ar);
+ va_end(ar);
+
+ return(count);
+}
+
diff --git a/roms/SLOF/lib/libc/stdio/stdchnls.c b/roms/SLOF/lib/libc/stdio/stdchnls.c
new file mode 100644
index 000000000..41ed958bf
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/stdchnls.c
@@ -0,0 +1,23 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include "stdio.h"
+
+static char stdin_buffer[BUFSIZ], stdout_buffer[BUFSIZ];
+
+FILE stdin_data = { .fd = 0, .mode = _IOLBF, .pos = 0,
+ .buf = stdin_buffer, .bufsiz = BUFSIZ };
+FILE stdout_data = { .fd = 1, .mode = _IOLBF, .pos = 0,
+ .buf = stdout_buffer, .bufsiz = BUFSIZ };
+FILE stderr_data = { .fd = 2, .mode = _IONBF, .pos = 0,
+ .buf = NULL, .bufsiz = 0 };
diff --git a/roms/SLOF/lib/libc/stdio/vfprintf.c b/roms/SLOF/lib/libc/stdio/vfprintf.c
new file mode 100644
index 000000000..765feeace
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/vfprintf.c
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "stdio.h"
+#include "unistd.h"
+
+
+int vfprintf(FILE *stream, const char *fmt, va_list ap)
+{
+ int count;
+ char buffer[320];
+
+ count = vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ write(stream->fd, buffer, count);
+
+ return count;
+}
+
diff --git a/roms/SLOF/lib/libc/stdio/vfscanf.c b/roms/SLOF/lib/libc/stdio/vfscanf.c
new file mode 100644
index 000000000..4ddd210a9
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/vfscanf.c
@@ -0,0 +1,266 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "string.h"
+#include "ctype.h"
+#include "stdlib.h"
+#include "stdio.h"
+#include "unistd.h"
+
+
+static int
+_getc(FILE * stream)
+{
+ int count;
+ char c;
+
+ if (stream->mode == _IONBF || stream->buf == NULL) {
+ if (read(stream->fd, &c, 1) == 1)
+ return (int) c;
+ else
+ return EOF;
+ }
+
+ if (stream->pos == 0 || stream->pos >= BUFSIZ ||
+ stream->buf[stream->pos] == '\0') {
+ count = read(stream->fd, stream->buf, BUFSIZ);
+ if (count < 0)
+ count = 0;
+ if (count < BUFSIZ)
+ stream->buf[count] = '\0';
+ stream->pos = 0;
+ }
+
+ return stream->buf[stream->pos++];
+}
+
+static void
+_ungetc(int ch, FILE * stream)
+{
+ if (stream->mode != _IONBF && stream->pos > 0)
+ stream->pos--;
+}
+
+static int
+_is_voidage(int ch)
+{
+ if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\0')
+ return 1;
+ else
+ return 0;
+}
+
+
+static int
+_scanf(FILE * stream, const char *fmt, va_list * ap)
+{
+ int i = 0;
+ int length = 0;
+
+ fmt++;
+
+ while (*fmt != '\0') {
+
+ char tbuf[256];
+ char ch;
+
+ switch (*fmt) {
+ case 'd':
+ case 'i':
+ ch = _getc(stream);
+ if (length == 0) {
+ while (!_is_voidage(ch) && isdigit(ch)) {
+ tbuf[i] = ch;
+ ch = _getc(stream);
+ i++;
+ }
+ } else {
+ while (!_is_voidage(ch) && i < length
+ && isdigit(ch)) {
+ tbuf[i] = ch;
+ ch = _getc(stream);
+ i++;
+ }
+ }
+ /* We tried to understand what this is good for...
+ * but we did not. We know for sure that it does not
+ * work on SLOF if this is active. */
+ /* _ungetc(ch, stream); */
+ tbuf[i] = '\0';
+
+ /* ch = _getc(stream); */
+ if (!_is_voidage(ch))
+ _ungetc(ch, stream);
+
+ if (strlen(tbuf) == 0)
+ return 0;
+
+ *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 10);
+ break;
+ case 'X':
+ case 'x':
+ ch = _getc(stream);
+ if (length == 0) {
+ while (!_is_voidage(ch) && isxdigit(ch)) {
+ tbuf[i] = ch;
+ ch = _getc(stream);
+ i++;
+ }
+ } else {
+ while (!_is_voidage(ch) && i < length
+ && isxdigit(ch)) {
+ tbuf[i] = ch;
+ ch = _getc(stream);
+ i++;
+ }
+ }
+ /* _ungetc(ch, stream); */
+ tbuf[i] = '\0';
+
+ /* ch = _getc(stream); */
+ if (!_is_voidage(ch))
+ _ungetc(ch, stream);
+
+ if (strlen(tbuf) == 0)
+ return 0;
+
+ *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 16);
+ break;
+ case 'O':
+ case 'o':
+ ch = _getc(stream);
+ if (length == 0) {
+ while (!_is_voidage(ch)
+ && !(ch < '0' || ch > '7')) {
+ tbuf[i] = ch;
+ ch = _getc(stream);
+ i++;
+ }
+ } else {
+ while (!_is_voidage(ch) && i < length
+ && !(ch < '0' || ch > '7')) {
+ tbuf[i] = ch;
+ ch = _getc(stream);
+ i++;
+ }
+ }
+ /* _ungetc(ch, stream); */
+ tbuf[i] = '\0';
+
+ /* ch = _getc(stream); */
+ if (!_is_voidage(ch))
+ _ungetc(ch, stream);
+
+ if (strlen(tbuf) == 0)
+ return 0;
+
+ *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 8);
+ break;
+ case 'c':
+ ch = _getc(stream);
+ while (_is_voidage(ch))
+ ch = _getc(stream);
+
+ *(va_arg(*ap, char *)) = ch;
+
+ ch = _getc(stream);
+ if (!_is_voidage(ch))
+ _ungetc(ch, stream);
+
+ break;
+ case 's':
+ ch = _getc(stream);
+ if (length == 0) {
+ while (!_is_voidage(ch)) {
+ tbuf[i] = ch;
+ ch = _getc(stream);
+ i++;
+ }
+ } else {
+ while (!_is_voidage(ch) && i < length) {
+ tbuf[i] = ch;
+ ch = _getc(stream);
+ i++;
+ }
+ }
+ /* _ungetc(ch, stream); */
+ tbuf[i] = '\0';
+
+ /* ch = _getc(stream); */
+ if (!_is_voidage(ch))
+ _ungetc(ch, stream);
+
+ strcpy(va_arg(*ap, char *), tbuf);
+ break;
+ default:
+ if (*fmt >= '0' && *fmt <= '9')
+ length += *fmt - '0';
+ break;
+ }
+ fmt++;
+ }
+
+ return 1;
+}
+
+
+
+int
+vfscanf(FILE * stream, const char *fmt, va_list ap)
+{
+ int args = 0;
+
+ while (*fmt != '\0') {
+
+ if (*fmt == '%') {
+
+ char formstr[20];
+ int i = 0;
+
+ do {
+ formstr[i] = *fmt;
+ fmt++;
+ i++;
+ } while (!
+ (*fmt == 'd' || *fmt == 'i' || *fmt == 'x'
+ || *fmt == 'X' || *fmt == 'p' || *fmt == 'c'
+ || *fmt == 's' || *fmt == '%' || *fmt == 'O'
+ || *fmt == 'o'));
+ formstr[i++] = *fmt;
+ formstr[i] = '\0';
+ if (*fmt != '%') {
+ if (_scanf(stream, formstr, &ap) <= 0)
+ return args;
+ else
+ args++;
+ }
+
+ }
+
+ fmt++;
+
+ }
+
+ return args;
+}
+
+int
+getc(FILE * stream)
+{
+ return _getc(stream);
+}
+
+int
+getchar(void)
+{
+ return _getc(stdin);
+}
diff --git a/roms/SLOF/lib/libc/stdio/vsnprintf.c b/roms/SLOF/lib/libc/stdio/vsnprintf.c
new file mode 100644
index 000000000..12f304398
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/vsnprintf.c
@@ -0,0 +1,299 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdbool.h>
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "ctype.h"
+
+static const unsigned long long convert[] = {
+ 0x0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFFFFULL, 0xFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL
+};
+
+static int
+print_str_fill(char **buffer, size_t bufsize, char *sizec,
+ const char *str, char c)
+{
+ unsigned i, sizei, len;
+ char *bstart = *buffer;
+
+ sizei = strtoul(sizec, NULL, 10);
+ len = strlen(str);
+ if (sizei > len) {
+ for (i = 0;
+ (i < (sizei - len)) && ((size_t)(*buffer - bstart) < bufsize);
+ i++) {
+ **buffer = c;
+ *buffer += 1;
+ }
+ }
+ return 1;
+}
+
+static int
+print_str(char **buffer, size_t bufsize, const char *str)
+{
+ char *bstart = *buffer;
+ size_t i;
+
+ for (i = 0; (i < strlen(str)) && ((size_t)(*buffer - bstart) < bufsize); i++) {
+ **buffer = str[i];
+ *buffer += 1;
+ }
+ return 1;
+}
+
+static unsigned int
+print_intlen(unsigned long value, unsigned short int base)
+{
+ int i = 0;
+
+ while (value > 0) {
+ value /= base;
+ i++;
+ }
+ if (i == 0)
+ i = 1;
+ return i;
+}
+
+static int
+print_itoa(char **buffer, size_t bufsize, unsigned long value,
+ unsigned short base, bool upper)
+{
+ const char zeichen[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+ char c;
+ size_t i, len;
+
+ if(base <= 2 || base > 16)
+ return 0;
+
+ len = i = print_intlen(value, base);
+
+ /* Don't print to buffer if bufsize is not enough. */
+ if (len > bufsize)
+ return 0;
+
+ do {
+ c = zeichen[value % base];
+ if (upper)
+ c = toupper(c);
+
+ (*buffer)[--i] = c;
+ value /= base;
+ } while(value);
+
+ *buffer += len;
+
+ return 1;
+}
+
+
+
+static int
+print_fill(char **buffer, size_t bufsize, char *sizec, unsigned long size,
+ unsigned short int base, char c, int optlen)
+{
+ int i, sizei, len;
+ char *bstart = *buffer;
+
+ sizei = strtoul(sizec, NULL, 10);
+ len = print_intlen(size, base) + optlen;
+ if (sizei > len) {
+ for (i = 0;
+ (i < (sizei - len)) && ((size_t)(*buffer - bstart) < bufsize);
+ i++) {
+ **buffer = c;
+ *buffer += 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+print_format(char **buffer, size_t bufsize, const char *format, void *var)
+{
+ char *start;
+ unsigned int i = 0, length_mod = sizeof(int);
+ unsigned long value = 0;
+ unsigned long signBit;
+ char *form, sizec[32];
+ char sign = ' ';
+ bool upper = false;
+
+ form = (char *) format;
+ start = *buffer;
+
+ form++;
+ if(*form == '0' || *form == '.') {
+ sign = '0';
+ form++;
+ }
+
+ while ((*form != '\0') && ((size_t)(*buffer - start) < bufsize)) {
+ switch(*form) {
+ case 'u':
+ case 'd':
+ case 'i':
+ sizec[i] = '\0';
+ value = (unsigned long) var;
+ signBit = 0x1ULL << (length_mod * 8 - 1);
+ if ((*form != 'u') && (signBit & value)) {
+ **buffer = '-';
+ *buffer += 1;
+ value = (-(unsigned long)value) & convert[length_mod];
+ }
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, value, 10, sign, 0);
+ print_itoa(buffer, bufsize - (*buffer - start),
+ value, 10, upper);
+ break;
+ case 'X':
+ upper = true;
+ /* fallthrough */
+ case 'x':
+ sizec[i] = '\0';
+ value = (unsigned long) var & convert[length_mod];
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, value, 16, sign, 0);
+ print_itoa(buffer, bufsize - (*buffer - start),
+ value, 16, upper);
+ break;
+ case 'O':
+ case 'o':
+ sizec[i] = '\0';
+ value = (long int) var & convert[length_mod];
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, value, 8, sign, 0);
+ print_itoa(buffer, bufsize - (*buffer - start),
+ value, 8, upper);
+ break;
+ case 'p':
+ sizec[i] = '\0';
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, (unsigned long) var, 16, ' ', 2);
+ print_str(buffer, bufsize - (*buffer - start),
+ "0x");
+ print_itoa(buffer, bufsize - (*buffer - start),
+ (unsigned long) var, 16, upper);
+ break;
+ case 'c':
+ sizec[i] = '\0';
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, 1, 10, ' ', 0);
+ **buffer = (unsigned long) var;
+ *buffer += 1;
+ break;
+ case 's':
+ sizec[i] = '\0';
+ print_str_fill(buffer,
+ bufsize - (*buffer - start), sizec,
+ (char *) var, ' ');
+
+ print_str(buffer, bufsize - (*buffer - start),
+ (char *) var);
+ break;
+ case 'l':
+ form++;
+ if(*form == 'l') {
+ length_mod = sizeof(long long int);
+ } else {
+ form--;
+ length_mod = sizeof(long int);
+ }
+ break;
+ case 'h':
+ form++;
+ if(*form == 'h') {
+ length_mod = sizeof(signed char);
+ } else {
+ form--;
+ length_mod = sizeof(short int);
+ }
+ break;
+ case 'z':
+ length_mod = sizeof(size_t);
+ break;
+ default:
+ if(*form >= '0' && *form <= '9')
+ sizec[i++] = *form;
+ }
+ form++;
+ }
+
+
+ return (long int) (*buffer - start);
+}
+
+
+/*
+ * The vsnprintf function prints a formated strings into a buffer.
+ * BUG: buffer size checking does not fully work yet
+ */
+int
+vsnprintf(char *buffer, size_t bufsize, const char *format, va_list arg)
+{
+ char *ptr, *bstart;
+
+ bstart = buffer;
+ ptr = (char *) format;
+
+ /*
+ * Return from here if size passed is zero, otherwise we would
+ * overrun buffer while setting NULL character at the end.
+ */
+ if (!buffer || !bufsize)
+ return 0;
+
+ /* Leave one space for NULL character */
+ bufsize--;
+
+ while(*ptr != '\0' && (size_t)(buffer - bstart) < bufsize)
+ {
+ if(*ptr == '%') {
+ char formstr[20];
+ int i=0;
+
+ do {
+ formstr[i] = *ptr;
+ ptr++;
+ i++;
+ } while(!(*ptr == 'd' || *ptr == 'i' || *ptr == 'u' || *ptr == 'x' || *ptr == 'X'
+ || *ptr == 'p' || *ptr == 'c' || *ptr == 's' || *ptr == '%'
+ || *ptr == 'O' || *ptr == 'o' ));
+ formstr[i++] = *ptr;
+ formstr[i] = '\0';
+ if(*ptr == '%') {
+ *buffer++ = '%';
+ } else {
+ print_format(&buffer,
+ bufsize - (buffer - bstart),
+ formstr, va_arg(arg, void *));
+ }
+ ptr++;
+ } else {
+
+ *buffer = *ptr;
+
+ buffer++;
+ ptr++;
+ }
+ }
+
+ *buffer = '\0';
+
+ return (buffer - bstart);
+}
diff --git a/roms/SLOF/lib/libc/stdio/vsprintf.c b/roms/SLOF/lib/libc/stdio/vsprintf.c
new file mode 100644
index 000000000..0dfd737bc
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/vsprintf.c
@@ -0,0 +1,19 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "stdio.h"
+
+int
+vsprintf(char *buffer, const char *format, va_list arg)
+{
+ return vsnprintf(buffer, 0x7fffffff, format, arg);
+}
diff --git a/roms/SLOF/lib/libc/stdio/vsscanf.c b/roms/SLOF/lib/libc/stdio/vsscanf.c
new file mode 100644
index 000000000..b9603e98b
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdio/vsscanf.c
@@ -0,0 +1,131 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+
+
+static void
+_scanf(const char **buffer, const char *fmt, va_list *ap)
+{
+ int i;
+ int length = 0;
+
+ fmt++;
+
+ while(*fmt != '\0') {
+
+ char tbuf[256];
+
+ switch(*fmt) {
+ case 'd':
+ case 'i':
+ if(length == 0) length = 256;
+
+ for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) {
+ tbuf[i] = **buffer;
+ *buffer += 1;
+ }
+ tbuf[i] = '\0';
+
+ *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 10);
+ break;
+ case 'X':
+ case 'x':
+ if(length == 0) length = 256;
+
+ for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) {
+ tbuf[i] = **buffer;
+ *buffer += 1;
+ }
+ tbuf[i] = '\0';
+
+ *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 16);
+ break;
+ case 'O':
+ case 'o':
+ if(length == 0) length = 256;
+
+ for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) {
+ tbuf[i] = **buffer;
+ *buffer += 1;
+ }
+ tbuf[i] = '\0';
+
+ *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 8);
+ break;
+ case 'c':
+ *(va_arg(*ap, char *)) = **buffer;
+ *buffer += 1;
+ if(length > 1)
+ for(i = 1; i < length; i++)
+ *buffer += 1;
+ break;
+ case 's':
+ if(length == 0) length = 256;
+
+ for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) {
+ tbuf[i] = **buffer;
+ *buffer += 1;
+ }
+
+ tbuf[i] = '\0';
+
+ strcpy(va_arg(*ap, char *), tbuf);
+ break;
+ default:
+ if(*fmt >= '0' && *fmt <= '9')
+ length += *fmt - '0';
+ break;
+ }
+ fmt++;
+ }
+
+}
+
+
+int
+vsscanf(const char *buffer, const char *fmt, va_list ap)
+{
+
+ while(*fmt != '\0') {
+
+ if(*fmt == '%') {
+
+ char formstr[20];
+ int i=0;
+
+ do {
+ formstr[i] = *fmt;
+ fmt++;
+ i++;
+ } while(!(*fmt == 'd' || *fmt == 'i' || *fmt == 'x' || *fmt == 'X'
+ || *fmt == 'p' || *fmt == 'c' || *fmt == 's' || *fmt == '%'
+ || *fmt == 'O' || *fmt == 'o' ));
+ formstr[i++] = *fmt;
+ formstr[i] = '\0';
+ if(*fmt != '%') {
+ while(*buffer == ' ' || *buffer == '\t' || *buffer == '\n')
+ buffer++;
+ _scanf(&buffer, formstr, &ap);
+ }
+
+ }
+
+ fmt++;
+
+ }
+
+ return 0;
+}
+
diff --git a/roms/SLOF/lib/libc/stdlib/Makefile.inc b/roms/SLOF/lib/libc/stdlib/Makefile.inc
new file mode 100644
index 000000000..702f6d7cf
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/Makefile.inc
@@ -0,0 +1,22 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+
+STDLIB_SRC_C = error.c atoi.c atol.c strtoul.c strtol.c rand.c \
+ malloc.c memalign.c realloc.c free.c
+
+STDLIB_SRC_ASM =
+STDLIB_SRCS = $(STDLIB_SRC_C:%=$(STDLIBCMNDIR)/%) $(STDLIB_SRC_ASM:%=$(STDLIBCMNDIR)/%)
+STDLIB_OBJS = $(STDLIB_SRC_C:%.c=%.o) $(STDLIB_SRC_ASM:%.S=%.o)
+
+%.o : $(STDLIBCMNDIR)/%.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
diff --git a/roms/SLOF/lib/libc/stdlib/atoi.c b/roms/SLOF/lib/libc/stdlib/atoi.c
new file mode 100644
index 000000000..d2fb33b88
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/atoi.c
@@ -0,0 +1,18 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+
+int atoi(const char *str)
+{
+ return strtol(str, NULL, 0);
+}
diff --git a/roms/SLOF/lib/libc/stdlib/atol.c b/roms/SLOF/lib/libc/stdlib/atol.c
new file mode 100644
index 000000000..a6aa47ba5
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/atol.c
@@ -0,0 +1,18 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+
+long atol(const char *str)
+{
+ return strtol(str, NULL, 0);
+}
diff --git a/roms/SLOF/lib/libc/stdlib/error.c b/roms/SLOF/lib/libc/stdlib/error.c
new file mode 100644
index 000000000..81020ca55
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/error.c
@@ -0,0 +1,15 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+int errno;
+
diff --git a/roms/SLOF/lib/libc/stdlib/free.c b/roms/SLOF/lib/libc/stdlib/free.c
new file mode 100644
index 000000000..d2765859b
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/free.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include "stdlib.h"
+#include "malloc_defs.h"
+
+void
+free(void *ptr)
+{
+ struct chunk *header;
+
+ if (!ptr)
+ return;
+
+ header = (struct chunk *) ptr;
+ header--;
+ header->inuse = 0;
+}
diff --git a/roms/SLOF/lib/libc/stdlib/malloc.c b/roms/SLOF/lib/libc/stdlib/malloc.c
new file mode 100644
index 000000000..b2a3138eb
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/malloc.c
@@ -0,0 +1,157 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include "stddef.h"
+#include "stdlib.h"
+#include "unistd.h"
+#include "malloc_defs.h"
+
+
+static int clean(void);
+
+
+/* act points to the end of the initialized heap and the start of uninitialized heap */
+static char *act;
+
+/* Pointers to start and end of heap: */
+static char *heap_start, *heap_end;
+
+
+/*
+ * Standard malloc function
+ */
+void *
+malloc(size_t size)
+{
+ char *header;
+ void *data;
+ size_t blksize; /* size of memory block including the chunk */
+
+ blksize = size + sizeof(struct chunk);
+
+ /* has malloc been called for the first time? */
+ if (act == 0) {
+ size_t initsize;
+ /* add some space so we have a good initial playground */
+ initsize = (blksize + 0x1000) & ~0x0fff;
+ /* get initial memory region with sbrk() */
+ heap_start = sbrk(initsize);
+ if (heap_start == (void*)-1)
+ return NULL;
+ heap_end = heap_start + initsize;
+ act = heap_start;
+ }
+
+ header = act;
+ data = act + sizeof(struct chunk);
+
+ /* Check if there is space left in the uninitialized part of the heap */
+ if (act + blksize > heap_end) {
+ //search at begin of heap
+ header = heap_start;
+
+ while ((((struct chunk *) header)->inuse != 0
+ || ((struct chunk *) header)->length < size)
+ && header < act) {
+ header = header + sizeof(struct chunk)
+ + ((struct chunk *) header)->length;
+ }
+
+ // check if heap is full
+ if (header >= act) {
+ if (clean()) {
+ // merging of free blocks succeeded, so try again
+ return malloc(size);
+ } else if (sbrk(blksize) == heap_end) {
+ // succeeded to get more memory, so try again
+ heap_end += blksize;
+ return malloc(size);
+ } else {
+ // No more memory available
+ return 0;
+ }
+ }
+
+ // Check if we need to split this memory block into two
+ if (((struct chunk *) header)->length > blksize) {
+ //available memory is too big
+ int alt;
+
+ alt = ((struct chunk *) header)->length;
+ ((struct chunk *) header)->inuse = 1;
+ ((struct chunk *) header)->length = size;
+ data = header + sizeof(struct chunk);
+
+ //mark the rest of the heap
+ header = data + size;
+ ((struct chunk *) header)->inuse = 0;
+ ((struct chunk *) header)->length =
+ alt - blksize;
+ } else {
+ //new memory matched exactly in available memory
+ ((struct chunk *) header)->inuse = 1;
+ data = header + sizeof(struct chunk);
+ }
+
+ } else {
+
+ ((struct chunk *) header)->inuse = 1;
+ ((struct chunk *) header)->length = size;
+
+ act += blksize;
+ }
+
+ return data;
+}
+
+
+/*
+ * Merge free memory blocks in initialized heap if possible
+ */
+static int
+clean(void)
+{
+ char *header;
+ char *firstfree = 0;
+ char check = 0;
+
+ header = heap_start;
+ //if (act == 0) // This should never happen
+ // act = heap_end;
+
+ while (header < act) {
+
+ if (((struct chunk *) header)->inuse == 0) {
+ if (firstfree == 0) {
+ /* First free block in a row, only save address */
+ firstfree = header;
+
+ } else {
+ /* more than one free block in a row, merge them! */
+ ((struct chunk *) firstfree)->length +=
+ ((struct chunk *) header)->length +
+ sizeof(struct chunk);
+ check = 1;
+ }
+ } else {
+ firstfree = 0;
+
+ }
+
+ header = header + sizeof(struct chunk)
+ + ((struct chunk *) header)->length;
+
+ }
+
+ return check;
+}
diff --git a/roms/SLOF/lib/libc/stdlib/malloc_defs.h b/roms/SLOF/lib/libc/stdlib/malloc_defs.h
new file mode 100644
index 000000000..19330267e
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/malloc_defs.h
@@ -0,0 +1,16 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+struct chunk {
+ unsigned inuse : 4;
+ unsigned length : 28;
+} __attribute__((packed));
diff --git a/roms/SLOF/lib/libc/stdlib/memalign.c b/roms/SLOF/lib/libc/stdlib/memalign.c
new file mode 100644
index 000000000..3b678aaf5
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/memalign.c
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include "stdlib.h"
+
+
+void *
+memalign(size_t blocksize, size_t bytes)
+{
+ void *x;
+
+ x = malloc(bytes + blocksize);
+ x = (void *) (((unsigned long) x + blocksize - 1) & ~(blocksize - 1));
+
+ return (void *) x;
+}
diff --git a/roms/SLOF/lib/libc/stdlib/rand.c b/roms/SLOF/lib/libc/stdlib/rand.c
new file mode 100644
index 000000000..39f5a9a2c
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/rand.c
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+
+
+static unsigned long _rand = 1;
+
+int
+rand(void)
+{
+ _rand = _rand * 1237732973 + 34563;
+
+ return ((unsigned int) (_rand >> 16) & RAND_MAX);
+}
+
+void srand(unsigned int seed)
+{
+ _rand = seed;
+}
diff --git a/roms/SLOF/lib/libc/stdlib/realloc.c b/roms/SLOF/lib/libc/stdlib/realloc.c
new file mode 100644
index 000000000..652e90077
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/realloc.c
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include "stdlib.h"
+#include "string.h"
+#include "malloc_defs.h"
+
+void *
+realloc(void *ptr, size_t size)
+{
+ struct chunk *header;
+ char *newptr, *start;
+
+ header = (struct chunk *) ptr;
+ header--;
+
+ if (size <= header->length)
+ return ptr;
+
+ newptr = (char *) malloc(size);
+ if (newptr == NULL)
+ return 0;
+
+ start = newptr;
+ memcpy((void *) newptr, (const void *) ptr, header->length);
+
+ header->inuse = 0;
+
+ return start;
+}
diff --git a/roms/SLOF/lib/libc/stdlib/strtol.c b/roms/SLOF/lib/libc/stdlib/strtol.c
new file mode 100644
index 000000000..474597a23
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/strtol.c
@@ -0,0 +1,115 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+
+long int strtol(const char *S, char **PTR,int BASE)
+{
+ long rval = 0;
+ short int negative = 0;
+ short int digit;
+ // *PTR is S, unless PTR is NULL, in which case i override it with my own ptr
+ char* ptr;
+ if (PTR == 0)
+ {
+ //override
+ PTR = &ptr;
+ }
+ // i use PTR to advance through the string
+ *PTR = (char *) S;
+ //check if BASE is ok
+ if ((BASE < 0) || BASE > 36)
+ {
+ return 0;
+ }
+ // ignore white space at beginning of S
+ while ((**PTR == ' ')
+ || (**PTR == '\t')
+ || (**PTR == '\n')
+ || (**PTR == '\r')
+ )
+ {
+ (*PTR)++;
+ }
+ // check if S starts with "-" in which case the return value is negative
+ if (**PTR == '-')
+ {
+ negative = 1;
+ (*PTR)++;
+ }
+ // if BASE is 0... determine the base from the first chars...
+ if (BASE == 0)
+ {
+ // if S starts with "0x", BASE = 16, else 10
+ if ((**PTR == '0') && (*((*PTR)+1) == 'x'))
+ {
+ BASE = 16;
+ (*PTR)++;
+ (*PTR)++;
+ }
+ else
+ {
+ BASE = 10;
+ }
+ }
+ if (BASE == 16)
+ {
+ // S may start with "0x"
+ if ((**PTR == '0') && (*((*PTR)+1) == 'x'))
+ {
+ (*PTR)++;
+ (*PTR)++;
+ }
+ }
+ //until end of string
+ while (**PTR)
+ {
+ if (((**PTR) >= '0') && ((**PTR) <= '9'))
+ {
+ //digit (0..9)
+ digit = **PTR - '0';
+ }
+ else if (((**PTR) >= 'a') && ((**PTR) <='z'))
+ {
+ //alphanumeric digit lowercase(a (10) .. z (35) )
+ digit = (**PTR - 'a') + 10;
+ }
+ else if (((**PTR) >= 'A') && ((**PTR) <='Z'))
+ {
+ //alphanumeric digit uppercase(a (10) .. z (35) )
+ digit = (**PTR - 'A') + 10;
+ }
+ else
+ {
+ //end of parseable number reached...
+ break;
+ }
+ if (digit < BASE)
+ {
+ rval = (rval * BASE) + digit;
+ }
+ else
+ {
+ //digit found, but its too big for current base
+ //end of parseable number reached...
+ break;
+ }
+ //next...
+ (*PTR)++;
+ }
+ if (negative)
+ {
+ return rval * -1;
+ }
+ //else
+ return rval;
+}
diff --git a/roms/SLOF/lib/libc/stdlib/strtoul.c b/roms/SLOF/lib/libc/stdlib/strtoul.c
new file mode 100644
index 000000000..754e7db4b
--- /dev/null
+++ b/roms/SLOF/lib/libc/stdlib/strtoul.c
@@ -0,0 +1,105 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+
+unsigned long int strtoul(const char *S, char **PTR,int BASE)
+{
+ unsigned long rval = 0;
+ short int digit;
+ // *PTR is S, unless PTR is NULL, in which case i override it with my own ptr
+ char* ptr;
+ if (PTR == 0)
+ {
+ //override
+ PTR = &ptr;
+ }
+ // i use PTR to advance through the string
+ *PTR = (char *) S;
+ //check if BASE is ok
+ if ((BASE < 0) || BASE > 36)
+ {
+ return 0;
+ }
+ // ignore white space at beginning of S
+ while ((**PTR == ' ')
+ || (**PTR == '\t')
+ || (**PTR == '\n')
+ || (**PTR == '\r')
+ )
+ {
+ (*PTR)++;
+ }
+ // if BASE is 0... determine the base from the first chars...
+ if (BASE == 0)
+ {
+ // if S starts with "0x", BASE = 16, else 10
+ if ((**PTR == '0') && (*((*PTR)+1) == 'x'))
+ {
+ BASE = 16;
+ (*PTR)++;
+ (*PTR)++;
+ }
+ else
+ {
+ BASE = 10;
+ }
+ }
+ if (BASE == 16)
+ {
+ // S may start with "0x"
+ if ((**PTR == '0') && (*((*PTR)+1) == 'x'))
+ {
+ (*PTR)++;
+ (*PTR)++;
+ }
+ }
+ //until end of string
+ while (**PTR)
+ {
+ if (((**PTR) >= '0') && ((**PTR) <='9'))
+ {
+ //digit (0..9)
+ digit = **PTR - '0';
+ }
+ else if (((**PTR) >= 'a') && ((**PTR) <='z'))
+ {
+ //alphanumeric digit lowercase(a (10) .. z (35) )
+ digit = (**PTR - 'a') + 10;
+ }
+ else if (((**PTR) >= 'A') && ((**PTR) <='Z'))
+ {
+ //alphanumeric digit uppercase(a (10) .. z (35) )
+ digit = (**PTR - 'A') + 10;
+ }
+ else
+ {
+ //end of parseable number reached...
+ break;
+ }
+ if (digit < BASE)
+ {
+ rval = (rval * BASE) + digit;
+ }
+ else
+ {
+ //digit found, but its too big for current base
+ //end of parseable number reached...
+ break;
+ }
+ //next...
+ (*PTR)++;
+ }
+ //done
+ return rval;
+}
+
diff --git a/roms/SLOF/lib/libc/string/Makefile.inc b/roms/SLOF/lib/libc/string/Makefile.inc
new file mode 100644
index 000000000..0a7773839
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/Makefile.inc
@@ -0,0 +1,22 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+
+STRING_SRC_C = strcat.c strchr.c strcmp.c strcpy.c strlen.c strncmp.c \
+ strncpy.c strstr.c memset.c memcpy.c memmove.c memchr.c \
+ memcmp.c strcasecmp.c strncasecmp.c strtok.c strrchr.c
+STRING_SRC_ASM =
+STRING_SRCS = $(STRING_SRC_C:%=$(STRINGCMNDIR)/%) $(STRING_SRC_ASM:%=$(STRINGCMNDIR)/%)
+STRING_OBJS = $(STRING_SRC_C:%.c=%.o) $(STRING_SRC_ASM:%.S=%.o)
+
+%.o : $(STRINGCMNDIR)/%.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
diff --git a/roms/SLOF/lib/libc/string/memchr.c b/roms/SLOF/lib/libc/string/memchr.c
new file mode 100644
index 000000000..c3fe751c6
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/memchr.c
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "string.h"
+
+
+void *
+memchr(const void *ptr, int c, size_t n)
+{
+ unsigned char ch = (unsigned char)c;
+ const unsigned char *p = ptr;
+
+ while (n-- > 0) {
+ if (*p == ch)
+ return (void *)p;
+ p += 1;
+ }
+
+ return NULL;
+}
diff --git a/roms/SLOF/lib/libc/string/memcmp.c b/roms/SLOF/lib/libc/string/memcmp.c
new file mode 100644
index 000000000..3b69cefb9
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/memcmp.c
@@ -0,0 +1,30 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "string.h"
+
+
+int
+memcmp(const void *ptr1, const void *ptr2, size_t n)
+{
+ const unsigned char *p1 = ptr1;
+ const unsigned char *p2 = ptr2;
+
+ while (n-- > 0) {
+ if (*p1 != *p2)
+ return (*p1 - *p2);
+ p1 += 1;
+ p2 += 1;
+ }
+
+ return 0;
+}
diff --git a/roms/SLOF/lib/libc/string/memcpy.c b/roms/SLOF/lib/libc/string/memcpy.c
new file mode 100644
index 000000000..00f419b80
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/memcpy.c
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "string.h"
+
+void *
+memcpy(void *dest, const void *src, size_t n)
+{
+ char *cdest;
+ const char *csrc = src;
+
+ cdest = dest;
+ while (n-- > 0) {
+ *cdest++ = *csrc++;
+ }
+
+ return dest;
+}
diff --git a/roms/SLOF/lib/libc/string/memmove.c b/roms/SLOF/lib/libc/string/memmove.c
new file mode 100644
index 000000000..9d0962847
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/memmove.c
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "string.h"
+
+
+void *
+memmove(void *dest, const void *src, size_t n)
+{
+ char *cdest;
+ const char *csrc;
+ size_t i;
+
+ /* Do the buffers overlap in a bad way? */
+ if (src < dest && src + n >= dest) {
+ /* Copy from end to start */
+ cdest = dest + n - 1;
+ csrc = src + n - 1;
+ for (i = 0; i < n; i++) {
+ *cdest-- = *csrc--;
+ }
+ }
+ else {
+ /* Normal copy is possible */
+ cdest = dest;
+ csrc = src;
+ for (i = 0; i < n; i++) {
+ *cdest++ = *csrc++;
+ }
+ }
+
+ return dest;
+}
diff --git a/roms/SLOF/lib/libc/string/memset.c b/roms/SLOF/lib/libc/string/memset.c
new file mode 100644
index 000000000..f8dfbf524
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/memset.c
@@ -0,0 +1,25 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "string.h"
+
+void *
+memset(void *dest, int c, size_t size)
+{
+ unsigned char *d = (unsigned char *)dest;
+
+ while (size-- > 0) {
+ *d++ = (unsigned char)c;
+ }
+
+ return dest;
+}
diff --git a/roms/SLOF/lib/libc/string/strcasecmp.c b/roms/SLOF/lib/libc/string/strcasecmp.c
new file mode 100644
index 000000000..f75294fb9
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strcasecmp.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include <ctype.h>
+
+int
+strcasecmp(const char *s1, const char *s2)
+{
+ while (*s1 != 0 && *s2 != 0) {
+ if (toupper(*s1) != toupper(*s2))
+ break;
+ ++s1;
+ ++s2;
+ }
+
+ return *s1 - *s2;
+}
+
diff --git a/roms/SLOF/lib/libc/string/strcat.c b/roms/SLOF/lib/libc/string/strcat.c
new file mode 100644
index 000000000..eb597a025
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strcat.c
@@ -0,0 +1,24 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+char *
+strcat(char *dst, const char *src)
+{
+ int p;
+
+ p = strlen(dst);
+ strcpy(&dst[p], src);
+
+ return dst;
+}
diff --git a/roms/SLOF/lib/libc/string/strchr.c b/roms/SLOF/lib/libc/string/strchr.c
new file mode 100644
index 000000000..528a319c9
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strchr.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+char *
+strchr(const char *s, int c)
+{
+ char cb = c;
+
+ while (*s != 0) {
+ if (*s == cb) {
+ return (char *)s;
+ }
+ s += 1;
+ }
+
+ return NULL;
+}
diff --git a/roms/SLOF/lib/libc/string/strcmp.c b/roms/SLOF/lib/libc/string/strcmp.c
new file mode 100644
index 000000000..48eaed246
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strcmp.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+
+int
+strcmp(const char *s1, const char *s2)
+{
+ while (*s1 != 0 && *s2 != 0) {
+ if (*s1 != *s2)
+ break;
+ s1 += 1;
+ s2 += 1;
+ }
+
+ return *s1 - *s2;
+}
+
diff --git a/roms/SLOF/lib/libc/string/strcpy.c b/roms/SLOF/lib/libc/string/strcpy.c
new file mode 100644
index 000000000..48eb62cb5
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strcpy.c
@@ -0,0 +1,25 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+char *
+strcpy(char *dst, const char *src)
+{
+ char *ptr = dst;
+
+ do {
+ *ptr++ = *src;
+ } while (*src++ != 0);
+
+ return dst;
+}
diff --git a/roms/SLOF/lib/libc/string/strlen.c b/roms/SLOF/lib/libc/string/strlen.c
new file mode 100644
index 000000000..37a1b7812
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strlen.c
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+size_t
+strlen(const char *s)
+{
+ int len = 0;
+
+ while (*s != 0) {
+ len += 1;
+ s += 1;
+ }
+
+ return len;
+}
+
diff --git a/roms/SLOF/lib/libc/string/strncasecmp.c b/roms/SLOF/lib/libc/string/strncasecmp.c
new file mode 100644
index 000000000..4140931e3
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strncasecmp.c
@@ -0,0 +1,32 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include <ctype.h>
+
+
+int
+strncasecmp(const char *s1, const char *s2, size_t n)
+{
+ if (n < 1)
+ return 0;
+
+ while (*s1 != 0 && *s2 != 0 && --n > 0) {
+ if (toupper(*s1) != toupper(*s2))
+ break;
+ ++s1;
+ ++s2;
+ }
+
+ return toupper(*s1) - toupper(*s2);
+}
+
diff --git a/roms/SLOF/lib/libc/string/strncmp.c b/roms/SLOF/lib/libc/string/strncmp.c
new file mode 100644
index 000000000..a886736a9
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strncmp.c
@@ -0,0 +1,31 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+
+int
+strncmp(const char *s1, const char *s2, size_t n)
+{
+ if (n < 1)
+ return 0;
+
+ while (*s1 != 0 && *s2 != 0 && --n > 0) {
+ if (*s1 != *s2)
+ break;
+ s1 += 1;
+ s2 += 1;
+ }
+
+ return *s1 - *s2;
+}
+
diff --git a/roms/SLOF/lib/libc/string/strncpy.c b/roms/SLOF/lib/libc/string/strncpy.c
new file mode 100644
index 000000000..0f41f93c9
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strncpy.c
@@ -0,0 +1,33 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+char *
+strncpy(char *dst, const char *src, size_t n)
+{
+ char *ret = dst;
+
+ /* Copy string */
+ while (*src != 0 && n > 0) {
+ *dst++ = *src++;
+ n -= 1;
+ }
+
+ /* strncpy always clears the rest of destination string... */
+ while (n > 0) {
+ *dst++ = 0;
+ n -= 1;
+ }
+
+ return ret;
+}
diff --git a/roms/SLOF/lib/libc/string/strrchr.c b/roms/SLOF/lib/libc/string/strrchr.c
new file mode 100644
index 000000000..ccfaa9fcf
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strrchr.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * libc strrchr() implementation
+ *
+ * This program and the accompanying materials are made available under
+ * the terms of the BSD License which accompanies this distribution, and
+ * is available at http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+char *
+strrchr(const char *s, int c)
+{
+ char cb = c;
+ char *ptr = (char *)s + strlen(s) - 1;
+
+ while (ptr >= s) {
+ if (*ptr == cb) {
+ return ptr;
+ }
+ --ptr;
+ }
+
+ return NULL;
+}
diff --git a/roms/SLOF/lib/libc/string/strstr.c b/roms/SLOF/lib/libc/string/strstr.c
new file mode 100644
index 000000000..3e090d2c5
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strstr.c
@@ -0,0 +1,37 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+char *
+strstr(const char *hay, const char *needle)
+{
+ char *pos;
+ int hlen, nlen;
+
+ if (hay == NULL || needle == NULL)
+ return NULL;
+
+ hlen = strlen(hay);
+ nlen = strlen(needle);
+ if (nlen < 1)
+ return (char *)hay;
+
+ for (pos = (char *)hay; pos < hay + hlen; pos++) {
+ if (strncmp(pos, needle, nlen) == 0) {
+ return pos;
+ }
+ }
+
+ return NULL;
+}
+
diff --git a/roms/SLOF/lib/libc/string/strtok.c b/roms/SLOF/lib/libc/string/strtok.c
new file mode 100644
index 000000000..665c08db6
--- /dev/null
+++ b/roms/SLOF/lib/libc/string/strtok.c
@@ -0,0 +1,45 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+
+char *
+strtok(char *src, const char *pattern)
+{
+ static char *nxtTok;
+ char *retVal = NULL;
+
+ if (!src)
+ src = nxtTok;
+
+ while (*src) {
+ const char *pp = pattern;
+ while (*pp) {
+ if (*pp == *src) {
+ break;
+ }
+ pp++;
+ }
+ if (!*pp) {
+ if (!retVal)
+ retVal = src;
+ else if (!src[-1])
+ break;
+ } else
+ *src = '\0';
+ src++;
+ }
+
+ nxtTok = src;
+
+ return retVal;
+}
diff --git a/roms/SLOF/lib/libe1k/Makefile b/roms/SLOF/lib/libe1k/Makefile
new file mode 100644
index 000000000..0c3169f02
--- /dev/null
+++ b/roms/SLOF/lib/libe1k/Makefile
@@ -0,0 +1,51 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008, 2013 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
+
+LDFLAGS = -nostdlib
+
+TARGET = ../libe1k.a
+
+
+all: $(TARGET) Makefile.dep
+
+SRCS = e1k.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libe1k/e1k.c b/roms/SLOF/lib/libe1k/e1k.c
new file mode 100644
index 000000000..3bbc75fba
--- /dev/null
+++ b/roms/SLOF/lib/libe1k/e1k.c
@@ -0,0 +1,1000 @@
+/******************************************************************************
+ * Copyright (c) 2007, 2011, 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * e1000 Gigabit Ethernet Driver for SLOF
+ *
+ * Reference:
+ * PCI/PCI-X Family of Gigabit Ethernet Controllers
+ * Software Developer's Manual Rev. 3.3, Intel, December 2006
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <byteorder.h>
+#include <helpers.h>
+#include <netdriver.h>
+#include "e1k.h"
+
+/*
+ * local defines
+ ******************************************************************************
+ */
+#define E1K_NUM_RX_DESC 128 // do not change
+#define E1K_NUM_TX_DESC 128 // do not change
+#define E1K_BUF_SIZE 2096 // do not change
+
+#define NUM_MAC_ADDR 16 // number of mac address register pairs
+#define EEPROM_MAC_OFFS 0 // position of mac address in eeprom
+
+/*
+ * local types
+ ******************************************************************************
+ */
+typedef struct {
+ uint32_t m_dev_u32;
+ uint64_t m_devmsk_u64;
+ char *m_name;
+} e1k_dev_t;
+
+/*
+ * e1k common data structures
+ */
+
+/*
+ * transmit buffer descriptor
+ */
+typedef struct {
+ uint64_t m_buffer_u64;
+ uint16_t m_len_u16;
+ uint8_t m_cso_u08;
+ uint8_t m_cmd_u08;
+ uint8_t m_sta_u08;
+ uint8_t m_css_u08;
+ uint16_t m_spe_u16;
+} __attribute__ ((packed)) e1k_tx_desc_st;
+
+
+/*
+ * receive buffer descriptor
+ */
+typedef struct {
+ uint64_t m_buffer_u64;
+ uint16_t m_len_u16;
+ uint16_t m_csm_u16;
+ uint8_t m_sta_u08;
+ uint8_t m_err_u08;
+ uint16_t m_spe_u16;
+} __attribute__ ((packed)) e1k_rx_desc_st;
+
+/*
+ * e1k device structure
+ */
+typedef struct {
+ /*
+ * device identification mask
+ */
+ uint64_t m_device_u64;
+
+ /*
+ * memory mapped base address of NIC
+ */
+ uint64_t m_baseaddr_u64;
+
+ /*
+ * transmit & receive rings
+ * must be 16 byte aligned
+ */
+ e1k_tx_desc_st m_tx_ring_pst[E1K_NUM_TX_DESC];
+ e1k_rx_desc_st m_rx_ring_pst[E1K_NUM_RX_DESC];
+
+ /*
+ * transmit & receive buffers
+ * must be 16 byte aligned
+ */
+ uint8_t m_tx_buffer_pu08[E1K_NUM_TX_DESC][E1K_BUF_SIZE];
+ uint8_t m_rx_buffer_pu08[E1K_NUM_RX_DESC][E1K_BUF_SIZE];
+
+ /*
+ * next receive descriptor index
+ */
+ uint32_t m_rx_next_u32;
+
+ /*
+ * command register storage
+ */
+ uint16_t m_com_r_u16;
+
+ /*
+ * padding to make the size of the structure a multiple of 16 byte
+ */
+ uint16_t m_pad16_u16;
+ uint64_t m_pad64_u32;
+
+} __attribute__ ((packed)) e1k_st;
+
+/*
+ * local constants
+ ******************************************************************************
+ */
+#define E1K_82540 ((uint64_t) 0x1)
+#define E1K_82541 ((uint64_t) 0x2)
+#define E1K_82544 ((uint64_t) 0x4)
+#define E1K_82545 ((uint64_t) 0x8)
+#define E1K_82546 ((uint64_t) 0x10)
+#define E1K_82547 ((uint64_t) 0x20)
+
+#define IS_82541 ((m_e1k.m_device_u64 & E1K_82541) != 0)
+#define IS_82546 ((m_e1k.m_device_u64 & E1K_82546) != 0)
+#define IS_82547 ((m_e1k.m_device_u64 & E1K_82547) != 0)
+
+static const e1k_dev_t e1k_dev[] = {
+ { 0x1019, E1K_82547, "82547EI/GI Copper" },
+ { 0x101A, E1K_82547, "82547EI Mobile" },
+ { 0x1010, E1K_82546, "52546EB Copper, Dual Port" },
+ { 0x1012, E1K_82546, "82546EB Fiber, Dual Port" },
+/* { 0x101D, E1K_82546, "82546EB Copper, Quad Port" }, */
+ { 0x1079, E1K_82546, "82546GB Copper, Dual Port" },
+ { 0x107A, E1K_82546, "82546GB Fiber, Dual Port" },
+ { 0x107B, E1K_82546, "82546GB SerDes, Dual Port" },
+ { 0x100F, E1K_82545, "82545EM Copper" },
+ { 0x1011, E1K_82545, "82545EM Fiber" },
+ { 0x1026, E1K_82545, "82545GM Copper" },
+ { 0x1027, E1K_82545, "82545GM Fiber" },
+ { 0x1028, E1K_82545, "82545GM SerDes" },
+ { 0x1107, E1K_82544, "82544EI Copper" },
+ { 0x1112, E1K_82544, "82544GC Copper" },
+ { 0x1013, E1K_82541, "82541EI Copper" },
+ { 0x1018, E1K_82541, "82541EI Mobile" },
+ { 0x1076, E1K_82541, "82541GI Copper" },
+ { 0x1077, E1K_82541, "82541GI Mobile" },
+ { 0x1078, E1K_82541, "82541ER Copper" },
+ { 0x107C, E1K_82541, "82541PI" },
+ { 0x1015, E1K_82540, "82540EM Mobile" },
+ { 0x1016, E1K_82540, "82540EP Mobile" },
+ { 0x1017, E1K_82540, "82540EP Desktop" },
+ { 0x100E, E1K_82540, "82540EM Desktop" },
+ { 0 }
+};
+
+/*
+ * local variables
+ ******************************************************************************
+ */
+static e1k_st m_e1k __attribute__ ((aligned(16)));
+static long dma_offset;
+
+/*
+ * global functions
+ ******************************************************************************
+ */
+int
+check_driver(uint16_t vendor_id, uint16_t device_id);
+
+static int e1k_init(net_driver_t *driver);
+static int e1k_term(void);
+static int e1k_xmit(char *f_buffer_pc, unsigned f_len_i);
+static int e1k_receive(char *f_buffer_pc, unsigned f_len_i);
+
+/**
+ * Translate virtual to "physical" address, ie. an address
+ * which can be used for DMA transfers.
+ */
+static uint64_t
+virt2dma(void *addr)
+{
+ return (uint64_t)addr + dma_offset;
+}
+
+static void *
+dma2virt(uint64_t addr)
+{
+ return (void *)(addr - dma_offset);
+}
+
+/*
+ * local inline functions for e1k register access
+ ******************************************************************************
+ */
+static uint32_t
+e1k_rd32(uint16_t f_offs_u16)
+{ // caution: shall only be used after initialization!
+ return bswap_32(rd32(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16));
+}
+
+/* not used so far
+static uint16_t
+e1k_rd16(uint16_t f_offs_u16)
+{ // caution: shall only be used after initialization!
+ return bswap_16(rd16(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16));
+}*/
+
+/* not used so far
+static uint8_t
+e1k_rd08(uint16_t f_offs_u16)
+{ // caution: shall only be used after initialization!
+ return rd08(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16);
+}*/
+
+static void
+e1k_wr32(uint16_t f_offs_u16, uint32_t f_val_u32)
+{ // caution: shall only be used after initialization!
+ wr32(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16, bswap_32(f_val_u32));
+}
+
+/* not used so far
+static void
+e1k_wr16(uint16_t f_offs_u16, uint16_t f_val_u16)
+{ // caution: shall only be used after initialization!
+ wr16(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16, bswap_16(f_val_u16));
+}*/
+
+/* not used so far
+static void
+e1k_wr08(uint16_t f_offs_u16, uint8_t f_val_u08)
+{ // caution: shall only be used after initialization!
+ wr08(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u08);
+}*/
+
+static void
+e1k_setb32(uint16_t f_offs_u16, uint32_t f_mask_u32)
+{
+ uint32_t v;
+
+ v = e1k_rd32(f_offs_u16);
+ v |= f_mask_u32;
+ e1k_wr32(f_offs_u16, v);
+}
+
+/* not used so far
+static void
+e1k_setb16(uint16_t f_offs_u16, uint16_t f_mask_u16)
+{
+ uint16_t v;
+ v = e1k_rd16(f_offs_u16);
+ v |= f_mask_u16;
+ e1k_wr16(f_offs_u16, v);
+}*/
+
+/* not used so far
+static void
+e1k_setb08(uint16_t f_offs_u16, uint8_t f_mask_u08)
+{
+ uint8_t v;
+ v = e1k_rd08(f_offs_u16);
+ v |= f_mask_u08;
+ e1k_wr08(f_offs_u16, v);
+}*/
+
+static void
+e1k_clrb32(uint16_t f_offs_u16, uint32_t f_mask_u32)
+{
+ uint32_t v;
+
+ v = e1k_rd32(f_offs_u16);
+ v &= ~f_mask_u32;
+ e1k_wr32(f_offs_u16, v);
+}
+
+/* not used so far
+static void
+e1k_clrb16(uint16_t f_offs_u16, uint16_t f_mask_u16)
+{
+ uint16_t v;
+
+ v = e1k_rd16(f_offs_u16);
+ v &= ~f_mask_u16;
+ e1k_wr16(f_offs_u16, v);
+}*/
+
+/* not used so far
+static void
+e1k_clrb08(uint16_t f_offs_u16, uint8_t f_mask_u08)
+{
+ uint8_t v;
+ v = e1k_rd08(f_offs_u16);
+ v &= ~f_mask_u08;
+ e1k_wr08(f_offs_u16, v);
+}*/
+
+static int32_t
+e1k_eep_rd16(uint8_t f_offs_u08, uint16_t *f_data_pu16)
+{
+ uint32_t i;
+ uint32_t v;
+ int32_t done_shft;
+ int32_t addr_shft;
+
+ if(IS_82541 || IS_82547) {
+ addr_shft = 2;
+ done_shft = 1;
+ } else {
+ addr_shft = 8;
+ done_shft = 4;
+ }
+
+ /*
+ * initiate eeprom read
+ */
+ e1k_wr32(EERD, ((uint32_t) f_offs_u08 << addr_shft) | // address
+ BIT32(0)); // start read
+
+ /*
+ * wait for read done bit to be set
+ */
+ i = 1000;
+ v = e1k_rd32(EERD);
+ while ((--i) &&
+ ((v & BIT32(done_shft)) == 0)) {
+ SLOF_msleep(1);
+ v = e1k_rd32(EERD);
+ }
+
+ /*
+ * return on error
+ */
+ if ((v & BIT32(done_shft)) == 0) {
+ return -1;
+ }
+
+ /*
+ * return data
+ */
+ *f_data_pu16 = (uint16_t) ((v >> 16) & 0xffff);
+
+ return 0;
+}
+
+/*
+ * ring initialization
+ */
+static void
+e1k_init_receiver(void)
+{
+ uint32_t i;
+ uint64_t addr;
+
+ /*
+ * disable receiver for initialization
+ */
+ e1k_wr32(RCTL, 0);
+
+ /*
+ * clear receive desciptors and setup buffer pointers
+ */
+ for (i = 0; i < E1K_NUM_RX_DESC; i++) {
+ memset((uint8_t *) &m_e1k.m_rx_ring_pst[i], 0,
+ sizeof(e1k_rx_desc_st));
+ mb();
+
+ m_e1k.m_rx_ring_pst[i].m_buffer_u64 =
+ bswap_64(virt2dma(&m_e1k.m_rx_buffer_pu08[i][0]));
+ }
+
+ /*
+ * initialize previously received index
+ */
+ m_e1k.m_rx_next_u32 = 0;
+
+ /*
+ * setup the base address and the length of the rx descriptor ring
+ */
+ addr = virt2dma(&m_e1k.m_rx_ring_pst[0]);
+ e1k_wr32(RDBAH, (uint32_t) ((uint64_t) addr >> 32));
+ e1k_wr32(RDBAL, (uint32_t) ((uint64_t) addr & 0xffffffff));
+ e1k_wr32(RDLEN, E1K_NUM_RX_DESC * sizeof(e1k_rx_desc_st));
+
+ /*
+ * setup the rx head and tail descriptor indices
+ */
+ e1k_wr32(RDH, 0);
+ e1k_wr32(RDT, E1K_NUM_RX_DESC - 1);
+
+ /*
+ * setup the receive delay timer register
+ */
+ e1k_wr32(RDTR, 0);
+
+ /*
+ * setup the receive control register
+ */
+ e1k_wr32(RCTL, BIT32( 1) | // enable receiver
+ BIT32( 4) | // enable multicast reception
+ BIT32(15)); // broadcast accept mode
+ // packet size 2048
+ // no buffer extension
+}
+
+static void
+e1k_init_transmitter(void)
+{
+ uint32_t i;
+ uint64_t addr;
+
+ /*
+ * clear transmit desciptors and setup buffer pointers
+ */
+ for (i = 0; i < E1K_NUM_TX_DESC; i++) {
+ memset((uint8_t *) &m_e1k.m_tx_ring_pst[i], 0,
+ sizeof(e1k_tx_desc_st));
+ mb();
+
+ m_e1k.m_tx_ring_pst[i].m_buffer_u64 =
+ bswap_64(virt2dma(&m_e1k.m_tx_buffer_pu08[i][0]));
+ }
+
+ /*
+ * setup the base address and the length of the tx descriptor ring
+ */
+ addr = virt2dma(&m_e1k.m_tx_ring_pst[0]);
+ e1k_wr32(TDBAH, (uint32_t) ((uint64_t) addr >> 32));
+ e1k_wr32(TDBAL, (uint32_t) ((uint64_t) addr & 0xffffffff));
+ e1k_wr32(TDLEN, E1K_NUM_TX_DESC * sizeof(e1k_tx_desc_st));
+
+ /*
+ * setup the rx head and tail descriptor indices
+ */
+ e1k_wr32(TDH, 0);
+ e1k_wr32(TDT, 0);
+
+ /*
+ * initialize the transmit control register
+ */
+ e1k_wr32(TCTL, BIT32(1) | // enable transmitter
+ BIT32(3) | // pad short packets
+ ((uint32_t) 0x0f << 4) | // collision threshhold
+ ((uint32_t) 0x40 << 12)); // collision distance
+}
+
+static int32_t
+e1k_mac_init(uint8_t *f_mac_pu08)
+{
+ uint32_t l_ah_u32;
+ uint32_t l_al_u32;
+ uint32_t i;
+ uint32_t v;
+
+ /*
+ * Use MAC address from device tree if possible
+ */
+ for (i = 0, v = 0; i < 6; i++) {
+ v += (uint32_t) f_mac_pu08[i];
+ }
+
+ if (v != 0) {
+ /*
+ * use passed mac address for transmission to nic
+ */
+ l_al_u32 = ((uint32_t) f_mac_pu08[3] << 24);
+ l_al_u32 |= ((uint32_t) f_mac_pu08[2] << 16);
+ l_al_u32 |= ((uint32_t) f_mac_pu08[1] << 8);
+ l_al_u32 |= ((uint32_t) f_mac_pu08[0] << 0);
+ l_ah_u32 = ((uint32_t) f_mac_pu08[5] << 8);
+ l_ah_u32 |= ((uint32_t) f_mac_pu08[4] << 0);
+ } else {
+ /*
+ * read mac address from eeprom
+ */
+ uint16_t w[3]; // 3 16 bit words from eeprom
+
+ for (i = 0; i < 3; i++) {
+ if (e1k_eep_rd16(EEPROM_MAC_OFFS + i, &w[i]) != 0) {
+ printf("Failed to read MAC address from EEPROM!\n");
+ return -1;
+ }
+ }
+
+ /*
+ * invert the least significant bit for 82546 dual port
+ * if the second device is in use (remember word is byteswapped)
+ */
+ if ((IS_82546) &&
+ ((e1k_rd32(STATUS) & BIT32(2)) != 0)) {
+ w[2] ^= (uint16_t) 0x100;
+ }
+
+ /*
+ * store mac address for transmission to nic
+ */
+ l_ah_u32 = ((uint32_t) w[2] << 0);
+ l_al_u32 = ((uint32_t) w[1] << 16);
+ l_al_u32 |= ((uint32_t) w[0] << 0);
+
+ /*
+ * return mac address
+ * mac address in eeprom is stored byteswapped
+ */
+ f_mac_pu08[1] = (uint8_t) ((w[0] >> 8) & 0xff);
+ f_mac_pu08[0] = (uint8_t) ((w[0] >> 0) & 0xff);
+ f_mac_pu08[3] = (uint8_t) ((w[1] >> 8) & 0xff);
+ f_mac_pu08[2] = (uint8_t) ((w[1] >> 0) & 0xff);
+ f_mac_pu08[5] = (uint8_t) ((w[2] >> 8) & 0xff);
+ f_mac_pu08[4] = (uint8_t) ((w[2] >> 0) & 0xff);
+ }
+
+ /*
+ * insert mac address in receive address register
+ * and set AV bit
+ */
+ e1k_wr32(RAL0, l_al_u32);
+ e1k_wr32(RAH0, l_ah_u32 | BIT32(31));
+
+ /*
+ * clear remaining receive address registers
+ */
+ for (i = 1; i < NUM_MAC_ADDR; i++) {
+ e1k_wr32(RAL0 + i * sizeof(uint64_t), 0);
+ e1k_wr32(RAH0 + i * sizeof(uint64_t), 0);
+ }
+
+ return 0;
+}
+
+
+/*
+ * interface
+ ******************************************************************************
+ */
+
+/*
+ * e1k_receive
+ */
+static int
+e1k_receive(char *f_buffer_pc, unsigned f_len_i)
+{
+ uint32_t l_rdh_u32 = e1k_rd32(RDH); // this includes needed dummy read
+ e1k_rx_desc_st *rx;
+ unsigned l_ret_i;
+
+ #ifdef E1K_DEBUG
+ #ifdef E1K_SHOW_RCV_DATA
+ int i;
+ #endif
+ #endif
+
+ /*
+ * check whether new packets have arrived
+ */
+ if (m_e1k.m_rx_next_u32 == l_rdh_u32) {
+ return 0;
+ }
+
+ /*
+ * get a pointer to the next rx descriptor for ease of use
+ */
+ rx = &m_e1k.m_rx_ring_pst[m_e1k.m_rx_next_u32];
+
+ /*
+ * check whether the descriptor done bit is set
+ */
+ if ((rx->m_sta_u08 & 0x1) == 0) {
+ return 0;
+ }
+
+ /*
+ * get the length of the packet, throw away checksum
+ */
+ l_ret_i = (int) bswap_16(rx->m_len_u16) - (int) 4;
+
+ /*
+ * copy the data
+ */
+ memcpy((uint8_t *) f_buffer_pc, dma2virt(bswap_64(rx->m_buffer_u64)),
+ (size_t) MIN(l_ret_i, f_len_i));
+
+ #ifdef E1K_DEBUG
+ #if defined(E1K_SHOW_RCV) || defined(E1K_SHOW_RCV_DATA)
+ printf("e1k: %d bytes received (max %d)\n", l_ret_i, f_len_i);
+ #endif
+
+ #ifdef E1K_SHOW_RCV_DATA
+ for (i = 0; i < l_ret_i; i++) {
+
+ if ((i & 0x1f) == 0) {
+ printf("\n ");
+ }
+
+ printf("%02X ", f_buffer_pc[i]);
+ }
+
+ printf("\n\n");
+ #endif
+ #endif
+
+ /*
+ * clear descriptor for reusage, but leave buffer pointer untouched
+ */
+ memset((uint8_t *) &rx->m_len_u16, 0,
+ sizeof(e1k_rx_desc_st) - sizeof(uint64_t));
+ mb();
+
+ /*
+ * write new tail pointer
+ */
+ e1k_wr32(RDT, m_e1k.m_rx_next_u32);
+
+ /*
+ * update next receive index
+ */
+ m_e1k.m_rx_next_u32 = (m_e1k.m_rx_next_u32 + 1) & (E1K_NUM_RX_DESC - 1);
+
+ return l_ret_i;
+}
+
+static int
+e1k_xmit(char *f_buffer_pc, unsigned f_len_i)
+{
+ uint32_t l_tdh_u32 = e1k_rd32(TDH);
+ uint32_t l_tdt_u32 = e1k_rd32(TDT);
+ uint32_t l_pre_u32 = (l_tdh_u32 + (E1K_NUM_TX_DESC - 1)) &
+ (E1K_NUM_TX_DESC - 1);
+ e1k_tx_desc_st *tx;
+ #if defined(E1K_DEBUG) && defined(E1K_SHOW_XMIT_DATA)
+ int i;
+ #endif
+
+ /*
+ * check for available buffers
+ */
+ if (l_pre_u32 == l_tdt_u32) {
+ return 0;
+ }
+
+ /*
+ * get a pointer to the next tx descriptor for ease of use
+ */
+ tx = &m_e1k.m_tx_ring_pst[l_tdt_u32];
+
+ /*
+ * copy the data
+ */
+ memcpy(dma2virt(bswap_64(tx->m_buffer_u64)), (uint8_t *) f_buffer_pc,
+ (size_t) f_len_i);
+
+ /*
+ * insert length & command flags
+ */
+ tx->m_len_u16 = bswap_16((uint16_t) f_len_i);
+ tx->m_cmd_u08 = (BIT08(0) | // EOP
+ BIT08(1)); // IFCS
+ tx->m_sta_u08 = 0;
+ mb();
+
+ /*
+ * update tail index
+ */
+ l_tdt_u32 = (l_tdt_u32 + 1) & (E1K_NUM_TX_DESC - 1);
+ e1k_wr32(TDT, l_tdt_u32);
+
+ #ifdef E1K_DEBUG
+ #if defined(E1K_SHOW_XMIT) || defined(E1K_SHOW_XMIT_DATA)
+ printf("e1k: %d bytes transmitted\n", bswap_16(tx->m_len_u16));
+ #endif
+
+ #ifdef E1K_SHOW_XMIT_DATA
+ for (i = 0; i < bswap_16(tx->m_len_u16); i++) {
+
+ if ((i & 0x1f) == 0) {
+ printf("\n ");
+ }
+
+ f_buffer_pc = dma2virt(bswap_64(tx->m_buffer_u64));
+ printf("%02X ", f_buffer_pc[i]);
+ }
+
+ printf("\n\n");
+ #endif
+ #endif
+
+ return f_len_i;
+}
+
+int
+check_driver(uint16_t vendor_id, uint16_t device_id)
+{
+ uint64_t i;
+
+ /*
+ * checks whether the driver is handling this device
+ * by verifying vendor & device id
+ * vendor id 0x8086 == Intel
+ */
+ if (vendor_id != 0x8086) {
+ #ifdef E1K_DEBUG
+ printf("e1k: netdevice with vendor id %04X not supported\n",
+ vendor_id);
+ #endif
+ return -1;
+ }
+
+ for (i = 0; e1k_dev[i].m_dev_u32 != 0; i++) {
+ if (e1k_dev[i].m_dev_u32 == (uint32_t) device_id) {
+ break;
+ }
+ }
+
+ if (e1k_dev[i].m_dev_u32 == 0) {
+ #ifdef E1K_DEBUG
+ printf("e1k: netdevice with device id %04X not supported\n",
+ device_id);
+ #endif
+ return -1;
+ }
+
+ /*
+ * initialize static variables
+ */
+ m_e1k.m_device_u64 = e1k_dev[i].m_devmsk_u64;
+ m_e1k.m_baseaddr_u64 = 0;
+
+ // success
+ #ifdef E1K_DEBUG
+ printf("e1k: found device %s\n", e1k_dev[i].m_name);
+ #endif
+
+ return 0;
+}
+
+static int
+e1k_init(net_driver_t *driver)
+{
+ uint32_t i;
+ uint32_t v;
+
+ if (!driver)
+ return -1;
+
+ #ifdef E1K_DEBUG
+ printf("\ne1k: initializing\n");
+ #endif
+
+ dma_offset = SLOF_dma_map_in(&m_e1k, sizeof(m_e1k), 0);
+ #ifdef E1K_DEBUG
+ printf("e1k: dma offset: %lx - %lx = %lx\n", dma_offset, (long)&m_e1k,
+ dma_offset - (long)&m_e1k);
+ #endif
+ dma_offset = dma_offset - (long)&m_e1k;
+
+ /*
+ * setup register & memory base addresses of NIC
+ */
+ //m_e1k.m_baseaddr_u64 = baseaddr;
+ #ifdef E1K_DEBUG
+ printf("e1k: base address register = 0x%llx\n", m_e1k.m_baseaddr_u64);
+ #endif
+
+ /*
+ * e1k hardware initialization
+ */
+
+ /*
+ * at first disable all interrupts
+ */
+ e1k_wr32(IMC, (uint32_t) ~0);
+
+ /*
+ * check for link up
+ */
+ #ifdef E1K_DEBUG
+ printf("e1k: checking link status..\n");
+ #endif
+
+ i = 50;
+ v = e1k_rd32(STATUS);
+ while ((--i) &&
+ ((v & BIT32(1)) == 0)) {
+ SLOF_msleep(100);
+ v = e1k_rd32(STATUS);
+ }
+
+ if ((v & BIT32(1)) == 0) {
+ #ifdef E1K_DEBUG
+ printf("e1k: link is down.\n");
+ printf(" terminating.\n");
+ #endif
+
+ return -1;
+ }
+
+ #ifdef E1K_DEBUG
+ printf("e1k: link is up\n");
+
+ switch ((v >> 6) & 0x3) {
+ case 0: {
+ printf(" 10 Mb/s\n");
+ } break;
+ case 1: {
+ printf(" 100 Mb/s\n");
+ } break;
+ case 2:
+ case 3: {
+ printf(" 1000 Mb/s\n");
+ } break;
+ }
+
+ if ((v & BIT32(0)) == 0) {
+ printf(" half-duplex\n");
+ } else {
+ printf(" full-duplex\n");
+ }
+ #endif
+
+ /*
+ * initialize mac address
+ */
+ #ifdef E1K_DEBUG
+ printf("e1k: initializing mac address.. ");
+ #endif
+ if (e1k_mac_init((uint8_t *)driver->mac_addr) != 0) {
+ #ifdef E1K_DEBUG
+ printf("failed.\n");
+ printf(" terminating.\n");
+ #endif
+
+ return -1;
+ }
+
+ #ifdef E1K_DEBUG
+ printf("done.\n");
+ printf(" mac address = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ driver->mac_addr[0], driver->mac_addr[1], driver->mac_addr[2],
+ driver->mac_addr[3], driver->mac_addr[4], driver->mac_addr[5]);
+ #endif
+
+ /*
+ * initialize transmitter
+ */
+ #ifdef E1K_DEBUG
+ printf("e1k: initializing transmitter.. ");
+ #endif
+ e1k_init_transmitter();
+ #ifdef E1K_DEBUG
+ printf("done.\n");
+ #endif
+
+ /*
+ * initialize receiver
+ */
+ #ifdef E1K_DEBUG
+ printf("e1k: initializing receiver.. ");
+ #endif
+ e1k_init_receiver();
+ #ifdef E1K_DEBUG
+ printf("done.\n");
+ printf("e1k: initialization complete\n");
+ #endif
+
+ driver->running = 1;
+
+ return 0;
+}
+
+static int
+e1k_reset(void)
+{
+ /*
+ * reset the PHY
+ */
+ e1k_setb32(CTRL, BIT32(31));
+ SLOF_msleep(10);
+
+ /*
+ * reset the MAC
+ */
+ e1k_setb32(CTRL, BIT32(26));
+ SLOF_msleep(10);
+
+ return 0;
+}
+
+static int
+e1k_term(void)
+{
+ #ifdef E1K_DEBUG
+ printf("e1k: shutdown.. ");
+ #endif
+
+ /*
+ * disable receiver & transmitter
+ */
+ e1k_wr32(RCTL, 0);
+ e1k_wr32(TCTL, 0);
+ SLOF_msleep(10);
+
+ /*
+ * reset the ring indices
+ */
+ e1k_wr32(RDH, 0);
+ e1k_wr32(RDT, 0);
+ e1k_wr32(TDH, 0);
+ e1k_wr32(TDT, 0);
+
+ /*
+ * disable receive address
+ */
+ e1k_clrb32(RAH0, BIT32(31));
+
+ /*
+ * reset the mac/phy
+ */
+ e1k_reset();
+
+ /*
+ * Disable DMA translation
+ */
+ SLOF_dma_map_out((long)virt2dma(&m_e1k), (void *)&m_e1k, (long)sizeof(m_e1k));
+
+ #ifdef E1K_DEBUG
+ printf("done.\n");
+ #endif
+
+ return 0;
+}
+
+net_driver_t *e1k_open(uint64_t baseaddr)
+{
+ net_driver_t *driver;
+
+ m_e1k.m_baseaddr_u64 = baseaddr;
+ driver = SLOF_alloc_mem(sizeof(*driver));
+ if (!driver) {
+ printf("Unable to allocate virtio-net driver\n");
+ return NULL;
+ }
+ memset(driver, 0, sizeof(*driver));
+
+ if (e1k_init(driver))
+ goto FAIL;
+
+ return driver;
+
+FAIL: SLOF_free_mem(driver, sizeof(*driver));
+ return NULL;
+
+ return 0;
+}
+
+void e1k_close(net_driver_t *driver)
+{
+ if (driver->running == 0)
+ return;
+
+ e1k_term();
+ driver->running = 0;
+ SLOF_free_mem(driver, sizeof(*driver));
+}
+
+int e1k_read(char *buf, int len)
+{
+ if (buf)
+ return e1k_receive(buf, len);
+ return -1;
+}
+
+int e1k_write(char *buf, int len)
+{
+ if (buf)
+ return e1k_xmit(buf, len);
+ return -1;
+}
+
+int e1k_mac_setup(uint16_t vendor_id, uint16_t device_id,
+ uint64_t baseaddr, char *mac_addr)
+{
+ if (check_driver(vendor_id, device_id))
+ return -1;
+
+ m_e1k.m_baseaddr_u64 = baseaddr;
+ memset(mac_addr, 0, 6);
+
+ return e1k_mac_init((uint8_t *)mac_addr);
+}
diff --git a/roms/SLOF/lib/libe1k/e1k.code b/roms/SLOF/lib/libe1k/e1k.code
new file mode 100644
index 000000000..225ed4e83
--- /dev/null
+++ b/roms/SLOF/lib/libe1k/e1k.code
@@ -0,0 +1,75 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * libe1k Forth wrapper
+ */
+
+#include <e1k.h>
+
+// : e1k-open ( baseaddr -- false | [ driver true ] )
+PRIM(E1K_X2d_OPEN)
+{
+ uint64_t baseaddr = TOS.u; POP;
+ net_driver_t *net_driver = e1k_open(baseaddr);
+ if (net_driver) {
+ PUSH;
+ TOS.u = (unsigned long)net_driver; PUSH;
+ TOS.n = -1;
+ } else {
+ PUSH;
+ TOS.n = 0;
+ }
+}
+MIRP
+
+// : e1k-close ( driver -- )
+PRIM(E1K_X2d_CLOSE)
+{
+ net_driver_t *driver = TOS.a; POP;
+ e1k_close(driver);
+}
+MIRP
+
+
+// : e1k-read ( addr len -- actual )
+PRIM(E1K_X2d_READ)
+{
+ int len = TOS.u; POP;
+ TOS.n = e1k_read(TOS.a, len);
+}
+MIRP
+
+// : e1k-write ( addr len -- actual )
+PRIM(E1K_X2d_WRITE)
+{
+ int len = TOS.u; POP;
+ TOS.n = e1k_write(TOS.a, len);
+}
+MIRP
+
+// : e1k-mac-setup ( vendor-id device-id baseaddr addr -- false | [ mac-addr len true ] )
+PRIM(E1K_X2d_MAC_X2d_SETUP)
+{
+ char *mac_addr = TOS.a; POP;
+ uint64_t baseaddr = TOS.u; POP;
+ unsigned int device_id = TOS.u; POP;
+
+ int ret = e1k_mac_setup(TOS.u, device_id, baseaddr, mac_addr);
+ if (!ret) {
+ TOS.a = mac_addr; PUSH;
+ TOS.n = 6; PUSH;
+ TOS.n = -1;
+ } else
+ TOS.n = 0;
+}
+MIRP
diff --git a/roms/SLOF/lib/libe1k/e1k.h b/roms/SLOF/lib/libe1k/e1k.h
new file mode 100644
index 000000000..c88b3e561
--- /dev/null
+++ b/roms/SLOF/lib/libe1k/e1k.h
@@ -0,0 +1,108 @@
+/******************************************************************************
+ * Copyright (c) 2007, 2011, 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * Definitions for the e1000 Gigabit Ethernet Driver for SLOF
+ */
+
+#include <stdint.h>
+#include <cache.h>
+
+// compiler switches
+
+// Debug switches
+//#define E1K_DEBUG // main debug switch, w/o it the other ones don't work
+//#define E1K_SHOW_RCV
+//#define E1K_SHOW_RCV_DATA
+//#define E1K_SHOW_XMIT
+//#define E1K_SHOW_XMIT_DATA
+
+/*
+ * pci register offsets
+ */
+// PCI command register
+#define PCI_COM_R ((uint16_t) 0x0004)
+// PCI Cache Line Size register
+#define PCI_CACHELS_R ((uint16_t) 0x000c)
+// PCI bar1 register
+#define PCI_BAR1_R ((uint16_t) 0x0010)
+// PCI bar2 register
+#define PCI_BAR2_R ((uint16_t) 0x0014)
+// PCI bar1 register
+#define PCI_SUBID_R ((uint16_t) 0x002e)
+
+/*
+ * e1000 register offsets
+ */
+// Device Control register
+#define CTRL ((uint16_t) 0x0000)
+// Device Status register
+#define STATUS ((uint16_t) 0x0008)
+// Eeprom Read register
+#define EERD ((uint16_t) 0x0014)
+// Interrupt Mask Clear register
+#define IMC ((uint16_t) 0x00d8)
+// Receive Control register
+#define RCTL ((uint16_t) 0x0100)
+// Receive Descriptor Base Address Low register
+#define RDBAL ((uint16_t) 0x2800)
+// Receive Descriptor Base Address High register
+#define RDBAH ((uint16_t) 0x2804)
+// Receive Descriptor Length register
+#define RDLEN ((uint16_t) 0x2808)
+// Receive Descriptor Head register
+#define RDH ((uint16_t) 0x2810)
+// Receive Descriptor Tail register
+#define RDT ((uint16_t) 0x2818)
+// Receive Delay Timer register
+#define RDTR ((uint16_t) 0x2820)
+// Transmit Control register
+#define TCTL ((uint16_t) 0x0400)
+// Transmit Descriptor Base Address Low register
+#define TDBAL ((uint16_t) 0x3800)
+// Transmit Descriptor Base Address High register
+#define TDBAH ((uint16_t) 0x3804)
+// Transmit Descriptor Length register
+#define TDLEN ((uint16_t) 0x3808)
+// Transmit Descriptor Head register
+#define TDH ((uint16_t) 0x3810)
+// Transmit Descriptor Tail register
+#define TDT ((uint16_t) 0x3818)
+// Receive Address Low register
+#define RAL0 ((uint16_t) 0x5400)
+// Receive Address High register
+#define RAH0 ((uint16_t) 0x5404)
+
+
+/*
+ * useful def's
+ */
+#define rd08(a) ci_read_8((uint32_t *)(a))
+#define rd16(a) ci_read_16((uint32_t *)(a))
+#define rd32(a) ci_read_32((uint32_t *)(a))
+#define wr08(a,v) ci_write_8((uint32_t *)(a), (v))
+#define wr16(a,v) ci_write_16((uint32_t *)(a), (v))
+#define wr32(a,v) ci_write_32((uint32_t *)(a), (v))
+//#define printk snk_kernel_interface->print
+//#define ms_delay snk_kernel_interface->ms_delay
+
+#define BIT08(bit) ((uint8_t) 0x1 << (bit))
+#define BIT16(bit) ((uint16_t) 0x1 << (bit))
+#define BIT32(bit) ((uint32_t) 0x1 << (bit))
+
+//#define mb() asm volatile("sync" ::: "memory");
+
+extern net_driver_t *e1k_open(uint64_t baseaddr);
+extern void e1k_close(net_driver_t *driver);
+extern int e1k_read(char *buf, int len);
+extern int e1k_write(char *buf, int len);
+extern int e1k_mac_setup(uint16_t vendor_id, uint16_t device_id,
+ uint64_t baseaddr, char *mac_addr);
diff --git a/roms/SLOF/lib/libe1k/e1k.in b/roms/SLOF/lib/libe1k/e1k.in
new file mode 100644
index 000000000..a9cb13f83
--- /dev/null
+++ b/roms/SLOF/lib/libe1k/e1k.in
@@ -0,0 +1,21 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * libe1k bindings for Forth - definitions
+ */
+
+cod(E1K-OPEN)
+cod(E1K-CLOSE)
+cod(E1K-READ)
+cod(E1K-WRITE)
+cod(E1K-MAC-SETUP)
diff --git a/roms/SLOF/lib/libelf/Makefile b/roms/SLOF/lib/libelf/Makefile
new file mode 100644
index 000000000..34a8f20b6
--- /dev/null
+++ b/roms/SLOF/lib/libelf/Makefile
@@ -0,0 +1,47 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2011 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
+LDFLAGS= -nostdlib
+
+TARGET = ../libelf.a
+
+all: $(TARGET)
+
+SRCS = elf.c elf32.c elf64.c elf_claim.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libelf/elf.c b/roms/SLOF/lib/libelf/elf.c
new file mode 100644
index 000000000..f6a052ef3
--- /dev/null
+++ b/roms/SLOF/lib/libelf/elf.c
@@ -0,0 +1,224 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * ELF loader
+ */
+
+#include <string.h>
+#include <cache.h>
+#include <libelf.h>
+#include <byteorder.h>
+
+/**
+ * elf_check_file tests if the file at file_addr is
+ * a correct endian, ELF PPC executable
+ * @param file_addr pointer to the start of the ELF file
+ * @return the class (1 for 32 bit, 2 for 64 bit)
+ * -1 if it is not an ELF file
+ * -2 if it has the wrong endianness
+ * -3 if it is not an ELF executable
+ * -4 if it is not for PPC
+ */
+static int
+elf_check_file(unsigned long *file_addr)
+{
+ struct ehdr *ehdr = (struct ehdr *) file_addr;
+ uint8_t native_endian;
+
+ /* check if it is an ELF image at all */
+ if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46)
+ return -1;
+
+#ifdef __BIG_ENDIAN__
+ native_endian = ELFDATA2MSB;
+#else
+ native_endian = ELFDATA2LSB;
+#endif
+
+ if (native_endian != ehdr->ei_data) {
+ switch (ehdr->ei_class) {
+ case 1:
+ elf_byteswap_header32(file_addr);
+ break;
+ case 2:
+ elf_byteswap_header64(file_addr);
+ break;
+ }
+ }
+
+ /* check if it is an ELF executable ... and also
+ * allow DYN files, since this is specified by ePAPR */
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ return -3;
+
+ /* check if it is a PPC ELF executable */
+ if (ehdr->e_machine != 0x14 && ehdr->e_machine != 0x15)
+ return -4;
+
+ return ehdr->ei_class;
+}
+
+/**
+ * load_elf_file tries to load the ELF file specified in file_addr
+ *
+ * it first checks if the file is a PPC ELF executable and then loads
+ * the segments depending if it is a 64bit or 32 bit ELF file
+ *
+ * @param file_addr pointer to the start of the elf file
+ * @param entry pointer where the ELF loader will store
+ * the entry point
+ * @param pre_load handler that is called before copying a segment
+ * @param post_load handler that is called after copying a segment
+ * @return 1 for a 32 bit file
+ * 2 for a 64 bit BE file
+ * 3 for a 64 bit LE ABIv1 file
+ * 4 for a 64 bit LE ABIv2 file
+ * 5 for a 32 bit LE ABIv1 file
+ * anything else means an error during load
+ */
+int
+elf_load_file(void *file_addr, unsigned long *entry,
+ int (*pre_load)(void*, long),
+ void (*post_load)(void*, long))
+{
+ int type = elf_check_file(file_addr);
+ struct ehdr *ehdr = (struct ehdr *) file_addr;
+
+ switch (type) {
+ case 1:
+ *entry = elf_load_segments32(file_addr, 0, pre_load, post_load);
+ if (ehdr->ei_data != ELFDATA2MSB) {
+ type = 5; /* LE32 ABIv1 */
+ }
+ break;
+ case 2:
+ *entry = elf_load_segments64(file_addr, 0, pre_load, post_load);
+ if (ehdr->ei_data != ELFDATA2MSB) {
+ uint32_t flags = elf_get_eflags_64(file_addr);
+ if ((flags & 0x3) == 2)
+ type = 4; /* LE64 ABIv2 */
+ else
+ type = 3; /* LE64 ABIv1 */
+ }
+ break;
+ }
+ if (*entry == 0)
+ type = 0;
+
+ return type;
+}
+
+
+/**
+ * load_elf_file_to_addr loads an ELF file to given address.
+ * This is useful for 64-bit vmlinux images that use the virtual entry
+ * point address in their headers, and thereby need a special treatment.
+ *
+ * @param file_addr pointer to the start of the elf file
+ * @param entry pointer where the ELF loader will store
+ * the entry point
+ * @param pre_load handler that is called before copying a segment
+ * @param post_load handler that is called after copying a segment
+ * @return 1 for a 32 bit file
+ * 2 for a 64 bit file
+ * anything else means an error during load
+ */
+int
+elf_load_file_to_addr(void *file_addr, void *addr, unsigned long *entry,
+ int (*pre_load)(void*, long),
+ void (*post_load)(void*, long))
+{
+ int type;
+ long offset;
+ struct ehdr *ehdr = (struct ehdr *) file_addr;
+
+ type = elf_check_file(file_addr);
+
+ switch (type) {
+ case 1:
+ /* Parse 32-bit image */
+ offset = (long)addr - elf_get_base_addr32(file_addr);
+ *entry = elf_load_segments32(file_addr, offset, pre_load,
+ post_load) + offset;
+ // TODO: elf_relocate32(...)
+ break;
+ case 2:
+ /* Parse 64-bit image */
+ offset = (long)addr - elf_get_base_addr64(file_addr);
+ *entry = elf_load_segments64(file_addr, offset, pre_load,
+ post_load) + offset;
+ elf_relocate64(file_addr, offset);
+ if (ehdr->ei_data != ELFDATA2MSB) {
+ uint32_t flags = elf_get_eflags_64(file_addr);
+ if ((flags & 0x3) == 2)
+ type = 4; /* LE64 ABIv2 */
+ else
+ type = 3; /* LE64 ABIv1 */
+ }
+ break;
+ }
+
+ return type;
+}
+
+
+/**
+ * Get the base load address of the ELF image
+ * @return The base address or -1 for error
+ */
+long
+elf_get_base_addr(void *file_addr)
+{
+ int type;
+
+ type = elf_check_file(file_addr);
+
+ switch (type) {
+ case 1:
+ /* Return 32-bit image base address */
+ return elf_get_base_addr32(file_addr);
+ break;
+ case 2:
+ /* Return 64-bit image base address */
+ return elf_get_base_addr64(file_addr);
+ break;
+ }
+
+ return -1;
+}
+
+/**
+ * Get the file size of the ELF image that has been loaded into a
+ * buffer larger than the size of the file
+ * @return The size of the ELF image or < 0 for error
+ */
+long elf_get_file_size(const void *buffer, const unsigned long buffer_size)
+{
+ const struct ehdr *ehdr = (const struct ehdr *)buffer;
+
+ if (buffer_size < sizeof(struct ehdr))
+ return -1;
+
+ /* check if it is an ELF image at all */
+ if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46)
+ return -1;
+
+ switch (ehdr->ei_class) {
+ case 1:
+ return elf_get_file_size32(buffer, buffer_size);
+ case 2:
+ return elf_get_file_size64(buffer, buffer_size);
+ }
+
+ return -1;
+}
diff --git a/roms/SLOF/lib/libelf/elf32.c b/roms/SLOF/lib/libelf/elf32.c
new file mode 100644
index 000000000..45b0015b8
--- /dev/null
+++ b/roms/SLOF/lib/libelf/elf32.c
@@ -0,0 +1,262 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * 32-bit ELF loader
+ */
+#include <stdio.h>
+#include <string.h>
+#include <libelf.h>
+#include <byteorder.h>
+#include <helpers.h>
+
+struct ehdr32 {
+ uint32_t ei_ident;
+ uint8_t ei_class;
+ uint8_t ei_data;
+ uint8_t ei_version;
+ uint8_t ei_pad[9];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ uint32_t e_entry;
+ uint32_t e_phoff;
+ uint32_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+};
+
+struct phdr32 {
+ uint32_t p_type;
+ uint32_t p_offset;
+ uint32_t p_vaddr;
+ uint32_t p_paddr;
+ uint32_t p_filesz;
+ uint32_t p_memsz;
+ uint32_t p_flags;
+ uint32_t p_align;
+};
+
+struct shdr32 {
+ uint32_t sh_name;
+ uint32_t sh_type;
+ uint32_t sh_flags;
+ uint32_t sh_addr;
+ uint32_t sh_offset;
+ uint32_t sh_size;
+ uint32_t sh_link;
+ uint32_t sh_info;
+ uint32_t sh_addralign;
+ uint32_t sh_entsize;
+};
+
+static struct phdr32*
+get_phdr32(void *file_addr)
+{
+ return (struct phdr32 *) (((unsigned char *)file_addr)
+ + ((struct ehdr32 *)file_addr)->e_phoff);
+}
+
+static void
+load_segment(void *file_addr, struct phdr32 *phdr, signed long offset,
+ int (*pre_load)(void*, long),
+ void (*post_load)(void*, long))
+{
+ unsigned long src = phdr->p_offset + (unsigned long) file_addr;
+ unsigned long destaddr;
+
+ destaddr = (unsigned long)phdr->p_paddr;
+ destaddr = destaddr + offset;
+
+ /* check if we're allowed to copy */
+ if (pre_load != NULL) {
+ if (pre_load((void*)destaddr, phdr->p_memsz) != 0)
+ return;
+ }
+
+ /* copy into storage */
+ memmove((void *)destaddr, (void *)src, phdr->p_filesz);
+
+ /* clear bss */
+ memset((void *)(destaddr + phdr->p_filesz), 0,
+ phdr->p_memsz - phdr->p_filesz);
+
+ if (phdr->p_memsz && post_load) {
+ post_load((void*)destaddr, phdr->p_memsz);
+ }
+}
+
+unsigned int
+elf_load_segments32(void *file_addr, signed long offset,
+ int (*pre_load)(void*, long),
+ void (*post_load)(void*, long))
+{
+ struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+ /* Calculate program header address */
+ struct phdr32 *phdr = get_phdr32(file_addr);
+ int i;
+
+ /* loop e_phnum times */
+ for (i = 0; i <= ehdr->e_phnum; i++) {
+ /* PT_LOAD ? */
+ if (phdr->p_type == 1) {
+ if (phdr->p_paddr != phdr->p_vaddr) {
+ printf("ELF32: VirtAddr(%lx) != PhysAddr(%lx) not supported, aborting\n",
+ (long)phdr->p_vaddr, (long)phdr->p_paddr);
+ return 0;
+ }
+
+ /* copy segment */
+ load_segment(file_addr, phdr, offset, pre_load,
+ post_load);
+ }
+ /* step to next header */
+ phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+ }
+
+ /* Entry point is always a virtual address, so translate it
+ * to physical before returning it */
+ return ehdr->e_entry;
+}
+
+/**
+ * Return the base address for loading (i.e. the address of the first PT_LOAD
+ * segment)
+ * @param file_addr pointer to the ELF file in memory
+ * @return the base address
+ */
+long
+elf_get_base_addr32(void *file_addr)
+{
+ struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+ struct phdr32 *phdr = get_phdr32(file_addr);
+ int i;
+
+ /* loop e_phnum times */
+ for (i = 0; i <= ehdr->e_phnum; i++) {
+ /* PT_LOAD ? */
+ if (phdr->p_type == 1) {
+ return phdr->p_paddr;
+ }
+ /* step to next header */
+ phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+ }
+
+ return 0;
+}
+
+uint32_t elf_get_eflags_32(void *file_addr)
+{
+ struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+
+ return ehdr->e_flags;
+}
+
+void
+elf_byteswap_header32(void *file_addr)
+{
+ struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+ struct phdr32 *phdr;
+ int i;
+
+ bswap_16p(&ehdr->e_type);
+ bswap_16p(&ehdr->e_machine);
+ bswap_32p(&ehdr->e_version);
+ bswap_32p(&ehdr->e_entry);
+ bswap_32p(&ehdr->e_phoff);
+ bswap_32p(&ehdr->e_shoff);
+ bswap_32p(&ehdr->e_flags);
+ bswap_16p(&ehdr->e_ehsize);
+ bswap_16p(&ehdr->e_phentsize);
+ bswap_16p(&ehdr->e_phnum);
+ bswap_16p(&ehdr->e_shentsize);
+ bswap_16p(&ehdr->e_shnum);
+ bswap_16p(&ehdr->e_shstrndx);
+
+ phdr = get_phdr32(file_addr);
+
+ /* loop e_phnum times */
+ for (i = 0; i <= ehdr->e_phnum; i++) {
+ bswap_32p(&phdr->p_type);
+ bswap_32p(&phdr->p_offset);
+ bswap_32p(&phdr->p_vaddr);
+ bswap_32p(&phdr->p_paddr);
+ bswap_32p(&phdr->p_filesz);
+ bswap_32p(&phdr->p_memsz);
+ bswap_32p(&phdr->p_flags);
+ bswap_32p(&phdr->p_align);
+
+ /* step to next header */
+ phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+ }
+}
+
+/*
+ * Determine the size of an ELF image that has been loaded into
+ * a buffer larger than its size. We search all program headers
+ * and sections for the one that shows the farthest extent of the
+ * file.
+ * @return Return -1 on error, size of file otherwise.
+ */
+long elf_get_file_size32(const void *buffer, const unsigned long buffer_size)
+{
+ const struct ehdr32 *ehdr = (const struct ehdr32 *) buffer;
+ const uint8_t *buffer_end = buffer + buffer_size;
+ const struct phdr32 *phdr;
+ const struct shdr32 *shdr;
+ unsigned long elf_size = 0;
+ uint16_t entsize;
+ unsigned i;
+
+ if (buffer_size < sizeof(struct ehdr) ||
+ ehdr->e_ehsize != sizeof(struct ehdr32))
+ return -1;
+
+ phdr = buffer + elf32_to_cpu(ehdr->e_phoff, ehdr);
+ entsize = elf16_to_cpu(ehdr->e_phentsize, ehdr);
+ for (i = 0; i < elf16_to_cpu(ehdr->e_phnum, ehdr); i++) {
+ if (((uint8_t *)phdr) + entsize > buffer_end)
+ return -1;
+
+ elf_size = MAX(elf32_to_cpu(phdr->p_offset, ehdr) +
+ elf32_to_cpu(phdr->p_filesz, ehdr),
+ elf_size);
+
+ /* step to next header */
+ phdr = (struct phdr32 *)(((uint8_t *)phdr) + entsize);
+ }
+
+ shdr = buffer + elf32_to_cpu(ehdr->e_shoff, ehdr);
+ entsize = elf16_to_cpu(ehdr->e_shentsize, ehdr);
+ for (i = 0; i < elf16_to_cpu(ehdr->e_shnum, ehdr); i++) {
+ if (((uint8_t *)shdr) + entsize > buffer_end)
+ return -1;
+
+ elf_size = MAX(elf32_to_cpu(shdr->sh_offset, ehdr) +
+ elf32_to_cpu(shdr->sh_size, ehdr),
+ elf_size);
+
+ /* step to next header */
+ shdr = (struct shdr32 *)(((uint8_t *)shdr) + entsize);
+ }
+
+ elf_size = ROUNDUP(elf_size, 4);
+ if (elf_size > buffer_size)
+ return -1;
+
+ return (long) elf_size;
+}
diff --git a/roms/SLOF/lib/libelf/elf64.c b/roms/SLOF/lib/libelf/elf64.c
new file mode 100644
index 000000000..3bc40402f
--- /dev/null
+++ b/roms/SLOF/lib/libelf/elf64.c
@@ -0,0 +1,531 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * 64-bit ELF loader for PowerPC.
+ * See the "64-bit PowerPC ELF Application Binary Interface Supplement" and
+ * the "ELF-64 Object File Format" documentation for details.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <libelf.h>
+#include <byteorder.h>
+#include <helpers.h>
+
+struct ehdr64
+{
+ uint32_t ei_ident;
+ uint8_t ei_class;
+ uint8_t ei_data;
+ uint8_t ei_version;
+ uint8_t ei_pad[9];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ uint64_t e_entry;
+ uint64_t e_phoff;
+ uint64_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+};
+
+struct phdr64
+{
+ uint32_t p_type;
+ uint32_t p_flags;
+ uint64_t p_offset;
+ uint64_t p_vaddr;
+ uint64_t p_paddr;
+ uint64_t p_filesz;
+ uint64_t p_memsz;
+ uint64_t p_align;
+};
+
+struct shdr64
+{
+ uint32_t sh_name; /* Section name */
+ uint32_t sh_type; /* Section type */
+ uint64_t sh_flags; /* Section attributes */
+ uint64_t sh_addr; /* Virtual address in memory */
+ uint64_t sh_offset; /* Offset in file */
+ uint64_t sh_size; /* Size of section */
+ uint32_t sh_link; /* Link to other section */
+ uint32_t sh_info; /* Miscellaneous information */
+ uint64_t sh_addralign; /* Address alignment boundary */
+ uint64_t sh_entsize; /* Size of entries, if section has table */
+};
+
+struct rela /* RelA relocation table entry */
+{
+ uint64_t r_offset; /* Address of reference */
+ uint64_t r_info; /* Symbol index and type of relocation */
+ int64_t r_addend; /* Constant part of expression */
+};
+
+struct sym64
+{
+ uint32_t st_name; /* Symbol name */
+ uint8_t st_info; /* Type and Binding attributes */
+ uint8_t st_other; /* Reserved */
+ uint16_t st_shndx; /* Section table index */
+ uint64_t st_value; /* Symbol value */
+ uint64_t st_size; /* Size of object (e.g., common) */
+};
+
+
+/* For relocations */
+#define ELF_R_SYM(i) ((i)>>32)
+#define ELF_R_TYPE(i) ((uint32_t)(i) & 0xFFFFFFFF)
+#define ELF_R_INFO(s,t) ((((uint64_t) (s)) << 32) + (t))
+
+/*
+ * Relocation types for PowerPC64.
+ */
+#define R_PPC64_NONE 0
+#define R_PPC64_ADDR32 1
+#define R_PPC64_ADDR24 2
+#define R_PPC64_ADDR16 3
+#define R_PPC64_ADDR16_LO 4
+#define R_PPC64_ADDR16_HI 5
+#define R_PPC64_ADDR16_HA 6
+#define R_PPC64_ADDR14 7
+#define R_PPC64_ADDR14_BRTAKEN 8
+#define R_PPC64_ADDR14_BRNTAKEN 9
+#define R_PPC64_REL24 10
+#define R_PPC64_REL14 11
+#define R_PPC64_REL14_BRTAKEN 12
+#define R_PPC64_REL14_BRNTAKEN 13
+#define R_PPC64_GOT16 14
+#define R_PPC64_GOT16_LO 15
+#define R_PPC64_GOT16_HI 16
+#define R_PPC64_GOT16_HA 17
+#define R_PPC64_COPY 19
+#define R_PPC64_GLOB_DAT 20
+#define R_PPC64_JMP_SLOT 21
+#define R_PPC64_RELATIVE 22
+#define R_PPC64_UADDR32 24
+#define R_PPC64_UADDR16 25
+#define R_PPC64_REL32 26
+#define R_PPC64_PLT32 27
+#define R_PPC64_PLTREL32 28
+#define R_PPC64_PLT16_LO 29
+#define R_PPC64_PLT16_HI 30
+#define R_PPC64_PLT16_HA 31
+#define R_PPC64_SECTOFF 33
+#define R_PPC64_SECTOFF_LO 34
+#define R_PPC64_SECTOFF_HI 35
+#define R_PPC64_SECTOFF_HA 36
+#define R_PPC64_ADDR30 37
+#define R_PPC64_ADDR64 38
+#define R_PPC64_ADDR16_HIGHER 39
+#define R_PPC64_ADDR16_HIGHERA 40
+#define R_PPC64_ADDR16_HIGHEST 41
+#define R_PPC64_ADDR16_HIGHESTA 42
+#define R_PPC64_UADDR64 43
+#define R_PPC64_REL64 44
+#define R_PPC64_PLT64 45
+#define R_PPC64_PLTREL64 46
+#define R_PPC64_TOC16 47
+#define R_PPC64_TOC16_LO 48
+#define R_PPC64_TOC16_HI 49
+#define R_PPC64_TOC16_HA 50
+#define R_PPC64_TOC 51
+#define R_PPC64_PLTGOT16 52
+#define R_PPC64_PLTGOT16_LO 53
+#define R_PPC64_PLTGOT16_HI 54
+#define R_PPC64_PLTGOT16_HA 55
+#define R_PPC64_ADDR16_DS 56
+#define R_PPC64_ADDR16_LO_DS 57
+#define R_PPC64_GOT16_DS 58
+#define R_PPC64_GOT16_LO_DS 59
+#define R_PPC64_PLT16_LO_DS 60
+#define R_PPC64_SECTOFF_DS 61
+#define R_PPC64_SECTOFF_LO_DS 62
+#define R_PPC64_TOC16_DS 63
+#define R_PPC64_TOC16_LO_DS 64
+#define R_PPC64_PLTGOT16_DS 65
+#define R_PPC64_PLTGOT16_LO_DS 66
+#define R_PPC64_TLS 67
+#define R_PPC64_DTPMOD64 68
+#define R_PPC64_TPREL16 69
+#define R_PPC64_TPREL16_LO 60
+#define R_PPC64_TPREL16_HI 71
+#define R_PPC64_TPREL16_HA 72
+#define R_PPC64_TPREL64 73
+#define R_PPC64_DTPREL16 74
+#define R_PPC64_DTPREL16_LO 75
+#define R_PPC64_DTPREL16_HI 76
+#define R_PPC64_DTPREL16_HA 77
+#define R_PPC64_DTPREL64 78
+#define R_PPC64_GOT_TLSGD16 79
+#define R_PPC64_GOT_TLSGD16_LO 80
+#define R_PPC64_GOT_TLSGD16_HI 81
+#define R_PPC64_GOT_TLSGD16_HA 82
+#define R_PPC64_GOT_TLSLD16 83
+#define R_PPC64_GOT_TLSLD16_LO 84
+#define R_PPC64_GOT_TLSLD16_HI 85
+#define R_PPC64_GOT_TLSLD16_HA 86
+#define R_PPC64_GOT_TPREL16_DS 87
+#define R_PPC64_GOT_TPREL16_LO_ DS 88
+#define R_PPC64_GOT_TPREL16_HI 89
+#define R_PPC64_GOT_TPREL16_HA 90
+#define R_PPC64_GOT_DTPREL16_DS 91
+#define R_PPC64_GOT_DTPREL16_LO_DS 92
+#define R_PPC64_GOT_DTPREL16_HI 93
+#define R_PPC64_GOT_DTPREL16_HA 94
+#define R_PPC64_TPREL16_DS 95
+#define R_PPC64_TPREL16_LO_DS 96
+#define R_PPC64_TPREL16_HIGHER 97
+#define R_PPC64_TPREL16_HIGHERA 98
+#define R_PPC64_TPREL16_HIGHEST 99
+#define R_PPC64_TPREL16_HIGHESTA 100
+#define R_PPC64_DTPREL16_DS 101
+#define R_PPC64_DTPREL16_LO_DS 102
+#define R_PPC64_DTPREL16_HIGHER 103
+#define R_PPC64_DTPREL16_HIGHERA 104
+#define R_PPC64_DTPREL16_HIGHEST 105
+#define R_PPC64_DTPREL16_HIGHESTA 106
+
+
+static struct phdr64*
+get_phdr64(unsigned long *file_addr)
+{
+ return (struct phdr64 *) (((unsigned char *) file_addr)
+ + ((struct ehdr64 *)file_addr)->e_phoff);
+}
+
+static void
+load_segment64(unsigned long *file_addr, struct phdr64 *phdr, signed long offset,
+ int (*pre_load)(void*, long),
+ void (*post_load)(void*, long))
+{
+ unsigned long src = phdr->p_offset + (unsigned long) file_addr;
+ unsigned long destaddr;
+
+ destaddr = phdr->p_paddr + offset;
+
+ /* check if we're allowed to copy */
+ if (pre_load != NULL) {
+ if (pre_load((void*)destaddr, phdr->p_memsz) != 0)
+ return;
+ }
+
+ /* copy into storage */
+ memmove((void*)destaddr, (void*)src, phdr->p_filesz);
+
+ /* clear bss */
+ memset((void*)(destaddr + phdr->p_filesz), 0,
+ phdr->p_memsz - phdr->p_filesz);
+
+ if (phdr->p_memsz && post_load != NULL) {
+ post_load((void*)destaddr, phdr->p_memsz);
+ }
+}
+
+unsigned long
+elf_load_segments64(void *file_addr, signed long offset,
+ int (*pre_load)(void*, long),
+ void (*post_load)(void*, long))
+{
+ struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
+ /* Calculate program header address */
+ struct phdr64 *phdr = get_phdr64(file_addr);
+ int i;
+
+ /* loop e_phnum times */
+ for (i = 0; i <= ehdr->e_phnum; i++) {
+ /* PT_LOAD ? */
+ if (phdr->p_type == PT_LOAD) {
+ if (phdr->p_paddr != phdr->p_vaddr) {
+ printf("ELF64: VirtAddr(%lx) != PhysAddr(%lx) not supported, aborting\n",
+ (long)phdr->p_vaddr, (long)phdr->p_paddr);
+ return 0;
+ }
+
+ /* copy segment */
+ load_segment64(file_addr, phdr, offset, pre_load, post_load);
+ }
+ /* step to next header */
+ phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+ }
+
+ /* Entry point is always a virtual address, so translate it
+ * to physical before returning it */
+ return ehdr->e_entry;
+}
+
+/**
+ * Return the base address for loading (i.e. the address of the first PT_LOAD
+ * segment)
+ * @param file_addr pointer to the ELF file in memory
+ * @return the base address
+ */
+long
+elf_get_base_addr64(void *file_addr)
+{
+ struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
+ /* Calculate program header address */
+ struct phdr64 *phdr = get_phdr64(file_addr);
+ int i;
+
+ /* loop e_phnum times */
+ for (i = 0; i <= ehdr->e_phnum; i++) {
+ /* PT_LOAD ? */
+ if (phdr->p_type == PT_LOAD) {
+ /* Return base address */
+ return phdr->p_paddr;
+ }
+ /* step to next header */
+ phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Apply one relocation entry.
+ */
+static void
+elf_apply_rela64(void *file_addr, signed long offset, struct rela *relaentry,
+ struct sym64 *symtabentry)
+{
+ void *addr;
+ unsigned long s_a;
+ unsigned long base_addr;
+
+ base_addr = elf_get_base_addr64(file_addr);
+
+ /* Sanity check */
+ if (relaentry->r_offset < base_addr) {
+ printf("\nELF relocation out of bounds!\n");
+ return;
+ }
+
+ base_addr += offset;
+
+ /* Actual address where the relocation will be applied at. */
+ addr = (void*)(relaentry->r_offset + offset);
+
+ /* Symbol value (S) + Addend (A) */
+ s_a = symtabentry->st_value + offset + relaentry->r_addend;
+
+ switch (ELF_R_TYPE(relaentry->r_info)) {
+ case R_PPC64_ADDR32: /* S + A */
+ *(uint32_t *)addr = (uint32_t) s_a;
+ break;
+ case R_PPC64_ADDR64: /* S + A */
+ *(uint64_t *)addr = (uint64_t) s_a;
+ break;
+ case R_PPC64_TOC: /* .TOC */
+ *(uint64_t *)addr += offset;
+ break;
+ case R_PPC64_ADDR16_HIGHEST: /* #highest(S + A) */
+ *(uint16_t *)addr = ((s_a >> 48) & 0xffff);
+ break;
+ case R_PPC64_ADDR16_HIGHER: /* #higher(S + A) */
+ *(uint16_t *)addr = ((s_a >> 32) & 0xffff);
+ break;
+ case R_PPC64_ADDR16_HI: /* #hi(S + A) */
+ *(uint16_t *)addr = ((s_a >> 16) & 0xffff);
+ break;
+ case R_PPC64_ADDR16_LO: /* #lo(S + A) */
+ *(uint16_t *)addr = s_a & 0xffff;
+ break;
+ case R_PPC64_ADDR16_LO_DS:
+ *(uint16_t *)addr = (s_a & 0xfffc);
+ break;
+ case R_PPC64_ADDR16_HA: /* #ha(S + A) */
+ *(uint16_t *)addr = (((s_a >> 16) + ((s_a & 0x8000) ? 1 : 0))
+ & 0xffff);
+ break;
+
+ case R_PPC64_TOC16: /* half16* S + A - .TOC. */
+ case R_PPC64_TOC16_LO_DS:
+ case R_PPC64_TOC16_LO: /* #lo(S + A - .TOC.) */
+ case R_PPC64_TOC16_HI: /* #hi(S + A - .TOC.) */
+ case R_PPC64_TOC16_HA:
+ case R_PPC64_TOC16_DS: /* (S + A - .TOC) >> 2 */
+ case R_PPC64_REL14:
+ case R_PPC64_REL24: /* (S + A - P) >> 2 */
+ case R_PPC64_REL32: /* S + A - P */
+ case R_PPC64_REL64: /* S + A - P */
+ case R_PPC64_GOT16_DS:
+ case R_PPC64_GOT16_LO_DS:
+ // printf("\t\tignoring relocation type %i\n",
+ // ELF_R_TYPE(relaentry->r_info));
+ break;
+ default:
+ printf("ERROR: Unhandled relocation (A) type %i\n",
+ ELF_R_TYPE(relaentry->r_info));
+ }
+}
+
+
+/**
+ * Step through all relocation entries and apply them one by one.
+ */
+static void
+elf_apply_all_rela64(void *file_addr, signed long offset, struct shdr64 *shdrs, int idx)
+{
+ struct shdr64 *rela_shdr = &shdrs[idx];
+ struct shdr64 *dst_shdr = &shdrs[rela_shdr->sh_info];
+ struct shdr64 *sym_shdr = &shdrs[rela_shdr->sh_link];
+ struct rela *relaentry;
+ struct sym64 *symtabentry;
+ uint32_t symbolidx;
+ unsigned i;
+
+ /* If the referenced section has not been allocated, then it has
+ * not been loaded and thus does not need to be relocated. */
+ if ((dst_shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
+ return;
+
+ for (i = 0; i < rela_shdr->sh_size; i += rela_shdr->sh_entsize) {
+ relaentry = (struct rela *)(file_addr + rela_shdr->sh_offset + i);
+
+ symbolidx = ELF_R_SYM(relaentry->r_info);
+ symtabentry = (struct sym64*)(file_addr + sym_shdr->sh_offset) + symbolidx;
+
+ elf_apply_rela64(file_addr, offset, relaentry, symtabentry);
+ }
+}
+
+
+/**
+ * Apply ELF relocations
+ */
+void
+elf_relocate64(void *file_addr, signed long offset)
+{
+ struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
+ /* Calculate section header address */
+ struct shdr64 *shdrs = (struct shdr64 *)
+ (((unsigned char *) file_addr) + ehdr->e_shoff);
+ int i;
+
+ /* loop over all segments */
+ for (i = 0; i <= ehdr->e_shnum; i++) {
+ /* Skip if it is not a relocation segment */
+ if (shdrs[i].sh_type == SHT_RELA) {
+ elf_apply_all_rela64(file_addr, offset, shdrs, i);
+ }
+ }
+}
+
+void
+elf_byteswap_header64(void *file_addr)
+{
+ struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
+ struct phdr64 *phdr;
+ int i;
+
+ bswap_16p(&ehdr->e_type);
+ bswap_16p(&ehdr->e_machine);
+ bswap_32p(&ehdr->e_version);
+ bswap_64p(&ehdr->e_entry);
+ bswap_64p(&ehdr->e_phoff);
+ bswap_64p(&ehdr->e_shoff);
+ bswap_32p(&ehdr->e_flags);
+ bswap_16p(&ehdr->e_ehsize);
+ bswap_16p(&ehdr->e_phentsize);
+ bswap_16p(&ehdr->e_phnum);
+ bswap_16p(&ehdr->e_shentsize);
+ bswap_16p(&ehdr->e_shnum);
+ bswap_16p(&ehdr->e_shstrndx);
+
+ phdr = get_phdr64(file_addr);
+
+ /* loop e_phnum times */
+ for (i = 0; i <= ehdr->e_phnum; i++) {
+ bswap_32p(&phdr->p_type);
+ bswap_32p(&phdr->p_flags);
+ bswap_64p(&phdr->p_offset);
+ bswap_64p(&phdr->p_vaddr);
+ bswap_64p(&phdr->p_paddr);
+ bswap_64p(&phdr->p_filesz);
+ bswap_64p(&phdr->p_memsz);
+ bswap_64p(&phdr->p_align);
+
+ /* step to next header */
+ phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+ }
+}
+
+uint32_t elf_get_eflags_64(void *file_addr)
+{
+ struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
+
+ return ehdr->e_flags;
+}
+
+/*
+ * Determine the size of an ELF image that has been loaded into
+ * a buffer larger than its size. We search all program headers
+ * and sections for the one that shows the farthest extent of the
+ * file.
+ * @return Return -1 on error, size of file otherwise.
+ */
+long elf_get_file_size64(const void *buffer, const unsigned long buffer_size)
+{
+ const struct ehdr64 *ehdr = (const struct ehdr64 *) buffer;
+ const uint8_t *buffer_end = buffer + buffer_size;
+ const struct phdr64 *phdr;
+ const struct shdr64 *shdr;
+ unsigned long elf_size = 0;
+ uint16_t entsize;
+ unsigned i;
+
+ if (buffer_size < sizeof(struct ehdr) ||
+ ehdr->e_ehsize != sizeof(struct ehdr64))
+ return -1;
+
+ phdr = buffer + elf64_to_cpu(ehdr->e_phoff, ehdr);
+ entsize = elf16_to_cpu(ehdr->e_phentsize, ehdr);
+ for (i = 0; i < elf16_to_cpu(ehdr->e_phnum, ehdr); i++) {
+ if (((uint8_t *)phdr) + entsize > buffer_end)
+ return -1;
+
+ elf_size = MAX(elf64_to_cpu(phdr->p_offset, ehdr) +
+ elf64_to_cpu(phdr->p_filesz, ehdr),
+ elf_size);
+
+ /* step to next header */
+ phdr = (struct phdr64 *)(((uint8_t *)phdr) + entsize);
+ }
+
+ shdr = buffer + elf64_to_cpu(ehdr->e_shoff, ehdr);
+ entsize = elf16_to_cpu(ehdr->e_shentsize, ehdr);
+ for (i = 0; i < elf16_to_cpu(ehdr->e_shnum, ehdr); i++) {
+ if (((uint8_t *)shdr) + entsize > buffer_end)
+ return -1;
+
+ elf_size = MAX(elf64_to_cpu(shdr->sh_offset, ehdr) +
+ elf64_to_cpu(shdr->sh_size, ehdr),
+ elf_size);
+
+ /* step to next header */
+ shdr = (struct shdr64 *)(((uint8_t *)shdr) + entsize);
+ }
+
+ elf_size = ROUNDUP(elf_size, 4);
+ if (elf_size > buffer_size)
+ return -1;
+
+ return (long) elf_size;
+}
diff --git a/roms/SLOF/lib/libelf/elf_claim.c b/roms/SLOF/lib/libelf/elf_claim.c
new file mode 100644
index 000000000..43540f9b6
--- /dev/null
+++ b/roms/SLOF/lib/libelf/elf_claim.c
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2009, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include <libelf.h>
+#include "../../slof/paflof.h"
+
+
+/**
+ * Call Forth code to try to claim the memory region
+ */
+int
+elf_forth_claim(void *addr, long size)
+{
+ forth_push((long)addr);
+ forth_push(size);
+ forth_eval("elf-claim-segment");
+ return forth_pop();
+}
diff --git a/roms/SLOF/lib/libelf/libelf.code b/roms/SLOF/lib/libelf/libelf.code
new file mode 100644
index 000000000..551468bdd
--- /dev/null
+++ b/roms/SLOF/lib/libelf/libelf.code
@@ -0,0 +1,43 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * libelf Forth wrapper
+ */
+
+#include <libelf.h>
+
+// : elf-load-file ( fileaddr -- entry type )
+PRIM(ELF_X2d_LOAD_X2d_FILE)
+{
+ void *file_addr = TOS.a;
+ int type;
+ unsigned long entry;
+ type = elf_load_file(file_addr, &entry, elf_forth_claim, flush_cache);
+ TOS.u = entry;
+ PUSH; TOS.n = type;
+}
+MIRP
+
+// : elf-load-file-to-addr ( fileaddr destaddr -- entry type )
+PRIM(ELF_X2d_LOAD_X2d_FILE_X2d_TO_X2d_ADDR)
+{
+ void *dest_addr = TOS.a; POP;
+ void *file_addr = TOS.a;
+ int type;
+ unsigned long entry;
+ type = elf_load_file_to_addr(file_addr, dest_addr, &entry,
+ elf_forth_claim, flush_cache);
+ TOS.u = entry;
+ PUSH; TOS.n = type;
+}
+MIRP
diff --git a/roms/SLOF/lib/libelf/libelf.in b/roms/SLOF/lib/libelf/libelf.in
new file mode 100644
index 000000000..9c5f019ce
--- /dev/null
+++ b/roms/SLOF/lib/libelf/libelf.in
@@ -0,0 +1,18 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * libelf bindings for Forth - definitions
+ */
+
+cod(ELF-LOAD-FILE)
+cod(ELF-LOAD-FILE-TO-ADDR)
diff --git a/roms/SLOF/lib/libhvcall/Makefile b/roms/SLOF/lib/libhvcall/Makefile
new file mode 100644
index 000000000..def532509
--- /dev/null
+++ b/roms/SLOF/lib/libhvcall/Makefile
@@ -0,0 +1,57 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
+LDFLAGS = -nostdlib
+
+TARGET = ../libhvcall.a
+
+
+all: $(TARGET)
+
+SRCS = brokensc1.c rfill.c
+SRCSS = hvcall.S
+
+
+OBJS = $(SRCS:%.c=%.o) $(SRCSS:%.S=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+%.o: %.S
+ $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
+
diff --git a/roms/SLOF/lib/libhvcall/brokensc1.c b/roms/SLOF/lib/libhvcall/brokensc1.c
new file mode 100644
index 000000000..f01157029
--- /dev/null
+++ b/roms/SLOF/lib/libhvcall/brokensc1.c
@@ -0,0 +1,162 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <cpu.h>
+#include "libhvcall.h"
+#include "byteorder.h"
+
+// #define DEBUG_PATCHERY
+
+#define H_SET_DABR 0x28
+#define INS_SC1 0x44000022
+#define INS_SC1_REPLACE 0x7c000268
+
+extern volatile uint32_t sc1ins;
+
+static unsigned long hcall(uint32_t inst, unsigned long arg0, unsigned long arg1)
+{
+ register unsigned long r3 asm("r3") = arg0;
+ register unsigned long r4 asm("r4") = arg1;
+ register unsigned long r5 asm("r5") = inst;
+ asm volatile("bl 1f \n"
+ "1: \n"
+ "li 11, 2f - 1b \n"
+ "mflr 12 \n"
+ "add 11, 11, 12 \n"
+ "stw 5, 0(11) \n"
+ "dcbst 0, 11 \n"
+ "sync \n"
+ "icbi 0, 11 \n"
+ "isync \n"
+ "2: \n"
+ ".long 0 \n"
+ : "=r" (r3)
+ : "r" (r3), "r" (r4), "r" (r5)
+ : "ctr", "r0", "r6", "r7", "r8", "r9", "r10", "r11",
+ "r12", "r13", "r31", "lr", "cc");
+ return r3;
+}
+
+int check_broken_sc1(void)
+{
+ long r;
+
+ /*
+ * Check if we can do a simple hcall. If it works, we are running in
+ * a sane environment and everything's fine. If it doesn't, we need
+ * to patch the hypercall instruction to something that traps into
+ * supervisor mode.
+ */
+ r = hcall(INS_SC1, H_SET_DABR, 0);
+ if (r == H_PRIVILEGE) {
+ /* We found a broken sc1 host! */
+ return 1;
+ }
+
+ /* All is fine */
+ return 0;
+}
+
+int patch_broken_sc1(void *start, void *end, uint32_t *test_ins)
+{
+ uint32_t *p;
+ /* The sc 1 instruction */
+ uint32_t sc1 = INS_SC1;
+ /* An illegal instruction that KVM interprets as sc 1 */
+ uint32_t sc1_replacement = INS_SC1_REPLACE;
+ int is_le = (test_ins && *test_ins == 0x48000008);
+#ifdef DEBUG_PATCHERY
+ int cnt = 0;
+#endif
+
+ /* The host is sane, get out of here */
+ if (!check_broken_sc1())
+ return 0;
+
+ /* We only get here with a broken sc1 implementation */
+
+ /* Trim the range we scan to not cover the data section */
+ if (test_ins) {
+ /* This is the cpu table matcher for 970FX */
+ uint32_t end_bytes[] = { 0xffff0000, 0x3c0000 };
+ /*
+ * The .__start symbol contains a trap instruction followed
+ * by lots of zeros.
+ */
+ uint32_t start_bytes[] = { 0x7fe00008, 0, 0, 0, 0 };
+
+ if (is_le) {
+ end_bytes[0] = bswap_32(end_bytes[0]);
+ end_bytes[1] = bswap_32(end_bytes[1]);
+ start_bytes[1] = bswap_32(start_bytes[1]);
+ }
+
+ /* Find the start of the text section */
+ for (p = test_ins; (long)p > (long)start; p--) {
+ if (p[0] == start_bytes[0] &&
+ p[1] == start_bytes[1] &&
+ p[2] == start_bytes[2] &&
+ p[3] == start_bytes[3] &&
+ p[4] == start_bytes[4]) {
+ /*
+ * We found a match of the instruction sequence
+ * trap
+ * .long 0
+ * .long 0
+ * .long 0
+ * .long 0
+ * which marks the beginning of the .text
+ * section on all Linux kernels I've checked.
+ */
+#ifdef DEBUG_PATCHERY
+ printf("Shortened start from %p to %p\n", end, p);
+#endif
+ start = p;
+ break;
+ }
+ }
+
+ /* Find the end of the text section */
+ for (p = start; (long)p < (long)end; p++) {
+ if (p[0] == end_bytes[0] && p[1] == end_bytes[1]) {
+ /*
+ * We found a match of the PPC970FX entry in the
+ * guest kernel's CPU table. That table is
+ * usually found early in the .data section and
+ * thus marks the end of the .text section for
+ * us which we need to patch.
+ */
+#ifdef DEBUG_PATCHERY
+ printf("Shortened end from %p to %p\n", end, p);
+#endif
+ end = p;
+ break;
+ }
+ }
+ }
+
+ if (is_le) {
+ /*
+ * The kernel was built for LE mode, so our sc1 and replacement
+ * opcodes are in the wrong byte order. Reverse them.
+ */
+ sc1 = bswap_32(sc1);
+ sc1_replacement = bswap_32(sc1_replacement);
+ }
+
+ /* Patch all sc 1 instructions to reserved instruction 31/308 */
+ for (p = start; (long)p < (long)end; p++) {
+ if (*p == sc1) {
+ *p = sc1_replacement;
+ flush_cache(p, sizeof(*p));
+#ifdef DEBUG_PATCHERY
+ cnt++;
+#endif
+ }
+ }
+
+#ifdef DEBUG_PATCHERY
+ printf("Patched %d instructions (%p - %p)\n", cnt, start, end);
+#endif
+
+ return 1;
+}
diff --git a/roms/SLOF/lib/libhvcall/hvcall.S b/roms/SLOF/lib/libhvcall/hvcall.S
new file mode 100644
index 000000000..b19f6dbef
--- /dev/null
+++ b/roms/SLOF/lib/libhvcall/hvcall.S
@@ -0,0 +1,155 @@
+#define _ASM
+#define __ASSEMBLY__
+#include "macros.h"
+#include "libhvcall.h"
+#include <termctrl.h>
+#include <product.h>
+
+#define HVCALL .long 0x44000022
+ .text
+ .align 3
+
+ENTRY(get_print_banner)
+ LOAD32(r4, print_version)
+ LOAD32(r5, print_version_end)
+ std r4,0(r3)
+ std r5,8(r3)
+ blr
+
+ENTRY(hv_generic)
+ HVCALL
+ blr
+
+/* r3 = char, r4 = hvtermno */
+ENTRY(hv_putchar)
+ sldi r6,r3,(24+32)
+ li r3,H_PUT_TERM_CHAR
+ li r5,1
+ HVCALL
+ blr
+
+/* r3 = hvtermno */
+ENTRY(hv_getchar)
+ mflr r10
+ bl .hv_haschar
+ mtlr r10
+ cmpwi cr0,r3,0
+ beqlr
+ lis r9,inbuf@h
+ ori r9,r9,inbuf@l
+ lwz r4,20(r9)
+ lbzx r3,r4,r9
+ addi r4,r4,1
+ stw r4,20(r9)
+ blr
+
+/* r3 = hvtermno */
+ENTRY(hv_haschar)
+ mr r4,r3
+ li r3,-1
+ lis r9,inbuf@h
+ ori r9,r9,inbuf@l
+ lwz r5,16(r9)
+ lwz r6,20(r9)
+ cmplw cr0,r5,r6
+ bnelr
+ li r3,H_GET_TERM_CHAR
+ HVCALL
+ lis r9,inbuf@h
+ ori r9,r9,inbuf@l
+ stw r4,16(r9)
+ li r3,0
+ stw r3,20(r9)
+ cmplwi cr0,r4,0
+ beqlr
+ li r3,-1
+ std r5,0(r9)
+ std r6,8(r9)
+ blr
+
+ENTRY(hv_send_crq)
+ ld r5,0(r4)
+ ld r6,8(r4)
+ mr r4,r3
+ li r3,H_SEND_CRQ
+ HVCALL
+ blr
+
+ENTRY(hv_send_logical_lan)
+ li r11,0 /* no continue token for now */
+ mr r10,r9
+ mr r9,r8
+ mr r8,r7
+ mr r7,r6
+ mr r6,r5
+ mr r5,r4
+ mr r4,r3
+ li r3,H_SEND_LOGICAL_LAN
+ HVCALL
+ blr
+
+ENTRY(hv_logical_ci_load)
+ mr r5,r4
+ mr r4,r3
+ li r3,H_LOGICAL_CI_LOAD
+ HVCALL
+ cmpdi cr0,r3,0
+ mr r3,r4
+ beqlr
+ li r3,-1
+ blr
+
+ENTRY(hv_logical_ci_store)
+ mr r6,r5
+ mr r5,r4
+ mr r4,r3
+ li r3,H_LOGICAL_CI_STORE
+ HVCALL
+ blr
+
+ENTRY(hv_logical_memop)
+ mr r8,r7
+ mr r7,r6
+ mr r6,r5
+ mr r5,r4
+ mr r4,r3
+ lis r3,KVMPPC_H_LOGICAL_MEMOP@h
+ ori r3,r3,KVMPPC_H_LOGICAL_MEMOP@l
+ HVCALL
+ blr
+
+ENTRY(hv_cas)
+ mr r6,r5
+ mr r5,r4
+ mr r4,r3
+ lis r3,KVMPPC_H_CAS@h
+ ori r3,r3,KVMPPC_H_CAS@l
+ HVCALL
+ blr
+
+/* This is the actual RTAS blob copied to the OS at instantiate-rtas */
+ENTRY(hv_rtas)
+ mr r4,r3
+ lis r3,KVMPPC_H_RTAS@h
+ ori r3,r3,KVMPPC_H_RTAS@l
+ HVCALL
+ blr
+ .globl hv_rtas_size
+hv_rtas_size:
+ .long . - hv_rtas;
+
+ENTRY(hv_rtas_broken_sc1)
+ mr r4,r3
+ lis r3,KVMPPC_H_RTAS@h
+ ori r3,r3,KVMPPC_H_RTAS@l
+ .long 0x7c000268
+ blr
+ .globl hv_rtas_broken_sc1_size
+hv_rtas_broken_sc1_size:
+ .long . - hv_rtas_broken_sc1;
+
+ .section ".bss"
+inbuf: .space 16
+inlen: .space 4
+inpos: .space 4
+ .text
diff --git a/roms/SLOF/lib/libhvcall/hvcall.code b/roms/SLOF/lib/libhvcall/hvcall.code
new file mode 100644
index 000000000..7e8536aa8
--- /dev/null
+++ b/roms/SLOF/lib/libhvcall/hvcall.code
@@ -0,0 +1,144 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <libhvcall.h>
+
+// : hv-putchar ( hvtermno char -- )
+PRIM(hv_X2d_putchar)
+ char c = TOS.n; POP;
+ int hvtermno = TOS.n; POP;
+ hv_putchar(c, hvtermno);
+MIRP
+
+// : hv-getchar ( hvtermno -- char )
+PRIM(hv_X2d_getchar)
+ TOS.n = hv_getchar(TOS.n);
+MIRP
+
+// : hv-haschar ( hvtermno -- res )
+PRIM(hv_X2d_haschar)
+ TOS.n = hv_haschar(TOS.n);
+MIRP
+
+// : hv-reg-crq ( unit qaddr qsize -- res )
+PRIM(hv_X2d_reg_X2d_crq)
+ unsigned long qsize = TOS.u; POP;
+ unsigned long qaddr = TOS.u; POP;
+ unsigned int unit = TOS.u;
+ TOS.n = hv_reg_crq(unit, qaddr, qsize);
+MIRP
+
+// : hv-free-crq ( unit -- )
+PRIM(hv_X2d_free_X2d_crq)
+ unsigned int unit = TOS.u; POP;
+ hv_free_crq(unit);
+MIRP
+
+// : hv-send-crq ( unit msgaddr -- rc )
+PRIM(hv_X2d_send_X2d_crq)
+ uint64_t *msgaddr = (uint64_t *)TOS.u; POP;
+ unsigned int unit = TOS.u;
+ TOS.n = hv_send_crq(unit, msgaddr);
+MIRP
+
+// : hv-put-tce ( liobn ioba tce -- rc )
+PRIM(hv_X2d_put_X2d_tce)
+ uint64_t tce = TOS.u; POP;
+ uint64_t ioba = TOS.u; POP;
+ uint32_t liobn = TOS.u;
+ TOS.u = hv_generic(H_PUT_TCE, liobn, ioba, tce);
+MIRP
+
+PRIM(RB_X40)
+ unsigned long qaddr = TOS.u;
+ TOS.u = hv_logical_ci_load(1, qaddr);
+MIRP
+PRIM(RB_X21)
+ unsigned long qaddr = TOS.u; POP;
+ unsigned char val = TOS.u; POP;
+ hv_logical_ci_store(1, qaddr, val);
+MIRP
+PRIM(RW_X40)
+ unsigned long qaddr = TOS.u;
+ TOS.u = hv_logical_ci_load(2, qaddr);
+MIRP
+PRIM(RW_X21)
+ unsigned long qaddr = TOS.u; POP;
+ unsigned short val = TOS.u; POP;
+ hv_logical_ci_store(2, qaddr, val);
+MIRP
+PRIM(RL_X40)
+ unsigned long qaddr = TOS.u;
+ TOS.u = hv_logical_ci_load(4, qaddr);
+MIRP
+PRIM(RL_X21)
+ unsigned long qaddr = TOS.u; POP;
+ unsigned int val = TOS.u; POP;
+ hv_logical_ci_store(4, qaddr, val);
+MIRP
+PRIM(RX_X40)
+ unsigned long qaddr = TOS.u;
+ TOS.u = hv_logical_ci_load(8, qaddr);
+MIRP
+PRIM(RX_X21)
+ unsigned long qaddr = TOS.u; POP;
+ unsigned long val = TOS.u; POP;
+ hv_logical_ci_store(8, qaddr, val);
+MIRP
+
+PRIM(hv_X2d_logical_X2d_memop)
+ unsigned long op = TOS.u; POP;
+ unsigned long count = TOS.u; POP;
+ unsigned long esize = TOS.u; POP;
+ unsigned long src = TOS.u; POP;
+ unsigned long dst = TOS.u;
+ TOS.u = hv_logical_memop(dst, src, esize, count, op);
+MIRP
+
+PRIM(hv_X2d_cas)
+ unsigned long size = TOS.u; POP;
+ unsigned long buf = TOS.u; POP;
+ unsigned long vec = TOS.u;
+ TOS.u = hv_cas(vec, buf, size);
+MIRP
+
+PRIM(get_X2d_print_X2d_version)
+ unsigned long addr = TOS.u; POP;
+ get_print_banner(addr);
+MIRP
+
+PRIM(check_X2d_and_X2d_patch_X2d_sc1)
+ unsigned long end = TOS.u; POP;
+ unsigned long start = TOS.u; POP;
+ unsigned long patch_ins = TOS.u; POP;
+
+ patch_broken_sc1((void*)start, (void*)end, (void*)patch_ins);
+MIRP
+
+PRIM(hv_X2d_update_X2d_dt)
+ unsigned long dt = TOS.u;
+ TOS.u = hv_generic(KVMPPC_H_UPDATE_DT, dt);
+MIRP
+
+PRIM(hv_X2d_rtas_X2d_get)
+ if (check_broken_sc1()) {
+ PUSH;
+ TOS.u = (unsigned long) hv_rtas_broken_sc1;
+ PUSH;
+ TOS.u = hv_rtas_broken_sc1_size;
+ } else {
+ PUSH;
+ TOS.u = (unsigned long) hv_rtas;
+ PUSH;
+ TOS.u = hv_rtas_size;
+ }
+MIRP
diff --git a/roms/SLOF/lib/libhvcall/hvcall.in b/roms/SLOF/lib/libhvcall/hvcall.in
new file mode 100644
index 000000000..05ac3865a
--- /dev/null
+++ b/roms/SLOF/lib/libhvcall/hvcall.in
@@ -0,0 +1,35 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+cod(hv-putchar)
+cod(hv-getchar)
+cod(hv-haschar)
+cod(hv-reg-crq)
+cod(hv-free-crq)
+cod(hv-send-crq)
+cod(hv-put-tce)
+cod(check-and-patch-sc1)
+cod(hv-rtas-get)
+
+cod(RB@)
+cod(RB!)
+cod(RW@)
+cod(RW!)
+cod(RL@)
+cod(RL!)
+cod(RX@)
+cod(RX!)
+
+cod(hv-logical-memop)
+cod(hv-cas)
+cod(hv-update-dt)
+cod(get-print-version)
diff --git a/roms/SLOF/lib/libhvcall/libhvcall.h b/roms/SLOF/lib/libhvcall/libhvcall.h
new file mode 100644
index 000000000..72c1f0ffb
--- /dev/null
+++ b/roms/SLOF/lib/libhvcall/libhvcall.h
@@ -0,0 +1,112 @@
+#ifndef __LIBHVCALL_H__
+#define __LIBHVCALL_H__
+
+#define H_SUCCESS 0
+#define H_HARDWARE -1
+#define H_PRIVILEGE -3 /* Caller not privileged */
+
+#define H_GET_TCE 0x1C
+#define H_PUT_TCE 0x20
+#define H_LOGICAL_CI_LOAD 0x3c
+#define H_LOGICAL_CI_STORE 0x40
+#define H_GET_TERM_CHAR 0x54
+#define H_PUT_TERM_CHAR 0x58
+#define H_REG_CRQ 0xFC
+#define H_FREE_CRQ 0x100
+#define H_SEND_CRQ 0x108
+#define H_REGISTER_LOGICAL_LAN 0x114
+#define H_FREE_LOGICAL_LAN 0x118
+#define H_ADD_LOGICAL_LAN_BUFFER 0x11C
+#define H_SEND_LOGICAL_LAN 0x120
+
+/* KVM specific ones */
+#define KVMPPC_HCALL_BASE 0xf000
+#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0)
+#define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1)
+/* Client Architecture support */
+#define KVMPPC_H_CAS (KVMPPC_HCALL_BASE + 0x2)
+#define KVMPPC_H_UPDATE_DT (KVMPPC_HCALL_BASE + 0x3)
+#define KVMPPC_HCALL_MAX KVMPPC_H_UPDATE_DT
+
+#ifndef __ASSEMBLY__
+
+extern long hv_generic(unsigned long opcode, ...);
+
+extern void hv_putchar(char c, int hvtermno);
+extern char hv_getchar(int hvtermno);
+extern char hv_haschar(int hvtermno);
+extern void get_print_banner(unsigned long addr);
+
+extern int hv_send_crq(unsigned int unit, uint64_t *msgaddr);
+
+static inline long hv_reg_crq(unsigned int unit, unsigned long qaddr,
+ unsigned long qsize)
+{
+ return hv_generic(H_REG_CRQ, unit, qaddr, qsize);
+}
+
+static inline void hv_free_crq(unsigned int unit)
+{
+ hv_generic(H_FREE_CRQ, unit);
+}
+
+extern long hv_send_logical_lan(unsigned long unit_address,
+ unsigned long desc1, unsigned long desc2,
+ unsigned long desc3, unsigned long desc4,
+ unsigned long desc5, unsigned long desc6);
+
+static inline long h_register_logical_lan(unsigned long unit_address,
+ unsigned long buf_list,
+ unsigned long rec_q,
+ unsigned long filter_list,
+ unsigned long mac_address)
+{
+ return hv_generic(H_REGISTER_LOGICAL_LAN, unit_address,
+ buf_list, rec_q, filter_list, mac_address);
+}
+
+static inline long h_free_logical_lan(unsigned long unit_address)
+{
+ return hv_generic(H_FREE_LOGICAL_LAN, unit_address);
+}
+
+static inline long h_add_logical_lan_buffer(unsigned long unit_address,
+ unsigned long buffer)
+{
+ return hv_generic(H_ADD_LOGICAL_LAN_BUFFER, unit_address, buffer);
+}
+
+#define HV_RTAS_MAX_ARGRET 5
+
+struct hv_rtas_call {
+ uint32_t token;
+ uint32_t nargs;
+ uint32_t nrets;
+ uint32_t argret[HV_RTAS_MAX_ARGRET];
+};
+
+static inline unsigned long h_rtas(struct hv_rtas_call *rtas_buf)
+{
+ return hv_generic(KVMPPC_H_RTAS, (unsigned long)rtas_buf);
+}
+
+extern unsigned long hv_logical_ci_load(unsigned long size, unsigned long addr);
+extern unsigned long hv_logical_ci_store(unsigned long size, unsigned long addr,
+ unsigned long value);
+
+extern unsigned long hv_logical_memop(unsigned long dst, unsigned long src,
+ unsigned long esize, unsigned long count,
+ unsigned long op);
+extern int check_broken_sc1(void);
+extern int patch_broken_sc1(void *start, void *end, uint32_t *test_ins);
+
+extern unsigned long hv_cas(unsigned long vec, unsigned long buf,
+ unsigned long size);
+
+extern unsigned long hv_rtas(unsigned long params);
+extern unsigned long hv_rtas_broken_sc1(unsigned long params);
+extern unsigned int hv_rtas_size, hv_rtas_broken_sc1_size;
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __LIBHVCALL_H__ */
diff --git a/roms/SLOF/lib/libhvcall/rfill.c b/roms/SLOF/lib/libhvcall/rfill.c
new file mode 100644
index 000000000..377fb892b
--- /dev/null
+++ b/roms/SLOF/lib/libhvcall/rfill.c
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ * Fast function for filling cache-inhibited memory regions via h-call.
+ *
+ * Copyright 2015 Red Hat, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth, Red Hat Inc. - initial implementation
+ *****************************************************************************/
+
+#include <cache.h>
+#include <string.h>
+
+typedef unsigned long type_u;
+
+/**
+ * fast_rfill is the implementation of the FAST_RFILL macro with h-calls.
+ * This is defined here instead of cache.h since we need a temporary
+ * local buffer - and that caused stack size problems in engine() when
+ * we used it directly in the FAST_RFILL macro.
+ */
+void fast_rfill(char *dst, unsigned long size, char pat)
+{
+ type_u buf[64];
+
+ memset(buf, pat, size < sizeof(buf) ? size : sizeof(buf));
+
+ while (size > sizeof(buf)) {
+ FAST_MRMOVE(buf, dst, sizeof(buf));
+ dst += sizeof(buf);
+ size -= sizeof(buf);
+ }
+ FAST_MRMOVE(buf, dst, size);
+}
diff --git a/roms/SLOF/lib/libipmi/Makefile b/roms/SLOF/lib/libipmi/Makefile
new file mode 100644
index 000000000..4777f9edd
--- /dev/null
+++ b/roms/SLOF/lib/libipmi/Makefile
@@ -0,0 +1,28 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2007 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+LIBIPMICMNDIR = $(shell pwd)
+
+include $(TOPCMNDIR)/make.rules
+
+TARGET = ../libipmi.a
+
+all: $(TARGET)
+
+$(TARGET):
+ cp libipmi.oco $@
+
+clean:
+
+distclean:
diff --git a/roms/SLOF/lib/libipmi/libipmi.code b/roms/SLOF/lib/libipmi/libipmi.code
new file mode 100644
index 000000000..59c124418
--- /dev/null
+++ b/roms/SLOF/lib/libipmi/libipmi.code
@@ -0,0 +1,120 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <libipmi.h>
+
+// : ipmi-kcs-cmd ( in-buf in-len out-buf out-maxlen -- out-len errorcode )
+PRIM(IPMI_X2d_KCS_X2d_CMD)
+ cell maxlen = TOS; POP;
+ cell outbuf = TOS; POP;
+ int len = TOS.n; POP;
+ cell inbuf = TOS;
+ int retval;
+ retval = ipmi_kcs_cmd(inbuf.a, outbuf.a, maxlen.n, (uint32_t *) &len);
+ TOS.n = len;
+ PUSH; TOS.n = retval;
+MIRP
+
+
+PRIM(IPMI_X2d_SYSTEM_X2d_REBOOT)
+ ipmi_system_reboot();
+MIRP
+
+
+PRIM(IPMI_X2d_POWER_X2d_OFF)
+ ipmi_power_off();
+MIRP
+
+
+// : ipmi-oem-stop-bootwatchdog ( -- errorcode )
+PRIM(IPMI_X2d_OEM_X2d_STOP_X2d_BOOTWATCHDOG)
+ PUSH;
+ TOS.n = ipmi_oem_stop_bootwatchdog();
+MIRP
+
+
+// : ipmi-oem-set-bootwatchdog ( seconds -- errorcode )
+PRIM(IPMI_X2d_OEM_X2d_SET_X2d_BOOTWATCHDOG)
+ int sec = TOS.n;
+ TOS.n = ipmi_oem_set_bootwatchdog(sec);
+MIRP
+
+
+// : ipmi-oem-reset-bootwatchdog ( -- errorcode )
+PRIM(IPMI_X2d_OEM_X2d_RESET_X2d_BOOTWATCHDOG)
+ PUSH;
+ TOS.n = ipmi_oem_reset_bootwatchdog();
+MIRP
+
+
+// : ipmi-oem-led-set ( type instance state -- errorcode )
+PRIM(IPMI_X2d_OEM_X2d_LED_X2d_SET)
+ int state = TOS.n; POP;
+ int instance = TOS.n; POP;
+ int type = TOS.n;
+ TOS.n = ipmi_oem_led_set(type, instance, state);
+MIRP
+
+
+// : ipmi-oem-read-vpd ( offset length dst -- status )
+PRIM(IPMI_X2d_OEM_X2d_READ_X2d_VPD)
+ cell dest = TOS; POP;
+ int len = TOS.n; POP;
+ int offset = TOS.n;
+ TOS.n = ipmi_oem_read_vpd(dest.a, len, offset);
+MIRP
+
+// : ipmi-oem-write-vpd ( offset length src -- status )
+PRIM(IPMI_X2d_OEM_X2d_WRITE_X2d_VPD)
+ cell src = TOS; POP;
+ int len = TOS.n; POP;
+ int offset = TOS.n;
+ TOS.n = ipmi_oem_write_vpd(src.a, len, offset);
+MIRP
+
+
+// : ipmi-oem-get-blade-descr ( buf maxlen -- len status )
+PRIM(IPMI_X2d_OEM_X2d_GET_X2d_BLADE_X2d_DESCR)
+ int maxlen = TOS.n; POP;
+ cell buf = TOS;
+ int len = 0;
+ int retval;
+ retval = ipmi_oem_get_blade_descr(buf.a, maxlen, (uint32_t *) &len);
+ TOS.n = len;
+ PUSH; TOS.n = retval;
+MIRP
+
+
+// : ipmi-oem-bios2sp ( str-ptr str-len swid type -- errorcode )
+PRIM(IPMI_X2d_OEM_X2d_BIOS2SP)
+ int type = TOS.n; POP;
+ int swid = TOS.n; POP;
+ int len = TOS.n; POP;
+ void* addr = TOS.a;
+ TOS.n = ipmi_oem_bios2sp(swid, type, addr, len);
+MIRP
+
+// : ipmi-set-sensor ( param-1 ... param-n n command sensor - errorcode )
+PRIM(IPMI_X2d_SET_X2d_SENSOR)
+ int sensor = TOS.n; POP;
+ int cmd = TOS.n; POP;
+ int n = TOS.n;
+ int i = n;
+ uint8_t param[10];
+ while (i>0) {
+ i--;
+ POP; param[i]=TOS.n;
+ };
+ TOS.n = ipmi_set_sensor((cmd<<8)+sensor,n,
+ param[0],param[1],param[2],param[3],param[4],
+ param[5],param[6],param[7],param[8],param[9]);
+MIRP
diff --git a/roms/SLOF/lib/libipmi/libipmi.h b/roms/SLOF/lib/libipmi/libipmi.h
new file mode 100644
index 000000000..9ac83087e
--- /dev/null
+++ b/roms/SLOF/lib/libipmi/libipmi.h
@@ -0,0 +1,33 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef __LIBIPMI_H
+#define __LIBIPMI_H
+
+#include <stdint.h>
+
+extern int ipmi_kcs_cmd(uint8_t *, uint8_t *, uint32_t, uint32_t *);
+
+extern void ipmi_system_reboot(void);
+extern void ipmi_power_off(void);
+extern int ipmi_set_sensor(const int sensor, int number_of_args, ...);
+
+extern int ipmi_oem_stop_bootwatchdog(void);
+extern int ipmi_oem_set_bootwatchdog(uint16_t seconds);
+extern int ipmi_oem_reset_bootwatchdog(void);
+extern int ipmi_oem_led_set(int type, int instance, int state);
+extern uint32_t ipmi_oem_read_vpd(uint8_t *dst, uint32_t len, uint32_t offset);
+extern uint32_t ipmi_oem_write_vpd(uint8_t *src, uint32_t len, uint32_t offset);
+extern uint32_t ipmi_oem_get_blade_descr(uint8_t *dst, uint32_t maxlen, uint32_t *len);
+extern int ipmi_oem_bios2sp(int swid, int type, char *data, int len);
+
+#endif
diff --git a/roms/SLOF/lib/libipmi/libipmi.in b/roms/SLOF/lib/libipmi/libipmi.in
new file mode 100644
index 000000000..5b0e0ec5e
--- /dev/null
+++ b/roms/SLOF/lib/libipmi/libipmi.in
@@ -0,0 +1,24 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+cod(IPMI-KCS-CMD)
+cod(IPMI-SYSTEM-REBOOT)
+cod(IPMI-POWER-OFF)
+cod(IPMI-OEM-STOP-BOOTWATCHDOG)
+cod(IPMI-OEM-SET-BOOTWATCHDOG)
+cod(IPMI-OEM-RESET-BOOTWATCHDOG)
+cod(IPMI-OEM-LED-SET)
+cod(IPMI-OEM-READ-VPD)
+cod(IPMI-OEM-WRITE-VPD)
+cod(IPMI-OEM-GET-BLADE-DESCR)
+cod(IPMI-OEM-BIOS2SP)
+cod(IPMI-SET-SENSOR)
diff --git a/roms/SLOF/lib/libipmi/libipmi.oco b/roms/SLOF/lib/libipmi/libipmi.oco
new file mode 100644
index 000000000..74af7249c
--- /dev/null
+++ b/roms/SLOF/lib/libipmi/libipmi.oco
Binary files differ
diff --git a/roms/SLOF/lib/libnativeio/nativeio.code b/roms/SLOF/lib/libnativeio/nativeio.code
new file mode 100644
index 000000000..4887b115a
--- /dev/null
+++ b/roms/SLOF/lib/libnativeio/nativeio.code
@@ -0,0 +1,25 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/* There are 970 implementations. If we are ever to run native IOs
+ * on Power7 in hypervisor mode, these will have to change to use
+ * the new CI-load/store instructions
+ */
+PRIM(RB_X40) GET_CHAR1; SET_CI; GET_CHAR2; CLR_CI; GET_CHAR3; MIRP
+PRIM(RB_X21) PUT_CHAR1; SET_CI; PUT_CHAR2; CLR_CI; MIRP
+PRIM(RW_X40) GET_WORD1; SET_CI; GET_WORD2; CLR_CI; GET_WORD3; MIRP
+PRIM(RW_X21) PUT_WORD1; SET_CI; PUT_WORD2; CLR_CI; MIRP
+PRIM(RL_X40) GET_LONG1; SET_CI; GET_LONG2; CLR_CI; GET_LONG3; MIRP
+PRIM(RL_X21) PUT_LONG1; SET_CI; PUT_LONG2; CLR_CI; MIRP
+PRIM(RX_X40) GET_XONG1; SET_CI; GET_XONG2; CLR_CI; GET_XONG3; MIRP
+PRIM(RX_X21) PUT_XONG1; SET_CI; PUT_XONG2; CLR_CI; MIRP
+
diff --git a/roms/SLOF/lib/libnativeio/nativeio.in b/roms/SLOF/lib/libnativeio/nativeio.in
new file mode 100644
index 000000000..f5fb9d977
--- /dev/null
+++ b/roms/SLOF/lib/libnativeio/nativeio.in
@@ -0,0 +1,22 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+// I/O accesses.
+cod(RB@)
+cod(RB!)
+cod(RW@)
+cod(RW!)
+cod(RL@)
+cod(RL!)
+cod(RX@)
+cod(RX!)
+
diff --git a/roms/SLOF/lib/libnet/Makefile b/roms/SLOF/lib/libnet/Makefile
new file mode 100644
index 000000000..a2a6570db
--- /dev/null
+++ b/roms/SLOF/lib/libnet/Makefile
@@ -0,0 +1,50 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+ifndef TOP
+ TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd)
+ export TOP
+endif
+include $(TOP)/make.rules
+
+CFLAGS += -I. -I.. -I../libc/include -I$(TOP)/include $(FLAG)
+
+SRCS = ethernet.c ipv4.c udp.c tcp.c dns.c bootp.c dhcp.c tftp.c \
+ ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c ping.c args.c pxelinux.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+TARGET = ../libnet.a
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libnet/args.c b/roms/SLOF/lib/libnet/args.c
new file mode 100644
index 000000000..3f057c3d5
--- /dev/null
+++ b/roms/SLOF/lib/libnet/args.c
@@ -0,0 +1,179 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdint.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "args.h"
+
+/**
+ * Returns pointer of the n'th argument within a string.
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @param index index of the requested arguments within arg_str
+ * @return pointer of argument[index] on success
+ * NULL if index is out of range
+ */
+const char *
+get_arg_ptr(const char *arg_str, unsigned int index)
+{
+ unsigned int i;
+
+ for (i = 0; i < index; ++i) {
+ for (; *arg_str != ',' && *arg_str != 0; ++arg_str);
+ if (*arg_str == 0)
+ return 0;
+ ++arg_str;
+ }
+ return arg_str;
+}
+
+/**
+ * Returns number of arguments within a string.
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @return number of arguments
+ */
+unsigned int
+get_args_count(const char *arg_str)
+{
+ unsigned int count = 1;
+
+ while ((arg_str = get_arg_ptr(arg_str, 1)) != 0)
+ ++count;
+ return count;
+}
+
+/**
+ * Returns the length of the first argument.
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @return length of first argument
+ */
+unsigned int
+get_arg_length(const char *arg_str)
+{
+ unsigned int i;
+
+ for (i = 0; *arg_str != ',' && *arg_str != 0; ++i)
+ ++arg_str;
+ return i;
+}
+
+/**
+ * Copy the n'th argument within a string into a buffer in respect
+ * to a limited buffer size
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @param index index of the requested arguments within arg_str
+ * @param buffer pointer to the buffer
+ * @param length size of the buffer
+ * @return pointer of buffer on success
+ * NULL if index is out of range.
+ */
+char *
+argncpy(const char *arg_str, unsigned int index, char *buffer,
+ unsigned int length)
+{
+ const char *ptr = get_arg_ptr(arg_str, index);
+ unsigned int len;
+
+ if (!ptr)
+ return 0;
+ len = get_arg_length(ptr);
+ if (!strncpy(buffer, ptr, length))
+ return 0;
+ buffer[len] = 0;
+ return buffer;
+}
+
+/**
+ * Converts "255.255.255.255\nn" -> char[4] = { 0xff, 0xff, 0xff, 0xff }
+ * *netmask = subnet_netmask(nn)
+ *
+ * @param str string to be converted
+ * @param ip in case of SUCCESS - 32-bit long IP
+ * in case of FAULT - zero
+ * @param netmask return netmask if there is a valid /nn encoding in IP
+ * @return TRUE - IP converted successfully;
+ * FALSE - error condition occurs (e.g. bad format)
+ */
+int
+strtoip_netmask(const char *str, char ip[4], unsigned int *netmask)
+{
+ char octet[10];
+ int res;
+ unsigned int i = 0, len, has_nn = 0;
+
+ while (*str != 0) {
+ if (i > 3 || !isdigit(*str))
+ return 0;
+ if (strstr(str, ".") != NULL) {
+ len = (int16_t) (strstr(str, ".") - str);
+ if (len >= 10)
+ return 0;
+ strncpy(octet, str, len);
+ octet[len] = 0;
+ str += len;
+ } else if (strchr(str, '\\') != NULL) {
+ len = (short) (strchr(str, '\\') - str);
+ if (len >= 10)
+ return 0;
+ strncpy(octet, str, len);
+ octet[len] = 0;
+ str += len;
+ has_nn = 1;
+ } else {
+ strncpy(octet, str, 9);
+ octet[9] = 0;
+ str += strlen(octet);
+ }
+ res = strtol(octet, NULL, 10);
+ if ((res > 255) || (res < 0))
+ return 0;
+ ip[i] = (char) res;
+ i++;
+ if (*str == '.')
+ str++;
+ if(has_nn) {
+ str++;
+ strncpy(octet, str, 9);
+ octet[9] = 0;
+ res = strtol(octet, NULL, 10);
+ str += strlen(octet);
+ if (res > 31 || res < 1)
+ return 0;
+ if (netmask)
+ *netmask = 0xFFFFFFFF << (32 - res);
+ }
+ }
+
+ if (i != 4)
+ return 0;
+ return -1;
+}
+
+/**
+ * Converts "255.255.255.255" -> char[4] = { 0xff, 0xff, 0xff, 0xff }
+ *
+ * @param str string to be converted
+ * @param ip in case of SUCCESS - 32-bit long IP
+ * in case of FAULT - zero
+ * @return TRUE - IP converted successfully;
+ * FALSE - error condition occurs (e.g. bad format)
+ */
+int
+strtoip(const char *str, char ip[4])
+{
+ return strtoip_netmask(str, ip, NULL);
+}
diff --git a/roms/SLOF/lib/libnet/args.h b/roms/SLOF/lib/libnet/args.h
new file mode 100644
index 000000000..1ede9a8c8
--- /dev/null
+++ b/roms/SLOF/lib/libnet/args.h
@@ -0,0 +1,23 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _ARGS_H
+#define _ARGS_H
+
+const char *get_arg_ptr(const char *, unsigned int);
+unsigned int get_args_count(const char *);
+unsigned int get_arg_length(const char *);
+char *argncpy(const char *, unsigned int, char *, unsigned int);
+int strtoip(const char *, char[4]);
+int strtoip_netmask(const char *, char[4], unsigned int *netmask);
+
+#endif /* _ARGS_H */
diff --git a/roms/SLOF/lib/libnet/bootp.c b/roms/SLOF/lib/libnet/bootp.c
new file mode 100644
index 000000000..464cb665d
--- /dev/null
+++ b/roms/SLOF/lib/libnet/bootp.c
@@ -0,0 +1,255 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <time.h>
+
+#include <ethernet.h>
+#include <ipv4.h>
+#include <udp.h>
+#include <dhcp.h>
+
+#define DEBUG 0
+
+static char * response_buffer;
+
+#if DEBUG
+static void
+print_ip(char *ip)
+{
+ printf("%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
+}
+#endif
+
+/* IP header checksum calculation */
+static unsigned short
+checksum(unsigned short *packet, int words)
+{
+ unsigned long checksum;
+ for (checksum = 0; words > 0; words--)
+ checksum += *packet++;
+ checksum = (checksum >> 16) + (checksum & 0xffff);
+ checksum += (checksum >> 16);
+ return ~checksum;
+}
+
+
+static int
+send_bootp(filename_ip_t * fn_ip)
+{
+#if DEBUG
+ int i;
+#endif
+ unsigned int packetsize =
+ sizeof(struct iphdr) + sizeof(struct ethhdr) +
+ sizeof(struct udphdr) + sizeof(struct btphdr);
+ unsigned char packet[packetsize];
+ struct iphdr *iph;
+ struct udphdr *udph;
+ struct btphdr *btph;
+
+ iph = (struct iphdr *) packet;
+ udph = (struct udphdr *) (iph + 1);
+ btph = (struct btphdr *) (udph + 1);
+
+ memset(packet, 0, packetsize);
+
+ fill_iphdr((uint8_t *) iph, htons(packetsize - sizeof(struct ethhdr)),
+ IPTYPE_UDP, 0, fn_ip->server_ip);
+ fill_udphdr((uint8_t *) udph,
+ htons(sizeof(struct udphdr) + sizeof(struct btphdr)),
+ htons(UDPPORT_BOOTPC), htons(UDPPORT_BOOTPS));
+ btph->op = 1;
+ btph->htype = 1;
+ btph->hlen = 6;
+ strcpy((char *) btph->file, "bla");
+ memcpy(btph->chaddr, get_mac_address(), 6);
+
+#if DEBUG
+ printf("Sending packet\n");
+ printf("Packet is ");
+ for (i = 0; i < packetsize; i++)
+ printf(" %02x", packet[i]);
+ printf(".\n");
+#endif
+
+ send_ipv4(fn_ip->fd, packet, iph->ip_len);
+#if DEBUG
+ printf("%d bytes transmitted over socket.\n", i);
+#endif
+
+ return 0;
+}
+
+
+static int
+receive_bootp(filename_ip_t * fn_ip)
+{
+ int len, old_sum;
+ unsigned int packetsize = 2000;
+ unsigned char packet[packetsize];
+ struct iphdr *iph;
+ struct udphdr *udph;
+ struct btphdr *btph;
+
+#if DEBUG
+ struct ethhdr *ethh;
+ ethh = (struct ethhdr *) packet;
+#endif
+
+ iph = (struct iphdr *) (packet + sizeof(struct ethhdr));
+ udph = (struct udphdr *) (iph + 1);
+ btph = (struct btphdr *) (udph + 1);
+
+ memset(packet, 0, packetsize);
+
+ /* setting up a timer with a timeout of one second */
+ set_timer(TICKS_SEC);
+
+ do {
+
+ /* let's receive a packet */
+ len = recv(fn_ip->fd, packet, packetsize, 0);
+
+#if DEBUG
+ int j;
+ printf("%d bytes received, %d expected \n", len, packetsize);
+ if (len == 346) {
+ printf("Rec packet\n");
+ printf("Packet is ");
+ for (j = 0; j < len; j++) {
+ if (j % 16 == 0)
+ printf("\n");
+ printf(" %02x", packet[j]);
+ }
+ printf(".\n");
+ }
+#endif
+ if (len == 0)
+ continue;
+
+ /* check if the ip checksum is correct */
+ old_sum = iph->ip_sum;
+ iph->ip_sum = 0x00;
+ if (old_sum !=
+ checksum((unsigned short *) iph, sizeof(struct iphdr) >> 1))
+ /* checksum failed */
+ continue;
+ /* is it a udp packet */
+ if (iph->ip_p != IPTYPE_UDP)
+ continue;
+ /* check if the source port and destination port and the packet
+ * say that it is a bootp answer */
+ if (udph->uh_dport != htons(UDPPORT_BOOTPC) || udph->uh_sport != htons(UDPPORT_BOOTPS))
+ continue;
+ /* check if it is a Boot Reply */
+ if (btph->op != 2)
+ continue;
+ /* Comparing our mac address with the one in the bootp reply */
+ if (memcmp(get_mac_address(), btph->chaddr, ETH_ALEN))
+ continue;
+
+ if(response_buffer)
+ memcpy(response_buffer, btph, 1720);
+
+ fn_ip->own_ip = btph->yiaddr;
+ fn_ip->server_ip = btph->siaddr;
+ strcpy(fn_ip->filename, (char *)btph->file);
+
+#if DEBUG
+ printf("\nThese are the details of the bootp reply:\n");
+ printf("Our IP address: ");
+ print_ip((char*) &fn_ip->own_ip);
+ printf("Next server IP address: ");
+ print_ip((char*) &fn_ip->server_ip);
+ printf("Boot file name: %s\n", btph->file);
+ printf("Packet is: %s\n", btph->file);
+ for (j = 0; j < len; j++) {
+ if (j % 16 == 0)
+ printf("\n");
+ printf(" %02x", packet[j]);
+ }
+ printf(".\n");
+ printf("fn_ip->own_mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ get_mac_address()[0], get_mac_address()[1],
+ get_mac_address()[2], get_mac_address()[3],
+ get_mac_address()[4], get_mac_address()[5]);
+ printf("Header ethh->dest_mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ethh->dest_mac[0], ethh->dest_mac[1], ethh->dest_mac[2],
+ ethh->dest_mac[3], ethh->dest_mac[4], ethh->dest_mac[5]);
+ printf("Header ethh->src_mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ethh->src_mac[0], ethh->src_mac[1], ethh->src_mac[2],
+ ethh->src_mac[3], ethh->src_mac[4], ethh->src_mac[5]);
+ printf("Header ethh->typ: %x\n",ethh->type);
+ printf("Header iph->ip_hlv: %x\n",iph->ip_hlv);
+ printf("Header iph->ip_len: %x\n",iph->ip_len);
+ printf("Header iph->ip_id: %x\n",iph->ip_id);
+ printf("Header iph->ip_off: %x\n",iph->ip_off);
+ printf("Header iph->ip_ttl: %x\n",iph->ip_ttl);
+ printf("Header iph->ip_p: %x\n",iph->ip_p);
+ printf("Header iph->ip_sum: %x\n",iph->ip_sum);
+ printf("Header iph->ip_src: %x\n",iph->ip_src);
+ printf("Header iph->ip_dst: %x\n",iph->ip_dst);
+
+ printf("Header btph->op: %x\n",btph->op);
+ printf("Header btph->htype: %x\n",btph->htype);
+ printf("Header btph->hlen: %x\n",btph->hlen);
+ printf("Header btph->hops: %x\n",btph->hops);
+ printf("Header btph->xid: %x\n",btph->xid);
+ printf("Header btph->secs: %x\n",btph->secs);
+ printf("Header btph->ciaddr: %x\n",btph->ciaddr);
+ printf("Header btph->yiaddr: %x\n",btph->yiaddr);
+ printf("Header btph->siaddr: %x\n",btph->siaddr);
+ printf("Header btph->giaddr: %x\n",btph->giaddr);
+
+ printf("Header btph->chaddr: %02x:%02x:%02x:%02x:%02x:%02x:\n",
+ btph->chaddr[0], btph->chaddr[1], btph->chaddr[2],
+ btph->chaddr[3], btph->chaddr[4], btph->chaddr[5]);
+#endif
+ return 0;
+
+ /* only do this for the time specified during set_timer() */
+ } while (get_timer() > 0);
+ return -1;
+}
+
+
+int
+bootp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries)
+{
+ int i = (int) retries+1;
+ fn_ip->own_ip = 0;
+
+ printf(" Requesting IP address via BOOTP: ");
+
+ response_buffer = ret_buffer;
+
+ do {
+ printf("\b\b%02d", i);
+ if (!i--) {
+ printf("\nGiving up after %d bootp requests\n",
+ retries+1);
+ return -1;
+ }
+ send_bootp(fn_ip);
+ /* if the timer in receive_bootp expired it will return
+ * -1 and we will just send another bootp request just
+ * in case the previous one was lost. And because we don't
+ * trust the network cable we keep on doing this 30 times */
+ } while (receive_bootp(fn_ip) != 0);
+
+ printf("\b\b\bdone\n");
+ return 0;
+}
diff --git a/roms/SLOF/lib/libnet/dhcp.c b/roms/SLOF/lib/libnet/dhcp.c
new file mode 100644
index 000000000..85cd7c096
--- /dev/null
+++ b/roms/SLOF/lib/libnet/dhcp.c
@@ -0,0 +1,991 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+/******************************* ALGORITHMS ******************************/
+
+/** \file dhcp.c <pre>
+ * **************** State-transition diagram for DHCP client *************
+ *
+ * +---------+ Note: DHCP-server msg / DHCP-client msg
+ * | INIT |
+ * +---------+
+ * |
+ * | - / Discover
+ * V
+ * +---------+
+ * | SELECT | Timeout
+ * +---------+ |
+ * | |
+ * | Offer / Request |
+ * | |
+ * V V
+ * +---------+ NACK / - ***********
+ * | REQUEST | ----------------> * FAULT *
+ * +---------+ ***********
+ * |
+ * | ACK / - ***********
+ * +----------------------> * SUCCESS *
+ * ***********
+ *
+ * ************************************************************************
+ * </pre> */
+
+
+/********************** DEFINITIONS & DECLARATIONS ***********************/
+
+#include <dhcp.h>
+#include <ethernet.h>
+#include <ipv4.h>
+#include <udp.h>
+#include <dns.h>
+#include <args.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+/* DHCP Message Types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNACK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+/* DHCP Option Codes */
+#define DHCP_MASK 1
+#define DHCP_ROUTER 3
+#define DHCP_DNS 6
+#define DHCP_REQUESTED_IP 50
+#define DHCP_OVERLOAD 52
+#define DHCP_MSG_TYPE 53
+#define DHCP_SERVER_ID 54
+#define DHCP_REQUEST_LIST 55
+#define DHCP_TFTP_SERVER 66
+#define DHCP_BOOTFILE 67
+#define DHCP_CLIENT_ARCH 93
+#define DHCP_PXELINUX_CFGFILE 209 /* See RFC 5071 */
+#define DHCP_PXELINUX_PREFIX 210
+#define DHCP_ENDOPT 0xFF
+#define DHCP_PADOPT 0x00
+
+/* "file/sname" overload option values */
+#define DHCP_OVERLOAD_FILE 1
+#define DHCP_OVERLOAD_SNAME 2
+#define DHCP_OVERLOAD_BOTH 3
+
+/* DHCP states codes */
+#define DHCP_STATE_SELECT 1
+#define DHCP_STATE_REQUEST 2
+#define DHCP_STATE_SUCCESS 3
+#define DHCP_STATE_FAULT 4
+
+/* DHCP Client Architecture */
+#ifndef DHCPARCH
+#define USE_DHCPARCH 0
+#define DHCPARCH 0
+#else
+#define USE_DHCPARCH 1
+#endif
+
+static uint8_t dhcp_magic[] = {0x63, 0x82, 0x53, 0x63};
+/**< DHCP_magic is a cookie, that identifies DHCP options (see RFC 2132) */
+
+/** \struct dhcp_options_t
+ * This structure is used to fill options in DHCP-msg during transmitting
+ * or to retrieve options from DHCP-msg during receiving.
+ * <p>
+ * If flag[i] == TRUE then field for i-th option retains valid value and
+ * information from this field may retrived (in case of receiving) or will
+ * be transmitted (in case of transmitting).
+ *
+ */
+typedef struct {
+ uint8_t flag[256]; /**< Show if corresponding opt. is valid */
+ uint8_t request_list[256]; /**< o.55 If i-th member is TRUE, then i-th
+ option will be requested from server */
+ uint32_t server_ID; /**< o.54 Identifies DHCP-server */
+ uint32_t requested_IP; /**< o.50 Must be filled in DHCP-Request */
+ uint32_t dns_IP; /**< o. 6 DNS IP */
+ uint32_t router_IP; /**< o. 3 Router IP */
+ uint32_t subnet_mask; /**< o. 1 Subnet mask */
+ uint8_t msg_type; /**< o.53 DHCP-message type */
+ uint8_t overload; /**< o.52 Overload sname/file fields */
+ char tftp_server[256]; /**< o.66 TFTP server name */
+ char bootfile[256]; /**< o.67 Boot file name */
+ uint16_t client_arch; /**< o.93 Client architecture type */
+} dhcp_options_t;
+
+/** Stores state of DHCP-client (refer to State-transition diagram) */
+static uint8_t dhcp_state;
+
+
+/***************************** PROTOTYPES ********************************/
+
+static int32_t dhcp_attempt(int fd);
+
+static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct);
+
+static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len,
+ dhcp_options_t * opt_struct);
+
+static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len,
+ uint8_t src_options[], uint32_t src_len);
+
+static int8_t dhcp_find_option(uint8_t options[], uint32_t len,
+ uint8_t op_code, uint32_t * op_offset);
+
+static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len,
+ uint8_t * new_option);
+
+static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len,
+ uint32_t dst_offset, uint8_t * new_option);
+
+static void dhcp_send_discover(int fd);
+
+static void dhcp_send_request(int fd);
+
+/***************************** LOCAL VARIABLES ***************************/
+
+static uint8_t ether_packet[ETH_MTU_SIZE];
+static uint32_t dhcp_own_ip = 0;
+static uint32_t dhcp_server_ip = 0;
+static uint32_t dhcp_siaddr_ip = 0;
+static char dhcp_filename[256];
+static char dhcp_tftp_name[256];
+static uint32_t dhcp_xid;
+static char *pxelinux_cfgfile;
+static char *pxelinux_prefix;
+
+static char * response_buffer;
+
+/***************************** IMPLEMENTATION ****************************/
+
+void dhcpv4_generate_transaction_id(void)
+{
+ dhcp_xid = (rand() << 16) ^ rand();
+}
+
+int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip)
+{
+ uint32_t dhcp_tftp_ip = 0;
+ int fd = fn_ip->fd;
+
+ strcpy(dhcp_filename, "");
+ strcpy(dhcp_tftp_name, "");
+
+ pxelinux_cfgfile = pxelinux_prefix = NULL;
+
+ response_buffer = ret_buffer;
+
+ if (dhcp_attempt(fd) == 0)
+ return -1;
+
+ if (fn_ip->own_ip) {
+ dhcp_own_ip = fn_ip->own_ip;
+ }
+ if (fn_ip->server_ip) {
+ dhcp_siaddr_ip = fn_ip->server_ip;
+ }
+ if(fn_ip->filename[0] != 0) {
+ strcpy(dhcp_filename, fn_ip->filename);
+ }
+
+ // TFTP SERVER
+ if (!strlen(dhcp_tftp_name)) {
+ if (!dhcp_siaddr_ip) {
+ // ERROR: TFTP name is not presented
+ return -3;
+ }
+
+ // take TFTP-ip from siaddr field
+ dhcp_tftp_ip = dhcp_siaddr_ip;
+ }
+ else {
+ // TFTP server defined by its name
+ if (!strtoip(dhcp_tftp_name, (char *)&dhcp_tftp_ip)) {
+ if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&dhcp_tftp_ip, 4)) {
+ // DNS error - can't obtain TFTP-server name
+ // Use TFTP-ip from siaddr field, if presented
+ if (dhcp_siaddr_ip) {
+ dhcp_tftp_ip = dhcp_siaddr_ip;
+ }
+ else {
+ // ERROR: Can't obtain TFTP server IP
+ return -4;
+ }
+ }
+ }
+ }
+
+ // Store configuration info into filename_ip strucutre
+ fn_ip -> own_ip = dhcp_own_ip;
+ fn_ip -> server_ip = dhcp_tftp_ip;
+ strcpy(fn_ip->filename, dhcp_filename);
+
+ fn_ip->pl_cfgfile = pxelinux_cfgfile;
+ fn_ip->pl_prefix = pxelinux_prefix;
+ pxelinux_cfgfile = pxelinux_prefix = NULL;
+
+ return 0;
+}
+
+/**
+ * DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram
+ */
+static int32_t dhcp_attempt(int fd)
+{
+ int sec;
+
+ // Send DISCOVER message and switch DHCP-client to SELECT state
+ dhcp_send_discover(fd);
+
+ dhcp_state = DHCP_STATE_SELECT;
+
+ // setting up a timer with a timeout of two seconds
+ for (sec = 0; sec < 2; sec++) {
+ set_timer(TICKS_SEC);
+ do {
+ receive_ether(fd);
+
+ // Wait until client will switch to Final state or Timeout occurs
+ switch (dhcp_state) {
+ case DHCP_STATE_SUCCESS :
+ return 1;
+ case DHCP_STATE_FAULT :
+ return 0;
+ }
+ } while (get_timer() > 0);
+ }
+
+ // timeout
+ return 0;
+}
+
+/**
+ * DHCP: Supplements DHCP-message with options stored in structure.
+ * For more information about option coding see dhcp_options_t.
+ *
+ * @param opt_field Points to the "vend" field of DHCP-message
+ * (destination)
+ * @param opt_struct this structure stores info about the options which
+ * will be added to DHCP-message (source)
+ * @return TRUE - options packed;
+ * FALSE - error condition occurs.
+ * @see dhcp_options_t
+ */
+static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct)
+{
+ uint8_t * options = opt_field;
+ uint16_t i, sum; // used to define is any options set
+
+ // magic
+ memcpy(options, dhcp_magic, 4);
+ options += 4;
+
+ // fill message type
+ switch (opt_struct -> msg_type) {
+ case DHCPDISCOVER :
+ case DHCPREQUEST :
+ case DHCPDECLINE :
+ case DHCPINFORM :
+ case DHCPRELEASE :
+ options[0] = DHCP_MSG_TYPE;
+ options[1] = 1;
+ options[2] = opt_struct -> msg_type;
+ options += 3;
+ break;
+ default :
+ return 0; // Unsupported DHCP-message
+ }
+
+ if (opt_struct -> overload) {
+ options[0] = DHCP_OVERLOAD;
+ options[1] = 0x01;
+ options[2] = opt_struct -> overload;
+ options +=3;
+ }
+
+ if (opt_struct -> flag[DHCP_REQUESTED_IP]) {
+ options[0] = DHCP_REQUESTED_IP;
+ options[1] = 0x04;
+ * (uint32_t *) (options + 2) = htonl (opt_struct -> requested_IP);
+ options +=6;
+ }
+
+ if (opt_struct -> flag[DHCP_SERVER_ID]) {
+ options[0] = DHCP_SERVER_ID;
+ options[1] = 0x04;
+ * (uint32_t *) (options + 2) = htonl (opt_struct -> server_ID);
+ options +=6;
+ }
+
+ sum = 0;
+ for (i = 0; i < 256; i++)
+ sum += opt_struct -> request_list[i];
+
+ if (sum) {
+ options[0] = DHCP_REQUEST_LIST;
+ options[1] = sum;
+ options += 2;
+ for (i = 0; i < 256; i++) {
+ if (opt_struct -> request_list[i]) {
+ options[0] = i; options++;
+ }
+ }
+ }
+
+ if (opt_struct -> flag[DHCP_TFTP_SERVER]) {
+ options[0] = DHCP_TFTP_SERVER;
+ options[1] = strlen(opt_struct->tftp_server) + 1;
+ memcpy(options + 2, opt_struct -> tftp_server, options[1]);
+ options += options[1] + 2;
+ }
+
+ if (opt_struct -> flag[DHCP_BOOTFILE]) {
+ options[0] = DHCP_BOOTFILE;
+ options[1] = strlen(opt_struct->bootfile) + 1;
+ memcpy(options + 2, opt_struct -> bootfile, options[1]);
+ options += options[1] + 2;
+ }
+
+ if (opt_struct -> flag[DHCP_CLIENT_ARCH]) {
+ options[0] = DHCP_CLIENT_ARCH;
+ options[1] = 2;
+ options[2] = (DHCPARCH >> 8);
+ options[3] = DHCPARCH & 0xff;
+ options += 4;
+ }
+
+ // end options
+ options[0] = 0xFF;
+ options++;
+
+ return 1;
+}
+
+/**
+ * DHCP: Extracts encoded options from DHCP-message into the structure.
+ * For more information about option coding see dhcp_options_t.
+ *
+ * @param opt_field Points to the "options" field of DHCP-message
+ * (source).
+ * @param opt_len Length of "options" field.
+ * @param opt_struct this structure stores info about the options which
+ * was extracted from DHCP-message (destination).
+ * @return TRUE - options extracted;
+ * FALSE - error condition occurs.
+ * @see dhcp_options_t
+ */
+static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len,
+ dhcp_options_t * opt_struct)
+{
+ uint32_t offset = 0;
+
+ memset(opt_struct, 0, sizeof(dhcp_options_t));
+
+ // magic
+ if (memcmp(opt_field, dhcp_magic, 4)) {
+ return 0;
+ }
+
+ offset += 4;
+ while (offset < opt_len) {
+ opt_struct -> flag[opt_field[offset]] = 1;
+ switch(opt_field[offset]) {
+ case DHCP_OVERLOAD :
+ opt_struct -> overload = opt_field[offset + 2];
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_REQUESTED_IP :
+ opt_struct -> requested_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_MASK :
+ opt_struct -> flag[DHCP_MASK] = 1;
+ opt_struct -> subnet_mask = htonl(* (uint32_t *) (opt_field + offset + 2));
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_DNS :
+ opt_struct -> flag[DHCP_DNS] = 1;
+ opt_struct -> dns_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_ROUTER :
+ opt_struct -> flag[DHCP_ROUTER] = 1;
+ opt_struct -> router_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_MSG_TYPE :
+ if ((opt_field[offset + 2] > 0) && (opt_field[offset + 2] < 9))
+ opt_struct -> msg_type = opt_field[offset + 2];
+ else
+ return 0;
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_SERVER_ID :
+ opt_struct -> server_ID = htonl(* (uint32_t *) (opt_field + offset + 2));
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_TFTP_SERVER :
+ memcpy(opt_struct -> tftp_server, opt_field + offset + 2, opt_field[offset + 1]);
+ (opt_struct -> tftp_server)[opt_field[offset + 1]] = 0;
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_BOOTFILE :
+ memcpy(opt_struct -> bootfile, opt_field + offset + 2, opt_field[offset + 1]);
+ (opt_struct -> bootfile)[opt_field[offset + 1]] = 0;
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_CLIENT_ARCH :
+ opt_struct -> client_arch = ((opt_field[offset + 2] << 8) & 0xFF00) | (opt_field[offset + 3] & 0xFF);
+ offset += 4;
+ break;
+
+ case DHCP_PXELINUX_CFGFILE:
+ pxelinux_cfgfile = malloc(opt_field[offset + 1] + 1);
+ if (pxelinux_cfgfile) {
+ memcpy(pxelinux_cfgfile, opt_field + offset + 2,
+ opt_field[offset + 1]);
+ pxelinux_cfgfile[opt_field[offset + 1]] = 0;
+ }
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_PXELINUX_PREFIX:
+ pxelinux_prefix = malloc(opt_field[offset + 1] + 1);
+ if (pxelinux_prefix) {
+ memcpy(pxelinux_prefix, opt_field + offset + 2,
+ opt_field[offset + 1]);
+ pxelinux_prefix[opt_field[offset + 1]] = 0;
+ }
+ offset += 2 + opt_field[offset + 1];
+ break;
+
+ case DHCP_PADOPT :
+ offset++;
+ break;
+
+ case DHCP_ENDOPT : // End of options
+ return 1;
+
+ default :
+ offset += 2 + opt_field[offset + 1]; // Unsupported opt. - do nothing
+ }
+ }
+ if (offset == opt_len)
+ return 1; // options finished without 0xFF
+
+ return 0;
+}
+
+/**
+ * DHCP: Appends information from source "options" into dest "options".
+ * This function is used to support "file/sname" overloading.
+ *
+ * @param dst_options destanation "options" field
+ * @param dst_len size of dst_options (modified by this function)
+ * @param src_options source "options" field
+ * @param src_len size of src_options
+ * @return TRUE - options merged;
+ * FALSE - error condition occurs.
+ */
+static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len,
+ uint8_t src_options[], uint32_t src_len)
+{
+ uint32_t dst_offset, src_offset = 0;
+
+ // remove ENDOPT if presented
+ if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, &dst_offset))
+ * dst_len = dst_offset;
+
+ while (src_offset < src_len) {
+ switch(src_options[src_offset]) {
+ case DHCP_PADOPT:
+ src_offset++;
+ break;
+ case DHCP_ENDOPT:
+ return 1;
+ default:
+ if (dhcp_find_option(dst_options, * dst_len,
+ src_options[src_offset],
+ &dst_offset)) {
+ dhcp_combine_option(dst_options, dst_len,
+ dst_offset,
+ (uint8_t *) src_options +
+ src_offset);
+ }
+ else {
+ dhcp_append_option(dst_options, dst_len, src_options + src_offset);
+ }
+ src_offset += 2 + src_options[src_offset + 1];
+ }
+ }
+
+ if (src_offset == src_len)
+ return 1;
+ return 0;
+}
+
+/**
+ * DHCP: Finds given occurrence of the option with the given code (op_code)
+ * in "options" field of DHCP-message.
+ *
+ * @param options "options" field of DHCP-message
+ * @param len length of the "options" field
+ * @param op_code code of the option to find
+ * @param op_offset SUCCESS - offset to an option occurrence;
+ * FAULT - offset is set to zero.
+ * @return TRUE - option was find;
+ * FALSE - option wasn't find.
+ */
+static int8_t dhcp_find_option(uint8_t options[], uint32_t len,
+ uint8_t op_code, uint32_t * op_offset)
+{
+ uint32_t srch_offset = 0;
+ * op_offset = 0;
+
+ while (srch_offset < len) {
+ if (options[srch_offset] == op_code) {
+ * op_offset = srch_offset;
+ return 1;
+ }
+ if (options[srch_offset] == DHCP_ENDOPT)
+ return 0;
+
+ if (options[srch_offset] == DHCP_PADOPT)
+ srch_offset++;
+ else
+ srch_offset += 2 + options[srch_offset + 1];
+ }
+ return 0;
+}
+
+/**
+ * DHCP: Appends new option from one list (src) into the tail
+ * of another option list (dst)
+ *
+ * @param dst_options "options" field of DHCP-message
+ * @param dst_len length of the "options" field (modified)
+ * @param new_option points to an option in another list (src)
+ */
+static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len,
+ uint8_t * new_option)
+{
+ memcpy(dst_options + ( * dst_len), new_option, 2 + (* (new_option + 1)));
+ * dst_len += 2 + *(new_option + 1);
+}
+
+/**
+ * DHCP: This function is used when options with the same code are
+ * presented in both merged lists. In this case information
+ * about the option from one list (src) is combined (complemented)
+ * with information about the option in another list (dst).
+ *
+ * @param dst_options "options" field of DHCP-message
+ * @param dst_len length of the "options" field (modified)
+ * @param dst_offset offset of the option from beginning of the list
+ * @param new_option points to an option in another list (src)
+ */
+static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len,
+ uint32_t dst_offset, uint8_t * new_option)
+{
+ uint8_t tmp_buffer[1024]; // use to provide safe memcpy
+ uint32_t tail_len;
+
+ // move all subsequent options (allocate size for additional info)
+ tail_len = (* dst_len) - dst_offset - 2 - dst_options[dst_offset + 1];
+
+ memcpy(tmp_buffer, dst_options + (* dst_len) - tail_len, tail_len);
+ memcpy(dst_options + (* dst_len) - tail_len + (* (new_option + 1)),
+ tmp_buffer, tail_len);
+
+ // add new_content to option
+ memcpy(dst_options + (* dst_len) - tail_len, new_option + 2,
+ * (new_option + 1));
+ dst_options[dst_offset + 1] += * (new_option + 1);
+
+ // correct dst_len
+ * dst_len += * (new_option + 1);
+}
+
+/**
+ * DHCP: Sends DHCP-Discover message. Looks for DHCP servers.
+ */
+static void dhcp_send_discover(int fd)
+{
+ uint32_t packetsize = sizeof(struct iphdr) +
+ sizeof(struct udphdr) + sizeof(struct btphdr);
+ struct btphdr *btph;
+ dhcp_options_t opt;
+
+ memset(ether_packet, 0, packetsize);
+
+ btph = (struct btphdr *) (&ether_packet[
+ sizeof(struct iphdr) + sizeof(struct udphdr)]);
+
+ btph -> op = 1;
+ btph -> htype = 1;
+ btph -> hlen = 6;
+ btph -> xid = dhcp_xid;
+ memcpy(btph -> chaddr, get_mac_address(), 6);
+
+ memset(&opt, 0, sizeof(dhcp_options_t));
+
+ opt.msg_type = DHCPDISCOVER;
+
+ opt.request_list[DHCP_MASK] = 1;
+ opt.request_list[DHCP_DNS] = 1;
+ opt.request_list[DHCP_ROUTER] = 1;
+ opt.request_list[DHCP_TFTP_SERVER] = 1;
+ opt.request_list[DHCP_BOOTFILE] = 1;
+ opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
+
+ dhcp_encode_options(btph -> vend, &opt);
+
+ fill_udphdr(&ether_packet[sizeof(struct iphdr)],
+ sizeof(struct btphdr) + sizeof(struct udphdr),
+ UDPPORT_BOOTPC, UDPPORT_BOOTPS);
+ fill_iphdr(ether_packet, sizeof(struct btphdr) +
+ sizeof(struct udphdr) + sizeof(struct iphdr),
+ IPTYPE_UDP, dhcp_own_ip, 0xFFFFFFFF);
+
+ send_ipv4(fd, ether_packet, packetsize);
+}
+
+/**
+ * DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP.
+ */
+static void dhcp_send_request(int fd)
+{
+ uint32_t packetsize = sizeof(struct iphdr) +
+ sizeof(struct udphdr) + sizeof(struct btphdr);
+ struct btphdr *btph;
+ dhcp_options_t opt;
+
+ memset(ether_packet, 0, packetsize);
+
+ btph = (struct btphdr *) (&ether_packet[
+ sizeof(struct iphdr) + sizeof(struct udphdr)]);
+
+ btph -> op = 1;
+ btph -> htype = 1;
+ btph -> hlen = 6;
+ btph -> xid = dhcp_xid;
+ memcpy(btph -> chaddr, get_mac_address(), 6);
+
+ memset(&opt, 0, sizeof(dhcp_options_t));
+
+ opt.msg_type = DHCPREQUEST;
+ memcpy(&(opt.requested_IP), &dhcp_own_ip, 4);
+ opt.flag[DHCP_REQUESTED_IP] = 1;
+ memcpy(&(opt.server_ID), &dhcp_server_ip, 4);
+ opt.flag[DHCP_SERVER_ID] = 1;
+
+ opt.request_list[DHCP_MASK] = 1;
+ opt.request_list[DHCP_DNS] = 1;
+ opt.request_list[DHCP_ROUTER] = 1;
+ opt.request_list[DHCP_TFTP_SERVER] = 1;
+ opt.request_list[DHCP_BOOTFILE] = 1;
+ opt.request_list[DHCP_PXELINUX_CFGFILE] = 1;
+ opt.request_list[DHCP_PXELINUX_PREFIX] = 1;
+
+ opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
+ opt.flag[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
+
+ dhcp_encode_options(btph -> vend, &opt);
+
+ fill_udphdr(&ether_packet[sizeof(struct iphdr)],
+ sizeof(struct btphdr) + sizeof(struct udphdr),
+ UDPPORT_BOOTPC, UDPPORT_BOOTPS);
+ fill_iphdr(ether_packet, sizeof(struct btphdr) +
+ sizeof(struct udphdr) + sizeof(struct iphdr),
+ IPTYPE_UDP, 0, 0xFFFFFFFF);
+
+ send_ipv4(fd, ether_packet, packetsize);
+}
+
+
+/**
+ * DHCP: Sends DHCP-Release message. Releases occupied IP.
+ */
+void dhcp_send_release(int fd)
+{
+ uint32_t packetsize = sizeof(struct iphdr) +
+ sizeof(struct udphdr) + sizeof(struct btphdr);
+ struct btphdr *btph;
+ dhcp_options_t opt;
+
+ btph = (struct btphdr *) (&ether_packet[
+ sizeof(struct iphdr) + sizeof(struct udphdr)]);
+
+ memset(ether_packet, 0, packetsize);
+
+ btph -> op = 1;
+ btph -> htype = 1;
+ btph -> hlen = 6;
+ btph -> xid = dhcp_xid;
+ btph -> file[0] = 0;
+ memcpy(btph -> chaddr, get_mac_address(), 6);
+ btph -> ciaddr = htonl(dhcp_own_ip);
+
+ memset(&opt, 0, sizeof(dhcp_options_t));
+
+ opt.msg_type = DHCPRELEASE;
+ opt.server_ID = dhcp_server_ip;
+ opt.flag[DHCP_SERVER_ID] = 1;
+
+ dhcp_encode_options(btph -> vend, &opt);
+
+ fill_udphdr(&ether_packet[sizeof(struct iphdr)],
+ sizeof(struct btphdr) + sizeof(struct udphdr),
+ UDPPORT_BOOTPC, UDPPORT_BOOTPS);
+ fill_iphdr(ether_packet, sizeof(struct btphdr) +
+ sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP,
+ dhcp_own_ip, dhcp_server_ip);
+
+ send_ipv4(fd, ether_packet, packetsize);
+}
+
+/**
+ * DHCP: Handles DHCP-messages according to Receive-handle diagram.
+ * Changes the state of DHCP-client.
+ *
+ * @param fd socket descriptor
+ * @param packet BootP/DHCP-packet to be handled
+ * @param packetsize length of the packet
+ * @return ZERO - packet handled successfully;
+ * NON ZERO - packet was not handled (e.g. bad format)
+ * @see receive_ether
+ * @see btphdr
+ */
+
+int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize)
+{
+ struct btphdr * btph;
+ struct iphdr * iph;
+ dhcp_options_t opt;
+
+ memset(&opt, 0, sizeof(dhcp_options_t));
+ btph = (struct btphdr *) packet;
+ iph = (struct iphdr *) packet - sizeof(struct udphdr) -
+ sizeof(struct iphdr);
+
+ if (btph->op != 2)
+ return -1; /* It is not a Bootp/DHCP reply */
+ if (btph->xid != dhcp_xid)
+ return -1; /* The transaction ID does not match */
+
+ if (memcmp(btph -> vend, dhcp_magic, 4)) {
+ // It is BootP - RFC 951
+ dhcp_own_ip = htonl(btph -> yiaddr);
+ dhcp_siaddr_ip = htonl(btph -> siaddr);
+ dhcp_server_ip = htonl(iph -> ip_src);
+
+ if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) {
+ strncpy(dhcp_tftp_name, (char *)btph->sname,
+ sizeof(btph->sname));
+ dhcp_tftp_name[sizeof(btph -> sname)] = 0;
+ }
+
+ if (strlen((char *) btph -> file)) {
+ strncpy(dhcp_filename, (char *)btph->file,
+ sizeof(btph->file));
+ dhcp_filename[sizeof(btph -> file)] = 0;
+ }
+
+ dhcp_state = DHCP_STATE_SUCCESS;
+ return 0;
+ }
+
+
+ // decode options
+ if (!dhcp_decode_options(btph -> vend, packetsize -
+ sizeof(struct btphdr) + sizeof(btph -> vend),
+ &opt)) {
+ return -1; // can't decode options
+ }
+
+ if (opt.overload) {
+ int16_t decode_res = 0;
+ uint8_t options[1024]; // buffer for merged options
+ uint32_t opt_len;
+
+ // move 1-st part of options from vend field into buffer
+ opt_len = packetsize - sizeof(struct btphdr) +
+ sizeof(btph -> vend) - 4;
+ memcpy(options, btph -> vend, opt_len + 4);
+
+ // add other parts
+ switch (opt.overload) {
+ case DHCP_OVERLOAD_FILE:
+ decode_res = dhcp_merge_options(options + 4, &opt_len,
+ btph -> file,
+ sizeof(btph -> file));
+ break;
+ case DHCP_OVERLOAD_SNAME:
+ decode_res = dhcp_merge_options(options + 4, &opt_len,
+ btph -> sname,
+ sizeof(btph -> sname));
+ break;
+ case DHCP_OVERLOAD_BOTH:
+ decode_res = dhcp_merge_options(options + 4, &opt_len,
+ btph -> file,
+ sizeof(btph -> file));
+ if (!decode_res)
+ break;
+ decode_res = dhcp_merge_options(options + 4, &opt_len,
+ btph -> sname,
+ sizeof(btph -> sname));
+ break;
+ }
+
+ if (!decode_res)
+ return -1; // bad options in sname/file fields
+
+ // decode merged options
+ if (!dhcp_decode_options(options, opt_len + 4, &opt)) {
+ return -1; // can't decode options
+ }
+ }
+
+ if (!opt.msg_type) {
+ // It is BootP with Extensions - RFC 1497
+ // retrieve conf. settings from BootP - reply
+ dhcp_own_ip = htonl(btph -> yiaddr);
+ dhcp_siaddr_ip = htonl(btph -> siaddr);
+ if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) {
+ strncpy(dhcp_tftp_name, (char *)btph->sname,
+ sizeof(btph->sname));
+ dhcp_tftp_name[sizeof(btph -> sname)] = 0;
+ }
+
+ if (strlen((char *) btph -> file)) {
+ strncpy(dhcp_filename, (char *)btph->file,
+ sizeof(btph->file));
+ dhcp_filename[sizeof(btph -> file)] = 0;
+ }
+
+ // retrieve DHCP-server IP from IP-header
+ dhcp_server_ip = iph -> htonl(ip_src);
+
+ dhcp_state = DHCP_STATE_SUCCESS;
+ }
+ else {
+ // It is DHCP - RFC 2131 & RFC 2132
+ // opt contains parameters from server
+ switch (dhcp_state) {
+ case DHCP_STATE_SELECT :
+ if (opt.msg_type == DHCPOFFER) {
+ dhcp_own_ip = htonl(btph -> yiaddr);
+ dhcp_server_ip = opt.server_ID;
+ dhcp_send_request(fd);
+ dhcp_state = DHCP_STATE_REQUEST;
+ }
+ return 0;
+ case DHCP_STATE_REQUEST :
+ switch (opt.msg_type) {
+ case DHCPNACK :
+ dhcp_own_ip = 0;
+ dhcp_server_ip = 0;
+ dhcp_state = DHCP_STATE_FAULT;
+ break;
+ case DHCPACK :
+ dhcp_own_ip = htonl(btph -> yiaddr);
+ dhcp_server_ip = opt.server_ID;
+ dhcp_siaddr_ip = htonl(btph -> siaddr);
+ if (opt.flag[DHCP_TFTP_SERVER]) {
+ strcpy(dhcp_tftp_name, opt.tftp_server);
+ }
+ else {
+ dhcp_filename[0] = 0;
+ if ((opt.overload != DHCP_OVERLOAD_SNAME &&
+ opt.overload != DHCP_OVERLOAD_BOTH) &&
+ !dhcp_siaddr_ip) {
+ strncpy(dhcp_tftp_name,
+ (char *) btph->sname,
+ sizeof(btph -> sname));
+ dhcp_tftp_name[sizeof(btph->sname)] = 0;
+ }
+ }
+
+ if (opt.flag[DHCP_BOOTFILE]) {
+ strcpy(dhcp_filename, opt.bootfile);
+ }
+ else {
+ dhcp_filename[0] = 0;
+ if (opt.overload != DHCP_OVERLOAD_FILE &&
+ opt.overload != DHCP_OVERLOAD_BOTH &&
+ strlen((char *)btph->file)) {
+ strncpy(dhcp_filename,
+ (char *) btph->file,
+ sizeof(btph->file));
+ dhcp_filename[sizeof(btph -> file)] = 0;
+ }
+ }
+
+ dhcp_state = DHCP_STATE_SUCCESS;
+ break;
+ default:
+ break; // Unused DHCP-message - do nothing
+ }
+ break;
+ default :
+ return -1; // Illegal DHCP-client state
+ }
+ }
+
+ if (dhcp_state == DHCP_STATE_SUCCESS) {
+
+ // initialize network entity with real own_ip
+ // to be able to answer for foreign requests
+ set_ipv4_address(dhcp_own_ip);
+
+ if(response_buffer) {
+ if(packetsize <= 1720)
+ memcpy(response_buffer, packet, packetsize);
+ else
+ memcpy(response_buffer, packet, 1720);
+ }
+
+ /* Subnet mask */
+ if (opt.flag[DHCP_MASK]) {
+ /* Router */
+ if (opt.flag[DHCP_ROUTER]) {
+ set_ipv4_router(opt.router_IP);
+ set_ipv4_netmask(opt.subnet_mask);
+ }
+ }
+
+ /* DNS-server */
+ if (opt.flag[DHCP_DNS]) {
+ dns_init(opt.dns_IP, 0, 4);
+ }
+ }
+
+ return 0;
+}
diff --git a/roms/SLOF/lib/libnet/dhcp.h b/roms/SLOF/lib/libnet/dhcp.h
new file mode 100644
index 000000000..4432c9bbe
--- /dev/null
+++ b/roms/SLOF/lib/libnet/dhcp.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _DHCP_H_
+#define _DHCP_H_
+
+#include <stdint.h>
+#include "tftp.h"
+
+/** \struct btphdr
+ * A header for BootP/DHCP-messages.
+ * For more information see RFC 951 / RFC 2131.
+ */
+struct btphdr {
+ uint8_t op; /**< Identifies is it request (1) or reply (2) */
+ uint8_t htype; /**< HW address type (ethernet usually) */
+ uint8_t hlen; /**< HW address length */
+ uint8_t hops; /**< This info used by relay agents (not used) */
+ uint32_t xid; /**< This ID is used to match queries and replies */
+ uint16_t secs; /**< Unused */
+ uint16_t unused; /**< Unused */
+ uint32_t ciaddr; /**< Client IP address (if client knows it) */
+ uint32_t yiaddr; /**< "Your" (client) IP address */
+ uint32_t siaddr; /**< Next server IP address (TFTP server IP) */
+ uint32_t giaddr; /**< Gateway IP address (used by relay agents) */
+ uint8_t chaddr[16]; /**< Client HW address */
+ uint8_t sname[64]; /**< Server host name (TFTP server name) */
+ uint8_t file[128]; /**< Boot file name */
+ uint8_t vend[64]; /**< Optional parameters field (DHCP-options) */
+};
+
+void dhcpv4_generate_transaction_id(void);
+int bootp(char *ret_buffer, filename_ip_t *, unsigned int);
+int dhcpv4(char *ret_buffer, filename_ip_t *);
+void dhcp_send_release(int fd);
+
+/* Handles DHCP-packets, which are detected by receive_ether. */
+extern int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/dhcpv6.c b/roms/SLOF/lib/libnet/dhcpv6.c
new file mode 100644
index 000000000..e92fc0f41
--- /dev/null
+++ b/roms/SLOF/lib/libnet/dhcpv6.c
@@ -0,0 +1,211 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <time.h>
+#include "ethernet.h"
+#include "ipv6.h"
+#include "udp.h"
+#include "dhcpv6.h"
+#include "tftp.h"
+#include "dns.h"
+
+static uint8_t tid[3];
+static uint32_t dhcpv6_state = -1;
+static filename_ip_t *my_fn_ip;
+
+static struct ip6addr_list_entry all_dhcpv6_ll; /* All DHCPv6 servers address */
+
+void
+dhcpv6_generate_transaction_id(void)
+{
+ /* As per RFC 3315 transaction IDs should be generated randomly */
+ tid[0] = rand();
+ tid[1] = rand();
+ tid[2] = rand();
+}
+
+static void
+send_info_request(int fd)
+{
+ uint8_t ether_packet[ETH_MTU_SIZE];
+ uint32_t payload_length;
+ struct dhcp_message_header *dhcph;
+
+ memset(ether_packet, 0, ETH_MTU_SIZE);
+
+ /* Get an IPv6 packet */
+ payload_length = sizeof(struct udphdr) + sizeof(struct dhcp_message_header);
+ fill_ip6hdr (ether_packet + sizeof(struct ethhdr),
+ payload_length, IPTYPE_UDP,
+ get_ipv6_address(), &(all_dhcpv6_ll.addr));
+ fill_udphdr ( ether_packet + sizeof(struct ethhdr) + sizeof(struct ip6hdr),
+ payload_length, DHCP_CLIENT_PORT, DHCP_SERVER_PORT);
+ dhcph = (struct dhcp_message_header *) (ether_packet +
+ sizeof(struct ethhdr) +
+ sizeof(struct ip6hdr) +
+ sizeof(struct udphdr));
+
+ /* Fill in DHCPv6 data */
+ dhcph->type = DHCP_INFORMATION_REQUEST;
+ memcpy( &(dhcph->transaction_id), &tid, 3);
+ dhcph->option.client_id.code = DHCPV6_OPTION_CLIENTID;
+ dhcph->option.client_id.length = 10;
+ dhcph->option.client_id.duid_type = DUID_LL;
+ dhcph->option.client_id.hardware_type = 1;
+ memcpy( &(dhcph->option.client_id.mac),
+ get_mac_address(), 6);
+ dhcph->option.el_time.code = DHCPV6_OPTION_ELAPSED_TIME;
+ dhcph->option.el_time.length = 2;
+ dhcph->option.el_time.time = 0x190; /* 4000 ms */
+ dhcph->option.option_request_option.code = DHCPV6_OPTION_ORO;
+ dhcph->option.option_request_option.length = DHCPV6_OPTREQUEST_NUMOPTS * 2;
+ dhcph->option.option_request_option.option_code[0] = DHCPV6_OPTION_DNS_SERVERS;
+ dhcph->option.option_request_option.option_code[1] = DHCPV6_OPTION_DOMAIN_LIST;
+ dhcph->option.option_request_option.option_code[2] = DHCPV6_OPTION_BOOT_URL;
+
+ send_ipv6(fd, ether_packet + sizeof(struct ethhdr),
+ sizeof(struct ip6hdr) + sizeof(struct udphdr)
+ + sizeof(struct dhcp_message_header));
+}
+
+static int32_t
+dhcpv6_attempt(int fd)
+{
+ int sec;
+
+ // Send information request
+ send_info_request(fd);
+
+ dhcpv6_state = DHCPV6_STATE_SELECT;
+
+ // setting up a timer with a timeout of two seconds
+ for (sec = 0; sec < 2; sec++) {
+ set_timer(TICKS_SEC);
+ do {
+ receive_ether(fd);
+
+ // Wait until client will switch to Final state or Timeout occurs
+ switch (dhcpv6_state) {
+ case DHCP_STATUSCODE_SUCCESS:
+ return 1;
+ case DHCP_STATUSCODE_UNSPECFAIL: //FIXME
+ return 0;
+ }
+ } while (get_timer() > 0);
+ }
+
+ // timeout
+ return 0;
+}
+
+int32_t
+dhcpv6 ( char *ret_buffer, void *fn_ip)
+{
+ int fd;
+
+ all_dhcpv6_ll.addr.part.prefix = 0xff02000000000000ULL;
+ all_dhcpv6_ll.addr.part.interface_id = 0x10002ULL;
+
+ my_fn_ip = (filename_ip_t *) fn_ip;
+ fd = my_fn_ip->fd;
+
+ if( !dhcpv6_attempt(fd)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void dhcp6_process_options (uint8_t *option, int32_t option_length)
+{
+ struct dhcp_boot_url *option_boot_url;
+ struct client_identifier *option_clientid;
+ struct server_identifier *option_serverid;
+ struct dhcp_dns *option_dns;
+ struct dhcp_dns_list *option_dns_list;
+ struct dhcp6_gen_option *option_gen;
+ char buffer[256];
+
+ while (option_length > 0) {
+ switch ((uint16_t) *(option+1)) {
+ case DHCPV6_OPTION_CLIENTID:
+ option_clientid = (struct client_identifier *) option;
+ option = option + option_clientid->length + 4;
+ option_length = option_length - option_clientid->length - 4;
+ break;
+ case DHCPV6_OPTION_SERVERID:
+ option_serverid = (struct server_identifier *) option;
+ option = option + option_serverid->length + 4;
+ option_length = option_length - option_serverid->length - 4;
+ break;
+ case DHCPV6_OPTION_DNS_SERVERS:
+ option_dns = (struct dhcp_dns *) option;
+ option = option + option_dns->length + 4;
+ option_length = option_length - option_dns->length - 4;
+ memcpy( &(my_fn_ip->dns_ip6),
+ option_dns->p_ip6,
+ IPV6_ADDR_LENGTH);
+ dns_init(0, option_dns->p_ip6, 6);
+ break;
+ case DHCPV6_OPTION_DOMAIN_LIST:
+ option_dns_list = (struct dhcp_dns_list *) option;
+ option = option + option_dns_list->length + 4;
+ option_length = option_length - option_dns_list->length - 4;
+ break;
+ case DHCPV6_OPTION_BOOT_URL:
+ option_boot_url = (struct dhcp_boot_url *) option;
+ option = option + option_boot_url->length + 4;
+ option_length = option_length - option_boot_url->length - 4;
+ strncpy((char *)buffer,
+ (const char *)option_boot_url->url,
+ (size_t)option_boot_url->length);
+ buffer[option_boot_url->length] = 0;
+ if (parse_tftp_args(buffer,
+ (char *)my_fn_ip->server_ip6.addr,
+ my_fn_ip->filename, my_fn_ip->fd,
+ option_boot_url->length) == -1)
+ return;
+ break;
+ default:
+ option_gen = (struct dhcp6_gen_option *) option;
+ option = option + option_gen->length + 4;
+ option_length = option_length - option_gen->length - 4;
+ }
+ }
+}
+
+uint32_t
+handle_dhcpv6(uint8_t * packet, int32_t packetsize)
+{
+
+ uint8_t *first_option;
+ int32_t option_length;
+ struct dhcp_message_reply *reply;
+ reply = (struct dhcp_message_reply *) packet;
+
+ if (memcmp(reply->transaction_id, tid, 3))
+ return -1; /* Wrong transaction ID */
+
+ if (reply->type == 7)
+ dhcpv6_state = DHCP_STATUSCODE_SUCCESS;
+
+ first_option = packet + 4;
+ option_length = packet + packetsize - first_option;
+ dhcp6_process_options(first_option, option_length);
+
+ return 0;
+}
diff --git a/roms/SLOF/lib/libnet/dhcpv6.h b/roms/SLOF/lib/libnet/dhcpv6.h
new file mode 100644
index 000000000..02747a0e7
--- /dev/null
+++ b/roms/SLOF/lib/libnet/dhcpv6.h
@@ -0,0 +1,154 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _DHCPV6_H_
+#define _DHCPV6_H_
+
+#include <stdint.h>
+#include "ethernet.h"
+
+#define DHCPV6_STATELESS 0
+#define DHCPV6_STATEFUL 1
+
+/* DHCP port numbers */
+#define DHCP_CLIENT_PORT 546
+#define DHCP_SERVER_PORT 547
+
+/* DHCPv6 message types */
+#define DHCP_SOLICIT 1
+#define DHCP_ADVERTISE 2
+#define DHCP_REQUEST 3
+#define DHCP_CONFIRM 4
+#define DHCP_RENEW 5
+#define DHCP_REBIND 6
+#define DHCP_REPLY 7
+#define DHCP_RELEASE 8
+#define DHCP_DECLINE 9
+#define DHCP_RECONFIGURE 10
+#define DHCP_INFORMATION_REQUEST 11
+#define RELAY_FORW 12
+#define RELAY_REPL 13
+
+/* DHCPv6 option types */
+#define DHCPV6_OPTION_CLIENTID 0x0001
+#define DHCPV6_OPTION_SERVERID 0x0002
+#define DHCPV6_OPTION_IA_NA 3
+#define DHCPV6_OPTION_IA_TA 4
+#define DHCPV6_OPTION_IAADDR 5
+#define DHCPV6_OPTION_ORO 6
+#define DHCPV6_OPTION_PREFEREN 7
+#define DHCPV6_OPTION_ELAPSED_TIME 8
+#define DHCPV6_OPTION_RELAY_MS 9
+#define DHCPV6_OPTION_AUTH 11
+#define DHCPV6_OPTION_UNICAST 12
+#define DHCPV6_OPTION_STATUS_C 13
+#define DHCPV6_OPTION_RAPID_CO 14
+#define DHCPV6_OPTION_USER_CLA 15
+#define DHCPV6_OPTION_VENDOR_C 16
+#define DHCPV6_OPTION_VENDOR_O 17
+#define DHCPV6_OPTION_INTERFAC 18
+#define DHCPV6_OPTION_RECONF_M 19
+#define DHCPV6_OPTION_RECONF_A 20
+#define DHCPV6_OPTION_DNS_SERVERS 23
+#define DHCPV6_OPTION_DOMAIN_LIST 24
+#define DHCPV6_OPTION_BOOT_URL 59
+
+/* DHCPv6 status codes */
+#define DHCP_STATUSCODE_SUCCESS 0
+#define DHCP_STATUSCODE_UNSPECFAIL 1
+#define DHCP_STATUSCODE_NOADDRAVAIL 2
+#define DHCP_STATUSCODE_NOBINDING 3
+#define DHCP_STATUSCODE_NOTONLINK 4
+#define DHCP_STATUSCODE_USEMULTICAST 5
+#define DHCPV6_STATE_SELECT 6
+
+/* DUID types */
+#define DUID_LLT 1 /* DUID based on Link-layer Address Plus Time */
+#define DUID_EN 2 /* DUID based on Assigned by Vendor Based on Enterprise Number */
+#define DUID_LL 3 /* DUID based on Link-layer Address */
+
+/* Prototypes */
+void dhcpv6_generate_transaction_id(void);
+int32_t dhcpv6 ( char *ret_buffer, void *fn_ip);
+uint32_t handle_dhcpv6(uint8_t * , int32_t);
+
+struct dhcp6_gen_option {
+ uint16_t code;
+ uint16_t length;
+};
+
+struct client_identifier {
+ uint16_t code;
+ uint16_t length;
+ uint16_t duid_type;
+ uint16_t hardware_type;
+ uint8_t mac[6];
+};
+
+struct server_identifier {
+ uint16_t code;
+ uint16_t length;
+ uint16_t duid_type;
+ uint16_t hardware_type;
+ uint32_t time;
+ uint8_t mac[6];
+};
+
+#define DHCPV6_OPTREQUEST_NUMOPTS 3
+
+struct dhcp_info_request {
+ struct client_identifier client_id;
+ struct elapsed_time {
+ uint16_t code;
+ uint16_t length;
+ uint16_t time;
+ } el_time;
+ struct option_request {
+ uint16_t code;
+ uint16_t length;
+ uint16_t option_code[DHCPV6_OPTREQUEST_NUMOPTS];
+ } option_request_option;
+};
+
+struct dhcp_message_header {
+ uint8_t type; /* Message type */
+ uint8_t transaction_id[3]; /* Transaction id */
+ struct dhcp_info_request option;
+};
+
+struct dhcp_dns {
+ uint16_t code;
+ uint16_t length;
+ uint8_t p_ip6[16];
+ uint8_t s_ip6[16];
+}__attribute((packed));
+
+struct dhcp_dns_list {
+ uint16_t code;
+ uint16_t length;
+ uint8_t domain[256];
+}__attribute((packed));
+
+struct dhcp_boot_url {
+ uint16_t type;
+ uint16_t length;
+ uint8_t url[256];
+};
+
+struct dhcp_message_reply {
+ uint8_t type; /* Message type */
+ uint8_t transaction_id[3]; /* Transaction id */
+ struct client_identifier client_id;
+ struct server_identifier server_id;
+};
+
+#endif
diff --git a/roms/SLOF/lib/libnet/dns.c b/roms/SLOF/lib/libnet/dns.c
new file mode 100644
index 000000000..a7313a9b3
--- /dev/null
+++ b/roms/SLOF/lib/libnet/dns.c
@@ -0,0 +1,526 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/********************** DEFINITIONS & DECLARATIONS ***********************/
+
+#include <dns.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+
+#include <ethernet.h>
+#include <ipv4.h>
+#include <ipv6.h>
+#include <udp.h>
+
+#define DNS_FLAG_MSGTYPE 0xF800 /**< Message type mask (opcode) */
+#define DNS_FLAG_SQUERY 0x0000 /**< Standard query type */
+#define DNS_FLAG_SRESPONSE 0x8000 /**< Standard response type */
+#define DNS_FLAG_RD 0x0100 /**< Recursion desired flag */
+#define DNS_FLAG_RCODE 0x000F /**< Response code mask
+ (stores err.cond.) code */
+#define DNS_RCODE_NERROR 0 /**< "No errors" code */
+
+#define DNS_QTYPE_A 1 /**< A 32-bit IP record type */
+#define DNS_QTYPE_AAAA 0x1c /**< 128-bit IPv6 record type */
+#define DNS_QTYPE_CNAME 5 /**< Canonical name record type */
+
+#define DNS_QCLASS_IN 1 /**< Query class for internet msgs */
+
+/** \struct dnshdr
+ * A header for DNS-messages (see RFC 1035, paragraph 4.1.1).
+ * <p>
+ * DNS-message consist of DNS-header and 4 optional sections,
+ * arranged in the following order:<ul>
+ * <li> DNS-header
+ * <li> question section
+ * <li> answer section
+ * <li> authority section
+ * <li> additional section
+ * </ul>
+ */
+struct dnshdr {
+ uint16_t id; /**< an identifier used to match up replies */
+ uint16_t flags; /**< contains op_code, err_code, etc. */
+ uint16_t qdcount; /**< specifies the number of entries in the
+ question section */
+ uint16_t ancount; /**< specifies the number of entries in the
+ answer section */
+ uint16_t nscount; /**< specifies the number of entries in the
+ authority section */
+ uint16_t arcount; /**< specifies the number of entries in the
+ additional section */
+};
+
+
+/***************************** PROTOTYPES ********************************/
+
+static void
+dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version);
+
+static void
+fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version);
+
+static uint8_t *
+dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name);
+
+static int8_t
+urltohost(char * url, char * host_name);
+
+static int8_t
+hosttodomain(char * host_name, char * domain_name);
+
+/**************************** LOCAL VARIABLES ****************************/
+
+static uint8_t ether_packet[ETH_MTU_SIZE];
+static int32_t dns_server_ip = 0;
+static uint8_t dns_server_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static int32_t dns_result_ip = 0;
+static uint8_t dns_result_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static int8_t dns_error = 0; /**< Stores error code or 0 */
+static int8_t dns_domain_name[0x100]; /**< Raw domain name */
+static int8_t dns_domain_cname[0x100]; /**< Canonical domain name */
+
+/**************************** IMPLEMENTATION *****************************/
+
+/**
+ * DNS: Initialize the environment for DNS client.
+ * To perfrom DNS-queries use the function dns_get_ip.
+ *
+ * @param device_socket a socket number used to send and receive packets
+ * @param server_ip DNS-server IPv4 address (e.g. 127.0.0.1)
+ * @return TRUE in case of successful initialization;
+ * FALSE in case of fault (e.g. can't obtain MAC).
+ * @see dns_get_ip
+ */
+int8_t
+dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version)
+{
+ if(ip_version == 6)
+ memcpy(dns_server_ipv6, _dns_server_ipv6, 16);
+ else
+ dns_server_ip = _dns_server_ip;
+ return 0;
+}
+
+/**
+ * DNS: For given URL retrieves IPv4/IPv6 from DNS-server.
+ * <p>
+ * URL can be given in one of the following form: <ul>
+ * <li> scheme with full path with (without) user and password
+ * <br>(e.g. "http://user:pass@www.host.org/url-path");
+ * <li> host name with url-path
+ * <br>(e.g. "www.host.org/url-path");
+ * <li> nothing but host name
+ * <br>(e.g. "www.host.org");
+ * </ul>
+ *
+ * @param fd socket descriptor
+ * @param url the URL to be resolved
+ * @param domain_ip In case of SUCCESS stores extracted IP.
+ * In case of FAULT stores zeros (0.0.0.0).
+ * @return TRUE - IP successfuly retrieved;
+ * FALSE - error condition occurs.
+ */
+int8_t
+dns_get_ip(int fd, char* url, uint8_t * domain_ip, uint8_t ip_version)
+{
+ /* this counter is used so that we abort after 30 DNS request */
+ int32_t i;
+ /* this buffer stores host name retrieved from url */
+ static int8_t host_name[0x100];
+
+ (* domain_ip) = 0;
+
+ // Retrieve host name from URL
+ if (!urltohost(url, (char *) host_name)) {
+ printf("\nERROR:\t\t\tBad URL!\n");
+ return 0;
+ }
+
+ // Reformat host name into a series of labels
+ if (!hosttodomain((char *) host_name, (char *) dns_domain_name)) {
+ printf("\nERROR:\t\t\tBad host name!\n");
+ return 0;
+ }
+
+ // Check if DNS server is presented and accessible
+ if (dns_server_ip == 0) {
+ printf("\nERROR:\t\t\tCan't resolve domain name "
+ "(DNS server is not presented)!\n");
+ return 0;
+ }
+
+ // Use DNS-server to obtain IP
+ if (ip_version == 6)
+ memset(dns_result_ipv6, 0, 16);
+ else
+ dns_result_ip = 0;
+ dns_error = 0;
+ strcpy((char *) dns_domain_cname, "");
+
+ for(i = 0; i < 30; ++i) {
+ // Use canonical name in case we obtained it
+ if (strlen((char *) dns_domain_cname))
+ dns_send_query(fd, dns_domain_cname, ip_version);
+ else
+ dns_send_query(fd, dns_domain_name, ip_version);
+
+ // setting up a timer with a timeout of one seconds
+ set_timer(TICKS_SEC);
+ do {
+ receive_ether(fd);
+ if (dns_error)
+ return 0; // FALSE - error
+ if ((dns_result_ip != 0) && (ip_version == 4)) {
+ memcpy(domain_ip, &dns_result_ip, 4);
+ return 1; // TRUE - success (domain IP retrieved)
+ }
+ else if ((dns_result_ipv6[0] != 0) && (ip_version == 6)) {
+ memcpy(domain_ip, dns_result_ipv6, 16);
+ return 1; // TRUE - success (domain IP retrieved)
+ }
+ } while (get_timer() > 0);
+ }
+
+ printf("\nGiving up after %d DNS requests\n", i);
+ return 0; // FALSE - domain name wasn't retrieved
+}
+
+/**
+ * DNS: Handles DNS-messages according to Receive-handle diagram.
+ * Sets dns_result_ip for given dns_domain_name (see dns_get_ip)
+ * or signals error condition occurs during DNS-resolving process
+ * by setting dns_error flag.
+ *
+ * @param packet DNS-packet to be handled
+ * @param packetsize length of the packet
+ * @return ZERO - packet handled successfully;
+ * NON ZERO - packet was not handled (e.g. bad format)
+ * @see dns_get_ip
+ * @see receive_ether
+ * @see dnshdr
+ */
+int32_t
+handle_dns(uint8_t * packet, int32_t packetsize)
+{
+ struct dnshdr * dnsh = (struct dnshdr *) packet;
+ uint8_t * resp_section = packet + sizeof(struct dnshdr);
+ /* This string stores domain name from DNS-packets */
+ static int8_t handle_domain_name[0x100];
+ int i;
+
+ // verify ID - is it response for our query?
+ if (dnsh -> id != htons(0x1234))
+ return 0;
+
+ // Is it DNS response?
+ if ((dnsh -> flags & htons(DNS_FLAG_MSGTYPE)) != htons(DNS_FLAG_SRESPONSE))
+ return 0;
+
+ // Is error condition occurs? (check error field in incoming packet)
+ if ((dnsh -> flags & htons(DNS_FLAG_RCODE)) != DNS_RCODE_NERROR) {
+ dns_error = 1;
+ return 0;
+ }
+
+ /* Pass all (qdcount) records in question section */
+
+ for (i = 0; i < htons(dnsh -> qdcount); i++) {
+ // pass QNAME
+ resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section,
+ handle_domain_name);
+ if (resp_section == NULL) {
+ return -1; // incorrect domain name (bad packet)
+ }
+ // pass QTYPE & QCLASS
+ resp_section += 4;
+ }
+
+ /* Handle all (ancount) records in answer section */
+
+ for (i = 0; i < htons(dnsh -> ancount); i++) {
+ // retrieve domain name from the packet
+ resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section,
+ handle_domain_name);
+
+ if (resp_section == NULL) {
+ return -1; // incorrect domain name (bad packet)
+ }
+
+ // Check the class of the query (should be IN for Internet)
+ if (* (uint16_t *) (resp_section + 2) == htons(DNS_QCLASS_IN)) {
+ // check if retrieved name fit raw or canonical domain name
+ if (!strcmp((char *) handle_domain_name, (char *) dns_domain_name) ||
+ !strcmp((char *) handle_domain_name, (char *) dns_domain_cname)) {
+ switch (htons(* (uint16_t *) resp_section)) {
+
+ case DNS_QTYPE_A :
+ // rdata contains IP
+ dns_result_ip = htonl(* (uint32_t *) (resp_section + 10));
+ return 0; // IP successfully obtained
+
+ case DNS_QTYPE_CNAME :
+ // rdata contains canonical name, store it for further requests
+ if (dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section + 10,
+ dns_domain_cname) == NULL) {
+ // incorrect domain name (bad packet)
+ return -1;
+ }
+ break;
+ case DNS_QTYPE_AAAA :
+ memcpy(dns_result_ipv6, (resp_section + 10), 16);
+ return 0; // IP successfully obtained
+ }
+ }
+ // continue with next record in answer section
+ resp_section += htons(* (uint16_t *) (resp_section + 8)) + 10;
+ }
+ }
+ return 0; // Packet successfully handled but IP wasn't obtained
+}
+
+/**
+ * DNS: Sends a standard DNS-query (read request package) to a DNS-server.
+ * DNS-server respones with host IP or signals some error condition.
+ * Responses from the server are handled by handle_dns function.
+ *
+ * @param fd socket descriptor
+ * @param domain_name the domain name given as series of labels preceded
+ * with length(label) and terminated with 0
+ * <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0")
+ * @see handle_dns
+ */
+static void
+dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version)
+{
+ int qry_len = strlen((char *) domain_name) + 5;
+ int iphdr_len = (ip_version == 4) ? sizeof(struct iphdr) : sizeof(struct ip6hdr);
+ ip6_addr_t server_ipv6;
+
+ uint32_t packetsize = iphdr_len +
+ sizeof(struct udphdr) + sizeof(struct dnshdr) +
+ qry_len;
+
+ memset(ether_packet, 0, packetsize);
+ fill_dnshdr(&ether_packet[
+ iphdr_len + sizeof(struct udphdr)],
+ domain_name,
+ ip_version);
+ fill_udphdr(&ether_packet[iphdr_len],
+ sizeof(struct dnshdr) +
+ sizeof(struct udphdr) + qry_len,
+ UDPPORT_DNSC, UDPPORT_DNSS);
+ if (ip_version == 4) {
+ fill_iphdr(ether_packet,
+ sizeof(struct dnshdr) + sizeof(struct udphdr) +
+ iphdr_len + qry_len,
+ IPTYPE_UDP, 0, dns_server_ip);
+ } else {
+ memcpy(server_ipv6.addr, dns_server_ipv6, 16);
+ fill_ip6hdr(ether_packet,
+ sizeof(struct dnshdr) + sizeof(struct udphdr) + qry_len,
+ IPTYPE_UDP, get_ipv6_address(),
+ &server_ipv6);
+ }
+
+ send_ip(fd, ether_packet, packetsize);
+}
+
+/**
+ * DNS: Creates standard DNS-query package. Places DNS-header
+ * and question section in a packet and fills it with
+ * corresponding information.
+ * <p>
+ * Use this function with similar functions for other network layers
+ * (fill_udphdr, fill_iphdr, fill_ethhdr).
+ *
+ * @param packet Points to the place where ARP-header must be placed.
+ * @param domain_name the domain name given as series of labels preceded
+ * with length(label) and terminated with 0
+ * <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0")
+ * @see fill_udphdr
+ * @see fill_iphdr
+ * @see fill_ethhdr
+ */
+static void
+fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version)
+{
+ struct dnshdr * dnsh = (struct dnshdr *) packet;
+ uint8_t * qry_section = packet + sizeof(struct dnshdr);
+
+ dnsh -> id = htons(0x1234);
+ dnsh -> flags = htons(DNS_FLAG_SQUERY) | htons(DNS_FLAG_RD);
+ dnsh -> qdcount = htons(1);
+
+ strcpy((char *) qry_section, (char *) domain_name);
+ qry_section += strlen((char *) domain_name) + 1;
+
+ // fill QTYPE (ask for IP)
+ if (ip_version == 4)
+ * (uint16_t *) qry_section = htons(DNS_QTYPE_A);
+ else
+ * (uint16_t *) qry_section = htons(DNS_QTYPE_AAAA);
+ qry_section += 2;
+ // fill QCLASS (IN is a standard class for Internet)
+ * (uint16_t *) qry_section = htons(DNS_QCLASS_IN);
+}
+
+/**
+ * DNS: Extracts domain name from the question or answer section of
+ * the DNS-message. This function is need to support message
+ * compression requirement (see RFC 1035, paragraph 4.1.4).
+ *
+ * @param dnsh Points at the DNS-header.
+ * @param head Points at the beginning of the domain_name
+ * which has to be extracted.
+ * @param domain_name In case of SUCCESS this string stores extracted name.
+ * In case of FAULT this string is empty.
+ * @return NULL in case of FAULT (domain name > 255 octets);
+ * otherwise pointer to the data following the name.
+ * @see dnshdr
+ */
+static uint8_t *
+dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name)
+{
+ int8_t * tail = domain_name;
+ int8_t * ptr = head;
+ int8_t * next_section = NULL;
+
+ while (1) {
+ if ((ptr[0] & 0xC0) == 0xC0) {
+ // message compressed (reference is used)
+ next_section = ptr + 2;
+ ptr = (int8_t *) dnsh + (htons(* (uint16_t *) ptr) & 0x3FFF);
+ continue;
+ }
+ if (ptr[0] == 0) {
+ // message termination
+ tail[0] = 0;
+ ptr += 1;
+ break;
+ }
+ // maximum length for domain name is 255 octets w/o termination sym
+ if (tail - domain_name + ptr[0] + 1 > 255) {
+ strcpy((char *) domain_name, "");
+ return NULL;
+ }
+ memcpy(tail, ptr, ptr[0] + 1);
+ tail += ptr[0] + 1;
+ ptr += ptr[0] + 1;
+ }
+
+ if (next_section == NULL)
+ next_section = ptr;
+
+ return (uint8_t *) next_section;
+}
+
+/**
+ * DNS: Parses URL and returns host name.
+ * Input string can be given as: <ul>
+ * <li> scheme with full path with (without) user and password
+ * <br>(e.g. "http://user:pass@www.host.org/url-path");
+ * <li> host name with url-path
+ * <br>(e.g. "www.host.org/url-path");
+ * <li> nothing but host name
+ * <br>(e.g. "www.host.org");
+ * </ul>
+ *
+ * @param url string that stores incoming URL
+ * @param host_name In case of SUCCESS this string stores the host name,
+ * In case of FAULT this string is empty.
+ * @return TRUE - host name retrieved,
+ * FALSE - host name > 255 octets or empty.
+ */
+static int8_t
+urltohost(char * url, char * host_name)
+{
+ uint16_t length1;
+ uint16_t length2;
+
+ strcpy(host_name, "");
+
+ if (strstr(url, "://") != NULL)
+ url = strstr(url, "//") + 2; // URL
+
+ if (strstr(url, "@") != NULL) // truncate user & password
+ url = strstr(url, "@") + 1;
+
+ if (strstr(url, "/") != NULL) // truncate url path
+ length1 = strstr(url, "/") - url;
+ else
+ length1 = strlen(url);
+
+ if (strstr(url, ":") != NULL) // truncate port path
+ length2 = strstr(url, ":") - url;
+ else
+ length2 = strlen(url);
+
+ if(length1 > length2)
+ length1 = length2;
+
+ if (length1 == 0)
+ return 0; // string is empty
+ if(length1 >= 256)
+ return 0; // host name is too big
+
+ strncpy(host_name, url, length1);
+ host_name[length1] = 0;
+
+ return 1; // Host name is retrieved
+}
+
+/**
+ * DNS: Transforms host name string into a series of labels
+ * each of them preceded with length(label). 0 is a terminator.
+ * "www.domain.dom" -> "\3,w,w,w,\6,d,o,m,a,i,n,\3,c,o,m,\0"
+ * <p>
+ * This format is used in DNS-messages.
+ *
+ * @param host_name incoming string with the host name
+ * @param domain_name resulting string with series of labels
+ * or empty string in case of FAULT
+ * @return TRUE - host name transformed,
+ * FALSE - host name > 255 octets or label > 63 octets.
+ */
+static int8_t
+hosttodomain(char * host_name, char * domain_name)
+{
+ char * domain_iter = domain_name;
+ char * host_iter = host_name;
+
+ strcpy(domain_name, "");
+
+ if(strlen(host_name) > 255)
+ return 0; // invalid host name (refer to RFC 1035)
+
+ for(; 1; ++host_iter) {
+ if(*host_iter != '.' && *host_iter != 0)
+ continue;
+ *domain_iter = host_iter - host_name;
+ if (*domain_iter > 63) {
+ strcpy(domain_name, "");
+ return 0; // invalid host name (refer to RFC 1035)
+ }
+ ++domain_iter;
+ strncpy(domain_iter, host_name, host_iter - host_name);
+ domain_iter += (host_iter - host_name);
+ if(*host_iter == 0) {
+ *domain_iter = 0;
+ break;
+ }
+ host_name = host_iter + 1;
+ }
+ return 1; // ok
+}
diff --git a/roms/SLOF/lib/libnet/dns.h b/roms/SLOF/lib/libnet/dns.h
new file mode 100644
index 000000000..b8756afca
--- /dev/null
+++ b/roms/SLOF/lib/libnet/dns.h
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#ifndef _DNS_H_
+#define _DNS_H_
+
+#include <stdint.h>
+
+/* Initialize the environment for DNS client. */
+extern int8_t dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version);
+
+/* For given URL retrieves IPv4 from DNS-server. */
+extern int8_t dns_get_ip(int fd, char * url, uint8_t * domain_ip, uint8_t ip_version);
+
+/* Handles DNS-packets, which are detected by receive_ether. */
+extern int32_t handle_dns(uint8_t * packet, int32_t packetsize);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/ethernet.c b/roms/SLOF/lib/libnet/ethernet.c
new file mode 100644
index 000000000..1e03a0bf3
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ethernet.c
@@ -0,0 +1,189 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+/******************************* ALGORITHMS ******************************/
+
+/** \file netbase.c <pre>
+ * *********************** Receive-handle diagram *************************
+ *
+ * Note: Every layer calls out required upper layer
+ *
+ * lower
+ * | MAC/LLC Receive packet (receive_ether)
+ * | |
+ * | NETWORK +-----------+---------+
+ * | | |
+ * | IPv4 (handle_ipv4) IPv6 (handle_ipv4)
+ * | ARP (handle_arp) ICMP & NDP
+ * | ICMP |
+ * | | |
+ * | +---------+---------+
+ * | |
+ * | TRANSPORT +---------+---------+
+ * | | |
+ * | TCP (handle_tcp) UDP (handle_udp)
+ * | |
+ * | APPLICATION +----------------+-----------+
+ * V | |
+ * upper DNS (handle_dns) BootP / DHCP (handle_bootp_client)
+ *
+ * ************************************************************************
+ * </pre> */
+
+
+/************************ DEFINITIONS & DECLARATIONS *********************/
+
+#include <ethernet.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <ipv4.h>
+#include <ipv6.h>
+
+
+/****************************** LOCAL VARIABLES **************************/
+
+static uint8_t ether_packet[ETH_MTU_SIZE];
+static uint8_t own_mac[6] = {0, 0, 0, 0, 0, 0};
+static uint8_t multicast_mac[] = {0x01, 0x00, 0x5E};
+static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+/****************************** IMPLEMENTATION ***************************/
+
+/**
+ * Ethernet: Set the own MAC address to initializes ethernet layer.
+ *
+ * @param own_mac own hardware-address (MAC)
+ */
+void set_mac_address(const uint8_t * _own_mac)
+{
+ if (_own_mac)
+ memcpy(own_mac, _own_mac, 6);
+ else
+ memset(own_mac, 0, 6);
+}
+
+/**
+ * Ethernet: Set the own MAC address to initializes ethernet layer.
+ *
+ * @return own hardware-address (MAC)
+ */
+const uint8_t *get_mac_address(void)
+{
+ return own_mac;
+}
+
+/**
+ * Ethernet: Check if given multicast address is a multicast MAC address
+ * starting with 0x3333
+ *
+ * @return true or false
+ */
+static uint8_t is_multicast_mac(uint8_t * mac)
+{
+
+ uint16_t mc = 0x3333;
+ if (memcmp(mac, &mc, 2) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * Ethernet: Receives an ethernet-packet and handles it according to
+ * Receive-handle diagram.
+ *
+ * @param fd socket fd
+ * @return ZERO - packet was handled or no packets received;
+ * NON ZERO - error condition occurs.
+ */
+int32_t receive_ether(int fd)
+{
+ int32_t bytes_received;
+ struct ethhdr * ethh;
+
+ memset(ether_packet, 0, ETH_MTU_SIZE);
+ bytes_received = recv(fd, ether_packet, ETH_MTU_SIZE, 0);
+
+ if (!bytes_received) // No messages
+ return 0;
+
+ if (bytes_received < 0)
+ return -1; /* recv() failed */
+
+ if ((size_t) bytes_received < sizeof(struct ethhdr))
+ return -1; // packet is too small
+
+ ethh = (struct ethhdr *) ether_packet;
+
+ if(memcmp(ethh->dest_mac, broadcast_mac, 6) != 0
+ && memcmp(ethh->dest_mac, multicast_mac, 3) != 0
+ && memcmp(ethh->dest_mac, own_mac, 6 ) != 0
+ && !is_multicast_mac(ethh->dest_mac))
+ return -1; // packet is too small
+
+ switch (htons(ethh -> type)) {
+ case ETHERTYPE_IP:
+ return handle_ipv4(fd, (uint8_t*) (ethh + 1),
+ bytes_received - sizeof(struct ethhdr));
+
+ case ETHERTYPE_IPv6:
+ return handle_ipv6(fd, ether_packet + sizeof(struct ethhdr),
+ bytes_received - sizeof(struct ethhdr));
+
+ case ETHERTYPE_ARP:
+ return handle_arp(fd, (uint8_t*) (ethh + 1),
+ bytes_received - sizeof(struct ethhdr));
+ default:
+ break;
+ }
+ return -1; // unknown protocol
+}
+
+/**
+ * Ethernet: Sends an ethernet frame via the initialized file descriptor.
+ *
+ * @return number of transmitted bytes
+ */
+int
+send_ether(int fd, void* buffer, int len)
+{
+ return send(fd, buffer, len, 0);
+}
+
+/**
+ * Ethernet: Creates Ethernet-packet. Places Ethernet-header in a packet and
+ * fills it with corresponding information.
+ * <p>
+ * Use this function with similar functions for other network layers
+ * (fill_arphdr, fill_iphdr, fill_udphdr, fill_dnshdr, fill_btphdr).
+ *
+ * @param packet Points to the place where eth-header must be placed.
+ * @param eth_type Type of the next level protocol (e.g. IP or ARP).
+ * @param src_mac Sender MAC address
+ * @param dest_mac Receiver MAC address
+ * @see ethhdr
+ * @see fill_arphdr
+ * @see fill_iphdr
+ * @see fill_udphdr
+ * @see fill_dnshdr
+ * @see fill_btphdr
+ */
+void fill_ethhdr(uint8_t * packet, uint16_t eth_type,
+ const uint8_t * src_mac, const uint8_t * dest_mac)
+{
+ struct ethhdr * ethh = (struct ethhdr *) packet;
+
+ ethh -> type = htons(eth_type);
+ memcpy(ethh -> src_mac, src_mac, 6);
+ memcpy(ethh -> dest_mac, dest_mac, 6);
+}
diff --git a/roms/SLOF/lib/libnet/ethernet.h b/roms/SLOF/lib/libnet/ethernet.h
new file mode 100644
index 000000000..a09e36572
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ethernet.h
@@ -0,0 +1,50 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _ETHERNET_H
+#define _ETHERNET_H
+
+#include <stdint.h>
+
+#define ETH_MTU_SIZE 1518 /**< Maximum Transfer Unit */
+#define ETH_ALEN 6 /**< HW address length */
+#define ETHERTYPE_IP 0x0800
+#define ETHERTYPE_IPv6 0x86DD
+#define ETHERTYPE_ARP 0x0806
+
+/** \struct ethhdr
+ * A header for Ethernet-packets.
+ */
+struct ethhdr {
+ uint8_t dest_mac[ETH_ALEN]; /**< Destination HW address */
+ uint8_t src_mac[ETH_ALEN]; /**< Source HW address */
+ uint16_t type; /**< Next level protocol type */
+};
+
+/* Initializes ethernet layer */
+extern void set_mac_address(const uint8_t * own_mac);
+extern const uint8_t * get_mac_address(void);
+
+/* Receives and handles packets, according to Receive-handle diagram */
+extern int32_t receive_ether(int fd);
+
+/* Sends an ethernet frame. */
+extern int send_ether(int fd, void* buffer, int len);
+
+/* fills ethernet header */
+extern void fill_ethhdr(uint8_t * packet, uint16_t eth_type,
+ const uint8_t * src_mac, const uint8_t * dest_mac);
+
+/* Points either to send_ipv4() or send_ipv6() */
+extern int (*send_ip)(int fd, void *, int);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/icmpv6.c b/roms/SLOF/lib/libnet/icmpv6.c
new file mode 100644
index 000000000..34d7c6539
--- /dev/null
+++ b/roms/SLOF/lib/libnet/icmpv6.c
@@ -0,0 +1,407 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include "ethernet.h"
+#include "ipv6.h"
+#include "icmpv6.h"
+#include "ndp.h"
+#include "dhcpv6.h"
+
+static int ra_received = 0;
+
+/**
+ * NET:
+ * @param fd socket fd
+ */
+void
+send_router_solicitation (int fd)
+{
+ ip6_addr_t dest_addr;
+ uint8_t *ether_packet;
+ struct packeth headers;
+
+ ether_packet = malloc(ETH_MTU_SIZE);
+ if (!ether_packet) {
+ fprintf(stderr, "send_router_solicitation: Out of memory\n");
+ return;
+ }
+
+ headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr));
+ headers.icmp6h = (struct icmp6hdr *) (ether_packet +
+ sizeof(struct ethhdr) +
+ sizeof(struct ip6hdr));
+
+ /* Destination is "All routers multicast address" (link-local) */
+ dest_addr.part.prefix = 0xff02000000000000ULL;
+ dest_addr.part.interface_id = 2;
+
+ /* Fill IPv6 header */
+ fill_ip6hdr (ether_packet + sizeof(struct ethhdr),
+ ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation),
+ 0x3a, //ICMPV6
+ get_ipv6_address(), &dest_addr);
+
+ /* Fill ICMPv6 message */
+ headers.icmp6h->type = ICMPV6_ROUTER_SOLICITATION;
+ headers.icmp6h->code = 0;
+ headers.icmp6h->icmp6body.router_solicit.lladdr.type = 1;
+ headers.icmp6h->icmp6body.router_solicit.lladdr.length = 1;
+ memcpy( &(headers.icmp6h->icmp6body.router_solicit.lladdr.mac),
+ get_mac_address(), 6);
+
+ send_ip (fd, headers.ip6h, sizeof(struct ip6hdr) +
+ ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation));
+
+ free(ether_packet);
+}
+
+/**
+ * NET: Process prefix option in Router Advertisements
+ *
+ * @param ip6_packet pointer to an IPv6 packet
+ */
+static void
+handle_prefixoption (uint8_t *option)
+{
+ ip6_addr_t prefix;
+ struct ip6addr_list_entry *new_address;
+ struct option_prefix *prefix_option;
+ struct prefix_info *prfx_info;
+
+ prefix_option = (struct option_prefix *) option;
+ memcpy( &(prefix.addr), &(prefix_option->prefix.addr), IPV6_ADDR_LENGTH);
+
+ /* Link-local adresses in RAs are nonsense */
+ if (ip6_is_linklocal(&prefix))
+ return;
+
+ if (prefix_option->preferred_lifetime > prefix_option->valid_lifetime)
+ return;
+
+ /* Add address created from prefix to IPv6 address list */
+ new_address = ip6_prefix2addr (prefix);
+ if (!new_address)
+ return;
+
+ /* Process only prefixes we don't already have an adress from */
+ if (!unknown_prefix (&new_address->addr)) {
+ return;
+ }
+
+ /* Fill struct prefix_info from data in RA and store it in new_address */
+ prfx_info = ip6_create_prefix_info();
+ if (!prfx_info)
+ return;
+ memcpy (&(new_address->prfx_info), prfx_info, sizeof(struct prefix_info));
+
+ /* Add prefix received in RA to list of known prefixes */
+ ip6addr_add (new_address);
+}
+
+/**
+ * NET: Process source link layer addresses in Router Advertisements
+ *
+ * @param ip6_packet pointer to an IPv6 packet
+ */
+static void
+handle_source_lladdr ( struct option_ll_address *option, struct router *rtr)
+{
+ memcpy (&(rtr->mac), &(option->mac), 6);
+}
+
+/**
+ * NET: Process ICMPv6 options in Router Advertisements
+ *
+ * @param ip6_packet pointer to an IPv6 packet
+ */
+static void
+process_ra_options (uint8_t *option, int32_t option_length, struct router *r)
+{
+ while (option_length > 0) {
+ switch (*option) {
+ case ND_OPTION_SOURCE_LL_ADDR:
+ handle_source_lladdr ((struct option_ll_address *) option, r);
+ break;
+ case ND_OPTION_PREFIX_INFO:
+ handle_prefixoption(option);
+ break;
+ default:
+ break;
+ }
+ //option+1 is the length field. length is in units of 8 bytes
+ option_length = option_length - (*(option+1) * 8);
+ option = option + (*(option+1) * 8);
+ }
+
+ return;
+}
+
+/**
+ * NET: Process Router Advertisements
+ *
+ * @param ip6_packet pointer to an IPv6 packet
+ */
+static void
+handle_ra (struct icmp6hdr *icmp6h, uint8_t *ip6_packet)
+{
+ uint8_t *first_option;
+ int32_t option_length;
+ struct ip6hdr *ip6h;
+ struct router_advertisement *ra;
+ struct router *rtr;
+ uint8_t rtr_mac[] = {0, 0, 0, 0, 0, 0};
+
+ ip6h = (struct ip6hdr *) ip6_packet;
+ ra = (struct router_advertisement *) &icmp6h->icmp6body.ra;
+
+ rtr = find_router(ip6h->src);
+ if (!rtr) {
+ rtr = router_create (rtr_mac, ip6h->src);
+ router_add (rtr);
+ }
+
+ /* store info from router advertisement in router struct */
+ rtr->lifetime = ra->router_lifetime;
+ rtr->reachable_time = ra->reachable_time;
+ rtr->retrans_timer = ra->retrans_timer;
+
+ /* save flags concerning address (auto-) configuration */
+ ip6_state.managed_mode = ra->flags.managed;
+ ip6_state.other_config = ra->flags.other;
+
+ /* Process ICMPv6 options in Router Advertisement */
+ first_option = (uint8_t *) icmp6h + ICMPv6_HEADER_SIZE + 12;
+ option_length = (uint8_t *) icmp6h + ip6h->pl - first_option;
+ process_ra_options( (uint8_t *) first_option, option_length, rtr);
+
+ ra_received = 1;
+}
+
+int is_ra_received(void)
+{
+ return ra_received;
+}
+
+/**
+ * NET:
+ *
+ * @param fd socket fd
+ * @param ip6_addr_t *dest_ip6
+ */
+void
+send_neighbour_solicitation (int fd, ip6_addr_t *dest_ip6)
+{
+ ip6_addr_t snma;
+ uint8_t *ether_packet;
+ struct packeth headers;
+
+ ether_packet = malloc(ETH_MTU_SIZE);
+ if (!ether_packet) {
+ fprintf(stderr, "send_neighbour_solicitation: Out of memory\n");
+ return;
+ }
+
+ memset(ether_packet, 0, ETH_MTU_SIZE);
+ headers.ethh = (struct ethhdr *) ether_packet;
+ headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr));
+ headers.icmp6h = (struct icmp6hdr *) (ether_packet +
+ sizeof(struct ethhdr) +
+ sizeof(struct ip6hdr));
+
+ /* Fill IPv6 header */
+ snma.part.prefix = IPV6_SOLIC_NODE_PREFIX;
+ snma.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID;
+ snma.addr[13] = dest_ip6->addr[13];
+ snma.addr[14] = dest_ip6->addr[14];
+ snma.addr[15] = dest_ip6->addr[15];
+ fill_ip6hdr((uint8_t *) headers.ip6h,
+ ICMPv6_HEADER_SIZE + sizeof(struct neighbour_solicitation),
+ 0x3a, //ICMPv6
+ get_ipv6_address(), &snma);
+
+ /* Fill ICMPv6 message */
+ headers.icmp6h->type = ICMPV6_NEIGHBOUR_SOLICITATION;
+ headers.icmp6h->code = 0;
+ memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.target),
+ dest_ip6, IPV6_ADDR_LENGTH );
+ headers.icmp6h->icmp6body.nghb_solicit.lladdr.type = 1;
+ headers.icmp6h->icmp6body.nghb_solicit.lladdr.length = 1;
+ memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.lladdr.mac),
+ get_mac_address(), 6);
+
+ send_ip (fd, ether_packet + sizeof(struct ethhdr),
+ sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE +
+ sizeof(struct neighbour_solicitation));
+
+ free(ether_packet);
+}
+
+/**
+ * NET:
+ *
+ * @param fd socket fd
+ * @param ip6_packet pointer to an IPv6 packet
+ * @param icmp6hdr pointer to the icmp6 header in ip6_packet
+ * @param na_flags Neighbour advertisment flags
+ */
+static void
+send_neighbour_advertisement (int fd, struct neighbor *target)
+{
+ struct na_flags na_adv_flags;
+ uint8_t *ether_packet;
+ struct packeth headers;
+
+ ether_packet = malloc(ETH_MTU_SIZE);
+ if (!ether_packet) {
+ fprintf(stderr, "send_neighbour_advertisement: Out of memory\n");
+ return;
+ }
+
+ headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr));
+ headers.icmp6h = (struct icmp6hdr *) (ether_packet +
+ sizeof(struct ethhdr) +
+ sizeof(struct ip6hdr));
+
+ /* Fill IPv6 header */
+ fill_ip6hdr(ether_packet + sizeof(struct ethhdr),
+ ICMPv6_HEADER_SIZE + sizeof(struct neighbour_advertisement),
+ 0x3a, //ICMPv6
+ get_ipv6_address(), (ip6_addr_t *) &(target->ip.addr));
+
+ /* Fill ICMPv6 message */
+ memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target),
+ &(target->ip.addr), IPV6_ADDR_LENGTH );
+ headers.icmp6h->icmp6body.nghb_adv.lladdr.type = 1;
+ headers.icmp6h->icmp6body.nghb_adv.lladdr.length = 1;
+ memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac),
+ get_mac_address(), 6);
+
+ na_adv_flags.is_router = 0;
+ na_adv_flags.na_is_solicited = 1;
+ na_adv_flags.override = 1;
+
+ headers.icmp6h->type = ICMPV6_NEIGHBOUR_ADVERTISEMENT;
+ headers.icmp6h->code = 0;
+ headers.icmp6h->icmp6body.nghb_adv.router = na_adv_flags.is_router;
+
+ headers.icmp6h->icmp6body.nghb_adv.solicited = na_adv_flags.na_is_solicited;
+ headers.icmp6h->icmp6body.nghb_adv.override = na_adv_flags.override;
+ headers.icmp6h->icmp6body.nghb_adv.lladdr.type = 2;
+ headers.icmp6h->icmp6body.nghb_adv.lladdr.length = 1;
+
+ memset( &(headers.icmp6h->icmp6body.nghb_adv.target), 0,
+ IPV6_ADDR_LENGTH );
+
+ if( na_adv_flags.na_is_solicited ) {
+ memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target),
+ get_ipv6_address(), IPV6_ADDR_LENGTH);
+ }
+
+ memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac),
+ get_mac_address(), 6);
+
+ send_ip (fd, ether_packet + sizeof(struct ethhdr),
+ sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE +
+ sizeof(struct neighbour_advertisement));
+
+ free(ether_packet);
+}
+
+/**
+ * NET:
+ *
+ * @param fd socket fd
+ * @param ip6_packet pointer to an IPv6 packet
+ */
+static int8_t
+handle_na (int fd, uint8_t *packet)
+{
+ struct neighbor *n = NULL;
+ struct packeth headers;
+ ip6_addr_t ip;
+
+ headers.ethh = (struct ethhdr *) packet;
+ headers.ip6h = (struct ip6hdr *) (packet + sizeof(struct ethhdr));
+ headers.icmp6h = (struct icmp6hdr *) (packet +
+ sizeof(struct ethhdr) +
+ sizeof(struct ip6hdr));
+
+ memcpy(&(ip.addr), &(headers.ip6h->src), IPV6_ADDR_LENGTH);
+
+ n = find_neighbor(ip);
+
+ if (!n) {
+ n= (struct neighbor *)
+ neighbor_create( packet, &headers );
+ if (!n)
+ return 0;
+ if (!neighbor_add(n))
+ return 0;
+ } else {
+ memcpy (&(n->mac), &(headers.ethh->src_mac[0]), 6);
+ n->status = NB_REACHABLE;
+ if (n->eth_len > 0) {
+ struct ethhdr * ethh = (struct ethhdr *) &(n->eth_frame);
+ memcpy(ethh->dest_mac, &(n->mac), 6);
+ send_ether (fd, &(n->eth_frame), n->eth_len + sizeof(struct ethhdr));
+ n->eth_len = 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * NET: Handles ICMPv6 messages
+ *
+ * @param fd socket fd
+ * @param ip6_packet pointer to an IPv6 packet
+ * @param packetsize size of ipv6_packet
+ */
+int8_t
+handle_icmpv6 (int fd, struct ethhdr *etherhdr,
+ uint8_t *ip6_packet)
+{
+
+ struct icmp6hdr *received_icmp6 = NULL;
+ struct ip6hdr *received_ip6 = NULL;
+ struct neighbor target;
+
+ received_ip6 = (struct ip6hdr *) ip6_packet;
+ received_icmp6 = (struct icmp6hdr *) (ip6_packet +
+ sizeof(struct ip6hdr));
+ memcpy( &(target.ip.addr), &(received_ip6->src),
+ IPV6_ADDR_LENGTH );
+ memcpy( &(target.mac), etherhdr->src_mac, 6);
+
+ /* process ICMPv6 types */
+ switch(received_icmp6->type) {
+ case ICMPV6_NEIGHBOUR_SOLICITATION:
+ send_neighbour_advertisement(fd, &target);
+ break;
+ case ICMPV6_NEIGHBOUR_ADVERTISEMENT:
+ handle_na(fd, (uint8_t *) ip6_packet - sizeof(struct ethhdr));
+ break;
+ case ICMPV6_ROUTER_ADVERTISEMENT:
+ handle_ra(received_icmp6, (uint8_t *) received_ip6);
+ break;
+ default:
+ return -1;
+ }
+
+ return 1;
+}
diff --git a/roms/SLOF/lib/libnet/icmpv6.h b/roms/SLOF/lib/libnet/icmpv6.h
new file mode 100644
index 000000000..41b0c7071
--- /dev/null
+++ b/roms/SLOF/lib/libnet/icmpv6.h
@@ -0,0 +1,135 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _ICMPV6_H_
+#define _ICMPV6_H_
+
+#include <stdint.h>
+#include "ethernet.h"
+#include "ipv6.h"
+
+#define __ICMPV6_DEBUG__
+
+#ifdef __ICMPV6_DEBUG__
+#define ICMPV6_DEBUG_PRINT(format, ...) printf(format, ## __VA_ARGS__)
+#else
+#define ICMPV6_DEBUG_PRINT(format, ...)
+#endif
+
+#define ICMPv6_HEADER_SIZE 4 /* Size of common fields */
+#define IPTYPE_ICMPV6 0x3a
+
+/* Error message types */
+#define ICMPV6_DEST_UNREACHABLE 1 /* Destination unreachable */
+#define ICMPV6_PACKET_TOO_BIG 2 /* Packet too big */
+#define ICMPV6_TIME_EXCEEDED 3 /* Time exceeded */
+#define ICMPV6_PARAM_PROBLEM 4 /* Parameter problem */
+
+/* Informational message types */
+#define ICMPV6_ECHO_REQUEST 128 /* Echo request */
+#define ICMPV6_ECHO_REPLY 129 /* Echo reply */
+#define ICMPV6_MCAST_LISTENER_QUERY 130 /* Multicast listener query */
+#define ICMPV6_MCAST_LISTENER_REPORT 131 /* Multicast listener report */
+#define ICMPv6 MCAST_LISTENER_DONE 132 /* Multicast listener done */
+#define ICMPV6_ROUTER_SOLICITATION 133 /* Router solicitation */
+#define ICMPV6_ROUTER_ADVERTISEMENT 134 /* Router advertisement */
+#define ICMPV6_NEIGHBOUR_SOLICITATION 135 /* Neighbor solicitation */
+#define ICMPV6_NEIGHBOUR_ADVERTISEMENT 136 /* Neighbor advertisement */
+#define ICMPV6_REDIRECT_MSG 137 /* Redirect message */
+
+/******** Functions *******************/
+int8_t handle_icmpv6 (int fd, struct ethhdr *etherhdr, uint8_t *ip6_packet);
+void send_neighbour_solicitation(int fd, ip6_addr_t *target_ip6);
+void send_router_solicitation(int fd);
+int is_ra_received(void);
+
+/* Prefix information */
+struct option_prefix {
+ uint8_t type;
+ uint8_t length;
+ uint8_t prefix_length;
+ uint8_t onlink:1,
+ autom:1,
+ not_router:1,
+ not_site_prefix:1,
+ reserved:4;
+ uint32_t valid_lifetime;
+ uint32_t preferred_lifetime;
+ uint32_t reserved2;
+ ip6_addr_t prefix;
+} __attribute((packed));
+
+/* Neighbour advertisement/solicitation flags */
+struct na_flags {
+ uint8_t is_router:1, /* sender (we) is a router */
+ na_is_solicited:1, /* this NA was solicited (asked for) */
+ override:1, /* receiver shall override its cache entries */
+ unused:5;
+}__attribute((packed));
+
+/* Source/Target Link-layer address */
+struct option_ll_address{
+ uint8_t type;
+ uint8_t length;
+ uint8_t mac[ETH_ALEN];
+} __attribute((packed));
+
+struct neighbour_solicitation {
+ uint32_t router:1,
+ solicited:1,
+ override:1,
+ reserved:29;
+ ip6_addr_t target;
+ struct option_ll_address lladdr;
+} __attribute((packed));
+
+struct neighbour_advertisement {
+ uint32_t router:1,
+ solicited:1,
+ override:1,
+ reserved:29;
+ ip6_addr_t target;
+ struct option_ll_address lladdr;
+} __attribute((packed));
+
+struct router_solicitation {
+ uint32_t reserved;
+ struct option_ll_address lladdr;
+} __attribute((packed));
+
+struct router_advertisement {
+ uint8_t curr_hop_limit;
+ struct raflags {
+ uint8_t managed:1,
+ other:1,
+ reserved:6;
+ } flags;
+ uint16_t router_lifetime;
+ uint32_t reachable_time;
+ uint32_t retrans_timer;
+ struct option_prefix prefix;
+ struct option_ll_address ll_addr;
+} __attribute((packed));
+
+struct icmp6hdr {
+ uint8_t type;
+ uint8_t code;
+ uint16_t checksum;
+ union {
+ struct neighbour_solicitation nghb_solicit;
+ struct neighbour_advertisement nghb_adv;
+ struct router_solicitation router_solicit;
+ struct router_advertisement ra;
+ } icmp6body;
+} __attribute((packed));
+
+#endif
diff --git a/roms/SLOF/lib/libnet/ipv4.c b/roms/SLOF/lib/libnet/ipv4.c
new file mode 100644
index 000000000..3a1a78902
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ipv4.c
@@ -0,0 +1,898 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+/********************** DEFINITIONS & DECLARATIONS ***********************/
+
+#include <ipv4.h>
+#include <udp.h>
+#include <tcp.h>
+#include <ethernet.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <string.h>
+
+/* ARP Message types */
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+
+/* ARP talbe size (+1) */
+#define ARP_ENTRIES 10
+
+/* ICMP Message types */
+#define ICMP_ECHO_REPLY 0
+#define ICMP_DST_UNREACHABLE 3
+#define ICMP_SRC_QUENCH 4
+#define ICMP_REDIRECT 5
+#define ICMP_ECHO_REQUEST 8
+#define ICMP_TIME_EXCEEDED 11
+#define ICMP_PARAMETER_PROBLEM 12
+#define ICMP_TIMESTAMP_REQUEST 13
+#define ICMP_TIMESTAMP_REPLY 14
+#define ICMP_INFORMATION_REQUEST 15
+#define ICMP_INFORMATION_REPLY 16
+
+/** \struct arp_entry
+ * A entry that describes a mapping between IPv4- and MAC-address.
+ */
+typedef struct arp_entry arp_entry_t;
+struct arp_entry {
+ uint32_t ipv4_addr;
+ uint8_t mac_addr[6];
+ uint8_t eth_frame[ETH_MTU_SIZE];
+ int eth_len;
+ int pkt_pending;
+};
+
+/** \struct icmphdr
+ * ICMP packet
+ */
+struct icmphdr {
+ unsigned char type;
+ unsigned char code;
+ unsigned short int checksum;
+ union {
+ /* for type 3 "Destination Unreachable" */
+ unsigned int unused;
+ /* for type 0 and 8 */
+ struct echo {
+ unsigned short int id;
+ unsigned short int seq;
+ } echo;
+ } options;
+ union {
+ /* payload for destination unreachable */
+ struct dun {
+ unsigned char iphdr[20];
+ unsigned char data[64];
+ } dun;
+ /* payload for echo or echo reply */
+ /* maximum size supported is 84 */
+ unsigned char data[84];
+ } payload;
+};
+
+/****************************** PROTOTYPES *******************************/
+
+static unsigned short checksum(unsigned short *packet, int words);
+
+static void arp_send_request(int fd, uint32_t dest_ip);
+
+static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac);
+
+static void fill_arphdr(uint8_t * packet, uint8_t opcode,
+ const uint8_t * src_mac, uint32_t src_ip,
+ const uint8_t * dest_mac, uint32_t dest_ip);
+
+static arp_entry_t *lookup_mac_addr(uint32_t ipv4_addr);
+
+static void fill_udp_checksum(struct iphdr *ipv4_hdr);
+
+static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet,
+ int32_t packetsize);
+
+/****************************** LOCAL VARIABLES **************************/
+
+/* Routing parameters */
+static uint32_t own_ip = 0;
+static uint32_t multicast_ip = 0;
+static uint32_t router_ip = 0;
+static uint32_t subnet_mask = 0;
+
+/* helper variables */
+static uint32_t ping_dst_ip;
+static const uint8_t null_mac_addr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+static uint8_t multicast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+/* There are only (ARP_ENTRIES-1) effective entries because
+ * the entry that is pointed by arp_producer is never used.
+ */
+static unsigned int arp_consumer = 0;
+static unsigned int arp_producer = 0;
+static arp_entry_t arp_table[ARP_ENTRIES];
+
+static uint8_t pending_pkt_frame[ETH_MTU_SIZE];
+static int pending_pkt_len;
+
+/* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */
+int (*send_ip) (int fd, void *, int);
+
+/***************************** IMPLEMENTATION ****************************/
+
+/**
+ * IPv4: Initialize the environment for the IPv4 layer.
+ */
+static void ipv4_init(void)
+{
+ int i;
+
+ ping_dst_ip = 0;
+
+ // clear ARP table
+ arp_consumer = 0;
+ arp_producer = 0;
+ for(i=0; i<ARP_ENTRIES; ++i) {
+ arp_table[i].ipv4_addr = 0;
+ memset(arp_table[i].mac_addr, 0, 6);
+ arp_table[i].eth_len = 0;
+ arp_table[i].pkt_pending = 0;
+ }
+
+ /* Set IP send function to send_ipv4() */
+ send_ip = &send_ipv4;
+}
+
+/**
+ * IPv4: Set the own IPv4 address.
+ *
+ * @param _own_ip client IPv4 address (e.g. 127.0.0.1)
+ */
+void set_ipv4_address(uint32_t _own_ip)
+{
+ own_ip = _own_ip;
+ ipv4_init();
+}
+
+/**
+ * IPv4: Get the own IPv4 address.
+ *
+ * @return client IPv4 address (e.g. 127.0.0.1)
+ */
+uint32_t get_ipv4_address(void)
+{
+ return own_ip;
+}
+
+/**
+ * IPv4: Set the IPv4 multicast address.
+ *
+ * @param _own_ip multicast IPv4 address (224.0.0.0 - 239.255.255.255)
+ */
+void set_ipv4_multicast(uint32_t _multicast_ip)
+{
+ // is this IP Multicast out of range (224.0.0.0 - 239.255.255.255)
+ if((htonl(_multicast_ip) < 0xE0000000)
+ || (htonl(_multicast_ip) > 0xEFFFFFFF)) {
+ multicast_ip = 0;
+ memset(multicast_mac, 0xFF, 6);
+ return;
+ }
+
+ multicast_ip = _multicast_ip;
+ multicast_mac[0] = 0x01;
+ multicast_mac[1] = 0x00;
+ multicast_mac[2] = 0x5E;
+ multicast_mac[3] = (uint8_t) 0x7F & (multicast_ip >> 16);
+ multicast_mac[4] = (uint8_t) 0xFF & (multicast_ip >> 8);
+ multicast_mac[5] = (uint8_t) 0xFF & (multicast_ip >> 0);
+}
+
+/**
+ * IPv4: Get the IPv4 multicast address.
+ *
+ * @return multicast IPv4 address (224.0.0.0 - 239.255.255.255 or 0 if not set)
+ */
+uint32_t get_ipv4_multicast(void)
+{
+ return multicast_ip;
+}
+
+/**
+ * IPv4: Set the routers IPv4 address.
+ *
+ * @param _router_ip router IPv4 address
+ */
+void set_ipv4_router(uint32_t _router_ip)
+{
+ router_ip = _router_ip;
+ ipv4_init();
+}
+
+/**
+ * IPv4: Get the routers IPv4 address.
+ *
+ * @return router IPv4 address
+ */
+uint32_t get_ipv4_router(void)
+{
+ return router_ip;
+}
+
+/**
+ * IPv4: Set the subnet mask.
+ *
+ * @param _subnet_mask netmask of the own IPv4 address
+ */
+void set_ipv4_netmask(uint32_t _subnet_mask)
+{
+ subnet_mask = _subnet_mask;
+ ipv4_init();
+}
+
+/**
+ * IPv4: Get the subnet mask.
+ *
+ * @return netmask of the own IPv4 address
+ */
+uint32_t get_ipv4_netmask(void)
+{
+ return subnet_mask;
+}
+
+/**
+ * IPv4: Get the default subnet mask according to the IP class
+ *
+ * @param ip_addr IPv4 address
+ * @return default netmask according to the IP class
+ */
+uint32_t get_default_ipv4_netmask(char *ip_addr)
+{
+ unsigned char top;
+
+ top = ip_addr[0];
+ if (top > 0 && top < 128)
+ return 0xFF000000; /* Class A: 255.0.0.0 */
+ else if (top >= 128 && top < 192)
+ return 0xFFFF0000; /* Class B: 255.255.0.0 */
+ else if (top >= 192 && top < 224)
+ return 0xFFFFFF00; /* Class C: 255.255.255.0 */
+ else
+ return 0;
+}
+
+/**
+ * IPv4: Creates IP-packet. Places IP-header in a packet and fills it
+ * with corresponding information.
+ * <p>
+ * Use this function with similar functions for other network layers
+ * (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr).
+ *
+ * @param packet Points to the place where IP-header must be placed.
+ * @param packetsize Size of the packet in bytes incl. this hdr and data.
+ * @param ip_proto Type of the next level protocol (e.g. UDP).
+ * @param ip_src Sender IP address
+ * @param ip_dst Receiver IP address
+ * @see iphdr
+ * @see fill_ethhdr
+ * @see fill_udphdr
+ * @see fill_dnshdr
+ * @see fill_btphdr
+ */
+void fill_iphdr(uint8_t * packet, uint16_t packetsize,
+ uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst)
+{
+ struct iphdr * iph = (struct iphdr *) packet;
+
+ iph -> ip_hlv = 0x45;
+ iph -> ip_tos = 0x10;
+ iph -> ip_len = htons(packetsize);
+ iph -> ip_id = htons(0);
+ iph -> ip_off = 0;
+ iph -> ip_ttl = 0xFF;
+ iph -> ip_p = ip_proto;
+ iph -> ip_src = htonl(ip_src);
+ iph -> ip_dst = htonl(ip_dst);
+ iph -> ip_sum = 0;
+}
+
+/**
+ * IPv4: Handles IPv4-packets according to Receive-handle diagram.
+ *
+ * @param fd socket fd
+ * @param ip_packet IP-packet to be handled
+ * @param packetsize Length of the packet
+ * @return ZERO - packet handled successfully;
+ * NON ZERO - packet was not handled (e.g. bad format)
+ * @see receive_ether
+ * @see iphdr
+ */
+int8_t handle_ipv4(int fd, uint8_t * ip_packet, uint32_t packetsize)
+{
+ struct iphdr * iph;
+ int32_t old_sum;
+ static uint8_t ip_heap[65536 + ETH_MTU_SIZE];
+
+ if (packetsize < sizeof(struct iphdr))
+ return -1; // packet is too small
+
+ iph = (struct iphdr * ) ip_packet;
+
+ /* Drop it if destination IPv4 address is no IPv4 Broadcast, no
+ * registered IPv4 Multicast and not our Unicast address
+ */
+ if((multicast_ip == 0 && iph->ip_dst >= 0xE0000000 && iph->ip_dst <= 0xEFFFFFFF)
+ || (multicast_ip != iph->ip_dst && iph->ip_dst != 0xFFFFFFFF &&
+ own_ip != 0 && iph->ip_dst != own_ip)) {
+ return -1;
+ }
+
+ old_sum = iph -> ip_sum;
+ iph -> ip_sum = 0;
+ if (old_sum != checksum((uint16_t *) iph, sizeof (struct iphdr) >> 1))
+ return -1; // Wrong IP checksum
+
+ // is it the first fragment in a packet?
+ if (((iph -> ip_off) & 0x1FFF) == 0) {
+ // is it part of more fragments?
+ if (((iph -> ip_off) & 0x2000) == 0x2000) {
+ memcpy(ip_heap, ip_packet, iph->ip_len);
+ return 0;
+ }
+ }
+ // it's not the first fragment
+ else {
+ // get the first fragment
+ struct iphdr * iph_first = (struct iphdr * ) ip_heap;
+
+ // is this fragment not part of the first one, then exit
+ if ((iph_first->ip_id != iph->ip_id ) ||
+ (iph_first->ip_p != iph->ip_p ) ||
+ (iph_first->ip_src != iph->ip_src) ||
+ (iph_first->ip_dst != iph->ip_dst)) {
+ return 0;
+ }
+
+ // this fragment is part of the first one!
+ memcpy(ip_heap + sizeof(struct iphdr) +
+ ((iph -> ip_off) & 0x1FFF) * 8,
+ ip_packet + sizeof(struct iphdr),
+ iph -> ip_len - sizeof(struct iphdr));
+
+ // is it part of more fragments? Then return.
+ if (((iph -> ip_off) & 0x2000) == 0x2000) {
+ return 0;
+ }
+
+ // packet is completly reassambled now!
+
+ // recalculate ip_len and set iph and ip_packet to the
+ iph_first->ip_len = iph->ip_len + ((iph->ip_off) & 0x1FFF) * 8;
+
+ // set iph and ip_packet to the resulting packet.
+ ip_packet = ip_heap;
+ iph = (struct iphdr * ) ip_packet;
+ }
+
+ switch (iph -> ip_p) {
+ case IPTYPE_ICMP:
+ return handle_icmp(fd, iph, ip_packet + sizeof(struct iphdr),
+ iph -> ip_len - sizeof(struct iphdr));
+ case IPTYPE_UDP:
+ return handle_udp(fd, ip_packet + sizeof(struct iphdr),
+ iph -> ip_len - sizeof(struct iphdr));
+ case IPTYPE_TCP:
+ return handle_tcp(ip_packet + sizeof(struct iphdr),
+ iph -> ip_len - sizeof(struct iphdr));
+ default:
+ break;
+ }
+ return -1; // Unknown protocol
+}
+
+/**
+ * IPv4: Send IPv4-packets.
+ *
+ * Before the packet is sent there are some patcches performed:
+ * - IPv4 source address is replaced by our unicast IPV4 address
+ * if it is set to 0 or 1
+ * - IPv4 destination address is replaced by our multicast IPV4 address
+ * if it is set to 1
+ * - IPv4 checksum is calculaded.
+ * - If payload type is UDP, then the UDP checksum is calculated also.
+ *
+ * We send an ARP request first, if this is the first packet sent to
+ * the declared IPv4 destination address. In this case we store the
+ * the packet and send it later if we receive the ARP response.
+ * If the MAC address is known already, then we send the packet immediately.
+ * If there is already an ARP request pending, then we drop this packet
+ * and send again an ARP request.
+ *
+ * @param fd socket fd
+ * @param ip_packet IP-packet to be handled
+ * @param packetsize Length of the packet
+ * @return -2 - packet dropped (MAC address not resolved - ARP request pending)
+ * -1 - packet dropped (bad format)
+ * 0 - packet stored (ARP request sent - packet will be sent if
+ * ARP response is received)
+ * >0 - packet send (number of transmitted bytes is returned)
+ *
+ * @see receive_ether
+ * @see iphdr
+ */
+int send_ipv4(int fd, void* buffer, int len)
+{
+ arp_entry_t *arp_entry = 0;
+ struct iphdr *ip;
+ const uint8_t *mac_addr = 0;
+ uint32_t ip_dst = 0;
+
+ if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE)
+ return -1;
+
+ ip = (struct iphdr *) buffer;
+
+ /* Replace source IPv4 address with our own unicast IPv4 address
+ * if it's 0 (= own unicast source address not specified).
+ */
+ if(ip->ip_src == 0) {
+ ip->ip_src = htonl( own_ip );
+ }
+ /* Replace source IPv4 address with our unicast IPv4 address and
+ * replace destination IPv4 address with our multicast IPv4 address
+ * if source address is set to 1.
+ */
+ else if(ip->ip_src == 1) {
+ ip->ip_src = htonl( own_ip );
+ ip->ip_dst = htonl( multicast_ip );
+ }
+
+ // Calculate the IPv4 checksum
+ ip->ip_sum = 0;
+ ip->ip_sum = checksum((uint16_t *) ip, sizeof (struct iphdr) >> 1);
+
+ // if payload type is UDP, then we need to calculate the
+ // UDP checksum that depends on the IP header
+ if(ip->ip_p == IPTYPE_UDP) {
+ fill_udp_checksum(ip);
+ }
+
+ ip_dst = ip->ip_dst;
+ // Check if the MAC address is already cached
+ if(~ip->ip_dst == 0
+ || ( ((~subnet_mask) & ip->ip_dst) == ~subnet_mask &&
+ ( subnet_mask & ip->ip_dst) == (subnet_mask & own_ip))) {
+ arp_entry = &arp_table[arp_producer];
+ mac_addr = broadcast_mac;
+ }
+ else if(ip->ip_dst == multicast_ip) {
+ arp_entry = &arp_table[arp_producer];
+ mac_addr = multicast_mac;
+ }
+ else {
+ // Check if IP address is in the same subnet as we are
+ if((subnet_mask & own_ip) == (subnet_mask & ip->ip_dst))
+ arp_entry = lookup_mac_addr(ip->ip_dst);
+ // if not then we need to know the router's IP address
+ else {
+ ip_dst = router_ip;
+ arp_entry = lookup_mac_addr(router_ip);
+ }
+ if(arp_entry && memcmp(arp_entry->mac_addr, null_mac_addr, 6) != 0)
+ mac_addr = arp_entry->mac_addr;
+ }
+
+ // If we could not resolv the MAC address by our own...
+ if(!mac_addr) {
+ // send the ARP request
+ arp_send_request(fd, ip_dst);
+
+ // drop the current packet if there is already a ARP request pending
+ if(arp_entry)
+ return -2;
+
+ // take the next entry in the ARP table to prepare a the new ARP entry.
+ arp_entry = &arp_table[arp_producer];
+ arp_producer = (arp_producer+1)%ARP_ENTRIES;
+
+ // if ARP table is full then we must drop the oldes entry.
+ if(arp_consumer == arp_producer)
+ arp_consumer = (arp_consumer+1)%ARP_ENTRIES;
+
+ // store the packet to be send if the ARP reply is received
+ arp_entry->pkt_pending = 1;
+ arp_entry->ipv4_addr = ip_dst;
+ memset(arp_entry->mac_addr, 0, 6);
+ fill_ethhdr (pending_pkt_frame, htons(ETHERTYPE_IP),
+ get_mac_address(), null_mac_addr);
+ memcpy(&pending_pkt_frame[sizeof(struct ethhdr)],
+ buffer, len);
+ pending_pkt_len = len + sizeof(struct ethhdr);
+
+ set_timer(TICKS_SEC);
+ do {
+ receive_ether(fd);
+ if (!arp_entry->eth_len)
+ break;
+ } while (get_timer() > 0);
+
+ return 0;
+ }
+
+ // Send the packet with the known MAC address
+ fill_ethhdr(arp_entry->eth_frame, htons(ETHERTYPE_IP),
+ get_mac_address(), mac_addr);
+ memcpy(&arp_entry->eth_frame[sizeof(struct ethhdr)], buffer, len);
+ return send_ether(fd, arp_entry->eth_frame, len + sizeof(struct ethhdr));
+}
+
+/**
+ * IPv4: Calculate UDP checksum. Places the result into the UDP-header.
+ * <p>
+ * Use this function after filling the UDP payload.
+ *
+ * @param ipv4_hdr Points to the place where IPv4-header starts.
+ */
+static void fill_udp_checksum(struct iphdr *ipv4_hdr)
+{
+ unsigned i;
+ unsigned long checksum = 0;
+ struct iphdr ip_hdr;
+ char *ptr;
+ udp_hdr_t *udp_hdr;
+
+ udp_hdr = (udp_hdr_t *) (ipv4_hdr + 1);
+ udp_hdr->uh_sum = 0;
+
+ memset(&ip_hdr, 0, sizeof(struct iphdr));
+ ip_hdr.ip_src = ipv4_hdr->ip_src;
+ ip_hdr.ip_dst = ipv4_hdr->ip_dst;
+ ip_hdr.ip_len = udp_hdr->uh_ulen;
+ ip_hdr.ip_p = ipv4_hdr->ip_p;
+
+ ptr = (char*) udp_hdr;
+ for (i = 0; i < udp_hdr->uh_ulen; i+=2)
+ checksum += *((uint16_t*) &ptr[i]);
+
+ ptr = (char*) &ip_hdr;
+ for (i = 0; i < sizeof(struct iphdr); i+=2)
+ checksum += *((uint16_t*) &ptr[i]);
+
+ checksum = (checksum >> 16) + (checksum & 0xffff);
+ checksum += (checksum >> 16);
+ udp_hdr->uh_sum = ~checksum;
+
+ /* As per RFC 768, if the computed checksum is zero,
+ * it is transmitted as all ones (the equivalent in
+ * one's complement arithmetic).
+ */
+ if (udp_hdr->uh_sum == 0)
+ udp_hdr->uh_sum = ~udp_hdr->uh_sum;
+}
+
+/**
+ * IPv4: Calculates checksum for IP header.
+ *
+ * @param packet Points to the IP-header
+ * @param words Size of the packet in words incl. IP-header and data.
+ * @return Checksum
+ * @see iphdr
+ */
+static unsigned short checksum(unsigned short * packet, int words)
+{
+ unsigned long checksum;
+
+ for (checksum = 0; words > 0; words--)
+ checksum += *packet++;
+ checksum = (checksum >> 16) + (checksum & 0xffff);
+ checksum += (checksum >> 16);
+
+ return ~checksum;
+}
+
+static arp_entry_t* lookup_mac_addr(uint32_t ipv4_addr)
+{
+ unsigned int i;
+
+ for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) {
+ if(arp_table[i].ipv4_addr == ipv4_addr)
+ return &arp_table[i];
+ }
+ return 0;
+}
+
+
+/**
+ * ARP: Sends an ARP-request package.
+ * For given IPv4 retrieves MAC via ARP (makes several attempts)
+ *
+ * @param fd socket fd
+ * @param dest_ip IP of the host which MAC should be obtained
+ */
+static void arp_send_request(int fd, uint32_t dest_ip)
+{
+ arp_entry_t *arp_entry = &arp_table[arp_producer];
+
+ memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct arphdr));
+ fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REQUEST,
+ get_mac_address(), own_ip, broadcast_mac, dest_ip);
+ fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP,
+ get_mac_address(), broadcast_mac);
+
+ send_ether(fd, arp_entry->eth_frame,
+ sizeof(struct ethhdr) + sizeof(struct arphdr));
+}
+
+/**
+ * ARP: Sends an ARP-reply package.
+ * This package is used to serve foreign requests (in case IP in
+ * foreign request matches our host IP).
+ *
+ * @param fd socket fd
+ * @param src_ip requester IP address (foreign IP)
+ * @param src_mac requester MAC address (foreign MAC)
+ */
+static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac)
+{
+ arp_entry_t *arp_entry = &arp_table[arp_producer];
+
+ memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct arphdr));
+ fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP,
+ get_mac_address(), src_mac);
+ fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REPLY,
+ get_mac_address(), own_ip, src_mac, src_ip);
+
+ send_ether(fd, arp_entry->eth_frame,
+ sizeof(struct ethhdr) + sizeof(struct arphdr));
+}
+
+/**
+ * ARP: Creates ARP package. Places ARP-header in a packet and fills it
+ * with corresponding information.
+ * <p>
+ * Use this function with similar functions for other network layers
+ * (fill_ethhdr).
+ *
+ * @param packet Points to the place where ARP-header must be placed.
+ * @param opcode Identifies is it request (ARP_REQUEST)
+ * or reply (ARP_REPLY) package.
+ * @param src_mac sender MAC address
+ * @param src_ip sender IP address
+ * @param dest_mac receiver MAC address
+ * @param dest_ip receiver IP address
+ * @see arphdr
+ * @see fill_ethhdr
+ */
+static void fill_arphdr(uint8_t * packet, uint8_t opcode,
+ const uint8_t * src_mac, uint32_t src_ip,
+ const uint8_t * dest_mac, uint32_t dest_ip)
+{
+ struct arphdr * arph = (struct arphdr *) packet;
+
+ arph -> hw_type = htons(1);
+ arph -> proto_type = htons(ETHERTYPE_IP);
+ arph -> hw_len = 6;
+ arph -> proto_len = 4;
+ arph -> opcode = htons(opcode);
+
+ memcpy(arph->src_mac, src_mac, 6);
+ arph->src_ip = htonl(src_ip);
+ memcpy(arph->dest_mac, dest_mac, 6);
+ arph->dest_ip = htonl(dest_ip);
+}
+
+/**
+ * ARP: Handles ARP-messages according to Receive-handle diagram.
+ * Updates arp_table for outstanding ARP requests (see arp_getmac).
+ *
+ * @param fd socket fd
+ * @param packet ARP-packet to be handled
+ * @param packetsize length of the packet
+ * @return ZERO - packet handled successfully;
+ * NON ZERO - packet was not handled (e.g. bad format)
+ * @see arp_getmac
+ * @see receive_ether
+ * @see arphdr
+ */
+int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize)
+{
+ struct arphdr * arph = (struct arphdr *) packet;
+
+ if (packetsize < sizeof(struct arphdr))
+ return -1; // Packet is too small
+
+ if (arph -> hw_type != htons(1) || arph -> proto_type != htons(ETHERTYPE_IP))
+ return -1; // Unknown hardware or unsupported protocol
+
+ if (arph -> dest_ip != htonl(own_ip))
+ return -1; // receiver IP doesn't match our IP
+
+ switch(htons(arph -> opcode)) {
+ case ARP_REQUEST:
+ // foreign request
+ if(own_ip != 0)
+ arp_send_reply(fd, htonl(arph->src_ip), arph -> src_mac);
+ return 0; // no error
+ case ARP_REPLY: {
+ unsigned int i;
+ // if it is not for us -> return immediately
+ if(memcmp(get_mac_address(), arph->dest_mac, 6)) {
+ return 0; // no error
+ }
+
+ if(arph->src_ip == 0) {
+ // we are not interested for a MAC address if
+ // the IPv4 address is 0.0.0.0 or ff.ff.ff.ff
+ return -1;
+ }
+
+ // now let's find the corresponding entry in the ARP table
+
+ for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) {
+ if(arp_table[i].ipv4_addr == arph->src_ip)
+ break;
+ }
+ if(i == arp_producer || memcmp(arp_table[i].mac_addr, null_mac_addr, 6) != 0) {
+ // we have not asked to resolve this IPv4 address !
+ return -1;
+ }
+
+ memcpy(arp_table[i].mac_addr, arph->src_mac, 6);
+
+ // do we have something to send
+ if (arp_table[i].pkt_pending) {
+ struct ethhdr * ethh = (struct ethhdr *) pending_pkt_frame;
+ memcpy(ethh -> dest_mac, arp_table[i].mac_addr, 6);
+
+ send_ether(fd, pending_pkt_frame, pending_pkt_len);
+ arp_table[i].pkt_pending = 0;
+ arp_table[i].eth_len = 0;
+ }
+ return 0; // no error
+ }
+ default:
+ break;
+ }
+ return -1; // Invalid message type
+}
+
+/**
+ * ICMP: Send an ICMP Echo request to destination IPv4 address.
+ * This function does also set a global variable to the
+ * destination IPv4 address. If there is an ICMP Echo Reply
+ * received later then the variable is set back to 0.
+ * In other words, reading a value of 0 form this variable
+ * means that an answer to the request has been arrived.
+ *
+ * @param fd socket descriptor
+ * @param _ping_dst_ip destination IPv4 address
+ */
+void ping_ipv4(int fd, uint32_t _ping_dst_ip)
+{
+ unsigned char packet[sizeof(struct iphdr) + sizeof(struct icmphdr)];
+ struct icmphdr *icmp;
+
+ ping_dst_ip = _ping_dst_ip;
+
+ if(ping_dst_ip == 0)
+ return;
+
+ fill_iphdr(packet, sizeof(struct iphdr) + sizeof(struct icmphdr), IPTYPE_ICMP,
+ 0, ping_dst_ip);
+ icmp = (struct icmphdr *) (packet + sizeof(struct iphdr));
+ icmp->type = ICMP_ECHO_REQUEST;
+ icmp->code = 0;
+ icmp->checksum = 0;
+ icmp->options.echo.id = 0xd476;
+ icmp->options.echo.seq = 1;
+
+ memset(icmp->payload.data, '*', sizeof(icmp->payload.data));
+
+ icmp->checksum =
+ checksum((unsigned short *) icmp, sizeof(struct icmphdr) >> 1);
+ send_ipv4(fd, packet, sizeof(struct iphdr) + sizeof(struct icmphdr));
+}
+
+/**
+ * ICMP: Return host IPv4 address that we are waiting for a
+ * ICMP Echo reply message. If this value is 0 then we have
+ * received an reply.
+ *
+ * @return ping_dst_ip host IPv4 address
+ */
+uint32_t pong_ipv4(void)
+{
+ return ping_dst_ip;
+}
+
+/**
+ * ICMP: Handles ICMP-packets according to Receive-handle diagram.
+ *
+ * @param fd socket fd
+ * @param icmp_packet ICMP-packet to be handled
+ * @param packetsize Length of the packet
+ * @return ZERO - packet handled successfully;
+ * NON ZERO - packet was not handled (e.g. bad format)
+ * @see handle_ipv4
+ */
+static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet,
+ int32_t packetsize)
+{
+ struct icmphdr *icmp = (struct icmphdr *) packet;
+
+ switch(icmp->type) {
+ case ICMP_ECHO_REPLY:
+ if (icmp->options.echo.id != 0xd476)
+ return -1;
+ if (icmp->options.echo.seq != 1)
+ return -1;
+ if(ping_dst_ip != iph->ip_src
+ || ping_dst_ip == 0)
+ return -1;
+ ping_dst_ip = 0;
+ break;
+ case ICMP_DST_UNREACHABLE: {
+ // We've got Destination Unreachable msg
+ // Inform corresponding upper network layers
+ struct iphdr * bad_iph = (struct iphdr * ) &icmp->payload;
+
+ switch(bad_iph->ip_p) {
+ case IPTYPE_TCP:
+ handle_tcp_dun((uint8_t *) (bad_iph + 1), packetsize
+ - sizeof(struct icmphdr)
+ - sizeof(struct iphdr), icmp->code);
+ break;
+ case IPTYPE_UDP:
+ handle_udp_dun((uint8_t *) (bad_iph + 1), packetsize
+ - sizeof(struct icmphdr)
+ - sizeof(struct iphdr), icmp->code);
+ break;
+ }
+ break;
+ }
+ case ICMP_SRC_QUENCH:
+ break;
+ case ICMP_REDIRECT:
+ break;
+ case ICMP_ECHO_REQUEST: {
+ // We've got an Echo Request - answer with Echo Replay msg
+ unsigned char reply_packet[sizeof(struct iphdr) + packetsize];
+ struct icmphdr *reply_icmph;
+
+ fill_iphdr(reply_packet, sizeof(struct iphdr) + packetsize,
+ IPTYPE_ICMP, 0, iph->ip_src);
+
+ reply_icmph = (struct icmphdr *) &reply_packet[sizeof(struct iphdr)];
+ memcpy(reply_icmph, packet, packetsize);
+ reply_icmph -> type = ICMP_ECHO_REPLY;
+ reply_icmph -> checksum = 0;
+ reply_icmph->checksum = checksum((unsigned short *) reply_icmph,
+ sizeof(struct icmphdr) >> 1);
+
+ send_ipv4(fd, reply_packet, sizeof(struct iphdr) + packetsize);
+ break;
+ }
+ case ICMP_TIME_EXCEEDED:
+ break;
+ case ICMP_PARAMETER_PROBLEM:
+ break;
+ case ICMP_TIMESTAMP_REQUEST:
+ break;
+ case ICMP_TIMESTAMP_REPLY:
+ break;
+ case ICMP_INFORMATION_REQUEST:
+ break;
+ case ICMP_INFORMATION_REPLY:
+ break;
+ }
+ return 0;
+}
diff --git a/roms/SLOF/lib/libnet/ipv4.h b/roms/SLOF/lib/libnet/ipv4.h
new file mode 100644
index 000000000..3220ab59b
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ipv4.h
@@ -0,0 +1,95 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#ifndef _IPV4_H_
+#define _IPV4_H_
+
+#include <stdint.h>
+
+#define IPTYPE_ICMP 1
+
+/** \struct iphdr
+ * A header for IP-packets.
+ * For more information see RFC 791.
+ */
+struct iphdr {
+ uint8_t ip_hlv; /**< Header length and version of the header */
+ uint8_t ip_tos; /**< Type of Service */
+ uint16_t ip_len; /**< Length in octets, inlc. this header and data */
+ uint16_t ip_id; /**< ID is used to aid in assembling framents */
+ uint16_t ip_off; /**< Info about fragmentation (control, offset) */
+ uint8_t ip_ttl; /**< Time to Live */
+ uint8_t ip_p; /**< Next level protocol type */
+ uint16_t ip_sum; /**< Header checksum */
+ uint32_t ip_src; /**< Source IP address */
+ uint32_t ip_dst; /**< Destination IP address */
+};
+typedef struct iphdr ipv4_hdr_t;
+
+/* ICMP Error Codes */
+#define ICMP_NET_UNREACHABLE 0
+#define ICMP_HOST_UNREACHABLE 1
+#define ICMP_PROTOCOL_UNREACHABLE 2
+#define ICMP_PORT_UNREACHABLE 3
+#define ICMP_FRAGMENTATION_NEEDED 4
+#define ICMP_SOURCE_ROUTE_FAILED 5
+
+/** \struct arphdr
+ * A header for ARP-messages, retains info about HW and proto addresses.
+ * For more information see RFC 826.
+ */
+struct arphdr {
+ uint16_t hw_type; /**< HW address space (1 for Ethernet) */
+ uint16_t proto_type; /**< Protocol address space */
+ uint8_t hw_len; /**< Byte length of each HW address */
+ uint8_t proto_len; /**< Byte length of each proto address */
+ uint16_t opcode; /**< Identifies is it request (1) or reply (2) */
+ uint8_t src_mac[6]; /**< HW address of sender of this packet */
+ uint32_t src_ip; /**< Proto address of sender of this packet */
+ uint8_t dest_mac[6]; /**< HW address of target of this packet */
+ uint32_t dest_ip; /**< Proto address of target of this packet */
+} __attribute((packed));
+
+/************** Initialization of the IPv4 network layer. **************/
+extern void set_ipv4_address(uint32_t own_ip);
+extern uint32_t get_ipv4_address(void);
+extern void set_ipv4_multicast(uint32_t multicast_ip);
+extern uint32_t get_ipv4_multicast(void);
+extern void set_ipv4_router(uint32_t router_ip);
+extern uint32_t get_ipv4_router(void);
+extern void set_ipv4_netmask(uint32_t subnet_mask);
+extern uint32_t get_ipv4_netmask(void);
+extern uint32_t get_default_ipv4_netmask(char *ip_addr);
+
+/* fills ip header */
+extern void fill_iphdr(uint8_t * packet, uint16_t packetsize,
+ uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst);
+
+/* Send a IPv4 packet. Adding the Ethernet-Header and resolving the
+ * MAC address is done transparent in the background if necessary.
+ */
+extern int send_ipv4(int fd, void* buffer, int len);
+
+/* Sends an ICMP Echo request to destination IPv4 address */
+extern void ping_ipv4(int fd, uint32_t _ping_dst_ip);
+
+/* Returns host IPv4 address that we are waiting for a response */
+extern uint32_t pong_ipv4(void);
+
+/* Handles IPv4-packets that are detected by receive_ether. */
+extern int8_t handle_ipv4(int fd, uint8_t * packet, uint32_t packetsize);
+
+/* Handles ARP-packets that are detected by receive_ether. */
+extern int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/ipv6.c b/roms/SLOF/lib/libnet/ipv6.c
new file mode 100644
index 000000000..642000436
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ipv6.c
@@ -0,0 +1,768 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include "ethernet.h"
+#include "ipv6.h"
+#include "icmpv6.h"
+#include "ndp.h"
+#include "udp.h"
+
+#undef IPV6_DEBUG
+//#define IPV6_DEBUG
+#ifdef IPV6_DEBUG
+#define dprintf(_x ...) do { printf(_x); } while (0)
+#else
+#define dprintf(_x ...)
+#endif
+
+/****************************** PROTOTYPES *******************************/
+static void ipv6_init(int fd);
+static int ip6_is_multicast (ip6_addr_t * ip);
+
+/****************************** LOCAL VARIABLES **************************/
+
+/* List of Ipv6 Addresses */
+static struct ip6addr_list_entry *first_ip6;
+static struct ip6addr_list_entry *last_ip6;
+
+/* Own IPv6 address */
+static struct ip6addr_list_entry *own_ip6;
+
+/* All nodes link-local address */
+struct ip6addr_list_entry all_nodes_ll;
+
+/* Null IPv6 address */
+static ip6_addr_t null_ip6;
+
+/* helper variables */
+static uint8_t null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+struct ip6_config ip6_state;
+
+/****************************** IMPLEMENTATION ***************************/
+
+/**
+ * IPv6: Set the own IPv6 address.
+ *
+ * @param fd Socket descriptor
+ * @param _own_ip client IPv6 address (e.g. ::1)
+ */
+void set_ipv6_address(int fd, ip6_addr_t *_own_ip6)
+{
+ struct ip6addr_list_entry *ile;
+
+ ile = malloc(sizeof(struct ip6addr_list_entry));
+ if (!ile)
+ return;
+ memset(ile, 0, sizeof(struct ip6addr_list_entry));
+ own_ip6 = ile;
+
+ /* If no address was passed as a parameter generate a link-local
+ * address from our MAC address.*/
+ if (_own_ip6 == NULL)
+ ip6_create_ll_address(get_mac_address(), &own_ip6->addr);
+ else
+ memcpy (&(own_ip6->addr.addr), _own_ip6, 16);
+
+ /* Add to our list of IPv6 addresses */
+ ip6addr_add (own_ip6);
+
+ ipv6_init(fd);
+
+ /*
+ * Check whether we've got a non-link-local address during
+ * ipv6_init() and use that as preferred address if possible
+ */
+ if (_own_ip6 == NULL) {
+ for (ile = first_ip6; ile != NULL ; ile = ile->next) {
+ if (!ip6_is_multicast(&ile->addr) &&
+ !ip6_is_linklocal(&ile->addr)) {
+ own_ip6 = ile;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * IPv6: Get pointer to own IPv6 address.
+ *
+ * @return pointer to client IPv6 address (e.g. ::1)
+ */
+ip6_addr_t *get_ipv6_address(void)
+{
+ return (ip6_addr_t *) &(own_ip6->addr);
+}
+
+/**
+ * IPv6: Search for IPv6 address in list
+ *
+ * @return 0 - IPv6 address is not in list
+ * 1 - IPv6 address is in list
+ */
+static int8_t find_ip6addr(ip6_addr_t ip)
+{
+ struct ip6addr_list_entry *n = NULL;
+
+ for (n = first_ip6; n != NULL ; n=n->next)
+ if (ip6_cmp(n->addr, ip))
+ return 1; /* IPv6 address is in our list*/
+
+ return 0; /* not one of our IPv6 addresses*/
+}
+
+/**
+ * NET: Handles IPv6-packets
+ *
+ * @param fd - Socket descriptor
+ * @param ip6_packet - Pointer to IPv6 header
+ * @param packetsize - Size of Ipv6 packet
+ * @return ERROR - -1 if packet is too small or unknown protocol
+ * return value of handle_udp
+ *
+ * @see handle_udp
+ * @see ip6hdr
+ */
+int8_t handle_ipv6(int fd, uint8_t * ip6_packet, uint32_t packetsize)
+{
+
+ struct ip6hdr *ip6 = NULL;
+ ip6 = (struct ip6hdr *) ip6_packet;
+
+ /* Only handle packets which are for us */
+ if (!find_ip6addr(ip6->dst))
+ return -1;
+
+ if (packetsize < sizeof(struct ip6hdr))
+ return -1; // packet is too small
+
+ switch (ip6->nh) {
+ case IPTYPE_UDP:
+ return handle_udp (fd, ip6_packet + sizeof (struct ip6hdr),
+ ip6->pl);
+ case IPTYPE_ICMPV6:
+ return handle_icmpv6 (fd, (struct ethhdr *) ip6_packet - sizeof(struct ethhdr),
+ ip6_packet);
+ }
+
+ return -1; // unknown protocol
+}
+
+ /**
+ * NET: Creates IPv6-packet. Places IPv6-header in a packet and fills it
+ * with corresponding information.
+ * <p>
+ * Use this function with similar functions for other network layers
+ * (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr).
+ *
+ * @param packet Points to the place where IPv6-header must be placed.
+ * @param packetsize Size of payload (i.e. excluding ethhdr and ip6hdr)
+ * @param ip_proto Type of the next level protocol (e.g. UDP).
+ * @param ip6_src Sender IPv6 address
+ * @param ip6_dst Receiver IPv6 address
+ * @see ip6hdr
+ * @see fill_iphdr
+ * @see fill_ethhdr
+ * @see fill_udphdr
+ * @see fill_dnshdr
+ * @see fill_btphdr
+ */
+void fill_ip6hdr(uint8_t * packet, uint16_t packetsize,
+ uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst)
+{
+ struct ip6hdr * ip6h = (struct ip6hdr *) packet;
+
+ ip6h->ver_tc_fl = 6 << 28; // set version to 6
+ ip6h->pl = packetsize; // IPv6 payload size
+ ip6h->nh = ip_proto;
+ ip6h->hl = 255;
+ memcpy (&(ip6h->src), ip6_src, IPV6_ADDR_LENGTH);
+ memcpy (&(ip6h->dst), ip6_dst, IPV6_ADDR_LENGTH);
+}
+
+/**
+ * NET: For a given MAC calculates EUI64-Identifier.
+ * See RFC 4291 "IP Version 6 Addressing Architecture"
+ *
+ */
+uint64_t mac2eui64(const uint8_t *mac)
+{
+ uint8_t eui64id[8];
+ uint64_t retid;
+
+ memcpy (eui64id, mac, 3);
+ memcpy (eui64id + 5, mac + 3, 3);
+ eui64id[3] = 0xff;
+ eui64id[4] = 0xfe;
+
+ memcpy(&retid, eui64id, 8);
+ return retid;
+}
+
+/**
+ * NET: create link-local IPv6 address
+ *
+ * @param own_mac MAC of NIC
+ * @param ll_addr pointer to link-local address which should be created
+ */
+void ip6_create_ll_address(const uint8_t *own_mac, ip6_addr_t *ll_addr)
+{
+ ll_addr->part.prefix = IPV6_LL_PREFIX;
+ ll_addr->part.interface_id = mac2eui64((uint8_t *) own_mac);
+}
+
+/*
+ * NET: check if we already have an address with the same prefix.
+ * @param struct ip6_addr_list_entry *ip6
+ * @return true or false
+ */
+int8_t unknown_prefix(ip6_addr_t *ip)
+{
+ struct ip6addr_list_entry *node;
+
+ for( node = first_ip6; node != NULL; node=node->next )
+ if( node->addr.part.prefix == ip->part.prefix )
+ return 0; /* address is one of ours */
+
+ return 1; /* prefix not yet in our list */
+}
+
+/*
+ * NET: Create empty element for prefix list and return a pointer to it;
+ * @return NULL - malloc failed
+ * ! NULL - pointer to new prefix_info
+ */
+struct prefix_info *ip6_create_prefix_info(void)
+{
+ struct prefix_info *prfx_info;
+
+ prfx_info = malloc (sizeof(struct prefix_info));
+ if (!prfx_info)
+ return NULL;
+ memset(prfx_info, 0, sizeof(struct prefix_info));
+
+ return prfx_info;
+}
+
+/*
+ * NET: create a new IPv6 address with a given network prefix
+ * and add it to our IPv6 address list
+ *
+ * @param ip6_addr prefix (as received in RA)
+ * @return NULL - pointer to new ip6addr_list entry
+ */
+void *ip6_prefix2addr(ip6_addr_t prefix)
+{
+ struct ip6addr_list_entry *new_address;
+ uint64_t interface_id;
+
+ new_address = malloc (sizeof(struct ip6addr_list_entry));
+ if( !new_address )
+ return NULL;
+ memset(new_address, 0, sizeof(struct ip6addr_list_entry));
+
+ /* fill new addr struct */
+ /* extract prefix from Router Advertisement */
+ memcpy (&(new_address->addr.part.prefix), &prefix, 8 );
+
+ /* interface id is generated from MAC address */
+ interface_id = mac2eui64 (get_mac_address());
+ memcpy (&(new_address->addr.part.interface_id), &interface_id, 8);
+
+ return new_address;
+}
+
+/**
+ * NET: add new IPv6 adress to list
+ *
+ * @param ip6_addr *new_address
+ * @return 0 - passed pointer = NULL;
+ * 1 - ok
+ */
+int8_t ip6addr_add(struct ip6addr_list_entry *new_address)
+{
+ struct ip6addr_list_entry *solicited_node;
+
+
+ if (new_address == NULL)
+ return 0;
+
+ /* Don't add the same address twice */
+ if (find_ip6addr(new_address->addr))
+ return 0;
+
+ /* If address is a unicast address, we also have to process packets
+ * for its solicited-node multicast address.
+ * See RFC 2373 - IP Version 6 Adressing Architecture */
+ if (! ip6_is_multicast(&(new_address->addr))) {
+ solicited_node = malloc(sizeof(struct ip6addr_list_entry));
+ if (! solicited_node)
+ return 0;
+ memset(solicited_node, 0, sizeof(struct ip6addr_list_entry));
+
+ solicited_node->addr.part.prefix = IPV6_SOLIC_NODE_PREFIX;
+ solicited_node->addr.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID;
+ solicited_node->addr.addr[13] = new_address->addr.addr[13];
+ solicited_node->addr.addr[14] = new_address->addr.addr[14];
+ solicited_node->addr.addr[15] = new_address->addr.addr[15];
+ ip6addr_add (solicited_node);
+ }
+
+ if (first_ip6 == NULL)
+ first_ip6 = new_address;
+ else
+ last_ip6->next = new_address;
+ last_ip6 = new_address;
+ last_ip6->next = NULL;
+
+ return 1; /* no error */
+}
+
+/**
+ * NET: Initialize IPv6
+ *
+ * @param fd socket fd
+ */
+static void ipv6_init(int fd)
+{
+ int i = 0;
+
+ send_ip = &send_ipv6;
+
+ /* Address configuration parameters */
+ ip6_state.managed_mode = 0;
+
+ /* Null IPv6 address */
+ null_ip6.part.prefix = 0;
+ null_ip6.part.interface_id = 0;
+
+ /* Multicast addresses */
+ all_nodes_ll.addr.part.prefix = 0xff02000000000000;
+ all_nodes_ll.addr.part.interface_id = 1;
+ ip6addr_add(&all_nodes_ll);
+
+ ndp_init();
+
+ send_router_solicitation (fd);
+ for(i=0; i < 4 && !is_ra_received(); i++) {
+ set_timer(TICKS_SEC);
+ do {
+ receive_ether(fd);
+ if (is_ra_received())
+ break;
+ } while (get_timer() > 0);
+ }
+}
+
+/**
+ * NET: compare IPv6 adresses
+ *
+ * @param ip6_addr ip_1
+ * @param ip6_addr ip_2
+ */
+int8_t ip6_cmp(ip6_addr_t ip_1, ip6_addr_t ip_2)
+{
+ return !memcmp(ip_1.addr, ip_2.addr, IPV6_ADDR_LENGTH);
+}
+
+/**
+ * NET: Calculate checksum over IPv6 header and upper-layer protocol
+ * (e.g. UDP or ICMPv6)
+ *
+ * @param *ip - pointer to IPv6 address
+ * @return true or false
+ */
+int ip6_is_multicast(ip6_addr_t * ip)
+{
+ return ip->addr[0] == 0xFF;
+}
+
+/**
+ * NET: Generate multicast MAC address from IPv6 address
+ * (e.g. UDP or ICMPv6)
+ *
+ * @param *ip - pointer to IPv6 address
+ * @param *mc_mac pointer to an array with 6 bytes (for the MAC address)
+ * @return pointer to Multicast MAC address
+ */
+static uint8_t *ip6_to_multicast_mac(ip6_addr_t * ip, uint8_t *mc_mac)
+{
+ mc_mac[0] = 0x33;
+ mc_mac[1] = 0x33;
+ memcpy (mc_mac+2, (uint8_t *) &(ip->addr)+12, 4);
+
+ return mc_mac;
+}
+
+/**
+ * Check whether an IPv6 address is on the same network as we are
+ */
+static bool is_ip6addr_in_my_net(ip6_addr_t *ip)
+{
+ struct ip6addr_list_entry *n = NULL;
+
+ for (n = first_ip6; n != NULL; n = n->next) {
+ if (n->addr.part.prefix == ip->part.prefix)
+ return true; /* IPv6 address is in our neighborhood */
+ }
+
+ return false; /* not in our neighborhood */
+}
+
+/**
+ * NET: calculate checksum over IPv6 header and upper-layer protocol
+ * (e.g. UDP or ICMPv6)
+ *
+ * @param struct ip6hdr *ip6h - pointer to IPv6 header
+ * @param unsigned char *packet - pointer to header of upper-layer
+ * protocol
+ * @param int bytes - number of bytes
+ * starting from *packet
+ * @return checksum
+ */
+static unsigned short ip6_checksum(struct ip6hdr *ip6h, unsigned char *packet,
+ int bytes)
+{
+ int i;
+ unsigned long checksum;
+ const int ip6size = sizeof(struct ip6hdr)/sizeof(unsigned short);
+ union {
+ struct ip6hdr ip6h;
+ unsigned short raw[ip6size];
+ } pseudo;
+
+ memcpy (&pseudo.ip6h, ip6h, sizeof(struct ip6hdr));
+ pseudo.ip6h.hl = ip6h->nh;
+ pseudo.ip6h.ver_tc_fl = 0;
+ pseudo.ip6h.nh = 0;
+
+ for (checksum = 0, i = 0; i < bytes; i += 2)
+ checksum += (packet[i] << 8) | packet[i + 1];
+
+ for (i = 0; i < ip6size; i++)
+ checksum += pseudo.raw[i];
+
+ checksum = (checksum >> 16) + (checksum & 0xffff);
+ checksum += (checksum >> 16);
+
+ return ~checksum;
+}
+
+/**
+ * NET: Handles IPv6-packets
+ *
+ * @param fd socket fd
+ * @param ip6_packet Pointer to IPv6 header in packet
+ * @param packetsize Size of IPv6 packet
+ * @return -1 : Some error occured
+ * 0 : packet stored (NDP request sent - packet will be sent if
+ * NDP response is received)
+ * >0 : packet sent (number of transmitted bytes is returned)
+ *
+ * @see receive_ether
+ * @see ip6hdr
+ */
+int send_ipv6(int fd, void* buffer, int len)
+{
+ struct ip6hdr *ip6h;
+ struct udphdr *udph;
+ struct icmp6hdr *icmp6h;
+ ip6_addr_t ip_dst;
+ uint8_t *mac_addr, mc_mac[6];
+ static uint8_t ethframe[ETH_MTU_SIZE];
+
+ mac_addr = null_mac;
+
+ ip6h = (struct ip6hdr *) buffer;
+ udph = (struct udphdr *) ((uint8_t *) ip6h + sizeof (struct ip6hdr));
+ icmp6h = (struct icmp6hdr *) ((uint8_t *) ip6h + sizeof (struct ip6hdr));
+
+ memcpy(&ip_dst, &ip6h->dst, 16);
+
+ if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE)
+ return -1;
+
+ if ( ip6_cmp(ip6h->src, null_ip6))
+ memcpy (&(ip6h->src), get_ipv6_address(), IPV6_ADDR_LENGTH);
+
+ if (ip6h->nh == 17) {//UDP
+ udph->uh_sum = ip6_checksum (ip6h, (unsigned char *) udph,
+ ip6h->pl);
+ /* As per RFC 768, if the computed checksum is zero,
+ * it is transmitted as all ones (the equivalent in
+ * one's complement arithmetic).
+ */
+ if (udph->uh_sum == 0)
+ udph->uh_sum = ~udph->uh_sum;
+ }
+ else if (ip6h->nh == 0x3a) //ICMPv6
+ icmp6h->checksum = ip6_checksum (ip6h, (unsigned char *) icmp6h,
+ ip6h->pl);
+
+ if (ip6_is_multicast (&ip_dst)) {
+ /* If multicast, then create a proper multicast mac address */
+ mac_addr = ip6_to_multicast_mac (&ip_dst, mc_mac);
+ } else if (!is_ip6addr_in_my_net(&ip_dst)) {
+ /* If server is not in same subnet, user MAC of the router */
+ struct router *gw;
+ gw = ipv6_get_default_router(ip6h->src);
+ mac_addr = gw ? gw->mac : null_mac;
+ } else {
+ /* Normal unicast, so use neighbor cache to look up MAC */
+ struct neighbor *n = find_neighbor(ip_dst);
+ if (n) { /* Already cached ? */
+ if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
+ mac_addr = n->mac; /* found it */
+ } else {
+ mac_addr = null_mac;
+ n = malloc(sizeof(struct neighbor));
+ if (!n)
+ return -1;
+ memset(n, 0, sizeof(struct neighbor));
+ memcpy(&(n->ip.addr[0]), &ip_dst, 16);
+ n->status = NB_PROBE;
+ n->times_asked = 1;
+ neighbor_add(n);
+ }
+
+ if (! memcmp (mac_addr, &null_mac, 6)) {
+ if (n->eth_len == 0) {
+ send_neighbour_solicitation (fd, &ip_dst);
+
+ // Store the packet until we know the MAC address
+ fill_ethhdr (n->eth_frame,
+ htons(ETHERTYPE_IPv6),
+ get_mac_address(),
+ mac_addr);
+ memcpy (&(n->eth_frame[sizeof(struct ethhdr)]),
+ buffer, len);
+ n->eth_len = len;
+ set_timer(TICKS_SEC);
+ do {
+ receive_ether(fd);
+ if (n->status == NB_REACHABLE)
+ return len;
+ } while (get_timer() > 0);
+ return 0;
+ }
+ }
+ }
+
+ if (mac_addr == null_mac)
+ return -1;
+
+ fill_ethhdr(ethframe, htons(ETHERTYPE_IPv6), get_mac_address(),
+ mac_addr);
+ memcpy(&ethframe[sizeof(struct ethhdr)], buffer, len);
+
+ return send_ether(fd, ethframe, len + sizeof(struct ethhdr));
+}
+
+static int check_colons(const char *str)
+{
+ char *pch, *prv;
+ int col = 0;
+ int dcol = 0;
+
+ dprintf("str : %s\n",str);
+ pch = strchr(str, ':');
+ while(pch != NULL){
+ prv = pch;
+ pch = strchr(pch+1, ':');
+ if((pch-prv) != 1) {
+ col++;
+ } else {
+ col--; /* Its part of double colon */
+ dcol++;
+ }
+ }
+
+ dprintf("The number of col : %d \n",col);
+ dprintf("The number of dcol : %d \n",dcol);
+
+ if((dcol > 1) || /* Cannot have 2 "::" */
+ ((dcol == 1) && (col > 5)) || /* Too many ':'s */
+ ((dcol == 0) && (col != 7)) ) { /* Too few ':'s */
+ dprintf(" exiting for check_colons \n");
+ return 0;
+ }
+
+ return (col+dcol);
+}
+
+static int ipv6str_to_bytes(const char *str, char *ip)
+{
+ char block[5];
+ int res;
+ char *pos;
+ uint32_t cnt = 0, len;
+
+ dprintf("str : %s \n",str);
+
+ while (*str != 0) {
+ if (cnt > 15 || !isxdigit(*str)){
+ return 0;
+ }
+ if ((pos = strchr(str, ':')) != NULL) {
+ len = (int16_t) (pos - str);
+ dprintf("\t len is : %d \n",len);
+ if (len > 4)
+ return 0;
+ strncpy(block, str, len);
+ block[len] = 0;
+ dprintf("\t str : %s \n",str);
+ dprintf("\t block : %s \n",block);
+ str += len;
+ } else {
+ strncpy(block, str, 4);
+ block[4] = 0;
+ dprintf("\t str : %s \n",str);
+ dprintf("\t block : %s \n",block);
+ str += strlen(block);
+ }
+ res = strtol(block, NULL, 16);
+ dprintf("\t res : %x \n",res);
+ if ((res > 0xFFFF) || (res < 0))
+ return 0;
+ ip[cnt++] = (res & 0xFF00) >> 8;
+ ip[cnt++] = (res & 0x00FF);
+ if (*str == ':'){
+ str++;
+ }
+ }
+
+ dprintf("cnt : %d\n",cnt);
+ return cnt;
+}
+
+int str_to_ipv6(const char *str, uint8_t *ip)
+{
+ int i, k;
+ uint16_t len;
+ char *ptr;
+ char tmp[30], buf[16];
+
+ memset(ip,0,16);
+
+ if(!check_colons(str))
+ return 0;
+
+ if ((ptr = strstr(str, "::")) != NULL) {
+ /* Handle the ::1 IPv6 loopback */
+ if(!strcmp(str,"::1")) {
+ ip[15] = 1;
+ return 16;
+ }
+ len = (ptr-str);
+ dprintf(" len : %d \n",len);
+ if (len >= sizeof(tmp))
+ return 0;
+ strncpy(tmp, str, len);
+ tmp[len] = 0;
+ ptr += 2;
+
+ i = ipv6str_to_bytes(ptr, buf);
+ if(i == 0)
+ return i;
+
+ #if defined(ARGS_DEBUG)
+ int j;
+ dprintf("=========== bottom part i : %d \n",i);
+ for(j=0; j<i; j++)
+ dprintf("%02x \t",buf[j]);
+ #endif
+
+ /* Copy the bottom part i.e bytes following "::" */
+ memcpy(ip+(16-i), buf, i);
+
+ k = ipv6str_to_bytes(tmp, buf);
+ if(k == 0)
+ return k;
+
+ #if defined(ARGS_DEBUG)
+ dprintf("=========== top part k : %d \n",k);
+ for(j=0; j<k; j++)
+ printf("%02x \t",buf[j]);
+ #endif
+
+ /* Copy the top part i.e bytes before "::" */
+ memcpy(ip, buf, k);
+ #if defined(ARGS_DEBUG)
+ dprintf("\n");
+ for(j=0; j<16; j++)
+ dprintf("%02x \t",ip[j]);
+ #endif
+
+ } else {
+ i = ipv6str_to_bytes(str, (char *)ip);
+ }
+ return i;
+}
+
+void ipv6_to_str(const uint8_t *ip, char *str)
+{
+ int i, len;
+ uint8_t byte_even, byte_odd;
+ char *consec_zero, *strptr;
+
+ *str = 0;
+ for (i = 0; i < 16; i+=2) {
+ byte_even = ip[i];
+ byte_odd = ip[i+1];
+ if (byte_even)
+ sprintf(str, "%s%x%02x", str, byte_even, byte_odd);
+ else if (byte_odd)
+ sprintf(str, "%s%x", str, byte_odd);
+ else
+ strcat(str, "0");
+ if (i != 14)
+ strcat(str, ":");
+ }
+ strptr = str;
+ do {
+ consec_zero = strstr(strptr, "0:0:");
+ if (consec_zero) {
+ len = consec_zero - strptr;
+ if (!len)
+ break;
+ else if (strptr[len-1] == ':')
+ break;
+ else
+ strptr = consec_zero + 2;
+ }
+ } while (consec_zero);
+ if (consec_zero) {
+ len = consec_zero - str;
+ str[len] = 0;
+ if (len)
+ strcat(str, ":");
+ else
+ strcat(str, "::");
+ strptr = consec_zero + 4;
+ while (*strptr) {
+ if (!strncmp(strptr, "0:", 2))
+ strptr += 2;
+ else
+ break;
+ }
+ strcat(str, strptr);
+ if (!strcmp(str, "::0"))
+ strcpy(str, "::");
+ }
+}
diff --git a/roms/SLOF/lib/libnet/ipv6.h b/roms/SLOF/lib/libnet/ipv6.h
new file mode 100644
index 000000000..c6b681d7b
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ipv6.h
@@ -0,0 +1,186 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _IPV6_H_
+#define _IPV6_H_
+
+#include <stdint.h>
+#include "ethernet.h"
+
+#define __IPV6_DEBUG__
+
+#ifdef __IPV6_DEBUG__
+#define IPV6_DEBUG_PRINT(format, ...) do { printf(format, ## __VA_ARGS__); } while (0)
+#else
+#define IPV6_DEBUG_PRINT(format, ...)
+#endif
+
+#define IPV6_ADDR_LENGTH 16 /* Size of IPv6 adress in bytes */
+#define IPV6_LL_PREFIX 0xFE80000000000000ULL
+#define IPV6_LL_PREFIX_MASK 0xFFC0000000000000ULL
+#define IPV6_SOLIC_NODE_PREFIX 0xFF02000000000000ULL
+#define IPV6_SOLIC_NODE_IFACE_ID 0x00000001FF000000ULL
+
+/**
+ * An IPv6 Address
+ */
+typedef union {
+ uint8_t addr[IPV6_ADDR_LENGTH];
+ struct {
+ uint64_t prefix;
+ uint64_t interface_id;
+ } part;
+} ip6_addr_t;
+
+typedef struct {
+ uint8_t type;
+ uint8_t pad[7];
+ union {
+ ip6_addr_t v6;
+ char v4[4];
+ } addr;
+} netaddr_t;
+
+/** \struct prefix_info
+ *
+ * List of Prefixes we have adresses from
+ * Used for internal purposes, information derived from prefix option
+ * in Router Advertisements
+ * See RFC 4861 section 4.6.2
+ */
+struct prefix_info {
+ uint64_t prefix;
+ uint8_t on_link:1, /* When set prefix can be used for on-link
+ * determination */
+ autoconf:1, /* Prefix can be used for stateless address
+ * configuration */
+ reserved1:6;
+ uint32_t valid_lifetime; /* Time until prefix expires */
+ uint32_t preferred_lifetime; /* Time until prefix becomes deprecated */
+ uint32_t start_time; /* Time when received */
+ uint32_t reserved2;
+ struct prefix_info *next;
+};
+
+
+/* List of IPv6 addresses */
+struct ip6addr_list_entry {
+ ip6_addr_t addr;
+ struct prefix_info prfx_info;
+ struct ip6addr_list_entry *next;
+};
+
+/** \struct ip6hdr
+ * A header for IPv6 packets.
+ * For more information see RFC 2460
+ */
+struct ip6hdr {
+ uint32_t ver_tc_fl; /**< Version, Traffic class, Flow label */
+ uint16_t pl; /**< Payload length */
+ uint8_t nh; /**< Next header */
+ uint8_t hl; /**< Hop limit */
+ ip6_addr_t src; /**< IPv6 source address */
+ ip6_addr_t dst; /**< IPv6 destination address */
+} __attribute((packed));
+
+/** \struct packeth
+ * Struct with pointers to headers within a packet
+ */
+struct packeth {
+ struct ethhdr *ethh;
+ struct ip6hdr *ip6h;
+ struct icmp6hdr *icmp6h;
+ struct udphdr *udph;
+ /* ... */
+};
+
+/** \struct parseip6_state
+ * Stores information about state of IPv6 address parser
+ */
+struct parseip6_state {
+ char *lookahead;
+ char *ptr;
+ const char *addr;
+ int state;
+ int s1ctr;
+ int s2ctr;
+ int blocknr;
+ int zeroblocks;
+ int i;
+ int done;
+ int errorcode;
+};
+
+/** \struct ip6_config
+ * Stores flags wheter we use Stateless- or Stateful Autoconfiguration or DHCPv6
+ */
+struct ip6_config {
+ uint8_t managed_mode:1,
+ other_config:1,
+ reserved:6;
+};
+
+/******************** VARIABLES **********************************************/
+
+extern struct ip6_config ip6_state;
+
+/******************** FUNCTIONS *********************************************/
+/* Handles IPv6-packets that are detected by receive_ether. */
+int8_t handle_ipv6(int fd, uint8_t * ip6_packet, uint32_t packetsize);
+
+/* Fill IPv6 header */
+void fill_ip6hdr(uint8_t * packet, uint16_t packetsize,
+ uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst);
+
+/* Set own IPv6 address */
+void set_ipv6_address(int fd, ip6_addr_t *own_ip6);
+
+/* Get own IPv6 address */
+ip6_addr_t *get_ipv6_address(void);
+
+/* Create link-local address from a given Mac Address */
+void ip6_create_ll_address (const uint8_t *own_mac, ip6_addr_t *ll_addr);
+
+/* For a given MAC calculates EUI64-Identifier.*/
+uint64_t mac2eui64 (const uint8_t *mac);
+
+/* Create empty element for prefix list and return a pointer to it */
+struct prefix_info * ip6_create_prefix_info(void);
+
+/* Create a new IPv6 address with a given network prefix
+ * and add it to our IPv6 address list */
+void * ip6_prefix2addr (ip6_addr_t prefix);
+
+/* Compare IPv6 adresses */
+int8_t ip6_cmp(ip6_addr_t ip_1, ip6_addr_t ip_2);
+
+/* Check if it is a link-local address */
+static inline int ip6_is_linklocal(ip6_addr_t *ip)
+{
+ return (ip->part.prefix & IPV6_LL_PREFIX_MASK) == IPV6_LL_PREFIX;
+}
+
+/* Check if prefix is already in our list */
+int8_t unknown_prefix (ip6_addr_t *ip);
+
+/* Send IPv6 packet */
+int send_ipv6 (int fd, void* buffer, int len);
+
+/* Add IPv6 address to list */
+int8_t ip6addr_add (struct ip6addr_list_entry *new_address);
+
+/* Parse an IPv6 address */
+int parseip6(const char *addr, uint8_t *parsedaddr);
+int str_to_ipv6(const char *str, uint8_t *ip);
+void ipv6_to_str(const uint8_t *ip, char *str);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/libnet.code b/roms/SLOF/lib/libnet/libnet.code
new file mode 100644
index 000000000..419419d35
--- /dev/null
+++ b/roms/SLOF/lib/libnet/libnet.code
@@ -0,0 +1,16 @@
+
+#include <netapps.h>
+
+PRIM(NET_X2d_LOAD)
+ int alen = TOS.n; POP;
+ char *arg = TOS.a; POP;
+ long maxlen = TOS.n; POP;
+ void *loadaddr = TOS.a;
+ TOS.n = netload(loadaddr, maxlen, arg, alen);
+MIRP
+
+PRIM(NET_X2d_PING)
+ int alen = TOS.n; POP;
+ char *arg = TOS.a;
+ TOS.n = ping(arg, alen);
+MIRP
diff --git a/roms/SLOF/lib/libnet/libnet.in b/roms/SLOF/lib/libnet/libnet.in
new file mode 100644
index 000000000..00b8658f6
--- /dev/null
+++ b/roms/SLOF/lib/libnet/libnet.in
@@ -0,0 +1,3 @@
+
+cod(NET-LOAD)
+cod(NET-PING)
diff --git a/roms/SLOF/lib/libnet/ndp.c b/roms/SLOF/lib/libnet/ndp.c
new file mode 100644
index 000000000..1c420d63e
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ndp.c
@@ -0,0 +1,184 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "ipv6.h"
+#include "icmpv6.h"
+#include "ndp.h"
+
+/* Neighbor cache */
+static struct neighbor *first_neighbor;
+static struct neighbor *last_neighbor;
+
+/* Router list */
+static struct router *first_router;
+static struct router *last_router;
+
+/*
+ * NET: add new router to list
+ * @param struct router nghb - new router
+ * @return true or false
+ */
+int8_t
+router_add (struct router *nghb )
+{
+ if (nghb == NULL)
+ return -1;
+
+ if (first_router == NULL) {
+ first_router= nghb;
+ last_router = first_router;
+ last_router->next = NULL;
+ } else {
+ last_router->next = nghb;
+ last_router = nghb;
+ last_router->next = NULL;
+ }
+ return 1; /* no error */
+}
+
+/*
+ * NET: create a new router
+ * @param uint8_t *packet - received packet (Ethernet/IPv6/ICMPv6/ND_NghSlct)
+ * @param struct packeth - pointers to headers in packet
+ * @return pointer to new router
+ */
+void *
+router_create(uint8_t *mac, ip6_addr_t ip)
+{
+ struct router *new_router;
+
+ new_router = malloc (sizeof(struct router));
+ if( !new_router) {
+ return 0;
+ }
+ memset (new_router, 0, sizeof(struct router));
+
+ /* fill neighbor struct */
+ memcpy (new_router->mac, mac, 6);
+ memcpy (&(new_router->ip.addr[0]), ip.addr, IPV6_ADDR_LENGTH);
+
+ return new_router;
+}
+
+struct router *
+find_router(ip6_addr_t ip)
+{
+ struct router *n = NULL;
+
+ for (n = first_router; n != NULL ; n=n->next)
+ if (ip6_cmp(n->ip, ip))
+ return n; /* router is already in list*/
+
+ return NULL; /* router is unknown */
+}
+
+/**
+ * Find a router for a given host address
+ * @param ip - IPv6 address with the prefered prefix
+ * @return pointer to router, or NULL if none is available
+ */
+struct router *ipv6_get_default_router(ip6_addr_t ip)
+{
+ struct router *n = NULL;
+
+ for (n = first_router; n != NULL; n = n->next) {
+ if (n->ip.part.prefix == ip.part.prefix)
+ return n;
+ }
+
+ return first_router;
+}
+
+/*
+ * NET: add new neighbor to list
+ * @param struct neighbor nghb - new neighbor
+ * @return true or false
+ */
+int8_t
+neighbor_add (struct neighbor *nghb)
+{
+ if (nghb == NULL)
+ return -1;
+
+ if (first_neighbor == NULL) {
+ first_neighbor = nghb;
+ last_neighbor = first_neighbor;
+ last_neighbor->next = NULL;
+ } else {
+ last_neighbor->next = nghb;
+ last_neighbor = nghb;
+ last_neighbor->next = NULL;
+ }
+
+ return 1; /* no error */
+}
+
+/*
+ * NET: create a new neighbor
+ * @param uint8_t *packet - received packet (Ethernet/IPv6/ICMPv6/ND_NghSlct)
+ * @param struct packeth - pointers to headers in packet
+ * @return pointer - pointer to new neighbor
+ * NULL - malloc failed
+ */
+void *
+neighbor_create (uint8_t *packet, struct packeth *headers)
+{
+ struct neighbor *new_neighbor;
+
+ new_neighbor = malloc (sizeof(struct neighbor));
+ if( !new_neighbor )
+ return NULL;
+ memset(new_neighbor, 0, sizeof(struct neighbor));
+
+ /* fill neighbor struct */
+ memcpy (&(new_neighbor->mac),
+ &(headers->ethh->src_mac[0]), 6);
+ memcpy (&(new_neighbor->ip.addr), &(headers->ip6h->src), IPV6_ADDR_LENGTH);
+ new_neighbor->status = NB_INCOMPLETE;
+
+ return new_neighbor;
+}
+
+/**
+ * NET: Find neighbor with given IPv6 address in Neighbor Cache
+ *
+ * @param ip - Pointer to IPv6 address
+ * @return pointer - pointer to client IPv6 address (e.g. ::1)
+ * NULL - Neighbor not found
+ */
+struct neighbor *
+find_neighbor(ip6_addr_t ip)
+{
+ struct neighbor *n = NULL;
+
+ for (n = first_neighbor; n != NULL ; n=n->next) {
+ if (ip6_cmp(n->ip, ip)) {
+ return n; /* neighbor is already in cache */
+ }
+ }
+
+ return NULL; /* neighbor is unknown */
+}
+
+void ndp_init(void)
+{
+ /* Router list */
+ first_router = NULL;
+ last_router = first_router;
+
+ /* Init Neighbour cache */
+ first_neighbor = NULL;
+ last_neighbor = first_neighbor;
+}
diff --git a/roms/SLOF/lib/libnet/ndp.h b/roms/SLOF/lib/libnet/ndp.h
new file mode 100644
index 000000000..d0db198bf
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ndp.h
@@ -0,0 +1,72 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _NDP_H_
+#define _NDP_H_
+
+#include "ipv6.h"
+
+#define __NDP_DEBUG__
+
+#ifdef __NDP_DEBUG__
+#define NDP_DEBUG_PRINT(format, ...) do { printf(format, ## __VA_ARGS__); } while (0)
+#else
+#define NDP_DEBUG_PRINT(format, ...)
+#endif
+
+#define ND_OPTION_SOURCE_LL_ADDR 1
+#define ND_OPTION_TARGET_LL_ADDR 2
+#define ND_OPTION_PREFIX_INFO 3
+#define ND_OPTION_REDIRECT_HDR 4
+#define ND_OPTION_MTU 5
+
+/* Default Router List */
+struct router {
+ uint8_t mac[6];
+ ip6_addr_t ip;
+ uint32_t lifetime;
+ uint32_t reachable_time;
+ uint32_t retrans_timer;
+ struct router *next;
+};
+
+/* Neighbor cache */
+struct neighbor {
+ uint8_t mac[6];
+ ip6_addr_t ip;
+ uint8_t is_router;
+ uint8_t status;
+ uint8_t times_asked;
+ /* ... */
+ struct neighbor *next;
+ uint8_t eth_frame[ETH_MTU_SIZE];
+ uint32_t eth_len;
+
+#define NB_INCOMPLETE 1
+#define NB_REACHABLE 2
+#define NB_STALE 3
+#define NB_DELAY 4
+#define NB_PROBE 5
+};
+
+/******************** FUNCTIONS *********************************************/
+void ndp_init(void);
+int8_t neighbor_add (struct neighbor *);
+void * neighbor_create (uint8_t *packet, struct packeth *headers);
+struct neighbor *find_neighbor(ip6_addr_t ip);
+
+int8_t router_add(struct router*);
+void *router_create(uint8_t *mac, ip6_addr_t ip);
+struct router *find_router(ip6_addr_t ip);
+struct router *ipv6_get_default_router(ip6_addr_t ip);
+
+#endif //_NDP_H_
diff --git a/roms/SLOF/lib/libnet/netapps.h b/roms/SLOF/lib/libnet/netapps.h
new file mode 100644
index 000000000..722ca7ff7
--- /dev/null
+++ b/roms/SLOF/lib/libnet/netapps.h
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _NETAPPS_H_
+#define _NETAPPS_H_
+
+#define F_IPV4 4
+#define F_IPV6 6
+
+struct filename_ip;
+
+extern int netload(char *buffer, int len, char *args_fs, unsigned alen);
+extern int ping(char *args_fs, unsigned alen);
+extern int dhcp(char *ret_buffer, struct filename_ip *fn_ip,
+ unsigned int retries, int flags);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/netload.c b/roms/SLOF/lib/libnet/netload.c
new file mode 100644
index 000000000..ae169f3e0
--- /dev/null
+++ b/roms/SLOF/lib/libnet/netload.c
@@ -0,0 +1,804 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <unistd.h>
+#include <tftp.h>
+#include <ethernet.h>
+#include <dhcp.h>
+#include <dhcpv6.h>
+#include <ipv4.h>
+#include <ipv6.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <libbootmsg/libbootmsg.h>
+#include <helpers.h>
+#include "args.h"
+#include "netapps.h"
+#include "pxelinux.h"
+
+#define IP_INIT_DEFAULT 5
+#define IP_INIT_NONE 0
+#define IP_INIT_BOOTP 1
+#define IP_INIT_DHCP 2
+#define IP_INIT_DHCPV6_STATELESS 3
+#define IP_INIT_IPV6_MANUAL 4
+
+#define MAX_PKT_SIZE 1720
+#define DEFAULT_BOOT_RETRIES 10
+#define DEFAULT_TFTP_RETRIES 20
+static int ip_version;
+
+typedef struct {
+ char filename[100];
+ int ip_init;
+ char siaddr[4];
+ ip6_addr_t si6addr;
+ char ciaddr[4];
+ ip6_addr_t ci6addr;
+ char giaddr[4];
+ ip6_addr_t gi6addr;
+ int bootp_retries;
+ int tftp_retries;
+} obp_tftp_args_t;
+
+/**
+ * Print error with preceeding error code
+ */
+static void netload_error(int errcode, const char *format, ...)
+{
+ va_list vargs;
+ char buf[256];
+ int elen;
+
+ elen = sprintf(buf, "E%04X: (net) ", errcode);
+
+ va_start(vargs, format);
+ vsnprintf(&buf[elen], sizeof(buf) - elen, format, vargs);
+ va_end(vargs);
+
+ bootmsg_error(errcode, &buf[elen - 6]);
+ write_mm_log(buf, strlen(buf), 0x91);
+}
+
+/**
+ * Parses a argument string for IPv6 booting, extracts all
+ * parameters and fills a structure accordingly
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @param argc number of arguments
+ * @param obp_tftp_args structure which contains the result
+ * @return updated arg_str
+ */
+static const char *
+parse_ipv6args (const char *arg_str, unsigned int argc,
+ obp_tftp_args_t *obp_tftp_args)
+{
+ char *ptr = NULL;
+ char arg_buf[100];
+
+ // find out siaddr
+ if (argc == 0)
+ memset(&obp_tftp_args->si6addr.addr, 0, 16);
+ else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if(str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->si6addr.addr[0]))) {
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else if(arg_buf[0] == 0) {
+ memset(&obp_tftp_args->si6addr.addr, 0, 16);
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else
+ memset(&obp_tftp_args->si6addr.addr, 0, 16);
+ }
+
+ // find out filename
+ if (argc == 0)
+ obp_tftp_args->filename[0] = 0;
+ else {
+ argncpy(arg_str, 0, obp_tftp_args->filename, 100);
+ for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr)
+ if(*ptr == '\\') {
+ *ptr = '/';
+ }
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+
+ // find out ciaddr
+ if (argc == 0)
+ memset(&obp_tftp_args->ci6addr, 0, 16);
+ else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->ci6addr.addr[0]))) {
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else if(arg_buf[0] == 0) {
+ memset(&obp_tftp_args->ci6addr.addr, 0, 16);
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else
+ memset(&obp_tftp_args->ci6addr.addr, 0, 16);
+ }
+
+ // find out giaddr
+ if (argc == 0)
+ memset(&obp_tftp_args->gi6addr, 0, 16);
+ else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->gi6addr.addr)) ) {
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else if(arg_buf[0] == 0) {
+ memset(&obp_tftp_args->gi6addr, 0, 16);
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else
+ memset(&obp_tftp_args->gi6addr.addr, 0, 16);
+ }
+
+ return arg_str;
+}
+
+
+/**
+ * Parses a argument string for IPv4 booting, extracts all
+ * parameters and fills a structure accordingly
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @param argc number of arguments
+ * @param obp_tftp_args structure which contains the result
+ * @return updated arg_str
+ */
+static const char *
+parse_ipv4args (const char *arg_str, unsigned int argc,
+ obp_tftp_args_t *obp_tftp_args)
+{
+ char *ptr = NULL;
+ char arg_buf[100];
+
+ // find out siaddr
+ if(argc==0) {
+ memset(obp_tftp_args->siaddr, 0, 4);
+ } else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if(strtoip(arg_buf, obp_tftp_args->siaddr)) {
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else if(arg_buf[0] == 0) {
+ memset(obp_tftp_args->siaddr, 0, 4);
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else
+ memset(obp_tftp_args->siaddr, 0, 4);
+ }
+
+ // find out filename
+ if(argc==0)
+ obp_tftp_args->filename[0] = 0;
+ else {
+ argncpy(arg_str, 0, obp_tftp_args->filename, 100);
+ for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr)
+ if(*ptr == '\\')
+ *ptr = '/';
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+
+ // find out ciaddr
+ if(argc==0)
+ memset(obp_tftp_args->ciaddr, 0, 4);
+ else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if(strtoip(arg_buf, obp_tftp_args->ciaddr)) {
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else if(arg_buf[0] == 0) {
+ memset(obp_tftp_args->ciaddr, 0, 4);
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else
+ memset(obp_tftp_args->ciaddr, 0, 4);
+ }
+
+ // find out giaddr
+ if(argc==0)
+ memset(obp_tftp_args->giaddr, 0, 4);
+ else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if(strtoip(arg_buf, obp_tftp_args->giaddr)) {
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else if(arg_buf[0] == 0) {
+ memset(obp_tftp_args->giaddr, 0, 4);
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else
+ memset(obp_tftp_args->giaddr, 0, 4);
+ }
+
+ return arg_str;
+}
+
+/**
+ * Parses a argument string which is given by netload, extracts all
+ * parameters and fills a structure according to this
+ *
+ * Netload-Parameters:
+ * [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @param obp_tftp_args structure which contains the result
+ * @return none
+ */
+static void
+parse_args(const char *arg_str, obp_tftp_args_t *obp_tftp_args)
+{
+ unsigned int argc;
+ char arg_buf[100];
+
+ memset(obp_tftp_args, 0, sizeof(*obp_tftp_args));
+
+ argc = get_args_count(arg_str);
+
+ // find out if we should use BOOTP or DHCP
+ if(argc==0)
+ obp_tftp_args->ip_init = IP_INIT_DEFAULT;
+ else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if (strcasecmp(arg_buf, "bootp") == 0) {
+ obp_tftp_args->ip_init = IP_INIT_BOOTP;
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else if(strcasecmp(arg_buf, "dhcp") == 0) {
+ obp_tftp_args->ip_init = IP_INIT_DHCP;
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+ else if(strcasecmp(arg_buf, "ipv6") == 0) {
+ obp_tftp_args->ip_init = IP_INIT_DHCPV6_STATELESS;
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ ip_version = 6;
+ }
+ else
+ obp_tftp_args->ip_init = IP_INIT_DEFAULT;
+ }
+
+ if (ip_version == 4) {
+ arg_str = parse_ipv4args (arg_str, argc, obp_tftp_args);
+ }
+ else if (ip_version == 6) {
+ arg_str = parse_ipv6args (arg_str, argc, obp_tftp_args);
+ }
+
+ // find out bootp-retries
+ if (argc == 0)
+ obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
+ else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if(arg_buf[0] == 0)
+ obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
+ else {
+ obp_tftp_args->bootp_retries = strtol(arg_buf, 0, 10);
+ if(obp_tftp_args->bootp_retries < 0)
+ obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
+ }
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+
+ // find out tftp-retries
+ if (argc == 0)
+ obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
+ else {
+ argncpy(arg_str, 0, arg_buf, 100);
+ if(arg_buf[0] == 0)
+ obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
+ else {
+ obp_tftp_args->tftp_retries = strtol(arg_buf, 0, 10);
+ if(obp_tftp_args->tftp_retries < 0)
+ obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
+ }
+ arg_str = get_arg_ptr(arg_str, 1);
+ --argc;
+ }
+}
+
+/**
+ * DHCP: Wrapper for obtaining IP and configuration info from DHCP server
+ * for both IPv4 and IPv6.
+ * (makes several attempts).
+ *
+ * @param ret_buffer buffer for returning BOOTP-REPLY packet data
+ * @param fn_ip contains the following configuration information:
+ * client MAC, client IP, TFTP-server MAC,
+ * TFTP-server IP, Boot file name
+ * @param retries No. of DHCP attempts
+ * @param flags flags for specifying type of dhcp attempt (IPv4/IPv6)
+ * ZERO - attempt DHCPv4 followed by DHCPv6
+ * F_IPV4 - attempt only DHCPv4
+ * F_IPV6 - attempt only DHCPv6
+ * @return ZERO - IP and configuration info obtained;
+ * NON ZERO - error condition occurs.
+ */
+int dhcp(char *ret_buffer, struct filename_ip *fn_ip, unsigned int retries,
+ int flags)
+{
+ int i = (int) retries+1;
+ int rc = -1;
+
+ printf(" Requesting information via DHCP%s: ",
+ flags == F_IPV4 ? "v4" : flags == F_IPV6 ? "v6" : "");
+
+ if (flags != F_IPV6)
+ dhcpv4_generate_transaction_id();
+ if (flags != F_IPV4)
+ dhcpv6_generate_transaction_id();
+
+ do {
+ printf("\b\b\b%03d", i-1);
+ if (getchar() == 27) {
+ printf("\nAborted\n");
+ return -1;
+ }
+ if (!--i) {
+ printf("\nGiving up after %d DHCP requests\n", retries);
+ return -1;
+ }
+ if (!flags || (flags == F_IPV4)) {
+ ip_version = 4;
+ rc = dhcpv4(ret_buffer, fn_ip);
+ }
+ if ((!flags && (rc == -1)) || (flags == F_IPV6)) {
+ ip_version = 6;
+ set_ipv6_address(fn_ip->fd, 0);
+ rc = dhcpv6(ret_buffer, fn_ip);
+ if (rc == 0) {
+ memcpy(&fn_ip->own_ip6, get_ipv6_address(), 16);
+ break;
+ }
+
+ }
+ if (rc != -1) /* either success or non-dhcp failure */
+ break;
+ } while (1);
+ printf("\b\b\b\bdone\n");
+
+ return rc;
+}
+
+/**
+ * Seed the random number generator with our mac and current timestamp
+ */
+static void seed_rng(uint8_t mac[])
+{
+ unsigned int seed;
+
+ asm volatile("mftbl %0" : "=r"(seed));
+ seed ^= (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5];
+ srand(seed);
+}
+
+static int tftp_load(filename_ip_t *fnip, void *buffer, int len,
+ unsigned int retries)
+{
+ tftp_err_t tftp_err;
+ int rc;
+
+ rc = tftp(fnip, buffer, len, retries, &tftp_err);
+
+ if (rc > 0) {
+ printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename,
+ rc / 1024);
+ } else {
+ int ecode;
+ const char *errstr = NULL;
+ rc = tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode);
+ if (errstr)
+ netload_error(ecode, errstr);
+ }
+
+ return rc;
+}
+
+static const char *get_uuid(void)
+{
+ char *addr;
+ int len;
+
+ if (SLOF_get_property("/", "system-id", &addr, &len))
+ return NULL;
+ if (len < 37) { /* This should never happen... */
+ puts("Warning: UUID property is too short.");
+ return NULL;
+ }
+
+ return addr;
+}
+
+#define CFG_BUF_SIZE 2048
+#define MAX_PL_CFG_ENTRIES 16
+static int net_pxelinux_load(filename_ip_t *fnip, char *loadbase,
+ int maxloadlen, uint8_t *mac, int retries)
+{
+ struct pl_cfg_entry entries[MAX_PL_CFG_ENTRIES];
+ int def, rc, ilen;
+ static char *cfgbuf;
+
+ cfgbuf = malloc(CFG_BUF_SIZE);
+ if (!cfgbuf) {
+ puts("Not enough memory for pxelinux config file buffer!");
+ return -1;
+ }
+
+ rc = pxelinux_load_parse_cfg(fnip, mac, get_uuid(), retries,
+ cfgbuf, CFG_BUF_SIZE,
+ entries, MAX_PL_CFG_ENTRIES, &def);
+ if (rc < 0)
+ goto out_free;
+ if (rc == 0) {
+ puts("No valid entries in pxelinux config file.");
+ rc = -1;
+ goto out_free;
+ }
+
+ /* Load kernel */
+ strncpy(fnip->filename, entries[def].kernel,
+ sizeof(fnip->filename) - 1);
+ fnip->filename[sizeof(fnip->filename) - 1] = 0;
+ rc = tftp_load(fnip, loadbase, maxloadlen, retries);
+ if (rc <= 0)
+ goto out_free;
+
+ /* Load ramdisk */
+ if (entries[def].initrd) {
+ loadbase += rc;
+ maxloadlen -= rc;
+ if (maxloadlen <= 0) {
+ puts(" Not enough space for loading the initrd!");
+ rc = -1;
+ goto out_free;
+ }
+ strncpy(fnip->filename, entries[def].initrd,
+ sizeof(fnip->filename) - 1);
+ ilen = tftp_load(fnip, loadbase, maxloadlen, retries);
+ if (ilen < 0) {
+ rc = ilen;
+ goto out_free;
+ }
+ /* The ELF loader will move the kernel to some spot in low mem
+ * later, thus move the initrd to the end of the RAM instead */
+ memmove(loadbase + maxloadlen - ilen, loadbase, ilen);
+ /* Encode the initrd information in the device tree */
+ SLOF_set_chosen_int("linux,initrd-start",
+ (long)loadbase + maxloadlen - ilen);
+ SLOF_set_chosen_int("linux,initrd-end",
+ (long)loadbase + maxloadlen);
+ }
+
+ if (entries[def].append) {
+ SLOF_set_chosen_bytes("bootargs", entries[def].append,
+ strlen(entries[def].append) + 1);
+ }
+
+out_free:
+ free(cfgbuf);
+ return rc;
+}
+
+static void encode_response(char *pkt_buffer, size_t size, int ip_init)
+{
+ switch(ip_init) {
+ case IP_INIT_BOOTP:
+ SLOF_encode_bootp_response(pkt_buffer, size);
+ break;
+ case IP_INIT_DHCP:
+ case IP_INIT_DHCPV6_STATELESS:
+ case IP_INIT_DEFAULT:
+ SLOF_encode_dhcp_response(pkt_buffer, size);
+ break;
+ default:
+ break;
+ }
+}
+
+int netload(char *buffer, int len, char *args_fs, unsigned alen)
+{
+ int rc, filename_len;
+ filename_ip_t fn_ip;
+ int fd_device;
+ obp_tftp_args_t obp_tftp_args;
+ char null_ip[4] = { 0x00, 0x00, 0x00, 0x00 };
+ char null_ip6[16] = { 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
+ uint8_t own_mac[6];
+ char *pkt_buffer;
+
+ ip_version = 4;
+
+ pkt_buffer = SLOF_alloc_mem(MAX_PKT_SIZE);
+ if (!pkt_buffer) {
+ puts("ERROR: Unable to allocate memory");
+ return -1;
+ }
+ memset(pkt_buffer, 0, MAX_PKT_SIZE);
+
+ puts("\n Initializing NIC");
+ memset(&fn_ip, 0, sizeof(filename_ip_t));
+
+ /***********************************************************
+ *
+ * Initialize network stuff and retrieve boot informations
+ *
+ ***********************************************************/
+
+ /* Wait for link up and get mac_addr from device */
+ for(rc=0; rc<DEFAULT_BOOT_RETRIES; ++rc) {
+ if(rc > 0) {
+ set_timer(TICKS_SEC);
+ while (get_timer() > 0);
+ }
+ fd_device = socket(AF_INET, SOCK_DGRAM, 0, (char*) own_mac);
+ if(fd_device != -2)
+ break;
+ if(getchar() == 27) {
+ fd_device = -2;
+ break;
+ }
+ }
+
+ if (fd_device == -1) {
+ netload_error(0x3000, "Could not read MAC address");
+ rc = -100;
+ goto err_out;
+ }
+ else if (fd_device == -2) {
+ netload_error(0x3006, "Could not initialize network device");
+ rc = -101;
+ goto err_out;
+ }
+
+ fn_ip.fd = fd_device;
+
+ printf(" Reading MAC address from device: "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ own_mac[0], own_mac[1], own_mac[2],
+ own_mac[3], own_mac[4], own_mac[5]);
+
+ // init ethernet layer
+ set_mac_address(own_mac);
+
+ seed_rng(own_mac);
+
+ if (alen > 0) {
+ char args[256];
+ if (alen > sizeof(args) - 1) {
+ puts("ERROR: Parameter string is too long.");
+ rc = -7;
+ goto err_out;
+ }
+ /* Convert forth string into NUL-terminated C-string */
+ strncpy(args, args_fs, alen);
+ args[alen] = 0;
+ parse_args(args, &obp_tftp_args);
+ if(obp_tftp_args.bootp_retries - rc < DEFAULT_BOOT_RETRIES)
+ obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES;
+ else
+ obp_tftp_args.bootp_retries -= rc;
+ }
+ else {
+ memset(&obp_tftp_args, 0, sizeof(obp_tftp_args_t));
+ obp_tftp_args.ip_init = IP_INIT_DEFAULT;
+ obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES;
+ obp_tftp_args.tftp_retries = DEFAULT_TFTP_RETRIES;
+ }
+ memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4);
+
+ // reset of error code
+ rc = 0;
+
+ /* if we still have got all necessary parameters, then we don't
+ need to perform an BOOTP/DHCP-Request */
+ if (ip_version == 4) {
+ if (memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0
+ && memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0
+ && obp_tftp_args.filename[0] != 0) {
+
+ memcpy(&fn_ip.server_ip, &obp_tftp_args.siaddr, 4);
+ obp_tftp_args.ip_init = IP_INIT_NONE;
+ }
+ }
+ else if (ip_version == 6) {
+ if (memcmp(&obp_tftp_args.si6addr, null_ip6, 16) != 0
+ && obp_tftp_args.filename[0] != 0) {
+ memcpy(&fn_ip.server_ip6.addr[0],
+ &obp_tftp_args.si6addr.addr, 16);
+ obp_tftp_args.ip_init = IP_INIT_IPV6_MANUAL;
+ }
+ else {
+ obp_tftp_args.ip_init = IP_INIT_DHCPV6_STATELESS;
+ }
+ }
+
+ // construction of fn_ip from parameter
+ switch(obp_tftp_args.ip_init) {
+ case IP_INIT_BOOTP:
+ // if giaddr in not specified, then we have to identify
+ // the BOOTP server via broadcasts
+ if(memcmp(obp_tftp_args.giaddr, null_ip, 4) == 0) {
+ // don't do this, when using DHCP !!!
+ fn_ip.server_ip = 0xFFFFFFFF;
+ }
+ // if giaddr is specified, then we have to use this
+ // IP address as proxy to identify the BOOTP server
+ else {
+ memcpy(&fn_ip.server_ip, obp_tftp_args.giaddr, 4);
+ }
+ rc = bootp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries);
+ break;
+ case IP_INIT_DHCP:
+ rc = dhcp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4);
+ break;
+ case IP_INIT_DHCPV6_STATELESS:
+ rc = dhcp(pkt_buffer, &fn_ip,
+ obp_tftp_args.bootp_retries, F_IPV6);
+ break;
+ case IP_INIT_IPV6_MANUAL:
+ if (memcmp(&obp_tftp_args.ci6addr, null_ip6, 16)) {
+ set_ipv6_address(fn_ip.fd, &obp_tftp_args.ci6addr);
+ } else {
+ /*
+ * If no client address has been specified, then
+ * use a link-local or stateless autoconfig address
+ */
+ set_ipv6_address(fn_ip.fd, NULL);
+ memcpy(&fn_ip.own_ip6, get_ipv6_address(), 16);
+ }
+ break;
+ case IP_INIT_DEFAULT:
+ rc = dhcp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries, 0);
+ break;
+ case IP_INIT_NONE:
+ default:
+ break;
+ }
+
+ if(rc >= 0 && ip_version == 4) {
+ if(memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0
+ && memcmp(obp_tftp_args.ciaddr, &fn_ip.own_ip, 4) != 0)
+ memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4);
+
+ if(memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0
+ && memcmp(obp_tftp_args.siaddr, &fn_ip.server_ip, 4) != 0)
+ memcpy(&fn_ip.server_ip, obp_tftp_args.siaddr, 4);
+
+ // init IPv4 layer
+ set_ipv4_address(fn_ip.own_ip);
+ }
+ else if (rc >= 0 && ip_version == 6) {
+ if(memcmp(&obp_tftp_args.ci6addr.addr, null_ip6, 16) != 0
+ && memcmp(&obp_tftp_args.ci6addr.addr, &fn_ip.own_ip6, 16) != 0)
+ memcpy(&fn_ip.own_ip6, &obp_tftp_args.ci6addr.addr, 16);
+
+ if(memcmp(&obp_tftp_args.si6addr.addr, null_ip6, 16) != 0
+ && memcmp(&obp_tftp_args.si6addr.addr, &fn_ip.server_ip6.addr, 16) != 0)
+ memcpy(&fn_ip.server_ip6.addr, &obp_tftp_args.si6addr.addr, 16);
+ }
+ if (rc == -1) {
+ netload_error(0x3001, "Could not get IP address");
+ close(fn_ip.fd);
+ rc = -101;
+ goto err_out;
+ }
+
+ if (ip_version == 4) {
+ printf(" Using IPv4 address: %d.%d.%d.%d\n",
+ ((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF),
+ ((fn_ip.own_ip >> 8) & 0xFF), ( fn_ip.own_ip & 0xFF));
+ } else if (ip_version == 6) {
+ char ip6_str[40];
+ ipv6_to_str(fn_ip.own_ip6.addr, ip6_str);
+ printf(" Using IPv6 address: %s\n", ip6_str);
+ }
+
+ if (rc == -2) {
+ netload_error(0x3002, "ARP request to TFTP server "
+ "(%d.%d.%d.%d) failed",
+ ((fn_ip.server_ip >> 24) & 0xFF),
+ ((fn_ip.server_ip >> 16) & 0xFF),
+ ((fn_ip.server_ip >> 8) & 0xFF),
+ ( fn_ip.server_ip & 0xFF));
+ close(fn_ip.fd);
+ rc = -102;
+ goto err_out;
+ }
+ if (rc == -4 || rc == -3) {
+ netload_error(0x3008, "Can't obtain TFTP server IP address");
+ close(fn_ip.fd);
+ rc = -107;
+ goto err_out;
+ }
+
+ /***********************************************************
+ *
+ * Load file via TFTP into buffer provided by OpenFirmware
+ *
+ ***********************************************************/
+
+ if (obp_tftp_args.filename[0] != 0) {
+ strncpy(fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1);
+ fn_ip.filename[sizeof(fn_ip.filename)-1] = 0;
+ }
+
+ fn_ip.ip_version = ip_version;
+
+ if (ip_version == 4) {
+ printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n",
+ fn_ip.filename,
+ ((fn_ip.server_ip >> 24) & 0xFF),
+ ((fn_ip.server_ip >> 16) & 0xFF),
+ ((fn_ip.server_ip >> 8) & 0xFF),
+ ( fn_ip.server_ip & 0xFF));
+ } else if (ip_version == 6) {
+ char ip6_str[40];
+ printf(" Requesting file \"%s\" via TFTP from ", fn_ip.filename);
+ ipv6_to_str(fn_ip.server_ip6.addr, ip6_str);
+ printf("%s\n", ip6_str);
+ }
+
+ /* Do the TFTP load and print error message if necessary */
+ rc = 0;
+ filename_len = strlen(fn_ip.filename);
+ if (filename_len > 0 && fn_ip.filename[filename_len - 1] != '/' &&
+ !fn_ip.pl_cfgfile) {
+ rc = tftp_load(&fn_ip, buffer, len, obp_tftp_args.tftp_retries);
+ }
+
+ if (rc <= 0 && !obp_tftp_args.filename[0] &&
+ (!filename_len || fn_ip.filename[filename_len - 1] == '/')) {
+ rc = net_pxelinux_load(&fn_ip, buffer, len, own_mac,
+ obp_tftp_args.tftp_retries);
+ }
+
+ if (obp_tftp_args.ip_init == IP_INIT_DHCP)
+ dhcp_send_release(fn_ip.fd);
+
+ close(fn_ip.fd);
+
+ if (rc >= 0) {
+ encode_response(pkt_buffer, MAX_PKT_SIZE, obp_tftp_args.ip_init);
+ }
+ err_out:
+ SLOF_free_mem(pkt_buffer, MAX_PKT_SIZE);
+ free(fn_ip.pl_cfgfile);
+ free(fn_ip.pl_prefix);
+ return rc;
+}
diff --git a/roms/SLOF/lib/libnet/ping.c b/roms/SLOF/lib/libnet/ping.c
new file mode 100644
index 000000000..6ff6f9f91
--- /dev/null
+++ b/roms/SLOF/lib/libnet/ping.c
@@ -0,0 +1,225 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <unistd.h>
+#include <ipv4.h>
+#include <dhcp.h>
+#include <ethernet.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "args.h"
+#include "netapps.h"
+
+struct ping_args {
+ union {
+ char string[4];
+ unsigned int integer;
+ } server_ip;
+ union {
+ char string[4];
+ unsigned int integer;
+ } client_ip;
+ union {
+ char string[4];
+ unsigned int integer;
+ } gateway_ip;
+ unsigned int timeout;
+ unsigned int netmask;
+};
+
+static void
+usage(void)
+{
+ printf
+ ("\nping device-path:[device-args,]server-ip,[client-ip[\\nn]],[gateway-ip][,timeout]\n");
+
+}
+
+static int
+parse_args(const char *args, struct ping_args *ping_args)
+{
+ unsigned int argc = get_args_count(args);
+ char buf[64];
+ ping_args->timeout = 10;
+ if (argc == 0)
+ /* at least server-ip has to be specified */
+ return -1;
+ if (argc == 1) {
+ /* probably only server ip is specified */
+ argncpy(args, 0, buf, 64);
+ if (!strtoip(buf, ping_args->server_ip.string))
+ return -1;
+ return 0;
+ }
+ /* get first option from list */
+ argncpy(args, 0, buf, 64);
+ if (!strtoip(buf, ping_args->server_ip.string)) {
+ /* it is not an IP address
+ * therefore it has to be device-args
+ * device-args are not supported and just ignored */
+ args = get_arg_ptr(args, 1);
+ argc--;
+ }
+
+ argncpy(args, 0, buf, 64);
+ if (!strtoip(buf, ping_args->server_ip.string)) {
+ /* this should have been the server IP address */
+ return -1;
+ } else {
+ args = get_arg_ptr(args, 1);
+ if (!--argc)
+ return 0;
+ }
+
+ argncpy(args, 0, buf, 64);
+ if (!strtoip_netmask(buf, ping_args->client_ip.string, &ping_args->netmask)) {
+ /* this should have been the client (our) IP address */
+ return -1;
+ } else {
+ args = get_arg_ptr(args, 1);
+ if (!--argc)
+ return 0;
+ }
+ argncpy(args, 0, buf, 64);
+ if (!strtoip(buf, ping_args->gateway_ip.string)) {
+ /* this should have been the gateway IP address */
+ return -1;
+ } else {
+ args = get_arg_ptr(args, 1);
+ if (!--argc)
+ return 0;
+ }
+ argncpy(args, 0, buf, 64);
+ ping_args->timeout = strtol(args, 0, 10);
+ return 0;
+}
+
+int ping(char *args_fs, unsigned alen)
+{
+ short arp_failed = 0;
+ filename_ip_t fn_ip;
+ int fd_device;
+ struct ping_args ping_args;
+ uint8_t own_mac[6];
+ uint32_t netmask;
+ char args[256];
+ int ret = -1;
+
+ memset(&ping_args, 0, sizeof(struct ping_args));
+
+ if (alen == 0 || alen >= sizeof(args) - 1) {
+ usage();
+ return -1;
+ }
+
+ /* Convert forth string into NUL-terminated C-string */
+ memcpy(args, args_fs, alen);
+ args[alen] = 0;
+
+ if (parse_args(args, &ping_args)) {
+ usage();
+ return -1;
+ }
+
+ memset(&fn_ip, 0, sizeof(filename_ip_t));
+
+ /* Get mac_addr from device */
+ printf("\n Reading MAC address from device: ");
+ fd_device = socket(AF_INET, SOCK_DGRAM, 0, (char *) own_mac);
+ if (fd_device == -1) {
+ printf("\nE3000: Could not read MAC address\n");
+ return -100;
+ } else if (fd_device == -2) {
+ printf("\nE3006: Could not initialize network device\n");
+ return -101;
+ }
+
+ fn_ip.fd = fd_device;
+
+ printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
+ own_mac[0], own_mac[1], own_mac[2],
+ own_mac[3], own_mac[4], own_mac[5]);
+
+ // init ethernet layer
+ set_mac_address(own_mac);
+ // identify the BOOTP/DHCP server via broadcasts
+ // don't do this, when using DHCP !!!
+ // fn_ip.server_ip = 0xFFFFFFFF;
+ // memset(fn_ip.server_mac, 0xff, 6);
+
+ if (!ping_args.client_ip.integer) {
+ /* Get ip address for our mac address */
+ arp_failed = dhcp(0, &fn_ip, 30, F_IPV4);
+
+ if (arp_failed == -1) {
+ printf("\n DHCP: Could not get ip address\n");
+ goto free_out;
+ }
+
+ } else {
+ memcpy(&fn_ip.own_ip, &ping_args.client_ip.integer, 4);
+ if (ping_args.gateway_ip.integer)
+ set_ipv4_router(ping_args.gateway_ip.integer);
+ if (!ping_args.netmask) {
+ /* Netmask is not provided, assume default according to
+ * the network class
+ */
+ ping_args.netmask = get_default_ipv4_netmask(ping_args.client_ip.string);
+ }
+ set_ipv4_netmask(ping_args.netmask);
+
+ arp_failed = 1;
+ }
+
+ // reinit network stack
+ set_ipv4_address(fn_ip.own_ip);
+
+ printf(" Own IP address: %d.%d.%d.%d\n",
+ ((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF),
+ ((fn_ip.own_ip >> 8) & 0xFF), (fn_ip.own_ip & 0xFF));
+
+ netmask = get_ipv4_netmask();
+ if (netmask) {
+ printf(" Netmask : ");
+ printf("%d.%d.%d.%d\n", ((netmask >> 24) & 0xFF), ((netmask >> 16) & 0xFF),
+ ((netmask >> 8) & 0xFF), (netmask & 0xFF));
+ }
+
+ memcpy(&fn_ip.server_ip, &ping_args.server_ip.integer, 4);
+ printf(" Ping to %d.%d.%d.%d ", ((fn_ip.server_ip >> 24) & 0xFF),
+ ((fn_ip.server_ip >> 16) & 0xFF),
+ ((fn_ip.server_ip >> 8) & 0xFF), (fn_ip.server_ip & 0xFF));
+
+
+ ping_ipv4(fd_device, fn_ip.server_ip);
+
+ set_timer(TICKS_SEC / 10 * ping_args.timeout);
+ while(get_timer() > 0) {
+ receive_ether(fd_device);
+ if(pong_ipv4() == 0) {
+ printf("success\n");
+ ret = 0;
+ goto free_out;
+ }
+ }
+
+ printf("failed\n");
+free_out:
+ free(fn_ip.pl_cfgfile);
+ free(fn_ip.pl_prefix);
+ close(fn_ip.fd);
+
+ return ret;
+}
diff --git a/roms/SLOF/lib/libnet/pxelinux.c b/roms/SLOF/lib/libnet/pxelinux.c
new file mode 100644
index 000000000..b32f23351
--- /dev/null
+++ b/roms/SLOF/lib/libnet/pxelinux.c
@@ -0,0 +1,252 @@
+/*****************************************************************************
+ * pxelinux.cfg-style config file support.
+ *
+ * See https://www.syslinux.org/wiki/index.php?title=PXELINUX for information
+ * about the pxelinux config file layout.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the BSD License which accompanies this distribution, and is
+ * available at http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth, Red Hat Inc. - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "tftp.h"
+#include "pxelinux.h"
+
+/**
+ * Call tftp() and report errors (excet "file-not-found" errors)
+ */
+static int pxelinux_tftp_load(filename_ip_t *fnip, void *buffer, int len,
+ int retries)
+{
+ tftp_err_t tftp_err;
+ int rc, ecode;
+
+ rc = tftp(fnip, buffer, len, retries, &tftp_err);
+
+ if (rc > 0) {
+ printf("\r TFTP: Received %s (%d bytes)\n",
+ fnip->filename, rc);
+ } else if (rc == -3) {
+ /* Ignore file-not-found (since we are probing the files)
+ * and simply erase the "Receiving data: 0 KBytes" string */
+ printf("\r \r");
+ } else {
+ const char *errstr = NULL;
+ rc = tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode);
+ if (errstr)
+ printf("\r TFTP error: %s\n", errstr);
+ }
+
+ return rc;
+}
+
+/**
+ * Try to load a pxelinux.cfg file by probing the possible file names.
+ * Note that this function will overwrite filename_ip_t->filename.
+ */
+static int pxelinux_load_cfg(filename_ip_t *fn_ip, uint8_t *mac, const char *uuid,
+ int retries, char *cfgbuf, int cfgbufsize)
+{
+ int rc;
+ unsigned idx;
+ char *baseptr;
+
+ /* Did we get a usable base directory via DHCP? */
+ if (fn_ip->pl_prefix) {
+ idx = strlen(fn_ip->pl_prefix);
+ /* Do we have enough space left to store a UUID file name? */
+ if (idx > sizeof(fn_ip->filename) - 36) {
+ puts("Error: pxelinux prefix is too long!");
+ return -1;
+ }
+ strcpy(fn_ip->filename, fn_ip->pl_prefix);
+ baseptr = &fn_ip->filename[idx];
+ } else {
+ /* Try to get a usable base directory from the DHCP bootfile name */
+ baseptr = strrchr(fn_ip->filename, '/');
+ if (!baseptr)
+ baseptr = fn_ip->filename;
+ else
+ ++baseptr;
+ /* Check that we've got enough space to store "pxelinux.cfg/"
+ * and the UUID (which is the longest file name) there */
+ if ((size_t)(baseptr - fn_ip->filename) > (sizeof(fn_ip->filename) - 50)) {
+ puts("Error: The bootfile string is too long for "
+ "deriving the pxelinux.cfg file name from it.");
+ return -1;
+ }
+ strcpy(baseptr, "pxelinux.cfg/");
+ baseptr += strlen(baseptr);
+ }
+
+ puts("Trying pxelinux.cfg files...");
+
+ /* Try to load config file according to file name in DHCP option 209 */
+ if (fn_ip->pl_cfgfile) {
+ if (strlen(fn_ip->pl_cfgfile) + strlen(fn_ip->filename)
+ > sizeof(fn_ip->filename)) {
+ puts("Error: pxelinux.cfg prefix + filename too long!");
+ return -1;
+ }
+ strcpy(baseptr, fn_ip->pl_cfgfile);
+ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, retries);
+ if (rc > 0) {
+ return rc;
+ }
+ }
+
+ /* Try to load config file with name based on the VM UUID */
+ if (uuid) {
+ strcpy(baseptr, uuid);
+ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, retries);
+ if (rc > 0) {
+ return rc;
+ }
+ }
+
+ /* Look for config file with MAC address in its name */
+ sprintf(baseptr, "01-%02x-%02x-%02x-%02x-%02x-%02x",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, retries);
+ if (rc > 0) {
+ return rc;
+ }
+
+ /* Look for config file with IP address in its name */
+ if (fn_ip->ip_version == 4) {
+ sprintf(baseptr, "%02X%02X%02X%02X",
+ (fn_ip->own_ip >> 24) & 0xff,
+ (fn_ip->own_ip >> 16) & 0xff,
+ (fn_ip->own_ip >> 8) & 0xff,
+ fn_ip->own_ip & 0xff);
+ for (idx = 0; idx <= 7; idx++) {
+ baseptr[8 - idx] = 0;
+ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize,
+ retries);
+ if (rc > 0) {
+ return rc;
+ }
+ }
+ }
+
+ /* Try "default" config file */
+ strcpy(baseptr, "default");
+ rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize, retries);
+
+ return rc;
+}
+
+/**
+ * Parse a pxelinux-style configuration file.
+ * The discovered entries are filled into the "struct pl_cfg_entry entries[]"
+ * array. Note that the callers must keep the cfg buffer valid as long as
+ * they wish to access the "struct pl_cfg_entry" entries, since the pointers
+ * in entries point to the original location in the cfg buffer area. The cfg
+ * buffer is altered for this, too, e.g. terminating NUL-characters are put
+ * into the right locations.
+ * @param cfg Pointer to the buffer with contents of the config file.
+ * The caller must make sure that it is NUL-terminated.
+ * @param cfgsize Size of the cfg data (including the terminating NUL)
+ * @param entries Pointer to array where the results should be put into
+ * @param max_entries Number of available slots in the entries array
+ * @param def_ent Used to return the index of the default entry
+ * @return Number of valid entries
+ */
+int pxelinux_parse_cfg(char *cfg, int cfgsize, struct pl_cfg_entry *entries,
+ int max_entries, int *def_ent)
+{
+ int num_entries = 0;
+ char *ptr = cfg, *nextptr, *eol, *arg;
+ char *defaultlabel = NULL;
+
+ *def_ent = 0;
+
+ while (ptr < cfg + cfgsize && num_entries < max_entries) {
+ eol = strchr(ptr, '\n');
+ if (!eol) {
+ eol = cfg + cfgsize - 1;
+ }
+ nextptr = eol + 1;
+ do {
+ *eol-- = '\0'; /* Remove spaces, tabs and returns */
+ } while (eol >= ptr &&
+ (*eol == '\r' || *eol == ' ' || *eol == '\t'));
+ while (*ptr == ' ' || *ptr == '\t') {
+ ptr++;
+ }
+ if (*ptr == 0 || *ptr == '#') {
+ goto nextline; /* Ignore comments and empty lines */
+ }
+ arg = strchr(ptr, ' '); /* Search space between cmnd and arg */
+ if (!arg) {
+ arg = strchr(ptr, '\t');
+ }
+ if (!arg) {
+ printf("Failed to parse this line:\n %s\n", ptr);
+ goto nextline;
+ }
+ *arg++ = 0;
+ while (*arg == ' ' || *arg == '\t') {
+ arg++;
+ }
+ if (!strcasecmp("default", ptr)) {
+ defaultlabel = arg;
+ } else if (!strcasecmp("label", ptr)) {
+ entries[num_entries].label = arg;
+ if (defaultlabel && !strcmp(arg, defaultlabel)) {
+ *def_ent = num_entries;
+ }
+ num_entries++;
+ } else if (!strcasecmp("kernel", ptr) && num_entries) {
+ entries[num_entries - 1].kernel = arg;
+ } else if (!strcasecmp("initrd", ptr) && num_entries) {
+ entries[num_entries - 1].initrd = arg;
+ } else if (!strcasecmp("append", ptr) && num_entries) {
+ entries[num_entries - 1].append = arg;
+ } else {
+ printf("Command '%s' is not supported.\n", ptr);
+ }
+nextline:
+ ptr = nextptr;
+ }
+
+ return num_entries;
+}
+
+/**
+ * Try to load and parse a pxelinux-style configuration file.
+ * @param fn_ip must contain server and client IP information
+ * @param mac MAC address which should be used for probing
+ * @param uuid UUID which should be used for probing (can be NULL)
+ * @param retries Amount of TFTP retries before giving up
+ * @param cfgbuf Pointer to the buffer where config file should be loaded
+ * @param cfgsize Size of the cfgbuf buffer
+ * @param entries Pointer to array where the results should be put into
+ * @param max_entries Number of available slots in the entries array
+ * @param def_ent Used to return the index of the default entry
+ * @return Number of valid entries
+ */
+int pxelinux_load_parse_cfg(filename_ip_t *fn_ip, uint8_t *mac, const char *uuid,
+ int retries, char *cfgbuf, int cfgsize,
+ struct pl_cfg_entry *entries, int max_entries,
+ int *def_ent)
+{
+ int rc;
+
+ rc = pxelinux_load_cfg(fn_ip, mac, uuid, retries, cfgbuf, cfgsize - 1);
+ if (rc < 0)
+ return rc;
+ assert(rc < cfgsize);
+
+ cfgbuf[rc++] = '\0'; /* Make sure it is NUL-terminated */
+
+ return pxelinux_parse_cfg(cfgbuf, rc, entries, max_entries, def_ent);
+}
diff --git a/roms/SLOF/lib/libnet/pxelinux.h b/roms/SLOF/lib/libnet/pxelinux.h
new file mode 100644
index 000000000..0514d0d94
--- /dev/null
+++ b/roms/SLOF/lib/libnet/pxelinux.h
@@ -0,0 +1,33 @@
+/*****************************************************************************
+ * Definitions for pxelinux-style config file support
+ *
+ * Copyright 2018 Red Hat, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * Thomas Huth, Red Hat Inc. - initial implementation
+ *****************************************************************************/
+
+#ifndef LIBNET_PXELINUX_H
+#define LIBNET_PXELINUX_H
+
+/* This structure holds the data from one pxelinux.cfg file entry */
+struct pl_cfg_entry {
+ const char *label;
+ const char *kernel;
+ const char *initrd;
+ const char *append;
+};
+
+int pxelinux_parse_cfg(char *cfg, int cfgsize, struct pl_cfg_entry *entries,
+ int max_entries, int *def_ent);
+int pxelinux_load_parse_cfg(filename_ip_t *fn_ip, uint8_t *mac, const char *uuid,
+ int retries, char *cfgbuf, int cfgsize,
+ struct pl_cfg_entry *entries,
+ int max_entries, int *def_ent);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/tcp.c b/roms/SLOF/lib/libnet/tcp.c
new file mode 100644
index 000000000..faa0b83ac
--- /dev/null
+++ b/roms/SLOF/lib/libnet/tcp.c
@@ -0,0 +1,46 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/************************ DEFINITIONS & DECLARATIONS *********************/
+
+#include <tcp.h>
+#include <sys/socket.h>
+
+/****************************** LOCAL VARIABLES **************************/
+
+/****************************** IMPLEMENTATION ***************************/
+
+/**
+ * TCP: Handles TCP-packets according to Receive-handle diagram.
+ *
+ * @param tcp_packet TCP-packet to be handled
+ * @param packetsize Length of the packet
+ * @return ZERO - packet handled successfully;
+ * NON ZERO - packet was not handled (e.g. bad format)
+ */
+int8_t handle_tcp(uint8_t * tcp_packet, int32_t packetsize)
+{
+ return -1;
+}
+
+/**
+ * NET: This function handles situation when "Destination unreachable"
+ * ICMP-error occurs during sending TCP-packet.
+ *
+ * @param err_code Error Code (e.g. "Host unreachable")
+ * @param packet original TCP-packet
+ * @param packetsize length of the packet
+ * @see handle_icmp
+ */
+void handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t err_code)
+{
+}
diff --git a/roms/SLOF/lib/libnet/tcp.h b/roms/SLOF/lib/libnet/tcp.h
new file mode 100644
index 000000000..375afd771
--- /dev/null
+++ b/roms/SLOF/lib/libnet/tcp.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _TCP_H
+#define _TCP_H
+
+#include <stdint.h>
+
+#define IPTYPE_TCP 6
+
+/* Handles TCP-packets that are detected by any network layer. */
+extern int8_t handle_tcp(uint8_t * udp_packet, int32_t packetsize);
+
+/* Handles TCP related ICMP-Dest.Unreachable packets that are detected by
+ * the network layers. */
+extern void handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t err_code);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/tftp.c b/roms/SLOF/lib/libnet/tftp.c
new file mode 100644
index 000000000..9a5817a7b
--- /dev/null
+++ b/roms/SLOF/lib/libnet/tftp.c
@@ -0,0 +1,793 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <tftp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+
+#include <ethernet.h>
+#include <ipv4.h>
+#include <ipv6.h>
+#include <udp.h>
+#include <dns.h>
+
+//#define __DEBUG__
+
+#define MAX_BLOCKSIZE 1428
+#define BUFFER_LEN 256
+#define INVALID_BUFFER ((void *)-1L)
+
+#define ENOTFOUND 1
+#define EACCESS 2
+#define EBADOP 4
+#define EBADID 5
+#define ENOUSER 7
+//#define EUNDEF 0
+//#define ENOSPACE 3
+//#define EEXISTS 6
+
+#define RRQ 1
+#define WRQ 2
+#define DATA 3
+#define ACK 4
+#define ERROR 5
+#define OACK 6
+
+/* Local variables */
+static unsigned char packet[BUFFER_LEN];
+static unsigned char *buffer = INVALID_BUFFER;
+static unsigned short block;
+static unsigned short blocksize;
+static char blocksize_str[6]; /* Blocksize string for read request */
+static int received_len;
+static unsigned int retries;
+static int huge_load;
+static int len;
+static int tftp_finished;
+static int lost_packets;
+static int tftp_errno;
+static int ip_version;
+static short port_number;
+static tftp_err_t *tftp_err;
+static filename_ip_t *fn_ip;
+static int progress_first;
+static int progress_last_bytes;
+
+/**
+ * dump_package - Prints a package.
+ *
+ * @package: package which is to print
+ * @len: length of the package
+ */
+#ifdef __DEBUG__
+
+static void dump_package(unsigned char *buffer, unsigned int len)
+{
+ int i;
+
+ for (i = 1; i <= len; i++) {
+ printf("%02x%02x ", buffer[i - 1], buffer[i]);
+ i++;
+ if ((i % 16) == 0)
+ printf("\n");
+ }
+ printf("\n");
+}
+#endif
+
+/**
+ * send_rrq - Sends a read request package.
+ *
+ * @fd: Socket Descriptor
+ */
+static void send_rrq(int fd)
+{
+ int ip_len = 0;
+ int ip6_payload_len = 0;
+ unsigned short udp_len = 0;
+ const char mode[] = "octet";
+ char *ptr = NULL;
+ struct iphdr *ip = NULL;
+ struct ip6hdr *ip6 = NULL;
+ struct udphdr *udph = NULL;
+ struct tftphdr *tftp = NULL;
+
+ memset(packet, 0, BUFFER_LEN);
+
+ if (4 == ip_version) {
+ ip = (struct iphdr *) packet;
+ udph = (struct udphdr *) (ip + 1);
+ ip_len = sizeof(struct iphdr) + sizeof(struct udphdr)
+ + strlen(fn_ip->filename) + strlen(mode) + 4
+ + strlen("blksize") + strlen(blocksize_str) + 2;
+ fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0,
+ fn_ip->server_ip);
+ }
+ else if (6 == ip_version) {
+ ip6 = (struct ip6hdr *) packet;
+ udph = (struct udphdr *) (ip6 + 1);
+ ip6_payload_len = sizeof(struct udphdr)
+ + strlen(fn_ip->filename) + strlen(mode) + 4
+ + strlen("blksize") + strlen(blocksize_str) + 2;
+ ip_len = sizeof(struct ip6hdr) + ip6_payload_len;
+ fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(),
+ &(fn_ip->server_ip6));
+
+ }
+ udp_len = htons(sizeof(struct udphdr)
+ + strlen(fn_ip->filename) + strlen(mode) + 4
+ + strlen("blksize") + strlen(blocksize_str) + 2);
+ fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(69));
+
+ tftp = (struct tftphdr *) (udph + 1);
+ tftp->th_opcode = htons(RRQ);
+
+ ptr = (char *) &tftp->th_data;
+ memcpy(ptr, fn_ip->filename, strlen(fn_ip->filename) + 1);
+
+ ptr += strlen(fn_ip->filename) + 1;
+ memcpy(ptr, mode, strlen(mode) + 1);
+
+ ptr += strlen(mode) + 1;
+ memcpy(ptr, "blksize", strlen("blksize") + 1);
+
+ ptr += strlen("blksize") + 1;
+ memcpy(ptr, blocksize_str, strlen(blocksize_str) + 1);
+
+ send_ip (fd, packet, ip_len);
+
+#ifdef __DEBUG__
+ printf("tftp RRQ with %d bytes transmitted.\n", ip_len);
+#endif
+ return;
+}
+
+/**
+ * send_ack - Sends a acknowlege package.
+ *
+ * @blckno: block number
+ * @dport: UDP destination port
+ */
+static void send_ack(int fd, int blckno, unsigned short dport)
+{
+ int ip_len = 0;
+ int ip6_payload_len = 0;
+ unsigned short udp_len = 0;
+ struct iphdr *ip = NULL;
+ struct ip6hdr *ip6 = NULL;
+ struct udphdr *udph = NULL;
+ struct tftphdr *tftp = NULL;
+
+ memset(packet, 0, BUFFER_LEN);
+
+ if (4 == ip_version) {
+ ip = (struct iphdr *) packet;
+ udph = (struct udphdr *) (ip + 1);
+ ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 4;
+ fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0,
+ fn_ip->server_ip);
+ }
+ else if (6 == ip_version) {
+ ip6 = (struct ip6hdr *) packet;
+ udph = (struct udphdr *) (ip6 + 1);
+ ip6_payload_len = sizeof(struct udphdr) + 4;
+ ip_len = sizeof(struct ip6hdr) + ip6_payload_len;
+ fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(),
+ &(fn_ip->server_ip6));
+ }
+ udp_len = htons(sizeof(struct udphdr) + 4);
+ fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport));
+
+ tftp = (struct tftphdr *) (udph + 1);
+ tftp->th_opcode = htons(ACK);
+ tftp->th_data = htons(blckno);
+
+ send_ip(fd, packet, ip_len);
+
+#ifdef __DEBUG__
+ printf("tftp ACK %d bytes transmitted.\n", ip_len);
+#endif
+
+ return;
+}
+
+/**
+ * send_error - Sends an error package.
+ *
+ * @fd: Socket Descriptor
+ * @error_code: Used sub code for error packet
+ * @dport: UDP destination port
+ */
+static void send_error(int fd, int error_code, unsigned short dport)
+{
+ int ip_len = 0;
+ int ip6_payload_len = 0;
+ unsigned short udp_len = 0;
+ struct ip6hdr *ip6 = NULL;
+ struct iphdr *ip = NULL;
+ struct udphdr *udph = NULL;
+ struct tftphdr *tftp = NULL;
+
+ memset(packet, 0, BUFFER_LEN);
+
+ if (4 == ip_version) {
+ ip = (struct iphdr *) packet;
+ udph = (struct udphdr *) (ip + 1);
+ ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 5;
+ fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0,
+ fn_ip->server_ip);
+ }
+ else if (6 == ip_version) {
+ ip6 = (struct ip6hdr *) packet;
+ udph = (struct udphdr *) (ip6 + 1);
+ ip6_payload_len = sizeof(struct udphdr) + 5;
+ ip_len = sizeof(struct ip6hdr) + ip6_payload_len;
+ fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(),
+ &(fn_ip->server_ip6));
+ }
+ udp_len = htons(sizeof(struct udphdr) + 5);
+ fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport));
+
+ tftp = (struct tftphdr *) (udph + 1);
+ tftp->th_opcode = htons(ERROR);
+ tftp->th_data = htons(error_code);
+ ((char *) &tftp->th_data)[2] = 0;
+
+ send_ip(fd, packet, ip_len);
+
+#ifdef __DEBUG__
+ printf("tftp ERROR %d bytes transmitted.\n", ip_len);
+#endif
+
+ return;
+}
+
+static void print_progress(int urgent, int received_bytes)
+{
+ static unsigned int i = 1;
+ char buffer[100];
+ char *ptr;
+
+ // 1MB steps or 0x400 times or urgent
+ if(((received_bytes - progress_last_bytes) >> 20) > 0
+ || (i & 0x3FF) == 0 || urgent) {
+ if (!progress_first) {
+ sprintf(buffer, "%d KBytes", (progress_last_bytes >> 10));
+ for(ptr = buffer; *ptr != 0; ++ptr)
+ *ptr = '\b';
+ printf("%s", buffer);
+ }
+ printf("%d KBytes", (received_bytes >> 10));
+ i = 1;
+ progress_first = 0;
+ progress_last_bytes = received_bytes;
+ }
+ ++i;
+}
+
+/**
+ * get_blksize tries to extract the blksize from the OACK package
+ * the TFTP returned. From RFC 1782
+ * The OACK packet has the following format:
+ *
+ * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+ * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 |
+ * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+ *
+ * @param buffer the network packet
+ * @param len the length of the network packet
+ * @return the blocksize the server supports or 0 for error
+ */
+static int get_blksize(unsigned char *buffer, unsigned int len)
+{
+ unsigned char *orig = buffer;
+ /* skip all headers until tftp has been reached */
+ buffer += sizeof(struct udphdr);
+ /* skip opc */
+ buffer += 2;
+ while (buffer < orig + len) {
+ if (!memcmp(buffer, "blksize", strlen("blksize") + 1))
+ return (unsigned short) strtoul((char *) (buffer +
+ strlen("blksize") + 1),
+ (char **) NULL, 10);
+ else {
+ /* skip the option name */
+ buffer = (unsigned char *) strchr((char *) buffer, 0);
+ if (!buffer)
+ return 0;
+ buffer++;
+ /* skip the option value */
+ buffer = (unsigned char *) strchr((char *) buffer, 0);
+ if (!buffer)
+ return 0;
+ buffer++;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Handle incoming tftp packets after read request was sent
+ *
+ * this function also prints out some status characters
+ * \|-/ for each packet received
+ * A for an arp packet
+ * I for an ICMP packet
+ * #+* for different unexpected TFTP packets (not very good)
+ *
+ * @param fd socket descriptor
+ * @param packet points to the UDP header of the packet
+ * @param len the length of the network packet
+ * @return ZERO if packet was handled successfully
+ * ERRORCODE if error occurred
+ */
+int32_t handle_tftp(int fd, uint8_t *pkt, int32_t packetsize)
+{
+ struct udphdr *udph;
+ struct tftphdr *tftp;
+
+ /* buffer is only set if we are handling TFTP */
+ if (buffer == INVALID_BUFFER)
+ return 0;
+
+#ifndef __DEBUG__
+ print_progress(0, received_len);
+#endif
+ udph = (struct udphdr *) pkt;
+ tftp = (struct tftphdr *) ((void *) udph + sizeof(struct udphdr));
+ set_timer(TICKS_SEC);
+
+#ifdef __DEBUG__
+ dump_package(pkt, packetsize);
+#endif
+
+ port_number = udph->uh_sport;
+ if (tftp->th_opcode == htons(OACK)) {
+ /* an OACK means that the server answers our blocksize request */
+ blocksize = get_blksize(pkt, packetsize);
+ if (!blocksize || blocksize > MAX_BLOCKSIZE) {
+ send_error(fd, 8, port_number);
+ tftp_errno = -8;
+ goto error;
+ }
+ send_ack(fd, 0, port_number);
+ } else if (tftp->th_opcode == htons(ACK)) {
+ /* an ACK means that the server did not answers
+ * our blocksize request, therefore we will set the blocksize
+ * to the default value of 512 */
+ blocksize = 512;
+ send_ack(fd, 0, port_number);
+ } else if ((unsigned char) tftp->th_opcode == ERROR) {
+#ifdef __DEBUG__
+ printf("tftp->th_opcode : %x\n", tftp->th_opcode);
+ printf("tftp->th_data : %x\n", tftp->th_data);
+#endif
+ switch ( (uint8_t) tftp->th_data) {
+ case ENOTFOUND:
+ tftp_errno = -3; // ERROR: file not found
+ break;
+ case EACCESS:
+ tftp_errno = -4; // ERROR: access violation
+ break;
+ case EBADOP:
+ tftp_errno = -5; // ERROR: illegal TFTP operation
+ break;
+ case EBADID:
+ tftp_errno = -6; // ERROR: unknown transfer ID
+ break;
+ case ENOUSER:
+ tftp_errno = -7; // ERROR: no such user
+ break;
+ default:
+ tftp_errno = -1; // ERROR: unknown error
+ }
+ goto error;
+ } else if (tftp->th_opcode == DATA) {
+ /* DATA PACKAGE */
+ if (block + 1 == tftp->th_data) {
+ ++block;
+ }
+ else if( block == 0xffff && huge_load != 0
+ && (tftp->th_data == 0 || tftp->th_data == 1) ) {
+ block = tftp->th_data;
+ }
+ else if (tftp->th_data == block) {
+#ifdef __DEBUG__
+ printf
+ ("\nTFTP: Received block %x, expected block was %x\n",
+ tftp->th_data, block + 1);
+ printf("\b+ ");
+#endif
+ send_ack(fd, tftp->th_data, port_number);
+ lost_packets++;
+ tftp_err->bad_tftp_packets++;
+ return 0;
+ } else if (tftp->th_data < block) {
+#ifdef __DEBUG__
+ printf
+ ("\nTFTP: Received block %x, expected block was %x\n",
+ tftp->th_data, block + 1);
+ printf("\b* ");
+#endif
+ /* This means that an old data packet appears (again);
+ * this happens sometimes if we don't answer fast enough
+ * and a timeout is generated on the server side;
+ * as we already have this packet we just ignore it */
+ tftp_err->bad_tftp_packets++;
+ return 0;
+ } else {
+ tftp_err->blocks_missed = block + 1;
+ tftp_err->blocks_received = tftp->th_data;
+ tftp_errno = -42;
+ goto error;
+ }
+ tftp_err->bad_tftp_packets = 0;
+ /* check if our buffer is large enough */
+ if (received_len + udph->uh_ulen - 12 > len) {
+ tftp_errno = -2;
+ goto error;
+ }
+ memcpy(buffer + received_len, &tftp->th_data + 1,
+ udph->uh_ulen - 12);
+ send_ack(fd, tftp->th_data, port_number);
+ received_len += udph->uh_ulen - 12;
+ /* Last packet reached if the payload of the UDP packet
+ * is smaller than blocksize + 12
+ * 12 = UDP header (8) + 4 bytes TFTP payload */
+ if (udph->uh_ulen < blocksize + 12) {
+ tftp_finished = 1;
+ return 0;
+ }
+ /* 0xffff is the highest block number possible
+ * see the TFTP RFCs */
+
+ if (block >= 0xffff && huge_load == 0) {
+ tftp_errno = -9;
+ goto error;
+ }
+ } else {
+#ifdef __DEBUG__
+ printf("Unknown packet %x\n", tftp->th_opcode);
+ printf("\b# ");
+#endif
+ tftp_err->bad_tftp_packets++;
+ return 0;
+ }
+
+ return 0;
+
+error:
+#ifdef __DEBUG__
+ printf("\nTFTP errno: %d\n", tftp_errno);
+#endif
+ tftp_finished = 1;
+ return tftp_errno;
+}
+
+/**
+ * TFTP: This function handles situation when "Destination unreachable"
+ * ICMP-error occurs during sending TFTP-packet.
+ *
+ * @param err_code Error Code (e.g. "Host unreachable")
+ */
+void handle_tftp_dun(uint8_t err_code)
+{
+ tftp_errno = - err_code - 10;
+ tftp_finished = 1;
+}
+
+/**
+ * TFTP: Interface function to load files via TFTP.
+ *
+ * @param _fn_ip contains the following configuration information:
+ * client IP, TFTP-server IP, filename to be loaded
+ * @param _buffer destination buffer for the file
+ * @param _len size of destination buffer
+ * @param _retries max number of retries
+ * @param _tftp_err contains info about TFTP-errors (e.g. lost packets)
+ * @return ZERO - error condition occurs
+ * NON ZERO - size of received file
+ */
+int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len,
+ unsigned int _retries, tftp_err_t * _tftp_err)
+{
+ retries = _retries;
+ fn_ip = _fn_ip;
+ len = _len;
+ ip_version = _fn_ip->ip_version;
+ tftp_errno = 0;
+ tftp_err = _tftp_err;
+ tftp_err->bad_tftp_packets = 0;
+ tftp_err->no_packets = 0;
+
+ block = 0;
+ received_len = 0;
+ tftp_finished = 0;
+ lost_packets = 0;
+ port_number = -1;
+ progress_first = -1;
+ progress_last_bytes = 0;
+ huge_load = 1;
+
+ /* Default blocksize must be 512 for TFTP servers
+ * which do not support the RRQ blocksize option */
+ blocksize = 512;
+
+ /* Preferred blocksize - used as option for the read request */
+ sprintf(blocksize_str, "%d", MAX_BLOCKSIZE);
+
+ printf(" Receiving data: ");
+ print_progress(-1, 0);
+
+ /* Set buffer to a valid address, enables handling of received packets */
+ buffer = _buffer;
+
+ set_timer(TICKS_SEC);
+ send_rrq(fn_ip->fd);
+
+ while (! tftp_finished) {
+ /* if timeout (no packet received) */
+ if(get_timer() <= 0) {
+ /* the server doesn't seem to retry let's help out a bit */
+ if (tftp_err->no_packets > 4 && port_number != -1
+ && block > 1) {
+ send_ack(fn_ip->fd, block, port_number);
+ }
+ else if (port_number == -1 && block == 0
+ && (tftp_err->no_packets&3) == 3) {
+ printf("\nRepeating TFTP read request...\n");
+ send_rrq(fn_ip->fd);
+ }
+ tftp_err->no_packets++;
+ set_timer(TICKS_SEC);
+ }
+
+ /* handle received packets */
+ receive_ether(fn_ip->fd);
+
+ /* bad_tftp_packets are counted whenever we receive a TFTP packet
+ * which was not expected; if this gets larger than 'retries'
+ * we just exit */
+ if (tftp_err->bad_tftp_packets > retries) {
+ tftp_errno = -40;
+ break;
+ }
+
+ /* no_packets counts the times we have returned from receive_ether()
+ * without any packet received; if this gets larger than 'retries'
+ * we also just exit */
+ if (tftp_err->no_packets > retries) {
+ tftp_errno = -41;
+ break;
+ }
+ }
+
+ /* Setting buffer invalid to disable handling of received packets */
+ buffer = INVALID_BUFFER;
+
+ if (tftp_errno)
+ return tftp_errno;
+
+ print_progress(-1, received_len);
+ printf("\n");
+ if (lost_packets)
+ printf("Lost ACK packets: %d\n", lost_packets);
+
+ return received_len;
+}
+
+/**
+ * Parses a tftp arguments, extracts all
+ * parameters and fills server ip according to this
+ *
+ * Parameters:
+ * @param buffer string with arguments,
+ * @param server_ip server ip as result
+ * @param filename default filename
+ * @param fd Socket descriptor
+ * @param len len of the buffer,
+ * @return 0 on SUCCESS and -1 on failure
+ */
+int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd,
+ int len)
+{
+ char *raw;
+ char *tmp, *tmp1;
+ int i, j = 0;
+ char domainname[256];
+ uint8_t server_ip6[16];
+
+ raw = malloc(len);
+ if (raw == NULL) {
+ printf("\n unable to allocate memory, parsing failed\n");
+ return -1;
+ }
+ strncpy(raw, (const char *)buffer, len);
+ /* tftp url contains tftp://[fd00:4f53:4444:90:214:5eff:fed9:b200]/testfile */
+ if (strncmp(raw, "tftp://", 7)){
+ printf("\n tftp missing in %s\n", raw);
+ free(raw);
+ return -1;
+ }
+ tmp = strchr(raw, '[');
+ if (tmp != NULL && *tmp == '[') {
+ /* check for valid ipv6 address */
+ tmp1 = strchr(tmp, ']');
+ if (tmp1 == NULL) {
+ printf("\n missing ] in %s\n", raw);
+ free(raw);
+ return -1;
+ }
+ i = tmp1 - tmp;
+ /* look for file name */
+ tmp1 = strchr(tmp, '/');
+ if (tmp1 == NULL) {
+ printf("\n missing filename in %s\n", raw);
+ free(raw);
+ return -1;
+ }
+ tmp[i] = '\0';
+ /* check for 16 byte ipv6 address */
+ if (!str_to_ipv6(tmp + 1, (uint8_t *)server_ip)) {
+ printf("\n wrong format IPV6 address in %s\n", raw);
+ free(raw);
+ return -1;;
+ }
+ else {
+ /* found filename */
+ strcpy(filename, tmp1 + 1);
+ free(raw);
+ return 0;
+ }
+ }
+ else {
+ /* here tftp://hostname/testfile from option request of dhcp */
+ /* look for dns server name */
+ tmp1 = strchr(raw, '.');
+ if (tmp1 == NULL) {
+ printf("\n missing . seperator in %s\n", raw);
+ free(raw);
+ return -1;
+ }
+ /* look for domain name beyond dns server name
+ * so ignore the current . and look for one more */
+ tmp = strchr(tmp1 + 1, '.');
+ if (tmp == NULL) {
+ printf("\n missing domain in %s\n", raw);
+ free(raw);
+ return -1;
+ }
+ tmp1 = strchr(tmp1, '/');
+ if (tmp1 == NULL) {
+ printf("\n missing filename in %s\n", raw);
+ free(raw);
+ return -1;
+ }
+ j = tmp1 - (raw + 7);
+ tmp = raw + 7;
+ tmp[j] = '\0';
+ strcpy(domainname, tmp);
+ if (dns_get_ip(fd, domainname, server_ip6, 6) == 0) {
+ printf("\n DNS failed for IPV6\n");
+ return -1;
+ }
+ ipv6_to_str(server_ip6, server_ip);
+
+ strcpy(filename, tmp1 + 1);
+ free(raw);
+ return 0;
+ }
+}
+
+int tftp_get_error_info(filename_ip_t *fnip, tftp_err_t *tftperr, int rc,
+ const char **errstr, int *ecode)
+{
+ static char estrbuf[80];
+
+ if (rc == -1) {
+ *ecode = 0x3003;
+ *errstr = "unknown TFTP error";
+ return -103;
+ } else if (rc == -2) {
+ *ecode = 0x3004;
+ snprintf(estrbuf, sizeof(estrbuf),
+ "TFTP buffer of %d bytes is too small for %s", len,
+ fnip->filename);
+ *errstr = estrbuf;
+ return -104;
+ } else if (rc == -3) {
+ *ecode = 0x3009;
+ snprintf(estrbuf, sizeof(estrbuf), "file not found: %s",
+ fnip->filename);
+ *errstr = estrbuf;
+ return -108;
+ } else if (rc == -4) {
+ *ecode = 0x3010;
+ *errstr = "TFTP access violation";
+ return -109;
+ } else if (rc == -5) {
+ *ecode = 0x3011;
+ *errstr = "illegal TFTP operation";
+ return -110;
+ } else if (rc == -6) {
+ *ecode = 0x3012;
+ *errstr = "unknown TFTP transfer ID";
+ return -111;
+ } else if (rc == -7) {
+ *ecode = 0x3013;
+ *errstr = "no such TFTP user";
+ return -112;
+ } else if (rc == -8) {
+ *ecode = 0x3017;
+ *errstr = "TFTP blocksize negotiation failed";
+ return -116;
+ } else if (rc == -9) {
+ *ecode = 0x3018;
+ *errstr = "file exceeds maximum TFTP transfer size";
+ return -117;
+ } else if (rc <= -10 && rc >= -15) {
+ const char *icmp_err_str;
+ switch (rc) {
+ case -ICMP_NET_UNREACHABLE - 10:
+ icmp_err_str = "net unreachable";
+ break;
+ case -ICMP_HOST_UNREACHABLE - 10:
+ icmp_err_str = "host unreachable";
+ break;
+ case -ICMP_PROTOCOL_UNREACHABLE - 10:
+ icmp_err_str = "protocol unreachable";
+ break;
+ case -ICMP_PORT_UNREACHABLE - 10:
+ icmp_err_str = "port unreachable";
+ break;
+ case -ICMP_FRAGMENTATION_NEEDED - 10:
+ icmp_err_str = "fragmentation needed and DF set";
+ break;
+ case -ICMP_SOURCE_ROUTE_FAILED - 10:
+ icmp_err_str = "source route failed";
+ break;
+ default:
+ icmp_err_str = "UNKNOWN";
+ break;
+ }
+ *ecode = 0x3005;
+ sprintf(estrbuf, "ICMP ERROR \"%s\"", icmp_err_str);
+ *errstr = estrbuf;
+ return -105;
+ } else if (rc == -40) {
+ *ecode = 0x3014;
+ sprintf(estrbuf,
+ "TFTP error occurred after %d bad packets received",
+ tftperr->bad_tftp_packets);
+ *errstr = estrbuf;
+ return -113;
+ } else if (rc == -41) {
+ *ecode = 0x3015;
+ sprintf(estrbuf,
+ "TFTP error occurred after missing %d responses",
+ tftperr->no_packets);
+ *errstr = estrbuf;
+ return -114;
+ } else if (rc == -42) {
+ *ecode = 0x3016;
+ sprintf(estrbuf,
+ "TFTP error missing block %d, expected block was %d",
+ tftperr->blocks_missed, tftperr->blocks_received);
+ *errstr = estrbuf;
+ return -115;
+ }
+
+ return rc;
+}
diff --git a/roms/SLOF/lib/libnet/tftp.h b/roms/SLOF/lib/libnet/tftp.h
new file mode 100644
index 000000000..d2c091947
--- /dev/null
+++ b/roms/SLOF/lib/libnet/tftp.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+
+#ifndef _TFTP_H_
+#define _TFTP_H_
+
+#include <stdint.h>
+#include "ipv6.h"
+
+struct tftphdr {
+ int16_t th_opcode;
+ uint16_t th_data;
+};
+
+struct filename_ip {
+ uint32_t own_ip;
+ ip6_addr_t own_ip6;
+ uint32_t server_ip;
+ ip6_addr_t server_ip6;
+ ip6_addr_t dns_ip6;
+ char filename[256];
+ char *pl_cfgfile; /* For PXELINUX DHCPv4 option 209. Must be free()ed */
+ char *pl_prefix; /* For PXELINUX DHCPv4 option 210. Must be free()ed */
+ int fd;
+ int ip_version;
+};
+typedef struct filename_ip filename_ip_t;
+
+typedef struct {
+ uint32_t bad_tftp_packets;
+ uint32_t no_packets;
+ uint32_t blocks_missed;
+ uint32_t blocks_received;
+} tftp_err_t;
+
+int tftp(filename_ip_t *fnip, unsigned char *buf, int len,
+ unsigned int retries, tftp_err_t *err);
+int32_t handle_tftp(int fd, uint8_t *, int32_t);
+void handle_tftp_dun(uint8_t err_code);
+int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, int len);
+int tftp_get_error_info(filename_ip_t *fnip, tftp_err_t *tftperr, int rc,
+ const char **errstr, int *ecode);
+
+#endif
diff --git a/roms/SLOF/lib/libnet/time.h b/roms/SLOF/lib/libnet/time.h
new file mode 100644
index 000000000..fcd5cd5ae
--- /dev/null
+++ b/roms/SLOF/lib/libnet/time.h
@@ -0,0 +1,6 @@
+
+extern void set_timer(int);
+extern int get_timer(void);
+extern int get_sec_ticks(void);
+
+#define TICKS_SEC get_sec_ticks()
diff --git a/roms/SLOF/lib/libnet/udp.c b/roms/SLOF/lib/libnet/udp.c
new file mode 100644
index 000000000..d6982eac1
--- /dev/null
+++ b/roms/SLOF/lib/libnet/udp.c
@@ -0,0 +1,115 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/************************ DEFINITIONS & DECLARATIONS *********************/
+
+#include <udp.h>
+#include <sys/socket.h>
+#include <dhcp.h>
+#include <dhcpv6.h>
+#include <dns.h>
+#include <tftp.h>
+
+
+/****************************** IMPLEMENTATION ***************************/
+
+
+/**
+ * NET: Handles UDP-packets according to Receive-handle diagram.
+ *
+ * @param udp_packet UDP-packet to be handled
+ * @param packetsize Length of the packet
+ * @return ZERO - packet handled successfully;
+ * NON ZERO - packet was not handled (e.g. bad format)
+ * @see receive_ether
+ * @see udphdr
+ */
+int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize)
+{
+ struct udphdr * udph = (struct udphdr *) udp_packet;
+
+ if (packetsize < sizeof(struct udphdr))
+ return -1; // packet is too small
+
+ switch (htons(udph -> uh_dport)) {
+ case UDPPORT_BOOTPC:
+ if (udph -> uh_sport == htons(UDPPORT_BOOTPS))
+ return handle_dhcp(fd, udp_packet + sizeof(struct udphdr),
+ packetsize - sizeof(struct udphdr));
+ else
+ return -1;
+ case UDPPORT_DNSC:
+ if (udph -> uh_sport == htons(UDPPORT_DNSS))
+ return handle_dns(udp_packet + sizeof(struct udphdr),
+ packetsize - sizeof(struct udphdr));
+ else
+ return -1;
+ case UDPPORT_DHCPV6C:
+ return handle_dhcpv6(udp_packet+sizeof(struct udphdr),
+ packetsize - sizeof(struct udphdr));
+ case UDPPORT_TFTPC:
+ return handle_tftp(fd, udp_packet, packetsize);
+ default:
+ return -1;
+ }
+}
+
+/**
+ * NET: This function handles situation when "Destination unreachable"
+ * ICMP-error occurs during sending UDP-packet.
+ *
+ * @param err_code Error Code (e.g. "Host unreachable")
+ * @param packet original UDP-packet
+ * @param packetsize length of the packet
+ * @see handle_icmp
+ */
+void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code)
+{
+ struct udphdr * udph = (struct udphdr *) udp_packet;
+
+ if (packetsize < sizeof(struct udphdr))
+ return; // packet is too small
+
+ switch (htons(udph -> uh_sport)) {
+ case UDPPORT_TFTPC:
+ handle_tftp_dun(err_code);
+ break;
+ }
+}
+
+/**
+ * NET: Creates UDP-packet. Places UDP-header in a packet and fills it
+ * with corresponding information.
+ * <p>
+ * Use this function with similar functions for other network layers
+ * (fill_ethhdr, fill_iphdr, fill_dnshdr, fill_btphdr).
+ *
+ * @param packet Points to the place where UDP-header must be placed.
+ * @param packetsize Size of the packet in bytes incl. this hdr and data.
+ * @param src_port UDP source port
+ * @param dest_port UDP destination port
+ * @see udphdr
+ * @see fill_ethhdr
+ * @see fill_iphdr
+ * @see fill_dnshdr
+ * @see fill_btphdr
+ */
+void fill_udphdr(uint8_t * packet, uint16_t packetsize,
+ uint16_t src_port, uint16_t dest_port)
+{
+ struct udphdr * udph = (struct udphdr *) packet;
+
+ udph -> uh_sport = htons(src_port);
+ udph -> uh_dport = htons(dest_port);
+ udph -> uh_ulen = htons(packetsize);
+ udph -> uh_sum = htons(0);
+}
diff --git a/roms/SLOF/lib/libnet/udp.h b/roms/SLOF/lib/libnet/udp.h
new file mode 100644
index 000000000..e716e04d7
--- /dev/null
+++ b/roms/SLOF/lib/libnet/udp.h
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _UDP_H
+#define _UDP_H
+
+#include <stdint.h>
+
+#define IPTYPE_UDP 17
+
+#define UDPPORT_BOOTPS 67 /**< UDP port of BootP/DHCP-server */
+#define UDPPORT_BOOTPC 68 /**< UDP port of BootP/DHCP-client */
+#define UDPPORT_DNSS 53 /**< UDP port of DNS-server */
+#define UDPPORT_DNSC 32769 /**< UDP port of DNS-client */
+#define UDPPORT_TFTPC 2001 /**< UDP port of TFTP-client */
+#define UDPPORT_DHCPV6C 546 /**< UDP port of DHCPv6-client */
+
+/** \struct udphdr
+ * A header for UDP-packets.
+ * For more information see RFC 768.
+ */
+struct udphdr {
+ uint16_t uh_sport; /**< Source port */
+ uint16_t uh_dport; /**< Destinantion port */
+ uint16_t uh_ulen; /**< Length in octets, incl. this header and data */
+ uint16_t uh_sum; /**< Checksum */
+};
+typedef struct udphdr udp_hdr_t;
+
+typedef int32_t *(*handle_upper_udp_t)(uint8_t *, int32_t);
+typedef void *(*handle_upper_udp_dun_t)(uint8_t);
+
+/* Handles UDP-packets that are detected by any network layer. */
+extern int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize);
+
+/* Handles UDP related ICMP-Dest.Unreachable packets that are detected by
+ * the network layers. */
+extern void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code);
+
+/* fills udp header */
+extern void fill_udphdr(uint8_t *packet, uint16_t packetsize,
+ uint16_t src_port, uint16_t dest_port);
+
+#endif
diff --git a/roms/SLOF/lib/libnvram/Makefile b/roms/SLOF/lib/libnvram/Makefile
new file mode 100644
index 000000000..d4e9a617e
--- /dev/null
+++ b/roms/SLOF/lib/libnvram/Makefile
@@ -0,0 +1,53 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+SRCS = nvram.c envvar.c
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) $(FLAG) \
+ -I$(INCLBRDDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I. -I../../include
+LDFLAGS = -nostdlib
+
+TARGET = ../libnvram.a
+
+all: $(TARGET)
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep
+
+
+# Include dependency file if available:
+-include Makefile.dep
+
diff --git a/roms/SLOF/lib/libnvram/envvar.c b/roms/SLOF/lib/libnvram/envvar.c
new file mode 100644
index 000000000..d413e9750
--- /dev/null
+++ b/roms/SLOF/lib/libnvram/envvar.c
@@ -0,0 +1,242 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdint.h>
+#include "../libc/include/stdio.h"
+#include "../libc/include/string.h"
+#include "../libc/include/stdlib.h"
+#include "nvram.h"
+
+/* returns the offset of the first byte after the searched envvar */
+static int get_past_env_pos(partition_t part, char *envvar, int evlen)
+{
+ int offset, len;
+ static char temp[256];
+ uint8_t data;
+
+ offset=part.addr;
+
+ memset(temp, 0, 256);
+
+ do {
+ len=0;
+ while((data=nvram_read_byte(offset++)) && len < 256) {
+ temp[len++]=data;
+ }
+ if (!strncmp(envvar, temp, evlen)) {
+ return offset;
+ }
+ } while (len);
+
+ return -1;
+}
+
+/**
+ * @param partition name of the envvar partition
+ * @param envvar name of the environment variable
+ * @param evlen string length of the envvar parameter
+ * @return pointer to temporary string containing the value of envvar
+ */
+char *nvram_get_env(partition_t part, char *envvar, unsigned evlen)
+{
+ static char temp[256+1];
+ int len, offset;
+ uint8_t data;
+
+ DEBUG("nvram_get_env %p... ", envvar);
+ if(!part.addr) {
+ /* ERROR: No environment variable partition */
+ DEBUG("invalid partition.\n");
+ return NULL;
+ }
+
+ offset=part.addr;
+
+ do {
+ len=0;
+ while((data=nvram_read_byte(offset++)) && len < 256) {
+ temp[len++]=data;
+ }
+ temp[len]=0;
+
+ if (!strncmp(envvar, temp, evlen)) {
+ int pos=0;
+ while (temp[pos]!='=' && pos < len) pos++;
+ // DEBUG("value='%s'\n", temp+pos+1);
+ return temp+pos+1;
+ }
+ } while (len);
+
+ DEBUG("not found\n");
+ return NULL;
+}
+
+static int find_last_envvar(partition_t part)
+{
+ uint8_t last, current;
+ int offset;
+
+ offset=part.addr;
+
+ last=nvram_read_byte(part.addr);
+
+ for (offset=part.addr; offset<(int)(part.addr+part.len); offset++) {
+ current=nvram_read_byte(offset);
+ if(!last && !current)
+ return offset;
+
+ last=current;
+ }
+
+ return -1;
+}
+
+int nvram_add_env(partition_t part, char *envvar, unsigned evlen, char *value, unsigned vallen)
+{
+ unsigned i, freespace, last, len, offset;
+
+ /* Find offset where we can write */
+ last = find_last_envvar(part);
+
+ /* How much space do we have left? */
+ freespace = part.addr+part.len-last;
+
+ /* how long is the entry we want to write? */
+ len = evlen + vallen + 2;
+
+ if(freespace<len) {
+ // TODO try to increase partition size
+ return -1;
+ }
+
+ offset=last;
+
+ for (i = 0; i < evlen; i++)
+ nvram_write_byte(offset++, envvar[i]);
+
+ nvram_write_byte(offset++, '=');
+
+ for (i = 0; i < vallen; i++)
+ nvram_write_byte(offset++, value[i]);
+
+ return 0;
+}
+
+int nvram_del_env(partition_t part, char *envvar, unsigned evlen)
+{
+ int last, current, pos, i;
+ char *buffer;
+
+ if(!part.addr)
+ return -1;
+
+ last=find_last_envvar(part);
+ current = pos = get_past_env_pos(part, envvar, evlen);
+
+ // TODO is this really required?
+ /* go back to non-0 value */
+ current--;
+
+ while (nvram_read_byte(current))
+ current--;
+
+ // TODO is this required?
+ current++;
+
+ buffer=get_nvram_buffer(last-pos);
+
+ for (i=0; i<last-pos; i++)
+ buffer[i]=nvram_read_byte(i+pos);
+
+ for (i=0; i<last-pos; i++)
+ nvram_write_byte(i+current, buffer[i]);
+
+ free_nvram_buffer(buffer);
+
+ erase_nvram(last, current+last-pos);
+
+ return 0;
+}
+
+int nvram_set_env(partition_t part, char *envvar, unsigned evlen, char *value, unsigned vallen)
+{
+ char *oldvalue, *buffer;
+ unsigned last, current, buffersize, i;
+
+ DEBUG("nvram_set_env %lx[%lx]: %p=>%p\n", part.addr, part.len, envvar, value);
+
+ if(!part.addr)
+ return -1;
+
+ /* Check whether the environment variable exists already */
+ oldvalue = nvram_get_env(part, envvar, evlen);
+
+ if (oldvalue == NULL)
+ return nvram_add_env(part, envvar, evlen, value, vallen);
+
+
+ /* The value did not change. So we succeeded! */
+ if (strlen(oldvalue) == vallen && !strncmp(oldvalue, value, vallen))
+ return 0;
+
+ /* we need to overwrite environment variables, back them up first */
+
+ // DEBUG("overwriting existing environment variable\n");
+
+ /* allocate a buffer */
+ last=find_last_envvar(part);
+ current = get_past_env_pos(part, envvar, evlen);
+ buffersize = last - current;
+ buffer=get_nvram_buffer(buffersize);
+ if(!buffer)
+ return -1;
+
+ for (i=0; i<buffersize; i++) {
+ buffer[i] = nvram_read_byte(current+i);
+ }
+
+ /* walk back until the = */
+ while (nvram_read_byte(current)!='=') {
+ current--;
+ }
+
+ /* Start at envvar= */
+ current++;
+
+ /* Write the new value */
+ for(i = 0; i < vallen; i++) {
+ nvram_write_byte(current++, value[i]);
+ }
+
+ /* Write end of string marker */
+ nvram_write_byte(current++, 0);
+
+ /* Copy back the buffer */
+ for (i=0; i<buffersize; i++) {
+ nvram_write_byte(current++, buffer[i]);
+ }
+
+ free_nvram_buffer(buffer);
+
+ /* If the new environment variable content is shorter than the old one,
+ * we need to erase the rest of the bytes
+ */
+
+ if (current<last) {
+ for(i=current; i<last; i++) {
+ nvram_write_byte(i, 0);
+ }
+ }
+
+ return 0; /* success */
+}
+
diff --git a/roms/SLOF/lib/libnvram/libnvram.code b/roms/SLOF/lib/libnvram/libnvram.code
new file mode 100644
index 000000000..8481f57f5
--- /dev/null
+++ b/roms/SLOF/lib/libnvram/libnvram.code
@@ -0,0 +1,274 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+#include <nvram.h>
+
+PRIM(nvram_X2d_c_X40)
+ unsigned int offset = TOS.u;
+ TOS.u=nvram_read_byte(offset);
+MIRP
+
+PRIM(nvram_X2d_w_X40)
+ unsigned int offset = TOS.u;
+ TOS.u=nvram_read_word(offset);
+MIRP
+
+PRIM(nvram_X2d_l_X40)
+ unsigned int offset = TOS.u;
+ TOS.u=nvram_read_dword(offset);
+MIRP
+
+PRIM(nvram_X2d_x_X40)
+ unsigned int offset = TOS.u;
+ TOS.u=nvram_read_qword(offset);
+MIRP
+
+PRIM(nvram_X2d_c_X21)
+ nvram_write_byte(TOS.u, NOS.u);
+ POP; POP;
+MIRP
+
+PRIM(nvram_X2d_w_X21)
+ nvram_write_word(TOS.u, NOS.u);
+ POP; POP;
+MIRP
+
+PRIM(nvram_X2d_l_X21)
+ nvram_write_dword(TOS.u, NOS.u);
+ POP; POP;
+MIRP
+
+PRIM(nvram_X2d_x_X21)
+ nvram_write_qword(TOS.u, NOS.u);
+ POP; POP;
+MIRP
+
+/* get-nvram-partition ( type -- addr len FAILED? ) */
+PRIM(get_X2d_nvram_X2d_partition)
+ partition_t partition;
+ unsigned int ptype = TOS.u;
+ partition = get_partition(ptype, NULL);
+ if(partition.len && partition.len != -1) {
+ TOS.u = partition.addr;
+ PUSH;
+ TOS.u = partition.len;
+ PUSH;
+ TOS.u = 0; // FALSE
+ } else {
+ TOS.u = -1; // TRUE
+ }
+MIRP
+
+/* get-named-nvram-partition ( name.addr name.len -- addr len FAILED? ) */
+PRIM(get_X2d_named_X2d_nvram_X2d_partition)
+ partition_t partition;
+ int namelen = TOS.n; POP;
+
+ partition = get_partition_fs(TOS.a, namelen);
+
+ if(partition.len && partition.len != -1) {
+ TOS.u = partition.addr;
+ PUSH;
+ TOS.u = partition.len;
+ PUSH;
+ TOS.u = 0; // FALSE
+ } else {
+ TOS.u = -1; // TRUE
+ }
+MIRP
+
+
+
+/* new-nvram-partition ( type name.addr name.len len -- part.offs part.len FALSE | TRUE) */
+PRIM(new_X2d_nvram_X2d_partition)
+ int type, len, namelen;
+ partition_t partition;
+ char *name;
+
+ len = TOS.u; POP;
+ namelen = TOS.u; POP;
+ name = (char *)TOS.u; POP;
+ type = TOS.u; POP;
+
+ partition = new_nvram_partition_fs(type, name, namelen, len);
+
+ if(!partition.len) {
+ PUSH; TOS.u = -1; // TRUE
+ } else {
+ PUSH; TOS.u = partition.addr;
+ PUSH; TOS.u = partition.len;
+ PUSH; TOS.u = 0; // FALSE
+ }
+MIRP
+
+/* inrease-nvram-partition ( part.offs part.len new-len -- FALSE | TRUE ) */
+PRIM(increase_X2d_nvram_X2d_partition)
+ int len, ret;
+ partition_t partition;
+
+ // FIXME
+ partition.addr = TOS.u; POP;
+ partition.len = TOS.u; POP;
+ len = TOS.u; POP;
+
+ ret=increase_nvram_partition_size(partition, len);
+
+ PUSH;
+
+ if(!ret)
+ TOS.u=-1; // TRUE
+ else
+ TOS.u=0; // FALSE
+
+MIRP
+
+PRIM(internal_X2d_reset_X2d_nvram)
+ reset_nvram();
+MIRP
+
+PRIM(wipe_X2d_nvram)
+ wipe_nvram();
+MIRP
+
+PRIM(nvram_X2d_debug)
+ nvram_debug();
+MIRP
+
+// ( part.start part.len name.addr name.len -- var.addr var.len TRUE | false )
+PRIM(internal_X2d_get_X2d_env)
+ char *name;
+ int namelen;
+ partition_t part;
+ char *val;
+
+ namelen = TOS.u; POP;
+ name = TOS.a; POP;
+ part.len = TOS.u; POP;
+ part.addr = TOS.u; POP;
+
+ val = nvram_get_env(part, name, namelen);
+ if(val) {
+ PUSH; TOS.a = val;
+ PUSH; TOS.u = strlen(val);
+ PUSH; TOS.u = -1; // TRUE
+ } else {
+ PUSH; TOS.u = 0; // FALSE
+ }
+MIRP
+
+// ( part.start part.len name.addr name.len val.addr val.len -- FALSE|TRUE)
+PRIM(internal_X2d_add_X2d_env)
+ char *name, *val;
+ int namelen, vallen;
+ partition_t part;
+ int ret;
+
+ vallen = TOS.u; POP;
+ val = TOS.a; POP;
+ namelen = TOS.u; POP;
+ name = TOS.a; POP;
+ part.len = TOS.u; POP;
+ part.addr = TOS.u; POP;
+
+ ret = nvram_add_env(part, name, namelen, val, vallen);
+ if(ret) {
+ PUSH; TOS.u = -1; // TRUE
+ } else {
+ PUSH; TOS.u = 0; // FALSE
+ }
+MIRP
+
+// ( part.addr part.len name.addr name.len -- FALSE|TRUE)
+PRIM(internal_X2d_del_X2d_env)
+ char *name;
+ int namelen;
+ partition_t part;
+ int ret;
+
+ namelen = TOS.u; POP;
+ name = TOS.a; POP;
+ part.len = TOS.u; POP;
+ part.addr = TOS.u; POP;
+
+ ret = nvram_del_env(part, name, namelen);
+ if(ret) {
+ PUSH; TOS.u = -1; // TRUE
+ } else {
+ PUSH; TOS.u = 0; // FALSE
+ }
+
+MIRP
+
+// internal-set-env ( part.addr part.len name.addr name.len val.addr val.len -- FALSE|TRUE)
+PRIM(internal_X2d_set_X2d_env)
+ char *name, *value;
+ int namelen, valuelen;
+ partition_t part;
+ int ret;
+
+ valuelen = TOS.u; POP;
+ value = TOS.a; POP;
+ namelen = TOS.u; POP;
+ name = TOS.a; POP;
+ part.len = TOS.u; POP;
+ part.addr = TOS.u; POP;
+
+ ret = nvram_set_env(part, name, namelen, value, valuelen);
+ if(ret) {
+ PUSH; TOS.u = -1; // TRUE
+ } else {
+ PUSH; TOS.u = 0; // FALSE
+ }
+MIRP
+
+// ( part.addr part.len -- FALSE|TRUE)
+PRIM(erase_X2d_nvram_X2d_partition)
+ partition_t part;
+ int ret;
+
+ part.len = TOS.u; POP;
+ part.addr = TOS.u; POP;
+
+ ret=clear_nvram_partition(part);
+ if(ret) {
+ PUSH; TOS.u = -1; // TRUE
+ } else {
+ PUSH; TOS.u = 0; // FALSE
+ }
+
+MIRP
+
+// ( part.addr part.len -- FALSE|TRUE)
+PRIM(delete_X2d_nvram_X2d_partition)
+ partition_t part;
+ int ret;
+
+ part.len = TOS.u; POP;
+ part.addr = TOS.u; POP;
+
+ ret=delete_nvram_partition(part);
+ if(ret) {
+ PUSH; TOS.u = -1; // TRUE
+ } else {
+ PUSH; TOS.u = 0; // FALSE
+ }
+
+MIRP
+
+// ( fetch_token store_token size nvram-addr -- )
+PRIM(internal_X2d_nvram_X2d_init)
+ void *nvram_addr = TOS.a; POP;
+ uint32_t nvram_size = TOS.u; POP;
+ uint32_t store_token = TOS.u; POP;
+ long fetch_token = TOS.u; POP;
+
+ nvram_init(fetch_token, store_token, nvram_size, nvram_addr);
+MIRP
diff --git a/roms/SLOF/lib/libnvram/libnvram.in b/roms/SLOF/lib/libnvram/libnvram.in
new file mode 100644
index 000000000..bbb20a889
--- /dev/null
+++ b/roms/SLOF/lib/libnvram/libnvram.in
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/* NVRAM access primitives */
+cod(nvram-c@)
+cod(nvram-c!)
+cod(nvram-w@)
+cod(nvram-w!)
+cod(nvram-l@)
+cod(nvram-l!)
+cod(nvram-x@)
+cod(nvram-x!)
+
+/* Generic NVRAM helpers */
+cod(internal-reset-nvram)
+cod(nvram-debug)
+cod(wipe-nvram)
+
+/* NVRAM Partition Handling */
+cod(get-nvram-partition)
+cod(get-named-nvram-partition)
+cod(new-nvram-partition)
+cod(increase-nvram-partition)
+cod(erase-nvram-partition)
+cod(delete-nvram-partition)
+
+/* NVRAM environment access words */
+cod(internal-get-env)
+cod(internal-add-env)
+cod(internal-del-env)
+cod(internal-set-env)
+
+cod(internal-nvram-init)
diff --git a/roms/SLOF/lib/libnvram/nvram.c b/roms/SLOF/lib/libnvram/nvram.c
new file mode 100644
index 000000000..6d145d79e
--- /dev/null
+++ b/roms/SLOF/lib/libnvram/nvram.c
@@ -0,0 +1,660 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include "cache.h"
+#include "nvram.h"
+#include "../libhvcall/libhvcall.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <southbridge.h>
+#include <nvramlog.h>
+#include <byteorder.h>
+
+#ifdef RTAS_NVRAM
+static uint32_t fetch_token;
+static uint32_t store_token;
+static uint32_t NVRAM_LENGTH;
+static char *nvram_buffer; /* use buffer allocated by SLOF code */
+#else
+#ifndef NVRAM_LENGTH
+#define NVRAM_LENGTH 0x10000
+#endif
+/*
+ * This is extremely ugly, but still better than implementing
+ * another sbrk() around it.
+ */
+static char nvram_buffer[NVRAM_LENGTH];
+#endif
+
+static uint8_t nvram_buffer_locked=0x00;
+
+void nvram_init(uint32_t _fetch_token, uint32_t _store_token,
+ long _nvram_length, void* nvram_addr)
+{
+#ifdef RTAS_NVRAM
+ fetch_token = _fetch_token;
+ store_token = _store_token;
+ NVRAM_LENGTH = _nvram_length;
+ nvram_buffer = nvram_addr;
+
+ DEBUG("\nNVRAM: size=%d, fetch=%x, store=%x\n",
+ NVRAM_LENGTH, fetch_token, store_token);
+#endif
+}
+
+
+void asm_cout(long Character,long UART,long NVRAM);
+
+#if defined(DISABLE_NVRAM)
+
+static volatile uint8_t nvram[NVRAM_LENGTH]; /* FAKE */
+
+#define nvram_access(type,size,name) \
+ type nvram_read_##name(unsigned int offset) \
+ { \
+ type *pos; \
+ if (offset > (NVRAM_LENGTH - sizeof(type))) \
+ return 0; \
+ pos = (type *)(nvram+offset); \
+ return *pos; \
+ } \
+ void nvram_write_##name(unsigned int offset, type data) \
+ { \
+ type *pos; \
+ if (offset > (NVRAM_LENGTH - sizeof(type))) \
+ return; \
+ pos = (type *)(nvram+offset); \
+ *pos = data; \
+ }
+
+#elif defined(RTAS_NVRAM)
+
+static inline void nvram_fetch(unsigned int offset, void *buf, unsigned int len)
+{
+ struct hv_rtas_call rtas = {
+ .token = fetch_token,
+ .nargs = 3,
+ .nrets = 2,
+ .argret = { offset, (uint32_t)(unsigned long)buf, len },
+ };
+ h_rtas(&rtas);
+}
+
+static inline void nvram_store(unsigned int offset, void *buf, unsigned int len)
+{
+ struct hv_rtas_call rtas = {
+ .token = store_token,
+ .nargs = 3,
+ .nrets = 2,
+ .argret = { offset, (uint32_t)(unsigned long)buf, len },
+ };
+ h_rtas(&rtas);
+}
+
+#define nvram_access(type,size,name) \
+ type nvram_read_##name(unsigned int offset) \
+ { \
+ type val; \
+ if (offset > (NVRAM_LENGTH - sizeof(type))) \
+ return 0; \
+ nvram_fetch(offset, &val, size / 8); \
+ return val; \
+ } \
+ void nvram_write_##name(unsigned int offset, type data) \
+ { \
+ if (offset > (NVRAM_LENGTH - sizeof(type))) \
+ return; \
+ nvram_store(offset, &data, size / 8); \
+ }
+
+#else /* DISABLE_NVRAM */
+
+static volatile uint8_t *nvram = (volatile uint8_t *)SB_NVRAM_adr;
+
+#define nvram_access(type,size,name) \
+ type nvram_read_##name(unsigned int offset) \
+ { \
+ type *pos; \
+ if (offset > (NVRAM_LENGTH - sizeof(type))) \
+ return 0; \
+ pos = (type *)(nvram+offset); \
+ return ci_read_##size(pos); \
+ } \
+ void nvram_write_##name(unsigned int offset, type data) \
+ { \
+ type *pos; \
+ if (offset > (NVRAM_LENGTH - sizeof(type))) \
+ return; \
+ pos = (type *)(nvram+offset); \
+ ci_write_##size(pos, data); \
+ }
+
+#endif
+
+/*
+ * producer for nvram access functions. Since these functions are
+ * basically all the same except for the used data types, produce
+ * them via the nvram_access macro to keep the code from bloating.
+ */
+
+nvram_access(uint8_t, 8, byte)
+nvram_access(uint16_t, 16, word)
+nvram_access(uint32_t, 32, dword)
+nvram_access(uint64_t, 64, qword)
+
+
+
+/**
+ * This function is a minimal abstraction for our temporary
+ * buffer. It should have been malloced, but since there is no
+ * usable malloc, we go this route.
+ *
+ * @return pointer to temporary buffer
+ */
+
+char *get_nvram_buffer(unsigned len)
+{
+ if(len>NVRAM_LENGTH)
+ return NULL;
+
+ if(nvram_buffer_locked)
+ return NULL;
+
+ nvram_buffer_locked = 0xff;
+
+ return nvram_buffer;
+}
+
+/**
+ * @param buffer pointer to the allocated buffer. This
+ * is unused, but nice in case we ever get a real malloc
+ */
+
+void free_nvram_buffer(char *buffer __attribute__((unused)))
+{
+ nvram_buffer_locked = 0x00;
+}
+
+/**
+ * @param fmt format string, like in printf
+ * @param ... variable number of arguments
+ */
+
+int nvramlog_printf(const char* fmt, ...)
+{
+ char buff[256];
+ int count, i;
+ va_list ap;
+
+ va_start(ap, fmt);
+ count = vsprintf(buff, fmt, ap);
+ va_end(ap);
+
+ for (i=0; i<count; i++)
+ asm_cout(buff[i], 0, 1);
+
+ return count;
+}
+
+/**
+ * @param offset start offset of the partition header
+ */
+
+static uint8_t get_partition_type(int offset)
+{
+ return nvram_read_byte(offset);
+}
+
+/**
+ * @param offset start offset of the partition header
+ */
+
+static uint8_t get_partition_header_checksum(int offset)
+{
+ return nvram_read_byte(offset+1);
+}
+
+/**
+ * @param offset start offset of the partition header
+ */
+
+static uint16_t get_partition_len(int offset)
+{
+ return nvram_read_word(offset+2);
+}
+
+/**
+ * @param offset start offset of the partition header
+ * @return static char array containing the partition name
+ *
+ * NOTE: If the partition name needs to be non-temporary, strdup
+ * and use the copy instead.
+ */
+
+static char * get_partition_name(int offset)
+{
+ static char name[12];
+ int i;
+ for (i=0; i<12; i++)
+ name[i]=nvram_read_byte(offset+4+i);
+
+ DEBUG("name: \"%s\"\n", name);
+ return name;
+}
+
+static uint8_t calc_partition_header_checksum(int offset)
+{
+ uint16_t plainsum;
+ uint8_t checksum;
+ int i;
+
+ plainsum = nvram_read_byte(offset);
+
+ for (i=2; i<PARTITION_HEADER_SIZE; i++)
+ plainsum+=nvram_read_byte(offset+i);
+
+ checksum=(plainsum>>8)+(plainsum&0xff);
+
+ return checksum;
+}
+
+static int calc_used_nvram_space(void)
+{
+ unsigned walk, len;
+
+ for (walk=0; walk<NVRAM_LENGTH;) {
+ if(nvram_read_byte(walk) == 0
+ || get_partition_header_checksum(walk) !=
+ calc_partition_header_checksum(walk)) {
+ /* If there's no valid entry, bail out */
+ break;
+ }
+
+ len=get_partition_len(walk);
+ DEBUG("... part len=%x, %x\n", len, len*16);
+
+ if(!len) {
+ /* If there's a partition type but no len, bail out.
+ * Don't bail out if type is 0. This can be used to
+ * find the offset of the first free byte.
+ */
+ break;
+ }
+
+ walk += len * 16;
+ }
+ DEBUG("used nvram space: %d\n", walk);
+
+ return walk;
+}
+
+/**
+ *
+ * @param type partition type. Set this to the partition type you are looking
+ * for. If there are several partitions with the same type, only
+ * the first partition with that type will be found.
+ * Set to -1 to ignore. Set to 0 to find free unpartitioned space.
+ *
+ * @param name partition name. Set this to the name of the partition you are
+ * looking for. If there are several partitions with the same name,
+ * only the first partition with that name will be found.
+ * Set to NULL to ignore.
+ *
+ * To disambiguate the partitions you should have a unique name if you plan to
+ * have several partitions of the same type.
+ *
+ */
+
+partition_t get_partition(unsigned int type, char *name)
+{
+ partition_t ret={0,-1};
+ unsigned walk, len;
+
+ DEBUG("get_partition(%i, '%s')\n", type, name);
+
+ for (walk=0; walk<NVRAM_LENGTH;) {
+ // DEBUG("get_partition: walk=%x\n", walk);
+ if(get_partition_header_checksum(walk) !=
+ calc_partition_header_checksum(walk)) {
+ /* If there's no valid entry, bail out */
+ break;
+ }
+
+ len=get_partition_len(walk);
+ if(type && !len) {
+ /* If there's a partition type but no len, bail out.
+ * Don't bail out if type is 0. This can be used to
+ * find the offset of the first free byte.
+ */
+ break;
+ }
+
+ /* Check if either type or name or both do not match. */
+ if ( (type!=(unsigned int)-1 && type != get_partition_type(walk)) ||
+ (name && strncmp(get_partition_name(walk), name, 12)) ) {
+ /* We hit another partition. Continue
+ * at the end of this partition
+ */
+ walk += len*16;
+ continue;
+ }
+
+ ret.addr=walk+PARTITION_HEADER_SIZE;
+ ret.len=(len*16)-PARTITION_HEADER_SIZE;
+ break;
+ }
+
+ return ret;
+}
+
+/* Get partition specified by a Forth string */
+partition_t get_partition_fs(char *name, int namelen)
+{
+ char buf[namelen + 1];
+
+ memcpy(buf, name, namelen);
+ buf[namelen] = 0;
+
+ return get_partition(-1, buf);
+}
+
+void erase_nvram(int offset, int len)
+{
+ int i;
+
+#ifdef RTAS_NVRAM
+ char *erase_buf = get_nvram_buffer(len);
+ if (erase_buf) {
+ /* Speed up by erasing all memory at once */
+ memset(erase_buf, 0, len);
+ nvram_store(offset, erase_buf, len);
+ free_nvram_buffer(erase_buf);
+ return;
+ }
+ /* If get_nvram_buffer failed, fall through to default code */
+#endif
+ for (i=offset; i<offset+len; i++)
+ nvram_write_byte(i, 0);
+}
+
+void wipe_nvram(void)
+{
+ erase_nvram(0, NVRAM_LENGTH);
+}
+
+/**
+ * @param partition partition structure pointing to the partition to wipe.
+ * @param header_only if header_only is != 0 only the partition header is
+ * nulled out, not the whole partition.
+ */
+
+int wipe_partition(partition_t partition, int header_only)
+{
+ int pstart, len;
+
+ pstart=partition.addr-PARTITION_HEADER_SIZE;
+
+ len=PARTITION_HEADER_SIZE;
+
+ if(!header_only)
+ len += partition.len;
+
+ erase_nvram(pstart, len);
+
+ return 0;
+}
+
+
+static partition_t create_nvram_partition(int type, const char *name, unsigned len)
+{
+ partition_t ret = { 0, 0 };
+ unsigned i, offset, plen;
+
+ plen = ALIGN(len+PARTITION_HEADER_SIZE, 16);
+
+ DEBUG("Creating partition type=%x, name=%s, len=%d plen=%d\n",
+ type, name, len, plen);
+
+ offset = calc_used_nvram_space();
+
+ if (NVRAM_LENGTH-(calc_used_nvram_space())<plen) {
+ DEBUG("Not enough free space.\n");
+ return ret;
+ }
+
+ DEBUG("Writing header.");
+
+ nvram_write_byte(offset, type);
+ nvram_write_word(offset+2, plen/16);
+
+ for (i=0; i<strlen(name); i++)
+ nvram_write_byte(offset+4+i, name[i]);
+
+ nvram_write_byte(offset+1, calc_partition_header_checksum(offset));
+
+ ret.addr = offset+PARTITION_HEADER_SIZE;
+ ret.len = len;
+
+ DEBUG("partition created: addr=%lx len=%lx\n", ret.addr, ret.len);
+
+ return ret;
+}
+
+static int create_free_partition(void)
+{
+ int free_space;
+ partition_t free_part;
+
+ free_space = NVRAM_LENGTH - calc_used_nvram_space() - PARTITION_HEADER_SIZE;
+ free_part = create_nvram_partition(0x7f, "free space", free_space);
+
+ return (free_part.addr != 0);
+}
+
+partition_t new_nvram_partition(int type, char *name, int len)
+{
+ partition_t free_part, new_part = { 0, 0 };
+
+ /* NOTE: Assume all free space is consumed by the "free space"
+ * partition. This means a partition can not be increased in the middle
+ * of reset_nvram, which is obviously not a big loss.
+ */
+
+ free_part=get_partition(0x7f, NULL);
+ if( free_part.len && free_part.len != -1)
+ wipe_partition(free_part, 1);
+
+ new_part = create_nvram_partition(type, name, len);
+
+ if(new_part.len != len) {
+ new_part.len = 0;
+ new_part.addr = 0;
+ }
+
+ create_free_partition();
+
+ return new_part;
+}
+
+partition_t new_nvram_partition_fs(int type, char *name, int namelen, int len)
+{
+ char buf[13];
+ int i;
+
+ for (i = 0; i < 12; i++) {
+ if (i < namelen)
+ buf[i] = name[i];
+ else
+ buf[i] = 0;
+ }
+ buf[12] = 0;
+
+ return new_nvram_partition(type, buf, len);
+}
+
+/**
+ * @param partition partition structure pointing to the partition to wipe.
+ */
+
+int delete_nvram_partition(partition_t partition)
+{
+ unsigned i;
+ partition_t free_part;
+
+ if(!partition.len || partition.len == -1)
+ return 0;
+
+ for (i=partition.addr+partition.len; i< NVRAM_LENGTH; i++)
+ nvram_write_byte(i - partition.len - PARTITION_HEADER_SIZE, nvram_read_byte(i));
+
+ erase_nvram(NVRAM_LENGTH-partition.len-PARTITION_HEADER_SIZE,
+ partition.len-PARTITION_HEADER_SIZE);
+
+ free_part=get_partition(0x7f, NULL);
+ wipe_partition(free_part, 0);
+ create_free_partition();
+
+ return 1;
+}
+
+int clear_nvram_partition(partition_t part)
+{
+ if(!part.addr)
+ return 0;
+
+ erase_nvram(part.addr, part.len);
+
+ return 1;
+}
+
+
+int increase_nvram_partition_size(partition_t partition, int newsize)
+{
+ partition_t free_part;
+ int free_offset, end_offset, i;
+
+ /* We don't support shrinking partitions (yet) */
+ if (newsize < partition.len) {
+ return 0;
+ }
+
+ /* NOTE: Assume all free space is consumed by the "free space"
+ * partition. This means a partition can not be increased in the middle
+ * of reset_nvram, which is obviously not a big loss.
+ */
+
+ free_part=get_partition(0x7f, NULL);
+
+ // FIXME: It could be 16 byte more. Also handle empty "free" partition.
+ if (free_part.len == -1 || free_part.len < newsize - partition.len ) {
+ return 0;
+ }
+
+ free_offset=free_part.addr - PARTITION_HEADER_SIZE; // first unused byte
+ end_offset=partition.addr + partition.len; // last used byte of partition + 1
+
+ if(free_offset > end_offset) {
+ int j, bufferlen;
+ char *overlap_buffer;
+
+ bufferlen=free_offset - end_offset;
+
+ overlap_buffer=get_nvram_buffer(bufferlen);
+ if(!overlap_buffer) {
+ return 0;
+ }
+
+ for (i=end_offset, j=0; i<free_offset; i++, j++)
+ overlap_buffer[j]=nvram_read_byte(i);
+
+ /* Only wipe the header. The free space partition is empty per
+ * definition
+ */
+
+ wipe_partition(free_part, 1);
+
+ for (i=partition.addr+newsize, j=0; i<(int)(partition.addr+newsize+bufferlen); i++, j++)
+ nvram_write_byte(i, overlap_buffer[j]);
+
+ free_nvram_buffer(overlap_buffer);
+ } else {
+ /* Only wipe the header. */
+ wipe_partition(free_part, 1);
+ }
+
+ /* Clear the new partition space */
+ erase_nvram(partition.addr+partition.len, newsize-partition.len);
+
+ nvram_write_word(partition.addr - 16 + 2, newsize);
+
+ create_free_partition();
+
+ return 1;
+}
+
+static void init_cpulog_partition(partition_t cpulog)
+{
+ unsigned int offset=cpulog.addr;
+
+ /* see board-xxx/include/nvramlog.h for information */
+ nvram_write_word(offset+0, 0x40); // offset
+ nvram_write_word(offset+2, 0x00); // flags
+ nvram_write_dword(offset+4, 0x01); // pointer
+
+}
+
+void reset_nvram(void)
+{
+ partition_t cpulog0, cpulog1;
+ struct {
+ uint32_t prefix;
+ uint64_t name;
+ } __attribute__((packed)) header;
+
+ DEBUG("Erasing NVRAM\n");
+ erase_nvram(0, NVRAM_LENGTH);
+
+ DEBUG("Creating CPU log partitions\n");
+ header.prefix = be32_to_cpu(LLFW_LOG_BE0_NAME_PREFIX);
+ header.name = be64_to_cpu(LLFW_LOG_BE0_NAME);
+ cpulog0=create_nvram_partition(LLFW_LOG_BE0_SIGNATURE, (char *)&header,
+ (LLFW_LOG_BE0_LENGTH*16)-PARTITION_HEADER_SIZE);
+
+ header.prefix = be32_to_cpu(LLFW_LOG_BE1_NAME_PREFIX);
+ header.name = be64_to_cpu(LLFW_LOG_BE1_NAME);
+ cpulog1=create_nvram_partition(LLFW_LOG_BE1_SIGNATURE, (char *)&header,
+ (LLFW_LOG_BE1_LENGTH*16)-PARTITION_HEADER_SIZE);
+
+ DEBUG("Initializing CPU log partitions\n");
+ init_cpulog_partition(cpulog0);
+ init_cpulog_partition(cpulog1);
+
+ nvramlog_printf("Creating common NVRAM partition\r\n");
+ create_nvram_partition(0x70, "common", 0x01000-PARTITION_HEADER_SIZE);
+
+ create_free_partition();
+}
+
+void nvram_debug(void)
+{
+#ifndef RTAS_NVRAM
+ printf("\nNVRAM_BASE: %p\n", nvram);
+ printf("NVRAM_LEN: 0x%x\n", NVRAM_LENGTH);
+#endif
+}
+
+unsigned int get_nvram_size(void)
+{
+ return NVRAM_LENGTH;
+}
diff --git a/roms/SLOF/lib/libnvram/nvram.h b/roms/SLOF/lib/libnvram/nvram.h
new file mode 100644
index 000000000..c8aad3151
--- /dev/null
+++ b/roms/SLOF/lib/libnvram/nvram.h
@@ -0,0 +1,75 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef __NVRAM_H
+#define __NVRAM_H 1
+
+/* data structures */
+
+typedef struct {
+ unsigned long addr;
+ long len;
+} partition_t;
+
+/* macros */
+
+#define DEBUG(x...)
+// #define DEBUG(x...) printf(x);
+
+#ifndef ALIGN
+#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
+#endif
+
+#define NULL ((void *)0)
+
+#define PARTITION_HEADER_SIZE 16
+
+
+/* exported functions */
+
+#define nvram_access_proto(type,name) \
+ type nvram_read_##name(unsigned int offset); \
+ void nvram_write_##name(unsigned int offset, type data);
+
+nvram_access_proto(uint8_t, byte)
+nvram_access_proto(uint16_t, word)
+nvram_access_proto(uint32_t, dword)
+nvram_access_proto(uint64_t, qword)
+
+/* nvram.c */
+
+char *get_nvram_buffer(unsigned len);
+void free_nvram_buffer(char *buffer);
+int nvramlog_printf(const char* fmt, ...);
+partition_t get_partition(unsigned int type, char *name);
+partition_t get_partition_fs(char *name, int namelen);
+void erase_nvram(int offset, int len);
+int wipe_partition(partition_t partition, int header_only);
+partition_t new_nvram_partition(int type, char *name, int len);
+partition_t new_nvram_partition_fs(int type, char *name, int namelen, int len);
+int increase_nvram_partition_size(partition_t partition, int newsize);
+int clear_nvram_partition(partition_t part);
+int delete_nvram_partition(partition_t part);
+void reset_nvram(void);
+void wipe_nvram(void);
+void nvram_debug(void);
+void nvram_init(uint32_t store_token, uint32_t fetch_token,
+ long nv_size, void* nvram_addr);
+unsigned int get_nvram_size(void);
+
+/* envvar.c */
+char *nvram_get_env(partition_t part, char *envvar, unsigned evlen);
+int nvram_add_env(partition_t part, char *envvar, unsigned evlen, char *value, unsigned vallen);
+int nvram_del_env(partition_t part, char *envvar, unsigned evlen);
+int nvram_set_env(partition_t part, char *envvar, unsigned evlen, char *val, unsigned vlen);
+
+#endif
diff --git a/roms/SLOF/lib/libtpm/Makefile b/roms/SLOF/lib/libtpm/Makefile
new file mode 100644
index 000000000..895dbfdfb
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/Makefile
@@ -0,0 +1,51 @@
+# *****************************************************************************
+# * Copyright (c) 2015-2020 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I$(SLOFCMNDIR)
+CPPFLAGS += -I../libhvcall
+CPPFLAGS += $(RELEASE)
+
+LDFLAGS = -nostdlib
+
+TARGET = ../libtpm.a
+
+
+all: $(TARGET)
+
+SRCS = tpm_drivers.c sha.c sha256.c sha512.c tcgbios.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libtpm/Readme b/roms/SLOF/lib/libtpm/Readme
new file mode 100644
index 000000000..2c362ac07
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/Readme
@@ -0,0 +1,57 @@
+This directory hosts (v)TPM related code.
+
+Background:
+-----------
+
+A TPM is a crypto chip that is found in many systems. Besides it offering
+a secure key store, among other functionality, it is also used to implement
+'trusted boot'. This is realized by code in the firmware measuring parts of the
+firmware's code and data as well as system data, such as the boot block, and
+logging these measurements and storing (extending) them in the TPM's platform
+configuration register (PCR).
+
+The benefits of having a TPM (or vTPM) in a system are:
+
+- enablement of trusted boot; this allow us to eventually extend the chain of
+ trust from the hypervisor to the guests
+- enablement of attestation so that one can verify what software is running on
+ a machine (OpenPTS, OpenAttestation)
+- provides TPM functionality to VMs, which includes a standardized mechanism
+ to store keys and other blobs (Linux trusted keys, GNU TLS's TPM extensions)
+
+
+QEMU/KVM + SLOF support:
+------------------------
+
+vTPM for QEMU/KVM pSeries virtual machines is support in QEMU 5.0.
+
+To start a QEMU VM with an attached vTPM (swtpm), run the below shown commands.
+The following will setup the vTPM so that its state will be stored in
+/tmp/myvtpm1. A unique directory for each VM instance with attached vTPM
+must be provided. Whenever QEMU is started, the swtpm has to be started
+before it. The file 'boot_rom.bin' is SLOF with vTPM extensions built-in.
+
+ #> mkdir -p /tmp/mytpm1
+ #> swtpm socket --tpm2 --tpmstate dir=/tmp/mytpm1 \
+ --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock
+
+ In another terminal:
+
+ #> sudo qemu-system-ppc64 -display sdl \
+ -machine pseries,accel=kvm \
+ -m 1024 -bios boot_rom.bin -boot menu=on \
+ -nodefaults -device VGA -device pci-ohci -device usb-kbd \
+ -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
+ -tpmdev emulator,id=tpm0,chardev=chrtpm \
+ -device tpm-spapr,tpmdev=tpm0 \
+ -device spapr-vscsi,id=scsi0,reg=0x00002000 \
+ -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \
+ -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0
+
+Notes:
+ - The Linux kernel in the VM must have the tpm_ibmvtpm module available
+ or built-in. A recent kernel is needed that enables TPM 2.0 support
+ in this module.
+
+ - 'swtpm_ioctl --unix /tmp/mytpm1/swtpm-sock -s' can be used to gracefully
+ shut down the vTPM.
diff --git a/roms/SLOF/lib/libtpm/sha.c b/roms/SLOF/lib/libtpm/sha.c
new file mode 100644
index 000000000..902a4bac0
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha.c
@@ -0,0 +1,232 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2021 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * See: NIST standard for SHA-1 in FIPS PUB 180-4
+ */
+
+#include "byteorder.h"
+#include "sha.h"
+#include "string.h"
+
+typedef struct _sha1_ctx {
+ uint32_t h[5];
+} sha1_ctx;
+
+#define rol(VAL, N) \
+({ \
+ uint32_t res; \
+ __asm__ ( \
+ "rotlwi %0, %1, %2\n\t" \
+ : "=r" (res) \
+ : "r" (VAL), "i" (N) \
+ ); \
+ res; \
+})
+
+static void sha1_block(uint32_t *w, sha1_ctx *ctx)
+{
+ uint32_t i;
+ uint32_t a,b,c,d,e,f;
+ uint32_t tmp;
+ uint32_t idx;
+
+ /*
+ * FIPS 180-4 4.2.1: SHA1 Constants
+ */
+ static const uint32_t sha_ko[4] = {
+ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
+ };
+
+ /*
+ * FIPS 180-4 6.1.2: step 1
+ *
+ * 0 <= i <= 15:
+ * W(t) = M(t)
+ * 16 <= i <= 79:
+ * W(t) = ROTL(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16), 1)
+ */
+
+ /* w(0)..w(15) are in big endian format */
+ for (i = 0; i <= 15; i++)
+ w[i] = be32_to_cpu(w[i]);
+
+ for (i = 16; i <= 79; i++) {
+ tmp = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16];
+ w[i] = rol(tmp, 1);
+ }
+
+ /*
+ * step 2: a = H0, b = H1, c = H2, d = H3, e = H4.
+ */
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+
+ /*
+ * step 3: For i = 0 to 79:
+ * T = ROTL(a, 5) + f(i; b,c,d) + e + W(t) + K(t);
+ */
+ for (i = 0; i <= 79; i++) {
+ /*
+ * FIPS 180-4: 4.1.1 : definition of f(i; b,c,d)
+ */
+ if (i <= 19) {
+ /*
+ * 0 <= i <= 19:
+ * f(i; b,c,d) = (b AND c) OR ((NOT b) AND d)
+ */
+ f = (b & c) | ((b ^ 0xffffffff) & d);
+ idx = 0;
+ } else if (i <= 39) {
+ /*
+ * 20 <= i <= 39:
+ * f(i; b,c,d) = b XOR c XOR d
+ */
+ f = b ^ c ^ d;
+ idx = 1;
+ } else if (i <= 59) {
+ /*
+ * 40 <= i <= 59:
+ * f(i; b,c,d) = (b AND c) OR (b AND d) OR (c AND d)
+ */
+ f = (b & c) | (b & d) | (c & d);
+ idx = 2;
+ } else {
+ /*
+ * 60 <= i <= 79:
+ * f(i; b,c,d) = b XOR c XOR d
+ */
+ f = b ^ c ^ d;
+ idx = 3;
+ }
+
+ /*
+ * step 3:
+ * t = ROTL(a, 5) + f(t;b,c,d) + e + K(t) + W(t);
+ * e = d; d = c; c = ROTL(b, 30); b = a; a = t;
+ */
+ tmp = rol(a, 5) +
+ f +
+ e +
+ sha_ko[idx] +
+ w[i];
+ e = d;
+ d = c;
+ c = rol(b, 30);
+ b = a;
+ a = tmp;
+ }
+
+ /*
+ * step 4:
+ * H0 = a + H0, H1 = b + H1, H2 = c + H2, H3 = d + H3, H4 = e + H4
+ */
+ ctx->h[0] += a;
+ ctx->h[1] += b;
+ ctx->h[2] += c;
+ ctx->h[3] += d;
+ ctx->h[4] += e;
+}
+
+static void sha1_do(sha1_ctx *ctx, const uint8_t *data32, uint32_t length)
+{
+ uint32_t offset = 0;
+ uint16_t num;
+ uint64_t bits = 0;
+ uint32_t w[80];
+ uint64_t tmp;
+
+ /* treat data in 64-byte chunks */
+ for (offset = 0; length - offset >= 64; offset += 64) {
+ memcpy(w, data32 + offset, 64);
+ sha1_block((uint32_t *)w, ctx);
+ bits += (64 * 8);
+ }
+
+ /* last block with less than 64 bytes */
+ num = length - offset;
+ bits += (num << 3);
+
+ memcpy(w, data32 + offset, num);
+ /*
+ * FIPS 180-4 5.1: Padding the Message
+ */
+ ((uint8_t *)w)[num] = 0x80;
+ if (64 - (num + 1) > 0)
+ memset( &((uint8_t *)w)[num + 1], 0, 64 - (num + 1));
+
+ if (num >= 56) {
+ /* cannot append number of bits here */
+ sha1_block((uint32_t *)w, ctx);
+ memset(w, 0, 60);
+ }
+
+ /* write number of bits to end of block */
+ tmp = cpu_to_be64(bits);
+ memcpy(&w[14], &tmp, 8);
+
+ sha1_block(w, ctx);
+
+ /* need to switch result's endianness */
+ for (num = 0; num < 5; num++)
+ ctx->h[num] = cpu_to_be32(ctx->h[num]);
+}
+
+void sha1(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ sha1_ctx ctx = {
+ .h = {
+ /*
+ * FIPS 180-4: 6.1.1
+ * -> 5.3.1: initial hash value
+ */
+ 0x67452301,
+ 0xefcdab89,
+ 0x98badcfe,
+ 0x10325476,
+ 0xc3d2e1f0,
+ }
+ };
+
+ sha1_do(&ctx, data, length);
+ memcpy(hash, &ctx.h[0], 20);
+}
+
+#ifdef MAIN
+
+#include "sha_test.h"
+
+int main(void)
+{
+ TESTVECTORS(data);
+ uint8_t hash[20];
+ char input[64];
+ int err = 0;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ err |= test_hash(sha1, hash, sizeof(hash),
+ data[i], strlen(data[i]),
+ SHA1);
+
+ memset(input, 'a', sizeof(input));
+ /* cover critical input size around 56 bytes */
+ for (i = 50; i < sizeof(input); i++)
+ err |= test_hash(sha1, hash, sizeof(hash),
+ input, i, SHA1);
+
+ return err;
+}
+#endif
diff --git a/roms/SLOF/lib/libtpm/sha.h b/roms/SLOF/lib/libtpm/sha.h
new file mode 100644
index 000000000..0bb031e5d
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha.h
@@ -0,0 +1,23 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef __SHA_H
+#define __SHA_H
+
+#include "types.h"
+
+void sha1(const uint8_t *data, uint32_t length, uint8_t *hash);
+void sha256(const uint8_t *data, uint32_t length, uint8_t *hash);
+void sha384(const uint8_t *data, uint32_t length, uint8_t *hash);
+void sha512(const uint8_t *data, uint32_t length, uint8_t *hash);
+
+#endif /* __SHA_H */
diff --git a/roms/SLOF/lib/libtpm/sha256.c b/roms/SLOF/lib/libtpm/sha256.c
new file mode 100644
index 000000000..79bcb8336
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha256.c
@@ -0,0 +1,246 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * See: NIST standard for SHA-256 in FIPS PUB 180-4
+ */
+
+#include "byteorder.h"
+#include "sha.h"
+#include "string.h"
+
+typedef struct _sha256_ctx {
+ uint32_t h[8];
+} sha256_ctx;
+
+#define rotr(VAL, N) \
+({ \
+ uint32_t res; \
+ __asm__ ( \
+ "rotrwi %0, %1, %2\n\t" \
+ : "=r" (res) \
+ : "r" (VAL), "i" (N) \
+ ); \
+ res; \
+})
+
+static inline uint32_t Ch(uint32_t x, uint32_t y, uint32_t z)
+{
+ return (x & y) | ((x ^ 0xffffffff) & z);
+}
+
+static inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
+{
+ return (x & y) | (x & z) | (y & z);
+}
+
+static inline uint32_t sum0(uint32_t x)
+{
+ return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
+}
+
+static inline uint32_t sum1(uint32_t x)
+{
+ return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
+}
+
+static inline uint32_t sigma0(uint32_t x)
+{
+ return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
+}
+
+static inline uint32_t sigma1(uint32_t x)
+{
+ return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
+}
+
+static void sha256_block(uint32_t *w, sha256_ctx *ctx)
+{
+ uint32_t t;
+ uint32_t a, b, c, d, e, f, g, h;
+ uint32_t T1, T2;
+
+ /*
+ * FIPS 180-4 4.2.2: SHA256 Constants
+ */
+ static const uint32_t sha_ko[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+
+ /*
+ * FIPS 180-4 6.2.2: step 1
+ *
+ * 0 <= i <= 15:
+ * W(t) = M(t)
+ * 16 <= i <= 63:
+ * W(t) = sigma1(W(t-2)) + W(t-7) + sigma0(W(t-15)) + W(t-16)
+ */
+
+ /* w(0)..w(15) are in big endian format */
+ for (t = 0; t <= 15; t++)
+ w[t] = be32_to_cpu(w[t]);
+
+ for (t = 16; t <= 63; t++)
+ w[t] = sigma1(w[t-2]) + w[t-7] + sigma0(w[t-15]) + w[t-16];
+
+ /*
+ * step 2: a = H0, b = H1, c = H2, d = H3, e = H4, f = H5, g = H6, h = H7
+ */
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+ f = ctx->h[5];
+ g = ctx->h[6];
+ h = ctx->h[7];
+
+ /*
+ * step 3: For i = 0 to 63:
+ * T1 = h + sum1(e) + Ch(e,f,g) + K(t) + W(t);
+ * T2 = sum0(a) + Maj(a,b,c)
+ * h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a + T1 + T2
+ */
+ for (t = 0; t <= 63; t++) {
+ T1 = h + sum1(e) + Ch(e, f, g) + sha_ko[t] + w[t];
+ T2 = sum0(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ /*
+ * step 4:
+ * H0 = a + H0, H1 = b + H1, H2 = c + H2, H3 = d + H3, H4 = e + H4
+ */
+ ctx->h[0] += a;
+ ctx->h[1] += b;
+ ctx->h[2] += c;
+ ctx->h[3] += d;
+ ctx->h[4] += e;
+ ctx->h[5] += f;
+ ctx->h[6] += g;
+ ctx->h[7] += h;
+}
+
+static void sha256_do(sha256_ctx *ctx, const uint8_t *data32, uint32_t length)
+{
+ uint32_t offset;
+ uint16_t num;
+ uint32_t bits = 0;
+ uint32_t w[64];
+ uint64_t tmp;
+
+ /* treat data in 64-byte chunks */
+ for (offset = 0; length - offset >= 64; offset += 64) {
+ memcpy(w, data32 + offset, 64);
+ sha256_block((uint32_t *)w, ctx);
+ bits += (64 * 8);
+ }
+
+ /* last block with less than 64 bytes */
+ num = length - offset;
+ bits += (num << 3);
+
+ memcpy(w, data32 + offset, num);
+ /*
+ * FIPS 180-4 5.1: Padding the Message
+ */
+ ((uint8_t *)w)[num] = 0x80;
+ if (64 - (num + 1) > 0)
+ memset( &((uint8_t *)w)[num + 1], 0, 64 - (num + 1));
+
+ if (num >= 56) {
+ /* cannot append number of bits here */
+ sha256_block((uint32_t *)w, ctx);
+ memset(w, 0, 60);
+ }
+
+ /* write number of bits to end of block */
+ tmp = cpu_to_be64(bits);
+ memcpy(&w[14], &tmp, 8);
+
+ sha256_block(w, ctx);
+
+ /* need to switch result's endianness */
+ for (num = 0; num < 8; num++)
+ ctx->h[num] = cpu_to_be32(ctx->h[num]);
+}
+
+void sha256(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ sha256_ctx ctx = {
+ .h = {
+ /*
+ * FIPS 180-4: 6.2.1
+ * -> 5.3.3: initial hash value
+ */
+ 0x6a09e667,
+ 0xbb67ae85,
+ 0x3c6ef372,
+ 0xa54ff53a,
+ 0x510e527f,
+ 0x9b05688c,
+ 0x1f83d9ab,
+ 0x5be0cd19
+ }
+ };
+
+ sha256_do(&ctx, data, length);
+ memcpy(hash, ctx.h, sizeof(ctx.h));
+}
+
+#ifdef MAIN
+
+#include "sha_test.h"
+
+int main(void)
+{
+ TESTVECTORS(data);
+ uint8_t hash[32];
+ char input[64];
+ int err = 0;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ err |= test_hash(sha256, hash, sizeof(hash),
+ data[i], strlen(data[i]),
+ SHA256);
+
+ memset(input, 'a', sizeof(input));
+ /* cover critical input size around 56 bytes */
+ for (i = 50; i < sizeof(input); i++)
+ err |= test_hash(sha256, hash, sizeof(hash), input, i, SHA256);
+
+ return err;
+}
+#endif
diff --git a/roms/SLOF/lib/libtpm/sha512.c b/roms/SLOF/lib/libtpm/sha512.c
new file mode 100644
index 000000000..86831abb1
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha512.c
@@ -0,0 +1,285 @@
+/*****************************************************************************
+ * Copyright (c) 2021 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * See: NIST standard for SHA-512 and SHA-384 in FIPS PUB 180-4 & RFC 6234
+ */
+
+#include "byteorder.h"
+#include "sha.h"
+#include "string.h"
+
+typedef struct _sha512_ctx {
+ uint64_t h[8];
+} sha512_ctx;
+
+#define rotr(VAL, N) \
+({ \
+ uint64_t res; \
+ __asm__ ( \
+ "rotrdi %0, %1, %2\n\t" \
+ : "=r" (res) \
+ : "r" (VAL), "i" (N) \
+ ); \
+ res; \
+})
+
+static inline uint64_t Ch(uint64_t x, uint64_t y, uint64_t z)
+{
+ return (x & y) ^ ((x ^ 0xffffffffffffffffULL) & z);
+}
+
+static inline uint64_t Maj(uint64_t x, uint64_t y, uint64_t z)
+{
+ return (x & y) ^ (x & z) ^ (y & z);
+}
+
+static inline uint64_t sum0(uint64_t x)
+{
+ return rotr(x, 28) ^ rotr(x, 34) ^ rotr(x, 39);
+}
+
+static inline uint64_t sum1(uint64_t x)
+{
+ return rotr(x, 14) ^ rotr(x, 18) ^ rotr(x, 41);
+}
+
+static inline uint64_t sigma0(uint64_t x)
+{
+ return rotr(x, 1) ^ rotr(x, 8) ^ (x >> 7);
+}
+
+static inline uint64_t sigma1(uint64_t x)
+{
+ return rotr(x, 19) ^ rotr(x, 61) ^ (x >> 6);
+}
+
+static void sha512_block(uint64_t *w, sha512_ctx *ctx)
+{
+ uint32_t t;
+ uint64_t a, b, c, d, e, f, g, h;
+ uint64_t T1, T2;
+
+ /*
+ * FIPS 180-4 4.2.2: SHA512 Constants
+ */
+ static const uint64_t sha_ko[80] = {
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
+ 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
+ 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
+ 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
+ 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
+ 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
+ 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
+ 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
+ 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
+ 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
+ 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
+ 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
+ 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
+ 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
+ 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
+ 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
+ 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+ };
+
+ /*
+ * FIPS 180-4 6.4.2: step 1
+ *
+ * 0 <= i <= 15:
+ * W(t) = M(t)
+ * 16 <= i <= 79:
+ * W(t) = sigma1(W(t-2)) + W(t-7) + sigma0(W(t-15)) + W(t-16)
+ */
+
+ /* w(0)..w(15) are in big endian format */
+ for (t = 0; t <= 15; t++)
+ w[t] = be64_to_cpu(w[t]);
+
+ for (t = 16; t <= 79; t++)
+ w[t] = sigma1(w[t-2]) + w[t-7] + sigma0(w[t-15]) + w[t-16];
+
+ /*
+ * step 2: a = H0, b = H1, c = H2, d = H3, e = H4, f = H5, g = H6, h = H7
+ */
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+ f = ctx->h[5];
+ g = ctx->h[6];
+ h = ctx->h[7];
+
+ /*
+ * step 3: For i = 0 to 79:
+ * T1 = h + sum1(e) + Ch(e,f,g) + K(t) + W(t);
+ * T2 = sum0(a) + Maj(a,b,c)
+ * h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a + T1 + T2
+ */
+ for (t = 0; t <= 79; t++) {
+ T1 = h + sum1(e) + Ch(e, f, g) + sha_ko[t] + w[t];
+ T2 = sum0(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ /*
+ * step 4:
+ * H0 = a + H0, H1 = b + H1, H2 = c + H2, H3 = d + H3, H4 = e + H4
+ */
+ ctx->h[0] += a;
+ ctx->h[1] += b;
+ ctx->h[2] += c;
+ ctx->h[3] += d;
+ ctx->h[4] += e;
+ ctx->h[5] += f;
+ ctx->h[6] += g;
+ ctx->h[7] += h;
+}
+
+static void sha512_do(sha512_ctx *ctx, const uint8_t *data32, uint32_t length)
+{
+ uint32_t offset;
+ uint16_t num;
+ uint64_t bits = 0;
+ uint64_t w[80];
+ uint64_t tmp;
+
+ /* treat data in 128-byte/1024 bit chunks */
+ for (offset = 0; length - offset >= 128; offset += 128) {
+ memcpy(w, data32 + offset, 128);
+ sha512_block(w, ctx);
+ bits += (128 * 8);
+ }
+
+ /* last block with less than 128 bytes */
+ num = length - offset;
+ bits += (num << 3);
+
+ memcpy(w, data32 + offset, num);
+ /*
+ * FIPS 180-4 5.1: Padding the Message
+ */
+ ((uint8_t *)w)[num] = 0x80;
+ if (128 - (num + 1) > 0)
+ memset( &((uint8_t *)w)[num + 1], 0, 128 - (num + 1));
+
+ if (num >= 112) {
+ /* cannot append number of bits here;
+ * need space for 128 bits (16 bytes)
+ */
+ sha512_block((uint64_t *)w, ctx);
+ memset(w, 0, 128);
+ }
+
+ /* write number of bits to end of the block; we write 64 bits */
+ tmp = cpu_to_be64(bits);
+ memcpy(&w[15], &tmp, 8);
+
+ sha512_block(w, ctx);
+
+ /* need to switch result's endianness */
+ for (num = 0; num < 8; num++)
+ ctx->h[num] = cpu_to_be64(ctx->h[num]);
+}
+
+void sha384(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ sha512_ctx ctx = {
+ .h = {
+ /*
+ * FIPS 180-4: 6.2.1
+ * -> 5.3.4: initial hash value
+ */
+ 0xcbbb9d5dc1059ed8,
+ 0x629a292a367cd507,
+ 0x9159015a3070dd17,
+ 0x152fecd8f70e5939,
+ 0x67332667ffc00b31,
+ 0x8eb44a8768581511,
+ 0xdb0c2e0d64f98fa7,
+ 0x47b5481dbefa4fa4
+ }
+ };
+
+ sha512_do(&ctx, data, length);
+ memcpy(hash, ctx.h, 384/8);
+}
+
+void sha512(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ sha512_ctx ctx = {
+ .h = {
+ /*
+ * FIPS 180-4: 6.2.1
+ * -> 5.3.5: initial hash value
+ */
+ 0x6a09e667f3bcc908,
+ 0xbb67ae8584caa73b,
+ 0x3c6ef372fe94f82b,
+ 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1,
+ 0x9b05688c2b3e6c1f,
+ 0x1f83d9abfb41bd6b,
+ 0x5be0cd19137e2179
+ }
+ };
+
+ sha512_do(&ctx, data, length);
+ memcpy(hash, ctx.h, sizeof(ctx.h));
+}
+
+
+#ifdef MAIN
+
+#include "sha_test.h"
+
+int main(void)
+{
+ TESTVECTORS(data);
+ uint8_t hash512[64];
+ uint8_t hash384[48];
+ char input[128];
+ int err = 0;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ err |= test_hash(sha384, hash384, sizeof(hash384),
+ data[i], strlen(data[i]),
+ SHA384);
+ err |= test_hash(sha512, hash512, sizeof(hash512),
+ data[i], strlen(data[i]),
+ SHA512);
+ }
+
+ memset(input, 'a', sizeof(input));
+ /* cover critical input size around 112 bytes */
+ for (i = 110; i < sizeof(input); i++) {
+ err |= test_hash(sha384, hash384, sizeof(hash384),
+ input, i, SHA384);
+ err |= test_hash(sha512, hash512, sizeof(hash512),
+ input, i, SHA512);
+ }
+
+ return err;
+}
+#endif
diff --git a/roms/SLOF/lib/libtpm/sha_test.h b/roms/SLOF/lib/libtpm/sha_test.h
new file mode 100644
index 000000000..af82fac25
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha_test.h
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * Copyright (c) 2021 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef SHA_TEST_H
+#define SHA_TEST_H
+
+#include <stdio.h>
+
+#include "helpers.h"
+
+/* to avoid compilation issues do not include openssl/sha.h */
+unsigned char *SHA1(const unsigned char *, size_t, unsigned char *);
+unsigned char *SHA256(const unsigned char *, size_t, unsigned char *);
+unsigned char *SHA384(const unsigned char *, size_t, unsigned char *);
+unsigned char *SHA512(const unsigned char *, size_t, unsigned char *);
+
+typedef void (*hashfunc)(const uint8_t *data, uint32_t length, uint8_t *hash);
+typedef unsigned char *(*osslhashfunc)(const unsigned char *, size_t,
+ unsigned char *);
+
+#define TESTVECTORS(NAME) \
+char *NAME[] = { \
+ "", \
+ "abc", \
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", \
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" \
+};
+
+static inline int
+test_hash(hashfunc hf, uint8_t *hash, size_t hashlen,
+ const char *data, uint32_t length,
+ osslhashfunc osslhf)
+{
+ unsigned char expected[hashlen];
+ int ret = 0;
+
+ osslhf((const unsigned char *)data, length, expected);
+
+ hf((uint8_t *)data, length, hash);
+ if (!memcmp(hash, expected, hashlen)) {
+ printf("PASS: input length: %u\n", length);
+ } else {
+ printf("FAIL data: %s\n", data);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+#endif /* SHA_TEST_H */
diff --git a/roms/SLOF/lib/libtpm/tcgbios.c b/roms/SLOF/lib/libtpm/tcgbios.c
new file mode 100644
index 000000000..e43745e42
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tcgbios.c
@@ -0,0 +1,1477 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ * Stefan Berger, stefanb@linux.ibm.com
+ * Kevin O'Connor, kevin@koconnor.net
+ *****************************************************************************/
+
+/*
+ * Implementation of the TPM BIOS extension according to the specification
+ * described in the IBM VTPM Firmware document and the TCG Specification
+ * that can be found here under the following link:
+ * https://trustedcomputinggroup.org/resource/pc-client-work-group-specific-implementation-specification-for-conventional-bios/
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "types.h"
+#include "byteorder.h"
+#include "tpm_drivers.h"
+#include "string.h"
+#include "tcgbios.h"
+#include "tcgbios_int.h"
+#include "stdio.h"
+#include "sha.h"
+#include "helpers.h"
+#include "version.h"
+#include "OF.h"
+#include "libelf.h"
+
+#undef TCGBIOS_DEBUG
+//#define TCGBIOS_DEBUG
+#ifdef TCGBIOS_DEBUG
+#define dprintf(_x ...) do { printf("TCGBIOS: " _x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+static struct {
+ unsigned tpm_probed:1;
+ unsigned tpm_found:1;
+ unsigned tpm_working:1;
+
+ /* base address of the log area */
+ uint8_t *log_base;
+
+ /* size of the logging area */
+ size_t log_area_size;
+
+ /* where to write the next log entry to */
+ uint8_t *log_area_next_entry;
+
+ /* PCR selection as received from TPM */
+ uint32_t tpm20_pcr_selection_size;
+ struct tpml_pcr_selection *tpm20_pcr_selection;
+} tpm_state;
+
+#define TPM2_ALG_SHA1_FLAG (1 << 0)
+#define TPM2_ALG_SHA256_FLAG (1 << 1)
+#define TPM2_ALG_SHA384_FLAG (1 << 2)
+#define TPM2_ALG_SHA512_FLAG (1 << 3)
+#define TPM2_ALG_SM3_256_FLAG (1 << 4)
+#define TPM2_ALG_SHA3_256_FLAG (1 << 5)
+#define TPM2_ALG_SHA3_384_FLAG (1 << 6)
+#define TPM2_ALG_SHA3_512_FLAG (1 << 7)
+
+static const uint8_t ZeroGuid[16] = { 0 };
+
+static UEFI_GPT_DATA *uefi_gpt_data;
+static size_t uefi_gpt_data_size;
+
+/*
+ * TPM 2 logs are written in little endian format.
+ */
+static inline uint32_t log32_to_cpu(uint32_t val)
+{
+ return le32_to_cpu(val);
+}
+
+static inline uint32_t cpu_to_log32(uint32_t val)
+{
+ return cpu_to_le32(val);
+}
+
+static inline uint16_t cpu_to_log16(uint16_t val)
+{
+ return cpu_to_le16(val);
+}
+
+/********************************************************
+ Extensions for TCG-enabled BIOS
+ *******************************************************/
+
+static void probe_tpm(void)
+{
+ tpm_state.tpm_probed = true;
+ tpm_state.tpm_found = spapr_is_vtpm_present();
+ tpm_state.tpm_working = tpm_state.tpm_found;
+}
+
+/****************************************************************
+ * Digest formatting
+ ****************************************************************/
+
+/* A 'struct tpm_log_entry' is a local data structure containing a
+ * 'TCG_PCR_EVENT2_Header' followed by space for the maximum supported
+ * digest. The digest is a series of TPMT_HA structs on tpm2.0.
+ */
+struct tpm_log_entry {
+ TCG_PCR_EVENT2_Header hdr;
+ uint8_t pad[sizeof(struct TPML_DIGEST_VALUES)
+ + 8 * sizeof(struct TPMT_HA)
+ + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
+ + SHA512_BUFSIZE + SM3_256_BUFSIZE + SHA3_256_BUFSIZE
+ + SHA3_384_BUFSIZE + SHA3_512_BUFSIZE];
+} __attribute__((packed));
+
+static const struct hash_parameters {
+ uint16_t hashalg;
+ uint8_t hashalg_flag;
+ uint8_t hash_buffersize;
+ const char *name;
+ void (*hashfunc)(const uint8_t *data, uint32_t length, uint8_t *hash);
+} hash_parameters[] = {
+ {
+ .hashalg = TPM2_ALG_SHA1,
+ .hashalg_flag = TPM2_ALG_SHA1_FLAG,
+ .hash_buffersize = SHA1_BUFSIZE,
+ .name = "SHA1",
+ .hashfunc = sha1,
+ }, {
+ .hashalg = TPM2_ALG_SHA256,
+ .hashalg_flag = TPM2_ALG_SHA256_FLAG,
+ .hash_buffersize = SHA256_BUFSIZE,
+ .name = "SHA256",
+ .hashfunc = sha256,
+ }, {
+ .hashalg = TPM2_ALG_SHA384,
+ .hashalg_flag = TPM2_ALG_SHA384_FLAG,
+ .hash_buffersize = SHA384_BUFSIZE,
+ .name = "SHA384",
+ .hashfunc = sha384,
+ }, {
+ .hashalg = TPM2_ALG_SHA512,
+ .hashalg_flag = TPM2_ALG_SHA512_FLAG,
+ .hash_buffersize = SHA512_BUFSIZE,
+ .name = "SHA512",
+ .hashfunc = sha512,
+ }, {
+ .hashalg = TPM2_ALG_SM3_256,
+ .hashalg_flag = TPM2_ALG_SM3_256_FLAG,
+ .hash_buffersize = SM3_256_BUFSIZE,
+ .name = "SM3-256",
+ }, {
+ .hashalg = TPM2_ALG_SHA3_256,
+ .hashalg_flag = TPM2_ALG_SHA3_256_FLAG,
+ .hash_buffersize = SHA3_256_BUFSIZE,
+ .name = "SHA3-256",
+ }, {
+ .hashalg = TPM2_ALG_SHA3_384,
+ .hashalg_flag = TPM2_ALG_SHA3_384_FLAG,
+ .hash_buffersize = SHA3_384_BUFSIZE,
+ .name = "SHA3-384",
+ }, {
+ .hashalg = TPM2_ALG_SHA3_512,
+ .hashalg_flag = TPM2_ALG_SHA3_512_FLAG,
+ .hash_buffersize = SHA3_512_BUFSIZE,
+ .name = "SHA3-512",
+ }
+};
+
+static const struct hash_parameters *tpm20_find_by_hashalg(uint16_t hashAlg)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+ if (hash_parameters[i].hashalg == hashAlg)
+ return &hash_parameters[i];
+ }
+ return NULL;
+}
+
+static const struct hash_parameters *
+tpm20_find_by_hashalg_flag(uint16_t hashalg_flag)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+ if (hash_parameters[i].hashalg_flag == hashalg_flag)
+ return &hash_parameters[i];
+ }
+ return NULL;
+}
+
+static inline int tpm20_get_hash_buffersize(uint16_t hashAlg)
+{
+ const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg);
+
+ if (hp)
+ return hp->hash_buffersize;
+ return -1;
+}
+
+static inline uint8_t tpm20_hashalg_to_flag(uint16_t hashAlg)
+{
+ const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg);
+
+ if (hp)
+ return hp->hashalg_flag;
+ return 0;
+}
+
+static uint16_t tpm20_hashalg_flag_to_hashalg(uint8_t hashalg_flag)
+{
+ const struct hash_parameters *hp;
+
+ hp = tpm20_find_by_hashalg_flag(hashalg_flag);
+ if (hp)
+ return hp->hashalg;
+ return 0;
+}
+
+static const char * tpm20_hashalg_flag_to_name(uint8_t hashalg_flag)
+{
+ const struct hash_parameters *hp;
+
+ hp = tpm20_find_by_hashalg_flag(hashalg_flag);
+ if (hp)
+ return hp->name;
+ return NULL;
+}
+
+static void tpm2_hash_data(uint16_t hashAlg,
+ const uint8_t *data, uint32_t data_len,
+ uint8_t *hash)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+ if (hash_parameters[i].hashalg == hashAlg) {
+ if (hash_parameters[i].hashfunc) {
+ hash_parameters[i].hashfunc(data, data_len,
+ hash);
+ } else {
+ memset(hash, 0xff,
+ hash_parameters[i].hash_buffersize);
+ }
+ }
+ }
+}
+
+/*
+ * Build the TPM2 TPML_DIGEST_VALUES data structure from the given hash.
+ * Follow the PCR bank configuration of the TPM and write the same hash
+ * in either truncated or zero-padded form in the areas of all the other
+ * hashes. For example, write the sha256 hash in the area of the sha384
+ * hash and fill the remaining bytes with zeros. Or truncate the sha256
+ * hash when writing it in the area of the sha1 hash.
+ *
+ * le: the log entry to build the digest in
+ * hashdata: the data to hash
+ * hashdata_len: the length of the hashdata
+ * bigEndian: whether to build in big endian format for the TPM or log
+ * little endian for the log (TPM 2.0)
+ *
+ * Returns the digest size; -1 on fatal error
+ */
+static int tpm20_build_digest(struct tpm_log_entry *le,
+ const uint8_t *hashdata, uint32_t hashdata_len,
+ bool bigEndian)
+{
+ struct tpms_pcr_selection *sel;
+ void *nsel, *end;
+ void *dest = le->hdr.digests + sizeof(struct TPML_DIGEST_VALUES);
+ uint32_t count, numAlgs;
+ struct TPMT_HA *v;
+ struct TPML_DIGEST_VALUES *vs;
+
+ sel = tpm_state.tpm20_pcr_selection->selections;
+ end = (void *)tpm_state.tpm20_pcr_selection +
+ tpm_state.tpm20_pcr_selection_size;
+
+ for (count = 0, numAlgs = 0;
+ count < be32_to_cpu(tpm_state.tpm20_pcr_selection->count);
+ count++) {
+ int hsize;
+ uint8_t sizeOfSelect = sel->sizeOfSelect;
+
+ nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+ if (nsel > end)
+ break;
+
+ /* PCR 0-7 unused ? -- skip */
+ if (!sizeOfSelect || sel->pcrSelect[0] == 0) {
+ sel = nsel;
+ continue;
+ }
+
+ hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg));
+ if (hsize < 0) {
+ dprintf("TPM is using an unsupported hash: %d\n",
+ be16_to_cpu(sel->hashAlg));
+ return -1;
+ }
+
+ /* buffer size sanity check before writing */
+ v = dest;
+ if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) {
+ dprintf("tpm_log_entry is too small\n");
+ return -1;
+ }
+
+ if (bigEndian)
+ v->hashAlg = sel->hashAlg;
+ else
+ v->hashAlg = cpu_to_le16(be16_to_cpu(sel->hashAlg));
+
+ tpm2_hash_data(be16_to_cpu(sel->hashAlg), hashdata, hashdata_len,
+ v->hash);
+
+ dest += sizeof(*v) + hsize;
+ sel = nsel;
+
+ numAlgs++;
+ }
+
+ if (sel != end) {
+ dprintf("Malformed pcr selection structure fron TPM\n");
+ return -1;
+ }
+
+ vs = (void*)le->hdr.digests;
+ if (bigEndian)
+ vs->count = cpu_to_be32(numAlgs);
+ else
+ vs->count = cpu_to_le32(numAlgs);
+
+ return dest - (void*)le->hdr.digests;
+}
+
+/****************************************************************
+ * TPM hardware command wrappers
+ ****************************************************************/
+
+/* Helper function for sending TPM commands that take a single
+ * optional parameter (0, 1, or 2 bytes) and have no special response.
+ */
+static int
+tpm_simple_cmd(uint8_t locty, uint32_t ordinal, int param_size, uint16_t param,
+ enum tpm_duration_type to_t)
+{
+ struct {
+ struct tpm_req_header trqh;
+ uint16_t param;
+ } __attribute__((packed)) req = {
+ .trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size),
+ .trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .trqh.ordinal = cpu_to_be32(ordinal),
+ };
+ uint8_t obuffer[64];
+ struct tpm_rsp_header *trsh = (void *)obuffer;
+ uint32_t obuffer_len = sizeof(obuffer);
+ int ret;
+
+ switch (param_size) {
+ case 2:
+ req.param = cpu_to_be16(param);
+ break;
+ case 1:
+ *(uint8_t *)&req.param = param;
+ break;
+ }
+
+ memset(obuffer, 0, sizeof(obuffer));
+ ret = spapr_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t);
+ ret = ret ? -1 : (int) be32_to_cpu(trsh->errcode);
+ dprintf("Return from tpm_simple_cmd(%x, %x) = %x\n",
+ ordinal, param, ret);
+
+ return ret;
+}
+
+static int
+tpm20_getcapability(uint32_t capability, uint32_t property, uint32_t count,
+ struct tpm_rsp_header *rsp, uint32_t rsize)
+{
+ struct tpm2_req_getcapability trg = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trg)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability),
+ .capability = cpu_to_be32(capability),
+ .property = cpu_to_be32(property),
+ .propertycount = cpu_to_be32(count),
+ };
+ uint32_t resp_size = rsize;
+ int ret;
+
+ ret = spapr_transmit(0, &trg.hdr, rsp, &resp_size,
+ TPM_DURATION_TYPE_SHORT);
+ ret = (ret ||
+ rsize < be32_to_cpu(rsp->totlen)) ? -1
+ : (int) be32_to_cpu(rsp->errcode);
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int
+tpm20_get_pcrbanks(void)
+{
+ uint8_t buffer[128];
+ uint32_t size;
+ struct tpm2_res_getcapability *trg =
+ (struct tpm2_res_getcapability *)&buffer;
+ uint32_t resplen;
+ int ret;
+
+ ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr,
+ sizeof(buffer));
+ if (ret)
+ return ret;
+
+ /* defend against (broken) TPM sending packets that are too short */
+ resplen = be32_to_cpu(trg->hdr.totlen);
+ if (resplen <= offset_of(struct tpm2_res_getcapability, data))
+ return -1;
+
+ size = resplen - offset_of(struct tpm2_res_getcapability, data);
+ /* we need a valid tpml_pcr_selection up to and including sizeOfSelect*/
+ if (size < offset_of(struct tpml_pcr_selection, selections) +
+ offset_of(struct tpms_pcr_selection, pcrSelect))
+ return -1;
+
+ tpm_state.tpm20_pcr_selection = SLOF_alloc_mem(size);
+ if (tpm_state.tpm20_pcr_selection) {
+ memcpy(tpm_state.tpm20_pcr_selection, &trg->data, size);
+ tpm_state.tpm20_pcr_selection_size = size;
+ } else {
+ printf("TCGBIOS: Failed to allocated %u bytes.\n", size);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tpm20_extend(struct tpm_log_entry *le, int digest_len)
+{
+ struct tpm2_req_extend tmp_tre = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.totlen = cpu_to_be32(0),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend),
+ .pcrindex = cpu_to_be32(log32_to_cpu(le->hdr.pcrindex)),
+ .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ };
+ uint8_t buffer[sizeof(tmp_tre) + sizeof(le->pad)];
+ struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer;
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret;
+
+ memcpy(tre, &tmp_tre, sizeof(tmp_tre));
+ memcpy(&tre->digest[0], le->hdr.digests, digest_len);
+
+ tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len);
+
+ ret = spapr_transmit(0, &tre->hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_SHORT);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ return -1;
+
+ return 0;
+}
+
+static int tpm20_stirrandom(void)
+{
+ struct tpm2_req_stirrandom stir = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(stir)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom),
+ .size = cpu_to_be16(sizeof(stir.stir)),
+ .stir = rand(),
+ };
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret = spapr_transmit(0, &stir.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_SHORT);
+
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ ret = -1;
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int tpm20_getrandom(uint8_t *buf, uint16_t buf_len)
+{
+ struct tpm2_res_getrandom rsp;
+ struct tpm2_req_getrandom trgr = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trgr)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom),
+ .bytesRequested = cpu_to_be16(buf_len),
+ };
+ uint32_t resp_length = sizeof(rsp);
+ int ret;
+
+ if (buf_len > sizeof(rsp.rnd.buffer))
+ return -1;
+
+ ret = spapr_transmit(0, &trgr.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_MEDIUM);
+ if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode)
+ ret = -1;
+ else
+ memcpy(buf, rsp.rnd.buffer, buf_len);
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int tpm20_hierarchychangeauth(uint8_t auth[20])
+{
+ struct tpm2_req_hierarchychangeauth trhca = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trhca)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth),
+ .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+ .authblocksize = cpu_to_be32(sizeof(trhca.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ .newAuth = {
+ .size = cpu_to_be16(sizeof(trhca.newAuth.buffer)),
+ },
+ };
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret;
+
+ memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer));
+
+ ret = spapr_transmit(0, &trhca.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_MEDIUM);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ ret = -1;
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int tpm20_hierarchycontrol(uint32_t hierarchy, uint8_t state)
+{
+ struct tpm2_req_hierarchycontrol trh = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trh)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl),
+ .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+ .authblocksize = cpu_to_be32(sizeof(trh.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ .enable = cpu_to_be32(hierarchy),
+ .state = state,
+ };
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret;
+
+ ret = spapr_transmit(0, &trh.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_MEDIUM);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ ret = -1;
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+/****************************************************************
+ * Setup and Measurements
+ ****************************************************************/
+
+bool tpm_is_working(void)
+{
+ if (!tpm_state.tpm_probed)
+ probe_tpm();
+
+ return tpm_state.tpm_working;
+}
+
+static void tpm_set_failure(void)
+{
+ tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO);
+ tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO);
+
+ tpm_state.tpm_working = false;
+}
+
+/*
+ * Extend the OFDT log with the given entry by copying the
+ * entry data into the log.
+ *
+ * @pcpes: Pointer to the structure to be copied into the log
+ * @event: The event to be appended to 'pcpes'
+ * @event_length: The length of the event
+ *
+ * Returns 0 on success, an error code otherwise.
+ */
+static uint32_t tpm_log_event_long(TCG_PCR_EVENT2_Header *entry,
+ int digest_len,
+ const void *event, uint32_t event_length)
+{
+ size_t size, logsize;
+ void *dest;
+ TCG_PCR_EVENT2_Trailer *t;
+
+ dprintf("log base address = %p, next entry = %p\n",
+ tpm_state.log_base, tpm_state.log_area_next_entry);
+
+ if (tpm_state.log_area_next_entry == NULL)
+ return TCGBIOS_LOGOVERFLOW;
+
+ size = sizeof(*entry) + digest_len +
+ sizeof(TCG_PCR_EVENT2_Trailer) + event_length;
+ logsize = (tpm_state.log_area_next_entry + size -
+ tpm_state.log_base);
+ if (logsize > tpm_state.log_area_size) {
+ dprintf("TCGBIOS: LOG OVERFLOW: size = %zu\n", size);
+ return TCGBIOS_LOGOVERFLOW;
+ }
+
+ dest = tpm_state.log_area_next_entry;
+ memcpy(dest, entry, sizeof(*entry) + digest_len);
+
+ t = dest + sizeof(*entry) + digest_len;
+ t->eventdatasize = cpu_to_log32(event_length);
+ if (event_length)
+ memcpy(t->event, event, event_length);
+
+ tpm_state.log_area_next_entry += size;
+
+ return 0;
+}
+
+/* Add an entry at the start of the log describing digest formats
+ */
+static int tpm20_write_EfiSpecIdEventStruct(void)
+{
+ struct {
+ struct TCG_EfiSpecIdEventStruct hdr;
+ uint32_t pad[sizeof(struct tpm_log_entry) +
+ sizeof(uint8_t)];
+ } event = {
+ .hdr.signature = "Spec ID Event03",
+ .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT,
+ .hdr.specVersionMinor = 0,
+ .hdr.specVersionMajor = 2,
+ .hdr.specErrata = 2,
+ .hdr.uintnSize = 2,
+ };
+ struct tpms_pcr_selection *sel;
+ void *nsel, *end;
+ unsigned event_size;
+ uint8_t *vendorInfoSize;
+ struct tpm_log_entry le = {
+ .hdr.eventtype = cpu_to_log32(EV_NO_ACTION),
+ };
+ uint32_t count, numAlgs;
+
+ sel = tpm_state.tpm20_pcr_selection->selections;
+ end = (void*)tpm_state.tpm20_pcr_selection +
+ tpm_state.tpm20_pcr_selection_size;
+
+ for (count = 0, numAlgs = 0;
+ count < be32_to_cpu(tpm_state.tpm20_pcr_selection->count);
+ count++) {
+ int hsize;
+ uint8_t sizeOfSelect = sel->sizeOfSelect;
+
+ nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+ if (nsel > end)
+ break;
+
+ /* PCR 0-7 unused ? -- skip */
+ if (!sizeOfSelect || sel->pcrSelect[0] == 0) {
+ sel = nsel;
+ continue;
+ }
+
+ hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg));
+ if (hsize < 0) {
+ dprintf("TPM is using an unsupported hash: %d\n",
+ be16_to_cpu(sel->hashAlg));
+ return -1;
+ }
+
+ event_size = offset_of(struct TCG_EfiSpecIdEventStruct,
+ digestSizes[count+1]);
+ if (event_size > sizeof(event) - sizeof(uint8_t)) {
+ dprintf("EfiSpecIdEventStruct pad too small\n");
+ return -1;
+ }
+
+ event.hdr.digestSizes[numAlgs].algorithmId =
+ cpu_to_log16(be16_to_cpu(sel->hashAlg));
+ event.hdr.digestSizes[numAlgs].digestSize = cpu_to_log16(hsize);
+ numAlgs++;
+
+ sel = nsel;
+ }
+
+ if (sel != end) {
+ dprintf("Malformed pcr selection structure fron TPM\n");
+ return -1;
+ }
+
+ event.hdr.numberOfAlgorithms = cpu_to_log32(numAlgs);
+ event_size = offset_of(struct TCG_EfiSpecIdEventStruct,
+ digestSizes[numAlgs]);
+ vendorInfoSize = (void*)&event + event_size;
+ *vendorInfoSize = 0;
+ event_size += sizeof(*vendorInfoSize);
+
+ return tpm_log_event_long(&le.hdr, SHA1_BUFSIZE, &event, event_size);
+}
+
+static int tpm20_startup(void)
+{
+ int ret;
+
+ ret = tpm_simple_cmd(0, TPM2_CC_Startup,
+ 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n",
+ ret);
+
+ if (ret)
+ goto err_exit;
+
+ ret = tpm_simple_cmd(0, TPM2_CC_SelfTest,
+ 1, TPM2_YES, TPM_DURATION_TYPE_LONG);
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_SELF_TEST = 0x%08x\n",
+ ret);
+
+ if (ret)
+ goto err_exit;
+
+ ret = tpm20_get_pcrbanks();
+ if (ret)
+ goto err_exit;
+
+ /* the log parameters will be passed from Forth layer */
+
+ return 0;
+
+err_exit:
+ dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+ tpm_set_failure();
+ return -1;
+}
+
+uint32_t tpm_start(void)
+{
+ probe_tpm();
+
+ if (!tpm_is_working()) {
+ dprintf("%s: Machine does not have a working TPM\n",
+ __func__);
+ return TCGBIOS_FATAL_COM_ERROR;
+ }
+
+ return tpm20_startup();
+}
+
+void tpm_finalize(void)
+{
+ spapr_vtpm_finalize();
+}
+
+static void tpm20_prepboot(void)
+{
+ uint8_t auth[20];
+ int ret;
+
+ ret = tpm20_stirrandom();
+ if (ret)
+ goto err_exit;
+
+ ret = tpm20_getrandom(&auth[0], sizeof(auth));
+ if (ret)
+ goto err_exit;
+
+ ret = tpm20_hierarchychangeauth(auth);
+ if (ret)
+ goto err_exit;
+
+ return;
+
+err_exit:
+ dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+ tpm_set_failure();
+}
+
+/*
+ * Prepare TPM for boot; this function has to be called before
+ * the firmware transitions to the boot loader.
+ */
+uint32_t tpm_leave_firmware(void)
+{
+ tpm20_prepboot();
+
+ return 0;
+}
+
+/****************************************************************
+ * Forth interface
+ ****************************************************************/
+
+void tpm_set_log_parameters(void *addr, size_t size)
+{
+ int ret;
+
+ dprintf("Log is at 0x%llx; size is %zu bytes\n",
+ (uint64_t)addr, size);
+ tpm_state.log_base = addr;
+ tpm_state.log_area_next_entry = addr;
+ tpm_state.log_area_size = size;
+
+ ret = tpm20_write_EfiSpecIdEventStruct();
+ if (ret)
+ tpm_set_failure();
+}
+
+uint32_t tpm_get_logsize(void)
+{
+ uint32_t logsize = tpm_state.log_area_next_entry - tpm_state.log_base;
+
+ dprintf("log size: %u\n", logsize);
+
+ return logsize;
+}
+
+/*
+ * Add a measurement to the log;
+ *
+ * Input parameters:
+ * @pcrindex : PCR to extend
+ * @event_type : type of event
+ * @info : pointer to info (i.e., string) to be added to the log as-is
+ * @info_length: length of the info
+ * @hashdata : pointer to data to be hashed
+ * @hashdata_length: length of the data
+ *
+ */
+static uint32_t tpm_add_measurement_to_log(uint32_t pcrindex,
+ uint32_t eventtype,
+ const char *info,
+ uint32_t infolen,
+ const uint8_t *hashdata,
+ uint32_t hashdatalen)
+{
+ struct tpm_log_entry le = {
+ .hdr.pcrindex = cpu_to_log32(pcrindex),
+ .hdr.eventtype = cpu_to_log32(eventtype),
+ };
+ int digest_len;
+ int ret;
+
+ digest_len = tpm20_build_digest(&le, hashdata, hashdatalen, true);
+ if (digest_len < 0)
+ return TCGBIOS_GENERAL_ERROR;
+ ret = tpm20_extend(&le, digest_len);
+ if (ret) {
+ tpm_set_failure();
+ return TCGBIOS_COMMAND_ERROR;
+ }
+ tpm20_build_digest(&le, hashdata, hashdatalen, false);
+ return tpm_log_event_long(&le.hdr, digest_len, info, infolen);
+}
+
+/*
+ * Measure the contents of a buffer into the given PCR and log it with the
+ * given eventtype. If is_elf is true, try to determine the size of the
+ * ELF file in the buffer and use its size rather than the much larger data
+ * buffer it is held in. In case of failure to detect the ELF file size,
+ * log an error.
+ *
+ * Input parameters:
+ * @pcrindex : PCR to extend
+ * @eventtype : type of event
+ * @data: the buffer to measure
+ * @datalen: length of the buffer
+ * @desc: The description to log
+ * @desclen: The length of the description
+ * @is_elf: Whether data buffer holds an ELF file and we should determine
+ * the original file size.
+ *
+ * Returns 0 on success, an error code otherwise.
+ */
+uint32_t tpm_hash_log_extend_event_buffer(uint32_t pcrindex, uint32_t eventtype,
+ const void *data, uint64_t datalen,
+ const char *desc, uint32_t desclen,
+ bool is_elf)
+{
+ long len;
+ char buf[256];
+
+ if (is_elf) {
+ len = elf_get_file_size(data, datalen);
+ if (len > 0) {
+ datalen = len;
+ } else {
+ snprintf(buf, sizeof(buf), "BAD ELF FILE: %s", desc);
+ return tpm_add_measurement_to_log(pcrindex, eventtype,
+ buf, strlen(buf),
+ (uint8_t *)buf, strlen(buf));
+ }
+ }
+ return tpm_add_measurement_to_log(pcrindex, eventtype,
+ desc, desclen,
+ data, datalen);
+}
+
+uint32_t tpm_2hash_ext_log(uint32_t pcrindex,
+ uint32_t eventtype,
+ const char *info, uint32_t infolen,
+ const void *data, uint64_t datalen)
+{
+ uint32_t ret;
+
+ ret = tpm_add_measurement_to_log(pcrindex, eventtype,
+ info, infolen,
+ data, datalen);
+ if (!ret)
+ return (uint32_t)-1; // TRUE
+ return 0; // FALSE
+}
+
+/*
+ * Add an EV_ACTION measurement to the list of measurements
+ */
+static uint32_t tpm_add_action(uint32_t pcrIndex, const char *string)
+{
+ uint32_t len = strlen(string);
+
+ return tpm_add_measurement_to_log(pcrIndex, EV_ACTION,
+ string, len, (uint8_t *)string, len);
+}
+
+/*
+ * Add event separators for a range of PCRs
+ */
+uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr)
+{
+ static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff};
+ uint32_t pcrIndex;
+ int rc;
+
+ if (!tpm_is_working())
+ return TCGBIOS_GENERAL_ERROR;
+
+ if (start_pcr >= 24 || start_pcr > end_pcr)
+ return TCGBIOS_INVALID_INPUT_PARA;
+
+ /* event separators need to be extended and logged for PCRs 0-7 */
+ for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) {
+ rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR,
+ (const char *)evt_separator,
+ sizeof(evt_separator),
+ evt_separator,
+ sizeof(evt_separator));
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr,
+ uint32_t length)
+{
+ uint32_t rc;
+ const char *string;
+
+ if (!tpm_is_working())
+ return TCGBIOS_GENERAL_ERROR;
+
+ if (length < 0x200)
+ return TCGBIOS_INVALID_INPUT_PARA;
+
+ string = "Booting BCV device 00h (Floppy)";
+ if (bootdrv == BCV_DEVICE_HDD)
+ string = "Booting BCV device 80h (HDD)";
+
+ rc = tpm_add_action(4, string);
+ if (rc)
+ return rc;
+
+ /*
+ * equivalent to: dd if=/dev/hda ibs=1 count=440 | sha256sum
+ */
+ string = "MBR";
+ rc = tpm_add_measurement_to_log(4, EV_IPL,
+ string, strlen(string),
+ addr, 0x1b8);
+ if (rc)
+ return rc;
+
+ /*
+ * equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha256sum
+ */
+ string = "MBR PARTITION TABLE";
+ return tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA,
+ string, strlen(string),
+ addr + 0x1b8, 0x48);
+}
+
+/*
+ * This is the first function to call when measuring a GPT table.
+ * It allocates memory for the data to log which are 'measured' later on.
+ */
+void tpm_gpt_set_lba1(const uint8_t *addr, uint32_t length)
+{
+ if (!tpm_is_working())
+ return;
+
+ SLOF_free_mem(uefi_gpt_data, uefi_gpt_data_size);
+
+ uefi_gpt_data_size = sizeof(UEFI_GPT_DATA);
+ uefi_gpt_data = SLOF_alloc_mem(uefi_gpt_data_size);
+ if (!uefi_gpt_data)
+ return;
+
+ memcpy(&uefi_gpt_data->EfiPartitionHeader,
+ addr, MIN(sizeof(uefi_gpt_data->EfiPartitionHeader), length));
+ uefi_gpt_data->NumberOfPartitions = 0;
+}
+
+/*
+ * This function adds a GPT entry to the data to measure. It must
+ * be called after tpm_gpt_set_lba1.
+ */
+void tpm_gpt_add_entry(const uint8_t *addr, uint32_t length)
+{
+ size_t sz;
+ UEFI_PARTITION_ENTRY *upe = (void *)addr;
+ void *tmp;
+
+ if (!tpm_is_working() ||
+ !uefi_gpt_data ||
+ length < sizeof(*upe) ||
+ !memcmp(upe->partTypeGuid, ZeroGuid, sizeof(ZeroGuid)))
+ return;
+
+ sz = offset_of(UEFI_GPT_DATA, Partitions) +
+ (uefi_gpt_data->NumberOfPartitions + 1)
+ * sizeof(UEFI_PARTITION_ENTRY);
+ if (sz > uefi_gpt_data_size) {
+ tmp = SLOF_alloc_mem(sz);
+ if (!tmp)
+ goto err_no_mem;
+
+ memcpy(tmp, uefi_gpt_data, uefi_gpt_data_size);
+ SLOF_free_mem(uefi_gpt_data, uefi_gpt_data_size);
+ uefi_gpt_data = tmp;
+ uefi_gpt_data_size = sz;
+ }
+
+ memcpy(&uefi_gpt_data->Partitions[uefi_gpt_data->NumberOfPartitions],
+ addr,
+ sizeof(UEFI_PARTITION_ENTRY));
+ uefi_gpt_data->NumberOfPartitions++;
+
+ return;
+
+err_no_mem:
+ SLOF_free_mem(uefi_gpt_data, uefi_gpt_data_size);
+ uefi_gpt_data_size = 0;
+ uefi_gpt_data = NULL;
+}
+
+/*
+ * tpm_measure_gpt finally measures the GPT table and adds an entry
+ * to the log.
+ */
+uint32_t tpm_measure_gpt(void)
+{
+ size_t sz;
+
+ if (!tpm_is_working())
+ return TCGBIOS_GENERAL_ERROR;
+
+ sz = offset_of(UEFI_GPT_DATA, Partitions) +
+ uefi_gpt_data->NumberOfPartitions * sizeof(UEFI_PARTITION_ENTRY);
+
+ return tpm_add_measurement_to_log(5, EV_EFI_GPT_EVENT,
+ (const char *)uefi_gpt_data, sz,
+ (const uint8_t *)uefi_gpt_data, sz);
+}
+
+uint32_t tpm_measure_scrtm(void)
+{
+ uint32_t rc, i;
+ char *slof_text_start = (char *)&_slof_text;
+ uint32_t slof_text_length = (long)&_slof_text_end - (long)&_slof_text;
+ const char *scrtm = "S-CRTM Contents";
+#define _TT(a, x) a##x
+#define _T(a, x) _TT(a, x)
+ unsigned short ucs2_version[] = _T(L, RELEASE);
+
+ dprintf("Measure S-CRTM Version: addr = %p, length = %d\n",
+ ucs2_version, ucs2_length);
+
+ for (i = 0; i < ARRAY_SIZE(ucs2_version); ++i)
+ ucs2_version[i] = cpu_to_le16(ucs2_version[i]);
+
+ rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION,
+ (char *)ucs2_version,
+ sizeof(ucs2_version),
+ (uint8_t *)ucs2_version,
+ sizeof(ucs2_version));
+ if (rc)
+ return rc;
+
+ dprintf("Measure S-CRTM Content (text): start = %p, length = %d\n",
+ slof_text_start, slof_text_length);
+
+ rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS,
+ scrtm, strlen(scrtm),
+ (uint8_t *)slof_text_start,
+ slof_text_length);
+
+ return rc;
+}
+
+/*
+ * tpm_driver_get_failure_reason: Function for interfacing with the firmware
+ * API
+ */
+uint32_t tpm_driver_get_failure_reason(void)
+{
+ /* do not check for a working TPM here */
+ if (!tpm_state.tpm_found)
+ return VTPM_DRV_STATE_INVALID;
+
+ return spapr_vtpm_get_error();
+}
+
+/*
+ * tpm_driver_set_failure_reason: Function for interfacing with the firmware
+ * API
+ */
+void tpm_driver_set_failure_reason(uint32_t errcode)
+{
+ if (!tpm_state.tpm_found)
+ return;
+
+ spapr_vtpm_set_error(errcode);
+}
+
+/****************************************************************
+ * TPM Configuration Menu
+ ****************************************************************/
+
+static int
+tpm20_get_suppt_pcrbanks(uint8_t *suppt_pcrbanks, uint8_t *active_pcrbanks)
+{
+ struct tpms_pcr_selection *sel;
+ void *end;
+
+ *suppt_pcrbanks = 0;
+ *active_pcrbanks = 0;
+
+ sel = tpm_state.tpm20_pcr_selection->selections;
+ end = (void*)tpm_state.tpm20_pcr_selection +
+ tpm_state.tpm20_pcr_selection_size;
+
+ while (1) {
+ uint16_t hashalg;
+ uint8_t hashalg_flag;
+ unsigned i;
+ uint8_t sizeOfSelect = sel->sizeOfSelect;
+ void *nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+
+ if (nsel > end)
+ return 0;
+
+ hashalg = be16_to_cpu(sel->hashAlg);
+ hashalg_flag = tpm20_hashalg_to_flag(hashalg);
+
+ *suppt_pcrbanks |= hashalg_flag;
+
+ for (i = 0; i < sizeOfSelect; i++) {
+ if (sel->pcrSelect[i]) {
+ *active_pcrbanks |= hashalg_flag;
+ break;
+ }
+ }
+
+ sel = nsel;
+ }
+}
+
+static int
+tpm20_set_pcrbanks(uint32_t active_banks)
+{
+ struct tpm2_req_pcr_allocate trpa = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Allocate),
+ .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+ .authblocksize = cpu_to_be32(sizeof(trpa.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ };
+ struct tpms_pcr_selection3 {
+ uint16_t hashAlg;
+ uint8_t sizeOfSelect;
+ uint8_t pcrSelect[3];
+ } tps[ARRAY_SIZE(trpa.tpms_pcr_selections)];
+ int i = 0;
+ uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG;
+ uint8_t dontcare, suppt_banks;
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ uint16_t hashalg;
+ int ret;
+
+ tpm20_get_suppt_pcrbanks(&suppt_banks, &dontcare);
+
+ while (hashalg_flag) {
+ if ((hashalg_flag & suppt_banks)) {
+ hashalg = tpm20_hashalg_flag_to_hashalg(hashalg_flag);
+
+ if (hashalg) {
+ uint8_t mask = 0;
+
+ tps[i].hashAlg = cpu_to_be16(hashalg);
+ tps[i].sizeOfSelect = 3;
+
+ if (active_banks & hashalg_flag)
+ mask = 0xff;
+
+ tps[i].pcrSelect[0] = mask;
+ tps[i].pcrSelect[1] = mask;
+ tps[i].pcrSelect[2] = mask;
+ i++;
+ }
+ }
+ hashalg_flag <<= 1;
+ }
+
+ trpa.count = cpu_to_be32(i);
+ memcpy(trpa.tpms_pcr_selections, tps, i * sizeof(tps[0]));
+ trpa.hdr.totlen = cpu_to_be32(offset_of(struct tpm2_req_pcr_allocate,
+ tpms_pcr_selections) +
+ i * sizeof(tps[0]));
+
+ ret = spapr_transmit(0, &trpa.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_SHORT);
+ ret = ret ? -1 : (int) be32_to_cpu(rsp.errcode);
+
+ return ret;
+}
+
+static int tpm20_activate_pcrbanks(uint32_t active_banks)
+{
+ int ret;
+
+ ret = tpm20_set_pcrbanks(active_banks);
+ if (!ret)
+ ret = tpm_simple_cmd(0, TPM2_CC_Shutdown,
+ 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
+ if (!ret)
+ SLOF_reset();
+ return ret;
+}
+
+static int
+tpm20_clearcontrol(uint8_t disable)
+{
+ struct tpm2_req_clearcontrol trc = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trc)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_ClearControl),
+ .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+ .authblocksize = cpu_to_be32(sizeof(trc.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ .disable = disable,
+ };
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret;
+
+ ret = spapr_transmit(0, &trc.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_SHORT);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ ret = -1;
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_ClearControl = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int
+tpm20_clear(void)
+{
+ struct tpm2_req_clear trq = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trq)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_Clear),
+ .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+ .authblocksize = cpu_to_be32(sizeof(trq.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ };
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret;
+
+ ret = spapr_transmit(0, &trq.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_MEDIUM);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ ret = -1;
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_Clear = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int tpm20_menu_change_active_pcrbanks(void)
+{
+ uint8_t active_banks, suppt_banks, activate_banks;
+
+ tpm20_get_suppt_pcrbanks(&suppt_banks, &active_banks);
+
+ activate_banks = active_banks;
+
+ while (1) {
+ uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG;
+ uint8_t i = 0;
+ uint8_t flagnum;
+ int show = 0;
+
+ printf("\nToggle active PCR banks by pressing number key\n\n");
+
+ while (hashalg_flag) {
+ uint8_t flag = hashalg_flag & suppt_banks;
+ const char *hashname = tpm20_hashalg_flag_to_name(flag);
+
+ i++;
+ if (hashname) {
+ printf(" %d: %s", i, hashname);
+ if (activate_banks & hashalg_flag)
+ printf(" (enabled)");
+ printf("\n");
+ }
+
+ hashalg_flag <<= 1;
+ }
+ printf("\n"
+ "ESC: return to previous menu without changes\n");
+ if (activate_banks)
+ printf("a : activate selection\n");
+
+ while (!show) {
+ int key_code = SLOF_get_keystroke();
+
+ switch (key_code) {
+ case ~0:
+ continue;
+ case 27: /* ESC */
+ printf("\n");
+ return -1;
+ case '1' ... '5': /* keys 1 .. 5 */
+ flagnum = key_code - '0';
+ if (flagnum > i)
+ continue;
+ if (suppt_banks & (1 << (flagnum - 1))) {
+ activate_banks ^= 1 << (flagnum - 1);
+ show = 1;
+ }
+ break;
+ case 'a': /* a */
+ if (activate_banks)
+ tpm20_activate_pcrbanks(activate_banks);
+ }
+ }
+ }
+}
+
+void tpm20_menu(void)
+{
+ int key_code;
+ int waitkey;
+ int ret;
+
+ for (;;) {
+ printf("1. Clear TPM\n");
+ printf("2. Change active PCR banks\n");
+
+ printf("\nIf not change is desired or if this menu was reached by "
+ "mistake, press ESC to\ncontinue the boot.\n");
+
+ waitkey = 1;
+
+ while (waitkey) {
+ key_code = SLOF_get_keystroke();
+ switch (key_code) {
+ case 27:
+ // ESC
+ return;
+ case '1':
+ ret = tpm20_clearcontrol(false);
+ if (!ret)
+ ret = tpm20_clear();
+ if (ret)
+ printf("An error occurred clearing "
+ "the TPM: 0x%x\n",
+ ret);
+ break;
+ case '2':
+ tpm20_menu_change_active_pcrbanks();
+ waitkey = 0;
+ continue;
+ default:
+ continue;
+ }
+
+ waitkey = 0;
+ }
+ }
+}
diff --git a/roms/SLOF/lib/libtpm/tcgbios.h b/roms/SLOF/lib/libtpm/tcgbios.h
new file mode 100644
index 000000000..021e21932
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tcgbios.h
@@ -0,0 +1,45 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef TCGBIOS_H
+#define TCGBIOS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+uint32_t tpm_start(void);
+void tpm_finalize(void);
+uint32_t tpm_leave_firmware(void);
+uint32_t tpm_measure_scrtm(void);
+void tpm_set_log_parameters(void *address, size_t size);
+uint32_t tpm_get_logsize(void);
+uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr,
+ uint32_t length);
+uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr);
+uint32_t tpm_driver_get_failure_reason(void);
+void tpm_driver_set_failure_reason(uint32_t errcode);
+bool tpm_is_working(void);
+void tpm20_menu(void);
+void tpm_gpt_set_lba1(const uint8_t *addr, uint32_t length);
+void tpm_gpt_add_entry(const uint8_t *addr, uint32_t length);
+uint32_t tpm_measure_gpt(void);
+uint32_t tpm_hash_log_extend_event_buffer(uint32_t pcrindex,
+ uint32_t eventtype,
+ const void *data, uint64_t datalen,
+ const char *desc, uint32_t desclen,
+ bool is_elf);
+uint32_t tpm_2hash_ext_log(uint32_t pcrindex,
+ uint32_t eventtype,
+ const char *info, uint32_t infolen,
+ const void *data, uint64_t datalen);
+
+#endif /* TCGBIOS_H */
diff --git a/roms/SLOF/lib/libtpm/tcgbios_int.h b/roms/SLOF/lib/libtpm/tcgbios_int.h
new file mode 100644
index 000000000..cc3845585
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tcgbios_int.h
@@ -0,0 +1,317 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef TCGBIOS_INT_H
+#define TCGBIOS_INT_H
+
+#include <stdint.h>
+
+/* internal error codes */
+#define TCGBIOS_OK 0x0
+#define TCGBIOS_LOGOVERFLOW 0x1
+#define TCGBIOS_GENERAL_ERROR 0x2
+#define TCGBIOS_FIRMWARE_ERROR 0x3
+#define TCGBIOS_FATAL_COM_ERROR 0x4
+#define TCGBIOS_INVALID_INPUT_PARA 0x5
+#define TCGBIOS_COMMAND_ERROR 0x6
+#define TCGBIOS_INTERFACE_SHUTDOWN 0x7
+
+/*
+ * event types from spec:
+ * TCG PC Client Specific Implementation Specification
+ * for Conventional BIOS
+ */
+#define EV_POST_CODE 1
+#define EV_NO_ACTION 3
+#define EV_SEPARATOR 4
+#define EV_ACTION 5
+#define EV_EVENT_TAG 6
+#define EV_S_CRTM_CONTENTS 7
+#define EV_S_CRTM_VERSION 8
+#define EV_IPL 13
+#define EV_IPL_PARTITION_DATA 14
+#define EV_EFI_EVENT_BASE 0x80000000
+#define EV_EFI_GPT_EVENT (EV_EFI_EVENT_BASE + 0x6)
+
+#define BCV_DEVICE_HDD 0x80
+
+/* hash sizes */
+#define SHA1_BUFSIZE 20
+#define SHA256_BUFSIZE 32
+#define SHA384_BUFSIZE 48
+#define SHA512_BUFSIZE 64
+#define SM3_256_BUFSIZE 32
+#define SHA3_256_BUFSIZE 32
+#define SHA3_384_BUFSIZE 48
+#define SHA3_512_BUFSIZE 64
+
+/*
+ * Logging for TPM 2 is specified in TCG spec "TCG PC Client Platform
+ * Firmware Profile Specification" in section "Event Logging" and sub-
+ * section "TCG_PCR_EVENT2 structure"
+ *
+ * Each entry in the TPM log contains: a TCG_PCR_EVENT2_Header, a variable
+ * length digest, a TCG_PCR_EVENT2_Trailer, and a variable length event.
+ * The 'digest' matches what is sent to the TPM hardware via the Extend
+ * command. On TPM2.0 the digest contains a TPML_DIGEST_VALUES struct
+ * followed by a variable number of TPMT_HA structs (as specified by the
+ * hardware via the TPM2_CAP_PCRS request).
+ */
+typedef struct tdTCG_PCR_EVENT2_Header {
+ uint32_t pcrindex;
+ uint32_t eventtype;
+ uint8_t digests[0];
+} __attribute__((packed)) TCG_PCR_EVENT2_Header;
+
+typedef struct tdTCG_PCR_EVENT2_Trailer {
+ uint32_t eventdatasize;
+ uint8_t event[0];
+} __attribute__((packed)) TCG_PCR_EVENT2_Trailer;
+
+struct TCG_EfiSpecIdEventStruct {
+ uint8_t signature[16];
+ uint32_t platformClass;
+#define TPM_TCPA_ACPI_CLASS_CLIENT 0
+ uint8_t specVersionMinor;
+ uint8_t specVersionMajor;
+ uint8_t specErrata;
+ uint8_t uintnSize;
+ uint32_t numberOfAlgorithms;
+ struct TCG_EfiSpecIdEventAlgorithmSize {
+ uint16_t algorithmId;
+ uint16_t digestSize;
+ } digestSizes[];
+ /*
+ uint8_t vendorInfoSize;
+ uint8_t vendorInfo[0];
+ */
+} __attribute__((packed));
+
+/* EFI related data structures for logging */
+typedef struct {
+ uint64_t signature;
+ uint32_t revision;
+ uint32_t size;
+ uint32_t crc32;
+ uint8_t reserved[4];
+} __attribute__((packed)) UEFI_TABLE_HEADER;
+
+typedef struct {
+ UEFI_TABLE_HEADER header;
+ uint64_t currentLba;
+ uint64_t backupLba;
+ uint64_t firstLba;
+ uint64_t lastLba;
+ uint8_t diskGuid[16];
+ uint64_t partEntryLba;
+ uint32_t numPartEntry;
+ uint32_t partEntrySize;
+ uint32_t partArrayCrc32;
+ uint8_t reserved[420];
+} __attribute__((packed)) UEFI_PARTITION_TABLE_HEADER;
+
+typedef struct {
+ uint8_t partTypeGuid[16];
+ uint8_t partGuid[16];
+ uint64_t firstLba;
+ uint64_t lastLba;
+ uint64_t attribute;
+ uint8_t partName[72];
+} __attribute__((packed)) UEFI_PARTITION_ENTRY;
+
+typedef struct {
+ UEFI_PARTITION_TABLE_HEADER EfiPartitionHeader;
+ uint64_t NumberOfPartitions;
+ UEFI_PARTITION_ENTRY Partitions[0];
+} __attribute__((packed)) UEFI_GPT_DATA;
+
+/* Input and Output headers for all TPM commands */
+struct tpm_req_header {
+ uint16_t tag;
+ uint32_t totlen;
+ uint32_t ordinal;
+} __attribute__((packed));
+
+struct tpm_rsp_header {
+ uint16_t tag;
+ uint32_t totlen;
+ uint32_t errcode;
+} __attribute__((packed));
+
+/****************************************************************
+ * TPM v2.0 hardware commands
+ *
+ * Relevant specs for #defines and commonly used structures:
+ * - Trusted Platform Module Library; Part 2: Structures
+ * Relevant specs for command structures:
+ * - Trusted Platform Module Library; Part 3: Commands
+ ****************************************************************/
+
+#define TPM2_NO 0
+#define TPM2_YES 1
+
+#define TPM2_SU_CLEAR 0x0000
+#define TPM2_SU_STATE 0x0001
+
+#define TPM2_RH_OWNER 0x40000001
+#define TPM2_RS_PW 0x40000009
+#define TPM2_RH_ENDORSEMENT 0x4000000b
+#define TPM2_RH_PLATFORM 0x4000000c
+
+#define TPM2_ALG_SHA1 0x0004
+#define TPM2_ALG_SHA256 0x000b
+#define TPM2_ALG_SHA384 0x000c
+#define TPM2_ALG_SHA512 0x000d
+#define TPM2_ALG_SM3_256 0x0012
+#define TPM2_ALG_SHA3_256 0x0027
+#define TPM2_ALG_SHA3_384 0x0028
+#define TPM2_ALG_SHA3_512 0x0029
+
+/* TPM 2 command tags */
+#define TPM2_ST_NO_SESSIONS 0x8001
+#define TPM2_ST_SESSIONS 0x8002
+
+/* TPM 2 commands */
+#define TPM2_CC_HierarchyControl 0x121
+#define TPM2_CC_Clear 0x126
+#define TPM2_CC_ClearControl 0x127
+#define TPM2_CC_HierarchyChangeAuth 0x129
+#define TPM2_CC_PCR_Allocate 0x12b
+#define TPM2_CC_SelfTest 0x143
+#define TPM2_CC_Startup 0x144
+#define TPM2_CC_Shutdown 0x145
+#define TPM2_CC_StirRandom 0x146
+#define TPM2_CC_GetCapability 0x17a
+#define TPM2_CC_GetRandom 0x17b
+#define TPM2_CC_PCR_Extend 0x182
+
+/* TPM 2 Capabilities */
+#define TPM2_CAP_PCRS 0x00000005
+
+/* TPM 2 data structures */
+
+struct TPMT_HA {
+ uint16_t hashAlg;
+ uint8_t hash[0]; /* size depends on hashAlg */
+} __attribute__((packed));
+
+struct TPML_DIGEST_VALUES {
+ uint32_t count;
+ struct TPMT_HA digest[0]; /* variable number of entries */
+} __attribute__((packed));
+
+struct tpm2_req_stirrandom {
+ struct tpm_req_header hdr;
+ uint16_t size;
+ uint64_t stir;
+} __attribute__((packed));
+
+struct tpm2_req_getrandom {
+ struct tpm_req_header hdr;
+ uint16_t bytesRequested;
+} __attribute__((packed));
+
+struct tpm2b_20 {
+ uint16_t size;
+ uint8_t buffer[20];
+} __attribute__((packed));
+
+struct tpm2_res_getrandom {
+ struct tpm_rsp_header hdr;
+ struct tpm2b_20 rnd;
+} __attribute__((packed));
+
+/*
+ * tpm2_authblock is used in TPM 2 commands using 'Auth. Handle'
+ */
+struct tpm2_authblock {
+ uint32_t handle;
+ uint16_t noncesize; /* always 0 */
+ uint8_t contsession; /* always TPM2_YES */
+ uint16_t pwdsize; /* always 0 */
+} __attribute__((packed));
+
+struct tpm2_req_hierarchychangeauth {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ struct tpm2b_20 newAuth;
+} __attribute__((packed));
+
+struct tpm2_req_extend {
+ struct tpm_req_header hdr;
+ uint32_t pcrindex;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint8_t digest[0];
+} __attribute__((packed));
+
+struct tpm2_req_clearcontrol {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint8_t disable;
+} __attribute__((packed));
+
+struct tpm2_req_clear {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+} __attribute__((packed));
+
+struct tpm2_req_hierarchycontrol {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint32_t enable;
+ uint8_t state;
+} __attribute__((packed));
+
+struct tpm2_req_getcapability {
+ struct tpm_req_header hdr;
+ uint32_t capability;
+ uint32_t property;
+ uint32_t propertycount;
+} __attribute__((packed));
+
+struct tpm2_res_getcapability {
+ struct tpm_rsp_header hdr;
+ uint8_t moreData;
+ uint32_t capability;
+ uint8_t data[0]; /* capability dependent data */
+} __attribute__((packed));
+
+struct tpm2_req_pcr_allocate {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint32_t count;
+ uint8_t tpms_pcr_selections[4];
+} __attribute__((packed));
+
+struct tpms_pcr_selection {
+ uint16_t hashAlg;
+ uint8_t sizeOfSelect;
+ uint8_t pcrSelect[0];
+} __attribute__((packed));
+
+struct tpml_pcr_selection {
+ uint32_t count;
+ struct tpms_pcr_selection selections[0];
+} __attribute__((packed));
+
+#endif /* TCGBIOS_INT_H */
diff --git a/roms/SLOF/lib/libtpm/test.sh b/roms/SLOF/lib/libtpm/test.sh
new file mode 100755
index 000000000..4b0567a1d
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/test.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+cd $(dirname "$0")
+
+CC=${HOSTCC:-gcc}
+CFLAGS="-Wall -Wextra -Werror -I../../include -I../../slof -I../../lib/libc/include -DMAIN"
+LDFLAGS="-lcrypto"
+
+function fail() {
+ rm -f ${EXEC}
+ echo "Test failed"
+ exit 1
+}
+
+function run_test() {
+ local msg="$1"
+ local src="$2"
+
+ EXEC="./${src%%.c}-test"
+
+ echo ${msg}
+ ${CC} ${CFLAGS} ${src} -o ${EXEC} ${LDFLAGS} || exit 1
+ ${EXEC} || fail
+ rm -f ${EXEC}
+}
+
+run_test "SHA-1 test:" sha.c
+run_test "SHA-256 test:" sha256.c
+run_test "SHA-384 & SHA-512 test:" sha512.c
+
+echo "All tests passed"
+exit 0
diff --git a/roms/SLOF/lib/libtpm/tpm.code b/roms/SLOF/lib/libtpm/tpm.code
new file mode 100644
index 000000000..f5e1d3930
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tpm.code
@@ -0,0 +1,208 @@
+/******************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * libtpm bindings for SLOF - implementation
+ */
+
+#include <tcgbios.h>
+#include <stdbool.h>
+
+/************************************************/
+/* Startup TPM code */
+/* SLOF: tpm-start ( -- errcode ) */
+/* LIBTPM: tpm_start(void) */
+/************************************************/
+PRIM(tpm_X2d_start)
+ PUSH;
+ TOS.n = tpm_start();
+MIRP
+
+/************************************************/
+/* Shutdown TPM layer before OS takes over */
+/* SLOF: tpm-finalize ( -- ) */
+/* LIBTPM: tpm_finalize(void) */
+/************************************************/
+PRIM(tpm_X2d_finalize)
+ tpm_finalize();
+MIRP
+
+/***************************************************************/
+/* Prepare TPM state for bootloader */
+/* SLOF: tpm-leave-firwmare ( -- errcode ) */
+/* LIBTPM: tpm_leave_firmware(void) */
+/***************************************************************/
+PRIM(tpm_X2d_leave_X2d_firmware)
+ PUSH;
+ TOS.n = tpm_leave_firmware();
+MIRP
+
+/*************************************************************/
+/* Convey log address and size */
+/* SLOF: tpm-set-log-parameters ( addr size -- ) */
+/* LIBTPM: tpm_set_log_parameters(void *addr, uint64_t size) */
+/*************************************************************/
+PRIM(tpm_X2d_set_X2d_log_X2d_parameters)
+ int size = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ tpm_set_log_parameters(addr, size);
+MIRP
+
+/*********************************************************/
+/* Firmware API */
+/* SLOF: tpm-driver-get_failure-reason ( -- errcode) */
+/* LIBTPM: errcode = tpm_driver_get_failure_reason(void) */
+/*********************************************************/
+PRIM(tpm_X2d_driver_X2d_get_X2d_failure_X2d_reason)
+ PUSH;
+ TOS.n = tpm_driver_get_failure_reason();
+MIRP
+
+/********************************************************/
+/* Firmware API */
+/* SLOF: tpm-driver-set-failure_reason ( errcode -- ) */
+/* LIBTPM: tpm_driver_set_failure_reason(errcode) */
+/********************************************************/
+PRIM(tpm_X2d_driver_X2d_set_X2d_failure_X2d_reason)
+ int errcode = TOS.u; POP;
+ tpm_driver_set_failure_reason(errcode);
+MIRP
+
+/************************************************/
+/* Get the size of the log */
+/* SLOF: tpm-get-logsize ( -- size ) */
+/* LIBTPM: logsize = tpm_get_logsize(void) */
+/************************************************/
+PRIM(tpm_X2d_get_X2d_logsize)
+ PUSH;
+ TOS.n = tpm_get_logsize();
+MIRP
+
+/**********************************************************************/
+/* Measure and log event separators */
+/* SLOF: tpm-add-event-separators ( start-pcr end-pcr -- errcode) */
+/* LIBTPM: errcode = tpm_add_event_separators(start_pcr, end_pcr) */
+/**********************************************************************/
+PRIM(tpm_X2d_add_X2d_event_X2d_separators)
+ int end_pcr = TOS.u; POP;
+ int start_pcr = TOS.u;
+ TOS.n = tpm_add_event_separators(start_pcr, end_pcr);
+MIRP
+
+/*************************************************************************/
+/* Measure and log boot connect vector (bcv) device's master boot record */
+/* SLOF: tpm-measure-bcv-mbr ( bootdrv addr length -- errcode ) */
+/* LIBTPM: errcode = tpm_measure_bcv_mbr(bbotdrv, addr, length) */
+/*************************************************************************/
+PRIM(tpm_X2d_measure_X2d_bcv_X2d_mbr)
+ int length = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ int bootdrv = TOS.u;
+ TOS.n = tpm_measure_bcv_mbr(bootdrv, addr, length);
+MIRP
+
+/************************************************/
+/* Check whether the TPM is working */
+/* SLOF: tpm-is-working ( -- true | false ) */
+/* LIBTPM: bool = tpm_is_working() */
+/************************************************/
+PRIM(tpm_X2d_is_X2d_working)
+ PUSH;
+ TOS.n = tpm_is_working();
+MIRP
+
+/************************************************/
+/* Have the S-CRTM measured */
+/* SLOF: tpm-measure-scrtm ( -- errcode ) */
+/* LIBTPM: errcode = tpm_measure_scrtm */
+/************************************************/
+PRIM(tpm_X2d_measure_X2d_scrtm)
+ PUSH;
+ TOS.n = tpm_measure_scrtm();
+MIRP
+
+/*******************************************************************/
+/* Firmware API */
+/* SLOF: tpm20-menu ( -- tpm-version ) */
+/* LIBTPM: tpm20_menu() */
+/*******************************************************************/
+PRIM(tpm20_X2d_menu)
+ tpm20_menu();
+MIRP
+
+/*************************************************************************/
+/* Set the LBA1 of the GPT */
+/* SLOF: tpm-gpt-set-lba1 ( addr length -- ) */
+/* LIBTPM: tpm_gpt_set_lba1(addr, length) */
+/*************************************************************************/
+PRIM(tpm_X2d_gpt_X2d_set_X2d_lba1)
+ int length = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ tpm_gpt_set_lba1(addr, length);
+MIRP
+
+/*************************************************************************/
+/* Add a GPT table entry */
+/* SLOF: tpm-gpt-add-entry ( addr length -- ) */
+/* LIBTPM: tpm_gpt_add_entry(addr, length) */
+/*************************************************************************/
+PRIM(tpm_X2d_gpt_X2d_add_X2d_entry)
+ int length = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ tpm_gpt_add_entry(addr, length);
+MIRP
+
+/*************************************************************************/
+/* Measure and log GPT EVENT */
+/* SLOF: tpm-measure-gpt ( -- errcode ) */
+/* LIBTPM: errcode = tpm_measure_gpt() */
+/*************************************************************************/
+PRIM(tpm_X2d_measure_X2d_gpt)
+ PUSH;
+ TOS.n = tpm_measure_gpt();
+MIRP
+
+/***********************************************************************************************************/
+/* Firmware API */
+/* SLOF: tpm-hash-log-extend-event-buffer ( pcr evt data-ptr data-len desc-ptr desclen is_elf -- errcode ) */
+/* LIBTPM: errcode = tpm-hash-log-extend-event-buffer */
+/***********************************************************************************************************/
+PRIM(tpm_X2d_hash_X2d_log_X2d_extend_X2d_event_X2d_buffer)
+ uint32_t is_elf = TOS.u; POP;
+ uint32_t desclen = TOS.u; POP;
+ const char *desc = TOS.a; POP;
+ uint64_t datalen = TOS.u; POP;
+ const void *data = TOS.a; POP;
+ uint32_t eventtype = TOS.u; POP;
+ uint32_t pcrindex = TOS.u;
+
+ TOS.n = tpm_hash_log_extend_event_buffer(pcrindex, eventtype,
+ data, datalen,
+ desc, desclen, is_elf);
+MIRP
+
+/****************************************************************************************/
+/* Firmware API */
+/* SLOF: tpm-2hash-ext-log ( pcr event-type info info-len data data-len -- success? ) */
+/* LIBTPM: success = tpm-2hash-ext-log */
+/****************************************************************************************/
+PRIM(tpm_X2d_2hash_X2d_ext_X2d_log)
+ uint32_t datalen = TOS.u; POP;
+ const void *data = TOS.a; POP;
+ uint64_t infolen = TOS.u; POP;
+ const char *info = TOS.a; POP;
+ uint32_t eventtype = TOS.u; POP;
+ uint32_t pcrindex = TOS.u;
+
+ TOS.u = tpm_2hash_ext_log(pcrindex, eventtype,
+ info, infolen,
+ data, datalen);
+MIRP
diff --git a/roms/SLOF/lib/libtpm/tpm.in b/roms/SLOF/lib/libtpm/tpm.in
new file mode 100644
index 000000000..2f8062473
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tpm.in
@@ -0,0 +1,32 @@
+/******************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * libtpm bindings for SLOF - definitions
+ */
+
+cod(tpm-start)
+cod(tpm-finalize)
+cod(tpm-leave-firmware)
+cod(tpm-set-log-parameters)
+cod(tpm-get-logsize)
+cod(tpm-add-event-separators)
+cod(tpm-measure-bcv-mbr)
+cod(tpm-is-working)
+cod(tpm-measure-scrtm)
+cod(tpm-driver-get-failure-reason)
+cod(tpm-driver-set-failure-reason)
+cod(tpm20-menu)
+cod(tpm-gpt-set-lba1)
+cod(tpm-gpt-add-entry)
+cod(tpm-measure-gpt)
+cod(tpm-hash-log-extend-event-buffer)
+cod(tpm-2hash-ext-log)
diff --git a/roms/SLOF/lib/libtpm/tpm_drivers.c b/roms/SLOF/lib/libtpm/tpm_drivers.c
new file mode 100644
index 000000000..4a4fde89a
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tpm_drivers.c
@@ -0,0 +1,436 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "string.h"
+#include "helpers.h"
+#include "byteorder.h"
+#include "tcgbios_int.h"
+#include "tpm_drivers.h"
+#include "libhvcall.h"
+#include "paflof.h"
+
+#undef PAPR_VTPM_DEBUG
+//#define PAPR_VTPM_DEBUG
+#ifdef PAPR_VTPM_DEBUG
+#define dprintf(_x ...) do { printf("VTPM CRQ: " _x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+/* layout of the command request queue for vTPM; all fields are big endian */
+struct crq {
+ uint8_t valid;
+ uint8_t msg;
+ uint16_t len;
+ uint32_t data;
+ uint64_t reserved;
+} __attribute__((packed));
+
+#define PAPR_VTPM_INIT_CRQ_COMMAND 0xC0
+#define PAPR_VTPM_VALID_COMMAND 0x80
+#define PAPR_VTPM_MSG_RESULT 0x80
+
+/* crq.msg request types when crq.valid = PAPR_VTPM_INIT_CRQ_COMMAND */
+#define PAPR_VTPM_INIT_CRQ_RESULT 0x1
+
+/* crq.msg request types when crq.valid = PAPR_VTPM_VALID_COMMAND */
+#define PAPR_VTPM_GET_VERSION 0x1
+#define PAPR_VTPM_TPM_COMMAND 0x2
+#define PAPR_VTPM_GET_RTCE_BUFFER_SIZE 0x3
+
+#define TPM2_DEFAULT_DURATION_SHORT 750000 /* us */
+#define TPM2_DEFAULT_DURATION_MEDIUM 2000000 /* us */
+#define TPM2_DEFAULT_DURATION_LONG 2000000 /* us */
+
+static const uint32_t tpm2_durations[3] = {
+ TPM2_DEFAULT_DURATION_SHORT,
+ TPM2_DEFAULT_DURATION_MEDIUM,
+ TPM2_DEFAULT_DURATION_LONG,
+};
+
+#define QUEUE_SIZE 4096
+
+/* state of the PAPR CRQ VTPM driver */
+static struct {
+ /* whether it driver been initialized */
+ bool initialized;
+
+ /* unit number */
+ unsigned long unit;
+
+ /* CRQ queue address and size */
+ unsigned char *qaddr;
+ unsigned long qsize;
+
+ /* current q_entry */
+ unsigned int curr_q_entry;
+
+ /* current response CRQ */
+ struct crq *response;
+
+ /* power firmware defined state and error code */
+ vtpm_drv_state driver_state;
+ vtpm_drv_error driver_error;
+
+ /* size of buffer supported by hypervisor */
+ unsigned int buffer_size;
+
+ /* buffer for commands and responses */
+ char *buffer;
+} spapr_vtpm = {
+ .qsize = QUEUE_SIZE,
+ .driver_state = VTPM_DRV_STATE_INVALID,
+ .driver_error = VTPM_DRV_ERROR_NO_FAILURE,
+};
+
+static void vtpm_drv_state_set(vtpm_drv_state s, vtpm_drv_error e)
+{
+ spapr_vtpm.driver_state = s;
+ spapr_vtpm.driver_error = e;
+}
+
+static vtpm_drv_error vtpm_drv_error_get(void)
+{
+ return spapr_vtpm.driver_error;
+}
+
+static struct crq *spapr_get_crq(void *qaddr, unsigned long q_entry)
+{
+ return &((struct crq *)qaddr)[q_entry];
+}
+
+/*
+ * Get the crq where the response will be found. This
+ * function will clear the CRQ's valid field and advance
+ * the entry counter to the next entry.
+ */
+static struct crq *spapr_get_response_crq(void)
+{
+ struct crq *crq;
+
+ dprintf("curr_q_entry = %d\n", spapr_vtpm.curr_q_entry);
+
+ crq = spapr_get_crq(spapr_vtpm.qaddr, spapr_vtpm.curr_q_entry);
+ memset(crq, 0, sizeof(*crq));
+
+ spapr_vtpm.curr_q_entry += 1;
+ if (spapr_vtpm.curr_q_entry == (spapr_vtpm.qsize / sizeof(struct crq)))
+ spapr_vtpm.curr_q_entry = 0;
+
+ return crq;
+}
+
+/*
+ * Send a message via CRQ and wait for the response
+ */
+static bool spapr_send_crq_and_wait(unsigned long unit,
+ struct crq *crq,
+ struct crq **response,
+ unsigned timeout,
+ vtpm_drv_state state1,
+ vtpm_drv_state state2)
+{
+ long rc;
+ unsigned i;
+
+ *response = spapr_get_response_crq();
+
+ vtpm_drv_state_set(state1, VTPM_DRV_ERROR_NO_FAILURE);
+
+ rc = hv_send_crq(unit, (uint64_t *)&crq->valid);
+ if (rc != H_SUCCESS) {
+ vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
+ VTPM_DRV_ERROR_TPM_CRQ_ERROR);
+ return false;
+ }
+
+ vtpm_drv_state_set(state2, VTPM_DRV_ERROR_NO_FAILURE);
+
+ for (i = 0; i < timeout; i += 1000) {
+ if (((*response)->valid & PAPR_VTPM_MSG_RESULT))
+ return true;
+ SLOF_usleep(1000);
+ }
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
+ VTPM_DRV_ERROR_WAIT_TIMEOUT);
+
+ dprintf("Received no response from CRQ\n");
+ return false;
+}
+
+/*
+ * Get parameters from the CRQ
+ */
+static bool spapr_vtpm_get_params(void)
+{
+ struct crq crq, *response;
+ static bool completed = false; /* only once */
+
+ if (completed)
+ return true;
+
+ /* get the TPM's buffer size */
+ crq.valid = PAPR_VTPM_VALID_COMMAND;
+ crq.msg = PAPR_VTPM_GET_RTCE_BUFFER_SIZE;
+
+ if (!spapr_send_crq_and_wait(spapr_vtpm.unit, &crq, &response, 10,
+ VTPM_DRV_STATE_SEND_BUFSIZE_REQ,
+ VTPM_DRV_STATE_WAIT_BUFSIZE)) {
+ printf("%s: Failure getting RTCE buffer size from CRQ\n",
+ __func__);
+ return false;
+ }
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_ALLOC_RTCE_BUF,
+ VTPM_DRV_ERROR_NO_FAILURE);
+
+ dprintf("RTCE buffer size: %u\n", be16_to_cpu(response->len));
+ spapr_vtpm.buffer_size = be16_to_cpu(response->len);
+ if (spapr_vtpm.buffer_size < 1024) {
+ printf("%s: RTCE buffer size of %u bytes is too small. "
+ "Minimum is 1024 bytes.\n", __func__,
+ spapr_vtpm.buffer_size);
+ vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
+ VTPM_DRV_ERROR_BAD_RTCE_SIZE);
+ return false;
+ }
+ spapr_vtpm.buffer = SLOF_alloc_mem(spapr_vtpm.buffer_size);
+ if (!spapr_vtpm.buffer) {
+ printf("%s: Could not allocate buffer of size %u.\n",
+ __func__, spapr_vtpm.buffer_size);
+ vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
+ VTPM_DRV_ERROR_BAD_RTCE_SIZE);
+ return false;
+ }
+
+ completed = true;
+
+ return true;
+}
+
+static bool spapr_vtpm_activate(void)
+{
+ long rc;
+ struct crq crq, *response;
+
+ if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
+ printf("%s: CRQ: In failure mode\n", __func__);
+ return false;
+ }
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_REG_CRQ,
+ VTPM_DRV_ERROR_NO_FAILURE);
+
+ rc = hv_reg_crq(spapr_vtpm.unit, (unsigned long)spapr_vtpm.qaddr,
+ spapr_vtpm.qsize);
+ if (rc != H_SUCCESS) {
+ vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
+ VTPM_DRV_ERROR_UNEXPECTED_REG_ERROR);
+ printf("%s: CRQ registration failed\n", __func__);
+ return false;
+ }
+
+ /* we always start with curr_q_entry 0 */
+ spapr_vtpm.curr_q_entry = 0;
+
+ if (!spapr_vtpm.initialized) {
+
+ crq.valid = PAPR_VTPM_INIT_CRQ_COMMAND;
+ crq.msg = PAPR_VTPM_INIT_CRQ_RESULT;
+
+ if (!spapr_send_crq_and_wait(spapr_vtpm.unit,
+ &crq,
+ &response,
+ 10,
+ VTPM_DRV_STATE_SEND_INIT,
+ VTPM_DRV_STATE_WAIT_INIT_COMP)) {
+ printf("%s: Initializing CRQ failed\n", __func__);
+ goto err_exit;
+ }
+ dprintf("Successfully initialized CRQ\n");
+
+ spapr_vtpm.initialized = true;
+ }
+
+ if (spapr_vtpm_get_params())
+ return true;
+
+err_exit:
+ hv_free_crq(spapr_vtpm.unit);
+ spapr_vtpm.unit = 0;
+
+ return false;
+}
+
+void spapr_vtpm_finalize(void)
+{
+ if (spapr_vtpm.unit) {
+ hv_free_crq(spapr_vtpm.unit);
+ spapr_vtpm.unit = 0;
+ }
+}
+
+/*
+ * Check whether we have a CRQ underneath us; if we do, the CRQ will
+ * be left open.
+ */
+static bool spapr_vtpm_probe(void)
+{
+ if (!spapr_vtpm.qaddr) {
+ spapr_vtpm.qaddr = SLOF_alloc_mem(spapr_vtpm.qsize);
+ if (!spapr_vtpm.qaddr) {
+ printf("%s: Unable to allocate memory\n", __func__);
+ return false;
+ }
+ memset(spapr_vtpm.qaddr, 0, spapr_vtpm.qsize);
+
+ dprintf("getting FORTH vtpm-unit\n");
+ spapr_vtpm.unit = SLOF_get_vtpm_unit();
+ if (!spapr_vtpm.unit) {
+ printf("%s: Could not get valid vtpm-unit\n", __func__);
+ return false;
+ }
+ }
+
+ dprintf("vtpm_unit = %lx, buffer = %p\n",
+ spapr_vtpm.unit, spapr_vtpm.qaddr);
+
+ if (!spapr_vtpm_activate())
+ return false;
+
+ return true;
+}
+
+static bool spapr_vtpm_senddata(const uint8_t *const data, uint32_t len)
+{
+ struct crq crq;
+ long rc;
+
+ if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
+ printf("%s: VTPM CRQ: In failure mode\n", __func__);
+ return false;
+ }
+
+ if (len > spapr_vtpm.buffer_size) {
+ printf("%s: VTPM CRQ: Send buffer too large: %u > %u\n",
+ __func__, len, spapr_vtpm.buffer_size);
+ return false;
+ }
+
+ spapr_vtpm.response = spapr_get_response_crq();
+ spapr_vtpm.response->data = (uint64_t)spapr_vtpm.buffer;
+
+ crq.valid = PAPR_VTPM_VALID_COMMAND;
+ crq.msg = PAPR_VTPM_TPM_COMMAND;
+ crq.len = cpu_to_be16(len);
+ crq.data = (uint64_t)spapr_vtpm.buffer;
+ memcpy(spapr_vtpm.buffer, data, MIN(len, spapr_vtpm.buffer_size));
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_SEND_TPM_CMD,
+ VTPM_DRV_ERROR_NO_FAILURE);
+
+ rc = hv_send_crq(spapr_vtpm.unit, (uint64_t *)&crq.valid);
+
+ if (rc == H_SUCCESS)
+ vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_TPM_RSP,
+ VTPM_DRV_ERROR_NO_FAILURE);
+ else
+ vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
+ VTPM_DRV_ERROR_UNEXPECTED_SEND_ERROR);
+
+ return (rc == H_SUCCESS);
+}
+
+static bool spapr_vtpm_waitresponseready(enum tpm_duration_type to_t)
+{
+ uint32_t i, timeout = tpm2_durations[to_t];
+
+ if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
+ printf("%s: VTPM CRQ: In failure mode\n", __func__);
+ return false;
+ }
+
+ for (i = 0; i < timeout; i += 1000) {
+ if (spapr_vtpm.response->valid & PAPR_VTPM_MSG_RESULT) {
+ /* TPM responded: move to Send tpm-cmd state */
+ vtpm_drv_state_set(VTPM_DRV_STATE_SEND_TPM_CMD,
+ VTPM_DRV_ERROR_NO_FAILURE);
+ dprintf("Received response to TPM command\n");
+ return true;
+ }
+ SLOF_usleep(1000);
+ }
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
+ VTPM_DRV_ERROR_WAIT_TIMEOUT);
+
+ dprintf("Received NO response to TPM command");
+
+ return false;
+}
+
+static bool spapr_vtpm_readresponse(uint8_t *buffer, uint32_t *len)
+{
+ uint32_t length;
+
+ if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
+ printf("%s: VTPM CRQ: In failure mode\n", __func__);
+ return false;
+ }
+
+ length = MIN(*len, be32_to_cpu(spapr_vtpm.response->len));
+
+ memcpy(buffer, (void *)(uint64_t)spapr_vtpm.response->data, length);
+
+ dprintf("Length of copied response: %d\n", length);
+
+ spapr_vtpm.response = NULL;
+ *len = length;
+
+ return true;
+}
+
+/**** higher layer interface ****/
+
+vtpm_drv_error spapr_vtpm_get_error(void)
+{
+ return vtpm_drv_error_get();
+}
+
+void spapr_vtpm_set_error(vtpm_drv_error errcode)
+{
+ spapr_vtpm.driver_error = errcode;
+}
+
+bool spapr_is_vtpm_present(void)
+{
+ return spapr_vtpm_probe();
+}
+
+int spapr_transmit(uint8_t locty, struct tpm_req_header *req,
+ void *respbuffer, uint32_t *respbufferlen,
+ enum tpm_duration_type to_t)
+{
+ if (locty)
+ return -1;
+ if (!spapr_vtpm_senddata((uint8_t *)req, be32_to_cpu(req->totlen)) ||
+ !spapr_vtpm_waitresponseready(to_t) ||
+ !spapr_vtpm_readresponse(respbuffer, respbufferlen) ||
+ *respbufferlen < sizeof(struct tpm_rsp_header))
+ return -1;
+ return 0;
+}
diff --git a/roms/SLOF/lib/libtpm/tpm_drivers.h b/roms/SLOF/lib/libtpm/tpm_drivers.h
new file mode 100644
index 000000000..b5d347f49
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tpm_drivers.h
@@ -0,0 +1,82 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef TPM_DRIVERS_H
+#define TPM_DRIVERS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "tcgbios_int.h"
+
+enum tpm_duration_type {
+ TPM_DURATION_TYPE_SHORT = 0,
+ TPM_DURATION_TYPE_MEDIUM,
+ TPM_DURATION_TYPE_LONG,
+};
+
+/* firmware driver states */
+typedef enum {
+ VTPM_DRV_STATE_INVALID = 0,
+ VTPM_DRV_STATE_INIT_CALLED = 1,
+ VTPM_DRV_STATE_REG_CRQ = 2,
+ VTPM_DRV_STATE_WAIT_INIT = 3,
+ VTPM_DRV_STATE_SEND_INIT = 4,
+ VTPM_DRV_STATE_FAILURE = 5,
+ VTPM_DRV_STATE_WAIT_INIT_COMP = 6,
+ VTPM_DRV_STATE_SEND_INIT_COMP = 7,
+ VTPM_DRV_STATE_SEND_GET_VERSION = 8,
+ VTPM_DRV_STATE_WAIT_VERSION = 9,
+ VTPM_DRV_STATE_CHECK_VERSION = 10,
+ VTPM_DRV_STATE_SEND_BUFSIZE_REQ = 11,
+ VTPM_DRV_STATE_WAIT_BUFSIZE = 12,
+ VTPM_DRV_STATE_ALLOC_RTCE_BUF = 13,
+ VTPM_DRV_STATE_SEND_TPM_CMD = 14,
+ VTPM_DRV_STATE_WAIT_TPM_RSP = 15,
+} vtpm_drv_state;
+
+/* firmware driver errors */
+typedef enum {
+ VTPM_DRV_ERROR_NO_FAILURE = -1,
+ VTPM_DRV_ERROR_NOT_FOUND_TIMEOUT = 0,
+ VTPM_DRV_ERROR_UNEXPECTED_REG_ERROR = 1,
+ VTPM_DRV_ERROR_PARTNER_FAILED = 2,
+ VTPM_DRV_ERROR_UNEXPECTED_TSP_ERROR = 3,
+ VTPM_DRV_ERROR_TPM_PROTOCOL_ERROR = 4,
+ VTPM_DRV_ERROR_WAIT_TIMEOUT = 5,
+ VTPM_DRV_ERROR_UNEXPECTED_SEND_ERROR = 6,
+ VTPM_DRV_ERROR_CRQ_OPEN_FAIL = 7,
+ VTPM_DRV_ERROR_BAD_STATE = 8,
+ VTPM_DRV_ERROR_TPM_FAIL = 9,
+ VTPM_DRV_ERROR_TPM_CRQ_ERROR = 10,
+ VTPM_DRV_ERROR_BAD_VERSION = 11,
+ VTPM_DRV_ERROR_BAD_RTCE_SIZE = 12,
+ VTPM_DRV_ERROR_SML_FAILURE = 13,
+ VTPM_DRV_ERROR_SML_HANDED_OVER = 14,
+} vtpm_drv_error;
+
+/* the max. buffer size by the external TPM is 4k */
+#define PAPR_VTPM_MAX_BUFFER_SIZE 4096
+
+/* exported functions */
+bool spapr_is_vtpm_present(void);
+void spapr_vtpm_finalize(void);
+vtpm_drv_error spapr_vtpm_get_error(void);
+void spapr_vtpm_set_error(vtpm_drv_error errcode);
+
+struct tpm_req_header;
+int spapr_transmit(uint8_t locty, struct tpm_req_header *req,
+ void *respbuffer, uint32_t *respbufferlen,
+ enum tpm_duration_type to_t);
+
+#endif /* TPM_DRIVERS_H */
diff --git a/roms/SLOF/lib/libusb/Makefile b/roms/SLOF/lib/libusb/Makefile
new file mode 100644
index 000000000..0780489ea
--- /dev/null
+++ b/roms/SLOF/lib/libusb/Makefile
@@ -0,0 +1,52 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I$(SLOFCMNDIR)
+LDFLAGS = -nostdlib
+
+TARGET = ../libusb.a
+
+
+all: $(TARGET)
+
+SRCS = usb-core.c usb-ohci.c usb-ehci.c usb-slof.c usb-key.c usb-hid.c \
+ usb-hub.c usb-xhci.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libusb/tools.h b/roms/SLOF/lib/libusb/tools.h
new file mode 100644
index 000000000..f531175c1
--- /dev/null
+++ b/roms/SLOF/lib/libusb/tools.h
@@ -0,0 +1,77 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef __TOOLS_H
+#define __TOOLS_H
+
+#include <stdint.h>
+#include <byteorder.h>
+#include <cache.h>
+
+#define PTR_U32(x) ((uint32_t) (uint64_t) (x))
+
+static inline uint32_t read_reg32(uint32_t *reg)
+{
+ return bswap_32(ci_read_32(reg));
+}
+
+static inline void write_reg32(uint32_t *reg, uint32_t value)
+{
+ mb();
+ ci_write_32(reg, bswap_32(value));
+}
+
+static inline uint8_t read_reg8(uint8_t *reg)
+{
+ return ci_read_8(reg);
+}
+
+static inline void write_reg8(uint8_t *reg, uint8_t value)
+{
+ mb();
+ ci_write_8(reg, value);
+}
+
+static inline uint16_t read_reg16(uint16_t *reg)
+{
+ return bswap_16(ci_read_16(reg));
+}
+
+static inline void write_reg16(uint16_t *reg, uint16_t value)
+{
+ mb();
+ ci_write_16(reg, bswap_16(value));
+}
+
+static inline uint64_t read_reg64(uint64_t *reg)
+{
+ return bswap_64(ci_read_64(reg));
+}
+
+static inline void write_reg64(uint64_t *reg, uint64_t value)
+{
+ mb();
+ ci_write_64(reg, bswap_64(value));
+}
+
+static inline uint32_t ci_read_reg(uint32_t *reg)
+{
+ return bswap_32(ci_read_32(reg));
+}
+
+static inline void ci_write_reg(uint32_t *reg, uint32_t value)
+{
+ mb();
+ ci_write_32(reg, bswap_32(value));
+}
+
+#endif
diff --git a/roms/SLOF/lib/libusb/usb-core.c b/roms/SLOF/lib/libusb/usb-core.c
new file mode 100644
index 000000000..6ad8028d0
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-core.c
@@ -0,0 +1,559 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include "usb-core.h"
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...) do {} while (0)
+#endif
+
+#define __unused __attribute__((unused))
+
+struct usb_hcd_ops *head;
+struct usb_dev *devpool;
+#define USB_DEVPOOL_SIZE 4096
+
+static struct usb_dev *usb_alloc_devpool(void)
+{
+ struct usb_dev *head, *curr, *prev;
+ unsigned int dev_count = 0, i;
+
+ head = SLOF_alloc_mem(USB_DEVPOOL_SIZE);
+ if (!head)
+ return NULL;
+
+ dev_count = USB_DEVPOOL_SIZE/sizeof(struct usb_dev);
+ dprintf("%s: %d number of devices\n", __func__, dev_count);
+ /* Although an array, link them*/
+ for (i = 0, curr = head, prev = NULL; i < dev_count; i++, curr++) {
+ if (prev)
+ prev->next = curr;
+ curr->next = NULL;
+ prev = curr;
+ }
+
+#ifdef DEBUG
+ for (i = 0, curr = head; curr; curr = curr->next)
+ printf("%s: %d dev %p\n", __func__, i++, curr);
+#endif
+
+ return head;
+}
+
+struct usb_dev *usb_devpool_get(void)
+{
+ struct usb_dev *new;
+
+ if (!devpool) {
+ devpool = usb_alloc_devpool();
+ if (!devpool)
+ return NULL;
+ }
+
+ new = devpool;
+ devpool = devpool->next;
+ memset(new, 0, sizeof(*new));
+ new->next = NULL;
+ return new;
+}
+
+void usb_devpool_put(struct usb_dev *dev)
+{
+ struct usb_dev *curr;
+ if (!dev && !devpool)
+ return;
+
+ curr = devpool;
+ while (curr->next)
+ curr = curr->next;
+ curr->next = dev;
+ dev->next = NULL;
+}
+
+#ifndef DEBUG
+#define validate_hcd_ops(dev) (dev && dev->hcidev && dev->hcidev->ops)
+#else
+int validate_hcd_ops(struct usb_dev *dev)
+{
+ int ret = true;
+
+ if (!dev) {
+ printf("dev is NULL\n");
+ ret = false;
+ } else if (!dev->hcidev) {
+ printf("hcidev is NULL\n");
+ ret = false;
+ } else if (!dev->hcidev->ops) {
+ printf("ops is NULL\n");
+ ret = false;
+ }
+ return ret;
+}
+#endif
+
+struct usb_pipe *usb_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
+ char *buf, size_t len)
+{
+ if (validate_hcd_ops(dev) && dev->hcidev->ops->get_pipe)
+ return dev->hcidev->ops->get_pipe(dev, ep, buf, len);
+ else {
+ printf("%s: Failed\n", __func__);
+ return NULL;
+ }
+}
+
+void usb_put_pipe(struct usb_pipe *pipe)
+{
+ struct usb_dev *dev = NULL;
+ if (pipe && pipe->dev) {
+ dev = pipe->dev;
+ if (validate_hcd_ops(dev) && dev->hcidev->ops->put_pipe)
+ dev->hcidev->ops->put_pipe(pipe);
+ }
+}
+
+int usb_poll_intr(struct usb_pipe *pipe, uint8_t *buf)
+{
+ struct usb_dev *dev = NULL;
+ if (pipe && pipe->dev) {
+ dev = pipe->dev;
+ if (validate_hcd_ops(dev) && dev->hcidev->ops->poll_intr)
+ return dev->hcidev->ops->poll_intr(pipe, buf);
+ }
+ return 0;
+}
+
+void usb_hcd_register(struct usb_hcd_ops *ops)
+{
+ struct usb_hcd_ops *list;
+
+ if (!ops)
+ printf("Error");
+ dprintf("Registering %s %d\n", ops->name, ops->usb_type);
+
+ if (head) {
+ list = head;
+ while (list->next)
+ list = list->next;
+ list->next = ops;
+ } else
+ head = ops;
+}
+
+void usb_hcd_init(void *hcidev)
+{
+ struct usb_hcd_dev *dev = hcidev;
+ struct usb_hcd_ops *list = head;
+
+ if (!dev) {
+ printf("Device Error");
+ return;
+ }
+
+ while (list) {
+ if (list->usb_type == dev->type) {
+ dprintf("usb_ops(%p) for the controller found\n", list);
+ dev->ops = list;
+ dev->ops->init(dev);
+ return;
+ }
+ list = list->next;
+ }
+
+ dprintf("usb_ops for the controller not found\n");
+}
+
+void usb_hcd_exit(void *_hcidev)
+{
+ struct usb_hcd_dev *hcidev = _hcidev;
+
+ dprintf("%s: enter \n", __func__);
+ if (!hcidev) {
+ printf("Device Error");
+ return;
+ }
+
+ if (hcidev->ops->exit)
+ hcidev->ops->exit(hcidev);
+}
+
+int usb_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data)
+{
+ struct usb_dev *dev = NULL;
+ if (!pipe)
+ return false;
+ dev = pipe->dev;
+ if (validate_hcd_ops(dev) && dev->hcidev->ops->send_ctrl)
+ return dev->hcidev->ops->send_ctrl(pipe, req, data);
+ else {
+ printf("%s: Failed\n", __func__);
+ return false;
+ }
+}
+
+int usb_transfer_ctrl(void *dev, void *req, void *data)
+{
+ struct usb_pipe *pipe = NULL;
+ struct usb_dev *usbdev;
+
+ if (!dev)
+ return false;
+ usbdev = (struct usb_dev *)dev;
+ pipe = usbdev->control;
+ return usb_send_ctrl(pipe, req, data);
+}
+
+int usb_transfer_bulk(void *dev, int dir, void *td, void *td_phys, void *data, int size)
+{
+ struct usb_pipe *pipe = NULL;
+ struct usb_dev *usbdev;
+
+ if (!dev)
+ return false;
+ usbdev = (struct usb_dev *)dev;
+ pipe = (dir == USB_PIPE_OUT) ? usbdev->bulk_out : usbdev->bulk_in;
+ if (!pipe)
+ return false;
+ if (validate_hcd_ops(usbdev) && usbdev->hcidev->ops->transfer_bulk)
+ return usbdev->hcidev->ops->transfer_bulk(pipe, td, td_phys, data, size);
+ else {
+ printf("%s: Failed\n", __func__);
+ return false;
+ }
+}
+
+/*
+ * USB Specification 1.1
+ * 9.3 USB Device Requests
+ * 9.4 Standard Device Requests
+ */
+static int usb_set_address(struct usb_dev *dev, uint32_t port)
+{
+ struct usb_dev_req req;
+ struct usb_hcd_dev *hcidev;
+
+ if (!dev)
+ return false;
+
+ hcidev = dev->hcidev;
+ req.bmRequestType = 0;
+ req.bRequest = REQ_SET_ADDRESS;
+ req.wIndex = 0;
+ req.wLength = 0;
+ req.wValue = cpu_to_le16((uint16_t)(hcidev->nextaddr));
+ if (usb_send_ctrl(dev->control, &req, NULL)) {
+ dev->addr = hcidev->nextaddr++;
+ return true;
+ } else
+ return false;
+}
+
+static int usb_get_device_descr(struct usb_dev *dev, void *data, size_t size)
+{
+ struct usb_dev_req req;
+
+ if (!dev)
+ return false;
+
+ req.bmRequestType = 0x80;
+ req.bRequest = REQ_GET_DESCRIPTOR;
+ req.wIndex = 0;
+ req.wLength = cpu_to_le16((uint16_t) size);
+ req.wValue = cpu_to_le16(DESCR_TYPE_DEVICE << 8);
+ return usb_send_ctrl(dev->control, &req, data);
+}
+
+static int usb_get_config_descr(struct usb_dev *dev, void *data, size_t size)
+{
+ struct usb_dev_req req;
+
+ if (!dev)
+ return false;
+
+ req.bmRequestType = 0x80;
+ req.bRequest = REQ_GET_DESCRIPTOR;
+ req.wIndex = 0;
+ req.wLength = cpu_to_le16((uint16_t) size);
+ req.wValue = cpu_to_le16(DESCR_TYPE_CONFIGURATION << 8);
+ return usb_send_ctrl(dev->control, &req, data);
+
+}
+
+static int usb_set_config(struct usb_dev *dev, uint8_t cfg_value)
+{
+ struct usb_dev_req req;
+
+ if (!dev)
+ return false;
+
+ req.bmRequestType = 0x00;
+ req.bRequest = REQ_SET_CONFIGURATION;
+ req.wIndex = 0;
+ req.wLength = 0;
+ req.wValue = cpu_to_le16(0x00FF & cfg_value);
+ return usb_send_ctrl(dev->control, &req, NULL);
+}
+
+static int usb_clear_halt(struct usb_pipe *pipe)
+{
+ struct usb_dev_req req;
+ struct usb_dev *dev;
+
+ if (pipe && pipe->dev) {
+ dev = pipe->dev;
+ dprintf("Clearing port %d dir %d type %d\n",
+ pipe->epno, pipe->dir, pipe->type);
+ req.bmRequestType = REQT_DIR_OUT | REQT_REC_EP;
+ req.bRequest = REQ_CLEAR_FEATURE;
+ req.wValue = FEATURE_ENDPOINT_HALT;
+ req.wIndex = cpu_to_le16(pipe->epno | pipe->dir);
+ req.wLength = 0;
+ return usb_send_ctrl(dev->control, &req, NULL);
+ }
+ return false;
+}
+
+int usb_dev_populate_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
+ void *buf, size_t len)
+{
+ uint8_t dir, type;
+
+ dir = (ep->bEndpointAddress & 0x80) >> 7;
+ type = ep->bmAttributes & USB_EP_TYPE_MASK;
+
+ dprintf("EP: %s: %d size %d type %d\n", dir ? "IN " : "OUT",
+ ep->bEndpointAddress & 0xF, le16_to_cpu(ep->wMaxPacketSize),
+ type);
+ if (type == USB_EP_TYPE_BULK) {
+ if (dir)
+ dev->bulk_in = usb_get_pipe(dev, ep, buf, len);
+ else
+ dev->bulk_out = usb_get_pipe(dev, ep, buf, len);
+ } else if (type == USB_EP_TYPE_INTR)
+ dev->intr = usb_get_pipe(dev, ep, buf, len);
+
+ return true;
+}
+
+static void usb_dev_copy_epdesc(struct usb_dev *dev, struct usb_ep_descr *ep)
+{
+ uint32_t ep_cnt;
+
+ ep_cnt = dev->ep_cnt;
+ if (ep_cnt < USB_DEV_EP_MAX)
+ memcpy((void *)&dev->ep[ep_cnt], ep, sizeof(*ep));
+ else
+ dprintf("usb-core: only %d EPs supported\n", USB_DEV_EP_MAX);
+ dev->ep_cnt++;
+}
+
+int usb_hid_init(void *vdev)
+{
+ struct usb_dev *dev;
+ dev = (struct usb_dev *) vdev;
+ if (!dev)
+ return false;
+ if (dev->class == DEV_HID_KEYB)
+ usb_hid_kbd_init(dev);
+ return true;
+}
+
+int usb_hid_exit(void *vdev)
+{
+ struct usb_dev *dev;
+ dev = (struct usb_dev *) vdev;
+ if (!dev)
+ return false;
+ if (dev->class == DEV_HID_KEYB)
+ usb_hid_kbd_exit(dev);
+ return true;
+}
+
+int usb_msc_init(void *vdev)
+{
+ struct usb_dev *dev;
+ unsigned i;
+
+ dev = (struct usb_dev *) vdev;
+ dprintf("%s: enter %x\n", __func__, dev->class);
+ if (!dev)
+ return false;
+ if (usb_get_intf_class(dev->class) == 8) {
+ for (i = 0; i < dev->ep_cnt; i++) {
+ if ((dev->ep[i].bmAttributes & USB_EP_TYPE_MASK)
+ == USB_EP_TYPE_BULK)
+ usb_dev_populate_pipe(dev, &dev->ep[i], NULL, 0);
+ }
+ }
+ return true;
+}
+
+int usb_msc_exit(void *vdev)
+{
+ struct usb_dev *dev;
+ dev = (struct usb_dev *) vdev;
+ dprintf("%s: enter %x\n", __func__, dev->class);
+ if (!dev)
+ return false;
+ if (usb_get_intf_class(dev->class) == 8) {
+ if (dev->bulk_in)
+ usb_put_pipe(dev->bulk_in);
+ if (dev->bulk_out)
+ usb_put_pipe(dev->bulk_out);
+ }
+ return true;
+}
+
+int usb_msc_reset(struct usb_dev *dev)
+{
+ struct usb_dev_req req;
+
+ if (!dev)
+ return false;
+
+ req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT;
+ req.bRequest = 0xFF;
+ req.wLength = 0;
+ req.wValue = 0;
+ req.wIndex = cpu_to_le16(dev->intf_num);
+ return usb_send_ctrl(dev->control, &req, NULL);
+}
+
+void usb_msc_resetrecovery(struct usb_dev *dev)
+{
+ // usb_msc_reset(dev);
+ usb_clear_halt(dev->bulk_in);
+ usb_clear_halt(dev->bulk_out);
+ SLOF_msleep(2);
+}
+
+static int usb_handle_device(struct usb_dev *dev, struct usb_dev_config_descr *cfg,
+ uint8_t *ptr, uint16_t len)
+{
+ struct usb_dev_intf_descr *intf = NULL;
+ struct usb_ep_descr *ep = NULL;
+ struct usb_dev_hid_descr *hid __unused = NULL;
+ uint8_t desc_len, desc_type;
+
+ len -= sizeof(struct usb_dev_config_descr);
+ ptr = (uint8_t *)(ptr + sizeof(struct usb_dev_config_descr));
+
+ while (len > 0) {
+ desc_len = *ptr;
+ desc_type = *(ptr + 1);
+ switch (desc_type) {
+ case DESCR_TYPE_INTERFACE:
+ intf = (struct usb_dev_intf_descr *)ptr;
+ dev->class = intf->bInterfaceClass << 16 |
+ intf->bInterfaceSubClass << 8 |
+ intf->bInterfaceProtocol;
+ break;
+ case DESCR_TYPE_ENDPOINT:
+ ep = (struct usb_ep_descr *)ptr;
+ dev->intf_num = intf->bInterfaceNumber;
+ usb_dev_copy_epdesc(dev, ep);
+ break;
+ case DESCR_TYPE_HID:
+ hid = (struct usb_dev_hid_descr *)ptr;
+ dprintf("hid-report %d size %d\n",
+ hid->bReportType, le16_to_cpu(hid->wReportLength));
+ break;
+ case DESCR_TYPE_HUB:
+ break;
+ default:
+ dprintf("ptr %p desc_type %d\n", ptr, desc_type);
+ }
+ ptr += desc_len;
+ len -= desc_len;
+ }
+ return true;
+}
+
+int usb_setup_new_device(struct usb_dev *dev, unsigned int port)
+{
+ struct usb_dev_descr descr;
+ struct usb_dev_config_descr cfg;
+ struct usb_ep_descr ep;
+ uint16_t len;
+ void *data = NULL;
+
+ dprintf("usb: %s - port %d\n", __func__, port);
+
+ dev->addr = 0;
+ dev->port = port;
+ ep.bEndpointAddress = 0;
+ ep.bmAttributes = USB_EP_TYPE_CONTROL;
+ ep.wMaxPacketSize = cpu_to_le16(8);
+ dev->control = usb_get_pipe(dev, &ep, NULL, 0);
+
+ if (!usb_get_device_descr(dev, &descr, 8))
+ goto fail;
+ dev->control->mps = descr.bMaxPacketSize0;
+
+ /*
+ * For USB3.0 ADDRESS-SLOT command takes care of setting
+ * address, skip this during generic device setup for USB3.0
+ * devices
+ */
+ if (dev->speed != USB_SUPER_SPEED) {
+ /*
+ * Qemu starts the port number from 1 which was
+ * revealed in bootindex and resulted in mismatch for
+ * storage devices names. Adjusting this here for
+ * compatibility.
+ */
+ dev->port = port + 1;
+ if(!usb_set_address(dev, dev->port))
+ goto fail;
+ }
+ mb();
+ SLOF_msleep(100);
+
+ if (!usb_get_device_descr(dev, &descr, sizeof(struct usb_dev_descr)))
+ goto fail;
+
+ if (!usb_get_config_descr(dev, &cfg, sizeof(struct usb_dev_config_descr)))
+ goto fail;
+
+ len = le16_to_cpu(cfg.wTotalLength);
+ /* No device config descriptor present */
+ if (len == sizeof(struct usb_dev_config_descr))
+ goto fail;
+
+ data = SLOF_dma_alloc(len);
+ if (!data) {
+ printf("%s: alloc failed %d\n", __func__, port);
+ goto fail;
+ }
+
+ if (!usb_get_config_descr(dev, data, len))
+ goto fail_mem_free;
+ if (!usb_set_config(dev, cfg.bConfigurationValue))
+ goto fail_mem_free;
+ mb();
+ SLOF_msleep(100);
+
+ if (!usb_handle_device(dev, &cfg, data, len))
+ goto fail_mem_free;
+
+ SLOF_dma_free(data, len);
+ return true;
+fail_mem_free:
+ SLOF_dma_free(data, len);
+fail:
+ return false;
+}
diff --git a/roms/SLOF/lib/libusb/usb-core.h b/roms/SLOF/lib/libusb/usb-core.h
new file mode 100644
index 000000000..d27107f46
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-core.h
@@ -0,0 +1,283 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef __USB_CORE_H
+#define __USB_CORE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include "helpers.h"
+#include "usb.h"
+#include "tools.h"
+
+enum usb_hcd_type {
+ USB_OHCI = 1,
+ USB_EHCI = 2,
+ USB_XHCI = 3,
+};
+
+struct usb_hcd_dev;
+
+struct usb_hcd_dev {
+ void *base;
+ long type;
+ long num;
+ struct usb_hcd_ops *ops;
+ void *priv; /* hcd owned structure */
+ long nextaddr; /* address for devices */
+};
+
+struct usb_pipe;
+
+/*******************************************/
+/* Standard Endpoint Descriptor */
+/*******************************************/
+/* bmAttributes */
+#define USB_EP_TYPE_MASK 0x03
+#define USB_EP_TYPE_CONTROL 0
+#define USB_EP_TYPE_ISOC 1
+#define USB_EP_TYPE_BULK 2
+#define USB_EP_TYPE_INTR 3
+
+struct usb_ep_descr {
+ uint8_t bLength; /* size of descriptor */
+ uint8_t bDescriptorType; /* Type = 5 */
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint16_t wMaxPacketSize;
+ uint8_t bInterval;
+} __attribute__((packed, aligned(4)));
+
+#define DEV_HID_KEYB 0x030101 /* class=HIB, protocol=Keyboard */
+#define DEV_HID_MOUSE 0x030102 /* class=HIB, protocol=Mouse */
+#define DEV_HUB 0x090000 /* class=HUB, subclass, protocol */
+#define DEV_MASS_RBC 0x080150 /* MassStorage, RBC, Bulk */
+#define DEV_CDROM_ATAPI 0x080250 /* MassStorage, SFF-8020i , Bulk */
+#define DEV_MASS_FLOPPY 0x080450 /* MassStorage, UFI, Bulk */
+#define DEV_MASS_ATAPI 0x080550 /* MassStorage, SFF-8070i , Bulk */
+#define DEV_MASS_SCSI 0x080650 /* MassStorage, SCSI, Bulk */
+
+enum USB_SPEED_TYPE {
+ USB_LOW_SPEED = 0,
+ USB_FULL_SPEED = 1,
+ USB_HIGH_SPEED = 2,
+ USB_SUPER_SPEED = 3,
+};
+
+/* Max number of endpoints supported in a device */
+#define USB_DEV_EP_MAX 4
+#define USB_TIMEOUT 5000 /* 5 sec usb timeout */
+
+struct usb_dev {
+ struct usb_dev *next;
+ struct usb_dev *hub;
+ struct usb_hcd_dev *hcidev;
+ struct usb_pipe *intr;
+ struct usb_pipe *control;
+ struct usb_pipe *bulk_in;
+ struct usb_pipe *bulk_out;
+ struct usb_ep_descr ep[USB_DEV_EP_MAX];
+ void *priv;
+ uint32_t ep_cnt;
+ uint32_t class;
+ uint32_t speed;
+ uint32_t addr;
+ uint32_t mps0;
+ uint32_t port;
+ uint16_t intf_num;
+};
+
+#define DEVICE_KEYBOARD 1
+#define DEVICE_MOUSE 2
+#define DEVICE_DISK 3
+#define DEVICE_HUB 4
+
+/* Structure in sync with FORTH code */
+struct slof_usb_dev {
+ void *udev;
+ uint32_t port;
+ uint32_t addr;
+ uint32_t hcitype;
+ uint32_t num;
+ uint32_t devtype;
+} __attribute__((packed));
+
+enum USB_PIPE_DIR {
+ USB_PIPE_OUT = 0,
+ USB_PIPE_IN,
+};
+
+struct usb_pipe {
+ struct usb_dev *dev;
+ struct usb_pipe *next;
+ uint32_t type;
+ uint32_t speed;
+ uint32_t dir;
+ uint16_t epno;
+ uint16_t mps;
+} __attribute__((packed));
+
+#define REQ_GET_STATUS 0 /* see Table 9-4 */
+#define REQ_CLEAR_FEATURE 1
+#define REQ_GET_STATE 2 /* HUB specific */
+#define REQ_SET_FEATURE 3
+#define REQ_SET_ADDRESS 5
+#define REQ_GET_DESCRIPTOR 6
+#define REQ_SET_DESCRIPTOR 7
+#define REQ_GET_CONFIGURATION 8
+#define REQ_SET_CONFIGURATION 9
+#define REQ_GET_INTERFACE 10
+#define REQ_SET_INTERFACE 11
+#define REQ_SYNCH_FRAME 12
+
+#define FEATURE_DEVICE_REMOTE_WAKEUP 1
+#define FEATURE_ENDPOINT_HALT 0
+
+#define REQT_REC_DEVICE 0
+#define REQT_REC_INTERFACE 1
+#define REQT_REC_EP 2
+#define REQT_REC_OTHER 3
+#define REQT_TYPE_STANDARD (0 << 5)
+#define REQT_TYPE_CLASS (1 << 5)
+#define REQT_TYPE_VENDOR (2 << 5)
+#define REQT_TYPE_RSRVD (3 << 5)
+#define REQT_DIR_OUT (0 << 7) /* host -> device */
+#define REQT_DIR_IN (1 << 7) /* device -> host */
+
+#define DESCR_TYPE_DEVICE 1 /* see Table 9-5 */
+#define DESCR_TYPE_CONFIGURATION 2
+#define DESCR_TYPE_STRING 3
+#define DESCR_TYPE_INTERFACE 4
+#define DESCR_TYPE_ENDPOINT 5
+#define DESCR_TYPE_HUB 0x29 /* Class Descriptor HUB */
+#define DESCR_TYPE_HID 0x21 /* Class Descriptor HID */
+#define DESCR_TYPE_REPORT 0x22 /* Class Descriptor HID */
+#define DESCR_TYPE_PHYSICAL 0x23 /* Class Descriptor HID */
+
+struct usb_dev_req {
+ uint8_t bmRequestType; /* direction, recipient */
+ uint8_t bRequest; /* see spec: Table 9-3 */
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength; /* number of bytes to transfer */
+} __attribute__((packed));
+
+/* Standard Device Descriptor (18 Bytes) */
+/*******************************************/
+struct usb_dev_descr {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdUSB;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+} __attribute__((packed));
+
+/*******************************************/
+/* Standard Configuration Descriptor */
+/*******************************************/
+struct usb_dev_config_descr {
+ uint8_t bLength; /* size of descriptor */
+ uint8_t bDescriptorType; /* Type = 2 */
+ uint16_t wTotalLength; /* total returned data */
+ uint8_t bNumInterfaces; /* interfaces supported by this config */
+ uint8_t bConfigurationValue; /* Configuration-ID for SetConfiguration */
+ uint8_t iConfiguration; /* index of string descriptor */
+ uint8_t bmAttributes; /* configuration characteristics */
+ uint8_t bMaxPower; /* in 2mA units */
+} __attribute__((packed));
+
+/*******************************************/
+/* Standard Interface Descriptor */
+/*******************************************/
+struct usb_dev_intf_descr {
+ uint8_t bLength; /* size of descriptor */
+ uint8_t bDescriptorType; /* Type = 4 */
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol; /* protocol code */
+ uint8_t iInterface; /* index to string descriptor */
+} __attribute__((packed));
+
+/*******************************************/
+/* HUB-Class Descriptor */
+/*******************************************/
+struct usb_dev_hub_descr {
+ uint8_t bLength; /* size of complete descriptor */
+ uint8_t bDescriptorType; /* type = 0x29 for HUB */
+ uint8_t bNbrPorts; /* number of downstream ports */
+ uint8_t wHubCharacteristics; /* mode bits 7..0 */
+ uint8_t reserved; /* mode bits 15..8 */
+ uint8_t bPwrOn2PwrGood; /* in 2ms units */
+ uint8_t bHubContrCurrent; /* current requirement in mA */
+ uint8_t DeviceTable; /* length depends on number of ports */
+} __attribute__((packed));
+
+/*******************************************/
+/* HID-Class Descriptor */
+/*******************************************/
+struct usb_dev_hid_descr {
+ uint8_t bLength; /* size of this descriptor */
+ uint8_t bDescriptorType; /* type = 0x21 for HID */
+ uint16_t bcdHID; /* Sample: 0x0102 for 2.01 */
+ uint8_t bCountryCode; /* Hardware target country */
+ uint8_t bNumDescriptors; /* Number of HID class descr. */
+ uint8_t bReportType; /* Report Descriptor Type */
+ uint16_t wReportLength; /* Total Length of Report Descr. */
+} __attribute__((packed));
+
+struct usb_hcd_ops {
+ const char *name;
+ void (*init)(struct usb_hcd_dev *);
+ void (*exit)(struct usb_hcd_dev *);
+ void (*detect)(void);
+ void (*disconnect)(void);
+ int (*send_ctrl)(struct usb_pipe *pipe, struct usb_dev_req *req, void *data);
+ struct usb_pipe* (*get_pipe)(struct usb_dev *dev, struct usb_ep_descr *ep,
+ char *buf, size_t len);
+ int (*transfer_bulk)(struct usb_pipe *pipe, void *td, void *td_phys, void *data, int size);
+ void (*put_pipe)(struct usb_pipe *);
+ int (*poll_intr)(struct usb_pipe *, uint8_t *);
+ struct usb_hcd_ops *next;
+ unsigned int usb_type;
+};
+
+#define usb_get_intf_class(x) ((x & 0x00FF0000) >> 16)
+
+extern void usb_hcd_register(struct usb_hcd_ops *ops);
+extern struct usb_pipe *usb_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
+ char *buf, size_t len);
+extern void usb_put_pipe(struct usb_pipe *pipe);
+extern int usb_poll_intr(struct usb_pipe *pipe, uint8_t *buf);
+extern int usb_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data);
+extern struct usb_dev *usb_devpool_get(void);
+extern void usb_devpool_put(struct usb_dev *);
+extern int usb_setup_new_device(struct usb_dev *dev, unsigned int port);
+extern void usb_slof_populate_new_device(struct usb_dev *dev);
+extern int usb_dev_populate_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
+ void *buf, size_t len);
+extern int usb_hid_kbd_init(struct usb_dev *dev);
+extern int usb_hid_kbd_exit(struct usb_dev *dev);
+extern int usb_msc_reset(struct usb_dev *dev);
+extern void usb_msc_resetrecovery(struct usb_dev *dev);
+#endif
diff --git a/roms/SLOF/lib/libusb/usb-ehci.c b/roms/SLOF/lib/libusb/usb-ehci.c
new file mode 100644
index 000000000..4b1b0a8de
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-ehci.c
@@ -0,0 +1,612 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include "usb.h"
+#include "usb-core.h"
+#include "usb-ehci.h"
+#include "tools.h"
+#include "paflof.h"
+
+#undef EHCI_DEBUG
+//#define EHCI_DEBUG
+#ifdef EHCI_DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...) do {} while (0)
+
+#endif
+
+#ifdef EHCI_DEBUG
+static void dump_ehci_regs(struct ehci_hcd *ehcd)
+{
+ struct ehci_cap_regs *cap_regs;
+ struct ehci_op_regs *op_regs;
+
+ cap_regs = ehcd->cap_regs;
+ op_regs = ehcd->op_regs;
+
+ dprintf("\n - CAPLENGTH %02X", read_reg8(&cap_regs->caplength));
+ dprintf("\n - HCIVERSION %04X", read_reg16(&cap_regs->hciversion));
+ dprintf("\n - HCSPARAMS %08X", read_reg32(&cap_regs->hcsparams));
+ dprintf("\n - HCCPARAMS %08X", read_reg32(&cap_regs->hccparams));
+ dprintf("\n - HCSP_PORTROUTE %016llX", read_reg64(&cap_regs->portroute));
+ dprintf("\n");
+
+ dprintf("\n - USBCMD %08X", read_reg32(&op_regs->usbcmd));
+ dprintf("\n - USBSTS %08X", read_reg32(&op_regs->usbsts));
+ dprintf("\n - USBINTR %08X", read_reg32(&op_regs->usbintr));
+ dprintf("\n - FRINDEX %08X", read_reg32(&op_regs->frindex));
+ dprintf("\n - CTRLDSSEGMENT %08X", read_reg32(&op_regs->ctrldssegment));
+ dprintf("\n - PERIODICLISTBASE %08X", read_reg32(&op_regs->periodiclistbase));
+ dprintf("\n - ASYNCLISTADDR %08X", read_reg32(&op_regs->asynclistaddr));
+ dprintf("\n - CONFIGFLAG %08X", read_reg32(&op_regs->configflag));
+ dprintf("\n - PORTSC %08X", read_reg32(&op_regs->portsc[0]));
+ dprintf("\n");
+}
+#endif
+
+static int ehci_hub_check_ports(struct ehci_hcd *ehcd)
+{
+ uint32_t num_ports, portsc, i;
+ struct usb_dev *dev;
+
+ dprintf("%s: enter\n", __func__);
+ num_ports = read_reg32(&ehcd->cap_regs->hcsparams) & HCS_NPORTS_MASK;
+ for (i = 0; i < num_ports; i++) {
+ dprintf("%s: device %d\n", __func__, i);
+ portsc = read_reg32(&ehcd->op_regs->portsc[i]);
+ if (portsc & PORT_CONNECT) { /* Device present */
+ dprintf("usb-ehci: Device present on port %d\n", i);
+ /* Reset the port */
+ portsc = read_reg32(&ehcd->op_regs->portsc[i]);
+ portsc = (portsc & ~PORT_PE) | PORT_RESET;
+ write_reg32(&ehcd->op_regs->portsc[i], portsc);
+ SLOF_msleep(20);
+ portsc = read_reg32(&ehcd->op_regs->portsc[i]);
+ portsc &= ~PORT_RESET;
+ write_reg32(&ehcd->op_regs->portsc[i], portsc);
+ SLOF_msleep(20);
+ dev = usb_devpool_get();
+ dprintf("usb-ehci: allocated device %p\n", dev);
+ dev->hcidev = ehcd->hcidev;
+ dev->speed = USB_HIGH_SPEED; /* TODO: Check for Low/Full speed device */
+ if (usb_setup_new_device(dev, i))
+ usb_slof_populate_new_device(dev);
+ else
+ printf("usb-ehci: unable to setup device on port %d\n", i);
+ }
+ }
+ dprintf("%s: exit\n", __func__);
+ return 0;
+}
+
+static int ehci_hcd_init(struct ehci_hcd *ehcd)
+{
+ uint32_t usbcmd;
+ uint32_t time;
+ struct ehci_framelist *fl;
+ struct ehci_qh *qh_intr, *qh_async;
+ int i;
+ long fl_phys = 0, qh_intr_phys = 0, qh_async_phys;
+
+ /* Reset the host controller */
+ time = SLOF_GetTimer() + 250;
+ usbcmd = read_reg32(&ehcd->op_regs->usbcmd);
+ write_reg32(&ehcd->op_regs->usbcmd, (usbcmd & ~(CMD_PSE | CMD_ASE)) | CMD_HCRESET);
+ while (time > SLOF_GetTimer())
+ cpu_relax();
+ usbcmd = read_reg32(&ehcd->op_regs->usbcmd);
+ if (usbcmd & CMD_HCRESET) {
+ printf("usb-ehci: reset failed\n");
+ return -1;
+ }
+
+ /* Initialize periodic list */
+ fl = SLOF_dma_alloc(sizeof(*fl));
+ if (!fl) {
+ printf("usb-ehci: Unable to allocate frame list\n");
+ goto fail;
+ }
+ fl_phys = SLOF_dma_map_in(fl, sizeof(*fl), true);
+ dprintf("fl %p, fl_phys %lx\n", fl, fl_phys);
+
+ /* TODO: allocate qh pool */
+ qh_intr = SLOF_dma_alloc(sizeof(*qh_intr));
+ if (!qh_intr) {
+ printf("usb-ehci: Unable to allocate interrupt queue head\n");
+ goto fail_qh_intr;
+ }
+ qh_intr_phys = SLOF_dma_map_in(qh_intr, sizeof(*qh_intr), true);
+ dprintf("qh_intr %p, qh_intr_phys %lx\n", qh_intr, qh_intr_phys);
+
+ memset(qh_intr, 0, sizeof(*qh_intr));
+ qh_intr->qh_ptr = QH_PTR_TERM;
+ qh_intr->ep_cap2 = cpu_to_le32(0x01 << QH_SMASK_SHIFT);
+ qh_intr->next_qtd = qh_intr->alt_next_qtd = QH_PTR_TERM;
+ qh_intr->token = cpu_to_le32(QH_STS_HALTED);
+ for (i = 0; i < FL_SIZE; i++)
+ fl->fl_ptr[i] = cpu_to_le32(qh_intr_phys | EHCI_TYP_QH);
+ write_reg32(&ehcd->op_regs->periodiclistbase, fl_phys);
+
+ /* Initialize async list */
+ qh_async = SLOF_dma_alloc(sizeof(*qh_async));
+ if (!qh_async) {
+ printf("usb-ehci: Unable to allocate async queue head\n");
+ goto fail_qh_async;
+ }
+ qh_async_phys = SLOF_dma_map_in(qh_async, sizeof(*qh_async), true);
+ dprintf("qh_async %p, qh_async_phys %lx\n", qh_async, qh_async_phys);
+
+ memset(qh_async, 0, sizeof(*qh_async));
+ qh_async->qh_ptr = cpu_to_le32(qh_async_phys | EHCI_TYP_QH);
+ qh_async->ep_cap1 = cpu_to_le32(QH_CAP_H);
+ qh_async->next_qtd = qh_async->alt_next_qtd = QH_PTR_TERM;
+ qh_async->token = cpu_to_le32(QH_STS_HALTED);
+ write_reg32(&ehcd->op_regs->asynclistaddr, qh_async_phys);
+ ehcd->qh_async = qh_async;
+ ehcd->qh_async_phys = qh_async_phys;
+ ehcd->qh_intr = qh_intr;
+ ehcd->qh_intr_phys = qh_intr_phys;
+ ehcd->fl = fl;
+ ehcd->fl_phys = fl_phys;
+
+ write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_ASE | CMD_RUN);
+ write_reg32(&ehcd->op_regs->configflag, 1);
+
+ return 0;
+
+fail_qh_async:
+ SLOF_dma_map_out(qh_intr_phys, qh_intr, sizeof(*qh_intr));
+ SLOF_dma_free(qh_intr, sizeof(*qh_intr));
+fail_qh_intr:
+ SLOF_dma_map_out(fl_phys, fl, sizeof(*fl));
+ SLOF_dma_free(fl, sizeof(*fl));
+fail:
+ return -1;
+}
+
+static int ehci_hcd_exit(struct ehci_hcd *ehcd)
+{
+ uint32_t usbcmd;
+
+ if (!ehcd) {
+ dprintf("NULL pointer\n");
+ return false;
+ }
+
+ usbcmd = read_reg32(&ehcd->op_regs->usbcmd);
+ write_reg32(&ehcd->op_regs->usbcmd, usbcmd | ~CMD_RUN);
+ write_reg32(&ehcd->op_regs->periodiclistbase, 0);
+
+ if (ehcd->pool) {
+ SLOF_dma_map_out(ehcd->pool_phys, ehcd->pool, EHCI_PIPE_POOL_SIZE);
+ SLOF_dma_free(ehcd->pool, EHCI_PIPE_POOL_SIZE);
+ }
+ if (ehcd->qh_intr) {
+ SLOF_dma_map_out(ehcd->qh_intr_phys, ehcd->qh_intr, sizeof(struct ehci_qh));
+ SLOF_dma_free(ehcd->qh_intr, sizeof(struct ehci_qh));
+ }
+ if (ehcd->qh_async) {
+ SLOF_dma_map_out(ehcd->qh_async_phys, ehcd->qh_async, sizeof(struct ehci_qh));
+ SLOF_dma_free(ehcd->qh_async, sizeof(struct ehci_qh));
+ }
+ if (ehcd->fl) {
+ SLOF_dma_map_out(ehcd->fl_phys, ehcd->fl, sizeof(struct ehci_framelist));
+ SLOF_dma_free(ehcd->fl, sizeof(struct ehci_framelist));
+ }
+ return true;
+}
+
+static int ehci_alloc_pipe_pool(struct ehci_hcd *ehcd)
+{
+ struct ehci_pipe *epipe, *curr, *prev;
+ unsigned int i, count;
+ long epipe_phys = 0;
+
+ count = EHCI_PIPE_POOL_SIZE/sizeof(*epipe);
+ ehcd->pool = epipe = SLOF_dma_alloc(EHCI_PIPE_POOL_SIZE);
+ if (!epipe)
+ return -1;
+ ehcd->pool_phys = epipe_phys = SLOF_dma_map_in(epipe, EHCI_PIPE_POOL_SIZE, true);
+ dprintf("%s: epipe %p, epipe_phys %lx\n", __func__, epipe, epipe_phys);
+
+ /* Although an array, link them */
+ for (i = 0, curr = epipe, prev = NULL; i < count; i++, curr++) {
+ if (prev)
+ prev->pipe.next = &curr->pipe;
+ curr->pipe.next = NULL;
+ prev = curr;
+ curr->qh_phys = epipe_phys + (curr - epipe) * sizeof(*curr) +
+ offset_of(struct ehci_pipe, qh);
+ dprintf("%s - %d: qh %p, qh_phys %lx\n", __func__,
+ i, &curr->qh, curr->qh_phys);
+ }
+
+ if (!ehcd->freelist)
+ ehcd->freelist = &epipe->pipe;
+ else
+ ehcd->end->next = &epipe->pipe;
+ ehcd->end = &prev->pipe;
+
+ return 0;
+}
+
+static void ehci_init(struct usb_hcd_dev *hcidev)
+{
+ struct ehci_hcd *ehcd;
+
+ printf(" EHCI: Initializing\n");
+ dprintf("%s: device base address %p\n", __func__, hcidev->base);
+
+ ehcd = SLOF_alloc_mem(sizeof(*ehcd));
+ if (!ehcd) {
+ printf("usb-ehci: Unable to allocate memory\n");
+ return;
+ }
+ memset(ehcd, 0, sizeof(*ehcd));
+
+ hcidev->nextaddr = 1;
+ hcidev->priv = ehcd;
+ ehcd->hcidev = hcidev;
+ ehcd->cap_regs = (struct ehci_cap_regs *)(hcidev->base);
+ ehcd->op_regs = (struct ehci_op_regs *)(hcidev->base +
+ read_reg8(&ehcd->cap_regs->caplength));
+#ifdef EHCI_DEBUG
+ dump_ehci_regs(ehcd);
+#endif
+ ehci_hcd_init(ehcd);
+ ehci_hub_check_ports(ehcd);
+}
+
+static void ehci_exit(struct usb_hcd_dev *hcidev)
+{
+ struct ehci_hcd *ehcd;
+ static int count = 0;
+
+ dprintf("%s: enter \n", __func__);
+
+ if (!hcidev && !hcidev->priv) {
+ return;
+ }
+ count++;
+ if (count > 1) {
+ printf("%s: already called once \n", __func__);
+ return;
+ }
+ ehcd = hcidev->priv;
+ ehci_hcd_exit(ehcd);
+ SLOF_free_mem(ehcd, sizeof(*ehcd));
+ hcidev->priv = NULL;
+}
+
+static void ehci_detect(void)
+{
+
+}
+
+static void ehci_disconnect(void)
+{
+
+}
+
+static int ehci_handshake(struct ehci_hcd *ehcd, uint32_t timeout)
+{
+ uint32_t usbsts = 0, time;
+ uint32_t usbcmd;
+ mb();
+ usbcmd = read_reg32(&ehcd->op_regs->usbcmd);
+ /* Ring a doorbell */
+ write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_IAAD);
+ mb();
+ time = SLOF_GetTimer() + timeout;
+ while ((time > SLOF_GetTimer())) {
+ /* Wait for controller to confirm */
+ usbsts = read_reg32(&ehcd->op_regs->usbsts);
+ if (usbsts & STS_IAA) {
+ /* Acknowledge it, for next doorbell to work */
+ write_reg32(&ehcd->op_regs->usbsts, STS_IAA);
+ return true;
+ }
+ cpu_relax();
+ }
+ return false;
+}
+
+static int fill_qtd_buff(struct ehci_qtd *qtd, long data, uint32_t size)
+{
+ long i, rem;
+ long pos = (data + 0x1000) & ~0xfff;
+
+ qtd->buffer[0] = cpu_to_le32(PTR_U32(data));
+ for (i = 1; i < 5; i++) {
+ if ((data + size - 1) >= pos) {
+ //dprintf("data spans page boundary: %d, %p\n", i, pos);
+ qtd->buffer[i] = cpu_to_le32(pos);
+ pos += 0x1000;
+ } else
+ break;
+ }
+ if ((data + size) > pos)
+ rem = data + size - pos;
+ else
+ rem = 0;
+ return rem;
+}
+
+static int ehci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data)
+{
+ struct ehci_hcd *ehcd;
+ struct ehci_qtd *qtd, *qtds, *qtds_phys;
+ struct ehci_pipe *epipe;
+ uint32_t transfer_size = sizeof(*req);
+ uint32_t datalen, pid;
+ uint32_t time;
+ long req_phys = 0, data_phys = 0;
+ int ret = true;
+
+ if (pipe->type != USB_EP_TYPE_CONTROL) {
+ printf("usb-ehci: Not a control pipe.\n");
+ return false;
+ }
+
+ ehcd = pipe->dev->hcidev->priv;
+ qtds = qtd = SLOF_dma_alloc(sizeof(*qtds) * 3);
+ if (!qtds) {
+ printf("Error allocating qTDs.\n");
+ return false;
+ }
+ qtds_phys = (struct ehci_qtd *)SLOF_dma_map_in(qtds, sizeof(*qtds) * 3, true);
+ memset(qtds, 0, sizeof(*qtds) * 3);
+ req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true);
+ qtd->next_qtd = cpu_to_le32(PTR_U32(&qtds_phys[1]));
+ qtd->alt_next_qtd = QH_PTR_TERM;
+ qtd->token = cpu_to_le32((transfer_size << TOKEN_TBTT_SHIFT) |
+ (3 << TOKEN_CERR_SHIFT) |
+ (PID_SETUP << TOKEN_PID_SHIFT) |
+ (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT));
+ fill_qtd_buff(qtd, req_phys, sizeof(*req));
+
+ qtd++;
+ datalen = cpu_to_le16(req->wLength);
+ pid = (req->bmRequestType & REQT_DIR_IN) ? PID_IN : PID_OUT;
+ if (datalen) {
+ data_phys = SLOF_dma_map_in(data, datalen, true);
+ qtd->next_qtd = cpu_to_le32(PTR_U32(&qtds_phys[2]));
+ qtd->alt_next_qtd = QH_PTR_TERM;
+ qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) |
+ (datalen << TOKEN_TBTT_SHIFT) |
+ (3 << TOKEN_CERR_SHIFT) |
+ (pid << TOKEN_PID_SHIFT) |
+ (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT));
+ fill_qtd_buff(qtd, data_phys, datalen);
+ qtd++;
+ }
+
+ if (pid == PID_IN)
+ pid = PID_OUT;
+ else
+ pid = PID_IN;
+ qtd->next_qtd = QH_PTR_TERM;
+ qtd->alt_next_qtd = QH_PTR_TERM;
+ qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) |
+ (3 << TOKEN_CERR_SHIFT) |
+ (pid << TOKEN_PID_SHIFT) |
+ (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT));
+
+ /* link qtd to qh and attach to ehcd */
+ mb();
+ epipe = container_of(pipe, struct ehci_pipe, pipe);
+ epipe->qh.next_qtd = cpu_to_le32(PTR_U32(qtds_phys));
+ epipe->qh.qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH);
+ epipe->qh.ep_cap1 = cpu_to_le32((pipe->mps << QH_MPS_SHIFT) |
+ (pipe->speed << QH_EPS_SHIFT) |
+ (pipe->epno << QH_EP_SHIFT) |
+ (pipe->dev->addr << QH_DEV_ADDR_SHIFT));
+ mb();
+
+ ehcd->qh_async->qh_ptr = cpu_to_le32(epipe->qh_phys | EHCI_TYP_QH);
+
+ /* transfer data */
+ mb();
+ qtd = &qtds[0];
+ time = SLOF_GetTimer() + USB_TIMEOUT;
+ do {
+ if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT))
+ mb();
+ else
+ qtd++;
+
+ if (time < SLOF_GetTimer()) { /* timed out */
+ printf("usb-ehci: control transfer timed out_\n");
+ ret = false;
+ break;
+ }
+ } while (qtd->next_qtd != QH_PTR_TERM);
+
+ ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH);
+ mb();
+ if (!ehci_handshake(ehcd, USB_TIMEOUT)) {
+ printf("%s: handshake failed\n", __func__);
+ ret = false;
+ }
+
+ SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req));
+ SLOF_dma_map_out(data_phys, data, datalen);
+ SLOF_dma_map_out(PTR_U32(qtds_phys), qtds, sizeof(*qtds) * 3);
+ SLOF_dma_free(qtds, sizeof(*qtds) * 3);
+
+ return ret;
+}
+
+static int ehci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys,
+ void *data_phys, int size)
+{
+ struct ehci_hcd *ehcd;
+ struct ehci_qtd *qtd, *qtd_phys;
+ struct ehci_pipe *epipe;
+ uint32_t pid;
+ int i, rem, ret = true;
+ uint32_t time;
+ long ptr;
+
+ dprintf("usb-ehci: bulk transfer: data %p, size %d, td %p, td_phys %p\n",
+ data_phys, size, td, td_phys);
+
+ if (pipe->type != USB_EP_TYPE_BULK) {
+ printf("usb-ehci: Not a bulk pipe.\n");
+ return false;
+ }
+
+ if (size > QTD_MAX_TRANSFER_LEN) {
+ printf("usb-ehci: bulk transfer size too big\n");
+ return false;
+ }
+
+ ehcd = pipe->dev->hcidev->priv;
+ pid = (pipe->dir == USB_PIPE_OUT) ? PID_OUT : PID_IN;
+ qtd = (struct ehci_qtd *)td;
+ qtd_phys = (struct ehci_qtd *)td_phys;
+ ptr = (long)data_phys;
+ for (i = 0; i < NUM_BULK_QTDS; i++) {
+ memset(qtd, 0, sizeof(*qtd));
+ rem = fill_qtd_buff(qtd, ptr, size);
+ qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) |
+ ((size - rem) << TOKEN_TBTT_SHIFT) |
+ (3 << TOKEN_CERR_SHIFT) |
+ (pid << TOKEN_PID_SHIFT) |
+ (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT));
+ if (rem) {
+ qtd->next_qtd = cpu_to_le32(PTR_U32(&qtd_phys[i+1]));
+ qtd->alt_next_qtd = QH_PTR_TERM;
+ ptr += size - rem;
+ size = rem;
+ qtd++;
+ } else {
+ qtd->next_qtd = qtd->alt_next_qtd = QH_PTR_TERM;
+ break; /* no more data */
+ }
+ }
+
+ /* link qtd to qh and attach to ehcd */
+ mb();
+ epipe = container_of(pipe, struct ehci_pipe, pipe);
+ epipe->qh.next_qtd = cpu_to_le32(PTR_U32(qtd_phys));
+ epipe->qh.qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH);
+ epipe->qh.ep_cap1 = cpu_to_le32((pipe->mps << QH_MPS_SHIFT) |
+ (pipe->speed << QH_EPS_SHIFT) |
+ (pipe->epno << QH_EP_SHIFT) |
+ (pipe->dev->addr << QH_DEV_ADDR_SHIFT));
+ mb();
+
+ ehcd->qh_async->qh_ptr = cpu_to_le32(epipe->qh_phys | EHCI_TYP_QH);
+
+ /* transfer data */
+ mb();
+ qtd = (struct ehci_qtd *)td;
+ for (i = 0; i < NUM_BULK_QTDS; i++) {
+ time = SLOF_GetTimer() + USB_TIMEOUT;
+ while ((time > SLOF_GetTimer()) &&
+ (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)))
+ cpu_relax();
+ mb();
+ if (qtd->next_qtd == QH_PTR_TERM)
+ break;
+
+ if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)) {
+ printf("usb-ehci: bulk transfer timed out_\n");
+ ret = false;
+ break;
+ }
+ qtd++;
+ }
+
+ ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH);
+ mb();
+ if (!ehci_handshake(ehcd, USB_TIMEOUT)) {
+ printf("%s: handshake failed\n", __func__);
+ ret = false;
+ }
+ return ret;
+}
+
+static struct usb_pipe *ehci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
+ char *buf, size_t len)
+{
+ struct ehci_hcd *ehcd;
+ struct usb_pipe *new = NULL;
+
+ if (!dev)
+ return NULL;
+
+ ehcd = (struct ehci_hcd *)dev->hcidev->priv;
+ if (!ehcd->freelist) {
+ dprintf("usb-ehci: %s allocating pool\n", __func__);
+ if (ehci_alloc_pipe_pool(ehcd))
+ return NULL;
+ }
+
+ new = ehcd->freelist;
+ ehcd->freelist = ehcd->freelist->next;
+ if (!ehcd->freelist)
+ ehcd->end = NULL;
+
+ memset(new, 0, sizeof(*new));
+ new->dev = dev;
+ new->next = NULL;
+ new->type = ep->bmAttributes & USB_EP_TYPE_MASK;
+ new->speed = dev->speed;
+ new->mps = ep->wMaxPacketSize;
+ new->dir = (ep->bEndpointAddress & 0x80) >> 7;
+ new->epno = ep->bEndpointAddress & 0x0f;
+
+ return new;
+}
+
+static void ehci_put_pipe(struct usb_pipe *pipe)
+{
+ struct ehci_hcd *ehcd;
+
+ dprintf("usb-ehci: %s enter - %p\n", __func__, pipe);
+ if (!pipe || !pipe->dev)
+ return;
+ ehcd = pipe->dev->hcidev->priv;
+ if (ehcd->end)
+ ehcd->end->next = pipe;
+ else
+ ehcd->freelist = pipe;
+
+ ehcd->end = pipe;
+ pipe->next = NULL;
+ pipe->dev = NULL;
+ memset(pipe, 0, sizeof(*pipe));
+ dprintf("usb-ehci: %s exit\n", __func__);
+}
+
+struct usb_hcd_ops ehci_ops = {
+ .name = "ehci-hcd",
+ .init = ehci_init,
+ .exit = ehci_exit,
+ .detect = ehci_detect,
+ .disconnect = ehci_disconnect,
+ .get_pipe = ehci_get_pipe,
+ .put_pipe = ehci_put_pipe,
+ .send_ctrl = ehci_send_ctrl,
+ .transfer_bulk = ehci_transfer_bulk,
+ .usb_type = USB_EHCI,
+ .next = NULL,
+};
+
+void usb_ehci_register(void)
+{
+ usb_hcd_register(&ehci_ops);
+}
diff --git a/roms/SLOF/lib/libusb/usb-ehci.h b/roms/SLOF/lib/libusb/usb-ehci.h
new file mode 100644
index 000000000..05ffb5296
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-ehci.h
@@ -0,0 +1,155 @@
+/******************************************************************************
+ * Copyright (c) 2007, 2012, 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * Definitions for EHCI Controller
+ *
+ */
+
+#ifndef USB_EHCI_H
+#define USB_EHCI_H
+
+#include <stdint.h>
+#include "usb-core.h"
+
+#define FL_SIZE 1024
+
+struct ehci_cap_regs {
+ uint8_t caplength;
+ uint8_t reserved;
+ uint16_t hciversion;
+ uint32_t hcsparams;
+ uint32_t hccparams;
+ uint64_t portroute;
+} __attribute__ ((packed, aligned(4)));
+
+struct ehci_op_regs {
+ uint32_t usbcmd;
+ uint32_t usbsts;
+ uint32_t usbintr;
+ uint32_t frindex;
+ uint32_t ctrldssegment;
+ uint32_t periodiclistbase;
+ uint32_t asynclistaddr;
+ uint32_t reserved[9];
+ uint32_t configflag;
+ uint32_t portsc[0];
+} __attribute__ ((packed, aligned(4)));
+
+struct ehci_framelist {
+ uint32_t fl_ptr[FL_SIZE];
+} __attribute__ ((packed));
+
+struct ehci_hcd {
+ struct ehci_cap_regs *cap_regs;
+ struct ehci_op_regs *op_regs;
+ struct usb_hcd_dev *hcidev;
+ struct ehci_qh *qh_async;
+ struct ehci_qh *qh_intr;
+ struct usb_pipe *freelist;
+ struct usb_pipe *end;
+ struct ehci_framelist *fl;
+ long qh_async_phys;
+ long qh_intr_phys;
+ long fl_phys;
+ void *pool;
+ long pool_phys;
+};
+
+struct ehci_qtd {
+ uint32_t next_qtd;
+ uint32_t alt_next_qtd;
+ uint32_t token;
+ uint32_t buffer[5];
+} __attribute__ ((packed));
+
+struct ehci_qh {
+ uint32_t qh_ptr;
+ uint32_t ep_cap1;
+ uint32_t ep_cap2;
+ uint32_t curr_qtd;
+ uint32_t next_qtd;
+ uint32_t alt_next_qtd;
+ uint32_t token;
+ uint32_t buffer[5];
+} __attribute__ ((packed)) __attribute__((aligned(32)));
+
+struct ehci_pipe {
+ struct ehci_qh qh;
+ struct usb_pipe pipe;
+ long qh_phys;
+};
+
+#define EHCI_PIPE_POOL_SIZE 4096
+
+#define EHCI_TYP_ITD 0x00
+#define EHCI_TYP_QH 0x02
+#define EHCI_TYP_SITD 0x04
+#define EHCI_TYP_FSTN 0x06
+
+#define PID_OUT 0x00
+#define PID_IN 0x01
+#define PID_SETUP 0x02
+
+#define HCS_NPORTS_MASK 0x000f
+
+#define CMD_IAAD (1 << 6)
+#define CMD_ASE (1 << 5)
+#define CMD_PSE (1 << 4)
+#define CMD_FLS_MASK (3 << 2)
+#define CMD_HCRESET (1 << 1)
+#define CMD_RUN (1 << 0)
+
+#define STS_IAA (1 << 5)
+
+#define PORT_RESET (1 << 8)
+#define PORT_PE (1 << 2)
+#define PORT_CSC (1 << 1)
+#define PORT_CONNECT (1 << 0)
+
+#define QH_LOW_SPEED 0
+#define QH_FULL_SPEED 1
+#define QH_HIGH_SPEED 2
+
+#define QH_RL_SHIFT 28
+#define QH_CAP_C (1 << 27)
+#define QH_MPS_SHIFT 16
+#define QH_CAP_H (1 << 15)
+#define QH_CAP_DTC (1 << 14)
+#define QH_EPS_SHIFT 12
+#define QH_EP_SHIFT 8
+#define QH_CAP_I (1 << 7)
+#define QH_DEV_ADDR_SHIFT 0
+
+#define QH_PTR_TERM __builtin_bswap32(1)
+#define QH_SMASK_SHIFT 0
+#define QH_STS_ACTIVE (1 << 7)
+#define QH_STS_HALTED (1 << 6)
+#define QH_STS_DBE (1 << 5)
+#define QH_STS_BABBLE (1 << 4)
+#define QH_STS_XACTERR (1 << 3)
+#define QH_STS_MMF (1 << 2)
+#define QH_STS_SXS (1 << 1)
+#define QH_STS_PING (1 << 0)
+
+#define NUM_BULK_QTDS 4
+#define MAX_XFER_PER_QTD (20 * 1024)
+#define QTD_MAX_TRANSFER_LEN (NUM_BULK_QTDS * MAX_XFER_PER_QTD)
+
+#define TOKEN_DT_SHIFT 31
+#define TOKEN_TBTT_SHIFT 16
+#define TOKEN_IOC_SHIFT 15
+#define TOKEN_CPAGE_SHIFT 12
+#define TOKEN_CERR_SHIFT 10
+#define TOKEN_PID_SHIFT 8
+#define TOKEN_STATUS_SHIFT 0
+
+#endif /* USB_EHCI_H */
diff --git a/roms/SLOF/lib/libusb/usb-hid.c b/roms/SLOF/lib/libusb/usb-hid.c
new file mode 100644
index 000000000..ce87d2194
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-hid.c
@@ -0,0 +1,461 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <termctrl.h>
+
+#include "usb-core.h"
+#include "usb-key.h"
+
+/*
+ * HID Spec Version 1.11
+ */
+
+#define HID_REQ_GET_REPORT 0x01
+#define HID_REQ_GET_IDLE 0x02
+#define HID_REQ_GET_PROTOCOL 0x03
+#define HID_REQ_SET_REPORT 0x09
+#define HID_REQ_SET_IDLE 0x0A
+#define HID_REQ_SET_PROTOCOL 0x0B
+
+//key position for latin letters
+#define KEYP_LATIN_A 4
+#define KEYP_LATIN_Z 29
+
+//#define KEY_DEBUG
+
+/* HID SPEC - 7.2.6 Set_Protocol Request */
+static int usb_hid_set_protocol(struct usb_dev *dev, uint16_t value)
+{
+ struct usb_dev_req req;
+ if (!dev)
+ return false;
+ req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT;
+ req.bRequest = HID_REQ_SET_PROTOCOL;
+ req.wValue = cpu_to_le16(value);
+ req.wIndex = cpu_to_le16(dev->intf_num);
+ req.wLength = 0;
+ return usb_send_ctrl(dev->control, &req, NULL);
+}
+
+/* HID SPEC - 7.2.4 Set_Idle Request */
+static int usb_hid_set_idle(struct usb_dev *dev, uint16_t ms_delay)
+{
+ struct usb_dev_req req;
+ if (!dev)
+ return false;
+ req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT;
+ req.bRequest = HID_REQ_SET_IDLE;
+ req.wValue = cpu_to_le16((ms_delay/4) << 8);
+ req.wIndex = cpu_to_le16(dev->intf_num);
+ req.wLength = 0;
+ return usb_send_ctrl(dev->control, &req, NULL);
+}
+
+/* HID SPEC - 7.2.1 Get Report Request */
+static int usb_hid_get_report(struct usb_dev *dev, void *data, size_t size)
+{
+ struct usb_dev_req req;
+ if (!dev)
+ return false;
+ req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_IN;
+ req.bRequest = HID_REQ_GET_REPORT;
+ req.wIndex = cpu_to_le16(dev->intf_num);
+
+ req.wLength = cpu_to_le16((uint16_t)size);
+ req.wValue = cpu_to_le16(1 << 8);
+ return usb_send_ctrl(dev->control, &req, data);
+}
+
+/* ring buffer with RD/WR indices for key buffering */
+static uint8_t keybuf[256]; /* size fixed to byte range ! */
+uint8_t r_ptr = 0; /* RD-index for Keyboard-Buffer */
+uint8_t w_ptr = 0; /* WR-index for Keyboard-Buffer */
+
+/* variables for LED status */
+uint8_t set_leds;
+const uint8_t *key_std = NULL;
+const uint8_t *key_std_shift = NULL;
+
+
+/**
+ * read character from Keyboard-Buffer
+ *
+ * @param -
+ * @return > 0 Keycode
+ * = 0 if no key available
+ */
+static int read_key(void)
+{
+ if (r_ptr != w_ptr)
+ return (int)keybuf[r_ptr++];
+ else
+ return false;
+}
+
+/**
+ * Store character into Keyboard-Buffer
+ *
+ * @param Key = detected ASCII-Key (> 0)
+ * @return -
+ */
+static void write_key(uint8_t key)
+{
+ if ((w_ptr + 1) != r_ptr)
+ keybuf[w_ptr++] = key;
+}
+
+/**
+ * Checks if keypos is a latin key
+ * @param keypos
+ * @return -
+ */
+static bool is_latin(uint8_t keypos)
+{
+ return keypos >= KEYP_LATIN_A && keypos <= KEYP_LATIN_Z;
+}
+
+/**
+ * Convert keyboard usage-ID to ANSI-Code
+ *
+ * @param Ctrl=Modifier Byte
+ * Key =Usage ID from USB Keyboard
+ * @return -
+ */
+static void get_char(uint8_t ctrl, uint8_t keypos)
+{
+ uint8_t ch;
+ bool caps = false;
+
+#ifdef KEY_DEBUG
+ printf("pos %02X\n", keypos);
+#endif
+
+ if (set_leds & LED_CAPS_LOCK) /* is CAPS Lock set ? */
+ caps = true;
+
+ /* caps is a shift only for latin chars */
+ if ((!caps && ctrl == 0) || (caps && !is_latin(keypos))) {
+ ch = key_std[keypos];
+ if (ch != 0)
+ write_key(ch);
+ return;
+ }
+
+ if ((ctrl & MODIFIER_SHIFT) || caps) {
+ ch = key_std_shift[keypos];
+ if (ch != 0)
+ write_key(ch);
+ return;
+ }
+
+ if (ctrl & MODIFIER_CTRL) {
+ ch = keycodes_ctrl[keypos];
+ if (ch != 0)
+ write_key(ch);
+ return;
+ }
+
+ if (ctrl == MODIFIER_ALT_GR) {
+ ch = keycodes_alt_GR[keypos];
+ if (ch != 0)
+ write_key(ch);
+ return;
+ }
+}
+
+static void check_key_code(uint8_t *buf)
+{
+ static uint8_t key_last[6]; /* list of processed keys */
+ uint8_t i, j, key_pos;
+
+ /* set translation table to defaults */
+ if ((key_std == NULL) || (key_std_shift == NULL)) {
+ key_std = keycodes_std_US;
+ key_std_shift = keycodes_shift_US;
+ }
+
+ if (buf[0] & MODIFIER_SHIFT) /* any shift key pressed ? */
+ set_leds &= ~LED_CAPS_LOCK; /* CAPS-LOCK-LED always off */
+
+ i = 2; /* skip modifier byte and reserved byte */
+ while (i < 8) {
+ key_pos = buf[i];
+ if ((key_pos != 0) && (key_pos <= 100)) { /* support for 101 keys */
+ j = 0;
+ /* search if already processed */
+ while ((j < 6) && (key_pos != key_last[j]))
+ j++;
+
+ if (j >= 6) { /* not found (= not processed) */
+ switch (key_pos) {
+ case 0x39: /* caps-lock key ? */
+ case 0x32: /* caps-lock key ? */
+ set_leds ^= LED_CAPS_LOCK;
+ break;
+
+ case 0x3a: /* F1 */
+ write_key(0x1b);
+ write_key(0x5b);
+ write_key(0x4f);
+ write_key(0x50);
+ break;
+
+ case 0x3b: /* F2 */
+ write_key(0x1b);
+ write_key(0x5b);
+ write_key(0x4f);
+ write_key(0x51);
+ break;
+
+ case 0x3c:
+ write_key(0x1b); /* F3 */
+ write_key(0x5b);
+ write_key(0x4f);
+ write_key(0x52);
+ break;
+
+ case 0x3d:
+ write_key(0x1b); /* F4 */
+ write_key(0x5b);
+ write_key(0x4f);
+ write_key(0x53);
+ break;
+
+ case 0x3e:
+ write_key(0x1b); /* F5 */
+ write_key(0x5b);
+ write_key(0x31);
+ write_key(0x35);
+ write_key(0x7e);
+ break;
+
+ case 0x3f:
+ write_key(0x1b); /* F6 */
+ write_key(0x5b);
+ write_key(0x31);
+ write_key(0x37);
+ write_key(0x7e);
+ break;
+
+ case 0x40:
+ write_key(0x1b); /* F7 */
+ write_key(0x5b);
+ write_key(0x31);
+ write_key(0x38);
+ write_key(0x7e);
+ break;
+
+ case 0x41:
+ write_key(0x1b); /* F8 */
+ write_key(0x5b);
+ write_key(0x31);
+ write_key(0x39);
+ write_key(0x7e);
+ break;
+
+ case 0x42:
+ write_key(0x1b); /* F9 */
+ write_key(0x5b);
+ write_key(0x32);
+ write_key(0x30);
+ write_key(0x7e);
+ break;
+
+ case 0x43:
+ write_key(0x1b); /* F10 */
+ write_key(0x5b);
+ write_key(0x32);
+ write_key(0x31);
+ write_key(0x7e);
+ break;
+
+ case 0x44:
+ write_key(0x1b); /* F11 */
+ write_key(0x5b);
+ write_key(0x32);
+ write_key(0x33);
+ write_key(0x7e);
+ break;
+
+ case 0x45:
+ write_key(0x1b); /* F12 */
+ write_key(0x5b);
+ write_key(0x32);
+ write_key(0x34);
+ write_key(0x7e);
+ break;
+
+ case 0x47: /* scroll-lock key ? */
+ set_leds ^= LED_SCROLL_LOCK;
+ break;
+
+ case 0x49:
+ write_key(0x1b); /* INS */
+ write_key(0x5b);
+ write_key(0x32);
+ write_key(0x7e);
+ break;
+
+ case 0x4a:
+ write_key(0x1b); /* HOME */
+ write_key(0x4f);
+ write_key(0x48);
+ break;
+
+ case 0x4b:
+ write_key(0x1b); /* PgUp */
+ write_key(0x5b);
+ write_key(0x35);
+ write_key(0x7e);
+ break;
+
+ case 0x4c:
+ write_key(0x1b); /* DEL */
+ write_key(0x5b);
+ write_key(0x33);
+ write_key(0x7e);
+ break;
+
+ case 0x4d:
+ write_key(0x1b); /* END */
+ write_key(0x4f);
+ write_key(0x46);
+ break;
+
+ case 0x4e:
+ write_key(0x1b); /* PgDn */
+ write_key(0x5b);
+ write_key(0x36);
+ write_key(0x7e);
+ break;
+
+ case 0x4f:
+ write_key(0x1b); /* R-Arrow */
+ write_key(0x5b);
+ write_key(0x43);
+ break;
+
+ case 0x50:
+ write_key(0x1b); /* L-Arrow */
+ write_key(0x5b);
+ write_key(0x44);
+ break;
+
+ case 0x51:
+ write_key(0x1b); /* D-Arrow */
+ write_key(0x5b);
+ write_key(0x42);
+ break;
+
+ case 0x52:
+ write_key(0x1b); /* U-Arrow */
+ write_key(0x5b);
+ write_key(0x41);
+ break;
+
+ case 0x53: /* num-lock key ? */
+ set_leds ^= LED_NUM_LOCK;
+ break;
+
+ default:
+ /* convert key position to ASCII code */
+ get_char(buf[0], key_pos);
+ break;
+ }
+ }
+ }
+ i++;
+ }
+ /*****************************************/
+ /* all keys are processed, create a copy */
+ /* to flag them as processed */
+ /*****************************************/
+ for (i = 2, j = 0; j < 6; i++, j++)
+ key_last[j] = buf[i]; /* copy all actual keys to last */
+}
+
+#define USB_HID_SIZE 128
+uint32_t *kbd_buffer;
+
+int usb_hid_kbd_init(struct usb_dev *dev)
+{
+ unsigned i;
+ uint8_t key[8];
+
+ usb_hid_set_protocol(dev, 0);
+ usb_hid_set_idle(dev, 500);
+
+ memset(key, 0, 8);
+ if (usb_hid_get_report(dev, key, 8))
+ check_key_code(key);
+
+ kbd_buffer = SLOF_dma_alloc(USB_HID_SIZE);
+ if (!kbd_buffer) {
+ printf("%s: unable to allocate keyboard buffer\n", __func__);
+ return false;
+ }
+
+#ifdef KEY_DEBUG
+ printf("HID kbd init %d\n", dev->ep_cnt);
+#endif
+ for (i = 0; i < dev->ep_cnt; i++) {
+ if ((dev->ep[i].bmAttributes & USB_EP_TYPE_MASK)
+ == USB_EP_TYPE_INTR)
+ usb_dev_populate_pipe(dev, &dev->ep[i], kbd_buffer, USB_HID_SIZE);
+ }
+ return true;
+}
+
+int usb_hid_kbd_exit(struct usb_dev *dev)
+{
+ if (dev->intr) {
+ usb_put_pipe(dev->intr);
+ dev->intr = NULL;
+ }
+ SLOF_dma_free(kbd_buffer, USB_HID_SIZE);
+ return true;
+}
+
+static int usb_poll_key(void *vdev)
+{
+ struct usb_dev *dev = vdev;
+ uint8_t key[8];
+ int rc;
+
+ memset(key, 0, 8);
+ rc = usb_poll_intr(dev->intr, key);
+ if (rc)
+ check_key_code(key);
+ return rc;
+}
+
+unsigned char usb_key_available(void *dev)
+{
+ if (!dev)
+ return false;
+
+ usb_poll_key(dev);
+ if (r_ptr != w_ptr)
+ return true;
+ else
+ return false;
+}
+
+unsigned char usb_read_keyb(void *vdev)
+{
+ if (usb_key_available(vdev))
+ return read_key();
+ else
+ return 0;
+}
diff --git a/roms/SLOF/lib/libusb/usb-hub.c b/roms/SLOF/lib/libusb/usb-hub.c
new file mode 100644
index 000000000..58e552f61
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-hub.c
@@ -0,0 +1,220 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "usb-core.h"
+#include "usb-xhci.h"
+
+#undef HUB_DEBUG
+//#define HUB_DEBUG
+#ifdef HUB_DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+/*
+ * USB Spec 1.1
+ * 11.16.2 Class-specific Requests
+ */
+struct usb_hub_ps {
+ uint16_t wPortStatus;
+ uint16_t wPortChange;
+} __attribute__((packed));
+
+#define HUB_PS_CONNECTION (1 << 0)
+#define HUB_PS_ENABLE (1 << 1)
+#define HUB_PS_SUSPEND (1 << 2)
+#define HUB_PS_OVER_CURRENT (1 << 3)
+#define HUB_PS_RESET (1 << 4)
+#define HUB_PS_POWER (1 << 8)
+#define HUB_PS_LOW_SPEED (1 << 9)
+#define HUB_PS_HIGH_SPEED (1 << 10)
+
+#define HUB_PF_CONNECTION 0
+#define HUB_PF_ENABLE 1
+#define HUB_PF_SUSPEND 2
+#define HUB_PF_OVER_CURRENT 3
+#define HUB_PF_RESET 4
+#define HUB_PF_POWER 8
+#define HUB_PF_LOWSPEED 9
+#define HUB_PF_C_CONNECTION 16
+#define HUB_PF_C_ENABLE 17
+#define HUB_PF_C_SUSPEND 18
+#define HUB_PF_C_OVER_CURRENT 19
+#define HUB_PF_C_RESET 20
+
+static int usb_get_hub_desc(struct usb_dev *dev, void *data, size_t size)
+{
+ struct usb_dev_req req;
+ if (!dev)
+ return false;
+ req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_DEVICE;
+ req.bRequest = REQ_GET_DESCRIPTOR;
+ req.wIndex = 0;
+ req.wLength = cpu_to_le16((uint16_t) size);
+ req.wValue = cpu_to_le16(DESCR_TYPE_HUB << 8);
+ return usb_send_ctrl(dev->control, &req, data);
+}
+
+static int hub_get_port_status(struct usb_dev *dev, int port, void *data, size_t size)
+{
+ struct usb_dev_req req;
+ if (!dev)
+ return false;
+ req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_OTHER;
+ req.bRequest = REQ_GET_STATUS;
+ req.wValue = 0;
+ req.wIndex = cpu_to_le16((uint16_t)(port + 1));
+ req.wLength = cpu_to_le16((uint16_t)size);
+ return usb_send_ctrl(dev->control, &req, data);
+}
+
+static int hub_set_port_feature(struct usb_dev *dev, int port, int feature)
+{
+ struct usb_dev_req req;
+ if (!dev)
+ return false;
+ req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER;
+ req.bRequest = REQ_SET_FEATURE;
+ req.wLength = 0;
+ req.wValue = cpu_to_le16((uint16_t)feature);
+ req.wIndex = cpu_to_le16((uint16_t)(port + 1));
+ return usb_send_ctrl(dev->control, &req, NULL);
+}
+
+#if 0
+static int hub_clear_port_feature(struct usb_dev *dev, int port, int feature)
+{
+ struct usb_dev_req req;
+ if (!dev)
+ return false;
+ req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER;
+ req.bRequest = REQ_CLEAR_FEATURE;
+ req.wLength = 0;
+ req.wValue = cpu_to_le16((uint16_t)feature);
+ req.wIndex = cpu_to_le16((uint16_t)(port + 1));
+ return usb_send_ctrl(dev->control, &req, NULL);
+}
+#endif
+
+static int hub_check_port(struct usb_dev *dev, int port)
+{
+ struct usb_hub_ps ps;
+ uint32_t time;
+
+ if (!hub_get_port_status(dev, port, &ps, sizeof(ps)))
+ return false;
+ dprintf("Port Status %04X Port Change %04X\n",
+ le16_to_cpu(ps.wPortStatus),
+ le16_to_cpu(ps.wPortChange));
+
+ if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_POWER)) {
+ hub_set_port_feature(dev, port, HUB_PF_POWER);
+ SLOF_msleep(100);
+ time = SLOF_GetTimer() + USB_TIMEOUT;
+ while (time > SLOF_GetTimer()) {
+ cpu_relax();
+ hub_get_port_status(dev, port, &ps, sizeof(ps));
+ if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) {
+ dprintf("power on Port Status %04X Port Change %04X\n",
+ le16_to_cpu(ps.wPortStatus),
+ le16_to_cpu(ps.wPortChange));
+ break;
+ }
+ }
+ }
+
+ if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) {
+ hub_set_port_feature(dev, port, HUB_PF_RESET);
+ SLOF_msleep(100);
+ time = SLOF_GetTimer() + USB_TIMEOUT;
+ while (time > SLOF_GetTimer()) {
+ cpu_relax();
+ hub_get_port_status(dev, port, &ps, sizeof(ps));
+ if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_RESET)) {
+ dprintf("reset Port Status %04X Port Change %04X\n",
+ le16_to_cpu(ps.wPortStatus),
+ le16_to_cpu(ps.wPortChange));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool usb_hub_init_dev(struct usb_dev *hub_dev, int port)
+{
+ struct usb_dev *newdev;
+
+ if (hub_dev->hcidev->type == USB_XHCI) {
+ struct usb_hub_ps ps;
+ int slotspeed;
+
+ hub_get_port_status(hub_dev, port, &ps, sizeof(ps));
+ if (le16_to_cpu(ps.wPortStatus) & HUB_PS_LOW_SPEED)
+ slotspeed = SLOT_SPEED_LS;
+ else if (le16_to_cpu(ps.wPortStatus) & HUB_PS_HIGH_SPEED)
+ slotspeed = SLOT_SPEED_HS;
+ else
+ slotspeed = SLOT_SPEED_FS;
+
+ /*
+ * USB3 devices need special setup (e.g. with assigning
+ * a slot ID and route string), which will all be done
+ * by usb3_dev_init() - it also calls usb_devpool_get(),
+ * usb_setup_new_device() and usb_slof_populate_new_device()
+ * internally, so we can return immediately after this step.
+ */
+ return usb3_dev_init(hub_dev->hcidev->priv, hub_dev, port,
+ slotspeed);
+ }
+
+ newdev = usb_devpool_get();
+ dprintf("usb-hub: allocated device %p\n", newdev);
+ newdev->hub = hub_dev;
+ newdev->hcidev = hub_dev->hcidev;
+ if (usb_setup_new_device(newdev, port)) {
+ usb_slof_populate_new_device(newdev);
+ return true;
+ }
+
+ return false;
+}
+
+unsigned int usb_hub_init(void *hubdev)
+{
+ struct usb_dev *dev = hubdev;
+ struct usb_dev_hub_descr hub;
+ int i;
+
+ dprintf("%s: enter %p\n", __func__, dev);
+ if (!dev) {
+ printf("usb-hub: NULL\n");
+ return false;
+ }
+ memset(&hub, 0, sizeof(hub));
+ usb_get_hub_desc(dev, &hub, sizeof(hub));
+ dprintf("usb-hub: ports connected %d\n", hub.bNbrPorts);
+ for (i = 0; i < hub.bNbrPorts; i++) {
+ dprintf("usb-hub: ports scanning %d\n", i);
+ if (hub_check_port(dev, i)) {
+ dprintf("***********************************************\n");
+ dprintf("\t\tusb-hub: device found %d\n", i);
+ dprintf("***********************************************\n");
+ if (!usb_hub_init_dev(dev, i))
+ printf("usb-hub: unable to setup device on port %d\n", i);
+ }
+ }
+ return true;
+}
diff --git a/roms/SLOF/lib/libusb/usb-key.c b/roms/SLOF/lib/libusb/usb-key.c
new file mode 100644
index 000000000..7fb45da2c
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-key.c
@@ -0,0 +1,446 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdint.h>
+
+/***********************************/
+/* Keycodes for US Keyboard */
+/* - no control keys pressed - */
+/***********************************/
+const uint8_t keycodes_std_US[] = {
+ 0, /* 0 00 Reserved (no event indicated) */
+ 0, /* 1 01 Keyboard ErrorRollOver */
+ 0, /* 2 02 Keyboard POSTFail */
+ 0, /* 3 03 Keyboard ErrorUndefined */
+ 'a', /* 4 04 Keyboard a and A 31 */
+ 'b', /* 5 05 Keyboard b and B 50 */
+ 'c', /* 6 06 Keyboard c and C 48 */
+ 'd', /* 7 07 Keyboard d and D 33 */
+ 'e', /* 8 08 Keyboard e and E 19 */
+ 'f', /* 9 09 Keyboard f and F 34 */
+ 'g', /* 10 0A Keyboard g and G 35 */
+ 'h', /* 11 0B Keyboard h and H 36 */
+ 'i', /* 12 0C Keyboard i and I 24 */
+ 'j', /* 13 0D Keyboard j and J 37 */
+ 'k', /* 14 0E Keyboard k and K 38 */
+ 'l', /* 15 0F Keyboard l and L 39 */
+ 'm', /* 16 10 Keyboard m and M 52 */
+ 'n', /* 17 11 Keyboard n and N 51 */
+ 'o', /* 18 12 Keyboard o and O 25 */
+ 'p', /* 19 13 Keyboard p and P 26 */
+ 'q', /* 20 14 Keyboard q and Q 17 */
+ 'r', /* 21 15 Keyboard r and R 20 */
+ 's', /* 22 16 Keyboard s and S 32 */
+ 't', /* 23 17 Keyboard t and T 21 */
+ 'u', /* 24 18 Keyboard u and U 23 */
+ 'v', /* 25 19 Keyboard v and V 49 */
+ 'w', /* 26 1A Keyboard w and W 18 */
+ 'x', /* 27 1B Keyboard x and X 47 */
+ 'y', /* 28 1C Keyboard y and Y 22 */
+ 'z', /* 29 1D Keyboard z and Z 46 */
+ '1', /* 30 1E Keyboard 1 and ! 2 */
+ '2', /* 31 1F Keyboard 2 and @ 3 */
+ '3', /* 32 20 Keyboard 3 and # 4 */
+ '4', /* 33 21 Keyboard 4 and $ 5 */
+ '5', /* 34 22 Keyboard 5 and % 6 */
+ '6', /* 35 23 Keyboard 6 and ^ 7 */
+ '7', /* 36 24 Keyboard 7 and & 8 */
+ '8', /* 37 25 Keyboard 8 and * 9 */
+ '9', /* 38 26 Keyboard 9 and ( 10 */
+ '0', /* 39 27 Keyboard 0 and ) 11 */
+ 13, /* 40 28 Keyboard Return (ENTER) 43 */
+ 27, /* 41 29 Keyboard ESCAPE 110 */
+ 8, /* 42 2A Keyboard DELETE (BS) 15 */
+ 9, /* 43 2B Keyboard Tab 16 */
+ ' ', /* 44 2C Keyboard Spacebar 61 */
+ '-', /* 45 2D Keyboard - and (underscore) 12 */
+ '=', /* 46 2E Keyboard = and + 13 */
+ '[', /* 47 2F Keyboard [ and { 27 */
+ ']', /* 48 30 Keyboard ] and } 28 */
+ '\\', /* 49 31 Keyboard \ and | 29 */
+ '\\', /* 50 32 Keyboard \ and | 42 */
+ ';', /* 51 33 Keyboard ; and : 40 */
+ 39, /* 52 34 Keyboard ' and " 41 */
+ 96, /* 53 35 Keyboard Grave Accent and Tilde 1 */
+ ',', /* 54 36 Keyboard , and < 53 */
+ '.', /* 55 37 Keyboard . and > 54 */
+ '/', /* 56 38 Keyboard / and ? 55 */
+ 0, /* 57 39 Keyboard Caps Lock 30 */
+ 0, /* 58 3A Keyboard F1 112 */
+ 0, /* 59 3B Keyboard F2 113 */
+ 0, /* 60 3C Keyboard F3 114 */
+ 0, /* 61 3D Keyboard F4 115 */
+ 0, /* 62 3E Keyboard F5 116 */
+ 0, /* 63 3F Keyboard F6 117 */
+ 0, /* 64 40 Keyboard F7 118 */
+ 0, /* 65 41 Keyboard F8 119 */
+ 0, /* 66 42 Keyboard F9 120 */
+ 0, /* 67 43 Keyboard F10 121 */
+ 0, /* 68 44 Keyboard F11 122 */
+ 0, /* 69 45 Keyboard F12 123 */
+ 0, /* 70 46 Keyboard PrintScreen 124 */
+ 0, /* 71 47 Keyboard Scroll Lock 125 */
+ 0, /* 72 48 Keyboard Pause 126 */
+ 0, /* 73 49 Keyboard Insert 75 */
+ 0, /* 74 4A Keyboard Home 80 */
+ 0, /* 75 4B Keyboard PageUp 85 */
+ 0, /* 76 4C Keyboard Delete Forward 76 */
+ 0, /* 77 4D Keyboard End 81 */
+ 0, /* 78 4E Keyboard PageDown 86 */
+ 0, /* 79 4F Keyboard RightArrow 89 */
+ 0, /* 80 50 Keyboard LeftArrow 79 */
+ 0, /* 81 51 Keyboard DownArrow 84 */
+ 0, /* 82 52 Keyboard UpArrow 83 */
+ 0, /* 83 53 Keypad Num Lock and Clear 90 */
+ '/', /* 84 54 Keypad / 95 */
+ '*', /* 85 55 Keypad * 100 */
+ '-', /* 86 56 Keypad - 105 */
+ '+', /* 87 57 Keypad + 106 */
+ 13, /* 88 58 Keypad ENTER 108 */
+ '1', /* 89 59 Keypad 1 and End 93 */
+ '2', /* 90 5A Keypad 2 and Down Arrow 98 */
+ '3', /* 91 5B Keypad 3 and PageDn 103 */
+ '4', /* 92 5C Keypad 4 and Left Arrow 92 */
+ '5', /* 93 5D Keypad 5 97 */
+ '6', /* 94 5E Keypad 6 and Right Arrow 102 */
+ '7', /* 95 5F Keypad 7 and Home 91 */
+ '8', /* 96 60 Keypad 8 and Up Arrow 96 */
+ '9', /* 97 61 Keypad 9 and PageUp 101 */
+ '0', /* 98 62 Keypad 0 and Insert 99 */
+ '.', /* 99 63 Keypad . and Delete 104 */
+ '\\' /* 100 64 Keyboard Non-US \ and | 45 */
+};
+
+/***********************************/
+/* Keycodes for US Keyboard */
+/* - SHIFT-KEY pressed - */
+/***********************************/
+const uint8_t keycodes_shift_US[] = {
+ 0, /* 0 00 Reserved (no event indicated) */
+ 0, /* 1 01 Keyboard ErrorRollOver */
+ 0, /* 2 02 Keyboard POSTFail */
+ 0, /* 3 03 Keyboard ErrorUndefined */
+ 'A', /* 4 04 Keyboard a and A 31 */
+ 'B', /* 5 05 Keyboard b and B 50 */
+ 'C', /* 6 06 Keyboard c and C 48 */
+ 'D', /* 7 07 Keyboard d and D 33 */
+ 'E', /* 8 08 Keyboard e and E 19 */
+ 'F', /* 9 09 Keyboard f and F 34 */
+ 'G', /* 10 0A Keyboard g and G 35 */
+ 'H', /* 11 0B Keyboard h and H 36 */
+ 'I', /* 12 0C Keyboard i and I 24 */
+ 'J', /* 13 0D Keyboard j and J 37 */
+ 'K', /* 14 0E Keyboard k and K 38 */
+ 'L', /* 15 0F Keyboard l and L 39 */
+ 'M', /* 16 10 Keyboard m and M 52 */
+ 'N', /* 17 11 Keyboard n and N 51 */
+ 'O', /* 18 12 Keyboard o and O 25 */
+ 'P', /* 19 13 Keyboard p and P 26 */
+ 'Q', /* 20 14 Keyboard q and Q 17 */
+ 'R', /* 21 15 Keyboard r and R 20 */
+ 'S', /* 22 16 Keyboard s and S 32 */
+ 'T', /* 23 17 Keyboard t and T 21 */
+ 'U', /* 24 18 Keyboard u and U 23 */
+ 'V', /* 25 19 Keyboard v and V 49 */
+ 'W', /* 26 1A Keyboard w and W 18 */
+ 'X', /* 27 1B Keyboard x and X 47 */
+ 'Y', /* 28 1C Keyboard y and Y 22 */
+ 'Z', /* 29 1D Keyboard z and Z 46 */
+ '!', /* 30 1E Keyboard 1 and ! 2 */
+ '@', /* 31 1F Keyboard 2 and @ 3 */
+ '#', /* 32 20 Keyboard 3 and # 4 */
+ '$', /* 33 21 Keyboard 4 and $ 5 */
+ '%', /* 34 22 Keyboard 5 and % 6 */
+ '^', /* 35 23 Keyboard 6 and ^ 7 */
+ '&', /* 36 24 Keyboard 7 and & 8 */
+ '*', /* 37 25 Keyboard 8 and * 9 */
+ '(', /* 38 26 Keyboard 9 and ( 10 */
+ ')', /* 39 27 Keyboard 0 and ) 11 */
+ 13, /* 40 28 Keyboard Return (ENTER) 43 */
+ 27, /* 41 29 Keyboard ESCAPE 110 */
+ 8, /* 42 2A Keyboard DELETE (BS) 15 */
+ 9, /* 43 2B Keyboard Tab 16 */
+ ' ', /* 44 2C Keyboard Spacebar 61 */
+ '_', /* 45 2D Keyboard - and (underscore) 12 */
+ '+', /* 46 2E Keyboard = and + 13 */
+ '{', /* 47 2F Keyboard [ and { 27 */
+ '}', /* 48 30 Keyboard ] and } 28 */
+ '|', /* 49 31 Keyboard \ and | 29 */
+ '|', /* 50 32 Keyboard \ and | 42 */
+ ':', /* 51 33 Keyboard ; and : 40 */
+ '"', /* 52 34 Keyboard ' and " 41 */
+ '~', /* 53 35 Keyboard Grave Accent and Tilde 1 */
+ '<', /* 54 36 Keyboard , and < 53 */
+ '>', /* 55 37 Keyboard . and > 54 */
+ '?', /* 56 38 Keyboard / and ? 55 */
+ 0, /* 57 39 Keyboard Caps Lock 30 */
+ 0, /* 58 3A Keyboard F1 112 */
+ 0, /* 59 3B Keyboard F2 113 */
+ 0, /* 60 3C Keyboard F3 114 */
+ 0, /* 61 3D Keyboard F4 115 */
+ 0, /* 62 3E Keyboard F5 116 */
+ 0, /* 63 3F Keyboard F6 117 */
+ 0, /* 64 40 Keyboard F7 118 */
+ 0, /* 65 41 Keyboard F8 119 */
+ 0, /* 66 42 Keyboard F9 120 */
+ 0, /* 67 43 Keyboard F10 121 */
+ 0, /* 68 44 Keyboard F11 122 */
+ 0, /* 69 45 Keyboard F12 123 */
+ 0, /* 70 46 Keyboard PrintScreen 124 */
+ 0, /* 71 47 Keyboard Scroll Lock 125 */
+ 0, /* 72 48 Keyboard Pause 126 */
+ 48, /* 73 49 Keyboard Insert 75 */
+ 55, /* 74 4A Keyboard Home 80 */
+ 57, /* 75 4B Keyboard PageUp 85 */
+ 46, /* 76 4C Keyboard Delete Forward 76 */
+ 49, /* 77 4D Keyboard End 81 */
+ 51, /* 78 4E Keyboard PageDown 86 */
+ 54, /* 79 4F Keyboard RightArrow 89 */
+ 52, /* 80 50 Keyboard LeftArrow 79 */
+ 50, /* 81 51 Keyboard DownArrow 84 */
+ 56, /* 82 52 Keyboard UpArrow 83 */
+ 0, /* 83 53 Keypad Num Lock and Clear 90 */
+ '/', /* 84 54 Keypad / 95 */
+ '*', /* 85 55 Keypad * 100 */
+ '-', /* 86 56 Keypad - 105 */
+ '+', /* 87 57 Keypad + 106 */
+ 13, /* 88 58 Keypad ENTER 108 */
+ '1', /* 89 59 Keypad 1 and End 93 */
+ '2', /* 90 5A Keypad 2 and Down Arrow 98 */
+ '3', /* 91 5B Keypad 3 and PageDn 103 */
+ '4', /* 92 5C Keypad 4 and Left Arrow 92 */
+ '5', /* 93 5D Keypad 5 97 */
+ '6', /* 94 5E Keypad 6 and Right Arrow 102 */
+ '7', /* 95 5F Keypad 7 and Home 91 */
+ '8', /* 96 60 Keypad 8 and Up Arrow 96 */
+ '9', /* 97 61 Keypad 9 and PageUp 101 */
+ '0', /* 98 62 Keypad 0 and Insert 99 */
+ '.', /* 99 63 Keypad . and Delete 104 */
+ '|' /* 100 64 Keyboard Non-US \ and | 45 */
+};
+
+/***********************************/
+/* Keycodes for 1 byte translation */
+/* - CONTROL-KEY pressed - */
+/***********************************/
+const uint8_t keycodes_alt_GR[] = {
+ 0, /* 0 00 Reserved (no event indicated) */
+ 0, /* 1 01 Keyboard ErrorRollOver */
+ 0, /* 2 02 Keyboard POSTFail */
+ 0, /* 3 03 Keyboard ErrorUndefined */
+ 0, /* 4 04 Keyboard a and A 31 */
+ 0, /* 5 05 Keyboard b and B 50 */
+ 0, /* 6 06 Keyboard c and C 48 */
+ 0, /* 7 07 Keyboard d and D 33 */
+ 0, /* 8 08 Keyboard e and E 19 */
+ 0, /* 9 09 Keyboard f and F 34 */
+ 0, /* 10 0A Keyboard g and G 35 */
+ 0, /* 11 0B Keyboard h and H 36 */
+ 0, /* 12 0C Keyboard i and I 24 */
+ 0, /* 13 0D Keyboard j and J 37 */
+ 0, /* 14 0E Keyboard k and K 38 */
+ 0, /* 15 0F Keyboard l and L 39 */
+ 0, /* 16 10 Keyboard m and M 52 */
+ 0, /* 17 11 Keyboard n and N 51 */
+ 0, /* 18 12 Keyboard o and O 25 */
+ 0, /* 19 13 Keyboard p and P 26 */
+ '@', /* 20 14 Keyboard q and Q 17 */
+ 0, /* 21 15 Keyboard r and R 20 */
+ 0, /* 22 16 Keyboard s and S 32 */
+ 0, /* 23 17 Keyboard t and T 21 */
+ 0, /* 24 18 Keyboard u and U 23 */
+ 0, /* 25 19 Keyboard v and V 49 */
+ 0, /* 26 1A Keyboard w and W 18 */
+ 0, /* 27 1B Keyboard x and X 47 */
+ 0, /* 28 1C Keyboard y and Y 22 */
+ 0, /* 29 1D Keyboard z and Z 46 */
+ 0, /* 30 1E Keyboard 1 and ! 2 */
+ 0, /* 31 1F Keyboard 2 and @ 3 */
+ 0, /* 32 20 Keyboard 3 and # 4 */
+ 0, /* 33 21 Keyboard 4 and $ 5 */
+ 0, /* 34 22 Keyboard 5 and % 6 */
+ 0, /* 35 23 Keyboard 6 and ^ 7 */
+ '{', /* 36 24 Keyboard 7 and & 8 */
+ '[', /* 37 25 Keyboard 8 and * 9 */
+ ']', /* 38 26 Keyboard 9 and ( 10 */
+ '}', /* 39 27 Keyboard 0 and ) 11 */
+ 0, /* 40 28 Keyboard Return (ENTER) 43 */
+ 0, /* 41 29 Keyboard ESCAPE 110 */
+ 0, /* 42 2A Keyboard DELETE (BS) 15 */
+ 0, /* 43 2B Keyboard Tab 16 */
+ 0, /* 44 2C Keyboard Spacebar 61 */
+ '\\', /* 45 2D Keyboard - and (underscore) 12 */
+ 0, /* 46 2E Keyboard = and + 13 */
+ 0, /* 47 2F Keyboard [ and { 27 */
+ '~', /* 48 30 Keyboard ] and } 28 */
+ 0, /* 49 31 Keyboard \ and | 29 */
+ 0, /* 50 32 Keyboard Non-US # and ~ 42 */
+ 0, /* 51 33 Keyboard ; and : 40 */
+ 0, /* 52 34 Keyboard ' and " 41 */
+ 0, /* 53 35 Keyboard Grave Accent and Tilde 1 */
+ 0, /* 54 36 Keyboard , and < 53 */
+ 0, /* 55 37 Keyboard . and > 54 */
+ 0, /* 56 38 Keyboard / and ? 55 */
+ 0, /* 57 39 Keyboard Caps Lock 30 */
+ 0, /* 58 3A Keyboard F1 112 */
+ 0, /* 59 3B Keyboard F2 113 */
+ 0, /* 60 3C Keyboard F3 114 */
+ 0, /* 61 3D Keyboard F4 115 */
+ 0, /* 62 3E Keyboard F5 116 */
+ 0, /* 63 3F Keyboard F6 117 */
+ 0, /* 64 40 Keyboard F7 118 */
+ 0, /* 65 41 Keyboard F8 119 */
+ 0, /* 66 42 Keyboard F9 120 */
+ 0, /* 67 43 Keyboard F10 121 */
+ 0, /* 68 44 Keyboard F11 122 */
+ 0, /* 69 45 Keyboard F12 123 */
+ 0, /* 70 46 Keyboard PrintScreen 124 */
+ 0, /* 71 47 Keyboard Scroll Lock 125 */
+ 0, /* 72 48 Keyboard Pause 126 */
+ 0, /* 73 49 Keyboard Insert 75 */
+ 0, /* 74 4A Keyboard Home 80 */
+ 0, /* 75 4B Keyboard PageUp 85 */
+ 0, /* 76 4C Keyboard Delete Forward 76 */
+ 0, /* 77 4D Keyboard End 81 */
+ 0, /* 78 4E Keyboard PageDown 86 */
+ 0, /* 79 4F Keyboard RightArrow 89 */
+ 0, /* 80 50 Keyboard LeftArrow 79 */
+ 0, /* 81 51 Keyboard DownArrow 84 */
+ 0, /* 82 52 Keyboard UpArrow 83 */
+ 0, /* 83 53 Keypad Num Lock and Clear 90 */
+ 0, /* 84 54 Keypad / 95 */
+ 0, /* 85 55 Keypad * 100 */
+ 0, /* 86 56 Keypad - 105 */
+ 0, /* 87 57 Keypad + 106 */
+ 0, /* 88 58 Keypad ENTER 108 */
+ 0, /* 89 59 Keypad 1 and End 93 */
+ 0, /* 90 5A Keypad 2 and Down Arrow 98 */
+ 0, /* 91 5B Keypad 3 and PageDn 103 */
+ 0, /* 92 5C Keypad 4 and Left Arrow 92 */
+ 0, /* 93 5D Keypad 5 97 */
+ 0, /* 94 5E Keypad 6 and Right Arrow 102 */
+ 0, /* 95 5F Keypad 7 and Home 91 */
+ 0, /* 96 60 Keypad 8 and Up Arrow 96 */
+ 0, /* 97 61 Keypad 9 and PageUp 101 */
+ 0, /* 98 62 Keypad 0 and Insert 99 */
+ 0, /* 99 63 Keypad . and Delete 104 */
+ '|' /* 100 64 Keyboard Non-US \ and | 45 */
+};
+
+
+/***********************************/
+/* Keycodes for 1 byte translation */
+/* - CONTROL-KEY pressed - */
+/***********************************/
+const uint8_t keycodes_ctrl[] = {
+ 0, /* 0 00 Reserved (no event indicated) */
+ 0, /* 1 01 Keyboard ErrorRollOver */
+ 0, /* 2 02 Keyboard POSTFail */
+ 0, /* 3 03 Keyboard ErrorUndefined */
+ 1, /* 4 04 Keyboard a and A 31 */
+ 2, /* 5 05 Keyboard b and B 50 */
+ 3, /* 6 06 Keyboard c and C 48 */
+ 4, /* 7 07 Keyboard d and D 33 */
+ 5, /* 8 08 Keyboard e and E 19 */
+ 6, /* 9 09 Keyboard f and F 34 */
+ 7, /* 10 0A Keyboard g and G 35 */
+ 8, /* 11 0B Keyboard h and H 36 */
+ 9, /* 12 0C Keyboard i and I 24 */
+ 10, /* 13 0D Keyboard j and J 37 */
+ 11, /* 14 0E Keyboard k and K 38 */
+ 12, /* 15 0F Keyboard l and L 39 */
+ 13, /* 16 10 Keyboard m and M 52 */
+ 14, /* 17 11 Keyboard n and N 51 */
+ 15, /* 18 12 Keyboard o and O 25 */
+ 16, /* 19 13 Keyboard p and P 26 */
+ 17, /* 20 14 Keyboard q and Q 17 */
+ 18, /* 21 15 Keyboard r and R 20 */
+ 19, /* 22 16 Keyboard s and S 32 */
+ 20, /* 23 17 Keyboard t and T 21 */
+ 21, /* 24 18 Keyboard u and U 23 */
+ 22, /* 25 19 Keyboard v and V 49 */
+ 23, /* 26 1A Keyboard w and W 18 */
+ 24, /* 27 1B Keyboard x and X 47 */
+ 25, /* 28 1C Keyboard y and Y 22 */
+ 26, /* 29 1D Keyboard z and Z 46 */
+ 0, /* 30 1E Keyboard 1 and ! 2 */
+ 0, /* 31 1F Keyboard 2 and @ 3 */
+ 0, /* 32 20 Keyboard 3 and # 4 */
+ 0, /* 33 21 Keyboard 4 and $ 5 */
+ 0, /* 34 22 Keyboard 5 and % 6 */
+ 0, /* 35 23 Keyboard 6 and ^ 7 */
+ 0, /* 36 24 Keyboard 7 and & 8 */
+ 0, /* 37 25 Keyboard 8 and * 9 */
+ 0, /* 38 26 Keyboard 9 and ( 10 */
+ 0, /* 39 27 Keyboard 0 and ) 11 */
+ 0, /* 40 28 Keyboard Return (ENTER) 43 */
+ 0, /* 41 29 Keyboard ESCAPE 110 */
+ 0, /* 42 2A Keyboard DELETE (BS) 15 */
+ 0, /* 43 2B Keyboard Tab 16 */
+ 0, /* 44 2C Keyboard Spacebar 61 */
+ 0, /* 45 2D Keyboard - and (underscore) 12 */
+ 0, /* 46 2E Keyboard = and + 13 */
+ 0, /* 47 2F Keyboard [ and { 27 */
+ 0, /* 48 30 Keyboard ] and } 28 */
+ 0, /* 49 31 Keyboard \ and | 29 */
+ 0, /* 50 32 Keyboard Non-US # and ~ 42 */
+ 0, /* 51 33 Keyboard ; and : 40 */
+ 0, /* 52 34 Keyboard ' and " 41 */
+ 0, /* 53 35 Keyboard Grave Accent and Tilde 1 */
+ 0, /* 54 36 Keyboard , and < 53 */
+ 0, /* 55 37 Keyboard . and > 54 */
+ 0, /* 56 38 Keyboard / and ? 55 */
+ 0, /* 57 39 Keyboard Caps Lock 30 */
+ 0, /* 58 3A Keyboard F1 112 */
+ 0, /* 59 3B Keyboard F2 113 */
+ 0, /* 60 3C Keyboard F3 114 */
+ 0, /* 61 3D Keyboard F4 115 */
+ 0, /* 62 3E Keyboard F5 116 */
+ 0, /* 63 3F Keyboard F6 117 */
+ 0, /* 64 40 Keyboard F7 118 */
+ 0, /* 65 41 Keyboard F8 119 */
+ 0, /* 66 42 Keyboard F9 120 */
+ 0, /* 67 43 Keyboard F10 121 */
+ 0, /* 68 44 Keyboard F11 122 */
+ 0, /* 69 45 Keyboard F12 123 */
+ 0, /* 70 46 Keyboard PrintScreen 124 */
+ 0, /* 71 47 Keyboard Scroll Lock 125 */
+ 0, /* 72 48 Keyboard Pause 126 */
+ 0, /* 73 49 Keyboard Insert 75 */
+ 0, /* 74 4A Keyboard Home 80 */
+ 0, /* 75 4B Keyboard PageUp 85 */
+ 0, /* 76 4C Keyboard Delete Forward 76 */
+ 0, /* 77 4D Keyboard End 81 */
+ 0, /* 78 4E Keyboard PageDown 86 */
+ 0, /* 79 4F Keyboard RightArrow 89 */
+ 0, /* 80 50 Keyboard LeftArrow 79 */
+ 0, /* 81 51 Keyboard DownArrow 84 */
+ 0, /* 82 52 Keyboard UpArrow 83 */
+ 0, /* 83 53 Keypad Num Lock and Clear 90 */
+ 0, /* 84 54 Keypad / 95 */
+ 0, /* 85 55 Keypad * 100 */
+ 0, /* 86 56 Keypad - 105 */
+ 0, /* 87 57 Keypad + 106 */
+ 0, /* 88 58 Keypad ENTER 108 */
+ 0, /* 89 59 Keypad 1 and End 93 */
+ 0, /* 90 5A Keypad 2 and Down Arrow 98 */
+ 0, /* 91 5B Keypad 3 and PageDn 103 */
+ 0, /* 92 5C Keypad 4 and Left Arrow 92 */
+ 0, /* 93 5D Keypad 5 97 */
+ 0, /* 94 5E Keypad 6 and Right Arrow 102 */
+ 0, /* 95 5F Keypad 7 and Home 91 */
+ 0, /* 96 60 Keypad 8 and Up Arrow 96 */
+ 0, /* 97 61 Keypad 9 and PageUp 101 */
+ 0, /* 98 62 Keypad 0 and Insert 99 */
+ 0, /* 99 63 Keypad . and Delete 104 */
+ 0 /* 100 64 Keyboard Non-US \ and | 45 */
+};
diff --git a/roms/SLOF/lib/libusb/usb-key.h b/roms/SLOF/lib/libusb/usb-key.h
new file mode 100644
index 000000000..1871a9956
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-key.h
@@ -0,0 +1,42 @@
+#ifndef _USB_KEYB_H
+#define _USB_KEYB_H
+
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#define BIT_0 1
+#define BIT_1 (BIT_0 << 1)
+#define BIT_2 (BIT_0 << 2)
+#define BIT_3 (BIT_0 << 3)
+#define BIT_4 (BIT_0 << 4)
+#define BIT_5 (BIT_0 << 5)
+#define BIT_6 (BIT_0 << 6)
+#define BIT_7 (BIT_0 << 7)
+
+/* bits from modifier input */
+#define MODIFIER_CTRL (BIT_0 | BIT_4)
+#define MODIFIER_SHIFT (BIT_1 | BIT_5)
+#define MODIFIER_ALT (BIT_2 | BIT_6)
+#define MODIFIER_GUI (BIT_3 | BIT_7)
+#define MODIFIER_ALT_GR BIT_6
+
+/* bits representing Keyboard-LEDs */
+#define LED_NUM_LOCK BIT_0
+#define LED_CAPS_LOCK BIT_1
+#define LED_SCROLL_LOCK BIT_2
+
+extern const uint8_t keycodes_std_US[];
+extern const uint8_t keycodes_shift_US[];
+extern const uint8_t keycodes_alt_GR[];
+extern const uint8_t keycodes_ctrl[];
+
+#endif
diff --git a/roms/SLOF/lib/libusb/usb-ohci.c b/roms/SLOF/lib/libusb/usb-ohci.c
new file mode 100644
index 000000000..3f2ecf327
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-ohci.c
@@ -0,0 +1,1055 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include <byteorder.h>
+#include "usb.h"
+#include "usb-core.h"
+#include "usb-ohci.h"
+
+#undef OHCI_DEBUG
+//#define OHCI_DEBUG
+#ifdef OHCI_DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...) do {} while (0)
+#endif
+
+#undef OHCI_DEBUG_PACKET
+//#define OHCI_DEBUG_PACKET
+#ifdef OHCI_DEBUG_PACKET
+#define dpprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dpprintf(_x ...) do {} while (0)
+#endif
+
+
+/*
+ * Dump OHCI register
+ *
+ * @param - ohci_hcd
+ * @return -
+ */
+static void ohci_dump_regs(struct ohci_regs *regs)
+{
+ dprintf("\n - HcRevision %08X", read_reg32(&regs->rev));
+ dprintf(" - HcControl %08X", read_reg32(&regs->control));
+ dprintf("\n - HcCommandStatus %08X", read_reg32(&regs->cmd_status));
+ dprintf(" - HcInterruptStatus %08X", read_reg32(&regs->intr_status));
+ dprintf("\n - HcInterruptEnable %08X", read_reg32(&regs->intr_enable));
+ dprintf(" - HcInterruptDisable %08X", read_reg32(&regs->intr_disable));
+ dprintf("\n - HcHCCA %08X", read_reg32(&regs->hcca));
+ dprintf(" - HcPeriodCurrentED %08X", read_reg32(&regs->period_curr_ed));
+ dprintf("\n - HcControlHeadED %08X", read_reg32(&regs->cntl_head_ed));
+ dprintf(" - HcControlCurrentED %08X", read_reg32(&regs->cntl_curr_ed));
+ dprintf("\n - HcBulkHeadED %08X", read_reg32(&regs->bulk_head_ed));
+ dprintf(" - HcBulkCurrentED %08X", read_reg32(&regs->bulk_curr_ed));
+ dprintf("\n - HcDoneHead %08X", read_reg32(&regs->done_head));
+ dprintf(" - HcFmInterval %08X", read_reg32(&regs->fm_interval));
+ dprintf("\n - HcFmRemaining %08X", read_reg32(&regs->fm_remaining));
+ dprintf(" - HcFmNumber %08X", read_reg32(&regs->fm_num));
+ dprintf("\n - HcPeriodicStart %08X", read_reg32(&regs->period_start));
+ dprintf(" - HcLSThreshold %08X", read_reg32(&regs->ls_threshold));
+ dprintf("\n - HcRhDescriptorA %08X", read_reg32(&regs->rh_desc_a));
+ dprintf(" - HcRhDescriptorB %08X", read_reg32(&regs->rh_desc_b));
+ dprintf("\n - HcRhStatus %08X", read_reg32(&regs->rh_status));
+ dprintf("\n");
+}
+
+/*
+ * OHCI Spec 5.1.1
+ * OHCI Spec 7.4 Root Hub Partition
+ */
+static int ohci_hcd_reset(struct ohci_regs *regs)
+{
+ uint32_t time;
+
+ /* USBRESET - 1sec */
+ write_reg32(&regs->control, 0);
+ SLOF_msleep(100);
+
+ write_reg32(&regs->intr_disable, ~0);
+ write_reg32(&regs->cmd_status, OHCI_CMD_STATUS_HCR);
+ mb();
+
+ time = 30; /* wait for not more than 30usec */
+ while ((read_reg32(&regs->cmd_status) & OHCI_CMD_STATUS_HCR) != 0) {
+ time--;
+ if (!time) {
+ printf(" ** HCD Reset failed...");
+ return -1;
+ }
+ SLOF_usleep(1);
+ }
+ return 0;
+}
+
+static int ohci_hcd_init(struct ohci_hcd *ohcd)
+{
+ struct ohci_regs *regs;
+ struct ohci_ed *ed;
+ long ed_phys = 0;
+ unsigned int i;
+ uint32_t oldrwc;
+ struct usb_dev *rhdev = NULL;
+ struct usb_ep_descr ep;
+ uint32_t reg;
+
+ if (!ohcd)
+ return -1;
+
+ regs = ohcd->regs;
+ rhdev = &ohcd->rhdev;
+ dprintf("%s: HCCA memory %p\n", __func__, ohcd->hcca);
+ dprintf("%s: OHCI Regs %p\n", __func__, regs);
+
+ rhdev->hcidev = ohcd->hcidev;
+ ep.bmAttributes = USB_EP_TYPE_INTR;
+ ep.wMaxPacketSize = 8;
+ rhdev->intr = usb_get_pipe(rhdev, &ep, NULL, 0);
+ if (!rhdev->intr) {
+ printf("usb-ohci: oops could not allocate intr_pipe\n");
+ return -1;
+ }
+
+ /*
+ * OHCI Spec 4.4: Host Controller Communications Area
+ */
+ ed = ohci_pipe_get_ed(rhdev->intr);
+ ed_phys = ohci_pipe_get_ed_phys(rhdev->intr);
+ memset(ohcd->hcca, 0, HCCA_SIZE);
+ memset(ed, 0, sizeof(struct ohci_ed));
+ ed->attr = cpu_to_le32(EDA_SKIP);
+ for (i = 0; i < HCCA_INTR_NUM; i++)
+ ohcd->hcca->intr_table[i] = cpu_to_le32(ed_phys);
+
+ write_reg32(&regs->hcca, ohcd->hcca_phys);
+ write_reg32(&regs->cntl_head_ed, 0);
+ write_reg32(&regs->bulk_head_ed, 0);
+
+ /* OHCI Spec 7.1.2 HcControl Register */
+ oldrwc = read_reg32(&regs->control) & OHCI_CTRL_RWC;
+ write_reg32(&regs->control, (OHCI_CTRL_CBSR | OHCI_CTRL_CLE |
+ OHCI_CTRL_BLE | OHCI_CTRL_PLE |
+ OHCI_USB_OPER | oldrwc));
+ SLOF_msleep(100);
+ /*
+ * For JS20/21 need to rewrite it after setting it to
+ * operational state
+ */
+ write_reg32(&regs->fm_interval, FRAME_INTERVAL);
+ write_reg32(&regs->period_start, PERIODIC_START);
+ reg = read_reg32(&regs->rh_desc_a);
+ reg &= ~( RHDA_PSM_INDIVIDUAL | RHDA_OCPM_PERPORT );
+ reg |= RHDA_NPS_ENABLE;
+ write_reg32(&regs->rh_desc_a, reg);
+ write_reg32(&regs->rh_desc_b, 0);
+ mb();
+ SLOF_msleep(100);
+ ohci_dump_regs(regs);
+ return 0;
+}
+
+/*
+ * OHCI Spec 7.4 Root Hub Partition
+ */
+static void ohci_hub_check_ports(struct ohci_hcd *ohcd)
+{
+ struct ohci_regs *regs;
+ struct usb_dev *dev;
+ unsigned int ports, i, port_status, port_clear = 0;
+
+ regs = ohcd->regs;
+ ports = read_reg32(&regs->rh_desc_a) & RHDA_NDP;
+ write_reg32(&regs->rh_status, RH_STATUS_LPSC);
+ SLOF_msleep(100);
+ dprintf("usb-ohci: ports connected %d\n", ports);
+ for (i = 0; i < ports; i++) {
+ dprintf("usb-ohci: ports scanning %d\n", i);
+ port_status = read_reg32(&regs->rh_ps[i]);
+ if (port_status & RH_PS_CSC) {
+ if (port_status & RH_PS_CCS) {
+ write_reg32(&regs->rh_ps[i], RH_PS_PRS);
+ mb();
+ port_clear |= RH_PS_CSC;
+ dprintf("Start enumerating device\n");
+ SLOF_msleep(100);
+ } else
+ printf("Start removing device\n");
+ }
+ port_status = read_reg32(&regs->rh_ps[i]);
+ if (port_status & RH_PS_PRSC) {
+ port_clear |= RH_PS_PRSC;
+ dev = usb_devpool_get();
+ dprintf("usb-ohci: Device reset, setting up %p\n", dev);
+ dev->hcidev = ohcd->hcidev;
+ if (usb_setup_new_device(dev, i))
+ usb_slof_populate_new_device(dev);
+ else
+ printf("usb-ohci: unable to setup device on port %d\n", i);
+ }
+ if (port_status & RH_PS_PESC) {
+ port_clear |= RH_PS_PESC;
+ dprintf((port_status & RH_PS_PES) ? "enabled\n" : "disabled\n");
+ }
+ if (port_status & RH_PS_PSSC) {
+ port_clear |= RH_PS_PESC;
+ dprintf("suspended\n");
+ }
+ port_clear &= 0xFFFF0000;
+ if (port_clear)
+ write_reg32(&regs->rh_ps[i], port_clear);
+ }
+}
+
+static inline struct ohci_ed *ohci_pipe_get_ed(struct usb_pipe *pipe)
+{
+ struct ohci_pipe *opipe;
+ opipe = container_of(pipe, struct ohci_pipe, pipe);
+ dpprintf("%s: ed is %p\n", __func__, &opipe->ed);
+ return &opipe->ed;
+}
+
+static inline long ohci_pipe_get_ed_phys(struct usb_pipe *pipe)
+{
+ struct ohci_pipe *opipe;
+ opipe = container_of(pipe, struct ohci_pipe, pipe);
+ dpprintf("%s: ed_phys is %x\n", __func__, opipe->ed_phys);
+ return opipe->ed_phys;
+}
+
+static inline struct ohci_pipe *ohci_pipe_get_opipe(struct usb_pipe *pipe)
+{
+ struct ohci_pipe *opipe;
+ opipe = container_of(pipe, struct ohci_pipe, pipe);
+ dpprintf("%s: opipe is %p\n", __func__, opipe);
+ return opipe;
+}
+
+static int ohci_alloc_pipe_pool(struct ohci_hcd *ohcd)
+{
+ struct ohci_pipe *opipe, *curr, *prev;
+ long opipe_phys = 0;
+ unsigned int i, count;
+#ifdef OHCI_DEBUG_PACKET
+ struct usb_pipe *pipe;
+#endif
+
+ dprintf("usb-ohci: %s enter\n", __func__);
+ count = OHCI_PIPE_POOL_SIZE/sizeof(*opipe);
+ ohcd->pool = opipe = SLOF_dma_alloc(OHCI_PIPE_POOL_SIZE);
+ if (!opipe)
+ return false;
+
+ ohcd->pool_phys = opipe_phys = SLOF_dma_map_in(opipe, OHCI_PIPE_POOL_SIZE, true);
+ dprintf("usb-ohci: %s opipe %p, opipe_phys %lx size %ld count %d\n",
+ __func__, opipe, opipe_phys, sizeof(*opipe), count);
+ /* Although an array, link them*/
+ for (i = 0, curr = opipe, prev = NULL; i < count; i++, curr++) {
+ if (prev)
+ prev->pipe.next = &curr->pipe;
+ curr->pipe.next = NULL;
+ prev = curr;
+
+ if (((uint64_t)&curr->ed) % 16)
+ printf("usb-ohci: Warning ED not aligned to 16byte boundary");
+ curr->ed_phys = opipe_phys + (curr - opipe) * sizeof(*curr) +
+ offset_of(struct ohci_pipe, ed);
+ }
+
+ if (!ohcd->freelist)
+ ohcd->freelist = &opipe->pipe;
+ else
+ ohcd->end->next = &opipe->pipe;
+ ohcd->end = &prev->pipe;
+
+#ifdef OHCI_DEBUG_PACKET
+ for (i = 0, pipe = ohcd->freelist; pipe; pipe = pipe->next)
+ dprintf("usb-ohci: %d: pipe cur %p ed %p ed_phys %x\n",
+ i++, pipe, ohci_pipe_get_ed(pipe),
+ ohci_pipe_get_ed_phys(pipe));
+#endif
+
+ dprintf("usb-ohci: %s exit\n", __func__);
+ return true;
+}
+
+static void ohci_init(struct usb_hcd_dev *hcidev)
+{
+ struct ohci_hcd *ohcd;
+
+ printf(" OHCI: initializing\n");
+ dprintf("%s: device base address %p\n", __func__, hcidev->base);
+
+ ohcd = SLOF_alloc_mem(sizeof(struct ohci_hcd));
+ if (!ohcd) {
+ printf("usb-ohci: Unable to allocate memory\n");
+ goto out;
+ }
+
+ hcidev->nextaddr = 1;
+ hcidev->priv = ohcd;
+ memset(ohcd, 0, sizeof(*ohcd));
+ ohcd->hcidev = hcidev;
+ ohcd->freelist = NULL;
+ ohcd->end = NULL;
+ ohcd->regs = (struct ohci_regs *)(hcidev->base);
+ ohcd->hcca = SLOF_dma_alloc(sizeof(struct ohci_hcca));
+ if (!ohcd->hcca || PTR_U32(ohcd->hcca) & HCCA_ALIGN) {
+ printf("usb-ohci: Unable to allocate/unaligned HCCA memory %p\n",
+ ohcd->hcca);
+ goto out_free_hcd;
+ }
+ ohcd->hcca_phys = SLOF_dma_map_in(ohcd->hcca,
+ sizeof(struct ohci_hcca), true);
+ dprintf("usb-ohci: HCCA memory %p HCCA-dev memory %08lx\n",
+ ohcd->hcca, ohcd->hcca_phys);
+
+ ohci_hcd_reset(ohcd->regs);
+ ohci_hcd_init(ohcd);
+ ohci_hub_check_ports(ohcd);
+ return;
+
+out_free_hcd:
+ SLOF_dma_free(ohcd->hcca, sizeof(struct ohci_hcca));
+ SLOF_free_mem(ohcd, sizeof(struct ohci_hcd));
+out:
+ return;
+}
+
+static void ohci_exit(struct usb_hcd_dev *hcidev)
+{
+ struct ohci_hcd *ohcd = NULL;
+
+ dprintf("%s: enter \n", __func__);
+ if (!hcidev && !hcidev->priv)
+ return;
+
+ ohcd = hcidev->priv;
+ write_reg32(&ohcd->regs->control, (OHCI_CTRL_CBSR | OHCI_USB_SUSPEND));
+ SLOF_msleep(20);
+ write_reg32(&ohcd->regs->hcca, cpu_to_le32(0));
+
+ if (ohcd->pool) {
+ SLOF_dma_map_out(ohcd->pool_phys, ohcd->pool, OHCI_PIPE_POOL_SIZE);
+ SLOF_dma_free(ohcd->pool, OHCI_PIPE_POOL_SIZE);
+ }
+ if (ohcd->hcca) {
+ SLOF_dma_map_out(ohcd->hcca_phys, ohcd->hcca, sizeof(struct ohci_hcca));
+ SLOF_dma_free(ohcd->hcca, sizeof(struct ohci_hcca));
+ }
+ SLOF_free_mem(ohcd, sizeof(struct ohci_hcd));
+ return;
+}
+
+static void ohci_detect(void)
+{
+
+}
+
+static void ohci_disconnect(void)
+{
+
+}
+
+#define OHCI_CTRL_TDS 3
+
+static void ohci_fill_td(struct ohci_td *td, long next,
+ long req, size_t size, unsigned int attr)
+{
+ if (size && req) {
+ td->cbp = cpu_to_le32(req);
+ td->be = cpu_to_le32(req + size - 1);
+ } else {
+ td->cbp = 0;
+ td->be = 0;
+ }
+ td->attr = cpu_to_le32(attr);
+ td->next_td = cpu_to_le32(next);
+
+ dpprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n", __func__,
+ le32_to_cpu(td->cbp), le32_to_cpu(td->attr),
+ le32_to_cpu(td->next_td), le32_to_cpu(td->be));
+}
+
+static void ohci_fill_ed(struct ohci_ed *ed, long headp, long tailp,
+ unsigned int attr, long next_ed)
+{
+ ed->attr = cpu_to_le32(attr);
+ ed->headp = cpu_to_le32(headp) | (ed->headp & ~EDA_HEADP_MASK_LE);
+ ed->tailp = cpu_to_le32(tailp);
+ ed->next_ed = cpu_to_le32(next_ed);
+ dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__,
+ le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp),
+ le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr));
+
+}
+
+static long ohci_get_td_phys(struct ohci_td *curr, struct ohci_td *start, long td_phys)
+{
+ dpprintf("position %d\n", curr - start);
+ return td_phys + (curr - start) * sizeof(*start);
+}
+
+static long ohci_get_td_virt(struct ohci_td *curr, struct ohci_td *start, long td_virt, long total_count)
+{
+ dpprintf("position %d\n", curr - start);
+ if ( (curr - start) >= total_count) {
+ /* busted position, should ignore this */
+ return 0;
+ }
+ return td_virt + (curr - start) * sizeof(*start);
+}
+
+/* OHCI Spec: 4.4.2.3 HccaDoneHead*/
+static int ohci_process_done_head(struct ohci_hcd *ohcd,
+ struct ohci_td *td_start,
+ long __td_start_phys, long total_count)
+{
+ struct ohci_hcca *hcca;
+ struct ohci_td *td_phys = NULL, *td_start_phys;
+ struct ohci_td *td, *prev_td = NULL;
+ uint32_t reg = 0, time = 0;
+ int ret = true;
+ long count;
+
+ count = total_count;
+ td_start_phys = (struct ohci_td *) __td_start_phys;
+ hcca = ohcd->hcca;
+ time = SLOF_GetTimer() + USB_TIMEOUT;
+ dpprintf("Claiming %ld\n", count);
+
+again:
+ mb();
+ /* Check if there is an interrupt */
+ reg = read_reg32(&ohcd->regs->intr_status);
+ while(!(reg & OHCI_INTR_STATUS_WD))
+ {
+ if (time < SLOF_GetTimer()) {
+ printf("Timed out waiting for interrupt %x\n", reg);
+ return false;
+ }
+ mb();
+ reg = read_reg32(&ohcd->regs->intr_status);
+ }
+
+ /* Interrupt is there, read from done_head pointer */
+ td_phys = (struct ohci_td *)(uint64_t) le32_to_cpu(hcca->done_head);
+ if (!td_phys) {
+ dprintf("Again td_phys null\n");
+ goto again;
+ }
+ hcca->done_head = 0;
+ mb();
+
+ while (td_phys && (count > 0)) {
+ td = (struct ohci_td *)(uint64_t) ohci_get_td_virt(td_phys,
+ td_start_phys,
+ PTR_U32(td_start),
+ total_count);
+
+ if (!td) {
+ printf("USB: Error TD null %p\n", td_phys);
+ break;
+ }
+ count--;
+ dprintf("Claimed %p(%p) td_start %p count %ld\n",
+ td, td_phys, td_start_phys, count);
+ dpprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n",
+ __func__,
+ le32_to_cpu(td->cbp), le32_to_cpu(td->attr),
+ le32_to_cpu(td->next_td), le32_to_cpu(td->be));
+ mb();
+ reg = (le32_to_cpu(td->attr) & TDA_CC) >> 28;
+ if (reg) {
+ dprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n",
+ __func__,
+ le32_to_cpu(td->cbp), le32_to_cpu(td->attr),
+ le32_to_cpu(td->next_td), le32_to_cpu(td->be));
+ printf("USB: Error %s %p\n", tda_cc_error[reg], td);
+ if (reg > 3) /* Return negative error code */
+ ret = reg * -1;
+ }
+ prev_td = td;
+ td_phys = (struct ohci_td *)(uint64_t) le32_to_cpu(td->next_td);
+ prev_td->attr |= cpu_to_le32(TDA_DONE);
+ prev_td->next_td = 0;
+ mb();
+ }
+ /* clear the WD interrupt status */
+ write_reg32(&ohcd->regs->intr_status, OHCI_INTR_STATUS_WD);
+ mb();
+ read_reg32(&ohcd->regs->intr_status);
+
+ if (count > 0) {
+ dpprintf("Pending count %d\n", count);
+ goto again;
+ }
+ dprintf("TD claims done\n");
+ return ret;
+}
+
+/*
+ * OHCI Spec:
+ * 4.2 Endpoint Descriptor
+ * 4.3.1 General Transfer Descriptor
+ * 5.2.8 Transfer Descriptor Queues
+ */
+static int ohci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data)
+{
+ struct ohci_ed *ed;
+ struct ohci_td *tds, *td, *td_phys;
+ struct ohci_regs *regs;
+ struct ohci_hcd *ohcd;
+ uint32_t datalen;
+ uint32_t dir, attr = 0;
+ uint32_t time;
+ int ret = true, i;
+ long req_phys = 0, data_phys = 0, td_next = 0, td_count = 0;
+ unsigned char *dbuf;
+
+ datalen = le16_to_cpu(req->wLength);
+ dir = (req->bmRequestType & REQT_DIR_IN) ? 1 : 0;
+
+ dprintf("usb-ohci: %s len %d DIR_IN %d\n", __func__, datalen, dir);
+
+ tds = td = (struct ohci_td *) SLOF_dma_alloc(sizeof(*td) * OHCI_CTRL_TDS);
+ td_phys = (struct ohci_td *) SLOF_dma_map_in(td, sizeof(*td) * OHCI_CTRL_TDS, true);
+ memset(td, 0, sizeof(*td) * OHCI_CTRL_TDS);
+
+ req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true);
+ attr = TDA_DP_SETUP | TDA_CC | TDA_TOGGLE_DATA0;
+ td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys));
+ ohci_fill_td(td, td_next, req_phys, sizeof(*req), attr);
+ td++; td_count++;
+
+ if (datalen) {
+ data_phys = SLOF_dma_map_in(data, datalen, true);
+ attr = 0;
+ attr = (dir ? TDA_DP_IN : TDA_DP_OUT) | TDA_TOGGLE_DATA1 | TDA_CC;
+ td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys));
+ ohci_fill_td(td, td_next, data_phys, datalen, attr);
+ td++; td_count++;
+ }
+
+ attr = 0;
+ attr = (dir ? TDA_DP_OUT : TDA_DP_IN) | TDA_CC | TDA_TOGGLE_DATA1;
+ td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys));
+ ohci_fill_td(td, 0, 0, 0, attr);
+ td_count++;
+
+ ed = ohci_pipe_get_ed(pipe);
+ attr = 0;
+ attr = EDA_FADDR(pipe->dev->addr) | EDA_MPS(pipe->mps) | EDA_SKIP;
+ ohci_fill_ed(ed, PTR_U32(td_phys), td_next, attr, 0);
+ ed->tailp = 0; /* HACK */
+ dprintf("usb-ohci: %s - td_start %p td_end %lx req %lx\n", __func__,
+ td_phys, td_next, req_phys);
+ mb();
+ ed->attr &= cpu_to_le32(~EDA_SKIP);
+
+ ohcd = pipe->dev->hcidev->priv;
+ regs = ohcd->regs;
+ write_reg32(&regs->cntl_head_ed, ohci_pipe_get_ed_phys(pipe));
+ mb();
+ write_reg32(&regs->cmd_status, OHCI_CMD_STATUS_CLF);
+
+ time = SLOF_GetTimer() + USB_TIMEOUT;
+ while ((time > SLOF_GetTimer()) &&
+ ((ed->headp & EDA_HEADP_MASK_LE) != ed->tailp))
+ cpu_relax();
+
+ if ((ed->headp & EDA_HEADP_MASK_LE) == ed->tailp) {
+ dprintf("%s: packet sent\n", __func__);
+#ifdef OHCI_DEBUG_PACKET
+ dpprintf("Request: ");
+ dbuf = (unsigned char *)req;
+ for(i = 0; i < 8; i++)
+ printf("%02X ", dbuf[i]);
+ dpprintf("\n");
+ if (datalen) {
+ dbuf = (unsigned char *)data;
+ dpprintf("Reply: ");
+ for(i = 0; i < datalen; i++)
+ printf("%02X ", dbuf[i]);
+ dpprintf("\n");
+ }
+#endif
+ }
+ else {
+ printf("%s: timed out - failed\n", __func__);
+ dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n",
+ __func__,
+ le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp),
+ le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr));
+ printf("Request: ");
+ dbuf = (unsigned char *)req;
+ for(i = 0; i < 8; i++)
+ printf("%02X ", dbuf[i]);
+ printf("\n");
+ }
+ ret = ohci_process_done_head(ohcd, tds, (long)td_phys, td_count);
+ mb();
+ ed->attr |= cpu_to_le32(EDA_SKIP);
+ mb();
+ write_reg32(&regs->cntl_head_ed, 0);
+ write_reg32(&regs->cntl_curr_ed, 0);
+
+ SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req));
+ if (datalen)
+ SLOF_dma_map_out(data_phys, data, datalen);
+ SLOF_dma_map_out(PTR_U32(td_phys), tds, sizeof(*td) * OHCI_CTRL_TDS);
+ SLOF_dma_free(tds, sizeof(*td) * OHCI_CTRL_TDS);
+ return (ret > 0) ? true : false;
+}
+
+static int ohci_transfer_bulk(struct usb_pipe *pipe, void *td_ptr,
+ void *td_phys_ptr, void *data_phys, int datalen)
+{
+ struct ohci_ed *ed;
+ struct ohci_td *td, *tds;
+ struct ohci_regs *regs;
+ struct ohci_hcd *ohcd;
+ long td_phys = 0, td_next, ed_phys, ptr, td_count = 0;
+ uint32_t dir, attr = 0, count;
+ size_t len, packet_len;
+ uint32_t time;
+ int ret = true;
+
+ if (pipe->type != USB_EP_TYPE_BULK) {
+ printf("usb-ohci: Not a bulk pipe.\n");
+ ret = false;
+ goto end;
+ }
+
+ dir = (pipe->dir == USB_PIPE_OUT) ? 0 : 1;
+ count = datalen / OHCI_MAX_BULK_SIZE;
+ if (count > OHCI_MAX_TDS) {
+ printf("usb-ohci: buffer size not supported - %d\n", datalen);
+ ret = false;
+ goto end;
+ }
+
+ td = tds = (struct ohci_td *) td_ptr;
+ td_phys = (long)td_phys_ptr;
+ dprintf("usb-ohci: %s pipe %p data_phys %p len %d DIR_IN %d td %p td_phys %lx\n",
+ __func__, pipe, data_phys, datalen, dir, td, td_phys);
+
+ if (!tds) {
+ printf("%s: tds NULL recieved\n", __func__);
+ ret = false;
+ goto end;
+ }
+ memset(td, 0, sizeof(*td) * OHCI_MAX_TDS);
+
+ len = datalen;
+ ptr = (long)data_phys;
+ attr = 0;
+ attr = (dir ? TDA_DP_IN : TDA_DP_OUT) | TDA_CC | TDA_ROUNDING;
+ while (len) {
+ packet_len = (OHCI_MAX_BULK_SIZE < len)? OHCI_MAX_BULK_SIZE : len;
+ td_next = ohci_get_td_phys((td + 1), tds, td_phys);
+ ohci_fill_td(td, td_next, ptr, packet_len, attr);
+ ptr = ptr + packet_len;
+ len = len - packet_len;
+ td++; td_count++;
+ }
+
+ ed = ohci_pipe_get_ed(pipe);
+ attr = 0;
+ dir = pipe->dir ? EDA_DIR_IN : EDA_DIR_OUT;
+ attr = dir | EDA_FADDR(pipe->dev->addr) | EDA_MPS(pipe->mps)
+ | EDA_SKIP | pipe->dev->speed | EDA_EP(pipe->epno);
+ td_next = ohci_get_td_phys(td, tds, td_phys);
+ ohci_fill_ed(ed, td_phys, td_next, attr, 0);
+ dprintf("usb-ohci: %s - tds %lx td %lx\n", __func__, td_phys, td_next);
+ mb();
+ ed->attr &= cpu_to_le32(~EDA_SKIP);
+
+ ohcd = pipe->dev->hcidev->priv;
+ regs = ohcd->regs;
+ ed_phys = ohci_pipe_get_ed_phys(pipe);
+ write_reg32(&regs->bulk_head_ed, ed_phys);
+ mb();
+ write_reg32(&regs->cmd_status, 0x4);
+
+ time = SLOF_GetTimer() + USB_TIMEOUT;
+ while ((time > SLOF_GetTimer()) &&
+ ((ed->headp & EDA_HEADP_MASK_LE) != ed->tailp))
+ cpu_relax();
+
+ if ((ed->headp & EDA_HEADP_MASK_LE) == ed->tailp)
+ dprintf("%s: packet sent\n", __func__);
+ else {
+ dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__,
+ le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp),
+ le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr));
+ }
+ mb();
+ ret = ohci_process_done_head(ohcd, tds, td_phys, td_count);
+ mb();
+ ed->attr |= cpu_to_le32(EDA_SKIP);
+ mb();
+ write_reg32(&regs->bulk_head_ed, 0);
+ write_reg32(&regs->bulk_curr_ed, 0);
+
+ if (le32_to_cpu(ed->headp) & EDA_HEADP_HALTED) {
+ printf("ED Halted\n");
+ printf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__,
+ le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp),
+ le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr));
+ ed->headp &= ~cpu_to_le32(EDA_HEADP_HALTED);
+ mb();
+ if (ret == USB_STALL) /* Call reset recovery */
+ usb_msc_resetrecovery(pipe->dev);
+ }
+
+end:
+ return (ret > 0) ? true : false;
+}
+
+/* Populate the hcca intr region with periodic intr */
+static int ohci_get_pipe_intr(struct usb_pipe *pipe, struct ohci_hcd *ohcd,
+ char *buf, size_t buflen)
+{
+ struct ohci_hcca *hcca;
+ struct ohci_pipe *opipe;
+ struct ohci_ed *ed;
+ struct usb_dev *dev;
+ struct ohci_td *tds, *td;
+ int32_t count = 0, i;
+ uint8_t *ptr;
+ uint16_t mps;
+ long ed_phys, td_phys, td_next, buf_phys;
+
+ if (!pipe || !ohcd)
+ return false;
+
+ hcca = ohcd->hcca;
+ dev = pipe->dev;
+ if (dev->class != DEV_HID_KEYB && dev->class != DEV_HUB)
+ return false;
+
+ opipe = ohci_pipe_get_opipe(pipe);
+ ed = &(opipe->ed);
+ ed_phys = opipe->ed_phys;
+ mps = pipe->mps;
+ ed->attr = cpu_to_le32(EDA_DIR_IN |
+ EDA_FADDR(dev->addr) |
+ dev->speed |
+ EDA_MPS(pipe->mps) |
+ EDA_SKIP |
+ EDA_EP(pipe->epno));
+ dprintf("%s: pipe %p ed %p dev %p opipe %p\n", __func__,
+ pipe, ed, dev, opipe);
+ count = (buflen/mps) + 1;
+ tds = td = SLOF_dma_alloc(sizeof(*td) * count);
+ if (!tds) {
+ printf("%s: alloc failed\n", __func__);
+ return false;
+ }
+ td_phys = SLOF_dma_map_in(td, sizeof(*td) * count, false);
+
+ memset(tds, 0, sizeof(*tds) * count);
+ memset(buf, 0, buflen);
+ buf_phys = SLOF_dma_map_in(buf, buflen, false);
+ opipe->td = td;
+ opipe->td_phys = td_phys;
+ opipe->count = count;
+ opipe->buf = buf;
+ opipe->buflen = buflen;
+ opipe->buf_phys = buf_phys;
+
+ ptr = (uint8_t *)buf_phys;
+ for (i = 0; i < count - 1; i++, ptr += mps) {
+ td = &tds[i];
+ td_next = ohci_get_td_phys(td + 1, &tds[0], td_phys);
+ td->cbp = cpu_to_le32(PTR_U32(ptr));
+ td->attr = cpu_to_le32(TDA_DP_IN | TDA_ROUNDING | TDA_CC);
+ td->next_td = cpu_to_le32(td_next);
+ td->be = cpu_to_le32(PTR_U32(ptr) + mps - 1);
+ dprintf("td %p td++ %x ptr %p be %x\n",
+ td, le32_to_cpu(td->next_td),
+ ptr, (PTR_U32(ptr) + mps - 1));
+ }
+ td->next_td = 0;
+ td_next = ohci_get_td_phys(td, &tds[0], td_phys);
+ ed->headp = cpu_to_le32(td_phys);
+ ed->tailp = cpu_to_le32(td_next);
+
+ dprintf("%s: head %08X tail %08X, count %d, mps %d\n", __func__,
+ le32_to_cpu(ed->headp),
+ le32_to_cpu(ed->tailp),
+ count, mps);
+ ed->next_ed = 0;
+
+
+ switch (dev->class) {
+ case DEV_HID_KEYB:
+ dprintf("%s: Keyboard class %d\n", __func__, dev->class);
+ hcca->intr_table[0] = cpu_to_le32(ed_phys);
+ hcca->intr_table[8] = cpu_to_le32(ed_phys);
+ hcca->intr_table[16] = cpu_to_le32(ed_phys);
+ hcca->intr_table[24] = cpu_to_le32(ed_phys);
+ ed->attr &= cpu_to_le32(~EDA_SKIP);
+ break;
+
+ case DEV_HUB:
+ dprintf("%s: HUB class %x\n", __func__, dev->class);
+ hcca->intr_table[1] = cpu_to_le32(ed_phys);
+ ed->attr &= cpu_to_le32(~EDA_SKIP);
+ break;
+
+ default:
+ dprintf("%s: unhandled class %d\n", __func__, dev->class);
+ }
+ return true;
+}
+
+static int ohci_put_pipe_intr(struct usb_pipe *pipe, struct ohci_hcd *ohcd)
+{
+ struct ohci_hcca *hcca;
+ struct ohci_pipe *opipe;
+ struct ohci_ed *ed;
+ struct usb_dev *dev;
+ struct ohci_td *td;
+ long ed_phys;
+
+ if (!pipe || !ohcd)
+ return false;
+
+ hcca = ohcd->hcca;
+ dev = pipe->dev;
+
+ if (dev->class != DEV_HID_KEYB && dev->class != DEV_HUB)
+ return false;
+
+ opipe = ohci_pipe_get_opipe(pipe);
+ ed = &(opipe->ed);
+ ed_phys = opipe->ed_phys;
+ dprintf("%s: td %p td_phys %08lx buf %p buf_phys %08lx\n", __func__,
+ opipe->td, opipe->td_phys, opipe->buf, opipe->buf_phys);
+
+ ed->attr |= cpu_to_le32(EDA_SKIP);
+ mb();
+ ed->headp = 0;
+ ed->tailp = 0;
+ ed->next_ed = 0;
+ SLOF_dma_map_out(opipe->buf_phys, opipe->buf, opipe->buflen);
+ SLOF_dma_map_out(opipe->td_phys, opipe->td, sizeof(*td) * opipe->count);
+ SLOF_dma_free(opipe->td, sizeof(*td) * opipe->count);
+
+ switch (dev->class) {
+ case DEV_HID_KEYB:
+ dprintf("%s: Keyboard class %d\n", __func__, dev->class);
+ hcca->intr_table[0] = cpu_to_le32(ed_phys);
+ hcca->intr_table[8] = cpu_to_le32(ed_phys);
+ hcca->intr_table[16] = cpu_to_le32(ed_phys);
+ hcca->intr_table[24] = cpu_to_le32(ed_phys);
+ break;
+
+ case DEV_HUB:
+ dprintf("%s: HUB class %d\n", __func__, dev->class);
+ hcca->intr_table[1] = cpu_to_le32(ed_phys);
+ break;
+
+ default:
+ dprintf("%s: unhandled class %d\n", __func__, dev->class);
+ }
+ return true;
+}
+
+static int ohci_init_bulk_ed(struct usb_dev *dev, struct usb_pipe *pipe)
+{
+ struct ohci_pipe *opipe;
+ struct ohci_ed *ed;
+ uint32_t dir;
+
+ if (!pipe || !dev)
+ return false;
+
+ opipe = ohci_pipe_get_opipe(pipe);
+ ed = &(opipe->ed);
+ dir = pipe->dir ? EDA_DIR_IN : EDA_DIR_OUT;
+
+ ed->attr = cpu_to_le32(dir |
+ EDA_FADDR(dev->addr) |
+ dev->speed |
+ EDA_MPS(pipe->mps) |
+ EDA_SKIP |
+ EDA_EP(pipe->epno));
+
+ dprintf("%s: pipe %p attr %x\n", __func__, pipe,
+ le32_to_cpu(ed->attr));
+ return true;
+}
+
+static struct usb_pipe *ohci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
+ char *buf, size_t buflen)
+{
+ struct ohci_hcd *ohcd;
+ struct usb_pipe *new = NULL;
+
+ dprintf("usb-ohci: %s enter %p\n", __func__, dev);
+ if (!dev)
+ return NULL;
+
+ ohcd = (struct ohci_hcd *)dev->hcidev->priv;
+ if (!ohcd->freelist) {
+ dprintf("usb-ohci: %s allocating pool\n", __func__);
+ if (!ohci_alloc_pipe_pool(ohcd))
+ return NULL;
+ }
+
+ new = ohcd->freelist;
+ ohcd->freelist = ohcd->freelist->next;
+ if (!ohcd->freelist)
+ ohcd->end = NULL;
+
+ memset(new, 0, sizeof(*new));
+ new->dev = dev;
+ new->next = NULL;
+ new->type = ep->bmAttributes & USB_EP_TYPE_MASK;
+ new->speed = dev->speed;
+ new->mps = le16_to_cpu(ep->wMaxPacketSize);
+ new->epno = ep->bEndpointAddress & 0xF;
+ new->dir = ep->bEndpointAddress & 0x80;
+ if (new->type == USB_EP_TYPE_INTR)
+ if (!ohci_get_pipe_intr(new, ohcd, buf, buflen)) {
+ dprintf("usb-ohci: %s alloc_intr failed %p\n",
+ __func__, new);
+ }
+ if (new->type == USB_EP_TYPE_BULK)
+ ohci_init_bulk_ed(dev, new);
+
+ dprintf("usb-ohci: %s exit %p\n", __func__, new);
+ return new;
+}
+
+static void ohci_put_pipe(struct usb_pipe *pipe)
+{
+ struct ohci_hcd *ohcd;
+
+ dprintf("usb-ohci: %s enter - %p\n", __func__, pipe);
+ if (!pipe || !pipe->dev)
+ return;
+ ohcd = pipe->dev->hcidev->priv;
+ if (ohcd->end)
+ ohcd->end->next = pipe;
+ else
+ ohcd->freelist = pipe;
+
+ if (pipe->type == USB_EP_TYPE_INTR)
+ if (!ohci_put_pipe_intr(pipe, ohcd))
+ dprintf("usb-ohci: %s alloc_intr failed %p\n",
+ __func__, pipe);
+
+ ohcd->end = pipe;
+ pipe->next = NULL;
+ pipe->dev = NULL;
+ memset(pipe, 0, sizeof(*pipe));
+ dprintf("usb-ohci: %s exit\n", __func__);
+}
+
+static uint16_t ohci_get_last_frame(struct usb_dev *dev)
+{
+ struct ohci_hcd *ohcd;
+ struct ohci_regs *regs;
+
+ ohcd = dev->hcidev->priv;
+ regs = ohcd->regs;
+ return read_reg32(&regs->fm_num);
+}
+
+static int ohci_poll_intr(struct usb_pipe *pipe, uint8_t *data)
+{
+ struct ohci_pipe *opipe;
+ struct ohci_ed *ed;
+ struct ohci_td *head, *tail, *curr, *next;
+ struct ohci_td *head_phys, *tail_phys, *curr_phys;
+ uint8_t *ptr = NULL;
+ unsigned int i, pos;
+ static uint16_t last_frame;
+ long ptr_phys = 0;
+ long td_next;
+
+ if (!pipe || last_frame == ohci_get_last_frame(pipe->dev))
+ return 0;
+
+ dprintf("%s: enter\n", __func__);
+
+ last_frame = ohci_get_last_frame(pipe->dev);
+ opipe = ohci_pipe_get_opipe(pipe);
+ ed = &opipe->ed;
+
+ head_phys = (struct ohci_td *)(long)(le32_to_cpu(ed->headp) & EDA_HEADP_MASK);
+ tail_phys = (struct ohci_td *)(long)le32_to_cpu(ed->tailp);
+ curr_phys = (struct ohci_td *) opipe->td_phys;
+ pos = (tail_phys - curr_phys + 1) % (opipe->count - 1);
+ dprintf("pos %d %ld -- %d\n", pos, (tail_phys - curr_phys + 1),
+ opipe->count);
+ curr = opipe->td + pos;
+ head = opipe->td + (head_phys - (struct ohci_td *) opipe->td_phys);
+ tail = opipe->td + (tail_phys - (struct ohci_td *) opipe->td_phys);
+
+ /* dprintf("%08X %08X %08X %08X\n",
+ opipe->td_phys, head_phys, tail_phys, curr_phys);
+ dprintf("%08X %08X %08X %08X\n", opipe->td, head, tail, curr); */
+
+ if (curr != head) {
+ ptr = (uint8_t *) ((long)opipe->buf + pipe->mps * pos);
+ ptr_phys = opipe->buf_phys + pipe->mps * pos;
+ if (le32_to_cpu(*(uint32_t *)ptr) != 0) {
+ for (i = 0; i < 8; i++)
+ data[i] = *(ptr + i);
+ }
+
+ next = curr + 1;
+ if (next == (opipe->td + opipe->count - 1))
+ next = opipe->td;
+
+ curr->attr = cpu_to_le32(TDA_DP_IN | TDA_ROUNDING | TDA_CC);
+ curr->next_td = cpu_to_le32(0);
+ curr->cbp = cpu_to_le32(PTR_U32(ptr_phys));
+ curr->be = cpu_to_le32(PTR_U32(ptr_phys + pipe->mps - 1));
+ td_next = ohci_get_td_phys(curr, opipe->td, opipe->td_phys);
+ dprintf("Connecting %p to %p(phys %08lx) ptr %p, "
+ "ptr_phys %08lx\n", tail, curr, td_next, ptr, ptr_phys);
+ tail->next_td = cpu_to_le32(td_next);
+ mb();
+ ed->tailp = cpu_to_le32(td_next);
+ } else
+ return 0;
+
+ dprintf("%s: exit\n", __func__);
+ return 1;
+}
+
+struct usb_hcd_ops ohci_ops = {
+ .name = "ohci-hcd",
+ .init = ohci_init,
+ .exit = ohci_exit,
+ .detect = ohci_detect,
+ .disconnect = ohci_disconnect,
+ .get_pipe = ohci_get_pipe,
+ .put_pipe = ohci_put_pipe,
+ .send_ctrl = ohci_send_ctrl,
+ .transfer_bulk = ohci_transfer_bulk,
+ .poll_intr = ohci_poll_intr,
+ .usb_type = USB_OHCI,
+ .next = NULL,
+};
+
+void usb_ohci_register(void)
+{
+ usb_hcd_register(&ohci_ops);
+}
diff --git a/roms/SLOF/lib/libusb/usb-ohci.h b/roms/SLOF/lib/libusb/usb-ohci.h
new file mode 100644
index 000000000..a37363477
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-ohci.h
@@ -0,0 +1,217 @@
+/******************************************************************************
+ * Copyright (c) 2007, 2012, 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * Definitions for OHCI Controller
+ *
+ * USB on the PowerStation:
+ * ohci0 - port 0 -> not connected
+ * ohci0 - port 1 - 2 -> Internal connector (J60_USBINT)
+ * ohci1 - port 0 -> not connected
+ * ohci1 - port 1 - 2 -> External connector (J10_USBEXT)
+ */
+
+#ifndef USB_OHCI_H
+#define USB_OHCI_H
+
+#include <stdint.h>
+
+struct ohci_regs {
+ uint32_t rev;
+ uint32_t control;
+ uint32_t cmd_status;
+ uint32_t intr_status;
+ uint32_t intr_enable;
+ uint32_t intr_disable;
+ uint32_t hcca;
+ uint32_t period_curr_ed;
+ uint32_t cntl_head_ed;
+ uint32_t cntl_curr_ed;
+ uint32_t bulk_head_ed;
+ uint32_t bulk_curr_ed;
+ uint32_t done_head;
+ uint32_t fm_interval;
+ uint32_t fm_remaining;
+ uint32_t fm_num;
+ uint32_t period_start;
+ uint32_t ls_threshold;
+ uint32_t rh_desc_a;
+ uint32_t rh_desc_b;
+ uint32_t rh_status;
+ uint32_t rh_ps[5];
+} __attribute__((packed, aligned(4)));
+
+#define EDA_FADDR(x) ((x & 0x7F))
+#define EDA_EP(x) ((x & 0x0F) << 7)
+#define EDA_DIR_OUT (1 << 11)
+#define EDA_DIR_IN (1 << 12)
+#define EDA_LOW_SPEED (1 << 13)
+#define EDA_SKIP (1 << 14)
+#define EDA_SKIP_LE (0x400000) /* avoiding conversions */
+#define EDA_FORMAT_ISO (1 << 15)
+#define EDA_MPS(x) ((x & 0x7FF) << 16)
+
+#define EDA_HEADP_MASK (0xFFFFFFFC)
+#define EDA_HEADP_MASK_LE (cpu_to_le32(EDA_HEADP_MASK))
+#define EDA_HEADP_HALTED (0x1)
+#define EDA_HEADP_CARRY (0x2)
+
+struct ohci_ed {
+ uint32_t attr;
+ uint32_t tailp;
+ uint32_t headp;
+ uint32_t next_ed;
+} __attribute__((packed));
+
+#define TDA_DONE (1 << 17)
+#define TDA_ROUNDING (1 << 18)
+#define TDA_DP_SETUP (0 << 19)
+#define TDA_DP_OUT (1 << 19)
+#define TDA_DP_IN (1 << 20)
+#define TDA_DI_NO (0x7 << 21)
+#define TDA_TOGGLE_DATA0 (0x02000000)
+#define TDA_TOGGLE_DATA1 (0x03000000)
+#define TDA_CC (0xF << 28)
+
+#define TDA_ERROR(x) ((x) * -1)
+
+/* Table 4-7: Completion Codes */
+const char *tda_cc_error[] = {
+#define USB_NOERROR TDA_ERROR(0)
+ "NOERROR",
+ "CRC",
+ "BITSTUFFING",
+ "DATATOGGLEMISMATCH",
+#define USB_STALL TDA_ERROR(4)
+ "STALL",
+ "DEVICENOTRESPONDING",
+ "PIDCHECKFAILURE",
+ "UNEXPECTEDPID",
+ "DATAOVERRUN",
+ "DATAUNDERRUN",
+ "reserved",
+ "reserved",
+ "BUFFEROVERRUN",
+ "BUFFERUNDERRUN",
+ "NOT ACCESSED",
+ "NOT ACCESSED",
+};
+
+struct ohci_td {
+ uint32_t attr;
+ uint32_t cbp;
+ uint32_t next_td;
+ uint32_t be;
+} __attribute__((packed));
+
+#define HCCA_SIZE 256
+#define HCCA_ALIGN (HCCA_SIZE - 1)
+#define HCCA_INTR_NUM 32
+struct ohci_hcca {
+ uint32_t intr_table[HCCA_INTR_NUM];
+ uint16_t frame_num;
+ uint16_t pad1;
+ uint32_t done_head;
+ uint32_t reserved[120];
+} __attribute__((packed));
+
+struct ohci_pipe {
+ struct ohci_ed ed; /* has to be aligned at 16 byte address*/
+ struct usb_pipe pipe;
+ struct ohci_td *td;
+ void *buf;
+ long ed_phys;
+ long td_phys;
+ long buf_phys;
+ uint32_t buflen;
+ uint32_t count;
+ uint8_t pad[0];
+}__attribute__((packed));
+
+#define OHCI_PIPE_POOL_SIZE 4096
+#define OHCI_MAX_TDS 256 /* supports 16k buffers, i.e. 64 * 256 */
+#define OHCI_MAX_BULK_SIZE 4096
+
+struct ohci_hcd {
+ struct ohci_hcca *hcca;
+ struct ohci_regs *regs;
+ struct usb_hcd_dev *hcidev;
+ struct usb_pipe *freelist;
+ struct usb_pipe *end;
+ struct usb_dev rhdev;
+ long hcca_phys;
+ void *pool;
+ long pool_phys;
+};
+
+#define OHCI_CTRL_CBSR (3 << 0)
+#define OHCI_CTRL_PLE (1 << 2)
+#define OHCI_CTRL_CLE (1 << 4)
+#define OHCI_CTRL_BLE (1 << 5)
+#define OHCI_CTRL_HCFS (3 << 6)
+#define OHCI_USB_RESET (0 << 6)
+#define OHCI_USB_OPER (2 << 6)
+#define OHCI_USB_SUSPEND (3 << 6)
+#define OHCI_CTRL_RWC (1 << 9)
+
+/* OHCI Command Status */
+#define OHCI_CMD_STATUS_HCR (1 << 0)
+#define OHCI_CMD_STATUS_CLF (1 << 1)
+#define OHCI_CMD_STATUS_BLF (1 << 2)
+
+/* OHCI Interrupt status */
+#define OHCI_INTR_STATUS_WD (1 << 1)
+
+/* Root Hub Descriptor A bits */
+#define RHDA_NDP (0xFF)
+#define RHDA_PSM_INDIVIDUAL (1 << 8)
+#define RHDA_NPS_ENABLE (1 << 9)
+#define RHDA_DT (1 << 10)
+#define RHDA_OCPM_PERPORT (1 << 11)
+#define RHDA_NOCP_ENABLE (1 << 12)
+
+/* Root Hub Descriptor B bits */
+#define RHDB_PPCM_PORT_POWER (0xFFFE)
+#define RHDB_PPCM_GLOBAL_POWER (0x0000)
+
+#define RH_STATUS_LPSC (1 << 16)
+#define RH_STATUS_OCIC (1 << 17)
+#define RH_STATUS_CREW (1 << 31)
+
+#define RH_PS_CCS (1 << 0)
+#define RH_PS_PES (1 << 1)
+#define RH_PS_PSS (1 << 2)
+#define RH_PS_POCI (1 << 3)
+#define RH_PS_PRS (1 << 4)
+#define RH_PS_PPS (1 << 8)
+#define RH_PS_LSDA (1 << 9)
+
+#define RH_PS_CSC (1 << 16)
+#define RH_PS_PESC (1 << 17)
+#define RH_PS_PSSC (1 << 18)
+#define RH_PS_OCIC (1 << 19)
+#define RH_PS_PRSC (1 << 20)
+
+/*********************************************************************/
+/* Values for USB Frame Timing */
+/* One USB frame (1ms) consists of 12000 bit-times as clock is 12MHz */
+/* controller can be adjusted for performance optimization */
+/* We use standard values (OHCI spec 6.3.1, 5.1.1.4, 5.4, 7.3.4) */
+/*********************************************************************/
+#define FRAME_INTERVAL (((((11999 - 210) * 6) / 7) << 16) | 11999)
+#define PERIODIC_START ((11999 * 9) / 10)
+
+
+static inline struct ohci_ed *ohci_pipe_get_ed(struct usb_pipe *pipe);
+static inline long ohci_pipe_get_ed_phys(struct usb_pipe *pipe);
+static int ohci_alloc_pipe_pool(struct ohci_hcd *ohcd);
+
+#endif /* USB_OHCI_H */
diff --git a/roms/SLOF/lib/libusb/usb-slof.c b/roms/SLOF/lib/libusb/usb-slof.c
new file mode 100644
index 000000000..ff070559a
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-slof.c
@@ -0,0 +1,93 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * All functions concerning interface to slof
+ */
+
+#include <string.h>
+#include "helpers.h"
+#include "usb-core.h"
+#include "paflof.h"
+
+#undef SLOF_DEBUG
+//#define SLOF_DEBUG
+#ifdef SLOF_DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+static int slof_usb_handle(struct usb_dev *dev)
+{
+ struct slof_usb_dev sdev;
+ sdev.port = dev->port;
+ sdev.addr = dev->addr;
+ sdev.hcitype = dev->hcidev->type;
+ sdev.num = dev->hcidev->num;
+ sdev.udev = dev;
+
+ if (dev->class == DEV_HID_KEYB) {
+ dprintf("Keyboard %ld %ld\n", dev->hcidev->type, dev->hcidev->num);
+ sdev.devtype = DEVICE_KEYBOARD;
+ forth_push((long)&sdev);
+ forth_eval("s\" dev-keyb.fs\" INCLUDED");
+ } else if (dev->class == DEV_HID_MOUSE) {
+ dprintf("Mouse %ld %ld\n", dev->hcidev->type, dev->hcidev->num);
+ sdev.devtype = DEVICE_MOUSE;
+ forth_push((long)&sdev);
+ forth_eval("s\" dev-mouse.fs\" INCLUDED");
+ } else if ((dev->class >> 16 & 0xFF) == 8) {
+ dprintf("MASS Storage device %ld %ld\n", dev->hcidev->type, dev->hcidev->num);
+ sdev.devtype = DEVICE_DISK;
+ forth_push((long)&sdev);
+ forth_eval("s\" dev-storage.fs\" INCLUDED");
+ } else if (dev->class == DEV_HUB) {
+ dprintf("Generic hub device %ld %ld\n", dev->hcidev->type,
+ dev->hcidev->num);
+ sdev.devtype = DEVICE_HUB;
+ forth_push((long)&sdev);
+ forth_eval("s\" dev-hub.fs\" INCLUDED");
+ }
+ return true;
+}
+
+void usb_slof_populate_new_device(struct usb_dev *dev)
+{
+ switch (usb_get_intf_class(dev->class)) {
+ case 3:
+ dprintf("HID found %06X\n", dev->class);
+ slof_usb_handle(dev);
+ break;
+ case 8:
+ dprintf("MASS STORAGE found %d %06X\n", dev->intf_num,
+ dev->class);
+ if ((dev->class & 0x50) != 0x50) { /* Bulk-only supported */
+ printf("Device not supported %06X\n", dev->class);
+ break;
+ }
+
+ if (!usb_msc_reset(dev)) {
+ printf("%s: bulk reset failed\n", __func__);
+ break;
+ }
+ SLOF_msleep(100);
+ slof_usb_handle(dev);
+ break;
+ case 9:
+ dprintf("HUB found\n");
+ slof_usb_handle(dev);
+ break;
+ default:
+ printf("USB Interface class -%x- Not supported\n", dev->class);
+ break;
+ }
+}
diff --git a/roms/SLOF/lib/libusb/usb-xhci.c b/roms/SLOF/lib/libusb/usb-xhci.c
new file mode 100644
index 000000000..cdf804287
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-xhci.c
@@ -0,0 +1,1553 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include "usb.h"
+#include "usb-core.h"
+#include "usb-xhci.h"
+#include "tools.h"
+#include "paflof.h"
+
+#undef XHCI_DEBUG
+//#define XHCI_DEBUG
+#ifdef XHCI_DEBUG
+#define dprintf(_x ...) do { printf("%s: ", __func__); printf(_x); } while (0)
+#else
+#define dprintf(_x ...) do {} while (0)
+#endif
+
+struct port_state ps_array_usb2[] = {
+ {1, 0, 0, 0, PORTSC_PLS_U0, "ERROR"}
+};
+
+struct port_state ps_array_usb3[] = {
+ {0, 0, 0, 0, PORTSC_PLS_DISABLED, "Powered-OFF"},
+ {1, 0, 0, 0, PORTSC_PLS_POLLING, "Polling"},
+ {1, 0, 0, 0, PORTSC_PLS_U0, "Polling"},
+ {1, 0, 0, 0, PORTSC_PLS_RXDETECT, "*** Disconnected ***"},
+ {1, 0, 0, 0, PORTSC_PLS_DISABLED, "Disabled"},
+ {1, 0, 0, 0, PORTSC_PLS_INACTIVE, "Error"},
+ {1, 0, 0, 0, PORTSC_PLS_TEST_MODE,"Loopback"},
+ {1, 0, 0, 0, PORTSC_PLS_COMP_MODE,"Compliancek"},
+ {1, 1, 0, 1, PORTSC_PLS_U0, "****** Reset ******"},
+ {1, 1, 1, 0, PORTSC_PLS_U0, "****** Enabled ******"},
+};
+
+#ifdef XHCI_DEBUG
+static void dump_xhci_regs(struct xhci_hcd *xhcd)
+{
+ struct xhci_cap_regs *cap;
+ struct xhci_op_regs *op;
+ struct xhci_run_regs *run;
+
+ cap = xhcd->cap_regs;
+ op = xhcd->op_regs;
+ run = xhcd->run_regs;
+
+ dprintf("\n");
+ dprintf(" - CAPLENGTH %02X\n", read_reg8 (&cap->caplength));
+ dprintf(" - HCIVERSION %04X\n", read_reg16(&cap->hciversion));
+ dprintf(" - HCSPARAMS1 %08X\n", read_reg32(&cap->hcsparams1));
+ dprintf(" - HCSPARAMS2 %08X\n", read_reg32(&cap->hcsparams2));
+ dprintf(" - HCSPARAMS3 %08X\n", read_reg32(&cap->hcsparams3));
+ dprintf(" - HCCPARAMS %08X\n", read_reg32(&cap->hccparams));
+ dprintf(" - DBOFF %08X\n", read_reg32(&cap->dboff));
+ dprintf(" - RTSOFF %08X\n", read_reg32(&cap->rtsoff));
+ dprintf("\n");
+
+ dprintf(" - USBCMD %08X\n", read_reg32(&op->usbcmd));
+ dprintf(" - USBSTS %08X\n", read_reg32(&op->usbsts));
+ dprintf(" - PAGESIZE %08X\n", read_reg32(&op->pagesize));
+ dprintf(" - DNCTRL %08X\n", read_reg32(&op->dnctrl));
+ dprintf(" - CRCR %016llX\n", read_reg64(&op->crcr));
+ dprintf(" - DCBAAP %016llX\n", read_reg64(&op->dcbaap));
+ dprintf(" - CONFIG %08X\n", read_reg32(&op->config));
+ dprintf("\n");
+
+ dprintf(" - MFINDEX %08X\n", read_reg32(&run->mfindex));
+ dprintf("\n");
+}
+
+static void print_port_status(struct xhci_port_regs *prs)
+{
+ uint32_t portsc;
+ uint32_t CCS, PED, PP, PLS, i, PR = 0;
+
+ portsc = read_reg32(&prs->portsc);
+ dprintf("portsc %08x portpmsc %08x portli %08x\n",
+ portsc,
+ read_reg32(&prs->portpmsc),
+ read_reg32(&prs->portli));
+
+ if (portsc & PORTSC_CCS) {
+ printf("CCS ");
+ CCS = 1;
+ }
+ if (portsc & PORTSC_PED) {
+ printf("PED ");
+ PED = 1;
+ }
+ if (portsc & PORTSC_OCA)
+ printf("OCA ");
+ if (portsc & PORTSC_PR)
+ printf("OCA ");
+ PLS = (portsc & PORTSC_PLS_MASK) >> 5;
+ printf("PLS:%d ", PLS);
+ if (portsc & PORTSC_PP) {
+ printf("PP ");
+ PP = 1;
+ }
+ printf("PS:%d ", (portsc & PORTSC_PS_MASK) >> 10);
+ printf("PIC:%d ", (portsc & PORTSC_PIC_MASK) >> 14);
+ if (portsc & PORTSC_LWS)
+ printf("LWS ");
+ if (portsc & PORTSC_CSC)
+ printf("CSC ");
+ if (portsc & PORTSC_PEC)
+ printf("PEC ");
+ if (portsc & PORTSC_WRC)
+ printf("WRC ");
+ if (portsc & PORTSC_OCC)
+ printf("OCC ");
+ if (portsc & PORTSC_PRC)
+ printf("PRC ");
+ if (portsc & PORTSC_PLC)
+ printf("PLC ");
+ if (portsc & PORTSC_CEC)
+ printf("CEC ");
+ if (portsc & PORTSC_CAS)
+ printf("CAS ");
+ if (portsc & PORTSC_WCE)
+ printf("WCE ");
+ if (portsc & PORTSC_WDE)
+ printf("WDE ");
+ if (portsc & PORTSC_WOE)
+ printf("WOE ");
+ if (portsc & PORTSC_DR)
+ printf("DR ");
+ if (portsc & PORTSC_WPR)
+ printf("WPR ");
+ printf("\n");
+
+ for (i = 0 ; i < (sizeof(ps_array_usb3)/sizeof(struct port_state)); i++) {
+ if (PP == ps_array_usb3[i].PP) {
+ if (CCS == ps_array_usb3[i].CCS) {
+ if (PED == ps_array_usb3[i].PED) {
+ if (PR == ps_array_usb3[i].PR) {
+ dprintf("%s - PLS %d\n", ps_array_usb3[i].state, PLS);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+#else
+#define dump_xhci_regs(r) do {} while (0)
+#define print_port_status(prs) do {} while (0)
+#endif
+
+static inline bool xhci_is_hc_ready(uint32_t *usbsts)
+{
+ return !(read_reg32(usbsts) & XHCI_USBSTS_CNR);
+}
+
+static inline bool xhci_wait_for_cnr(uint32_t *usbsts)
+{
+ /* Standard:
+ * Note: The xHC should halt within 16 ms. of software clearing the
+ * R/S bit to ‘0’.
+ * Give some more time... 32ms
+ */
+ int count = 320;
+ dprintf("Waiting for Controller ready ..");
+ while (!xhci_is_hc_ready(usbsts)) {
+ dprintf(".");
+ count--;
+ if (!count) {
+ dprintf(" failed %08X\n", read_reg32(usbsts));
+ return false;
+ }
+ SLOF_usleep(100);
+ }
+ dprintf(" done\n");
+ return true;
+}
+
+static bool xhci_hcd_set_runstop(struct xhci_op_regs *op, bool run_req)
+{
+ uint32_t reg;
+
+ dprintf("Request %s\n", run_req ? "RUN" : "STOP");
+ if (!xhci_is_hc_ready(&op->usbsts)) {
+ dprintf("Controller not ready\n");
+ return false;
+ }
+
+ reg = read_reg32(&op->usbcmd);
+ if (run_req)
+ reg |= run_req;
+ else
+ reg &= (uint32_t)~1;
+ dprintf("writing %08X\n", reg);
+ write_reg32(&op->usbcmd, reg);
+ mb();
+ xhci_wait_for_cnr(&op->usbsts);
+ return true;
+}
+
+static bool xhci_hcd_reset(struct xhci_op_regs *op)
+{
+ uint32_t reg;
+
+ /* Check if the controller is halted, else halt it */
+ if (!(read_reg32(&op->usbsts) & XHCI_USBSTS_HCH)) {
+ dprintf("HCHalted not set\n");
+ if (!xhci_hcd_set_runstop(op, false))
+ return false;
+ }
+
+ if (read_reg32(&op->usbsts) & XHCI_USBSTS_CNR) {
+ dprintf("Controller not ready\n");
+ return false;
+ }
+
+ reg = read_reg32(&op->usbcmd) | XHCI_USBCMD_HCRST;
+ /* Ready to Reset the controller now */
+ write_reg32(&op->usbcmd, reg);
+ xhci_wait_for_cnr(&op->usbsts);
+ return true;
+}
+
+static void xhci_handle_cmd_completion(struct xhci_hcd *xhcd,
+ struct xhci_event_trb *event)
+{
+ uint32_t flags, slot_id, status;
+
+ status = le32_to_cpu(event->status);
+ flags = le32_to_cpu(event->flags);
+ slot_id = TRB_SLOT_ID(flags);
+ if (TRB_STATUS(status) == COMP_SUCCESS)
+ xhcd->slot_id = slot_id;
+ else
+ xhcd->slot_id = 0;
+}
+
+static uint64_t xhci_poll_event(struct xhci_hcd *xhcd,
+ uint32_t event_type)
+{
+ struct xhci_event_trb *event;
+ uint64_t val, retval = 0;
+ uint32_t flags, time;
+ int index;
+
+ mb();
+ event = (struct xhci_event_trb *)xhcd->ering.deq;
+ flags = le32_to_cpu(event->flags);
+
+ dprintf("Reading from event ptr %p %08x\n", event, flags);
+ time = SLOF_GetTimer() + ((event_type == XHCI_POLL_NO_WAIT)? 0: USB_TIMEOUT);
+
+ while ((flags & TRB_CYCLE_STATE) != xhcd->ering.cycle_state) {
+ mb();
+ flags = le32_to_cpu(event->flags);
+ if (time < SLOF_GetTimer())
+ return 0;
+ }
+
+ mb();
+ flags = le32_to_cpu(event->flags);
+ switch(TRB_TYPE(flags))
+ {
+ case TRB_CMD_COMPLETION:
+ dprintf("CMD Completion\n");
+ xhci_handle_cmd_completion(xhcd, event);
+ break;
+ case TRB_PORT_STATUS:
+ dprintf("Port status event\n");
+ break;
+ case TRB_TRANSFER_EVENT:
+ dprintf("XFER event addr %16lx, status %08x, flags %08x\n",
+ le64_to_cpu(event->addr),
+ le32_to_cpu(event->status),
+ le32_to_cpu(event->flags));
+ break;
+ default:
+ printf("TRB_TYPE %d\n", TRB_TYPE(flags));
+ dprintf("Event addr %16lx, status %08x, flags %08x state %d\n",
+ le64_to_cpu(event->addr),
+ le32_to_cpu(event->status),
+ flags, xhcd->ering.cycle_state);
+ break;
+ }
+ xhcd->ering.deq = (uint64_t) (event + 1);
+ retval = le64_to_cpu(event->addr);
+
+ event->addr = 0;
+ event->status = 0;
+ event->flags = cpu_to_le32(xhcd->ering.cycle_state);
+
+ index = xhcd->ering.deq - (uint64_t)xhcd->ering.trbs;
+ val = xhcd->ering.trbs_dma;
+ val += (index % XHCI_EVENT_TRBS_SIZE);
+ if (!(index % XHCI_EVENT_TRBS_SIZE)) {
+ xhcd->ering.deq = (uint64_t)xhcd->ering.trbs;
+ xhcd->ering.cycle_state = xhcd->ering.cycle_state ? 0 : 1;
+ dprintf("Rounding %d\n", xhcd->ering.cycle_state);
+ }
+ dprintf("Update start %x deq %x index %d\n",
+ xhcd->ering.trbs_dma, val, index/sizeof(*event));
+ write_reg64(&xhcd->run_regs->irs[0].erdp, val);
+
+ if (retval == 0)
+ return (uint64_t)event;
+ else
+ return retval;
+}
+
+static void xhci_send_cmd(struct xhci_hcd *xhcd, uint32_t field1,
+ uint32_t field2, uint32_t field3, uint32_t field4)
+{
+ struct xhci_db_regs *dbr;
+ struct xhci_command_trb *cmd;
+ uint32_t val, cycle_state;
+
+ dbr = xhcd->db_regs;
+ cmd = (struct xhci_command_trb *)xhcd->crseg.enq;
+
+ cmd->field[0] = cpu_to_le32(field1);
+ cmd->field[1] = cpu_to_le32(field2);
+ cmd->field[2] = cpu_to_le32(field3);
+
+ val = le32_to_cpu(cmd->field[3]);
+ cycle_state = (val & 0x1) ? 0 : 1;
+ val = field4 | cycle_state;
+ cmd->field[3] = cpu_to_le32(val);
+
+ dprintf("CMD %016lx val %08x cycle_state %d field1 %08x, field2 %08x, field3 %08x field4 %08x\n",
+ cmd, val, cycle_state,
+ le32_to_cpu(cmd->field[0]),
+ le32_to_cpu(cmd->field[1]),
+ le32_to_cpu(cmd->field[2]),
+ le32_to_cpu(cmd->field[3])
+ );
+
+ /* Ring the doorbell */
+ write_reg32(&dbr->db[0], 0);
+ xhci_poll_event(xhcd, 0);
+ cmd++;
+ xhcd->crseg.enq = (uint64_t)cmd;
+ return;
+}
+
+static void xhci_send_enable_slot(struct xhci_hcd *xhcd, uint32_t port)
+{
+ uint32_t field1, field2, field3, field4;
+
+ field1 = 0;
+ field2 = 0;
+ field3 = 0;
+ field4 = TRB_CMD_TYPE(TRB_ENABLE_SLOT);
+ xhci_send_cmd(xhcd, field1, field2, field3, field4);
+}
+
+static void xhci_send_addr_device(struct xhci_hcd *xhcd, uint32_t slot_id,
+ uint64_t dma_in_ctx)
+{
+ uint32_t field1, field2, field3, field4;
+
+ dprintf("Address device %lx, low %x, high %x\n", dma_in_ctx,
+ TRB_ADDR_LOW(dma_in_ctx),
+ TRB_ADDR_HIGH(dma_in_ctx));
+ field1 = TRB_ADDR_LOW(dma_in_ctx) & ~0xF;
+ field2 = TRB_ADDR_HIGH(dma_in_ctx);
+ field3 = 0;
+ field4 = TRB_CMD_TYPE(TRB_ADDRESS_DEV) | TRB_CMD_SLOT_ID(slot_id);
+ xhci_send_cmd(xhcd, field1, field2, field3, field4);
+}
+
+static uint32_t xhci_get_epno(struct usb_pipe *pipe)
+{
+ uint32_t x_epno;
+ x_epno = pipe->dir | 2 * pipe->epno;
+ dprintf("EPno %d:%d DIR %d\n", pipe->epno, x_epno, pipe->dir);
+ return x_epno;
+}
+
+static void xhci_configure_ep(struct xhci_hcd *xhcd, uint32_t slot_id,
+ uint64_t dma_in_ctx)
+{
+ uint32_t field1, field2, field3, field4;
+
+ dprintf("Configure EP %lx, low %x, high %x\n", dma_in_ctx,
+ TRB_ADDR_LOW(dma_in_ctx),
+ TRB_ADDR_HIGH(dma_in_ctx));
+ field1 = TRB_ADDR_LOW(dma_in_ctx) & ~0xF;
+ field2 = TRB_ADDR_HIGH(dma_in_ctx);
+ field3 = 0;
+ field4 = TRB_CMD_TYPE(TRB_CONFIG_EP) | TRB_CMD_SLOT_ID(slot_id);
+ xhci_send_cmd(xhcd, field1, field2, field3, field4);
+}
+
+static void xhci_init_seg(struct xhci_seg *seg, uint32_t size, uint32_t type)
+{
+ struct xhci_link_trb *link;
+
+ seg->size = size / XHCI_TRB_SIZE;
+ seg->next = NULL;
+ seg->type = type;
+ seg->cycle_state = 1;
+ seg->enq = (uint64_t)seg->trbs;
+ seg->deq = (uint64_t)seg->trbs;
+ memset((void *)seg->trbs, 0, size);
+
+ if (type != TYPE_EVENT) {
+ link =(struct xhci_link_trb *) (seg->trbs + seg->size - 1);
+ link->addr = cpu_to_le64(seg->trbs_dma);
+ link->field2 = 0;
+ link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK));
+ }
+ return;
+}
+
+static bool xhci_alloc_seg(struct xhci_seg *seg, uint32_t size, uint32_t type)
+{
+ seg->trbs = (union xhci_trb *)SLOF_dma_alloc(size);
+ if (!seg->trbs) {
+ dprintf("Alloc failed\n");
+ return false;
+ }
+ xhci_init_seg(seg, size, type);
+ seg->trbs_dma = SLOF_dma_map_in((void *)seg->trbs, size, false);
+
+ dprintf(" TRBs %016lX TRBS-DMA %016lX\n", seg->trbs, seg->trbs_dma);
+ return true;
+}
+
+static void xhci_free_seg(struct xhci_seg *seg, uint32_t size)
+{
+ if (seg->trbs) {
+ dprintf(" TRBs %016lX TRBS-DMA %016lX size %x\n", seg->trbs, seg->trbs_dma, size);
+ SLOF_dma_map_out(seg->trbs_dma, (void *)seg->trbs, size);
+ SLOF_dma_free((void *)seg->trbs, size);
+ }
+ memset(seg, 0, sizeof(*seg));
+}
+
+#define CTX_SIZE(x) ( (x) ? 64 : 32 )
+
+static bool xhci_alloc_ctx(struct xhci_ctx *ctx, uint32_t size, uint32_t type)
+{
+ ctx->addr = (uint8_t *)SLOF_dma_alloc(size);
+ if (!ctx->addr) {
+ dprintf("Alloc failed\n");
+ return false;
+ }
+ ctx->size = size;
+ ctx->type = type;
+ memset((void *)ctx->addr, 0, size);
+ ctx->dma_addr = SLOF_dma_map_in((void *)ctx->addr, size, false);
+ dprintf("ctx %llx, ctx_dma %llx\n", ctx->addr, ctx->dma_addr);
+ return true;
+}
+
+static struct xhci_control_ctx *xhci_get_control_ctx(struct xhci_ctx *ctx)
+{
+ if (ctx->type == XHCI_CTX_TYPE_INPUT)
+ return (struct xhci_control_ctx *) ctx->addr;
+ return NULL;
+}
+
+static struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctx *ctx, uint32_t ctx_size)
+{
+ uint32_t offset = 0;
+
+ if (ctx->type == XHCI_CTX_TYPE_INPUT)
+ offset += ctx_size;
+ return (struct xhci_slot_ctx *)(ctx->addr + offset);
+}
+
+static struct xhci_ep_ctx *xhci_get_ep0_ctx(struct xhci_ctx *ctx, uint32_t ctx_size)
+{
+ uint32_t offset = 0;
+
+ offset = ctx_size;
+ if (ctx->type == XHCI_CTX_TYPE_INPUT)
+ offset += ctx_size;
+ return (struct xhci_ep_ctx *)(ctx->addr + offset);
+}
+
+static struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctx *ctx, uint32_t ctx_size,
+ uint32_t epno)
+{
+ uint32_t offset = 0;
+
+ offset = ctx_size * epno;
+ if (ctx->type == XHCI_CTX_TYPE_INPUT)
+ offset += ctx_size;
+ return (struct xhci_ep_ctx *)(ctx->addr + offset);
+}
+
+static void xhci_free_ctx(struct xhci_ctx *ctx, uint32_t size)
+{
+ SLOF_dma_map_out(ctx->dma_addr, (void *)ctx->addr, size);
+ SLOF_dma_free((void *)ctx->addr, size);
+}
+
+static uint32_t usb_control_max_packet(uint32_t speed)
+{
+ uint32_t max_packet = 0;
+
+ switch(speed)
+ {
+ case USB_LOW_SPEED:
+ max_packet = 8;
+ break;
+ case USB_FULL_SPEED:
+ max_packet = 8;
+ break;
+ case USB_HIGH_SPEED:
+ max_packet = 64;
+ break;
+ case USB_SUPER_SPEED:
+ max_packet = 512;
+ break;
+ default:
+ /* should not reach here */
+ dprintf("Unknown speed\n");
+ }
+ return max_packet;
+}
+
+static bool xhci_alloc_dev(struct xhci_hcd *xhcd, struct usb_dev *hub,
+ uint32_t slot_id, uint32_t port, uint32_t slotspeed)
+{
+ struct usb_dev *dev;
+ struct xhci_dev *xdev;
+ struct xhci_slot_ctx *slot;
+ struct xhci_control_ctx *ctrl;
+ struct xhci_ep_ctx *ep0;
+ uint32_t ctx_size, val;
+ uint16_t max_packet;
+ uint32_t newport, rootport;
+
+ if (slot_id > XHCI_CONFIG_MAX_SLOT) {
+ printf("USB3 slot ID %d is too high (max is %d)\n", slot_id,
+ XHCI_CONFIG_MAX_SLOT);
+ return false;
+ }
+
+ ctx_size = CTX_SIZE(xhcd->hcc_csz_64);
+ xdev = &xhcd->xdevs[slot_id];
+ xdev->slot_id = slot_id;
+ xdev->ctx_size = ctx_size;
+
+ /* 4.3.3 Device Slot initialization */
+ /* Step 1 */
+ if (!xhci_alloc_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE, XHCI_CTX_TYPE_INPUT)) {
+ dprintf("Failed allocating in_ctx\n");
+ return false;
+ }
+
+ /* Step 2 */
+ ctrl = xhci_get_control_ctx(&xdev->in_ctx);
+ ctrl->a_flags = cpu_to_le32(0x3); /* A0, A1 */
+ ctrl->d_flags = 0;
+
+ /* Step 3 */
+ slot = xhci_get_slot_ctx(&xdev->in_ctx, ctx_size);
+ newport = rootport = port + 1;
+ val = newport & 0xf;
+ for (dev = hub; dev != NULL; dev = dev->hub) {
+ val = (val << 4) | (dev->port & 0xf); /* Build route string */
+ rootport = dev->port;
+ }
+ val >>= 4; /* Remove root hub ID from the string */
+ val |= LAST_CONTEXT(1) | slotspeed;
+ slot->field1 = cpu_to_le32(val);
+ slot->field2 = cpu_to_le32(ROOT_HUB_PORT(rootport));
+
+ /* Step 4 */
+ if (!xhci_alloc_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE, TYPE_CTRL)) {
+ dprintf("Failed allocating control\n");
+ goto fail_in_ctx;
+ }
+
+ /* Step 5 */
+ ep0 = xhci_get_ep0_ctx(&xdev->in_ctx, ctx_size);
+ val = 0;
+ max_packet = usb_control_max_packet(USB_SUPER_SPEED);
+ max_packet = 64;
+ val = EP_TYPE(EP_CTRL) | MAX_BURST(0) | ERROR_COUNT(3) |
+ MAX_PACKET_SIZE(max_packet);
+ ep0->field2 = cpu_to_le32(val);;
+ ep0->deq_addr = cpu_to_le64(xdev->control.trbs_dma | xdev->control.cycle_state);
+ ep0->field4 = cpu_to_le32(8);
+
+ /* Step 6 */
+ if (!xhci_alloc_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE, XHCI_CTX_TYPE_DEVICE)) {
+ dprintf("Failed allocating out_ctx\n");
+ goto fail_control_seg;
+ }
+
+ /* Step 7 */
+ xhcd->dcbaap[slot_id] = cpu_to_le64(xdev->out_ctx.dma_addr);
+
+ /* Step 8 */
+ slot = xhci_get_slot_ctx(&xdev->out_ctx, ctx_size);
+ ep0 = xhci_get_ep0_ctx(&xdev->out_ctx, ctx_size);
+
+ dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4)));
+ xhci_send_addr_device(xhcd, slot_id, xdev->in_ctx.dma_addr);
+ mb();
+ dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4)));
+
+ dprintf("EP0 f0 %08X f1 %08X %016lX %08X\n",
+ le32_to_cpu(ep0->field1),
+ le32_to_cpu(ep0->field2),
+ le64_to_cpu(ep0->deq_addr),
+ le32_to_cpu(ep0->field4));
+
+ /* Step 9 - configure ep */
+ ctrl->a_flags = cpu_to_le32(0x1); /* A0 */
+ ctrl->d_flags = 0;
+ xhci_configure_ep(xhcd, slot_id, xdev->in_ctx.dma_addr);
+ mb();
+ dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4)));
+ dprintf("USB Device address %d \n", USB_DEV_ADDRESS(le32_to_cpu(slot->field4)));
+ dprintf("EP0 f0 %08X f1 %08X %016lX %08X\n",
+ le32_to_cpu(ep0->field1),
+ le32_to_cpu(ep0->field2),
+ le64_to_cpu(ep0->deq_addr),
+ le32_to_cpu(ep0->field4));
+
+ dev = usb_devpool_get();
+ dprintf("allocated device %p\n", dev);
+ dev->hcidev = xhcd->hcidev;
+ dev->speed = USB_SUPER_SPEED;
+ dev->addr = USB_DEV_ADDRESS(slot->field4);
+ dev->port = newport;
+ dev->hub = hub;
+ dev->priv = xdev;
+ xdev->dev = dev;
+ if (usb_setup_new_device(dev, newport)) {
+ usb_slof_populate_new_device(dev);
+ return true;
+ }
+
+ xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE);
+fail_control_seg:
+ xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE);
+fail_in_ctx:
+ xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE);
+ return false;
+}
+
+static void xhci_free_dev(struct xhci_dev *xdev)
+{
+ xhci_free_seg(&xdev->bulk_in, XHCI_DATA_TRBS_SIZE);
+ xhci_free_seg(&xdev->bulk_out, XHCI_DATA_TRBS_SIZE);
+ xhci_free_seg(&xdev->intr, XHCI_INTR_TRBS_SIZE);
+ xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE);
+ xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE);
+ xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE);
+}
+
+bool usb3_dev_init(struct xhci_hcd *xhcd, struct usb_dev *hub, uint32_t port,
+ uint32_t slotspeed)
+{
+ /* Device enable slot */
+ xhci_send_enable_slot(xhcd, port);
+ if (!xhcd->slot_id) {
+ dprintf("Unable to get slot id\n");
+ return false;
+ }
+ dprintf("SLOT ID: %d\n", xhcd->slot_id);
+ if (!xhci_alloc_dev(xhcd, hub, xhcd->slot_id, port, slotspeed)) {
+ dprintf("Unable to allocate device\n");
+ return false;
+ }
+ return true;
+}
+
+static int xhci_device_present(uint32_t portsc, uint32_t usb_ver)
+{
+ if (usb_ver == USB_XHCI) {
+ /* Device present and enabled state */
+ if ((portsc & PORTSC_CCS) &&
+ (portsc & PORTSC_PP) &&
+ (portsc & PORTSC_PED)) {
+ return true;
+ }
+ } else if (usb_ver == USB_EHCI) {
+ /* Device present and in disabled state */
+ if ((portsc & PORTSC_CCS) && (portsc & PORTSC_CSC))
+ return true;
+ }
+ return false;
+}
+
+static int xhci_port_scan(struct xhci_hcd *xhcd,
+ uint32_t usb_ver)
+{
+ uint32_t num_ports, portsc, i;
+ struct xhci_op_regs *op;
+ struct xhci_port_regs *prs;
+ struct xhci_cap_regs *cap;
+ uint32_t xecp_off;
+ uint32_t *xecp_addr, *base;
+ uint32_t port_off = 0, port_cnt;
+
+ dprintf("enter\n");
+
+ op = xhcd->op_regs;
+ cap = xhcd->cap_regs;
+ port_cnt = num_ports = read_reg32(&cap->hcsparams1) >> 24;
+
+ /* Read the xHCI extented capability to find usb3 ports and offset*/
+ xecp_off = XHCI_HCCPARAMS_XECP(read_reg32(&cap->hccparams));
+ base = (uint32_t *)cap;
+ while (xecp_off > 0) {
+ xecp_addr = base + xecp_off;
+ dprintf("xecp_off %d %p %p \n", xecp_off, base, xecp_addr);
+
+ if (XHCI_XECP_CAP_ID(read_reg32(xecp_addr)) == XHCI_XECP_CAP_SP &&
+ XHCI_XECP_CAP_SP_MJ(read_reg32(xecp_addr)) == usb_ver &&
+ XHCI_XECP_CAP_SP_MN(read_reg32(xecp_addr)) == 0) {
+ port_cnt = XHCI_XECP_CAP_SP_PC(read_reg32(xecp_addr + 2));
+ port_off = XHCI_XECP_CAP_SP_PO(read_reg32(xecp_addr + 2));
+ dprintf("PortCount %d Portoffset %d\n", port_cnt, port_off);
+ }
+ base = xecp_addr;
+ xecp_off = XHCI_XECP_NEXT_PTR(read_reg32(xecp_addr));
+ }
+ if (port_off == 0) /* port_off should always start from 1 */
+ return false;
+ for (i = (port_off - 1); i < (port_off + port_cnt - 1); i++) {
+ prs = &op->prs[i];
+ portsc = read_reg32(&prs->portsc);
+ if (xhci_device_present(portsc, usb_ver)) {
+ /* Device present */
+ dprintf("Device present on port %d\n", i);
+ /* Reset the port */
+ portsc = read_reg32(&prs->portsc);
+ portsc = portsc | PORTSC_PR;
+ write_reg32(&prs->portsc, portsc);
+ /* FIXME poll for port event */
+ SLOF_msleep(20);
+ xhci_poll_event(xhcd, 0);
+ portsc = read_reg32(&prs->portsc);
+ if (portsc & ~PORTSC_PRC) {
+ dprintf("Port reset complete %d\n", i);
+ }
+ print_port_status(prs);
+ if (!usb3_dev_init(xhcd, NULL, i - (port_off - 1),
+ ((portsc >> 10) & 0xf) << 20)) {
+ dprintf("USB device initialization failed\n");
+ }
+ }
+ }
+ dprintf("exit\n");
+ return true;
+}
+
+static int xhci_hub_check_ports(struct xhci_hcd *xhcd)
+{
+ return xhci_port_scan(xhcd, USB_XHCI) | xhci_port_scan(xhcd, USB_EHCI);
+}
+
+static bool xhci_hcd_init(struct xhci_hcd *xhcd)
+{
+ struct xhci_op_regs *op;
+ struct xhci_int_regs *irs;
+ uint64_t val;
+ uint32_t reg;
+
+ if (!xhcd) {
+ dprintf("NULL pointer\n");
+ goto fail;
+ }
+
+ op = xhcd->op_regs;
+ irs = &xhcd->run_regs->irs[0];
+ if (!xhci_hcd_reset(op)) {
+ dprintf("Reset failed\n");
+ goto fail;
+ }
+
+ write_reg32(&op->config, XHCI_CONFIG_MAX_SLOT);
+ reg = read_reg32(&xhcd->cap_regs->hccparams);
+ /* 64byte context !! */
+ xhcd->hcc_csz_64 = (reg & XHCI_HCCPARAMS_CSZ) ? 1 : 0;
+
+ if (xhcd->hcc_csz_64) {
+ printf("usb-xhci: 64 Byte context not supported\n");
+ goto fail;
+ }
+ /*
+ * 6.1 Device Context Base Address Array
+ *
+ * Allocate memory and initialize
+ */
+ xhcd->dcbaap = (uint64_t *)SLOF_dma_alloc(XHCI_DCBAAP_MAX_SIZE);
+ if (!xhcd->dcbaap) {
+ dprintf("Alloc failed\n");
+ goto fail;
+ }
+ memset((void *)xhcd->dcbaap, 0, XHCI_DCBAAP_MAX_SIZE);
+ xhcd->dcbaap_dma = SLOF_dma_map_in((void *)xhcd->dcbaap,
+ XHCI_DCBAAP_MAX_SIZE, false);
+ dprintf("dcbaap %llx, dcbaap_phys %llx\n", xhcd->dcbaap, xhcd->dcbaap_dma);
+ write_reg64(&op->dcbaap, xhcd->dcbaap_dma);
+
+ /*
+ * Command Ring Control - TRB
+ * FIXME - better way to allocate it...
+ */
+ if (!xhci_alloc_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE, TYPE_COMMAND))
+ goto fail_dcbaap;
+
+ val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK;
+ val = val | (xhcd->crseg.trbs_dma & XHCI_CRCR_CRP_MASK);
+ write_reg64(&op->crcr, val);
+
+ /*
+ * Event Ring Control - TRB
+ * Allocate event TRBS
+ */
+ if (!xhci_alloc_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE, TYPE_EVENT))
+ goto fail_crseg;
+
+ /*
+ * Populate event ring segment table.
+ * Note: only using one segment.
+ */
+ xhcd->erst.entries = SLOF_dma_alloc(XHCI_EVENT_TRBS_SIZE);
+ if (!xhcd->erst.entries)
+ goto fail_ering;
+ xhcd->erst.dma = SLOF_dma_map_in((void *)xhcd->erst.entries,
+ XHCI_EVENT_TRBS_SIZE, false);
+ xhcd->erst.num_segs = XHCI_ERST_NUM_SEGS;
+
+ /* populate entries[0] */
+ write_reg64(&xhcd->erst.entries->addr, xhcd->ering.trbs_dma);
+ write_reg32(&xhcd->erst.entries->size, xhcd->ering.size);
+ write_reg32(&xhcd->erst.entries->reserved, 0);
+
+ /* populate erdp */
+ val = read_reg64(&irs->erdp) & ~XHCI_ERDP_MASK;
+ val = val | (xhcd->ering.trbs_dma & XHCI_ERDP_MASK);
+ write_reg64(&irs->erdp, val);
+
+ /* populate erstsz */
+ val = read_reg32(&irs->erstsz) & ~XHCI_ERST_SIZE_MASK;
+ val = val | xhcd->erst.num_segs;
+ write_reg32(&irs->erstsz, val);
+
+ /* Now write the erstba */
+ val = read_reg64(&irs->erstba) & ~XHCI_ERST_ADDR_MASK;
+ val = val | (xhcd->erst.dma & XHCI_ERST_ADDR_MASK);
+ write_reg64(&irs->erstba, val);
+
+ dprintf("ERDP %llx TRB-DMA %llx\n", read_reg64(&irs->erdp),
+ xhcd->ering.trbs_dma);
+ dprintf("ERST %llx, ERST DMA %llx, size %d\n",
+ (uint64_t)xhcd->erst.entries, xhcd->erst.dma,
+ xhcd->erst.num_segs);
+
+ mb();
+ if (!xhci_hcd_set_runstop(op, true))
+ goto fail_erst_entries;
+
+ if (!xhci_hub_check_ports(xhcd))
+ goto fail_erst_entries;
+
+ return true;
+fail_erst_entries:
+ write_reg32(&irs->erstsz, 0);
+ write_reg64(&irs->erstba, 0);
+ mb();
+ SLOF_dma_map_out(xhcd->erst.dma, (void *)xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE);
+ SLOF_dma_free((void *)xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE);
+fail_ering:
+ xhci_free_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE);
+fail_crseg:
+ val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK;
+ write_reg64(&op->crcr, val);
+ mb();
+ xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE);
+fail_dcbaap:
+ write_reg64(&op->dcbaap, 0);
+ mb();
+ SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE);
+ SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE);
+fail:
+ return false;
+}
+
+static bool xhci_hcd_exit(struct xhci_hcd *xhcd)
+{
+ struct xhci_op_regs *op;
+ struct xhci_int_regs *irs;
+ uint64_t val;
+ int i;
+
+ if (!xhcd) {
+ dprintf("NULL pointer\n");
+ return false;
+ }
+ op = xhcd->op_regs;
+
+ if (!xhci_hcd_set_runstop(op, false)) {
+ dprintf("NULL pointer\n");
+ }
+
+ for (i = 1; i < XHCI_CONFIG_MAX_SLOT; i++) {
+ if (xhcd->xdevs[i].dev)
+ xhci_free_dev(&xhcd->xdevs[i]);
+ }
+
+ irs = &xhcd->run_regs->irs[0];
+ write_reg32(&irs->erstsz, 0);
+ write_reg64(&irs->erstba, 0);
+ mb();
+ if (xhcd->erst.entries) {
+ SLOF_dma_map_out(xhcd->erst.dma, xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE);
+ SLOF_dma_free(xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE);
+ }
+ xhci_free_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE);
+
+ val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK;
+ write_reg64(&op->crcr, val);
+ xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE);
+ write_reg64(&op->dcbaap, 0);
+ if (xhcd->dcbaap) {
+ SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE);
+ SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE);
+ }
+
+ /*
+ * QEMU implementation of XHCI doesn't implement halt
+ * properly. It basically says that it's halted immediately
+ * but doesn't actually terminate ongoing activities and
+ * DMAs. This needs to be fixed in QEMU.
+ *
+ * For now, wait for 50ms grace time till qemu stops using
+ * this device.
+ */
+ SLOF_msleep(50);
+
+ return true;
+}
+
+static void xhci_init(struct usb_hcd_dev *hcidev)
+{
+ struct xhci_hcd *xhcd;
+
+ printf(" XHCI: Initializing\n");
+ dprintf("device base address %p\n", hcidev->base);
+
+ hcidev->base = (void *)((uint64_t)hcidev->base & ~7);
+ xhcd = SLOF_alloc_mem(sizeof(*xhcd));
+ if (!xhcd) {
+ printf("usb-xhci: Unable to allocate memory\n");
+ return;
+ }
+ memset(xhcd, 0, sizeof(*xhcd));
+
+ hcidev->nextaddr = 1;
+ hcidev->priv = xhcd;
+ xhcd->hcidev = hcidev;
+ xhcd->cap_regs = (struct xhci_cap_regs *)(hcidev->base);
+ xhcd->op_regs = (struct xhci_op_regs *)(hcidev->base +
+ read_reg8(&xhcd->cap_regs->caplength));
+ xhcd->run_regs = (struct xhci_run_regs *)(hcidev->base +
+ read_reg32(&xhcd->cap_regs->rtsoff));
+ xhcd->db_regs = (struct xhci_db_regs *)(hcidev->base +
+ read_reg32(&xhcd->cap_regs->dboff));
+ dump_xhci_regs(xhcd);
+ if (!xhci_hcd_init(xhcd))
+ printf("usb-xhci: failed to initialize XHCI controller.\n");
+ dump_xhci_regs(xhcd);
+}
+
+static void xhci_exit(struct usb_hcd_dev *hcidev)
+{
+ struct xhci_hcd *xhcd;
+
+ dprintf("%s: enter \n", __func__);
+ if (!hcidev && !hcidev->priv) {
+ return;
+ }
+
+ xhcd = hcidev->priv;
+ xhci_hcd_exit(xhcd);
+ SLOF_free_mem(xhcd, sizeof(*xhcd));
+ hcidev->priv = NULL;
+}
+
+static void fill_trb_buff(struct xhci_command_trb *cmd, uint32_t field1,
+ uint32_t field2, uint32_t field3, uint32_t field4)
+{
+ uint32_t val, cycle_state;
+
+ cmd->field[0] = cpu_to_le32(field1);
+ cmd->field[1] = cpu_to_le32(field2);
+ cmd->field[2] = cpu_to_le32(field3);
+
+ val = le32_to_cpu(cmd->field[3]);
+ cycle_state = (val & 0x1) ? 0 : 1;
+ val = cycle_state | (field4 & ~0x1);
+ cmd->field[3] = cpu_to_le32(val);
+ mb();
+
+ dprintf("CMD %016lx val %08x cycle_state %d field1 %08x, field2 %08x, field3 %08x field4 %08x\n",
+ cmd, val, cycle_state,
+ le32_to_cpu(cmd->field[0]),
+ le32_to_cpu(cmd->field[1]),
+ le32_to_cpu(cmd->field[2]),
+ le32_to_cpu(cmd->field[3])
+ );
+
+ return;
+}
+
+static void fill_setup_trb(struct xhci_command_trb *cmd, struct usb_dev_req *req,
+ uint32_t size)
+{
+ uint32_t field1, field2, field3, field4 = 0;
+ uint64_t req_raw;
+ uint32_t datalen = 0, pid = 0;
+
+ req_raw = *((uint64_t *)req);
+ dprintf("%lx %lx \n", *((uint64_t *)req), req_raw);
+ /* req_raw is already in right byte order... */
+ field1 = cpu_to_le32(TRB_ADDR_HIGH(req_raw));
+ field2 = cpu_to_le32(TRB_ADDR_LOW(req_raw));
+ field3 = 8; /* ALWAYS 8 */
+
+ datalen = cpu_to_le16(req->wLength);
+ if (datalen) {
+ pid = (req->bmRequestType & REQT_DIR_IN) ? 3 : 2;
+ field4 = TRB_TRT(pid);
+ }
+ field4 |= TRB_CMD_TYPE(TRB_SETUP_STAGE) | TRB_IDT;
+ fill_trb_buff(cmd, field1, field2, field3, field4);
+}
+
+static void fill_setup_data(struct xhci_command_trb *cmd, void *data,
+ uint32_t size, uint32_t dir)
+{
+ uint32_t field1, field2, field3, field4;
+
+ field1 = TRB_ADDR_LOW(data);
+ field2 = TRB_ADDR_HIGH(data);
+ field3 = size;
+ field4 = TRB_CMD_TYPE(TRB_DATA_STAGE);
+ if (dir)
+ field4 |= TRB_DIR_IN;
+ fill_trb_buff(cmd, field1, field2, field3, field4);
+}
+
+static void fill_status_trb(struct xhci_command_trb *cmd, uint32_t dir)
+{
+ uint32_t field1, field2, field3, field4;
+
+ field1 = 0;
+ field2 = 0;
+ field3 = 0;
+ field4 = TRB_CMD_TYPE(TRB_STATUS_STAGE) | TRB_IOC;
+ if (dir)
+ field4 |= TRB_DIR_IN;
+ fill_trb_buff(cmd, field1, field2, field3, field4);
+}
+
+static void fill_normal_trb(struct xhci_transfer_trb *trb, void *data,
+ uint32_t size)
+{
+ uint32_t field1, field2, field3, field4;
+
+ field1 = TRB_ADDR_LOW(data);
+ field2 = TRB_ADDR_HIGH(data);
+ field3 = size;
+ field4 = TRB_CMD_TYPE(TRB_NORMAL) | TRB_IOC;
+ fill_trb_buff((struct xhci_command_trb *)trb, field1, field2, field3, field4);
+}
+
+static int xhci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data)
+{
+ struct xhci_dev *xdev;
+ struct xhci_seg *ctrl;
+ struct xhci_hcd *xhcd;
+ struct xhci_command_trb *cmd;
+ struct xhci_db_regs *dbr;
+ long req_phys = 0, data_phys = 0;
+ int ret = true;
+ uint32_t slot_id, pid = 0, datalen = 0;
+
+ if (!pipe->dev || !pipe->dev->hcidev) {
+ dprintf(" NULL pointer\n");
+ return false;
+ }
+
+ xdev = pipe->dev->priv;
+ slot_id = xdev->slot_id;
+ ctrl = &xdev->control;
+ xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv;
+ dbr = xhcd->db_regs;
+ if (!ctrl || !xdev || !xhcd) {
+ dprintf(" NULL pointer\n");
+ return false;
+ }
+
+ cmd = (struct xhci_command_trb *)ctrl->enq;
+ req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true);
+ fill_setup_trb(cmd, req, sizeof(*req));
+
+ cmd++;
+ datalen = cpu_to_le16(req->wLength);
+ if (datalen)
+ pid = 1;
+ if (datalen) {
+ data_phys = SLOF_dma_map_in(data, datalen, true);
+ fill_setup_data(cmd, (void *) data_phys, datalen, pid);
+ cmd++;
+ }
+
+ fill_status_trb(cmd, pid);
+ cmd++;
+
+ /* Ring the doorbell - ep0 */
+ write_reg32(&dbr->db[slot_id], 1);
+ if (!xhci_poll_event(xhcd, 0)) {
+ dprintf("Command failed\n");
+ ret = false;
+ }
+ ctrl->enq = (uint64_t) cmd;
+ SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req));
+ if (datalen)
+ SLOF_dma_map_out(data_phys, data, datalen);
+ return ret;
+}
+
+static inline struct xhci_pipe *xhci_pipe_get_xpipe(struct usb_pipe *pipe)
+{
+ struct xhci_pipe *xpipe;
+ xpipe = container_of(pipe, struct xhci_pipe, pipe);
+ dprintf("%s: xpipe is %p\n", __func__, xpipe);
+ return xpipe;
+}
+
+static inline struct xhci_seg *xhci_pipe_get_seg(struct usb_pipe *pipe)
+{
+ struct xhci_pipe *xpipe;
+ xpipe = xhci_pipe_get_xpipe(pipe);
+ return xpipe->seg;
+}
+
+static inline void *xhci_get_trb(struct xhci_seg *seg)
+{
+ uint64_t val, enq;
+ unsigned index;
+ struct xhci_link_trb *link;
+
+ enq = val = seg->enq;
+ val = val + XHCI_TRB_SIZE;
+ index = (enq - (uint64_t)seg->trbs) / XHCI_TRB_SIZE + 1;
+ dprintf("%s: enq %llx, val %llx %x\n", __func__, enq, val, index);
+ /* TRBs being a cyclic buffer, here we cycle back to beginning. */
+ if (index == (seg->size - 1)) {
+ dprintf("%s: rounding \n", __func__);
+ seg->enq = (uint64_t)seg->trbs;
+ seg->cycle_state ^= seg->cycle_state;
+ link = (struct xhci_link_trb *) (seg->trbs + seg->size - 1);
+ link->addr = cpu_to_le64(seg->trbs_dma);
+ link->field2 = 0;
+ link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK));
+ mb();
+ }
+ else {
+ seg->enq = seg->enq + XHCI_TRB_SIZE;
+ }
+
+ return (void *)enq;
+}
+
+static inline void *xhci_get_trb_deq(struct xhci_seg *seg)
+{
+ uint64_t deq_next, deq;
+ unsigned index;
+
+ deq = seg->deq;
+ deq_next = deq + XHCI_TRB_SIZE;
+ index = (deq - (uint64_t)seg->trbs) / XHCI_TRB_SIZE + 1;
+ dprintf("%s: deq %llx, deq_next %llx index %x\n", __func__, deq, deq_next, index);
+ /* TRBs being a cyclic buffer, here we cycle back to beginning. */
+ if (index == (seg->size - 1)) {
+ dprintf("%s: rounding \n", __func__);
+ seg->deq = (uint64_t)seg->trbs;
+ }
+ else {
+ seg->deq = deq_next;
+ }
+ return (void *)deq;
+}
+
+static uint64_t xhci_get_trb_phys(struct xhci_seg *seg, uint64_t trb)
+{
+ return seg->trbs_dma + (trb - (uint64_t)seg->trbs);
+}
+
+static uint32_t xhci_trb_get_index(struct xhci_seg *seg, struct xhci_transfer_trb *trb)
+{
+ return trb - (struct xhci_transfer_trb *)seg->trbs;
+}
+
+static int usb_kb = false;
+static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys,
+ void *data, int datalen)
+{
+ struct xhci_dev *xdev;
+ struct xhci_seg *seg;
+ struct xhci_hcd *xhcd;
+ struct xhci_transfer_trb *trb;
+ struct xhci_db_regs *dbr;
+ int ret = true;
+ uint32_t slot_id, epno, time;
+ uint64_t trb_phys, event_phys;
+
+ if (!pipe->dev || !pipe->dev->hcidev) {
+ dprintf(" NULL pointer\n");
+ dprintf(" pipe dev %p hcidev %p\n", pipe->dev, pipe->dev->hcidev);
+ return false;
+ }
+
+ xdev = pipe->dev->priv;
+ slot_id = xdev->slot_id;
+ seg = xhci_pipe_get_seg(pipe);
+ xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv;
+ dbr = xhcd->db_regs;
+ if (!seg || !xdev || !xhcd) {
+ dprintf(" NULL pointer\n");
+ dprintf(" seg %p xdev %p xhcd %p\n", seg, xdev, xhcd);
+ return false;
+ }
+
+ if (datalen > XHCI_MAX_BULK_SIZE) {
+ printf("usb-xhci: bulk transfer size too big\n");
+ return false;
+ }
+
+ trb = xhci_get_trb(seg);
+ trb_phys = xhci_get_trb_phys(seg, (uint64_t)trb);
+ fill_normal_trb(trb, (void *)data, datalen);
+
+ epno = xhci_get_epno(pipe);
+ write_reg32(&dbr->db[slot_id], epno);
+
+ time = SLOF_GetTimer() + USB_TIMEOUT;
+ while (1) {
+ event_phys = xhci_poll_event(xhcd, 0);
+ if (event_phys == trb_phys) {
+ break;
+ } else if (event_phys == 0) { /* polling timed out */
+ ret = false;
+ break;
+ } else
+ usb_kb = true;
+
+ /* transfer timed out */
+ if (time < SLOF_GetTimer())
+ return false;
+ }
+ trb->addr = 0;
+ trb->len = 0;
+ trb->flags = 0;
+ mb();
+
+ return ret;
+}
+
+static int xhci_alloc_pipe_pool(struct xhci_hcd *xhcd)
+{
+ struct xhci_pipe *xpipe, *curr, *prev;
+ unsigned int i, count;
+ long xpipe_phys = 0;
+
+ count = XHCI_PIPE_POOL_SIZE/sizeof(*xpipe);
+ xhcd->pool = xpipe = SLOF_dma_alloc(XHCI_PIPE_POOL_SIZE);
+ if (!xpipe)
+ return -1;
+ xhcd->pool_phys = xpipe_phys = SLOF_dma_map_in(xpipe, XHCI_PIPE_POOL_SIZE, true);
+ dprintf("%s: xpipe %p, xpipe_phys %lx\n", __func__, xpipe, xpipe_phys);
+
+ /* Although an array, link them */
+ for (i = 0, curr = xpipe, prev = NULL; i < count; i++, curr++) {
+ if (prev)
+ prev->pipe.next = &curr->pipe;
+ curr->pipe.next = NULL;
+ prev = curr;
+ }
+
+ if (!xhcd->freelist)
+ xhcd->freelist = &xpipe->pipe;
+ else
+ xhcd->end->next = &xpipe->pipe;
+ xhcd->end = &prev->pipe;
+
+ return 0;
+}
+
+static void xhci_init_bulk_ep(struct usb_dev *dev, struct usb_pipe *pipe)
+{
+ struct xhci_hcd *xhcd;
+ struct xhci_dev *xdev;
+ struct xhci_seg *seg;
+ struct xhci_pipe *xpipe;
+ struct xhci_control_ctx *ctrl;
+ struct xhci_ep_ctx *ep;
+ uint32_t x_epno, val, type;
+
+ if (!pipe || !dev || !dev->priv)
+ return;
+
+ xdev = dev->priv;
+ xhcd = dev->hcidev->priv;
+ dprintf("dir %d\n", pipe->dir);
+ seg = xhci_pipe_get_seg(pipe);
+ xpipe = xhci_pipe_get_xpipe(pipe);
+ if (pipe->dir) {
+ type = EP_BULK_IN;
+ seg = &xdev->bulk_in;
+ }
+ else {
+ type = EP_BULK_OUT;
+ seg = &xdev->bulk_out;
+ }
+
+ if (!seg->trbs) {
+ if (!xhci_alloc_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK)) {
+ printf("usb-xhci: allocation failed for bulk endpoint\n");
+ return;
+ }
+ } else {
+ xhci_init_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK);
+ }
+
+ pipe->mps = XHCI_MAX_BULK_SIZE;
+ ctrl = xhci_get_control_ctx(&xdev->in_ctx);
+ x_epno = xhci_get_epno(pipe);
+ ep = xhci_get_ep_ctx(&xdev->in_ctx, xdev->ctx_size, x_epno);
+ val = EP_TYPE(type) | MAX_BURST(0) | ERROR_COUNT(3) |
+ MAX_PACKET_SIZE(pipe->mps);
+ ep->field2 = cpu_to_le32(val);;
+ ep->deq_addr = cpu_to_le64(seg->trbs_dma | seg->cycle_state);
+ ep->field4 = cpu_to_le32(8);
+ ctrl->a_flags = cpu_to_le32(BIT(x_epno) | 0x1);
+ ctrl->d_flags = 0;
+ xhci_configure_ep(xhcd, xdev->slot_id, xdev->in_ctx.dma_addr);
+ xpipe->seg = seg;
+}
+
+static int xhci_get_pipe_intr(struct usb_pipe *pipe,
+ struct xhci_hcd *xhcd,
+ char *buf, size_t len)
+{
+ struct xhci_dev *xdev;
+ struct xhci_seg *seg;
+ struct xhci_pipe *xpipe;
+ struct xhci_control_ctx *ctrl;
+ struct xhci_ep_ctx *ep;
+ uint32_t x_epno, val, type;
+ struct usb_dev *dev;
+ struct xhci_transfer_trb *trb;
+
+ dev = pipe->dev;
+ if (dev->class != DEV_HID_KEYB)
+ return false;
+
+ xdev = dev->priv;
+ pipe->mps = 8;
+ seg = xhci_pipe_get_seg(pipe);
+ xpipe = xhci_pipe_get_xpipe(pipe);
+ type = EP_INT_IN;
+ seg = &xdev->intr;
+
+ if (!seg->trbs) {
+ if (!xhci_alloc_seg(seg, XHCI_INTR_TRBS_SIZE, TYPE_BULK)) {
+ printf("usb-xhci: allocation failed for interrupt endpoint\n");
+ return false;
+ }
+ } else {
+ xhci_init_seg(seg, XHCI_EVENT_TRBS_SIZE, TYPE_BULK);
+ }
+
+ xpipe->buflen = pipe->mps * XHCI_INTR_TRBS_SIZE/(sizeof(struct xhci_transfer_trb));
+ xpipe->buf = SLOF_dma_alloc(xpipe->buflen);
+ xpipe->buf_phys = SLOF_dma_map_in(xpipe->buf, xpipe->buflen, false);
+
+ ctrl = xhci_get_control_ctx(&xdev->in_ctx);
+ x_epno = xhci_get_epno(pipe);
+ ep = xhci_get_ep_ctx(&xdev->in_ctx, xdev->ctx_size, x_epno);
+ val = EP_TYPE(type) | MAX_BURST(0) | ERROR_COUNT(3) |
+ MAX_PACKET_SIZE(pipe->mps);
+ ep->field2 = cpu_to_le32(val);
+ ep->deq_addr = cpu_to_le64(seg->trbs_dma | seg->cycle_state);
+ ep->field4 = cpu_to_le32(8);
+ ctrl->a_flags = cpu_to_le32(BIT(x_epno) | 0x1);
+ ctrl->d_flags = 0;
+ xhci_configure_ep(xhcd, xdev->slot_id, xdev->in_ctx.dma_addr);
+ xpipe->seg = seg;
+
+ trb = xhci_get_trb(seg);
+ buf = (char *)(xpipe->buf_phys + xhci_trb_get_index(seg, trb) * pipe->mps);
+ fill_normal_trb(trb, (void *)buf, pipe->mps);
+ return true;
+}
+
+static struct usb_pipe* xhci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, char *buf, size_t len)
+{
+ struct xhci_hcd *xhcd;
+ struct usb_pipe *new = NULL;
+
+ if (!dev)
+ return NULL;
+
+ xhcd = (struct xhci_hcd *)dev->hcidev->priv;
+ if (!xhcd->freelist) {
+ dprintf("usb-xhci: %s allocating pool\n", __func__);
+ if (xhci_alloc_pipe_pool(xhcd))
+ return NULL;
+ }
+
+ new = xhcd->freelist;
+ xhcd->freelist = xhcd->freelist->next;
+ if (!xhcd->freelist)
+ xhcd->end = NULL;
+
+ memset(new, 0, sizeof(*new));
+ new->dev = dev;
+ new->next = NULL;
+ new->type = ep->bmAttributes & USB_EP_TYPE_MASK;
+ new->speed = dev->speed;
+ new->mps = ep->wMaxPacketSize;
+ new->dir = (ep->bEndpointAddress & 0x80) >> 7;
+ new->epno = ep->bEndpointAddress & 0x0f;
+
+ if (new->type == USB_EP_TYPE_INTR) {
+ if (!xhci_get_pipe_intr(new, xhcd, buf, len)) {
+ printf("usb-xhci: %s alloc_intr failed %p\n",
+ __func__, new);
+ }
+ }
+ if (new->type == USB_EP_TYPE_BULK)
+ xhci_init_bulk_ep(dev, new);
+
+ return new;
+}
+
+static void xhci_put_pipe(struct usb_pipe *pipe)
+{
+ struct xhci_hcd *xhcd;
+ struct xhci_pipe *xpipe;
+
+ dprintf("usb-xhci: %s enter - %p\n", __func__, pipe);
+ if (!pipe || !pipe->dev)
+ return;
+ xhcd = pipe->dev->hcidev->priv;
+
+ dprintf("dir %d\n", pipe->dir);
+ if (pipe->type == USB_EP_TYPE_BULK) {
+ xpipe = xhci_pipe_get_xpipe(pipe);
+ xpipe->seg = NULL;
+ } else if (pipe->type == USB_EP_TYPE_INTR) {
+ xpipe = xhci_pipe_get_xpipe(pipe);
+ SLOF_dma_map_out(xpipe->buf_phys, xpipe->buf, xpipe->buflen);
+ SLOF_dma_free(xpipe->buf, xpipe->buflen);
+ xpipe->seg = NULL;
+ }
+ if (xhcd->end)
+ xhcd->end->next = pipe;
+ else
+ xhcd->freelist = pipe;
+
+ xhcd->end = pipe;
+ pipe->next = NULL;
+ pipe->dev = NULL;
+ memset(pipe, 0, sizeof(*pipe));
+
+ dprintf("usb-xhci: %s exit\n", __func__);
+}
+
+static int xhci_poll_intr(struct usb_pipe *pipe, uint8_t *data)
+{
+ struct xhci_transfer_trb *trb;
+ struct xhci_seg *seg;
+ struct xhci_pipe *xpipe;
+ struct xhci_dev *xdev;
+ struct xhci_hcd *xhcd;
+ struct xhci_db_regs *dbr;
+ uint32_t x_epno;
+ uint8_t *buf, ret = 1;
+
+ if (!pipe || !pipe->dev || !pipe->dev->hcidev)
+ return 0;
+ xdev = pipe->dev->priv;
+ xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv;
+ x_epno = xhci_get_epno(pipe);
+ seg = xhci_pipe_get_seg(pipe);
+ xpipe = xhci_pipe_get_xpipe(pipe);
+
+ if (usb_kb == true) {
+ /* This event was consumed by bulk transfer */
+ usb_kb = false;
+ xhci_get_trb_deq(seg);
+ goto skip_poll;
+ }
+
+ /* Ring the doorbell - x_epno */
+ dbr = xhcd->db_regs;
+ write_reg32(&dbr->db[xdev->slot_id], x_epno);
+ if (!xhci_poll_event(xhcd, XHCI_POLL_NO_WAIT)) {
+ return 0;
+ }
+ mb();
+ trb = xhci_get_trb_deq(seg);
+ buf = xpipe->buf + xhci_trb_get_index(seg, trb) * pipe->mps;
+ memcpy(data, buf, 8);
+ memset(buf, 0, 8);
+
+skip_poll:
+ trb = xhci_get_trb(seg);
+ buf = (uint8_t *)(xpipe->buf_phys + xhci_trb_get_index(seg, trb) * pipe->mps);
+ fill_normal_trb(trb, (void *)buf, pipe->mps);
+ return ret;
+}
+
+struct usb_hcd_ops xhci_ops = {
+ .name = "xhci-hcd",
+ .init = xhci_init,
+ .exit = xhci_exit,
+ .usb_type = USB_XHCI,
+ .get_pipe = xhci_get_pipe,
+ .put_pipe = xhci_put_pipe,
+ .poll_intr = xhci_poll_intr,
+ .send_ctrl = xhci_send_ctrl,
+ .transfer_bulk = xhci_transfer_bulk,
+ .next = NULL,
+};
+
+void usb_xhci_register(void)
+{
+ usb_hcd_register(&xhci_ops);
+}
diff --git a/roms/SLOF/lib/libusb/usb-xhci.h b/roms/SLOF/lib/libusb/usb-xhci.h
new file mode 100644
index 000000000..8e94a4b03
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb-xhci.h
@@ -0,0 +1,378 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * Definitions for XHCI Controller - Revision 1.0 (5/21/10)
+ *
+ */
+
+#ifndef USB_XHCI_H
+#define USB_XHCI_H
+
+#include <stdint.h>
+#include "usb-core.h"
+
+#define BIT(x) (1 << x)
+
+/* 5.3 Host Controller Capability Registers
+ * Table 19
+ */
+struct xhci_cap_regs {
+ uint8_t caplength;
+ uint8_t reserved;
+ uint16_t hciversion;
+ uint32_t hcsparams1;
+ uint32_t hcsparams2;
+ uint32_t hcsparams3;
+ uint32_t hccparams;
+#define XHCI_HCCPARAMS_CSZ BIT(2)
+#define XHCI_HCCPARAMS_XECP(x) ((x & 0xFFFF0000) >> 16)
+ uint32_t dboff;
+ uint32_t rtsoff;
+} __attribute__ ((packed, aligned(4)));
+
+/* USB 3.0: Section 7 and 7.2 */
+#define XHCI_XECP_CAP_ID(x) ((x & 0xF))
+#define XHCI_XECP_CAP_SP 2
+#define XHCI_XECP_CAP_SP_MN(x) ((x & 0xFF0000) >> 16)
+#define XHCI_XECP_CAP_SP_MJ(x) ((x & 0xFF000000) >> 24)
+#define XHCI_XECP_CAP_SP_PC(x) ((x & 0xFF00) >> 8)
+#define XHCI_XECP_CAP_SP_PO(x) (x & 0xFF)
+#define XHCI_XECP_NEXT_PTR(x) ((x & 0xFF00) >> 8)
+
+/* Table 27: Host Controller USB Port Register Set */
+struct xhci_port_regs {
+ uint32_t portsc;
+#define PORTSC_CCS BIT(0)
+#define PORTSC_PED BIT(1)
+#define PORTSC_OCA BIT(3)
+#define PORTSC_PR BIT(4)
+#define PORTSC_PLS_MASK (0xF << 5)
+#define PORTSC_PLS_U0 0
+#define PORTSC_PLS_U1 1
+#define PORTSC_PLS_U2 2
+#define PORTSC_PLS_U3 3
+#define PORTSC_PLS_DISABLED 4
+#define PORTSC_PLS_RXDETECT 5
+#define PORTSC_PLS_INACTIVE 6
+#define PORTSC_PLS_POLLING 7
+#define PORTSC_PLS_RECOVERY 8
+#define PORTSC_PLS_HOTRESET 9
+#define PORTSC_PLS_COMP_MODE 10
+#define PORTSC_PLS_TEST_MODE 11
+#define PORTSC_PLS_RESUME 15
+#define PORTSC_PP BIT(9)
+#define PORTSC_PS_MASK (0xF << 10)
+#define PORTSC_PIC_MASK (0x3 << 14)
+#define PORTSC_LWS BIT(16)
+#define PORTSC_CSC BIT(17)
+#define PORTSC_PEC BIT(18)
+#define PORTSC_WRC BIT(19)
+#define PORTSC_OCC BIT(20)
+#define PORTSC_PRC BIT(21)
+#define PORTSC_PLC BIT(22)
+#define PORTSC_CEC BIT(23)
+#define PORTSC_CAS BIT(24)
+#define PORTSC_WCE BIT(25)
+#define PORTSC_WDE BIT(26)
+#define PORTSC_WOE BIT(27)
+#define PORTSC_DR BIT(30)
+#define PORTSC_WPR BIT(31)
+
+ uint32_t portpmsc;
+ uint32_t portli;
+ uint32_t reserved;
+} __attribute__ ((packed, aligned(4)));
+
+struct port_state {
+ bool PP;
+ bool CCS;
+ bool PED;
+ bool PR;
+ uint8_t PLS;
+ char *state;
+};
+
+/* 5.4 Host Controller Operational Registers
+ * Table 26
+ */
+struct xhci_op_regs {
+ uint32_t usbcmd;
+#define XHCI_USBCMD_RS BIT(0)
+#define XHCI_USBCMD_HCRST BIT(1)
+
+ uint32_t usbsts;
+#define XHCI_USBSTS_HCH BIT(0)
+#define XHCI_USBSTS_CNR BIT(11)
+
+ uint32_t pagesize;
+ uint8_t reserved[8]; /* 0C - 13 */
+ uint32_t dnctrl; /* Device notification control */
+ uint64_t crcr; /* Command ring control */
+#define XHCI_CRCR_CRP_MASK 0xFFFFFFFFFFFFFFC0
+#define XHCI_CRCR_CRR BIT(3)
+#define XHCI_CRCR_CRP_SIZE 4096
+
+ uint8_t reserved1[16]; /* 20 - 2F */
+ uint64_t dcbaap; /* Device Context Base Address Array Pointer */
+#define XHCI_DCBAAP_MAX_SIZE 2048
+
+ uint32_t config; /* Configure */
+#define XHCI_CONFIG_MAX_SLOT 44
+
+ uint8_t reserved2[964]; /* 3C - 3FF */
+ /* USB Port register set */
+#define XHCI_PORT_MAX 256
+ struct xhci_port_regs prs[XHCI_PORT_MAX];
+} __attribute__ ((packed, aligned(8)));
+
+/*
+ * 5.5.2 Interrupter Register Set
+ * Table 42: Interrupter Registers
+ */
+struct xhci_int_regs {
+ uint32_t iman;
+ uint32_t imod;
+ uint32_t erstsz;
+#define XHCI_ERST_SIZE_MASK 0xFFFF
+ uint32_t reserved;
+ uint64_t erstba;
+#define XHCI_ERST_ADDR_MASK (~(0x3FUL))
+ uint64_t erdp;
+#define XHCI_ERDP_MASK (~(0xFUL))
+} __attribute__ ((packed, aligned(8)));
+
+/* 5.5 Host Controller Runtime Registers */
+struct xhci_run_regs {
+ uint32_t mfindex; /* microframe index */
+ uint8_t reserved[28];
+#define XHCI_IRS_MAX 1024
+ struct xhci_int_regs irs[XHCI_IRS_MAX];
+} __attribute__ ((packed, aligned(8)));
+
+/* 5.6 Doorbell Registers*/
+struct xhci_db_regs {
+ uint32_t db[256];
+} __attribute__ ((packed, aligned(4)));
+
+#define COMP_SUCCESS 1
+
+#define TRB_SLOT_ID(x) (((x) & (0xFF << 24)) >> 24)
+#define TRB_CMD_SLOT_ID(x) ((x & 0xFF) << 24)
+#define TRB_TYPE(x) (((x) & (0x3F << 10)) >> 10)
+#define TRB_CMD_TYPE(x) ((x & 0x3F) << 10)
+#define TRB_STATUS(x) (((x) & (0xFF << 24)) >> 24)
+#define TRB_ADDR_LOW(x) ((uint32_t)((uint64_t)(x)))
+#define TRB_ADDR_HIGH(x) ((uint32_t)((uint64_t)(x) >> 32))
+#define TRB_TRT(x) (((x) & 0x3) << 16 )
+#define TRB_DIR_IN BIT(16)
+#define TRB_IOC BIT(5)
+#define TRB_IDT BIT(6)
+
+#define TRB_CYCLE_STATE BIT(0)
+
+struct xhci_transfer_trb {
+ uint64_t addr;
+ uint32_t len;
+ uint32_t flags;
+} __attribute__ ((packed));
+
+struct xhci_link_trb {
+ uint64_t addr;
+ uint32_t field2;
+ uint32_t field3;
+} __attribute__ ((packed));
+
+/* Event TRB */
+struct xhci_event_trb {
+ uint64_t addr;
+ uint32_t status;
+ uint32_t flags;
+} __attribute__ ((packed));
+
+#define TRB_NORMAL 1
+#define TRB_SETUP_STAGE 2
+#define TRB_DATA_STAGE 3
+#define TRB_STATUS_STAGE 4
+#define TRB_ISOCH 5
+#define TRB_LINK 6
+#define TRB_EVENT_DATA 7
+#define TRB_NOOP 8
+#define TRB_ENABLE_SLOT 9
+#define TRB_DISABLE_SLOT 10
+#define TRB_ADDRESS_DEV 11
+#define TRB_CONFIG_EP 12
+#define TRB_EVAL_CNTX 13
+#define TRB_TRANSFER_EVENT 32
+#define TRB_CMD_COMPLETION 33
+#define TRB_PORT_STATUS 34
+
+struct xhci_command_trb {
+ uint32_t field[4];
+}__attribute__ ((packed));
+
+union xhci_trb {
+ struct xhci_event_trb event;
+ struct xhci_transfer_trb xfer;
+ struct xhci_command_trb cmd;
+ struct xhci_link_trb link;
+};
+
+enum xhci_seg_type {
+ TYPE_CTRL = 0,
+ TYPE_BULK,
+ TYPE_COMMAND,
+ TYPE_EVENT,
+};
+
+struct xhci_seg {
+ union xhci_trb *trbs;
+ struct xhci_seg *next;
+ uint64_t enq;
+ uint64_t deq;
+ uint64_t trbs_dma;
+ uint32_t size;
+ uint32_t cycle_state;
+ enum xhci_seg_type type;
+};
+
+#define XHCI_TRB_SIZE 16
+#define XHCI_EVENT_TRBS_SIZE 4096
+#define XHCI_CONTROL_TRBS_SIZE 4096
+#define XHCI_DATA_TRBS_SIZE 4096
+#define XHCI_INTR_TRBS_SIZE 4096
+#define XHCI_ERST_NUM_SEGS 1
+
+#define XHCI_POLL_NO_WAIT 1
+
+#define XHCI_MAX_BULK_SIZE 0xF000
+
+struct xhci_erst_entry {
+ uint64_t addr;
+ uint32_t size;
+ uint32_t reserved;
+} __attribute__ ((packed, aligned(8)));
+
+struct xhci_erst {
+ struct xhci_erst_entry *entries;
+ uint64_t dma;
+ uint32_t num_segs; /* number of segments */
+};
+
+struct xhci_control_ctx {
+ uint32_t d_flags;
+ uint32_t a_flags;
+ uint32_t reserved[6];
+} __attribute__ ((packed));
+
+struct xhci_slot_ctx {
+ uint32_t field1;
+#define SLOT_SPEED_FS BIT(20)
+#define SLOT_SPEED_LS BIT(21)
+#define SLOT_SPEED_HS BIT(22)
+#define SLOT_SPEED_SS BIT(23)
+#define LAST_CONTEXT(x) (x << 27)
+
+ uint32_t field2;
+#define ROOT_HUB_PORT(x) ((x & 0xff) << 16)
+
+ uint32_t field3;
+ uint32_t field4;
+#define USB_DEV_ADDRESS(x) (x & 0xFFU)
+#define SLOT_STATE(x) ((x >> 27) & 0x1FU)
+#define SLOT_STATE_DIS_ENA 0
+#define SLOT_STATE_DEFAULT 1
+#define SLOT_STATE_ADDRESSED 2
+#define SLOT_STATE_CONFIGURED 3
+
+
+ uint32_t reserved[4];
+} __attribute__ ((packed));
+
+struct xhci_ep_ctx {
+ uint32_t field1;
+ uint32_t field2;
+#define MAX_PACKET_SIZE(x) (((x) & 0xFFFF) << 16)
+#define MAX_BURST(x) (((x) & 0xFF) << 8)
+#define EP_TYPE(x) (((x) & 0x07) << 3)
+#define EP_ISOC_OUT 1
+#define EP_BULK_OUT 2
+#define EP_INT_OUT 3
+#define EP_CTRL 4
+#define EP_ISOC_IN 5
+#define EP_BULK_IN 6
+#define EP_INT_IN 7
+
+#define ERROR_COUNT(x) (((x) & 0x03) << 1)
+
+ uint64_t deq_addr;
+ uint32_t field4;
+ uint32_t reserved[3];
+} __attribute__ ((packed));
+
+struct xhci_ctx {
+ uint8_t type;
+#define XHCI_CTX_TYPE_DEVICE 0x1
+#define XHCI_CTX_TYPE_INPUT 0x2
+ uint32_t size;
+ uint8_t *addr;
+#define XHCI_CTX_BUF_SIZE 4096
+ uint64_t dma_addr;
+};
+
+struct xhci_dev {
+ struct usb_dev *dev;
+ uint32_t slot_id;
+ struct xhci_ctx in_ctx;
+ struct xhci_ctx out_ctx;
+ struct xhci_seg control;
+ struct xhci_seg intr;
+ struct xhci_seg bulk_in;
+ struct xhci_seg bulk_out;
+ uint32_t ctx_size;
+};
+
+struct xhci_hcd {
+ struct xhci_cap_regs *cap_regs;
+ struct xhci_op_regs *op_regs;
+ struct xhci_run_regs *run_regs;
+ struct xhci_db_regs *db_regs;
+ struct usb_hcd_dev *hcidev;
+ struct xhci_dev xdevs[XHCI_CONFIG_MAX_SLOT + 1];
+ struct usb_pipe *freelist;
+ struct usb_pipe *end;
+ uint64_t *dcbaap;
+ uint64_t dcbaap_dma;
+ struct xhci_seg ering;
+ struct xhci_seg crseg;
+ struct xhci_erst erst;
+ uint64_t erds_dma;
+ uint32_t erds_size;
+ uint32_t slot_id;
+ uint32_t hcc_csz_64;
+ void *pool;
+#define XHCI_PIPE_POOL_SIZE 4096
+
+ long pool_phys;
+};
+
+struct xhci_pipe {
+ struct usb_pipe pipe;
+ struct xhci_seg *seg;
+ void *buf;
+ long buf_phys;
+ uint32_t buflen;
+};
+
+extern bool usb3_dev_init(struct xhci_hcd *xhcd, struct usb_dev *hub,
+ uint32_t port, uint32_t slotspeed);
+
+#endif /* USB_XHCI_H */
diff --git a/roms/SLOF/lib/libusb/usb.code b/roms/SLOF/lib/libusb/usb.code
new file mode 100644
index 000000000..fd92d9e78
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb.code
@@ -0,0 +1,162 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * libusb bindings for SLOF - implementation
+ */
+
+#include <usb.h>
+
+
+/************************************************/
+/* Register with the usb-core */
+/* SLOF: USB-OHCI-REGISTER ( -- ) */
+/* LIBNEWUSB: usb_ohci_register(void) */
+/************************************************/
+PRIM(USB_X2d_OHCI_X2d_REGISTER)
+ usb_ohci_register();
+MIRP
+
+/************************************************/
+/* Register with the usb-core */
+/* SLOF: USB-EHCI-REGISTER ( -- ) */
+/* LIBNEWUSB: usb_ehci_register(void) */
+/************************************************/
+PRIM(USB_X2d_EHCI_X2d_REGISTER)
+ usb_ehci_register();
+MIRP
+
+/************************************************/
+/* Register with the usb-core */
+/* SLOF: USB-XHCI-REGISTER ( -- ) */
+/* LIBNEWUSB: usb_xhci_register(void) */
+/************************************************/
+PRIM(USB_X2d_XHCI_X2d_REGISTER)
+ usb_xhci_register();
+MIRP
+
+/************************************************/
+/* Initialize hcidev with the usb-core */
+/* SLOF: USB-HCD-INIT ( hcidev -- ) */
+/* LIBNEWUSB: usb_hcd_init(hcidev) */
+/************************************************/
+PRIM(USB_X2d_HCD_X2d_INIT)
+ void *hcidev = TOS.a; POP;
+ usb_hcd_init(hcidev);
+MIRP
+
+/************************************************/
+/* Remove hcidev with the usb-core */
+/* SLOF: USB-HCD-EXIT ( hcidev -- ) */
+/* LIBNEWUSB: usb_hcd_exit(hcidev) */
+/************************************************/
+PRIM(USB_X2d_HCD_X2d_EXIT)
+ void *hcidev = TOS.a; POP;
+ usb_hcd_exit(hcidev);
+MIRP
+
+/************************************************/
+/* Initialize hid */
+/* SLOF: USB-HID-INIT ( dev -- true | false )*/
+/* LIBNEWUSB: usb_hid_init(hcidev) */
+/************************************************/
+PRIM(USB_X2d_HID_X2d_INIT)
+ void *dev = TOS.a;
+ TOS.n = usb_hid_init(dev);
+MIRP
+
+/************************************************/
+/* Exit hid */
+/* SLOF: USB-HID-EXIT ( dev -- true | false )*/
+/* LIBNEWUSB: usb_hid_exit(hcidev) */
+/************************************************/
+PRIM(USB_X2d_HID_X2d_EXIT)
+ void *dev = TOS.a;
+ TOS.n = usb_hid_exit(dev);
+MIRP
+
+/************************************************/
+/* Read usb keyboard for key */
+/* SLOF: USB-READ-KEYB ( dev -- */
+/* ( key | false )) */
+/* LIBNEWUSB: usb_read_keyb */
+/************************************************/
+PRIM(USB_X2d_READ_X2d_KEYB)
+ void *dev = TOS.a;
+ TOS.n = usb_read_keyb(dev);
+MIRP
+
+/************************************************/
+/* Is USB KEY available */
+/* SLOF: USB-KEY-AVAILABLE ( dev -- ( true | */
+/* false ))*/
+/* LIBNEWUSB: usb_key_available */
+/************************************************/
+PRIM(USB_X2d_KEY_X2d_AVAILABLE)
+ void *dev = TOS.a;
+ TOS.n = usb_key_available(dev);
+MIRP
+
+/************************************************/
+/* Initialize and enumerate generic hub */
+/* SLOF: USB-HUB-INIT ( dev -- true | false ) */
+/* LIBNEWUSB: usb_hub_init */
+/************************************************/
+PRIM(USB_X2d_HUB_X2d_INIT)
+ void *dev = TOS.a;
+ TOS.n = usb_hub_init(dev);
+MIRP
+
+/************************************************/
+/* Initialize msc */
+/* SLOF: USB-MSC-INIT ( dev -- true | false )*/
+/* LIBNEWUSB: usb_msc_init(hcidev) */
+/************************************************/
+PRIM(USB_X2d_MSC_X2d_INIT)
+ void *dev = TOS.a;
+ TOS.n = usb_msc_init(dev);
+MIRP
+
+/************************************************/
+/* Exit msc */
+/* SLOF: USB-MSC-EXIT ( dev -- true | false )*/
+/* LIBNEWUSB: usb_msc_exit(hcidev) */
+/************************************************/
+PRIM(USB_X2d_MSC_X2d_EXIT)
+ void *dev = TOS.a;
+ TOS.n = usb_msc_exit(dev);
+MIRP
+
+/*****************************************************************************/
+/* Transfer data through control endpoint */
+/* SLOF: USB-TRANSFER_CTRL ( dev req data -- true | false ) */
+/* LIBNEWUSB: int usb_transfer_ctrl(void *dev, void *req, void *data) */
+/*****************************************************************************/
+PRIM(USB_X2d_TRANSFER_X2d_CTRL)
+ void *data = TOS.a; POP;
+ void *req = TOS.a; POP;
+ TOS.n = usb_transfer_ctrl(TOS.a, req, data);
+MIRP
+
+/*****************************************************************************/
+/* Transfer data through bulk endpoint */
+/* SLOF: USB-TRANSFER_BULK ( dev dir td td-phys data size -- true | false ) */
+/* LIBNEWUSB: int usb_transfer_bulk(void *dev, int dir, void *td, */
+/* void *td_phys, void *data, int size) */
+/*****************************************************************************/
+PRIM(USB_X2d_TRANSFER_X2d_BULK)
+ int size = TOS.u; POP;
+ void *data = TOS.a; POP;
+ void *td_phys = TOS.a; POP;
+ void *td = TOS.a; POP;
+ int dir = TOS.u; POP;
+ TOS.n = usb_transfer_bulk(TOS.a, dir, td, td_phys, data, size);
+MIRP
diff --git a/roms/SLOF/lib/libusb/usb.h b/roms/SLOF/lib/libusb/usb.h
new file mode 100644
index 000000000..fba19d2a1
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb.h
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * Copyright (c) 2006, 2012, 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * prototypes for libusb implementation used in libusb.code
+ */
+
+#ifndef __LIBUSB_H
+#define __LIBUSB_H
+
+/*******************************************/
+/* SLOF: USB-OHCI-REGISTER */
+/*******************************************/
+extern void usb_ohci_register(void);
+/*******************************************/
+/* SLOF: USB-EHCI-REGISTER */
+/*******************************************/
+extern void usb_ehci_register(void);
+/*******************************************/
+/* SLOF: USB-XHCI-REGISTER */
+/*******************************************/
+extern void usb_xhci_register(void);
+/*******************************************/
+/* SLOF: USB-HCD-INIT */
+/*******************************************/
+extern void usb_hcd_init(void *hcidev);
+/*******************************************/
+/* SLOF: USB-HCD-EXIT */
+/*******************************************/
+extern void usb_hcd_exit(void *hcidev);
+/*******************************************/
+/* SLOF: USB-HID-INIT */
+/*******************************************/
+extern int usb_hid_init(void *dev);
+/*******************************************/
+/* SLOF: USB-HID-EXIT */
+/*******************************************/
+extern int usb_hid_exit(void *dev);
+/*******************************************/
+/* SLOF: USB-READ-KEYB */
+/*******************************************/
+extern unsigned char usb_read_keyb(void *dev);
+/*******************************************/
+/* SLOF: USB-KEY-AVAILABLE */
+/*******************************************/
+extern unsigned char usb_key_available(void *dev);
+/*******************************************/
+/* SLOF: USB-HUB-INIT */
+/*******************************************/
+extern unsigned int usb_hub_init(void *dev);
+/*******************************************/
+/* SLOF: USB-MSC-INIT */
+/*******************************************/
+extern int usb_msc_init(void *dev);
+/*******************************************/
+/* SLOF: USB-MSC-EXIT */
+/*******************************************/
+extern int usb_msc_exit(void *dev);
+/*******************************************/
+/* SLOF: USB-TRANSFER-CTRL */
+/*******************************************/
+extern int usb_transfer_ctrl(void *dev, void *req, void *data);
+/*******************************************/
+/* SLOF: USB-TRANSFER-BULK */
+/*******************************************/
+extern int usb_transfer_bulk(void *dev, int dir, void *td,
+ void *td_phys, void *data, int size);
+
+#endif
diff --git a/roms/SLOF/lib/libusb/usb.in b/roms/SLOF/lib/libusb/usb.in
new file mode 100644
index 000000000..7ceba7d2d
--- /dev/null
+++ b/roms/SLOF/lib/libusb/usb.in
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * Copyright (c) 2007, 2012, 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * libusb bindings for SLOF - definitions
+ */
+
+cod(USB-OHCI-REGISTER)
+cod(USB-EHCI-REGISTER)
+cod(USB-XHCI-REGISTER)
+cod(USB-HCD-INIT)
+cod(USB-HCD-EXIT)
+cod(USB-HID-INIT)
+cod(USB-HID-EXIT)
+cod(USB-READ-KEYB)
+cod(USB-KEY-AVAILABLE)
+cod(USB-HUB-INIT)
+cod(USB-MSC-INIT)
+cod(USB-MSC-EXIT)
+cod(USB-TRANSFER-CTRL)
+cod(USB-TRANSFER-BULK)
diff --git a/roms/SLOF/lib/libveth/Makefile b/roms/SLOF/lib/libveth/Makefile
new file mode 100644
index 000000000..dd1234af1
--- /dev/null
+++ b/roms/SLOF/lib/libveth/Makefile
@@ -0,0 +1,52 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
+CPPFLAGS += -I../libhvcall
+
+LDFLAGS = -nostdlib
+
+TARGET = ../libveth.a
+
+
+all: $(TARGET)
+
+SRCS = veth.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libveth/veth.c b/roms/SLOF/lib/libveth/veth.c
new file mode 100644
index 000000000..a8e19ba41
--- /dev/null
+++ b/roms/SLOF/lib/libveth/veth.c
@@ -0,0 +1,277 @@
+/******************************************************************************
+ * Copyright (c) 2011, 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <helpers.h>
+#include "veth.h"
+#include "libhvcall.h"
+
+#undef VETH_DEBUG
+//#define VETH_DEBUG
+#ifdef VETH_DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+/* *** WARNING: We pass our addresses as-is as DMA addresses,
+ * we -do- rely on the forth code to have enabled TCE bypass
+ * on our device !
+ */
+#define vaddr_to_dma(vaddr) ((uint64_t)vaddr)
+
+struct ibmveth_buf_desc_fields {
+ uint32_t flags_len;
+#define IBMVETH_BUF_VALID 0x80000000
+#define IBMVETH_BUF_TOGGLE 0x40000000
+#define IBMVETH_BUF_NO_CSUM 0x02000000
+#define IBMVETH_BUF_CSUM_GOOD 0x01000000
+#define IBMVETH_BUF_LEN_MASK 0x00FFFFFF
+ uint32_t address;
+};
+
+union ibmveth_buf_desc {
+ uint64_t desc;
+ struct ibmveth_buf_desc_fields fields;
+};
+
+struct ibmveth_rx_q_entry {
+ uint32_t flags_off;
+#define IBMVETH_RXQ_TOGGLE 0x80000000
+#define IBMVETH_RXQ_TOGGLE_SHIFT 31
+#define IBMVETH_RXQ_VALID 0x40000000
+#define IBMVETH_RXQ_NO_CSUM 0x02000000
+#define IBMVETH_RXQ_CSUM_GOOD 0x01000000
+#define IBMVETH_RXQ_OFF_MASK 0x0000FFFF
+
+ uint32_t length;
+ uint64_t correlator;
+};
+
+static void *buffer_list;
+static void *filter_list;
+static uint64_t *rx_bufs;
+static uint64_t *rx_bufs_aligned;
+static uint32_t cur_rx_toggle;
+static uint32_t cur_rx_index;
+
+#define RX_QUEUE_SIZE 256
+#define RX_BUF_SIZE 2048
+#define RX_BUF_MULT (RX_BUF_SIZE >> 3)
+
+static struct ibmveth_rx_q_entry *rx_queue;
+
+static inline uint64_t *veth_get_rx_buf(unsigned int i)
+{
+ return &rx_bufs_aligned[i * RX_BUF_MULT];
+}
+
+static int veth_init(net_driver_t *driver)
+{
+ char *mac_addr;
+ union ibmveth_buf_desc rxq_desc;
+ unsigned long rx_queue_len = sizeof(struct ibmveth_rx_q_entry) *
+ RX_QUEUE_SIZE;
+ unsigned int i;
+ long rc;
+
+ if (!driver)
+ return -1;
+
+ dprintf("veth_init(%02x:%02x:%02x:%02x:%02x:%02x)\n",
+ mac_addr[0], mac_addr[1], mac_addr[2],
+ mac_addr[3], mac_addr[4], mac_addr[5]);
+
+ if (driver->running != 0)
+ return 0;
+
+ mac_addr = (char *)driver->mac_addr;
+ cur_rx_toggle = IBMVETH_RXQ_TOGGLE;
+ cur_rx_index = 0;
+ buffer_list = SLOF_alloc_mem_aligned(8192, 4096);
+ filter_list = buffer_list + 4096;
+ rx_queue = SLOF_alloc_mem_aligned(rx_queue_len, 16);
+ rx_bufs = SLOF_alloc_mem(2048 * RX_QUEUE_SIZE + 4);
+ if (!buffer_list || !filter_list || !rx_queue || !rx_bufs) {
+ printf("veth: Failed to allocate memory !\n");
+ goto fail;
+ }
+ rx_bufs_aligned = (uint64_t *)(((uint64_t)rx_bufs | 3) + 1);
+ rxq_desc.fields.address = vaddr_to_dma(rx_queue);
+ rxq_desc.fields.flags_len = IBMVETH_BUF_VALID | rx_queue_len;
+
+ rc = h_register_logical_lan(driver->reg,
+ vaddr_to_dma(buffer_list),
+ rxq_desc.desc,
+ vaddr_to_dma(filter_list),
+ (*(uint64_t *)mac_addr) >> 16);
+ if (rc != H_SUCCESS) {
+ printf("veth: Error %ld registering interface !\n", rc);
+ goto fail;
+ }
+ for (i = 0; i < RX_QUEUE_SIZE; i++) {
+ uint64_t *buf = veth_get_rx_buf(i);
+ union ibmveth_buf_desc desc;
+ *buf = (uint64_t)buf;
+ desc.fields.address = vaddr_to_dma(buf);
+ desc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE;
+ h_add_logical_lan_buffer(driver->reg, desc.desc);
+ }
+
+ driver->running = 1;
+
+ return 0;
+ fail:
+ if (buffer_list)
+ SLOF_free_mem(buffer_list, 8192);
+ if (rx_queue)
+ SLOF_free_mem(rx_queue, rx_queue_len);
+ if (rx_bufs)
+ SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4);
+ return -1;
+}
+
+static int veth_term(net_driver_t *driver)
+{
+ dprintf("veth_term()\n");
+
+ if (driver->running == 0)
+ return 0;
+
+ h_free_logical_lan(driver->reg);
+
+ if (buffer_list)
+ SLOF_free_mem(buffer_list, 8192);
+ if (rx_queue)
+ SLOF_free_mem(rx_queue, sizeof(struct ibmveth_rx_q_entry) * RX_QUEUE_SIZE);
+ if (rx_bufs)
+ SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4);
+
+ driver->running = 0;
+
+ return 0;
+}
+
+static int veth_receive(char *f_buffer_pc, unsigned f_len_i, net_driver_t *driver)
+{
+ int packet = 0;
+
+ dprintf("veth_receive()\n");
+
+ while(!packet) {
+ struct ibmveth_rx_q_entry *desc = &rx_queue[cur_rx_index];
+ union ibmveth_buf_desc bdesc;
+ void *buf;
+
+ buf = (void *)desc->correlator;
+
+ if ((desc->flags_off & IBMVETH_RXQ_TOGGLE) != cur_rx_toggle)
+ break;
+
+ if (!(desc->flags_off & IBMVETH_RXQ_VALID))
+ goto recycle;
+ if (desc->length > f_len_i) {
+ printf("veth: Dropping too big packet [%d bytes]\n",
+ desc->length);
+ goto recycle;
+ }
+
+ packet = desc->length;
+ memcpy(f_buffer_pc,
+ buf + (desc->flags_off & IBMVETH_RXQ_OFF_MASK), packet);
+ recycle:
+ bdesc.fields.address = vaddr_to_dma(buf);
+ bdesc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE;
+ h_add_logical_lan_buffer(driver->reg, bdesc.desc);
+
+ cur_rx_index = (cur_rx_index + 1) % RX_QUEUE_SIZE;
+ if (cur_rx_index == 0)
+ cur_rx_toggle ^= IBMVETH_RXQ_TOGGLE;
+ }
+
+ return packet;
+}
+
+static int veth_xmit(char *f_buffer_pc, int f_len_i, net_driver_t *driver)
+{
+ union ibmveth_buf_desc tx_desc;
+ long rc;
+
+ dprintf("veth_xmit(packet at %p, %d bytes)\n", f_buffer_pc, f_len_i);
+
+ tx_desc.fields.address = vaddr_to_dma(f_buffer_pc);
+ tx_desc.fields.flags_len = IBMVETH_BUF_VALID | f_len_i;
+
+ rc = hv_send_logical_lan(driver->reg, tx_desc.desc, 0, 0, 0, 0, 0);
+ if (rc != H_SUCCESS) {
+ printf("veth: Error %ld sending packet !\n", rc);
+ return -1;
+ }
+
+ return f_len_i;
+}
+
+net_driver_t *libveth_open(char *mac_addr, unsigned mac_len, char *reg, unsigned reg_len)
+{
+ net_driver_t *driver;
+
+ if (reg_len != sizeof(uint32_t)) {
+ printf("vio reg must 1 cell long\n");
+ return NULL;
+ }
+ driver = SLOF_alloc_mem(sizeof(*driver));
+ if (!driver) {
+ printf("Unable to allocate veth driver\n");
+ return NULL;
+ }
+
+ /* veth uses a 8-byte wide property instead of 6-byte wide MACs */
+ if ((mac_len == 8) && (mac_addr[0] == 0) && mac_addr[1] == 0)
+ mac_addr += 2;
+ memcpy(driver->mac_addr, mac_addr, 6);
+ driver->reg = *(uint32_t *)reg;
+ driver->running = 0;
+
+ if (veth_init(driver)) {
+ SLOF_free_mem(driver, sizeof(*driver));
+ return NULL;
+ }
+
+ return driver;
+}
+
+void libveth_close(net_driver_t *driver)
+{
+ if (driver) {
+ veth_term(driver);
+ SLOF_free_mem(driver, sizeof(*driver));
+ }
+}
+
+int libveth_read(char *buf, int len, net_driver_t *driver)
+{
+ if (buf)
+ return veth_receive(buf, len, driver);
+
+ return -1;
+}
+
+int libveth_write(char *buf, int len, net_driver_t *driver)
+{
+ if (buf)
+ return veth_xmit(buf, len, driver);
+
+ return -1;
+}
diff --git a/roms/SLOF/lib/libveth/veth.code b/roms/SLOF/lib/libveth/veth.code
new file mode 100644
index 000000000..76d14a968
--- /dev/null
+++ b/roms/SLOF/lib/libveth/veth.code
@@ -0,0 +1,61 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * libveth Forth wrapper
+ */
+
+#include <veth.h>
+
+// : libveth-open ( mac-addr-str len reg-str len -- false | [ driver true ] )
+PRIM(LIBVETH_X2d_OPEN)
+{
+ int reg_len = TOS.u; POP;
+ char *reg = TOS.a; POP;
+ int len = TOS.u; POP;
+ char *mac_addr = TOS.a;
+
+ net_driver_t *net_driver = libveth_open(mac_addr, len, reg, reg_len);
+ if (net_driver) {
+ TOS.u = (unsigned long)net_driver; PUSH;
+ TOS.n = -1;
+ } else
+ TOS.n = 0;
+}
+MIRP
+
+// : libveth-close ( driver -- )
+PRIM(LIBVETH_X2d_CLOSE)
+{
+ net_driver_t *driver = TOS.a; POP;
+ libveth_close(driver);
+}
+MIRP
+
+
+// : libveth-read ( addr len driver -- actual )
+PRIM(LIBVETH_X2d_READ)
+{
+ net_driver_t *driver = TOS.a; POP;
+ int len = TOS.u; POP;
+ TOS.n = libveth_read(TOS.a, len, driver);
+}
+MIRP
+
+// : libveth-write ( addr len driver -- actual )
+PRIM(LIBVETH_X2d_WRITE)
+{
+ net_driver_t *driver = TOS.a; POP;
+ int len = TOS.u; POP;
+ TOS.n = libveth_write(TOS.a, len, driver);
+}
+MIRP
diff --git a/roms/SLOF/lib/libveth/veth.h b/roms/SLOF/lib/libveth/veth.h
new file mode 100644
index 000000000..6a1cb4cb5
--- /dev/null
+++ b/roms/SLOF/lib/libveth/veth.h
@@ -0,0 +1,24 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ ******************************************************************************/
+
+#ifndef _VETH_H
+#define _VETH_H
+
+#include <stdint.h>
+#include <netdriver.h>
+
+extern net_driver_t *libveth_open(char *mac_addr, unsigned mac_len, char *reg, unsigned reg_len);
+extern void libveth_close(net_driver_t *driver);
+extern int libveth_read(char *buf, int len, net_driver_t *driver);
+extern int libveth_write(char *buf, int len, net_driver_t *driver);
+
+#endif
diff --git a/roms/SLOF/lib/libveth/veth.in b/roms/SLOF/lib/libveth/veth.in
new file mode 100644
index 000000000..dc684fe9c
--- /dev/null
+++ b/roms/SLOF/lib/libveth/veth.in
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * libveth bindings for Forth - definitions
+ */
+
+cod(LIBVETH-OPEN)
+cod(LIBVETH-CLOSE)
+cod(LIBVETH-READ)
+cod(LIBVETH-WRITE)
diff --git a/roms/SLOF/lib/libvirtio/Makefile b/roms/SLOF/lib/libvirtio/Makefile
new file mode 100644
index 000000000..87d951390
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/Makefile
@@ -0,0 +1,55 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
+LDFLAGS = -nostdlib
+
+TARGET = ../libvirtio.a
+
+
+all: $(TARGET)
+
+SRCS = virtio.c virtio-blk.c p9.c virtio-9p.c virtio-scsi.c virtio-net.c virtio-serial.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+%.o: %.S
+ $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
+
diff --git a/roms/SLOF/lib/libvirtio/p9.c b/roms/SLOF/lib/libvirtio/p9.c
new file mode 100644
index 000000000..e9ba22886
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/p9.c
@@ -0,0 +1,575 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <byteorder.h>
+#include "p9.h"
+
+
+/* Protocol stack marshaling. */
+uint8_t *sp;
+
+#define GET_08(s,i) (s)[(i)]
+#define GET_16(s,i) le16_to_cpu(*(uint16_t*)(&(s)[(i)]))
+#define GET_32(s,i) le32_to_cpu(*(uint32_t*)(&(s)[(i)]))
+#define GET_64(s,i) le64_to_cpu(*(uint64_t*)(&(s)[(i)]))
+
+#define SET_08(s,i,v) (s)[(i)] = (v)
+#define SET_16(s,i,v) *(uint16_t*)(&(s)[(i)]) = cpu_to_le16(v)
+#define SET_32(s,i,v) *(uint32_t*)(&(s)[(i)]) = cpu_to_le32(v)
+#define SET_64(s,i,v) *(uint64_t*)(&(s)[(i)]) = cpu_to_le64(v)
+
+#define PUT_08(v) sp[0] = (v);sp+=1
+#define PUT_16(v) *(uint16_t*)(&sp[0]) = cpu_to_le16(v);sp+=2
+#define PUT_32(v) *(uint32_t*)(&sp[0]) = cpu_to_le32(v);sp+=4
+#define PUT_64(v) *(uint64_t*)(&sp[0]) = cpu_to_le64(v);sp+=8
+
+#define PUT_HD(m,t) PUT_32(0);PUT_08(m);PUT_16(t)
+#define PUT_SN(v,n) PUT_16(n);memcpy(sp,(v),(n));sp+=n
+#define PUT_ST(v) PUT_16(strlen(v));memcpy(sp,(v),strlen(v));\
+ sp+=strlen(v)
+
+#define GET_SIZE (sp - tx)
+
+
+/* General defines. */
+#define MIN(a,b) ((a)>(b)?(b):(a))
+
+#define NOTAG ((uint16_t)~0)
+#define NOFID ((uint32_t)~0)
+#define TAG 1
+#define BUF_SIZE (8*1024)
+
+#define VERSION "9P2000.u"
+#define UNKNOWN_VER "unknown"
+
+#define MSG_SIZE 0
+#define MSG_ID 4
+#define MSG_ERR 0x6b
+#define MSG_ERR_STR 9
+#define MSG_ERR_STR_LEN 7
+#define MSG_TAG 5
+#define MSG_VER_MSIZE 7
+#define MSG_VER_STR_LEN 11
+#define MSG_VER_STR 13
+#define MSG_WALK_TX_ELMT 15
+#define MSG_WALK_RX_ELMT 7
+#define MSG_SIZE 0
+#define MSG_WALK_MAX_ELMT 16
+#define MSG_QID_SIZE 13
+#define MSG_WALK_RX_HDR_SIZE 9
+#define MSG_OPEN_IOUNIT 20
+#define MSG_OPEN_MODE_MASK 0x5f
+#define MSG_READ_COUNT 7
+#define MSG_READ_DATA 11
+#define MSG_STAT_LEN 42
+#define MSG_STAT_TYPE 17
+
+#define T_VERSION 100
+#define R_VERSION (T_VERSION + 1)
+#define T_ATTACH 104
+#define R_ATTACH (T_ATTACH + 1)
+#define T_ERROR 106
+#define R_ERROR (T_ERROR + 1)
+#define T_WALK 110
+#define R_WALK (T_WALK + 1)
+#define T_OPEN 112
+#define R_OPEN (T_OPEN + 1)
+#define T_READ 116
+#define R_READ (T_READ + 1)
+#define T_CLUNK 120
+#define R_CLUNK (T_CLUNK + 1)
+#define T_STAT 124
+#define R_STAT (T_STAT + 1)
+
+static p9_transact_t transact;
+static void *transact_opaque;
+static uint8_t *tx;
+static uint8_t *rx;
+
+
+/**
+ * p9_reg_transport
+ *
+ * Registers a transport function for use by the P9 protocol. The transport
+ * connects the P9 Client (this library) to a server instance.
+ *
+ * @param transact_func[in] Function pointer to type p9_transact_t.
+ * @param tx_buffer[in] TX buffer, must be 8k in size.
+ * @param rx_buffer[in] RX buffer, must be 8k in size.
+ */
+void p9_reg_transport(p9_transact_t transact_func, void *opaque,
+ uint8_t *tx_buffer, uint8_t *rx_buffer)
+{
+ transact = transact_func;
+ transact_opaque = opaque;
+ tx = tx_buffer;
+ rx = rx_buffer;
+}
+
+/**
+ * reset_buffers
+ *
+ * Reset the RX and TX buffers to BUF_SIZE (8k) and reset the Stack Pointer
+ * for the TX buffer, which is referenced by the PUT_* macro's.
+ */
+void reset_buffers(void)
+{
+ memset(tx, 0, BUF_SIZE);
+ memset(rx, 0, BUF_SIZE);
+ sp = tx;
+}
+
+/**
+ * p9_transaction
+ *
+ * Perform a transaction (send/recv) over the registered transport.
+ *
+ * @param connection[in|out] Connection object.
+ * @return 0 = success, -ve = error.
+ */
+int p9_transaction(p9_connection_t *connection)
+{
+ int rc;
+ uint32_t tx_size = GET_SIZE;
+ uint32_t rx_size = connection->message_size;
+
+ if (transact == NULL) {
+ return P9_NO_TRANSPORT;
+ }
+ if (tx == NULL || rx == NULL) {
+ return P9_NO_BUFFER;
+ }
+ if (connection->message_size > BUF_SIZE) {
+ return P9_MSG_SIZE_TOO_BIG;
+ }
+ if (tx_size > connection->message_size) {
+ return P9_MSG_TOO_LONG;
+ }
+
+ SET_32(tx, MSG_SIZE, tx_size);
+ rc = transact(transact_opaque, tx, tx_size, rx, &rx_size);
+
+ if (rc != 0) {
+ return P9_TRANSPORT_ERROR;
+ }
+ if (GET_16(tx, MSG_TAG) != GET_16(rx, MSG_TAG)) {
+ return P9_UNEXPECTED_TAG;
+ }
+ if (GET_08(rx, MSG_ID) == MSG_ERR) {
+ char error_string[200];
+
+ memset(error_string, 0, 200);
+ strncpy(error_string, (char *)&rx[MSG_ERR_STR],
+ MIN(200 - 1, GET_16(rx, MSG_ERR_STR_LEN)));
+#ifndef TEST
+ printf("\nError: %s\n", error_string);
+#endif
+ return P9_R_ERROR;
+ }
+ if ((GET_08(tx, MSG_ID) + 1) != GET_08(rx, MSG_ID)) {
+ return P9_UNEXPECTED_MSG;
+ }
+
+ return 0;
+}
+
+/**
+ * p9_version
+ *
+ * Called to start a session. Negotiates the maximum message size for the
+ * P9 protocol.
+ *
+ * @param connection[in|out] Connection object, contains message_size.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Tversion tag[2] msize[4] version[s]
+ * size[4] Rversion tag[2] msize[4] version[s]
+ */
+int p9_version(p9_connection_t *connection)
+{
+ int rc;
+ char *ver_str;
+ int ver_len;
+
+ reset_buffers();
+
+ /* Build message. */
+ PUT_HD(T_VERSION, NOTAG);
+ PUT_32(connection->message_size);
+ PUT_ST(VERSION);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Handle response. */
+ connection->message_size = MIN(connection->message_size,
+ GET_32(rx, MSG_VER_MSIZE));
+
+ ver_str = (char *)&rx[MSG_VER_STR];
+ ver_len = GET_16(rx, MSG_VER_STR_LEN);
+ if (strncmp(UNKNOWN_VER, ver_str, ver_len) == 0) {
+ return P9_UNKNOWN_VERSION;
+ }
+
+
+ return 0;
+}
+
+/**
+ * p9_attach
+ *
+ * Called to open a connection for a user to a file tree on the server. There
+ * is no authorisation undertaken (NOFID).
+ *
+ * @param connection[in|out] Connection object, contains uname and aname as
+ * well as the connection fid and returned qid.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4]
+ * size[4] Rattach tag[2] qid[13]
+ */
+int p9_attach(p9_connection_t *connection)
+{
+ int rc;
+ unsigned length = 19 + strlen(connection->uname) + strlen(connection->aname);
+
+ if (length > connection->message_size) {
+ return P9_MSG_TOO_LONG;
+ }
+
+ reset_buffers();
+
+ /* Build message. */
+ PUT_HD(T_ATTACH, TAG);
+ PUT_32(connection->fid);
+ PUT_32(NOFID);
+ PUT_ST(connection->uname);
+ PUT_ST(connection->aname);
+ PUT_32(~0); /* ??? */
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+
+ return 0;
+}
+
+/**
+ * p9_clunk
+ *
+ * Called when closing a file or connection (or after failed opens). Tells the
+ * server that the supplied fid is no longer needed by this client.
+ *
+ * @param connection[in|out] Connection object.
+ * @param fid[in] Fid to be clunked (released) on the server.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Tclunk tag[2] fid[4]
+ * size[4] Rclunk tag[2]
+ */
+int p9_clunk(p9_connection_t *connection, uint32_t fid)
+{
+ int rc;
+
+ reset_buffers();
+
+ /* Build message. */
+ PUT_HD(T_CLUNK, TAG);
+ PUT_32(fid);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+
+ return 0;
+}
+
+/**
+ * p9_walk
+ *
+ * Walk the provided path to a file (or directory) starting at the directory
+ * indicated by fid and assigning new_fid to the last successfully walked
+ * element. If not all elements of the path can be walked then the pos
+ * pointer is set to the part of the path following the last successful
+ * walked element. The function can be called again to walk the remainder
+ * of the path (or produce an error).
+ *
+ * @param connection[in] Connection object.
+ * @param fid[in] Fid to start walk from, must be directory or root (from
+ * call to p9_attach).
+ * @param new_fid[in] Fid to be used for the last walked element.
+ * @param pos[in|out] Position in path that remains to be walked. If the
+ * path was completely walked without error this will point to the NULL
+ * at the end of path.
+ * @return 1 = partial walk, 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
+ * size[4] Rwalk tag[2] nwqid[2] nwqid*(qid[13])
+ */
+int p9_walk(p9_connection_t *connection, uint32_t fid, uint32_t new_fid,
+ uint8_t **pos)
+{
+ int rc;
+ const char *path = (const char *)*pos;
+ uint8_t *s_tok;
+ uint8_t *e_tok;
+ int element_count = 0;
+
+ if (path == NULL) {
+ *pos = NULL;
+ return P9_NULL_PATH;
+ }
+
+ reset_buffers();
+
+ /* Build message. */
+ PUT_HD(T_WALK, TAG); /* Length to 0, set later. */
+ PUT_32(fid);
+ PUT_32(new_fid);
+ PUT_16(0); /* Element count to 0, set later. */
+
+ /* Get elements from path, and append to message. */
+ s_tok = (uint8_t *)path;
+ e_tok = s_tok;
+
+ while (*s_tok != 0) {
+ while (*s_tok == '/') {
+ s_tok++;
+ }
+ e_tok = s_tok;
+ while ((*e_tok != '/') && (*e_tok != 0)) {
+ e_tok++;
+ }
+
+ /* Check the element is OK. */
+ if (strncmp(".", (const char *)s_tok, (e_tok - s_tok)) == 0) {
+ /* Don't send ".", continue to next. */
+ s_tok = e_tok;
+ continue;
+ }
+ unsigned tx_size = (e_tok - s_tok + 2 + GET_SIZE);
+ unsigned rx_size = ((element_count + 1) * MSG_QID_SIZE
+ + MSG_WALK_RX_HDR_SIZE);
+ if ((tx_size > connection->message_size)
+ || (rx_size > connection->message_size)) {
+ /*
+ * Element makes TX msg too long OR expected RX msg
+ * too long. Move pos to previous element and do
+ * partial walk.
+ */
+ e_tok = s_tok;
+ if (*(e_tok - 1) == '/') {
+ e_tok--;
+ }
+ break;
+ }
+
+ /* Add the element to the message. */
+ PUT_SN(s_tok, e_tok - s_tok);
+ element_count++;
+
+ /* Server supports no more than 16 elements, partial walk. */
+ if (element_count == MSG_WALK_MAX_ELMT) {
+ break;
+ }
+
+ /* Ready to find the next element. */
+ s_tok = e_tok;
+ }
+
+ if ((element_count == 0) && (strlen(path) > 0)) {
+ return P9_PATH_ELEMENT_TOO_LONG;
+ }
+
+ *pos = e_tok;
+
+ /* Update counts and then send message. */
+ SET_16(tx, MSG_WALK_TX_ELMT, element_count);
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Check for special return conditions. */
+ if (element_count != GET_16(rx, MSG_WALK_RX_ELMT)) {
+ /* Find the last element successfully walked */
+ s_tok = (uint8_t *)path;
+ e_tok = s_tok;
+ element_count = GET_16(rx, MSG_WALK_RX_ELMT);
+
+ while (element_count--) {
+ while (*s_tok == '/') {
+ s_tok++;
+ }
+
+ e_tok = s_tok;
+
+ while ((*e_tok != '/') && (*e_tok != 0)) {
+ e_tok++;
+ }
+
+ s_tok = e_tok;
+ }
+
+ *pos = e_tok;
+ }
+ if (**pos != 0) {
+ rc = P9_PARTIAL_WALK;
+ }
+
+
+ return rc;
+}
+
+/**
+ * p9_open
+ *
+ * Opens the file represented by fid with associated mode bit mask. The iounit
+ * size returned from the server is written to the connection object.
+ *
+ * @param file[in|out] File object, contains fid for file.
+ * @param mode[in] Mode to open with. Bit's 0=R, 1=W, 2=RW, 3=EX, 4=Trunc
+ * and 6=Delete on Close.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Topen tag[2] fid[4] mode[1]
+ * size[4] Ropen tag[2] qid[13] iounit[4]
+ */
+int p9_open(p9_file_t *file, uint8_t mode)
+{
+ int rc;
+ p9_connection_t *connection = file->connection;
+
+ reset_buffers();
+ file->iounit = 0;
+
+ /* Build message. */
+ PUT_HD(T_OPEN, TAG);
+ PUT_32(file->fid);
+ PUT_08(mode & MSG_OPEN_MODE_MASK);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Handle response. */
+ file->iounit = GET_32(rx, MSG_OPEN_IOUNIT);
+
+
+ return 0;
+}
+
+/**
+ * p9_read
+ *
+ * Reads the file in to buffer.
+ *
+ * @param file[in] File object, contains fid for file.
+ * @param buffer[out] Buffer for data.
+ * @param count[in] Number of bytes to read (less bytes than requested
+ * may be read).
+ * @param offset[in] Offset in file to read from.
+ * @return Bytes read, -ve = error.
+ *
+ * @remark
+ * size[4] Tread tag[2] fid[4] offset[8] count[4]
+ * size[4] Rread tag[2] count[4] data[count]
+ */
+int p9_read(p9_file_t *file, uint8_t *buffer,
+ uint32_t count, uint64_t offset)
+{
+ int rc;
+ p9_connection_t *connection = file->connection;
+ uint32_t got;
+
+ reset_buffers();
+ count = MIN((connection->message_size - MSG_READ_DATA), count);
+
+ /* Build message. */
+ PUT_HD(T_READ, TAG);
+ PUT_32(file->fid);
+ PUT_64(offset);
+ PUT_32(count);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+ got = GET_32(rx, MSG_READ_COUNT);
+ if (got > count) {
+ return P9_READ_UNEXPECTED_DATA;
+ }
+
+ /* Handle response. */
+ memcpy(buffer, &rx[MSG_READ_DATA], got);
+
+ return got;
+}
+
+/**
+ * p9_stat
+ *
+ * Stat's the fid and writes the type and length to the file object.
+ *
+ * @param file[in|out] File object, contains fid for file.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Tstat tag[2] fid[4]
+ * size[4] Rstat tag[2] size[2] stat[n]
+ */
+int p9_stat(p9_file_t *file)
+{
+ int rc;
+ p9_connection_t *connection = file->connection;
+
+ reset_buffers();
+ file->length = 0;
+ file->type = 0;
+
+ /* Build message. */
+ PUT_HD(T_STAT, TAG);
+ PUT_32(file->fid);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Handle response. */
+ file->length = GET_64(rx, MSG_STAT_LEN);
+ file->type = GET_08(rx, MSG_STAT_TYPE);
+
+
+ return 0;
+}
diff --git a/roms/SLOF/lib/libvirtio/p9.h b/roms/SLOF/lib/libvirtio/p9.h
new file mode 100644
index 000000000..3a35e80ed
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/p9.h
@@ -0,0 +1,68 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef P9_H
+#define P9_H
+
+#include <stdint.h>
+
+
+#define P9_ERROR -1
+#define P9_UNKNOWN_VERSION -2
+#define P9_R_ERROR -3
+#define P9_MSG_TOO_LONG -4
+#define P9_UNEXPECTED_MSG -5
+#define P9_UNEXPECTED_TAG -6
+#define P9_TRANSPORT_ERROR -7
+#define P9_NO_TRANSPORT -8
+#define P9_NULL_PATH -9
+#define P9_PATH_ELEMENT_TOO_LONG -10
+#define P9_READ_UNEXPECTED_DATA -11
+#define P9_NO_BUFFER -12
+#define P9_MSG_SIZE_TOO_BIG -13
+
+#define P9_PARTIAL_WALK 1
+
+typedef int (*p9_transact_t)(void *opaque, uint8_t *tx, int tx_size,
+ uint8_t *rx, uint32_t *rx_size);
+
+typedef struct {
+ uint32_t message_size;
+ char *uname; /* User name. */
+ char *aname; /* Tree/mount name/path. */
+ uint32_t fid; /* Represents mount point. */
+} p9_connection_t;
+
+typedef struct {
+ uint32_t fid; /* Identifies the file to P9 server. */
+ uint32_t iounit; /* Maximum read size in bytes. */
+ uint8_t type; /* Type of file. */
+ uint64_t length; /* Length of file. */
+ p9_connection_t *connection;
+} p9_file_t;
+
+
+void reset_buffers(void);
+void p9_reg_transport(p9_transact_t transact_func, void *opaque,
+ uint8_t *tx_buffer, uint8_t *rx_buffer);
+int p9_transaction(p9_connection_t *connection);
+int p9_version(p9_connection_t *connection);
+int p9_attach(p9_connection_t *connection);
+int p9_clunk(p9_connection_t *connection, uint32_t fid);
+int p9_walk(p9_connection_t *connection, uint32_t fid, uint32_t new_fid,
+ uint8_t **pos);
+int p9_open(p9_file_t *file, uint8_t mode);
+int p9_read(p9_file_t *file, uint8_t *buffer,
+ uint32_t count, uint64_t offset);
+int p9_stat(p9_file_t *file);
+
+#endif
diff --git a/roms/SLOF/lib/libvirtio/virtio-9p.c b/roms/SLOF/lib/libvirtio/virtio-9p.c
new file mode 100644
index 000000000..76078612b
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-9p.c
@@ -0,0 +1,340 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <byteorder.h>
+#include <cpu.h>
+
+#include "virtio-9p.h"
+#include "p9.h"
+#include "virtio-internal.h"
+
+/**
+ * Notes for 9P Server config:
+ *
+ * make distclean; cm make qemu
+ * sudo cp boot_rom.bin /opt/qemu/share/qemu/slof.bin
+ * /opt/qemu/bin/qemu-system-ppc64 -M pseries -m 512 -boot d -nographic -fsdev
+ * local,id=trule,path=/home/trule/virtfs,security_model=none -device
+ * virtio-9p-spapr,fsdev=trule,mount_tag=trule
+ * load virtfs:\some\file
+ */
+
+/* We support only one instance due to the (ab)use of globals. We
+ * use the buffer size as an open marker as well.
+ */
+static int __buf_size;
+
+
+#define ROOT_FID 1
+#define FILE_FID 2
+#define TAG_SIZE 128
+#define MIN(a,b) ((a)>(b)?(b):(a))
+
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+#ifdef DEBUG
+static void dprint_buffer(const char *name, uint8_t *buffer, int length)
+{
+ int i;
+
+ printf("*** %s ***", name);
+
+ for (i = 0; i < length; i++) {
+ if (i % 16 == 0) {
+ printf("\n %04x:", i);
+ }
+
+ printf(" %02x", buffer[i]);
+ }
+
+ printf("\n");
+}
+#else
+#define dprint_buffer(n, b, l)
+#endif
+
+/**
+ * virtio_9p_transact
+ *
+ * Perform a 9P transaction over the VIRTIO queue interface. This function is
+ * registered with the p9.c library via p9_reg_transport() to provide
+ * connectivity to the 9P server.
+ *
+ * @param tx[in] Data to send, mapped to first queue item.
+ * @param tx_size[in] Size of data to send.
+ * @param rx[out] Data to receive, mappend to second queue item.
+ * @param rx_size[out] Size of data received.
+ * @return 0 = success, -ve = error.
+ */
+static int virtio_9p_transact(void *opaque, uint8_t *tx, int tx_size, uint8_t *rx,
+ uint32_t *rx_size)
+{
+ struct virtio_device *dev = opaque;
+ int id, i;
+ uint32_t vq_size;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+ struct vqs *vq = &dev->vq[0];
+
+ /* Virt IO queues. */
+ vq_size = virtio_get_qsize(dev, 0);
+
+ last_used_idx = vq->used->idx;
+ current_used_idx = &vq->used->idx;
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq->avail->idx);
+
+ /* Determine descriptor index */
+ id = (avail_idx * 3) % vq_size;
+
+ /* TX in first queue item. */
+ dprint_buffer("TX", tx, tx_size);
+
+ virtio_fill_desc(vq, id, dev->features, (uint64_t)tx, tx_size,
+ VRING_DESC_F_NEXT, id + 1);
+
+ /* RX in the second queue item. */
+ virtio_fill_desc(vq, id + 1, dev->features, (uint64_t)rx,
+ *rx_size,
+ VRING_DESC_F_WRITE, 0);
+
+ /* Tell HV that the queue is ready */
+ vq->avail->ring[avail_idx % vq_size] = virtio_cpu_to_modern16 (dev, id);
+ mb();
+ vq->avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
+ virtio_queue_notify(dev, 0);
+
+ /* Receive the response. */
+ i = 10000000;
+ while (*current_used_idx == last_used_idx && i-- > 0) {
+ // do something better
+ mb();
+ }
+
+ virtio_free_desc(vq, id, dev->features);
+ virtio_free_desc(vq, id + 1, dev->features);
+
+ if (i == 0) {
+ return -1;
+ }
+
+ *rx_size = MIN(*rx_size, le32_to_cpu(*(uint32_t*)(&rx[0])));
+ dprint_buffer("RX", rx, *rx_size);
+
+ return 0;
+}
+
+/**
+ * virtio_9p_init
+ *
+ * Establish the VIRTIO connection for use with the 9P server. Setup queues
+ * and negotiate capabilities. Setup the 9P (Client) library.
+ *
+ * @param reg[in] Pointer to device tree node for VIRTIO/9P interface.
+ * @param tx_buf[in] TX buffer for use by 9P Client lib - 8K in size.
+ * @param rx_buf[in] TX buffer for use by 9P Client lib - 8K in size.
+ * @param buf_size Somewhat redundant, buffer size expected to be 8k.
+ * @return 0 = success, -ve = error.
+ */
+int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf,
+ int buf_size)
+{
+ struct vqs *vq;
+ int status = VIRTIO_STAT_ACKNOWLEDGE;
+
+ /* Check for double open */
+ if (__buf_size)
+ return -1;
+ __buf_size = buf_size;
+
+ dprintf("%s : device at %p\n", __func__, dev->base);
+ dprintf("%s : type is %04x\n", __func__, dev->type);
+
+ virtio_reset_device(dev);
+
+ /* Acknowledge device. */
+ virtio_set_status(dev, status);
+
+ /* Tell HV that we know how to drive the device. */
+ status |= VIRTIO_STAT_DRIVER;
+ virtio_set_status(dev, status);
+
+ /* Device specific setup - we do not support special features */
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ if (virtio_negotiate_guest_features(dev, VIRTIO_F_VERSION_1))
+ goto dev_error;
+ virtio_get_status(dev, &status);
+ } else {
+ virtio_set_guest_features(dev, 0);
+ }
+
+ vq = virtio_queue_init_vq(dev, 0);
+ if (!vq)
+ goto dev_error;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(dev, status);
+
+ /* Setup 9P library. */
+ p9_reg_transport(virtio_9p_transact, dev,(uint8_t *)tx_buf,
+ (uint8_t *)rx_buf);
+
+ dprintf("%s : complete\n", __func__);
+ return 0;
+
+dev_error:
+ printf("%s: failed\n", __func__);
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(dev, status);
+ return -1;
+}
+
+/**
+ * virtio_9p_shutdown
+ */
+void virtio_9p_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+
+ __buf_size = 0;
+}
+
+/**
+ * virtio_9p_load
+ *
+ * Read a file from the 9P Server on the VIRTIO interface.
+ *
+ * @param file_name[in] File to read, use Linux style paths.
+ * @param buffer[out] Where to read the file to.
+ * @return +ve = amount of data read, -ve = error.
+ */
+long virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer)
+{
+ int rc;
+ uint16_t tag_len;
+ char tag_name[TAG_SIZE];
+ uint64_t offset = 0;
+ uint8_t *pos = (uint8_t *)file_name;
+ int start_fid = ROOT_FID;
+ p9_connection_t connection = {
+ .message_size = __buf_size,
+ .fid = ROOT_FID,
+ .uname = "slof"
+ };
+ p9_file_t file = {
+ .connection = &connection,
+ .fid = FILE_FID,
+ };
+
+
+ /* Get the share name from 9P config space. */
+ tag_len = virtio_get_config(dev, 0, sizeof(tag_len));
+ if (tag_len >= TAG_SIZE)
+ tag_len = TAG_SIZE - 1;
+ __virtio_read_config(dev, tag_name, 2, tag_len);
+ connection.aname = tag_name;
+
+ /* Connect to the 9P server. */
+ dprintf("%s : connecting, tag = %s, user = %s, msgsize = %d\n",
+ __func__, connection.aname, connection.uname,
+ connection.message_size);
+ rc = p9_version(&connection);
+ if (rc != 0) {
+ printf("Version check failed, rc = %d\n", rc);
+ goto cleanup_connection;
+ }
+ rc = p9_attach(&connection);
+ if (rc != 0) {
+ printf("Attach failed, rc = %d\n", rc);
+ goto cleanup_connection;
+ }
+ dprintf("%s : connected, msgsize = %d\n", __func__,
+ connection.message_size);
+
+ /* Walk to the file. */
+ do {
+ dprintf("%s : walk path %s\n", __func__, pos);
+ rc = p9_walk(&connection, start_fid, FILE_FID, &pos);
+
+ if (rc < 0) { /* Some error. */
+ printf("Walk failed, rc = %d\n", rc);
+ goto cleanup_connection;
+ }
+
+ /*
+ * If partial walk (*pos != 0) then continue the walk from
+ * mid point with start_fid updated to current position
+ * FILE_FID. FILE_FID will then be reused for the result of
+ * the next call to walk.
+ */
+ start_fid = FILE_FID;
+ } while (*pos != 0);
+
+ /* Open the file. */
+ dprintf("%s : stat and open\n", __func__);
+ rc = p9_stat(&file);
+ if (rc != 0) {
+ printf("Stat failed, rc = %d\n", rc);
+ goto cleanup_file;
+ }
+ rc = p9_open(&file, 0x00); /* TODO find include for "read mode" */
+ if (rc != 0) {
+ printf("Open failed, rc = %d\n", rc);
+ goto cleanup_file;
+ }
+ dprintf("%s : file opened, size %lld\n", __func__, file.length);
+
+ /* Read the file contents to buffer. */
+ while (offset < file.length) {
+ dprintf("%s : read from offset %llu\n", __func__, offset);
+ rc = p9_read(&file, buffer + offset,
+ file.length - offset, offset);
+ dprintf("%s : read done, length was %d\n", __func__, rc);
+ if (rc < 0) {
+ printf("Read failed, rc = %d\n", rc);
+ goto cleanup_file;
+ }
+ if (rc == 0) {
+ break;
+ }
+ offset += rc;
+ rc = 0;
+ }
+
+ /* Cleanup and disconnect. */
+cleanup_file:
+ dprintf("%s : clunking file\n", __func__);
+ p9_clunk(&connection, file.fid);
+
+cleanup_connection:
+ dprintf("%s : clunking connection\n", __func__);
+ p9_clunk(&connection, connection.fid);
+
+
+ dprintf("%s : complete, read %llu bytes\n", __func__, offset);
+ return rc == 0 ? (long)offset : rc;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-9p.h b/roms/SLOF/lib/libvirtio/virtio-9p.h
new file mode 100644
index 000000000..db2cf6f11
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-9p.h
@@ -0,0 +1,32 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef VIRTIO_9P_H_
+#define VIRTIO_9P_H_
+
+#include <stdint.h>
+
+#include "virtio.h"
+
+#if 0
+typedef struct {
+ uint16_t tag_lenth;
+ char tag[0];
+} virtio_9p_config_t;
+#endif
+int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf,
+ int buf_size);
+void virtio_9p_shutdown(struct virtio_device *dev);
+long virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer);
+
+
+#endif /* VIRTIO_9P_H_ */
diff --git a/roms/SLOF/lib/libvirtio/virtio-blk.c b/roms/SLOF/lib/libvirtio/virtio-blk.c
new file mode 100644
index 000000000..0363038e5
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-blk.c
@@ -0,0 +1,209 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <cpu.h>
+#include <helpers.h>
+#include <byteorder.h>
+#include "virtio.h"
+#include "virtio-blk.h"
+#include "virtio-internal.h"
+
+#define DEFAULT_SECTOR_SIZE 512
+#define DRIVER_FEATURE_SUPPORT (VIRTIO_BLK_F_BLK_SIZE | VIRTIO_F_VERSION_1)
+
+/**
+ * Initialize virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+int
+virtioblk_init(struct virtio_device *dev)
+{
+ struct vqs *vq;
+ int blk_size = DEFAULT_SECTOR_SIZE;
+ uint64_t features;
+ int status = VIRTIO_STAT_ACKNOWLEDGE;
+
+ /* Reset device */
+ virtio_reset_device(dev);
+
+ /* Acknowledge device. */
+ virtio_set_status(dev, status);
+
+ /* Tell HV that we know how to drive the device. */
+ status |= VIRTIO_STAT_DRIVER;
+ virtio_set_status(dev, status);
+
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ /* Negotiate features and sets FEATURES_OK if successful */
+ if (virtio_negotiate_guest_features(dev, DRIVER_FEATURE_SUPPORT))
+ goto dev_error;
+
+ virtio_get_status(dev, &status);
+ } else {
+ /* Device specific setup - we support F_BLK_SIZE */
+ virtio_set_guest_features(dev, VIRTIO_BLK_F_BLK_SIZE);
+ }
+
+ vq = virtio_queue_init_vq(dev, 0);
+ if (!vq)
+ goto dev_error;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(dev, status);
+
+ features = virtio_get_host_features(dev);
+ if (features & VIRTIO_BLK_F_BLK_SIZE) {
+ blk_size = virtio_get_config(dev,
+ offset_of(struct virtio_blk_cfg, blk_size),
+ sizeof(blk_size));
+ }
+
+ return blk_size;
+dev_error:
+ printf("%s: failed\n", __func__);
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(dev, status);
+ return 0;
+}
+
+
+/**
+ * Shutdown the virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+void
+virtioblk_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+}
+
+static void fill_blk_hdr(struct virtio_blk_req *blkhdr, bool is_modern,
+ uint32_t type, uint32_t ioprio, uint32_t sector)
+{
+ if (is_modern) {
+ blkhdr->type = cpu_to_le32(type);
+ blkhdr->ioprio = cpu_to_le32(ioprio);
+ blkhdr->sector = cpu_to_le64(sector);
+ } else {
+ blkhdr->type = type;
+ blkhdr->ioprio = ioprio;
+ blkhdr->sector = sector;
+ }
+}
+
+/**
+ * Read / write blocks
+ * @param reg pointer to "reg" property
+ * @param buf pointer to destination buffer
+ * @param blocknum block number of the first block that should be transfered
+ * @param cnt amount of blocks that should be transfered
+ * @param type VIRTIO_BLK_T_OUT for write, VIRTIO_BLK_T_IN for read transfers
+ * @return number of blocks that have been transfered successfully
+ */
+int
+virtioblk_transfer(struct virtio_device *dev, char *buf, uint64_t blocknum,
+ long cnt, unsigned int type)
+{
+ int id;
+ static struct virtio_blk_req blkhdr;
+ //struct virtio_blk_config *blkconf;
+ uint64_t capacity;
+ uint32_t time;
+ struct vqs *vq = &dev->vq[0];
+ volatile uint8_t status = -1;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+ int blk_size = DEFAULT_SECTOR_SIZE;
+
+ //printf("virtioblk_transfer: dev=%p buf=%p blocknum=%lli cnt=%li type=%i\n",
+ // dev, buf, blocknum, cnt, type);
+
+ /* Check whether request is within disk capacity */
+ capacity = virtio_get_config(dev,
+ offset_of(struct virtio_blk_cfg, capacity),
+ sizeof(capacity));
+ if (blocknum + cnt - 1 > capacity) {
+ puts("virtioblk_transfer: Access beyond end of device!");
+ return 0;
+ }
+
+ blk_size = virtio_get_config(dev,
+ offset_of(struct virtio_blk_cfg, blk_size),
+ sizeof(blk_size));
+ if (blk_size % DEFAULT_SECTOR_SIZE) {
+ fprintf(stderr, "virtio-blk: Unaligned sector size %d\n", blk_size);
+ return 0;
+ }
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq->avail->idx);
+
+ last_used_idx = vq->used->idx;
+ current_used_idx = &vq->used->idx;
+
+ /* Set up header */
+ fill_blk_hdr(&blkhdr, dev->features, type | VIRTIO_BLK_T_BARRIER,
+ 1, blocknum * blk_size / DEFAULT_SECTOR_SIZE);
+
+ /* Determine descriptor index */
+ id = (avail_idx * 3) % vq->size;
+
+ /* Set up virtqueue descriptor for header */
+ virtio_fill_desc(vq, id, dev->features, (uint64_t)&blkhdr,
+ sizeof(struct virtio_blk_req),
+ VRING_DESC_F_NEXT, id + 1);
+
+ /* Set up virtqueue descriptor for data */
+ virtio_fill_desc(vq, id + 1, dev->features, (uint64_t)buf,
+ cnt * blk_size,
+ VRING_DESC_F_NEXT | ((type & 1) ? 0 : VRING_DESC_F_WRITE),
+ id + 2);
+
+ /* Set up virtqueue descriptor for status */
+ virtio_fill_desc(vq, id + 2, dev->features,
+ (uint64_t)&status, 1,
+ VRING_DESC_F_WRITE, 0);
+
+ vq->avail->ring[avail_idx % vq->size] = virtio_cpu_to_modern16 (dev, id);
+ mb();
+ vq->avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
+
+ /* Tell HV that the queue is ready */
+ virtio_queue_notify(dev, 0);
+
+ /* Wait for host to consume the descriptor */
+ time = SLOF_GetTimer() + VIRTIO_TIMEOUT;
+ while (*current_used_idx == last_used_idx) {
+ // do something better
+ mb();
+ if (time < SLOF_GetTimer())
+ break;
+ }
+
+ virtio_free_desc(vq, id, dev->features);
+ virtio_free_desc(vq, id + 1, dev->features);
+ virtio_free_desc(vq, id + 2, dev->features);
+
+ if (status == 0)
+ return cnt;
+
+ printf("virtioblk_transfer failed! type=%i, status = %i\n",
+ type, status);
+
+ return 0;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-blk.h b/roms/SLOF/lib/libvirtio/virtio-blk.h
new file mode 100644
index 000000000..21f0adca0
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-blk.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * Virtio block device definitions.
+ * See Virtio Spec, Appendix D, for details
+ */
+
+#ifndef _VIRTIO_BLK_H
+#define _VIRTIO_BLK_H
+
+#include <stdint.h>
+
+
+struct virtio_blk_cfg {
+ uint64_t capacity;
+ uint32_t size_max;
+ uint32_t seg_max;
+ struct virtio_blk_geometry {
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+ } geometry;
+ uint32_t blk_size;
+ uint32_t sectors_max;
+} __attribute__ ((packed)) ;
+
+/* Block request */
+struct virtio_blk_req {
+ uint32_t type ;
+ uint32_t ioprio ;
+ uint64_t sector ;
+};
+
+/* Block request types */
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+#define VIRTIO_BLK_T_SCSI_CMD 2
+#define VIRTIO_BLK_T_SCSI_CMD_OUT 3
+#define VIRTIO_BLK_T_FLUSH 4
+#define VIRTIO_BLK_T_FLUSH_OUT 5
+#define VIRTIO_BLK_T_BARRIER 0x80000000
+
+/* VIRTIO_BLK Feature bits */
+#define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
+
+extern int virtioblk_init(struct virtio_device *dev);
+extern void virtioblk_shutdown(struct virtio_device *dev);
+extern int virtioblk_transfer(struct virtio_device *dev, char *buf,
+ uint64_t blocknum, long cnt, unsigned int type);
+
+#endif /* _VIRTIO_BLK_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio-internal.h b/roms/SLOF/lib/libvirtio/virtio-internal.h
new file mode 100644
index 000000000..fe59c6b99
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-internal.h
@@ -0,0 +1,48 @@
+/******************************************************************************
+ * Copyright (c) 2016 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _LIBVIRTIO_INTERNAL_H
+#define _LIBVIRTIO_INTERNAL_H
+
+#include <byteorder.h>
+
+static inline uint16_t virtio_cpu_to_modern16(struct virtio_device *dev, uint16_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? cpu_to_le16(val) : val;
+}
+
+static inline uint32_t virtio_cpu_to_modern32(struct virtio_device *dev, uint32_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? cpu_to_le32(val) : val;
+}
+
+static inline uint64_t virtio_cpu_to_modern64(struct virtio_device *dev, uint64_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? cpu_to_le64(val) : val;
+}
+
+static inline uint16_t virtio_modern16_to_cpu(struct virtio_device *dev, uint16_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? le16_to_cpu(val) : val;
+}
+
+static inline uint32_t virtio_modern32_to_cpu(struct virtio_device *dev, uint32_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? le32_to_cpu(val) : val;
+}
+
+static inline uint64_t virtio_modern64_to_cpu(struct virtio_device *dev, uint64_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? le64_to_cpu(val) : val;
+}
+
+#endif /* _LIBVIRTIO_INTERNAL_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio-net.c b/roms/SLOF/lib/libvirtio/virtio-net.c
new file mode 100644
index 000000000..5a0d19088
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-net.c
@@ -0,0 +1,384 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * This is the implementation for the Virtio network device driver. Details
+ * about the virtio-net interface can be found in Rusty Russel's "Virtio PCI
+ * Card Specification v0.8.10", appendix C, which can be found here:
+ *
+ * http://ozlabs.org/~rusty/virtio-spec/virtio-spec.pdf
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <helpers.h>
+#include <cache.h>
+#include <byteorder.h>
+#include "virtio-net.h"
+#include "virtio-internal.h"
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+# define dprintf(fmt...) do { printf(fmt); } while(0)
+#else
+# define dprintf(fmt...)
+#endif
+
+#define sync() asm volatile (" sync \n" ::: "memory")
+
+#define DRIVER_FEATURE_SUPPORT (VIRTIO_NET_F_MAC | VIRTIO_F_VERSION_1)
+
+/* See Virtio Spec, appendix C, "Device Operation" */
+struct virtio_net_hdr {
+ uint8_t flags;
+ uint8_t gso_type;
+ uint16_t hdr_len;
+ uint16_t gso_size;
+ uint16_t csum_start;
+ uint16_t csum_offset;
+ // uint16_t num_buffers; /* Only if VIRTIO_NET_F_MRG_RXBUF */
+};
+
+static unsigned int net_hdr_size;
+
+struct virtio_net_hdr_v1 {
+ uint8_t flags;
+ uint8_t gso_type;
+ le16 hdr_len;
+ le16 gso_size;
+ le16 csum_start;
+ le16 csum_offset;
+ le16 num_buffers;
+};
+
+static uint16_t last_rx_idx; /* Last index in RX "used" ring */
+
+/**
+ * Module init for virtio via PCI.
+ * Checks whether we're reponsible for the given device and set up
+ * the virtqueue configuration.
+ */
+static int virtionet_init_pci(struct virtio_net *vnet, struct virtio_device *dev)
+{
+ struct virtio_device *vdev = &vnet->vdev;
+
+ dprintf("virtionet: doing virtionet_init_pci!\n");
+
+ if (!dev)
+ return -1;
+
+ /* make a copy of the device structure */
+ memcpy(vdev, dev, sizeof(struct virtio_device));
+
+ /* Reset device */
+ virtio_reset_device(vdev);
+
+ /* Acknowledge device. */
+ virtio_set_status(vdev, VIRTIO_STAT_ACKNOWLEDGE);
+
+ return 0;
+}
+
+/**
+ * Initialize the virtio-net device.
+ * See the Virtio Spec, chapter 2.2.1 and Appendix C "Device Initialization"
+ * for details.
+ */
+static int virtionet_init(struct virtio_net *vnet)
+{
+ int i;
+ int status = VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER;
+ struct virtio_device *vdev = &vnet->vdev;
+ net_driver_t *driver = &vnet->driver;
+ struct vqs *vq_tx, *vq_rx;
+
+ dprintf("virtionet_init(%02x:%02x:%02x:%02x:%02x:%02x)\n",
+ driver->mac_addr[0], driver->mac_addr[1],
+ driver->mac_addr[2], driver->mac_addr[3],
+ driver->mac_addr[4], driver->mac_addr[5]);
+
+ if (driver->running != 0)
+ return 0;
+
+ /* Tell HV that we know how to drive the device. */
+ virtio_set_status(vdev, status);
+
+ /* Device specific setup */
+ if (vdev->features & VIRTIO_F_VERSION_1) {
+ if (virtio_negotiate_guest_features(vdev, DRIVER_FEATURE_SUPPORT))
+ goto dev_error;
+ net_hdr_size = sizeof(struct virtio_net_hdr_v1);
+ virtio_get_status(vdev, &status);
+ } else {
+ net_hdr_size = sizeof(struct virtio_net_hdr);
+ virtio_set_guest_features(vdev, 0);
+ }
+
+ /* The queue information can be retrieved via the virtio header that
+ * can be found in the I/O BAR. First queue is the receive queue,
+ * second the transmit queue, and the forth is the control queue for
+ * networking options.
+ * We are only interested in the receive and transmit queue here. */
+ vq_rx = virtio_queue_init_vq(vdev, VQ_RX);
+ vq_tx = virtio_queue_init_vq(vdev, VQ_TX);
+ if (!vq_rx || !vq_tx) {
+ virtio_set_status(vdev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER
+ |VIRTIO_STAT_FAILED);
+ return -1;
+ }
+
+ /* Allocate memory for one transmit an multiple receive buffers */
+ vq_rx->buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+net_hdr_size)
+ * RX_QUEUE_SIZE);
+ if (!vq_rx->buf_mem) {
+ printf("virtionet: Failed to allocate buffers!\n");
+ goto dev_error;
+ }
+
+ /* Prepare receive buffer queue */
+ for (i = 0; i < RX_QUEUE_SIZE; i++) {
+ uint64_t addr = (uint64_t)vq_rx->buf_mem
+ + i * (BUFFER_ENTRY_SIZE+net_hdr_size);
+ uint32_t id = i*2;
+ /* Descriptor for net_hdr: */
+ virtio_fill_desc(vq_rx, id, vdev->features, addr, net_hdr_size,
+ VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, id + 1);
+
+ /* Descriptor for data: */
+ virtio_fill_desc(vq_rx, id + 1, vdev->features, addr + net_hdr_size,
+ BUFFER_ENTRY_SIZE, VRING_DESC_F_WRITE, 0);
+
+ vq_rx->avail->ring[i] = virtio_cpu_to_modern16(vdev, id);
+ }
+ sync();
+
+ vq_rx->avail->flags = virtio_cpu_to_modern16(vdev, VRING_AVAIL_F_NO_INTERRUPT);
+ vq_rx->avail->idx = virtio_cpu_to_modern16(vdev, RX_QUEUE_SIZE);
+
+ last_rx_idx = virtio_modern16_to_cpu(vdev, vq_rx->used->idx);
+
+ vq_tx->avail->flags = virtio_cpu_to_modern16(vdev, VRING_AVAIL_F_NO_INTERRUPT);
+ vq_tx->avail->idx = 0;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(vdev, status);
+
+ /* Tell HV that RX queues are ready */
+ virtio_queue_notify(vdev, VQ_RX);
+
+ driver->running = 1;
+ for(i = 0; i < (int)sizeof(driver->mac_addr); i++) {
+ driver->mac_addr[i] = virtio_get_config(vdev, i, 1);
+ }
+ return 0;
+
+dev_error:
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(vdev, status);
+ return -1;
+}
+
+
+/**
+ * Shutdown driver.
+ * We've got to make sure that the hosts stops all transfers since the buffers
+ * in our main memory will become invalid after this module has been terminated.
+ */
+static int virtionet_term(struct virtio_net *vnet)
+{
+ struct virtio_device *vdev = &vnet->vdev;
+ net_driver_t *driver = &vnet->driver;
+ struct vqs *vq_tx = &vnet->vdev.vq[VQ_TX];
+ struct vqs *vq_rx = &vnet->vdev.vq[VQ_RX];
+
+ dprintf("virtionet_term()\n");
+
+ if (driver->running == 0)
+ return 0;
+
+ /* Quiesce device */
+ virtio_set_status(vdev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(vdev);
+
+ driver->running = 0;
+
+ SLOF_free_mem(vq_rx->buf_mem,
+ (BUFFER_ENTRY_SIZE+net_hdr_size) * RX_QUEUE_SIZE);
+ vq_rx->buf_mem = NULL;
+
+ virtio_queue_term_vq(vdev, vq_rx, VQ_RX);
+ virtio_queue_term_vq(vdev, vq_tx, VQ_TX);
+
+ return 0;
+}
+
+
+/**
+ * Transmit a packet
+ */
+static int virtionet_xmit(struct virtio_net *vnet, char *buf, int len)
+{
+ int id, idx;
+ static struct virtio_net_hdr_v1 nethdr_v1;
+ static struct virtio_net_hdr nethdr_legacy;
+ void *nethdr = &nethdr_legacy;
+ struct virtio_device *vdev = &vnet->vdev;
+ struct vqs *vq_tx = &vdev->vq[VQ_TX];
+
+ if (len > BUFFER_ENTRY_SIZE) {
+ printf("virtionet: Packet too big!\n");
+ return 0;
+ }
+
+ dprintf("\nvirtionet_xmit(packet at %p, %d bytes)\n", buf, len);
+
+ if (vdev->features & VIRTIO_F_VERSION_1)
+ nethdr = &nethdr_v1;
+
+ memset(nethdr, 0, net_hdr_size);
+
+ /* Determine descriptor index */
+ idx = virtio_modern16_to_cpu(vdev, vq_tx->avail->idx);
+ id = (idx * 2) % vq_tx->size;
+
+ virtio_free_desc(vq_tx, id, vdev->features);
+ virtio_free_desc(vq_tx, id + 1, vdev->features);
+
+ /* Set up virtqueue descriptor for header */
+ virtio_fill_desc(vq_tx, id, vdev->features, (uint64_t)nethdr,
+ net_hdr_size, VRING_DESC_F_NEXT, id + 1);
+
+ /* Set up virtqueue descriptor for data */
+ virtio_fill_desc(vq_tx, id + 1, vdev->features, (uint64_t)buf, len, 0, 0);
+
+ vq_tx->avail->ring[idx % vq_tx->size] = virtio_cpu_to_modern16(vdev, id);
+ sync();
+ vq_tx->avail->idx = virtio_cpu_to_modern16(vdev, idx + 1);
+ sync();
+
+ /* Tell HV that TX queue is ready */
+ virtio_queue_notify(vdev, VQ_TX);
+
+ return len;
+}
+
+
+/**
+ * Receive a packet
+ */
+static int virtionet_receive(struct virtio_net *vnet, char *buf, int maxlen)
+{
+ uint32_t len = 0;
+ uint32_t id, idx;
+ uint16_t avail_idx;
+ struct virtio_device *vdev = &vnet->vdev;
+ struct vqs *vq_rx = &vnet->vdev.vq[VQ_RX];
+
+ idx = virtio_modern16_to_cpu(vdev, vq_rx->used->idx);
+
+ if (last_rx_idx == idx) {
+ /* Nothing received yet */
+ return 0;
+ }
+
+ id = (virtio_modern32_to_cpu(vdev, vq_rx->used->ring[last_rx_idx % vq_rx->size].id) + 1)
+ % vq_rx->size;
+ len = virtio_modern32_to_cpu(vdev, vq_rx->used->ring[last_rx_idx % vq_rx->size].len)
+ - net_hdr_size;
+ dprintf("virtionet_receive() last_rx_idx=%i, vq_rx->used->idx=%i,"
+ " id=%i len=%i\n", last_rx_idx, vq_rx->used->idx, id, len);
+
+ if (len > (uint32_t)maxlen) {
+ printf("virtio-net: Receive buffer not big enough!\n");
+ len = maxlen;
+ }
+
+#if 0
+ /* Dump packet */
+ printf("\n");
+ int i;
+ for (i=0; i<64; i++) {
+ printf(" %02x", *(uint8_t*)(vq_rx->desc[id].addr+i));
+ if ((i%16)==15)
+ printf("\n");
+ }
+ prinfk("\n");
+#endif
+
+ /* Copy data to destination buffer */
+ memcpy(buf, virtio_desc_addr(vdev, VQ_RX, id), len);
+
+ /* Move indices to next entries */
+ last_rx_idx = last_rx_idx + 1;
+
+ avail_idx = virtio_modern16_to_cpu(vdev, vq_rx->avail->idx);
+ vq_rx->avail->ring[avail_idx % vq_rx->size] = virtio_cpu_to_modern16(vdev, id - 1);
+ sync();
+ vq_rx->avail->idx = virtio_cpu_to_modern16(vdev, avail_idx + 1);
+
+ /* Tell HV that RX queue entry is ready */
+ virtio_queue_notify(vdev, VQ_RX);
+
+ return len;
+}
+
+struct virtio_net *virtionet_open(struct virtio_device *dev)
+{
+ struct virtio_net *vnet;
+
+ vnet = SLOF_alloc_mem(sizeof(*vnet));
+ if (!vnet) {
+ printf("Unable to allocate virtio-net driver\n");
+ return NULL;
+ }
+
+ vnet->driver.running = 0;
+
+ if (virtionet_init_pci(vnet, dev))
+ goto FAIL;
+
+ if (virtionet_init(vnet))
+ goto FAIL;
+
+ return vnet;
+
+FAIL:
+ SLOF_free_mem(vnet, sizeof(*vnet));
+ return NULL;
+}
+
+void virtionet_close(struct virtio_net *vnet)
+{
+ if (vnet) {
+ virtionet_term(vnet);
+ SLOF_free_mem(vnet, sizeof(*vnet));
+ }
+}
+
+int virtionet_read(struct virtio_net *vnet, char *buf, int len)
+{
+ if (vnet && buf)
+ return virtionet_receive(vnet, buf, len);
+ return -1;
+}
+
+int virtionet_write(struct virtio_net *vnet, char *buf, int len)
+{
+ if (vnet && buf)
+ return virtionet_xmit(vnet, buf, len);
+ return -1;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-net.h b/roms/SLOF/lib/libvirtio/virtio-net.h
new file mode 100644
index 000000000..c71fbded0
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-net.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef VIRTIO_NET_H
+#define VIRTIO_NET_H
+
+#include <netdriver.h>
+#include "virtio.h"
+
+#define RX_QUEUE_SIZE 128
+#define BUFFER_ENTRY_SIZE 1514
+
+enum {
+ VQ_RX = 0, /* Receive Queue */
+ VQ_TX = 1, /* Transmit Queue */
+};
+
+struct virtio_net {
+ net_driver_t driver;
+ struct virtio_device vdev;
+};
+
+/* VIRTIO_NET Feature bits */
+#define VIRTIO_NET_F_MAC (1 << 5)
+
+extern struct virtio_net *virtionet_open(struct virtio_device *dev);
+extern void virtionet_close(struct virtio_net *vnet);
+extern int virtionet_read(struct virtio_net *vnet, char *buf, int len);
+extern int virtionet_write(struct virtio_net *vnet, char *buf, int len);
+
+#endif
diff --git a/roms/SLOF/lib/libvirtio/virtio-scsi.c b/roms/SLOF/lib/libvirtio/virtio-scsi.c
new file mode 100644
index 000000000..96285e389
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-scsi.c
@@ -0,0 +1,152 @@
+/******************************************************************************
+ * Copyright (c) 2012 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <cpu.h>
+#include <helpers.h>
+#include "virtio.h"
+#include "virtio-internal.h"
+#include "virtio-scsi.h"
+
+int virtioscsi_send(struct virtio_device *dev,
+ struct virtio_scsi_req_cmd *req,
+ struct virtio_scsi_resp_cmd *resp,
+ int is_read, void *buf, uint64_t buf_len)
+{
+
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+ int id;
+ uint32_t time;
+ struct vqs *vq = &dev->vq[VIRTIO_SCSI_REQUEST_VQ];
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq->avail->idx);
+
+ last_used_idx = vq->used->idx;
+ current_used_idx = &vq->used->idx;
+
+ /* Determine descriptor index */
+ id = (avail_idx * 3) % vq->size;
+ virtio_fill_desc(vq, id, dev->features, (uint64_t)req, sizeof(*req), VRING_DESC_F_NEXT,
+ id + 1);
+
+ if (buf == NULL || buf_len == 0) {
+ /* Set up descriptor for response information */
+ virtio_fill_desc(vq, id + 1, dev->features,
+ (uint64_t)resp, sizeof(*resp),
+ VRING_DESC_F_WRITE, 0);
+ } else if (is_read) {
+ /* Set up descriptor for response information */
+ virtio_fill_desc(vq, id + 1, dev->features,
+ (uint64_t)resp, sizeof(*resp),
+ VRING_DESC_F_NEXT | VRING_DESC_F_WRITE,
+ id + 2);
+ /* Set up virtqueue descriptor for data from device */
+ virtio_fill_desc(vq, id + 2, dev->features,
+ (uint64_t)buf, buf_len, VRING_DESC_F_WRITE, 0);
+ } else {
+ /* Set up virtqueue descriptor for data to device */
+ virtio_fill_desc(vq, id + 1, dev->features,
+ (uint64_t)buf, buf_len, VRING_DESC_F_NEXT,
+ id + 2);
+ /* Set up descriptor for response information */
+ virtio_fill_desc(vq, id + 2, dev->features,
+ (uint64_t)resp, sizeof(*resp),
+ VRING_DESC_F_WRITE, 0);
+ }
+
+ vq->avail->ring[avail_idx % vq->size] = virtio_cpu_to_modern16(dev, id);
+ mb();
+ vq->avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
+
+ /* Tell HV that the vq is ready */
+ virtio_queue_notify(dev, VIRTIO_SCSI_REQUEST_VQ);
+
+ /* Wait for host to consume the descriptor */
+ time = SLOF_GetTimer() + VIRTIO_TIMEOUT;
+ while (*current_used_idx == last_used_idx) {
+ // do something better
+ mb();
+ if (time < SLOF_GetTimer())
+ break;
+ }
+
+ virtio_free_desc(vq, id, dev->features);
+ virtio_free_desc(vq, id + 1, dev->features);
+ if (!(buf == NULL || buf_len == 0))
+ virtio_free_desc(vq, id + 2, dev->features);
+
+ return 0;
+}
+
+/**
+ * Initialize virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+int virtioscsi_init(struct virtio_device *dev)
+{
+ struct vqs *vq_ctrl, *vq_event, *vq_request;
+ int status = VIRTIO_STAT_ACKNOWLEDGE;
+
+ /* Reset device */
+ // XXX That will clear the virtq base. We need to move
+ // initializing it to here anyway
+ //
+ // virtio_reset_device(dev);
+
+ /* Acknowledge device. */
+ virtio_set_status(dev, status);
+
+ /* Tell HV that we know how to drive the device. */
+ status |= VIRTIO_STAT_DRIVER;
+ virtio_set_status(dev, status);
+
+ /* Device specific setup - we do not support special features right now */
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ if (virtio_negotiate_guest_features(dev, VIRTIO_F_VERSION_1))
+ goto dev_error;
+ virtio_get_status(dev, &status);
+ } else {
+ virtio_set_guest_features(dev, 0);
+ }
+
+ vq_ctrl = virtio_queue_init_vq(dev, VIRTIO_SCSI_CONTROL_VQ);
+ vq_event = virtio_queue_init_vq(dev, VIRTIO_SCSI_EVENT_VQ);
+ vq_request = virtio_queue_init_vq(dev, VIRTIO_SCSI_REQUEST_VQ);
+ if (!vq_ctrl || !vq_event || !vq_request)
+ goto dev_error;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(dev, status);
+
+ return 0;
+dev_error:
+ printf("%s: failed\n", __func__);
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(dev, status);
+ return -1;
+}
+
+/**
+ * Shutdown the virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+void virtioscsi_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-scsi.h b/roms/SLOF/lib/libvirtio/virtio-scsi.h
new file mode 100644
index 000000000..d598dea43
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-scsi.h
@@ -0,0 +1,69 @@
+/******************************************************************************
+ * Copyright (c) 2012 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * Virtio SCSI Host device definitions.
+ * See Virtio Spec, Appendix I, for details
+ */
+
+#ifndef _VIRTIO_SCSI_H
+#define _VIRTIO_SCSI_H
+
+#define VIRTIO_SCSI_CDB_SIZE 32
+#define VIRTIO_SCSI_SENSE_SIZE 96
+
+#define VIRTIO_SCSI_CONTROL_VQ 0
+#define VIRTIO_SCSI_EVENT_VQ 1
+#define VIRTIO_SCSI_REQUEST_VQ 2
+
+struct virtio_scsi_config
+{
+ uint32_t num_queues;
+ uint32_t seg_max;
+ uint32_t max_sectors;
+ uint32_t cmd_per_lun;
+ uint32_t event_info_size;
+ uint32_t sense_size;
+ uint32_t cdb_size;
+ uint16_t max_channel;
+ uint16_t max_target;
+ uint32_t max_lun;
+} __attribute__((packed));
+
+/* This is the first element of the "out" scatter-gather list. */
+struct virtio_scsi_req_cmd {
+ uint8_t lun[8];
+ uint64_t tag;
+ uint8_t task_attr;
+ uint8_t prio;
+ uint8_t crn;
+ char cdb[VIRTIO_SCSI_CDB_SIZE];
+} __attribute__((packed));
+
+/* This is the first element of the "in" scatter-gather list. */
+struct virtio_scsi_resp_cmd {
+ uint32_t sense_len;
+ uint32_t residual;
+ uint16_t status_qualifier;
+ uint8_t status;
+ uint8_t response;
+ uint8_t sense[VIRTIO_SCSI_SENSE_SIZE];
+};
+
+extern int virtioscsi_init(struct virtio_device *dev);
+extern void virtioscsi_shutdown(struct virtio_device *dev);
+extern int virtioscsi_send(struct virtio_device *dev,
+ struct virtio_scsi_req_cmd *req,
+ struct virtio_scsi_resp_cmd *resp,
+ int is_read, void *buf, uint64_t buf_len);
+
+#endif /* _VIRTIO_SCSI_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio-serial.c b/roms/SLOF/lib/libvirtio/virtio-serial.c
new file mode 100644
index 000000000..92afb0201
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-serial.c
@@ -0,0 +1,202 @@
+/******************************************************************************
+ * Copyright (c) 2016 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * Virtio serial device definitions.
+ * See Virtio 1.0 - 5.3 Console Device, for details
+ */
+#include <stdio.h>
+#include <string.h>
+#include <cpu.h>
+#include <helpers.h>
+#include <byteorder.h>
+#include "virtio.h"
+#include "virtio-serial.h"
+#include "virtio-internal.h"
+
+#define DRIVER_FEATURE_SUPPORT VIRTIO_F_VERSION_1
+#define RX_ELEM_SIZE 4
+#define RX_NUM_ELEMS 128
+
+#define RX_Q 0
+#define TX_Q 1
+
+static uint16_t last_rx_idx; /* Last index in RX "used" ring */
+
+int virtio_serial_init(struct virtio_device *dev)
+{
+ struct vqs *vq_rx, *vq_tx;
+ int status = VIRTIO_STAT_ACKNOWLEDGE;
+ int i;
+
+ /* Reset device */
+ virtio_reset_device(dev);
+
+ /* Acknowledge device. */
+ virtio_set_status(dev, status);
+
+ /* Tell HV that we know how to drive the device. */
+ status |= VIRTIO_STAT_DRIVER;
+ virtio_set_status(dev, status);
+
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ /* Negotiate features and sets FEATURES_OK if successful */
+ if (virtio_negotiate_guest_features(dev, DRIVER_FEATURE_SUPPORT))
+ goto dev_error;
+
+ virtio_get_status(dev, &status);
+ }
+
+ vq_rx = virtio_queue_init_vq(dev, RX_Q);
+ if (!vq_rx)
+ goto dev_error;
+
+ /* Allocate memory for multiple receive buffers */
+ vq_rx->buf_mem = SLOF_alloc_mem(RX_ELEM_SIZE * RX_NUM_ELEMS);
+ if (!vq_rx->buf_mem) {
+ printf("virtio-serial: Failed to allocate buffers!\n");
+ goto dev_error;
+ }
+
+ /* Prepare receive buffer queue */
+ for (i = 0; i < RX_NUM_ELEMS; i++) {
+ uint64_t addr = (uint64_t)vq_rx->buf_mem + i * RX_ELEM_SIZE;
+
+ /* Descriptor for data: */
+ virtio_fill_desc(vq_rx, i, dev->features, addr, 1, VRING_DESC_F_WRITE, 0);
+ vq_rx->avail->ring[i] = virtio_cpu_to_modern16(dev, i);
+ }
+ vq_rx->avail->idx = virtio_cpu_to_modern16(dev, RX_NUM_ELEMS);
+ sync();
+
+ last_rx_idx = virtio_modern16_to_cpu(dev, vq_rx->used->idx);
+
+ vq_tx = virtio_queue_init_vq(dev, TX_Q);
+ if (!vq_tx)
+ goto dev_error;
+
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(dev, status);
+
+ return 1;
+ dev_error:
+ printf("%s: failed\n", __func__);
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(dev, status);
+ return 0;
+}
+
+void virtio_serial_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Stop queues */
+ virtio_queue_term_vq(dev, &dev->vq[TX_Q], TX_Q);
+ virtio_queue_term_vq(dev, &dev->vq[RX_Q], RX_Q);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+}
+
+int virtio_serial_putchar(struct virtio_device *dev, char c)
+{
+ int id, ret;
+ uint32_t time;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+ struct vqs *vq = &dev->vq[TX_Q];
+
+ if (!vq->desc)
+ return 0;
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq->avail->idx);
+
+ last_used_idx = vq->used->idx;
+ current_used_idx = &vq->used->idx;
+
+ /* Determine descriptor index */
+ id = avail_idx % vq->size;
+
+ /* Set up virtqueue descriptor for header */
+ virtio_fill_desc(vq, id, dev->features, (uint64_t)&c, 1, 0, 0);
+
+ vq->avail->ring[avail_idx % vq->size] = virtio_cpu_to_modern16 (dev, id);
+ mb();
+ vq->avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
+
+ /* Tell HV that the queue is ready */
+ virtio_queue_notify(dev, TX_Q);
+
+ /* Wait for host to consume the descriptor */
+ ret = 1;
+ time = SLOF_GetTimer() + VIRTIO_TIMEOUT;
+ while (*current_used_idx == last_used_idx) {
+ // do something better
+ mb();
+ if (time < SLOF_GetTimer()) {
+ printf("virtio_serial_putchar failed! \n");
+ ret = 0;
+ break;
+ }
+ }
+
+ virtio_free_desc(vq, id, dev->features);
+
+ return ret;
+}
+
+char virtio_serial_getchar(struct virtio_device *dev)
+{
+ int id, idx;
+ char buf[RX_NUM_ELEMS] = {0};
+ uint16_t avail_idx;
+ struct vqs *vq_rx = &dev->vq[RX_Q];
+
+ idx = virtio_modern16_to_cpu(dev, vq_rx->used->idx);
+ if (last_rx_idx == idx) {
+ /* Nothing received yet */
+ return 0;
+ }
+
+ id = (virtio_modern32_to_cpu(dev, vq_rx->used->ring[last_rx_idx % vq_rx->size].id) + 1)
+ % vq_rx->size;
+
+ /* Copy data to destination buffer */
+ memcpy(buf, virtio_desc_addr(dev, RX_Q, id - 1), RX_ELEM_SIZE);
+
+ /* Move indices to next entries */
+ last_rx_idx = last_rx_idx + 1;
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq_rx->avail->idx);
+ vq_rx->avail->ring[avail_idx % vq_rx->size] = virtio_cpu_to_modern16(dev, id - 1);
+ sync();
+ vq_rx->avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
+ sync();
+
+ /* Tell HV that RX queue entry is ready */
+ virtio_queue_notify(dev, RX_Q);
+
+ return buf[0];
+}
+
+int virtio_serial_haschar(struct virtio_device *dev)
+{
+ struct vqs *vq_rx = &dev->vq[RX_Q];
+
+ if (last_rx_idx == virtio_modern16_to_cpu(dev, vq_rx->used->idx))
+ return 0;
+ else
+ return 1;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-serial.h b/roms/SLOF/lib/libvirtio/virtio-serial.h
new file mode 100644
index 000000000..68af46de5
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-serial.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * Copyright (c) 2016 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * Virtio serial device definitions.
+ * See Virtio 1.0 - 5.3 Console Device, for details
+ */
+
+#ifndef _VIRTIO_SERIAL_H
+#define _VIRTIO_SERIAL_H
+
+extern int virtio_serial_init(struct virtio_device *dev);
+extern void virtio_serial_shutdown(struct virtio_device *dev);
+extern int virtio_serial_putchar(struct virtio_device *dev, char c);
+extern char virtio_serial_getchar(struct virtio_device *dev);
+extern int virtio_serial_haschar(struct virtio_device *dev);
+
+#endif /* _VIRTIO_SERIAL_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio.c b/roms/SLOF/lib/libvirtio/virtio.c
new file mode 100644
index 000000000..9dfc93cb2
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio.c
@@ -0,0 +1,645 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <cpu.h>
+#include <cache.h>
+#include <byteorder.h>
+#include "virtio.h"
+#include "helpers.h"
+#include "virtio-internal.h"
+
+/* PCI virtio header offsets */
+#define VIRTIOHDR_DEVICE_FEATURES 0
+#define VIRTIOHDR_GUEST_FEATURES 4
+#define VIRTIOHDR_QUEUE_ADDRESS 8
+#define VIRTIOHDR_QUEUE_SIZE 12
+#define VIRTIOHDR_QUEUE_SELECT 14
+#define VIRTIOHDR_QUEUE_NOTIFY 16
+#define VIRTIOHDR_DEVICE_STATUS 18
+#define VIRTIOHDR_ISR_STATUS 19
+#define VIRTIOHDR_DEVICE_CONFIG 20
+
+/* PCI defines */
+#define PCI_BASE_ADDR_SPACE_IO 0x01
+#define PCI_BASE_ADDR_SPACE_64BIT 0x04
+#define PCI_BASE_ADDR_MEM_MASK (~0x0fUL)
+#define PCI_BASE_ADDR_IO_MASK (~0x03UL)
+
+#define PCI_BASE_ADDR_REG_0 0x10
+#define PCI_CONFIG_CAP_REG 0x34
+
+#define PCI_CAP_ID_VNDR 0x9
+
+/* Common configuration */
+#define VIRTIO_PCI_CAP_COMMON_CFG 1
+/* Notifications */
+#define VIRTIO_PCI_CAP_NOTIFY_CFG 2
+/* ISR access */
+#define VIRTIO_PCI_CAP_ISR_CFG 3
+/* Device specific configuration */
+#define VIRTIO_PCI_CAP_DEVICE_CFG 4
+/* PCI configuration access */
+#define VIRTIO_PCI_CAP_PCI_CFG 5
+
+#define VIRTIO_PCI_CAP_VNDR 0 /* Generic PCI field: PCI_CAP_ID_VNDR */
+#define VIRTIO_PCI_CAP_NEXT 1 /* Generic PCI field: next ptr. */
+#define VIRTIO_PCI_CAP_LEN 2 /* Generic PCI field: capability length */
+#define VIRTIO_PCI_CAP_CFG_TYPE 3 /* Identifies the structure. */
+#define VIRTIO_PCI_CAP_BAR 4 /* Where to find it. */
+#define VIRTIO_PCI_CAP_OFFSET 8 /* Offset within bar. */
+#define VIRTIO_PCI_CAP_LENGTH 12 /* Length of the structure, in bytes. */
+
+struct virtio_dev_common {
+ le32 dev_features_sel;
+ le32 dev_features;
+ le32 drv_features_sel;
+ le32 drv_features;
+ le16 msix_config;
+ le16 num_queues;
+ uint8_t dev_status;
+ uint8_t cfg_generation;
+
+ le16 q_select;
+ le16 q_size;
+ le16 q_msix_vec;
+ le16 q_enable;
+ le16 q_notify_off;
+ le64 q_desc;
+ le64 q_avail;
+ le64 q_used;
+} __attribute__ ((packed));
+
+/* virtio 1.0 Spec: 4.1.3 PCI Device Layout
+ *
+ * Fields of different sizes are present in the device configuration regions.
+ * All 64-bit, 32-bit and 16-bit fields are little-endian. 64-bit fields are to
+ * be treated as two 32-bit fields, with low 32 bit part followed by the high 32
+ * bit part.
+ */
+static void virtio_pci_write64(void *addr, uint64_t val)
+{
+ uint32_t hi = (val >> 32) & 0xFFFFFFFF;
+ uint32_t lo = val & 0xFFFFFFFF;
+
+ ci_write_32(addr, cpu_to_le32(lo));
+ ci_write_32(addr + 4, cpu_to_le32(hi));
+}
+
+static void virtio_cap_set_base_addr(struct virtio_cap *cap, uint32_t offset)
+{
+ uint64_t addr;
+
+ addr = SLOF_pci_config_read32(PCI_BASE_ADDR_REG_0 + 4 * cap->bar);
+ if (addr & PCI_BASE_ADDR_SPACE_IO) {
+ addr = addr & PCI_BASE_ADDR_IO_MASK;
+ cap->is_io = 1;
+ } else {
+ if (addr & PCI_BASE_ADDR_SPACE_64BIT)
+ addr |= SLOF_pci_config_read32(PCI_BASE_ADDR_REG_0 + 4 * (cap->bar + 1)) << 32;
+ addr = addr & PCI_BASE_ADDR_MEM_MASK;
+ cap->is_io = 0;
+ }
+ addr = (uint64_t)SLOF_translate_my_address((void *)addr);
+ cap->addr = (void *)addr + offset;
+}
+
+static void virtio_process_cap(struct virtio_device *dev, uint8_t cap_ptr)
+{
+ struct virtio_cap *cap;
+ uint8_t cfg_type, bar;
+ uint32_t offset;
+
+ cfg_type = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_CFG_TYPE);
+ bar = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_BAR);
+ offset = SLOF_pci_config_read32(cap_ptr + VIRTIO_PCI_CAP_OFFSET);
+
+ switch(cfg_type) {
+ case VIRTIO_PCI_CAP_COMMON_CFG:
+ cap = &dev->common;
+ break;
+ case VIRTIO_PCI_CAP_NOTIFY_CFG:
+ cap = &dev->notify;
+ dev->notify_off_mul = SLOF_pci_config_read32(cap_ptr + sizeof(struct virtio_cap));
+ break;
+ case VIRTIO_PCI_CAP_ISR_CFG:
+ cap = &dev->isr;
+ break;
+ case VIRTIO_PCI_CAP_DEVICE_CFG:
+ cap = &dev->device;
+ break;
+ default:
+ return;
+ }
+
+ cap->bar = bar;
+ virtio_cap_set_base_addr(cap, offset);
+ cap->cap_id = cfg_type;
+}
+
+/**
+ * Reads the virtio device capabilities, gets called from SLOF routines The
+ * function determines legacy or modern device and sets up driver registers
+ */
+struct virtio_device *virtio_setup_vd(void)
+{
+ uint8_t cap_ptr, cap_vndr;
+ struct virtio_device *dev;
+
+ dev = SLOF_alloc_mem(sizeof(struct virtio_device));
+ if (!dev) {
+ printf("Failed to allocate memory");
+ return NULL;
+ }
+
+ cap_ptr = SLOF_pci_config_read8(PCI_CONFIG_CAP_REG);
+ while (cap_ptr != 0) {
+ cap_vndr = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_VNDR);
+ if (cap_vndr == PCI_CAP_ID_VNDR)
+ virtio_process_cap(dev, cap_ptr);
+ cap_ptr = SLOF_pci_config_read8(cap_ptr+VIRTIO_PCI_CAP_NEXT);
+ }
+
+ if (dev->common.cap_id && dev->notify.cap_id &&
+ dev->isr.cap_id && dev->device.cap_id) {
+ dev->features = VIRTIO_F_VERSION_1;
+ } else {
+ dev->features = 0;
+ dev->legacy.cap_id = 0;
+ dev->legacy.bar = 0;
+ virtio_cap_set_base_addr(&dev->legacy, 0);
+ }
+ return dev;
+}
+
+/**
+ * Calculate ring size according to queue size number
+ */
+unsigned long virtio_vring_size(unsigned int qsize)
+{
+ return VQ_ALIGN(sizeof(struct vring_desc) * qsize +
+ sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) +
+ VQ_ALIGN(sizeof(struct vring_used) +
+ sizeof(struct vring_used_elem) * qsize);
+}
+
+
+/**
+ * Get number of elements in a vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return number of elements
+ */
+unsigned int virtio_get_qsize(struct virtio_device *dev, int queue)
+{
+ unsigned int size = 0;
+
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ void *addr = dev->common.addr + offset_of(struct virtio_dev_common, q_select);
+ ci_write_16(addr, cpu_to_le16(queue));
+ eieio();
+ addr = dev->common.addr + offset_of(struct virtio_dev_common, q_size);
+ size = le16_to_cpu(ci_read_16(addr));
+ }
+ else {
+ ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT,
+ cpu_to_le16(queue));
+ eieio();
+ size = le16_to_cpu(ci_read_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SIZE));
+ }
+
+ return size;
+}
+
+
+/**
+ * Get address of descriptor vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the descriptor ring
+ */
+struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue)
+{
+ return dev->vq[queue].desc;
+}
+
+
+/**
+ * Get address of "available" vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the "available" ring
+ */
+struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue)
+{
+ return dev->vq[queue].avail;
+}
+
+
+/**
+ * Get address of "used" vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the "used" ring
+ */
+struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue)
+{
+ return dev->vq[queue].used;
+}
+
+/**
+ * Fill the virtio ring descriptor depending on the legacy mode or virtio 1.0
+ */
+void virtio_fill_desc(struct vqs *vq, int id, uint64_t features,
+ uint64_t addr, uint32_t len,
+ uint16_t flags, uint16_t next)
+{
+ struct vring_desc *desc;
+
+ id %= vq->size;
+ desc = &vq->desc[id];
+ next %= vq->size;
+
+ if (features & VIRTIO_F_VERSION_1) {
+ if (features & VIRTIO_F_IOMMU_PLATFORM) {
+ void *gpa = (void *) addr;
+
+ if (!vq->desc_gpas) {
+ fprintf(stderr, "IOMMU setup has not been done!\n");
+ return;
+ }
+
+ addr = SLOF_dma_map_in(gpa, len, 0);
+ vq->desc_gpas[id] = gpa;
+ }
+ desc->addr = cpu_to_le64(addr);
+ desc->len = cpu_to_le32(len);
+ desc->flags = cpu_to_le16(flags);
+ desc->next = cpu_to_le16(next);
+ } else {
+ desc->addr = addr;
+ desc->len = len;
+ desc->flags = flags;
+ desc->next = next;
+ }
+}
+
+void virtio_free_desc(struct vqs *vq, int id, uint64_t features)
+{
+ struct vring_desc *desc;
+
+ id %= vq->size;
+ desc = &vq->desc[id];
+
+ if (!(features & VIRTIO_F_VERSION_1) ||
+ !(features & VIRTIO_F_IOMMU_PLATFORM))
+ return;
+
+ if (!vq->desc_gpas[id])
+ return;
+
+ SLOF_dma_map_out(le64_to_cpu(desc->addr), 0, le32_to_cpu(desc->len));
+ vq->desc_gpas[id] = NULL;
+}
+
+void *virtio_desc_addr(struct virtio_device *vdev, int queue, int id)
+{
+ struct vqs *vq = &vdev->vq[queue];
+
+ if (vq->desc_gpas)
+ return vq->desc_gpas[id];
+
+ return (void *) virtio_modern64_to_cpu(vdev, vq->desc[id].addr);
+}
+
+/**
+ * Reset virtio device
+ */
+void virtio_reset_device(struct virtio_device *dev)
+{
+ virtio_set_status(dev, 0);
+}
+
+
+/**
+ * Notify hypervisor about queue update
+ */
+void virtio_queue_notify(struct virtio_device *dev, int queue)
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select);
+ void *q_ntfy = dev->common.addr + offset_of(struct virtio_dev_common, q_notify_off);
+ void *addr;
+ uint16_t q_notify_off;
+
+ ci_write_16(q_sel, cpu_to_le16(queue));
+ eieio();
+ q_notify_off = le16_to_cpu(ci_read_16(q_ntfy));
+ addr = dev->notify.addr + q_notify_off * dev->notify_off_mul;
+ ci_write_16(addr, cpu_to_le16(queue));
+ } else {
+ ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue));
+ }
+}
+
+/**
+ * Set queue address
+ */
+static void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned long qaddr)
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ uint64_t q_desc = qaddr;
+ uint64_t q_avail;
+ uint64_t q_used;
+ uint32_t q_size = virtio_get_qsize(dev, queue);
+
+ if (dev->features & VIRTIO_F_IOMMU_PLATFORM) {
+ unsigned long cb;
+
+ cb = q_size * sizeof(struct vring_desc);
+ cb += sizeof(struct vring_avail) +
+ sizeof(uint16_t) * q_size;
+ cb = VQ_ALIGN(cb);
+ cb += sizeof(struct vring_used) +
+ sizeof(struct vring_used_elem) * q_size;
+ cb = VQ_ALIGN(cb);
+ q_desc = SLOF_dma_map_in((void *)q_desc, cb, 0);
+
+ dev->vq[queue].bus_desc = q_desc;
+ }
+
+ virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_desc), q_desc);
+ q_avail = q_desc + q_size * sizeof(struct vring_desc);
+ virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_avail), q_avail);
+ q_used = VQ_ALIGN(q_avail + sizeof(struct vring_avail) + sizeof(uint16_t) * q_size);
+ virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_used), q_used);
+ ci_write_16(dev->common.addr + offset_of(struct virtio_dev_common, q_enable), cpu_to_le16(1));
+ } else {
+ uint32_t val = qaddr;
+ val = val >> 12;
+ ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT,
+ cpu_to_le16(queue));
+ eieio();
+ ci_write_32(dev->legacy.addr+VIRTIOHDR_QUEUE_ADDRESS,
+ cpu_to_le32(val));
+ }
+}
+
+struct vqs *virtio_queue_init_vq(struct virtio_device *dev, unsigned int id)
+{
+ struct vqs *vq;
+
+ if (id >= sizeof(dev->vq)/sizeof(dev->vq[0])) {
+ printf("Queue index is too big!\n");
+ return NULL;
+ }
+ vq = &dev->vq[id];
+
+ memset(vq, 0, sizeof(*vq));
+
+ vq->size = virtio_get_qsize(dev, id);
+ vq->desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq->size), 4096);
+ if (!vq->desc) {
+ printf("memory allocation failed!\n");
+ return NULL;
+ }
+
+ vq->avail = (void *) vq->desc + vq->size * sizeof(struct vring_desc);
+ vq->used = (void *) VQ_ALIGN((unsigned long) vq->avail +
+ sizeof(struct vring_avail) +
+ sizeof(uint16_t) * vq->size);
+
+ memset(vq->desc, 0, virtio_vring_size(vq->size));
+ virtio_set_qaddr(dev, id, (unsigned long)vq->desc);
+
+ vq->avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT);
+ vq->avail->idx = 0;
+ if (dev->features & VIRTIO_F_IOMMU_PLATFORM)
+ vq->desc_gpas = SLOF_alloc_mem_aligned(
+ vq->size * sizeof(vq->desc_gpas[0]), 4096);
+
+ return vq;
+}
+
+void virtio_queue_term_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id)
+{
+ if (vq->desc_gpas) {
+ unsigned i;
+
+ for (i = 0; i < vq->size; ++i)
+ virtio_free_desc(vq, i, dev->features);
+
+ SLOF_free_mem(vq->desc_gpas,
+ vq->size * sizeof(vq->desc_gpas[0]));
+ }
+ if (vq->desc) {
+ if (dev->features & VIRTIO_F_IOMMU_PLATFORM) {
+ unsigned long cb;
+ uint32_t q_size = virtio_get_qsize(dev, id);
+
+ cb = q_size * sizeof(struct vring_desc);
+ cb += sizeof(struct vring_avail) +
+ sizeof(uint16_t) * q_size;
+ cb = VQ_ALIGN(cb);
+ cb += sizeof(struct vring_used) +
+ sizeof(struct vring_used_elem) * q_size;
+ cb = VQ_ALIGN(cb);
+
+ SLOF_dma_map_out(vq->bus_desc, 0, cb);
+ }
+
+ SLOF_free_mem(vq->desc, virtio_vring_size(vq->size));
+ }
+ memset(vq, 0, sizeof(*vq));
+}
+
+/**
+ * Set device status bits
+ */
+void virtio_set_status(struct virtio_device *dev, int status)
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ ci_write_8(dev->common.addr +
+ offset_of(struct virtio_dev_common, dev_status), status);
+ } else {
+ ci_write_8(dev->legacy.addr+VIRTIOHDR_DEVICE_STATUS, status);
+ }
+}
+
+/**
+ * Get device status bits
+ */
+void virtio_get_status(struct virtio_device *dev, int *status)
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ *status = ci_read_8(dev->common.addr +
+ offset_of(struct virtio_dev_common, dev_status));
+ } else {
+ *status = ci_read_8(dev->legacy.addr+VIRTIOHDR_DEVICE_STATUS);
+ }
+}
+
+/**
+ * Set guest feature bits
+ */
+void virtio_set_guest_features(struct virtio_device *dev, uint64_t features)
+
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ uint32_t f1 = (features >> 32) & 0xFFFFFFFF;
+ uint32_t f0 = features & 0xFFFFFFFF;
+ void *addr = dev->common.addr;
+
+ ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features_sel),
+ cpu_to_le32(1));
+ ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features),
+ cpu_to_le32(f1));
+
+ ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features_sel),
+ cpu_to_le32(0));
+ ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features),
+ cpu_to_le32(f0));
+ } else {
+ ci_write_32(dev->legacy.addr+VIRTIOHDR_GUEST_FEATURES, cpu_to_le32(features));
+ }
+}
+
+/**
+ * Get host feature bits
+ */
+uint64_t virtio_get_host_features(struct virtio_device *dev)
+
+{
+ uint64_t features = 0;
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ uint32_t f0 = 0, f1 = 0;
+ void *addr = dev->common.addr;
+
+ ci_write_32(addr + offset_of(struct virtio_dev_common, dev_features_sel),
+ cpu_to_le32(1));
+ f1 = ci_read_32(addr +
+ offset_of(struct virtio_dev_common, dev_features));
+ ci_write_32(addr + offset_of(struct virtio_dev_common, dev_features_sel),
+ cpu_to_le32(0));
+ f0 = ci_read_32(addr +
+ offset_of(struct virtio_dev_common, dev_features));
+
+ features = ((uint64_t)le32_to_cpu(f1) << 32) | le32_to_cpu(f0);
+ } else {
+ features = le32_to_cpu(ci_read_32(dev->legacy.addr+VIRTIOHDR_DEVICE_FEATURES));
+ }
+ return features;
+}
+
+int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features)
+{
+ uint64_t host_features = 0;
+ int status;
+
+ /* Negotiate features */
+ host_features = virtio_get_host_features(dev);
+ if (!(host_features & VIRTIO_F_VERSION_1)) {
+ fprintf(stderr, "Device does not support virtio 1.0 %llx\n", host_features);
+ return -1;
+ }
+
+ if (host_features & VIRTIO_F_IOMMU_PLATFORM)
+ features |= VIRTIO_F_IOMMU_PLATFORM;
+
+ virtio_set_guest_features(dev, features);
+ host_features = virtio_get_host_features(dev);
+ if ((host_features & features) != features) {
+ fprintf(stderr, "Features error %llx\n", features);
+ return -1;
+ }
+
+ virtio_get_status(dev, &status);
+ status |= VIRTIO_STAT_FEATURES_OK;
+ virtio_set_status(dev, status);
+
+ /* Read back to verify the FEATURES_OK bit */
+ virtio_get_status(dev, &status);
+ if ((status & VIRTIO_STAT_FEATURES_OK) != VIRTIO_STAT_FEATURES_OK)
+ return -1;
+
+ dev->features = features;
+
+ return 0;
+}
+
+/**
+ * Get additional config values
+ */
+uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size)
+{
+ uint64_t val = ~0ULL;
+ uint32_t hi, lo;
+ void *confbase;
+
+ if (dev->features & VIRTIO_F_VERSION_1)
+ confbase = dev->device.addr;
+ else
+ confbase = dev->legacy.addr+VIRTIOHDR_DEVICE_CONFIG;
+
+ switch (size) {
+ case 1:
+ val = ci_read_8(confbase+offset);
+ break;
+ case 2:
+ val = ci_read_16(confbase+offset);
+ if (dev->features & VIRTIO_F_VERSION_1)
+ val = le16_to_cpu(val);
+ break;
+ case 4:
+ val = ci_read_32(confbase+offset);
+ if (dev->features & VIRTIO_F_VERSION_1)
+ val = le32_to_cpu(val);
+ break;
+ case 8:
+ /* We don't support 8 bytes PIO accesses
+ * in qemu and this is all PIO
+ */
+ lo = ci_read_32(confbase+offset);
+ hi = ci_read_32(confbase+offset+4);
+ if (dev->features & VIRTIO_F_VERSION_1)
+ val = (uint64_t)le32_to_cpu(hi) << 32 | le32_to_cpu(lo);
+ else
+ val = (uint64_t)hi << 32 | lo;
+ break;
+ }
+
+ return val;
+}
+
+/**
+ * Get config blob
+ */
+int __virtio_read_config(struct virtio_device *dev, void *dst,
+ int offset, int len)
+{
+ void *confbase;
+ unsigned char *buf = dst;
+ int i;
+
+ if (dev->features & VIRTIO_F_VERSION_1)
+ confbase = dev->device.addr;
+ else
+ confbase = dev->legacy.addr+VIRTIOHDR_DEVICE_CONFIG;
+
+ for (i = 0; i < len; i++)
+ buf[i] = ci_read_8(confbase + offset + i);
+
+ return len;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio.code b/roms/SLOF/lib/libvirtio/virtio.code
new file mode 100644
index 000000000..db3ed600f
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio.code
@@ -0,0 +1,203 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <virtio.h>
+#include <virtio-blk.h>
+#include <virtio-9p.h>
+#include <virtio-scsi.h>
+#include <virtio-net.h>
+#include <virtio-serial.h>
+
+/******** core virtio ********/
+
+// : virtio-setup-vd ( -- dev )
+PRIM(virtio_X2d_setup_X2d_vd)
+ PUSH; TOS.a = virtio_setup_vd();
+MIRP
+
+// : virtio-vring-size ( queuesize -- ringsize )
+PRIM(virtio_X2d_vring_X2d_size)
+ TOS.u = virtio_vring_size(TOS.u);
+MIRP
+
+// : virtio-get-qsize ( dev queue -- queuesize )
+PRIM(virtio_X2d_get_X2d_qsize)
+ int queue = TOS.u; POP;
+ TOS.u = virtio_get_qsize(TOS.a, queue);
+MIRP
+
+// : virtio-get-config ( dev offset size -- val )
+PRIM(virtio_X2d_get_X2d_config)
+ int size = TOS.u; POP;
+ int offset = TOS.u; POP;
+ TOS.u = virtio_get_config(TOS.a, offset, size);
+MIRP
+
+/******** virtio-blk ********/
+
+// : virtio-blk-init ( dev -- blk-size)
+PRIM(virtio_X2d_blk_X2d_init)
+ void *dev = TOS.a;
+ TOS.u = virtioblk_init(dev);
+MIRP
+
+// : virtio-blk-shutdown ( dev -- )
+PRIM(virtio_X2d_blk_X2d_shutdown)
+ void *dev = TOS.a; POP;
+ virtioblk_shutdown(dev);
+MIRP
+
+// : virtio-blk-read ( buf blkno cnt dev -- #read )
+PRIM(virtio_X2d_blk_X2d_read)
+ void *dev = TOS.a; POP;
+ long cnt = TOS.n; POP;
+ long blkno = TOS.n; POP;
+ void *buf = TOS.a;
+ TOS.n = virtioblk_transfer(dev, buf, blkno, cnt, VIRTIO_BLK_T_IN);
+MIRP
+
+// : virtio-blk-write ( buf blkno cnt dev -- #written )
+PRIM(virtio_X2d_blk_X2d_write)
+ void *dev = TOS.a; POP;
+ long cnt = TOS.n; POP;
+ long blkno = TOS.n; POP;
+ void *buf = TOS.a;
+ TOS.n = virtioblk_transfer(dev, buf, blkno, cnt, VIRTIO_BLK_T_OUT);
+MIRP
+
+/******** virtio-fs ********/
+
+// : virtio-fs-init ( dev tx rx size -- success )
+PRIM(virtio_X2d_fs_X2d_init)
+ int size = TOS.n; POP;
+ void *rx = TOS.a; POP;
+ void *tx = TOS.a; POP;
+ void *dev = TOS.a;
+
+ TOS.n = virtio_9p_init(dev, tx, rx, size) == 0 ? -1 : 0;
+MIRP
+
+// : virtio-fs-shutdown ( dev -- )
+PRIM(virtio_X2d_fs_X2d_shutdown)
+ void *dev = TOS.a; POP;
+
+ virtio_9p_shutdown(dev);
+MIRP
+
+// : virtio-fs-load ( dev buf str -- #read )
+PRIM(virtio_X2d_fs_X2d_load)
+ char *str = TOS.a; POP;
+ void *buf = TOS.a; POP;
+ void *dev = TOS.a;
+
+ TOS.n = virtio_9p_load(dev, str, buf);
+MIRP
+
+/******** virtio-scsi ********/
+
+// : virtio-scsi-init ( dev -- success )
+PRIM(virtio_X2d_scsi_X2d_init)
+ void *dev = TOS.a;
+ TOS.u = virtioscsi_init(dev);
+MIRP
+
+// : virtio-scsi-shutdown ( dev -- )
+PRIM(virtio_X2d_scsi_X2d_shutdown)
+ void *dev = TOS.a; POP;
+ virtioscsi_shutdown(dev);
+MIRP
+
+// : virtio-scsi-send ( buf_addr buf_len is_read req_ptr rsp_ptr dev -- success)
+PRIM(virtio_X2d_scsi_X2d_send)
+ void *dev = TOS.a; POP;
+ void *resp = TOS.a; POP;
+ void *req = TOS.a; POP;
+ int is_read = !!TOS.n; POP;
+ uint64_t blen = TOS.n; POP;
+ void *buf = TOS.a;
+ TOS.n = virtioscsi_send(dev, req, resp, is_read, buf, blen);
+MIRP
+
+/******** virtio-net ********/
+
+// : virtio-net-open ( dev -- false | [ vnet true ] )
+PRIM(virtio_X2d_net_X2d_open)
+{
+ void *dev = TOS.a;
+
+ void *vnet = virtionet_open(dev);
+
+ if (vnet) {
+ TOS.u = (unsigned long)vnet; PUSH;
+ TOS.n = -1;
+ } else
+ TOS.n = 0;
+}
+MIRP
+
+// : virtio-net-close ( vnet -- )
+PRIM(virtio_X2d_net_X2d_close)
+{
+ void *vnet = TOS.a; POP;
+ virtionet_close(vnet);
+}
+MIRP
+
+// : virtio-net-read ( addr len vnet -- actual )
+PRIM(virtio_X2d_net_X2d_read)
+{
+ void *vnet = TOS.a; POP;
+ int len = TOS.u; POP;
+ TOS.n = virtionet_read(vnet, TOS.a, len);
+}
+MIRP
+
+// : virtio-net-write ( addr len vnet -- actual )
+PRIM(virtio_X2d_net_X2d_write)
+{
+ void *vnet = TOS.a; POP;
+ int len = TOS.u; POP;
+ TOS.n = virtionet_write(vnet, TOS.a, len);
+}
+MIRP
+
+/*********** virtio-serial ***********/
+// : virtio-serial-init ( dev -- false | true)
+PRIM(virtio_X2d_serial_X2d_init)
+ void *dev = TOS.a;
+ TOS.u = virtio_serial_init(dev);
+MIRP
+
+// : virtio-serial-shutdown ( dev -- )
+PRIM(virtio_X2d_serial_X2d_shutdown)
+ void *dev = TOS.a; POP;
+ virtio_serial_shutdown(dev);
+MIRP
+
+// : virtio-serial-putchar ( dev char -- )
+PRIM(virtio_X2d_serial_X2d_putchar)
+ unsigned long c = TOS.n; POP;
+ void *dev = TOS.a; POP;
+ virtio_serial_putchar(dev, c);
+MIRP
+
+// : virtio-serial-getchar ( dev -- char)
+PRIM(virtio_X2d_serial_X2d_getchar)
+ void *dev = TOS.a;
+ TOS.n = virtio_serial_getchar(dev);
+MIRP
+
+// : virtio-serial-haschar ( dev -- true | false)
+PRIM(virtio_X2d_serial_X2d_haschar)
+ void *dev = TOS.a;
+ TOS.n = virtio_serial_haschar(dev);
+MIRP
diff --git a/roms/SLOF/lib/libvirtio/virtio.h b/roms/SLOF/lib/libvirtio/virtio.h
new file mode 100644
index 000000000..c4eafe40d
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio.h
@@ -0,0 +1,132 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef _LIBVIRTIO_H
+#define _LIBVIRTIO_H
+
+#include <stdint.h>
+
+/* Device status bits */
+#define VIRTIO_STAT_ACKNOWLEDGE 1
+#define VIRTIO_STAT_DRIVER 2
+#define VIRTIO_STAT_DRIVER_OK 4
+#define VIRTIO_STAT_FEATURES_OK 8
+#define VIRTIO_STAT_NEEDS_RESET 64
+#define VIRTIO_STAT_FAILED 128
+
+#define BIT(x) (1UL << (x))
+
+/* VIRTIO 1.0 Device independent feature bits */
+#define VIRTIO_F_RING_INDIRECT_DESC BIT(28)
+#define VIRTIO_F_RING_EVENT_IDX BIT(29)
+#define VIRTIO_F_VERSION_1 BIT(32)
+#define VIRTIO_F_IOMMU_PLATFORM BIT(33)
+
+#define VIRTIO_TIMEOUT 5000 /* 5 sec timeout */
+
+/* Definitions for vring_desc.flags */
+#define VRING_DESC_F_NEXT 1 /* buffer continues via the next field */
+#define VRING_DESC_F_WRITE 2 /* buffer is write-only (otherwise read-only) */
+#define VRING_DESC_F_INDIRECT 4 /* buffer contains a list of buffer descriptors */
+
+/* Descriptor table entry - see Virtio Spec chapter 2.3.2 */
+struct vring_desc {
+ uint64_t addr; /* Address (guest-physical) */
+ uint32_t len; /* Length */
+ uint16_t flags; /* The flags as indicated above */
+ uint16_t next; /* Next field if flags & NEXT */
+};
+
+/* Definitions for vring_avail.flags */
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+/* Available ring - see Virtio Spec chapter 2.3.4 */
+struct vring_avail {
+ uint16_t flags;
+ uint16_t idx;
+ uint16_t ring[];
+};
+
+/* Definitions for vring_used.flags */
+#define VRING_USED_F_NO_NOTIFY 1
+
+struct vring_used_elem {
+ uint32_t id; /* Index of start of used descriptor chain */
+ uint32_t len; /* Total length of the descriptor chain which was used */
+};
+
+struct vring_used {
+ uint16_t flags;
+ uint16_t idx;
+ struct vring_used_elem ring[];
+};
+
+/* Structure shared with SLOF and is 16bytes */
+struct virtio_cap {
+ void *addr;
+ uint8_t bar;
+ uint8_t is_io;
+ uint8_t cap_id;
+};
+
+struct vqs {
+ uint32_t size;
+ void *buf_mem;
+ struct vring_desc *desc;
+ struct vring_avail *avail;
+ struct vring_used *used;
+ void **desc_gpas; /* to get gpa from desc->addr (which is ioba) */
+ uint64_t bus_desc;
+};
+
+struct virtio_device {
+ uint64_t features;
+ struct virtio_cap legacy;
+ struct virtio_cap common;
+ struct virtio_cap notify;
+ struct virtio_cap isr;
+ struct virtio_cap device;
+ struct virtio_cap pci;
+ uint32_t notify_off_mul;
+ struct vqs vq[3];
+};
+
+/* Parts of the virtqueue are aligned on a 4096 byte page boundary */
+#define VQ_ALIGN(addr) (((addr) + 0xfff) & ~0xfff)
+
+extern unsigned long virtio_vring_size(unsigned int qsize);
+extern unsigned int virtio_get_qsize(struct virtio_device *dev, int queue);
+extern struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue);
+extern struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue);
+extern struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue);
+extern void virtio_fill_desc(struct vqs *vq, int id, uint64_t features,
+ uint64_t addr, uint32_t len,
+ uint16_t flags, uint16_t next);
+extern void virtio_free_desc(struct vqs *vq, int id, uint64_t features);
+void *virtio_desc_addr(struct virtio_device *vdev, int queue, int id);
+extern struct vqs *virtio_queue_init_vq(struct virtio_device *dev, unsigned int id);
+extern void virtio_queue_term_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id);
+
+extern struct virtio_device *virtio_setup_vd(void);
+extern void virtio_reset_device(struct virtio_device *dev);
+extern void virtio_queue_notify(struct virtio_device *dev, int queue);
+extern void virtio_set_status(struct virtio_device *dev, int status);
+extern void virtio_get_status(struct virtio_device *dev, int *status);
+extern void virtio_set_guest_features(struct virtio_device *dev, uint64_t features);
+extern uint64_t virtio_get_host_features(struct virtio_device *dev);
+extern int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features);
+extern uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size);
+extern int __virtio_read_config(struct virtio_device *dev, void *dst,
+ int offset, int len);
+
+
+#endif /* _LIBVIRTIO_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio.in b/roms/SLOF/lib/libvirtio/virtio.in
new file mode 100644
index 000000000..5c96c7d0d
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio.in
@@ -0,0 +1,41 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+cod(virtio-setup-vd)
+
+cod(virtio-vring-size)
+cod(virtio-get-qsize)
+cod(virtio-get-config)
+
+cod(virtio-blk-init)
+cod(virtio-blk-shutdown)
+cod(virtio-blk-read)
+cod(virtio-blk-write)
+
+cod(virtio-scsi-init)
+cod(virtio-scsi-shutdown)
+cod(virtio-scsi-send)
+
+cod(virtio-fs-init)
+cod(virtio-fs-shutdown)
+cod(virtio-fs-load)
+
+cod(virtio-net-open)
+cod(virtio-net-close)
+cod(virtio-net-read)
+cod(virtio-net-write)
+
+cod(virtio-serial-init)
+cod(virtio-serial-shutdown)
+cod(virtio-serial-putchar)
+cod(virtio-serial-getchar)
+cod(virtio-serial-haschar)