All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
@ 2017-06-27 11:48 Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware Thomas Huth
                   ` (18 more replies)
  0 siblings, 19 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

It's already possible to do a network boot of an s390x guest with an
external netboot image (based on a Linux installation), but it would
be much more convenient if the s390-ccw firmware supported network
booting right out of the box, without the need to assemble such an
external image first.

This patch series now introduces network booting via DHCP and TFTP
directly into the s390-ccw firmware by re-using the networking stack
from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
and adds a driver for virtio-net-ccw devices.

Once the patches have been applied, you can download an .INS file
via TFTP which contains the information about the further files
that have to be loaded - kernel, initrd, etc. For example, you can
use the built-in TFTP and DHCP server of QEMU for this by starting
QEMU with:

 qemu-system-s390x ... -device virtio-net,netdev=n1,bootindex=1 \
       -netdev user,id=n1,tftp=/path/to/tftp,bootfile=generic.ins

The .INS file has to use the same syntax as the .INS files that can
already be found on s390x Linux distribution installation CD-ROMs.

The patches are still in a rough shape, but before I continue here,
I though I'd get some feedback first. Specifically:

- This adds a lot of additional code to the s390-ccw firmware (and
  the binary is afterwards three times as big as before, 75k instead
  of 25k) ... is that still acceptable?

- Is it OK to require loading an .INS file first? Or does anybody
  have a better idea how to load multiple files (kernel, initrd,
  etc. ...)?

- The code from SLOF uses a different coding style (TABs instead
  of space) ... is it OK to keep that coding style here so we
  can share patches between SLOF and s390-ccw more easily?

- The code only supports TFTP (via UDP) ... I think that is OK for
  most use-cases, but if we ever want to support network booting
  via HTTP or something else that is based on TCP, we would need to
  use something else instead... Should we maybe rather head towards
  grub2, petitboot or something different instead?

 Thomas


Thomas Huth (14):
  pc-bios/s390-ccw: Add the libc from the SLOF firmware
  pc-bios/s390-ccw: Start using the libc from SLOF
  pc-bios/s390-ccw: Add a write() function for stdio
  pc-bios/s390-ccw: Add implementation of sbrk()
  pc-bios/s390-ccw: Add the TFTP network loading stack from SLOF
  libnet: Remove remainders of netsave code
  libnet: Rework error message printing
  libnet: Refactor some code of netload() into a separate function
  pc-bios/s390-ccw: Make the basic libnet code compilable
  pc-bios/s390-ccw: Add timer code for the libnet
  pc-bios/s390-ccw: Add virtio-net driver code
  pc-bios/s390-ccw: Load file via an intermediate .INS file
  pc-bios/s390-ccw: Allow loading to address 0
  pc-bios/s390-ccw: Wire up the netload code

 configure                                  |   6 +-
 hw/s390x/ipl.c                             |   2 +-
 pc-bios/s390-ccw/Makefile                  |  14 +-
 pc-bios/s390-ccw/bootmap.c                 |  10 +-
 pc-bios/s390-ccw/bootmap.h                 |   1 +
 pc-bios/s390-ccw/libc/Makefile             |  47 ++
 pc-bios/s390-ccw/libc/README.txt           |  49 ++
 pc-bios/s390-ccw/libc/ctype/Makefile.inc   |  21 +
 pc-bios/s390-ccw/libc/ctype/isdigit.c      |  25 +
 pc-bios/s390-ccw/libc/ctype/isprint.c      |  18 +
 pc-bios/s390-ccw/libc/ctype/isspace.c      |  29 +
 pc-bios/s390-ccw/libc/ctype/isxdigit.c     |  21 +
 pc-bios/s390-ccw/libc/ctype/tolower.c      |  18 +
 pc-bios/s390-ccw/libc/ctype/toupper.c      |  21 +
 pc-bios/s390-ccw/libc/include/ctype.h      |  24 +
 pc-bios/s390-ccw/libc/include/errno.h      |  34 +
 pc-bios/s390-ccw/libc/include/limits.h     |  32 +
 pc-bios/s390-ccw/libc/include/stdarg.h     |  22 +
 pc-bios/s390-ccw/libc/include/stdbool.h    |  20 +
 pc-bios/s390-ccw/libc/include/stddef.h     |  25 +
 pc-bios/s390-ccw/libc/include/stdint.h     |  28 +
 pc-bios/s390-ccw/libc/include/stdio.h      |  63 ++
 pc-bios/s390-ccw/libc/include/stdlib.h     |  34 +
 pc-bios/s390-ccw/libc/include/string.h     |  37 ++
 pc-bios/s390-ccw/libc/include/sys/socket.h |  53 ++
 pc-bios/s390-ccw/libc/include/unistd.h     |  28 +
 pc-bios/s390-ccw/libc/stdio/Makefile.inc   |  24 +
 pc-bios/s390-ccw/libc/stdio/fileno.c       |  19 +
 pc-bios/s390-ccw/libc/stdio/fprintf.c      |  26 +
 pc-bios/s390-ccw/libc/stdio/printf.c       |  27 +
 pc-bios/s390-ccw/libc/stdio/putc.c         |  25 +
 pc-bios/s390-ccw/libc/stdio/putchar.c      |  21 +
 pc-bios/s390-ccw/libc/stdio/puts.c         |  28 +
 pc-bios/s390-ccw/libc/stdio/setvbuf.c      |  28 +
 pc-bios/s390-ccw/libc/stdio/sprintf.c      |  30 +
 pc-bios/s390-ccw/libc/stdio/stdchnls.c     |  23 +
 pc-bios/s390-ccw/libc/stdio/vfprintf.c     |  27 +
 pc-bios/s390-ccw/libc/stdio/vsnprintf.c    | 298 +++++++++
 pc-bios/s390-ccw/libc/stdio/vsprintf.c     |  19 +
 pc-bios/s390-ccw/libc/stdlib/Makefile.inc  |  23 +
 pc-bios/s390-ccw/libc/stdlib/atoi.c        |  18 +
 pc-bios/s390-ccw/libc/stdlib/atol.c        |  18 +
 pc-bios/s390-ccw/libc/stdlib/error.c       |  15 +
 pc-bios/s390-ccw/libc/stdlib/free.c        |  26 +
 pc-bios/s390-ccw/libc/stdlib/malloc.c      | 157 +++++
 pc-bios/s390-ccw/libc/stdlib/malloc_defs.h |  16 +
 pc-bios/s390-ccw/libc/stdlib/memalign.c    |  26 +
 pc-bios/s390-ccw/libc/stdlib/rand.c        |  29 +
 pc-bios/s390-ccw/libc/stdlib/realloc.c     |  40 ++
 pc-bios/s390-ccw/libc/stdlib/strtol.c      | 115 ++++
 pc-bios/s390-ccw/libc/stdlib/strtoul.c     | 105 ++++
 pc-bios/s390-ccw/libc/string/Makefile.inc  |  23 +
 pc-bios/s390-ccw/libc/string/memchr.c      |  29 +
 pc-bios/s390-ccw/libc/string/memcmp.c      |  30 +
 pc-bios/s390-ccw/libc/string/memcpy.c      |  27 +
 pc-bios/s390-ccw/libc/string/memmove.c     |  42 ++
 pc-bios/s390-ccw/libc/string/memset.c      |  25 +
 pc-bios/s390-ccw/libc/string/strcasecmp.c  |  28 +
 pc-bios/s390-ccw/libc/string/strcat.c      |  24 +
 pc-bios/s390-ccw/libc/string/strchr.c      |  28 +
 pc-bios/s390-ccw/libc/string/strcmp.c      |  28 +
 pc-bios/s390-ccw/libc/string/strcpy.c      |  25 +
 pc-bios/s390-ccw/libc/string/strlen.c      |  27 +
 pc-bios/s390-ccw/libc/string/strncasecmp.c |  32 +
 pc-bios/s390-ccw/libc/string/strncmp.c     |  31 +
 pc-bios/s390-ccw/libc/string/strncpy.c     |  33 +
 pc-bios/s390-ccw/libc/string/strstr.c      |  37 ++
 pc-bios/s390-ccw/libc/string/strtok.c      |  45 ++
 pc-bios/s390-ccw/libnet/Makefile           |  42 ++
 pc-bios/s390-ccw/libnet/args.c             | 179 ++++++
 pc-bios/s390-ccw/libnet/args.h             |  23 +
 pc-bios/s390-ccw/libnet/dhcp.c             | 955 +++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/dhcp.h             |  49 ++
 pc-bios/s390-ccw/libnet/dhcpv6.c           | 212 +++++++
 pc-bios/s390-ccw/libnet/dhcpv6.h           | 154 +++++
 pc-bios/s390-ccw/libnet/dns.c              | 526 ++++++++++++++++
 pc-bios/s390-ccw/libnet/dns.h              |  28 +
 pc-bios/s390-ccw/libnet/ethernet.c         | 189 ++++++
 pc-bios/s390-ccw/libnet/ethernet.h         |  47 ++
 pc-bios/s390-ccw/libnet/icmpv6.c           | 409 ++++++++++++
 pc-bios/s390-ccw/libnet/icmpv6.h           | 135 ++++
 pc-bios/s390-ccw/libnet/ipv4.c             | 898 +++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/ipv4.h             |  97 +++
 pc-bios/s390-ccw/libnet/ipv6.c             | 774 +++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/ipv6.h             | 188 ++++++
 pc-bios/s390-ccw/libnet/ndp.c              | 184 ++++++
 pc-bios/s390-ccw/libnet/ndp.h              |  72 +++
 pc-bios/s390-ccw/libnet/netapps.h          |  25 +
 pc-bios/s390-ccw/libnet/netload.c          | 600 ++++++++++++++++++
 pc-bios/s390-ccw/libnet/tcp.c              |  46 ++
 pc-bios/s390-ccw/libnet/tcp.h              |  27 +
 pc-bios/s390-ccw/libnet/tftp.c             | 595 ++++++++++++++++++
 pc-bios/s390-ccw/libnet/tftp.h             |  50 ++
 pc-bios/s390-ccw/libnet/time.h             |   6 +
 pc-bios/s390-ccw/libnet/timer.c            |  40 ++
 pc-bios/s390-ccw/libnet/udp.c              | 115 ++++
 pc-bios/s390-ccw/libnet/udp.h              |  53 ++
 pc-bios/s390-ccw/main.c                    |   3 +-
 pc-bios/s390-ccw/s390-ccw.h                |  30 +-
 pc-bios/s390-ccw/sbrk.c                    |  39 ++
 pc-bios/s390-ccw/sclp.c                    |  32 +-
 pc-bios/s390-ccw/virtio-net.c              | 125 ++++
 pc-bios/s390-ccw/virtio.c                  |  16 +-
 pc-bios/s390-ccw/virtio.h                  |  11 +
 104 files changed, 9296 insertions(+), 57 deletions(-)
 create mode 100644 pc-bios/s390-ccw/libc/Makefile
 create mode 100644 pc-bios/s390-ccw/libc/README.txt
 create mode 100644 pc-bios/s390-ccw/libc/ctype/Makefile.inc
 create mode 100644 pc-bios/s390-ccw/libc/ctype/isdigit.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/isprint.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/isspace.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/isxdigit.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/tolower.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/toupper.c
 create mode 100644 pc-bios/s390-ccw/libc/include/ctype.h
 create mode 100644 pc-bios/s390-ccw/libc/include/errno.h
 create mode 100644 pc-bios/s390-ccw/libc/include/limits.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdarg.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdbool.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stddef.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdint.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdio.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdlib.h
 create mode 100644 pc-bios/s390-ccw/libc/include/string.h
 create mode 100644 pc-bios/s390-ccw/libc/include/sys/socket.h
 create mode 100644 pc-bios/s390-ccw/libc/include/unistd.h
 create mode 100644 pc-bios/s390-ccw/libc/stdio/Makefile.inc
 create mode 100644 pc-bios/s390-ccw/libc/stdio/fileno.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/fprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/printf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/putc.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/putchar.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/puts.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/setvbuf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/sprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/stdchnls.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/vfprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/vsnprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/vsprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/Makefile.inc
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/atoi.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/atol.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/error.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/free.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/malloc.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/malloc_defs.h
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/memalign.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/rand.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/realloc.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/strtol.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/strtoul.c
 create mode 100644 pc-bios/s390-ccw/libc/string/Makefile.inc
 create mode 100644 pc-bios/s390-ccw/libc/string/memchr.c
 create mode 100644 pc-bios/s390-ccw/libc/string/memcmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/memcpy.c
 create mode 100644 pc-bios/s390-ccw/libc/string/memmove.c
 create mode 100644 pc-bios/s390-ccw/libc/string/memset.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strcasecmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strcat.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strchr.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strcmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strcpy.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strlen.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strncasecmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strncmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strncpy.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strstr.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strtok.c
 create mode 100644 pc-bios/s390-ccw/libnet/Makefile
 create mode 100644 pc-bios/s390-ccw/libnet/args.c
 create mode 100644 pc-bios/s390-ccw/libnet/args.h
 create mode 100644 pc-bios/s390-ccw/libnet/dhcp.c
 create mode 100644 pc-bios/s390-ccw/libnet/dhcp.h
 create mode 100644 pc-bios/s390-ccw/libnet/dhcpv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/dhcpv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/dns.c
 create mode 100644 pc-bios/s390-ccw/libnet/dns.h
 create mode 100644 pc-bios/s390-ccw/libnet/ethernet.c
 create mode 100644 pc-bios/s390-ccw/libnet/ethernet.h
 create mode 100644 pc-bios/s390-ccw/libnet/icmpv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/icmpv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/ipv4.c
 create mode 100644 pc-bios/s390-ccw/libnet/ipv4.h
 create mode 100644 pc-bios/s390-ccw/libnet/ipv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/ipv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/ndp.c
 create mode 100644 pc-bios/s390-ccw/libnet/ndp.h
 create mode 100644 pc-bios/s390-ccw/libnet/netapps.h
 create mode 100644 pc-bios/s390-ccw/libnet/netload.c
 create mode 100644 pc-bios/s390-ccw/libnet/tcp.c
 create mode 100644 pc-bios/s390-ccw/libnet/tcp.h
 create mode 100644 pc-bios/s390-ccw/libnet/tftp.c
 create mode 100644 pc-bios/s390-ccw/libnet/tftp.h
 create mode 100644 pc-bios/s390-ccw/libnet/time.h
 create mode 100644 pc-bios/s390-ccw/libnet/timer.c
 create mode 100644 pc-bios/s390-ccw/libnet/udp.c
 create mode 100644 pc-bios/s390-ccw/libnet/udp.h
 create mode 100644 pc-bios/s390-ccw/sbrk.c
 create mode 100644 pc-bios/s390-ccw/virtio-net.c

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 15:32   ` David Hildenbrand
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 02/14] pc-bios/s390-ccw: Start using the libc from SLOF Thomas Huth
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

To be able to use some more advanced libc functions in the s390-ccw
firmware, like printf() and malloc(), we need a better libc here.
This patch adds the C library from the SLOF firmware (taken from
the SLOF commit ID 62674aabe20612a9786fa03e87cf6916ba97a99a). The
files are copied without modifications here and will be adapted for
the s390-ccw firmware by the next patch. I just removed the getopt()
and scanf()-like functions from the libc since we likely do not need
them in the s390-ccw firmware.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/libc/Makefile             |  61 ++++++
 pc-bios/s390-ccw/libc/README.txt           |  49 +++++
 pc-bios/s390-ccw/libc/ctype/Makefile.inc   |  20 ++
 pc-bios/s390-ccw/libc/ctype/isdigit.c      |  25 +++
 pc-bios/s390-ccw/libc/ctype/isprint.c      |  18 ++
 pc-bios/s390-ccw/libc/ctype/isspace.c      |  29 +++
 pc-bios/s390-ccw/libc/ctype/isxdigit.c     |  21 ++
 pc-bios/s390-ccw/libc/ctype/tolower.c      |  18 ++
 pc-bios/s390-ccw/libc/ctype/toupper.c      |  21 ++
 pc-bios/s390-ccw/libc/include/ctype.h      |  24 +++
 pc-bios/s390-ccw/libc/include/errno.h      |  34 ++++
 pc-bios/s390-ccw/libc/include/limits.h     |  32 ++++
 pc-bios/s390-ccw/libc/include/stdarg.h     |  22 +++
 pc-bios/s390-ccw/libc/include/stdbool.h    |  20 ++
 pc-bios/s390-ccw/libc/include/stddef.h     |  25 +++
 pc-bios/s390-ccw/libc/include/stdint.h     |  28 +++
 pc-bios/s390-ccw/libc/include/stdio.h      |  63 ++++++
 pc-bios/s390-ccw/libc/include/stdlib.h     |  34 ++++
 pc-bios/s390-ccw/libc/include/string.h     |  37 ++++
 pc-bios/s390-ccw/libc/include/sys/socket.h |  53 +++++
 pc-bios/s390-ccw/libc/include/unistd.h     |  28 +++
 pc-bios/s390-ccw/libc/stdio/Makefile.inc   |  23 +++
 pc-bios/s390-ccw/libc/stdio/fileno.c       |  19 ++
 pc-bios/s390-ccw/libc/stdio/fprintf.c      |  26 +++
 pc-bios/s390-ccw/libc/stdio/printf.c       |  27 +++
 pc-bios/s390-ccw/libc/stdio/putc.c         |  25 +++
 pc-bios/s390-ccw/libc/stdio/putchar.c      |  21 ++
 pc-bios/s390-ccw/libc/stdio/puts.c         |  28 +++
 pc-bios/s390-ccw/libc/stdio/setvbuf.c      |  28 +++
 pc-bios/s390-ccw/libc/stdio/sprintf.c      |  30 +++
 pc-bios/s390-ccw/libc/stdio/stdchnls.c     |  23 +++
 pc-bios/s390-ccw/libc/stdio/vfprintf.c     |  27 +++
 pc-bios/s390-ccw/libc/stdio/vsnprintf.c    | 298 +++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libc/stdio/vsprintf.c     |  19 ++
 pc-bios/s390-ccw/libc/stdlib/Makefile.inc  |  22 +++
 pc-bios/s390-ccw/libc/stdlib/atoi.c        |  18 ++
 pc-bios/s390-ccw/libc/stdlib/atol.c        |  18 ++
 pc-bios/s390-ccw/libc/stdlib/error.c       |  15 ++
 pc-bios/s390-ccw/libc/stdlib/free.c        |  26 +++
 pc-bios/s390-ccw/libc/stdlib/malloc.c      | 157 +++++++++++++++
 pc-bios/s390-ccw/libc/stdlib/malloc_defs.h |  16 ++
 pc-bios/s390-ccw/libc/stdlib/memalign.c    |  26 +++
 pc-bios/s390-ccw/libc/stdlib/rand.c        |  29 +++
 pc-bios/s390-ccw/libc/stdlib/realloc.c     |  40 ++++
 pc-bios/s390-ccw/libc/stdlib/strtol.c      | 115 +++++++++++
 pc-bios/s390-ccw/libc/stdlib/strtoul.c     | 105 ++++++++++
 pc-bios/s390-ccw/libc/string/Makefile.inc  |  22 +++
 pc-bios/s390-ccw/libc/string/memchr.c      |  29 +++
 pc-bios/s390-ccw/libc/string/memcmp.c      |  30 +++
 pc-bios/s390-ccw/libc/string/memcpy.c      |  27 +++
 pc-bios/s390-ccw/libc/string/memmove.c     |  42 ++++
 pc-bios/s390-ccw/libc/string/memset.c      |  25 +++
 pc-bios/s390-ccw/libc/string/strcasecmp.c  |  28 +++
 pc-bios/s390-ccw/libc/string/strcat.c      |  24 +++
 pc-bios/s390-ccw/libc/string/strchr.c      |  28 +++
 pc-bios/s390-ccw/libc/string/strcmp.c      |  28 +++
 pc-bios/s390-ccw/libc/string/strcpy.c      |  25 +++
 pc-bios/s390-ccw/libc/string/strlen.c      |  27 +++
 pc-bios/s390-ccw/libc/string/strncasecmp.c |  32 ++++
 pc-bios/s390-ccw/libc/string/strncmp.c     |  31 +++
 pc-bios/s390-ccw/libc/string/strncpy.c     |  33 ++++
 pc-bios/s390-ccw/libc/string/strstr.c      |  37 ++++
 pc-bios/s390-ccw/libc/string/strtok.c      |  45 +++++
 63 files changed, 2356 insertions(+)
 create mode 100644 pc-bios/s390-ccw/libc/Makefile
 create mode 100644 pc-bios/s390-ccw/libc/README.txt
 create mode 100644 pc-bios/s390-ccw/libc/ctype/Makefile.inc
 create mode 100644 pc-bios/s390-ccw/libc/ctype/isdigit.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/isprint.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/isspace.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/isxdigit.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/tolower.c
 create mode 100644 pc-bios/s390-ccw/libc/ctype/toupper.c
 create mode 100644 pc-bios/s390-ccw/libc/include/ctype.h
 create mode 100644 pc-bios/s390-ccw/libc/include/errno.h
 create mode 100644 pc-bios/s390-ccw/libc/include/limits.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdarg.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdbool.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stddef.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdint.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdio.h
 create mode 100644 pc-bios/s390-ccw/libc/include/stdlib.h
 create mode 100644 pc-bios/s390-ccw/libc/include/string.h
 create mode 100644 pc-bios/s390-ccw/libc/include/sys/socket.h
 create mode 100644 pc-bios/s390-ccw/libc/include/unistd.h
 create mode 100644 pc-bios/s390-ccw/libc/stdio/Makefile.inc
 create mode 100644 pc-bios/s390-ccw/libc/stdio/fileno.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/fprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/printf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/putc.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/putchar.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/puts.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/setvbuf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/sprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/stdchnls.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/vfprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/vsnprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdio/vsprintf.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/Makefile.inc
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/atoi.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/atol.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/error.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/free.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/malloc.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/malloc_defs.h
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/memalign.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/rand.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/realloc.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/strtol.c
 create mode 100644 pc-bios/s390-ccw/libc/stdlib/strtoul.c
 create mode 100644 pc-bios/s390-ccw/libc/string/Makefile.inc
 create mode 100644 pc-bios/s390-ccw/libc/string/memchr.c
 create mode 100644 pc-bios/s390-ccw/libc/string/memcmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/memcpy.c
 create mode 100644 pc-bios/s390-ccw/libc/string/memmove.c
 create mode 100644 pc-bios/s390-ccw/libc/string/memset.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strcasecmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strcat.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strchr.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strcmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strcpy.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strlen.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strncasecmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strncmp.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strncpy.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strstr.c
 create mode 100644 pc-bios/s390-ccw/libc/string/strtok.c

diff --git a/pc-bios/s390-ccw/libc/Makefile b/pc-bios/s390-ccw/libc/Makefile
new file mode 100644
index 0000000..0c762ec
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/README.txt b/pc-bios/s390-ccw/libc/README.txt
new file mode 100644
index 0000000..eaafdf4
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/ctype/Makefile.inc b/pc-bios/s390-ccw/libc/ctype/Makefile.inc
new file mode 100644
index 0000000..25513a9
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/ctype/isdigit.c b/pc-bios/s390-ccw/libc/ctype/isdigit.c
new file mode 100644
index 0000000..62d08a1
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/ctype/isprint.c b/pc-bios/s390-ccw/libc/ctype/isprint.c
new file mode 100644
index 0000000..c74880f
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/ctype/isspace.c b/pc-bios/s390-ccw/libc/ctype/isspace.c
new file mode 100644
index 0000000..5123019
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/ctype/isxdigit.c b/pc-bios/s390-ccw/libc/ctype/isxdigit.c
new file mode 100644
index 0000000..9d323f3
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/ctype/tolower.c b/pc-bios/s390-ccw/libc/ctype/tolower.c
new file mode 100644
index 0000000..f775e90
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/ctype/toupper.c b/pc-bios/s390-ccw/libc/ctype/toupper.c
new file mode 100644
index 0000000..9bcee52
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/ctype.h b/pc-bios/s390-ccw/libc/include/ctype.h
new file mode 100644
index 0000000..9051a75
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/errno.h b/pc-bios/s390-ccw/libc/include/errno.h
new file mode 100644
index 0000000..d585934
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/limits.h b/pc-bios/s390-ccw/libc/include/limits.h
new file mode 100644
index 0000000..4726835
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/stdarg.h b/pc-bios/s390-ccw/libc/include/stdarg.h
new file mode 100644
index 0000000..d3d12f7
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/stdbool.h b/pc-bios/s390-ccw/libc/include/stdbool.h
new file mode 100644
index 0000000..5b7d36a
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/stddef.h b/pc-bios/s390-ccw/libc/include/stddef.h
new file mode 100644
index 0000000..ba2d960
--- /dev/null
+++ b/pc-bios/s390-ccw/libc/include/stddef.h
@@ -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
+ *****************************************************************************/
+
+#ifndef _STDDEF_H
+#define _STDDEF_H
+
+
+#define NULL ((void *)0)
+
+
+typedef unsigned int size_t;
+
+
+#endif
+
+
diff --git a/pc-bios/s390-ccw/libc/include/stdint.h b/pc-bios/s390-ccw/libc/include/stdint.h
new file mode 100644
index 0000000..518a723
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/stdio.h b/pc-bios/s390-ccw/libc/include/stdio.h
new file mode 100644
index 0000000..c54528f
--- /dev/null
+++ b/pc-bios/s390-ccw/libc/include/stdio.h
@@ -0,0 +1,63 @@
+/******************************************************************************
+ * 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 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/pc-bios/s390-ccw/libc/include/stdlib.h b/pc-bios/s390-ccw/libc/include/stdlib.h
new file mode 100644
index 0000000..5e0eda9
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/string.h b/pc-bios/s390-ccw/libc/include/string.h
new file mode 100644
index 0000000..0163c9a
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/sys/socket.h b/pc-bios/s390-ccw/libc/include/sys/socket.h
new file mode 100644
index 0000000..e9175be
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/include/unistd.h b/pc-bios/s390-ccw/libc/include/unistd.h
new file mode 100644
index 0000000..07210d6
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/Makefile.inc b/pc-bios/s390-ccw/libc/stdio/Makefile.inc
new file mode 100644
index 0000000..ac5302d
--- /dev/null
+++ b/pc-bios/s390-ccw/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
+
+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/pc-bios/s390-ccw/libc/stdio/fileno.c b/pc-bios/s390-ccw/libc/stdio/fileno.c
new file mode 100644
index 0000000..6e23951
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/fprintf.c b/pc-bios/s390-ccw/libc/stdio/fprintf.c
new file mode 100644
index 0000000..866df39
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/printf.c b/pc-bios/s390-ccw/libc/stdio/printf.c
new file mode 100644
index 0000000..01f4592
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/putc.c b/pc-bios/s390-ccw/libc/stdio/putc.c
new file mode 100644
index 0000000..230e9d1
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/putchar.c b/pc-bios/s390-ccw/libc/stdio/putchar.c
new file mode 100644
index 0000000..5c750d9
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/puts.c b/pc-bios/s390-ccw/libc/stdio/puts.c
new file mode 100644
index 0000000..9a93008
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/setvbuf.c b/pc-bios/s390-ccw/libc/stdio/setvbuf.c
new file mode 100644
index 0000000..9b62dd8
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/sprintf.c b/pc-bios/s390-ccw/libc/stdio/sprintf.c
new file mode 100644
index 0000000..9c4540e
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/stdchnls.c b/pc-bios/s390-ccw/libc/stdio/stdchnls.c
new file mode 100644
index 0000000..41ed958
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/vfprintf.c b/pc-bios/s390-ccw/libc/stdio/vfprintf.c
new file mode 100644
index 0000000..765feea
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdio/vsnprintf.c b/pc-bios/s390-ccw/libc/stdio/vsnprintf.c
new file mode 100644
index 0000000..21dd04d
--- /dev/null
+++ b/pc-bios/s390-ccw/libc/stdio/vsnprintf.c
@@ -0,0 +1,298 @@
+/******************************************************************************
+ * 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)
+{
+	int i, sizei, len;
+	char *bstart = *buffer;
+
+	sizei = strtoul(sizec, NULL, 10);
+	len = strlen(str);
+	if (sizei > len) {
+		for (i = 0;
+			(i < (sizei - len)) && ((*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)) && ((*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)) && ((*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') && ((*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;
+			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' && (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/pc-bios/s390-ccw/libc/stdio/vsprintf.c b/pc-bios/s390-ccw/libc/stdio/vsprintf.c
new file mode 100644
index 0000000..0dfd737
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/Makefile.inc b/pc-bios/s390-ccw/libc/stdlib/Makefile.inc
new file mode 100644
index 0000000..702f6d7
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/atoi.c b/pc-bios/s390-ccw/libc/stdlib/atoi.c
new file mode 100644
index 0000000..d2fb33b
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/atol.c b/pc-bios/s390-ccw/libc/stdlib/atol.c
new file mode 100644
index 0000000..a6aa47b
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/error.c b/pc-bios/s390-ccw/libc/stdlib/error.c
new file mode 100644
index 0000000..81020ca
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/free.c b/pc-bios/s390-ccw/libc/stdlib/free.c
new file mode 100644
index 0000000..9005450
--- /dev/null
+++ b/pc-bios/s390-ccw/libc/stdlib/free.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"
+#include "malloc_defs.h"
+
+void
+free(void *ptr)
+{
+	struct chunk *header;
+
+	header = (struct chunk *) ptr;
+	header--;
+	header->inuse = 0;
+
+}
diff --git a/pc-bios/s390-ccw/libc/stdlib/malloc.c b/pc-bios/s390-ccw/libc/stdlib/malloc.c
new file mode 100644
index 0000000..b2a3138
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/malloc_defs.h b/pc-bios/s390-ccw/libc/stdlib/malloc_defs.h
new file mode 100644
index 0000000..1933026
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/memalign.c b/pc-bios/s390-ccw/libc/stdlib/memalign.c
new file mode 100644
index 0000000..3b678aa
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/rand.c b/pc-bios/s390-ccw/libc/stdlib/rand.c
new file mode 100644
index 0000000..39f5a9a
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/realloc.c b/pc-bios/s390-ccw/libc/stdlib/realloc.c
new file mode 100644
index 0000000..652e900
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/strtol.c b/pc-bios/s390-ccw/libc/stdlib/strtol.c
new file mode 100644
index 0000000..474597a
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/stdlib/strtoul.c b/pc-bios/s390-ccw/libc/stdlib/strtoul.c
new file mode 100644
index 0000000..754e7db
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/Makefile.inc b/pc-bios/s390-ccw/libc/string/Makefile.inc
new file mode 100644
index 0000000..7ccf3c4
--- /dev/null
+++ b/pc-bios/s390-ccw/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
+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/pc-bios/s390-ccw/libc/string/memchr.c b/pc-bios/s390-ccw/libc/string/memchr.c
new file mode 100644
index 0000000..c3fe751
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/memcmp.c b/pc-bios/s390-ccw/libc/string/memcmp.c
new file mode 100644
index 0000000..3b69cef
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/memcpy.c b/pc-bios/s390-ccw/libc/string/memcpy.c
new file mode 100644
index 0000000..00f419b
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/memmove.c b/pc-bios/s390-ccw/libc/string/memmove.c
new file mode 100644
index 0000000..3acf1a9
--- /dev/null
+++ b/pc-bios/s390-ccw/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;
+	int 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/pc-bios/s390-ccw/libc/string/memset.c b/pc-bios/s390-ccw/libc/string/memset.c
new file mode 100644
index 0000000..f8dfbf5
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strcasecmp.c b/pc-bios/s390-ccw/libc/string/strcasecmp.c
new file mode 100644
index 0000000..f75294f
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strcat.c b/pc-bios/s390-ccw/libc/string/strcat.c
new file mode 100644
index 0000000..eb597a0
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strchr.c b/pc-bios/s390-ccw/libc/string/strchr.c
new file mode 100644
index 0000000..528a319
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strcmp.c b/pc-bios/s390-ccw/libc/string/strcmp.c
new file mode 100644
index 0000000..48eaed2
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strcpy.c b/pc-bios/s390-ccw/libc/string/strcpy.c
new file mode 100644
index 0000000..48eb62c
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strlen.c b/pc-bios/s390-ccw/libc/string/strlen.c
new file mode 100644
index 0000000..37a1b78
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strncasecmp.c b/pc-bios/s390-ccw/libc/string/strncasecmp.c
new file mode 100644
index 0000000..4140931
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strncmp.c b/pc-bios/s390-ccw/libc/string/strncmp.c
new file mode 100644
index 0000000..a886736
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strncpy.c b/pc-bios/s390-ccw/libc/string/strncpy.c
new file mode 100644
index 0000000..0f41f93
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strstr.c b/pc-bios/s390-ccw/libc/string/strstr.c
new file mode 100644
index 0000000..3e090d2
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libc/string/strtok.c b/pc-bios/s390-ccw/libc/string/strtok.c
new file mode 100644
index 0000000..665c08d
--- /dev/null
+++ b/pc-bios/s390-ccw/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;
+}
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 02/14] pc-bios/s390-ccw: Start using the libc from SLOF
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 03/14] pc-bios/s390-ccw: Add a write() function for stdio Thomas Huth
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

Change the Makefiles to make the libc compilable within the
s390-ccw firmware build system, link it and start using it by
switching the implementations of the memset() and memcpy()
functions to the ones from the libc.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 configure                                 |  5 ++--
 pc-bios/s390-ccw/Makefile                 |  8 ++++++-
 pc-bios/s390-ccw/bootmap.h                |  1 +
 pc-bios/s390-ccw/libc/Makefile            | 38 ++++++++++---------------------
 pc-bios/s390-ccw/libc/ctype/Makefile.inc  |  3 ++-
 pc-bios/s390-ccw/libc/stdio/Makefile.inc  |  9 ++++----
 pc-bios/s390-ccw/libc/stdlib/Makefile.inc |  3 ++-
 pc-bios/s390-ccw/libc/string/Makefile.inc |  3 ++-
 pc-bios/s390-ccw/s390-ccw.h               | 30 +++---------------------
 9 files changed, 37 insertions(+), 63 deletions(-)

diff --git a/configure b/configure
index c571ad1..954c286 100755
--- a/configure
+++ b/configure
@@ -6377,7 +6377,8 @@ fi
 # build tree in object directory in case the source is not in the current directory
 DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
 DIRS="$DIRS docs docs/interop fsdev"
-DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
+DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas"
+DIRS="$DIRS pc-bios/s390-ccw pc-bios/s390-ccw/libc"
 DIRS="$DIRS roms/seabios roms/vgabios"
 DIRS="$DIRS qapi-generated"
 FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
@@ -6385,7 +6386,7 @@ FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
 FILES="$FILES tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile"
 FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
 FILES="$FILES pc-bios/spapr-rtas/Makefile"
-FILES="$FILES pc-bios/s390-ccw/Makefile"
+FILES="$FILES pc-bios/s390-ccw/Makefile pc-bios/s390-ccw/libc/Makefile"
 FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
 FILES="$FILES pc-bios/qemu-icon.bmp"
 FILES="$FILES .gdbinit scripts" # scripts needed by relative path in .gdbinit
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index fb88c13..3371c5b 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -7,12 +7,14 @@ include $(SRC_PATH)/rules.mak
 
 $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
 
-.PHONY : all clean build-all
+.PHONY : all clean build-all libc.a
 
 OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o
+OBJECTS += libc.a
 QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
 QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
 QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
+QEMU_CFLAGS += -I$(SRC_PATH)/pc-bios/s390-ccw/libc/include
 QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector)
 LDFLAGS += -Wl,-pie -nostdlib
 
@@ -21,6 +23,9 @@ build-all: s390-ccw.img
 s390-ccw.elf: $(OBJECTS)
 	$(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),"BUILD","$(TARGET_DIR)$@")
 
+libc.a:
+	@$(MAKE) -C libc V="$(V)"
+
 STRIP ?= strip
 
 s390-ccw.img: s390-ccw.elf
@@ -30,3 +35,4 @@ $(OBJECTS): Makefile
 
 clean:
 	rm -f *.o *.d *.img *.elf *~
+	@$(MAKE) -C libc clean
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 7f36782..608bb5d 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -11,6 +11,7 @@
 #ifndef _PC_BIOS_S390_CCW_BOOTMAP_H
 #define _PC_BIOS_S390_CCW_BOOTMAP_H
 
+#include <stddef.h>
 #include "s390-ccw.h"
 #include "virtio.h"
 
diff --git a/pc-bios/s390-ccw/libc/Makefile b/pc-bios/s390-ccw/libc/Makefile
index 0c762ec..12f57e8 100644
--- a/pc-bios/s390-ccw/libc/Makefile
+++ b/pc-bios/s390-ccw/libc/Makefile
@@ -10,52 +10,38 @@
 # *     IBM Corporation - initial implementation
 # ****************************************************************************/
 
-TOPCMNDIR ?= ../..
+include ../../../config-host.mak
+include $(SRC_PATH)/rules.mak
 
-LIBCCMNDIR = $(shell pwd)
+LIBCCMNDIR = $(SRC_PATH)/pc-bios/s390-ccw/libc
 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
+QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
+QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
+QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
+QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector)
+QEMU_CFLAGS += -I$(LIBCCMNDIR)/include
+LDFLAGS += -Wl,-pie -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
+OBJS = $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS)
 
 
 $(TARGET): $(OBJS)
-	$(AR) -rc $@ $(OBJS)
-	$(RANLIB) $@
-
+	$(call quiet-command,$(AR) -rc $@ $(OBJS),"AR","$(TARGET_DIR)$@")
 
 clean:
-	$(RM) $(TARGET) $(OBJS)
+	rm -f $(TARGET) $(OBJS)
 
 distclean: clean
diff --git a/pc-bios/s390-ccw/libc/ctype/Makefile.inc b/pc-bios/s390-ccw/libc/ctype/Makefile.inc
index 25513a9..6d4bec3 100644
--- a/pc-bios/s390-ccw/libc/ctype/Makefile.inc
+++ b/pc-bios/s390-ccw/libc/ctype/Makefile.inc
@@ -17,4 +17,5 @@ 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 $@
+	$(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $($@-cflags) \
+		-c -o $@ $<,"CC","$(TARGET_DIR)$@")
diff --git a/pc-bios/s390-ccw/libc/stdio/Makefile.inc b/pc-bios/s390-ccw/libc/stdio/Makefile.inc
index ac5302d..1281b57 100644
--- a/pc-bios/s390-ccw/libc/stdio/Makefile.inc
+++ b/pc-bios/s390-ccw/libc/stdio/Makefile.inc
@@ -11,13 +11,14 @@
 # ****************************************************************************/
 
 
-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
+STDIO_SRC_C = sprintf.c vfprintf.c vsnprintf.c vsprintf.c fprintf.c \
+	      printf.c setvbuf.c putc.c puts.c putchar.c stdchnls.c \
+	      fileno.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 $@
+	$(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $($@-cflags) \
+		-c -o $@ $<,"CC","$(TARGET_DIR)$@")
diff --git a/pc-bios/s390-ccw/libc/stdlib/Makefile.inc b/pc-bios/s390-ccw/libc/stdlib/Makefile.inc
index 702f6d7..4b3cd67 100644
--- a/pc-bios/s390-ccw/libc/stdlib/Makefile.inc
+++ b/pc-bios/s390-ccw/libc/stdlib/Makefile.inc
@@ -19,4 +19,5 @@ STDLIB_SRCS = $(STDLIB_SRC_C:%=$(STDLIBCMNDIR)/%) $(STDLIB_SRC_ASM:%=$(STDLIBCMN
 STDLIB_OBJS = $(STDLIB_SRC_C:%.c=%.o) $(STDLIB_SRC_ASM:%.S=%.o)
 
 %.o : $(STDLIBCMNDIR)/%.c
-	$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+	$(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $($@-cflags) \
+		-c -o $@ $<,"CC","$(TARGET_DIR)$@")
diff --git a/pc-bios/s390-ccw/libc/string/Makefile.inc b/pc-bios/s390-ccw/libc/string/Makefile.inc
index 7ccf3c4..82cc734 100644
--- a/pc-bios/s390-ccw/libc/string/Makefile.inc
+++ b/pc-bios/s390-ccw/libc/string/Makefile.inc
@@ -19,4 +19,5 @@ STRING_SRCS = $(STRING_SRC_C:%=$(STRINGCMNDIR)/%) $(STRING_SRC_ASM:%=$(STRINGCMN
 STRING_OBJS = $(STRING_SRC_C:%.c=%.o) $(STRING_SRC_ASM:%.S=%.o)
 
 %.o : $(STRINGCMNDIR)/%.c
-	$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+	$(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $($@-cflags) \
+		-c -o $@ $<,"CC","$(TARGET_DIR)$@")
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 2089274..410d9ac 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -11,6 +11,8 @@
 #ifndef S390_CCW_H
 #define S390_CCW_H
 
+#include <string.h>
+
 /* #define DEBUG */
 
 typedef unsigned char      u8;
@@ -18,7 +20,6 @@ typedef unsigned short     u16;
 typedef unsigned int       u32;
 typedef unsigned long long u64;
 typedef unsigned long      ulong;
-typedef long               size_t;
 typedef int                bool;
 typedef unsigned char      uint8_t;
 typedef unsigned short     uint16_t;
@@ -39,9 +40,7 @@ typedef unsigned long long __u64;
 #ifndef EBUSY
 #define EBUSY   2
 #endif
-#ifndef NULL
-#define NULL    0
-#endif
+
 #ifndef MIN
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
 #endif
@@ -88,18 +87,6 @@ ulong get_second(void);
 /* bootmap.c */
 void zipl_load(void);
 
-static inline void *memset(void *s, int c, size_t n)
-{
-    int i;
-    unsigned char *p = s;
-
-    for (i = 0; i < n; i++) {
-        p[i] = c;
-    }
-
-    return s;
-}
-
 static inline void fill_hex(char *out, unsigned char val)
 {
     const char hex[] = "0123456789abcdef";
@@ -169,17 +156,6 @@ static inline void sleep(unsigned int seconds)
     }
 }
 
-static inline void *memcpy(void *s1, const void *s2, size_t n)
-{
-    uint8_t *p1 = s1;
-    const uint8_t *p2 = s2;
-
-    while (n--) {
-        p1[n] = p2[n];
-    }
-    return s1;
-}
-
 static inline void IPL_assert(bool term, const char *message)
 {
     if (!term) {
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 03/14] pc-bios/s390-ccw: Add a write() function for stdio
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 02/14] pc-bios/s390-ccw: Start using the libc from SLOF Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 04/14] pc-bios/s390-ccw: Add implementation of sbrk() Thomas Huth
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

The stdio functions from the SLOF libc need a write() function for
printing text to stdout/stderr. Let's implement this function by
refactoring the code from sclp_print().

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/sclp.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)

diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c
index a1639ba..a23bdd3 100644
--- a/pc-bios/s390-ccw/sclp.c
+++ b/pc-bios/s390-ccw/sclp.c
@@ -8,6 +8,7 @@
  * directory.
  */
 
+#include <unistd.h>
 #include "s390-ccw.h"
 #include "sclp.h"
 
@@ -51,34 +52,29 @@ void sclp_setup(void)
     sclp_set_write_mask();
 }
 
-static int _strlen(const char *str)
+ssize_t write(int fd, const void *buf, size_t len)
 {
-    int i;
-    for (i = 0; *str; i++)
-        str++;
-    return i;
-}
-
-static void _memcpy(char *dest, const char *src, int len)
-{
-    int i;
-    for (i = 0; i < len; i++)
-        dest[i] = src[i];
-}
-
-void sclp_print(const char *str)
-{
-    int len = _strlen(str);
     WriteEventData *sccb = (void *)_sccb;
 
+    if (fd != 1 && fd != 2) {
+        return -EIO;
+    }
+
     sccb->h.length = sizeof(WriteEventData) + len;
     sccb->h.function_code = SCLP_FC_NORMAL_WRITE;
     sccb->ebh.length = sizeof(EventBufferHeader) + len;
     sccb->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
     sccb->ebh.flags = 0;
-    _memcpy(sccb->data, str, len);
+    memcpy(sccb->data, buf, len);
 
     sclp_service_call(SCLP_CMD_WRITE_EVENT_DATA, sccb);
+
+    return len;
+}
+
+void sclp_print(const char *str)
+{
+    write(1, str, strlen(str));
 }
 
 void sclp_get_loadparm_ascii(char *loadparm)
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 04/14] pc-bios/s390-ccw: Add implementation of sbrk()
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (2 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 03/14] pc-bios/s390-ccw: Add a write() function for stdio Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 05/14] pc-bios/s390-ccw: Add the TFTP network loading stack from SLOF Thomas Huth
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

To be able to use malloc() and friends from the SLOF libc, we need to
provide an implementation of the sbrk() function. This patch adds such
an implemenation, which has been taken from the SLOF firmware, too.
Since the sbrk() function uses a big array as the heap, we now also
have got to lower the fwbase in hw/s390x/ipl.c accordingly.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 hw/s390x/ipl.c            |  2 +-
 pc-bios/s390-ccw/Makefile |  2 +-
 pc-bios/s390-ccw/sbrk.c   | 39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 41 insertions(+), 2 deletions(-)
 create mode 100644 pc-bios/s390-ccw/sbrk.c

diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 4e6469d..913eee5 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -113,7 +113,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp)
      * even if an external kernel has been defined.
      */
     if (!ipl->kernel || ipl->enforce_bios) {
-        uint64_t fwbase = (MIN(ram_size, 0x80000000U) - 0x200000) & ~0xffffUL;
+        uint64_t fwbase = (MIN(ram_size, 0x80000000U) - 0x400000) & ~0xffffUL;
 
         if (bios_name == NULL) {
             bios_name = ipl->firmware;
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 3371c5b..8fbefe8 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
 .PHONY : all clean build-all libc.a
 
 OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o
-OBJECTS += libc.a
+OBJECTS += libc.a sbrk.o
 QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
 QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
 QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
diff --git a/pc-bios/s390-ccw/sbrk.c b/pc-bios/s390-ccw/sbrk.c
new file mode 100644
index 0000000..2ec1b5f
--- /dev/null
+++ b/pc-bios/s390-ccw/sbrk.c
@@ -0,0 +1,39 @@
+/******************************************************************************
+ * 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>
+
+#define HEAP_SIZE 0x200000
+
+
+static char heap[HEAP_SIZE];
+static char *actptr;
+
+void *sbrk(int increment)
+{
+	char *oldptr;
+
+	/* Called for the first time? Then init the actual pointer */
+	if (!actptr) {
+		actptr = heap;
+	}
+
+	if (actptr + increment > heap + HEAP_SIZE) {
+		/* Out of memory */
+		return (void *)-1;
+	}
+
+	oldptr = actptr;
+	actptr += increment;
+
+	return oldptr;
+}
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 05/14] pc-bios/s390-ccw: Add the TFTP network loading stack from SLOF
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (3 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 04/14] pc-bios/s390-ccw: Add implementation of sbrk() Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 06/14] libnet: Remove remainders of netsave code Thomas Huth
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

Add the files for TFTP network loading from SLOF to the s390-ccw
firmware. The files have been copied unmodified from SLOF commit
ID 62674aabe20612a9786fa03e87cf6916ba97a99a an will be adjusted
for the s390-ccw firmware by the following commits.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/libnet/Makefile   |  50 ++
 pc-bios/s390-ccw/libnet/args.c     | 179 +++++++
 pc-bios/s390-ccw/libnet/args.h     |  23 +
 pc-bios/s390-ccw/libnet/dhcp.c     | 955 +++++++++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/dhcp.h     |  49 ++
 pc-bios/s390-ccw/libnet/dhcpv6.c   | 212 ++++++++
 pc-bios/s390-ccw/libnet/dhcpv6.h   | 154 ++++++
 pc-bios/s390-ccw/libnet/dns.c      | 526 ++++++++++++++++++++
 pc-bios/s390-ccw/libnet/dns.h      |  28 ++
 pc-bios/s390-ccw/libnet/ethernet.c | 189 ++++++++
 pc-bios/s390-ccw/libnet/ethernet.h |  47 ++
 pc-bios/s390-ccw/libnet/icmpv6.c   | 409 ++++++++++++++++
 pc-bios/s390-ccw/libnet/icmpv6.h   | 135 ++++++
 pc-bios/s390-ccw/libnet/ipv4.c     | 898 ++++++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/ipv4.h     |  97 ++++
 pc-bios/s390-ccw/libnet/ipv6.c     | 774 ++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/ipv6.h     | 188 ++++++++
 pc-bios/s390-ccw/libnet/ndp.c      | 184 +++++++
 pc-bios/s390-ccw/libnet/ndp.h      |  72 +++
 pc-bios/s390-ccw/libnet/netapps.h  |  28 ++
 pc-bios/s390-ccw/libnet/netload.c  | 868 +++++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/tcp.c      |  46 ++
 pc-bios/s390-ccw/libnet/tcp.h      |  27 ++
 pc-bios/s390-ccw/libnet/tftp.c     | 594 +++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/tftp.h     |  52 ++
 pc-bios/s390-ccw/libnet/time.h     |   6 +
 pc-bios/s390-ccw/libnet/udp.c      | 115 +++++
 pc-bios/s390-ccw/libnet/udp.h      |  53 ++
 28 files changed, 6958 insertions(+)
 create mode 100644 pc-bios/s390-ccw/libnet/Makefile
 create mode 100644 pc-bios/s390-ccw/libnet/args.c
 create mode 100644 pc-bios/s390-ccw/libnet/args.h
 create mode 100644 pc-bios/s390-ccw/libnet/dhcp.c
 create mode 100644 pc-bios/s390-ccw/libnet/dhcp.h
 create mode 100644 pc-bios/s390-ccw/libnet/dhcpv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/dhcpv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/dns.c
 create mode 100644 pc-bios/s390-ccw/libnet/dns.h
 create mode 100644 pc-bios/s390-ccw/libnet/ethernet.c
 create mode 100644 pc-bios/s390-ccw/libnet/ethernet.h
 create mode 100644 pc-bios/s390-ccw/libnet/icmpv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/icmpv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/ipv4.c
 create mode 100644 pc-bios/s390-ccw/libnet/ipv4.h
 create mode 100644 pc-bios/s390-ccw/libnet/ipv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/ipv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/ndp.c
 create mode 100644 pc-bios/s390-ccw/libnet/ndp.h
 create mode 100644 pc-bios/s390-ccw/libnet/netapps.h
 create mode 100644 pc-bios/s390-ccw/libnet/netload.c
 create mode 100644 pc-bios/s390-ccw/libnet/tcp.c
 create mode 100644 pc-bios/s390-ccw/libnet/tcp.h
 create mode 100644 pc-bios/s390-ccw/libnet/tftp.c
 create mode 100644 pc-bios/s390-ccw/libnet/tftp.h
 create mode 100644 pc-bios/s390-ccw/libnet/time.h
 create mode 100644 pc-bios/s390-ccw/libnet/udp.c
 create mode 100644 pc-bios/s390-ccw/libnet/udp.h

diff --git a/pc-bios/s390-ccw/libnet/Makefile b/pc-bios/s390-ccw/libnet/Makefile
new file mode 100644
index 0000000..83ac1e5
--- /dev/null
+++ b/pc-bios/s390-ccw/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
+
+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
+
+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/pc-bios/s390-ccw/libnet/args.c b/pc-bios/s390-ccw/libnet/args.c
new file mode 100644
index 0000000..3f057c3
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/args.h b/pc-bios/s390-ccw/libnet/args.h
new file mode 100644
index 0000000..1ede9a8
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/dhcp.c b/pc-bios/s390-ccw/libnet/dhcp.c
new file mode 100644
index 0000000..0cb4fa4
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/dhcp.c
@@ -0,0 +1,955 @@
+/******************************************************************************
+ * 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_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     */
+	int8_t     tftp_server[256];  /**< o.66 TFTP server name               */
+	int8_t     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   * 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, "");
+
+	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, (char *) 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((char *) fn_ip -> filename, dhcp_filename);
+
+	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((char *) 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((char *) 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_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_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;
+	strcpy((char *) btph -> file, "");
+	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((char *) dhcp_tftp_name, (char *) btph -> sname,
+			        sizeof(btph -> sname));
+			dhcp_tftp_name[sizeof(btph -> sname)] = 0;
+		}
+
+		if (strlen((char *) btph -> file)) {
+			strncpy((char *) 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((char *) dhcp_tftp_name, (char *) btph -> sname, sizeof(btph -> sname));
+			dhcp_tftp_name[sizeof(btph -> sname)] = 0;
+		}
+
+		if (strlen((char *) btph -> file)) {
+			strncpy((char *) 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((char *) dhcp_tftp_name, (char *) opt.tftp_server);
+				}
+				else {
+					strcpy((char *) dhcp_tftp_name, "");
+					if ((opt.overload != DHCP_OVERLOAD_SNAME &&
+					     opt.overload != DHCP_OVERLOAD_BOTH) &&
+					     !dhcp_siaddr_ip) {
+						strncpy((char *) dhcp_tftp_name,
+						        (char *) btph->sname,
+						        sizeof(btph -> sname));
+						dhcp_tftp_name[sizeof(btph->sname)] = 0;
+					}
+				}
+
+				if (opt.flag[DHCP_BOOTFILE]) {
+					strcpy((char *) dhcp_filename, (char *) opt.bootfile);
+				}
+				else {
+					strcpy((char *) dhcp_filename, "");
+					if (opt.overload != DHCP_OVERLOAD_FILE &&
+						opt.overload != DHCP_OVERLOAD_BOTH &&
+						strlen((char *) btph -> file)) {
+						strncpy((char *) 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/pc-bios/s390-ccw/libnet/dhcp.h b/pc-bios/s390-ccw/libnet/dhcp.h
new file mode 100644
index 0000000..4432c9b
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/dhcpv6.c b/pc-bios/s390-ccw/libnet/dhcpv6.c
new file mode 100644
index 0000000..491d540
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/dhcpv6.c
@@ -0,0 +1,212 @@
+/******************************************************************************
+ * 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,
+					    (char *)my_fn_ip->filename,
+					    (int)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/pc-bios/s390-ccw/libnet/dhcpv6.h b/pc-bios/s390-ccw/libnet/dhcpv6.h
new file mode 100644
index 0000000..02747a0
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/dns.c b/pc-bios/s390-ccw/libnet/dns.c
new file mode 100644
index 0000000..a7313a9
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/dns.h b/pc-bios/s390-ccw/libnet/dns.h
new file mode 100644
index 0000000..b8756af
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/ethernet.c b/pc-bios/s390-ccw/libnet/ethernet.c
new file mode 100644
index 0000000..1e03a0b
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/ethernet.h b/pc-bios/s390-ccw/libnet/ethernet.h
new file mode 100644
index 0000000..e541c8f
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ethernet.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ * 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);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/icmpv6.c b/pc-bios/s390-ccw/libnet/icmpv6.c
new file mode 100644
index 0000000..d44ce84
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/icmpv6.c
@@ -0,0 +1,409 @@
+/******************************************************************************
+ * 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;
+	ip6_addr_t *rtr_ip;
+	uint8_t rtr_mac[] = {0, 0, 0, 0, 0, 0};
+
+	ip6h = (struct ip6hdr *) ip6_packet;
+	ra = (struct router_advertisement *) &icmp6h->icmp6body.ra;
+	rtr_ip = (ip6_addr_t *) &ip6h->src;
+
+	rtr = find_router (&(ip6h->src));
+	if (!rtr) {
+		rtr = router_create (rtr_mac, rtr_ip);
+		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/pc-bios/s390-ccw/libnet/icmpv6.h b/pc-bios/s390-ccw/libnet/icmpv6.h
new file mode 100644
index 0000000..41b0c70
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/ipv4.c b/pc-bios/s390-ccw/libnet/ipv4.c
new file mode 100644
index 0000000..3a1a789
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/ipv4.h b/pc-bios/s390-ccw/libnet/ipv4.h
new file mode 100644
index 0000000..5717c9a
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ipv4.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ * 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);
+
+extern int   (*send_ip) (int fd, void *, int);
+
+/* 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/pc-bios/s390-ccw/libnet/ipv6.c b/pc-bios/s390-ccw/libnet/ipv6.c
new file mode 100644
index 0000000..62a444e
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ipv6.c
@@ -0,0 +1,774 @@
+/******************************************************************************
+ * 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;
+
+	if (ip == NULL)
+	    return 0;
+
+	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 ((int8_t) !memcmp( &(ip_1->addr[0]), &(ip_2->addr[0]),
+		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 short *packet - pointer to header of upper-layer
+ *				    protocol
+ * @param  int words              - number of words (as in 2 bytes)
+ *				    starting from *packet
+ * @return checksum
+ */
+static unsigned short ip6_checksum(struct ip6hdr *ip6h, unsigned short *packet,
+				   int words)
+{
+	int i=0;
+	unsigned long checksum;
+	struct ip6hdr pseudo_ip6h;
+	unsigned short *pip6h;
+
+	memcpy (&pseudo_ip6h, ip6h, sizeof(struct ip6hdr));
+	pseudo_ip6h.hl	      = ip6h->nh;
+	pseudo_ip6h.ver_tc_fl = 0;
+	pseudo_ip6h.nh	      = 0;
+	pip6h = (unsigned short *) &pseudo_ip6h;
+
+	for (checksum = 0; words > 0; words--) {
+		checksum += *packet++;
+		i++;
+	}
+
+	for (i = 0; i < 20; i++) {
+		checksum += *pip6h++;
+	}
+
+	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 short *) udph ,
+					     ip6h->pl >> 1);
+		/* 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 short *) icmp6h,
+						 ip6h->pl >> 1);
+
+	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/pc-bios/s390-ccw/libnet/ipv6.h b/pc-bios/s390-ccw/libnet/ipv6.h
new file mode 100644
index 0000000..6f783b3
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ipv6.h
@@ -0,0 +1,188 @@
+/******************************************************************************
+ * 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 **********************************************/
+/* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */
+extern int   (*send_ip) (int fd, void *, int);
+
+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/pc-bios/s390-ccw/libnet/ndp.c b/pc-bios/s390-ccw/libnet/ndp.c
new file mode 100644
index 0000000..f1f51c7
--- /dev/null
+++ b/pc-bios/s390-ccw/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[0]), 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/pc-bios/s390-ccw/libnet/ndp.h b/pc-bios/s390-ccw/libnet/ndp.h
new file mode 100644
index 0000000..cd18158
--- /dev/null
+++ b/pc-bios/s390-ccw/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 *);
+
+int8_t router_add(struct router*);
+void * router_create(uint8_t *mac, ip6_addr_t *ip);
+struct router * find_router(ip6_addr_t *);
+struct router *ipv6_get_default_router(ip6_addr_t *ip);
+
+#endif //_NDP_H_
diff --git a/pc-bios/s390-ccw/libnet/netapps.h b/pc-bios/s390-ccw/libnet/netapps.h
new file mode 100644
index 0000000..91c1ebd
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/netapps.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 _NETAPPS_H_
+#define _NETAPPS_H_
+
+#define F_IPV4	4
+#define F_IPV6	6
+
+struct filename_ip;
+
+extern int netload(char *buffer, int len, char *ret_buffer, int huge_load,
+		   int block_size, char *args_fs, int alen);
+extern int netsave(int argc, char *argv[]);
+extern int ping(char *args_fs, int alen);
+extern int dhcp(char *ret_buffer, struct filename_ip *fn_ip,
+		unsigned int retries, int flags);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/netload.c b/pc-bios/s390-ccw/libnet/netload.c
new file mode 100644
index 0000000..cd3720a
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/netload.c
@@ -0,0 +1,868 @@
+/******************************************************************************
+ * 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 <dns.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"
+
+#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 DEFAULT_BOOT_RETRIES 10
+#define DEFAULT_TFTP_RETRIES 20
+static int ip_version = 4;
+
+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;
+
+
+/**
+ * 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);
+}
+
+int netload(char *buffer, int len, char *ret_buffer, int huge_load,
+	    int block_size, char *args_fs, int alen)
+{
+	char buf[256];
+	int rc;
+	filename_ip_t fn_ip;
+	int fd_device;
+	tftp_err_t tftp_err;
+	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];
+
+	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(0, 0, 0, (char*) own_mac);
+		if(fd_device != -2)
+			break;
+		if(getchar() == 27) {
+			fd_device = -2;
+			break;
+		}
+	}
+
+	if (fd_device == -1) {
+		strcpy(buf,"E3000: (net) Could not read MAC address");
+		bootmsg_error(0x3000, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -100;
+	}
+	else if (fd_device == -2) {
+		strcpy(buf,"E3006: (net) Could not initialize network device");
+		bootmsg_error(0x3006, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -101;
+	}
+
+	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.");
+			return -7;
+		}
+		/* 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(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries);
+		break;
+	case IP_INIT_DHCP:
+		rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4);
+		break;
+	case IP_INIT_DHCPV6_STATELESS:
+		rc = dhcp(ret_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(ret_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) {
+		strcpy(buf,"E3001: (net) Could not get IP address");
+		bootmsg_error(0x3001, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		close(fn_ip.fd);
+		return -101;
+	}
+
+	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) {
+		sprintf(buf,
+			"E3002: (net) 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));
+		bootmsg_error(0x3002, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		close(fn_ip.fd);
+		return -102;
+	}
+	if (rc == -4 || rc == -3) {
+		strcpy(buf,"E3008: (net) Can't obtain TFTP server IP address");
+		bootmsg_error(0x3008, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		close(fn_ip.fd);
+		return -107;
+	}
+
+
+	/***********************************************************
+	 *
+	 * Load file via TFTP into buffer provided by OpenFirmware
+	 *
+	 ***********************************************************/
+
+	if (obp_tftp_args.filename[0] != 0) {
+		strncpy((char *) fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1);
+		fn_ip.filename[sizeof(fn_ip.filename)-1] = 0;
+	}
+
+	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);
+	}
+
+	// accept at most 20 bad packets
+	// wait at most for 40 packets
+	rc = tftp(&fn_ip, (unsigned char *) buffer,
+	          len, obp_tftp_args.tftp_retries,
+	          &tftp_err, huge_load, block_size, ip_version);
+
+	if(obp_tftp_args.ip_init == IP_INIT_DHCP)
+		dhcp_send_release(fn_ip.fd);
+
+	close(fn_ip.fd);
+
+	if (rc > 0) {
+		printf("  TFTP: Received %s (%d KBytes)\n", fn_ip.filename,
+		       rc / 1024);
+	} else if (rc == -1) {
+		bootmsg_error(0x3003, "(net) unknown TFTP error");
+		return -103;
+	} else if (rc == -2) {
+		sprintf(buf,
+			"E3004: (net) TFTP buffer of %d bytes "
+			"is too small for %s",
+			len, fn_ip.filename);
+		bootmsg_error(0x3004, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -104;
+	} else if (rc == -3) {
+		sprintf(buf,"E3009: (net) file not found: %s",
+		       fn_ip.filename);
+		bootmsg_error(0x3009, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -108;
+	} else if (rc == -4) {
+		strcpy(buf,"E3010: (net) TFTP access violation");
+		bootmsg_error(0x3010, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -109;
+	} else if (rc == -5) {
+		strcpy(buf,"E3011: (net) illegal TFTP operation");
+		bootmsg_error(0x3011, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -110;
+	} else if (rc == -6) {
+		strcpy(buf, "E3012: (net) unknown TFTP transfer ID");
+		bootmsg_error(0x3012, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -111;
+	} else if (rc == -7) {
+		strcpy(buf, "E3013: (net) no such TFTP user");
+		bootmsg_error(0x3013, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -112;
+	} else if (rc == -8) {
+		strcpy(buf, "E3017: (net) TFTP blocksize negotiation failed");
+		bootmsg_error(0x3017, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -116;
+	} else if (rc == -9) {
+		strcpy(buf,"E3018: (net) file exceeds maximum TFTP transfer size");
+		bootmsg_error(0x3018, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -117;
+	} else if (rc <= -10 && rc >= -15) {
+		sprintf(buf,"E3005: (net) ICMP ERROR \"");
+		switch (rc) {
+		case -ICMP_NET_UNREACHABLE - 10:
+			sprintf(buf+strlen(buf),"net unreachable");
+			break;
+		case -ICMP_HOST_UNREACHABLE - 10:
+			sprintf(buf+strlen(buf),"host unreachable");
+			break;
+		case -ICMP_PROTOCOL_UNREACHABLE - 10:
+			sprintf(buf+strlen(buf),"protocol unreachable");
+			break;
+		case -ICMP_PORT_UNREACHABLE - 10:
+			sprintf(buf+strlen(buf),"port unreachable");
+			break;
+		case -ICMP_FRAGMENTATION_NEEDED - 10:
+			sprintf(buf+strlen(buf),"fragmentation needed and DF set");
+			break;
+		case -ICMP_SOURCE_ROUTE_FAILED - 10:
+			sprintf(buf+strlen(buf),"source route failed");
+			break;
+		default:
+			sprintf(buf+strlen(buf)," UNKNOWN");
+			break;
+		}
+		sprintf(buf+strlen(buf),"\"");
+		bootmsg_error(0x3005, &buf[7]);
+
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -105;
+	} else if (rc == -40) {
+		sprintf(buf,
+			"E3014: (net) TFTP error occurred after "
+			"%d bad packets received",
+			tftp_err.bad_tftp_packets);
+		bootmsg_error(0x3014, &buf[7]);
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -113;
+	} else if (rc == -41) {
+		sprintf(buf,
+			"E3015: (net) TFTP error occurred after "
+			"missing %d responses",
+			tftp_err.no_packets);
+		bootmsg_error(0x3015, &buf[7]);
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -114;
+	} else if (rc == -42) {
+		sprintf(buf,
+			"E3016: (net) TFTP error missing block %d, "
+			"expected block was %d",
+			tftp_err.blocks_missed,
+			tftp_err.blocks_received);
+		bootmsg_error(0x3016, &buf[7]);
+		write_mm_log(buf, strlen(buf), 0x91);
+		return -115;
+	}
+	return rc;
+}
+
+/**
+ * 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;
+	}
+
+}
diff --git a/pc-bios/s390-ccw/libnet/tcp.c b/pc-bios/s390-ccw/libnet/tcp.c
new file mode 100644
index 0000000..faa0b83
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/tcp.h b/pc-bios/s390-ccw/libnet/tcp.h
new file mode 100644
index 0000000..375afd7
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/tftp.c b/pc-bios/s390-ccw/libnet/tftp.c
new file mode 100644
index 0000000..d0c2f13
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/tftp.c
@@ -0,0 +1,594 @@
+/******************************************************************************
+ * 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>
+
+//#define __DEBUG__
+
+#define MAX_BLOCKSIZE 1428
+#define BUFFER_LEN 256
+
+#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 = NULL;
+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;
+	unsigned 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((char *) fn_ip->filename) + strlen((char *) 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((char *) fn_ip->filename) + strlen((char *) 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((char *) fn_ip->filename) + strlen((char *) 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((char *) fn_ip->filename) + 1);
+
+	ptr += strlen((char *) fn_ip->filename) + 1;
+	memcpy(ptr, mode, strlen((char *) mode) + 1);
+
+	ptr += strlen((char *) 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(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 == NULL )
+		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)
+ * @param  _mode         NON ZERO - multicast, ZERO - unicast
+ * @param  _blocksize    blocksize for DATA-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,
+	 int32_t _mode, int32_t _blocksize, int _ip_version)
+{
+	retries     = _retries;
+	fn_ip       = _fn_ip;
+	len         = _len;
+	huge_load   = _mode;
+	ip_version  = _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;
+
+	/* 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 */
+	if (_blocksize < 8)
+		_blocksize = 8;
+	else if (_blocksize > MAX_BLOCKSIZE)
+		_blocksize = MAX_BLOCKSIZE;
+	sprintf(blocksize_str, "%d", _blocksize);
+
+	printf("  Receiving data:  ");
+	print_progress(-1, 0);
+
+	// Setting buffer to a non-zero address enabled handling of received TFTP 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 to NULL disables handling of received TFTP packets.
+	buffer = NULL;
+
+	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;
+}
diff --git a/pc-bios/s390-ccw/libnet/tftp.h b/pc-bios/s390-ccw/libnet/tftp.h
new file mode 100644
index 0000000..303feaf
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/tftp.h
@@ -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
+ *****************************************************************************/
+
+
+#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;
+	int8_t filename[256];
+	int    fd;
+} __attribute__ ((packed));
+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 *, unsigned char  *, int, unsigned int,
+         tftp_err_t *, int32_t mode, int32_t blocksize, int ip_version);
+int tftp_netsave(filename_ip_t *, uint8_t * buffer, int len,
+		 int use_ci, unsigned int retries, tftp_err_t * tftp_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);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/time.h b/pc-bios/s390-ccw/libnet/time.h
new file mode 100644
index 0000000..fcd5cd5
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/udp.c b/pc-bios/s390-ccw/libnet/udp.c
new file mode 100644
index 0000000..d6982ea
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/udp.h b/pc-bios/s390-ccw/libnet/udp.h
new file mode 100644
index 0000000..e716e04
--- /dev/null
+++ b/pc-bios/s390-ccw/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
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 06/14] libnet: Remove remainders of netsave code
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (4 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 05/14] pc-bios/s390-ccw: Add the TFTP network loading stack from SLOF Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 07/14] libnet: Rework error message printing Thomas Huth
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

The code does not exist in the repository, so it does not make
sense to keep the prototypes and the Forth wrapper around.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/libnet/netapps.h | 1 -
 pc-bios/s390-ccw/libnet/tftp.h    | 2 --
 2 files changed, 3 deletions(-)

diff --git a/pc-bios/s390-ccw/libnet/netapps.h b/pc-bios/s390-ccw/libnet/netapps.h
index 91c1ebd..2fea4a7 100644
--- a/pc-bios/s390-ccw/libnet/netapps.h
+++ b/pc-bios/s390-ccw/libnet/netapps.h
@@ -20,7 +20,6 @@ struct filename_ip;
 
 extern int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 		   int block_size, char *args_fs, int alen);
-extern int netsave(int argc, char *argv[]);
 extern int ping(char *args_fs, int alen);
 extern int dhcp(char *ret_buffer, struct filename_ip *fn_ip,
 		unsigned int retries, int flags);
diff --git a/pc-bios/s390-ccw/libnet/tftp.h b/pc-bios/s390-ccw/libnet/tftp.h
index 303feaf..b1dbc21 100644
--- a/pc-bios/s390-ccw/libnet/tftp.h
+++ b/pc-bios/s390-ccw/libnet/tftp.h
@@ -42,8 +42,6 @@ typedef struct {
 
 int tftp(filename_ip_t *, unsigned char  *, int, unsigned int,
          tftp_err_t *, int32_t mode, int32_t blocksize, int ip_version);
-int tftp_netsave(filename_ip_t *, uint8_t * buffer, int len,
-		 int use_ci, unsigned int retries, tftp_err_t * tftp_err);
 
 int32_t handle_tftp(int fd, uint8_t *, int32_t);
 void handle_tftp_dun(uint8_t err_code);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 07/14] libnet: Rework error message printing
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (5 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 06/14] libnet: Remove remainders of netsave code Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 08/14] libnet: Refactor some code of netload() into a separate function Thomas Huth
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

There is a repetive pattern of code in netload.c to print out
error message: snprintf(buf, ...) + bootmsg_error() + write_mm_log().
The code can be simplified / shortened quite a bit by consolidating
this pattern in a helper function.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/libnet/netload.c | 126 +++++++++++++-------------------------
 1 file changed, 44 insertions(+), 82 deletions(-)

diff --git a/pc-bios/s390-ccw/libnet/netload.c b/pc-bios/s390-ccw/libnet/netload.c
index cd3720a..8afe341 100644
--- a/pc-bios/s390-ccw/libnet/netload.c
+++ b/pc-bios/s390-ccw/libnet/netload.c
@@ -52,6 +52,23 @@ typedef struct {
 	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];
+
+	sprintf(buf, "E%04X: (net) ", errcode);
+
+	va_start(vargs, format);
+	vsnprintf(&buf[13], sizeof(buf) - 13, format, vargs);
+	va_end(vargs);
+
+	bootmsg_error(errcode, &buf[7]);
+	write_mm_log(buf, strlen(buf), 0x91);
+}
 
 /**
  * Parses a argument string for IPv6 booting, extracts all
@@ -389,7 +406,6 @@ static void seed_rng(uint8_t mac[])
 int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 	    int block_size, char *args_fs, int alen)
 {
-	char buf[256];
 	int rc;
 	filename_ip_t fn_ip;
 	int fd_device;
@@ -427,17 +443,11 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 	}
 
 	if (fd_device == -1) {
-		strcpy(buf,"E3000: (net) Could not read MAC address");
-		bootmsg_error(0x3000, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3000, "Could not read MAC address");
 		return -100;
 	}
 	else if (fd_device == -2) {
-		strcpy(buf,"E3006: (net) Could not initialize network device");
-		bootmsg_error(0x3006, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3006, "Could not initialize network device");
 		return -101;
 	}
 
@@ -567,10 +577,7 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 			memcpy(&fn_ip.server_ip6.addr, &obp_tftp_args.si6addr.addr, 16);
 	}
 	if (rc == -1) {
-		strcpy(buf,"E3001: (net) Could not get IP address");
-		bootmsg_error(0x3001, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3001, "Could not get IP address");
 		close(fn_ip.fd);
 		return -101;
 	}
@@ -586,29 +593,21 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 	}
 
 	if (rc == -2) {
-		sprintf(buf,
-			"E3002: (net) ARP request to TFTP server "
+		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));
-		bootmsg_error(0x3002, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
 		close(fn_ip.fd);
 		return -102;
 	}
 	if (rc == -4 || rc == -3) {
-		strcpy(buf,"E3008: (net) Can't obtain TFTP server IP address");
-		bootmsg_error(0x3008, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3008, "Can't obtain TFTP server IP address");
 		close(fn_ip.fd);
 		return -107;
 	}
 
-
 	/***********************************************************
 	 *
 	 * Load file via TFTP into buffer provided by OpenFirmware
@@ -649,114 +648,77 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 		printf("  TFTP: Received %s (%d KBytes)\n", fn_ip.filename,
 		       rc / 1024);
 	} else if (rc == -1) {
-		bootmsg_error(0x3003, "(net) unknown TFTP error");
+		netload_error(0x3003, "unknown TFTP error");
 		return -103;
 	} else if (rc == -2) {
-		sprintf(buf,
-			"E3004: (net) TFTP buffer of %d bytes "
+		netload_error(0x3004, "TFTP buffer of %d bytes "
 			"is too small for %s",
 			len, fn_ip.filename);
-		bootmsg_error(0x3004, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
 		return -104;
 	} else if (rc == -3) {
-		sprintf(buf,"E3009: (net) file not found: %s",
-		       fn_ip.filename);
-		bootmsg_error(0x3009, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3009, "file not found: %s",
+			fn_ip.filename);
 		return -108;
 	} else if (rc == -4) {
-		strcpy(buf,"E3010: (net) TFTP access violation");
-		bootmsg_error(0x3010, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3010, "TFTP access violation");
 		return -109;
 	} else if (rc == -5) {
-		strcpy(buf,"E3011: (net) illegal TFTP operation");
-		bootmsg_error(0x3011, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3011, "illegal TFTP operation");
 		return -110;
 	} else if (rc == -6) {
-		strcpy(buf, "E3012: (net) unknown TFTP transfer ID");
-		bootmsg_error(0x3012, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3012, "unknown TFTP transfer ID");
 		return -111;
 	} else if (rc == -7) {
-		strcpy(buf, "E3013: (net) no such TFTP user");
-		bootmsg_error(0x3013, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3013, "no such TFTP user");
 		return -112;
 	} else if (rc == -8) {
-		strcpy(buf, "E3017: (net) TFTP blocksize negotiation failed");
-		bootmsg_error(0x3017, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3017, "TFTP blocksize negotiation failed");
 		return -116;
 	} else if (rc == -9) {
-		strcpy(buf,"E3018: (net) file exceeds maximum TFTP transfer size");
-		bootmsg_error(0x3018, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3018, "file exceeds maximum TFTP transfer size");
 		return -117;
 	} else if (rc <= -10 && rc >= -15) {
-		sprintf(buf,"E3005: (net) ICMP ERROR \"");
+		char *icmp_err_str;
 		switch (rc) {
 		case -ICMP_NET_UNREACHABLE - 10:
-			sprintf(buf+strlen(buf),"net unreachable");
+			icmp_err_str = "net unreachable";
 			break;
 		case -ICMP_HOST_UNREACHABLE - 10:
-			sprintf(buf+strlen(buf),"host unreachable");
+			icmp_err_str = "host unreachable";
 			break;
 		case -ICMP_PROTOCOL_UNREACHABLE - 10:
-			sprintf(buf+strlen(buf),"protocol unreachable");
+			icmp_err_str = "protocol unreachable";
 			break;
 		case -ICMP_PORT_UNREACHABLE - 10:
-			sprintf(buf+strlen(buf),"port unreachable");
+			icmp_err_str = "port unreachable";
 			break;
 		case -ICMP_FRAGMENTATION_NEEDED - 10:
-			sprintf(buf+strlen(buf),"fragmentation needed and DF set");
+			icmp_err_str = "fragmentation needed and DF set";
 			break;
 		case -ICMP_SOURCE_ROUTE_FAILED - 10:
-			sprintf(buf+strlen(buf),"source route failed");
+			icmp_err_str = "source route failed";
 			break;
 		default:
-			sprintf(buf+strlen(buf)," UNKNOWN");
+			icmp_err_str = " UNKNOWN";
 			break;
 		}
-		sprintf(buf+strlen(buf),"\"");
-		bootmsg_error(0x3005, &buf[7]);
-
-		write_mm_log(buf, strlen(buf), 0x91);
+		netload_error(0x3005, "ICMP ERROR \"%s\"", icmp_err_str);
 		return -105;
 	} else if (rc == -40) {
-		sprintf(buf,
-			"E3014: (net) TFTP error occurred after "
+		netload_error(0x3014, "TFTP error occurred after "
 			"%d bad packets received",
 			tftp_err.bad_tftp_packets);
-		bootmsg_error(0x3014, &buf[7]);
-		write_mm_log(buf, strlen(buf), 0x91);
 		return -113;
 	} else if (rc == -41) {
-		sprintf(buf,
-			"E3015: (net) TFTP error occurred after "
+		netload_error(0x3015, "TFTP error occurred after "
 			"missing %d responses",
 			tftp_err.no_packets);
-		bootmsg_error(0x3015, &buf[7]);
-		write_mm_log(buf, strlen(buf), 0x91);
 		return -114;
 	} else if (rc == -42) {
-		sprintf(buf,
-			"E3016: (net) TFTP error missing block %d, "
+		netload_error(0x3016, "TFTP error missing block %d, "
 			"expected block was %d",
 			tftp_err.blocks_missed,
 			tftp_err.blocks_received);
-		bootmsg_error(0x3016, &buf[7]);
-		write_mm_log(buf, strlen(buf), 0x91);
 		return -115;
 	}
 	return rc;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 08/14] libnet: Refactor some code of netload() into a separate function
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (6 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 07/14] libnet: Rework error message printing Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 09/14] pc-bios/s390-ccw: Make the basic libnet code compilable Thomas Huth
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

netload() is a huge function, it's easy to lose track here. So
let's refactor the TFTP-related loading and error printing code
into a separate function instead.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/libnet/netload.c | 177 ++++++++++++++++++++------------------
 1 file changed, 93 insertions(+), 84 deletions(-)

diff --git a/pc-bios/s390-ccw/libnet/netload.c b/pc-bios/s390-ccw/libnet/netload.c
index 8afe341..f872884 100644
--- a/pc-bios/s390-ccw/libnet/netload.c
+++ b/pc-bios/s390-ccw/libnet/netload.c
@@ -403,13 +403,101 @@ static void seed_rng(uint8_t mac[])
 	srand(seed);
 }
 
+static int tftp_load(filename_ip_t *fnip, unsigned char *buffer, int len,
+		     unsigned int retries, int ip_vers)
+{
+	tftp_err_t tftp_err;
+	int rc;
+
+	rc = tftp(fnip, buffer, len, retries, &tftp_err, 1, 1428, ip_vers);
+
+	if (rc > 0) {
+		printf("  TFTP: Received %s (%d KBytes)\n", fnip->filename,
+		       rc / 1024);
+	} else if (rc == -1) {
+		netload_error(0x3003, "unknown TFTP error");
+		return -103;
+	} else if (rc == -2) {
+		netload_error(0x3004, "TFTP buffer of %d bytes "
+			"is too small for %s",
+			len, fnip->filename);
+		return -104;
+	} else if (rc == -3) {
+		netload_error(0x3009, "file not found: %s",
+			fnip->filename);
+		return -108;
+	} else if (rc == -4) {
+		netload_error(0x3010, "TFTP access violation");
+		return -109;
+	} else if (rc == -5) {
+		netload_error(0x3011, "illegal TFTP operation");
+		return -110;
+	} else if (rc == -6) {
+		netload_error(0x3012, "unknown TFTP transfer ID");
+		return -111;
+	} else if (rc == -7) {
+		netload_error(0x3013, "no such TFTP user");
+		return -112;
+	} else if (rc == -8) {
+		netload_error(0x3017, "TFTP blocksize negotiation failed");
+		return -116;
+	} else if (rc == -9) {
+		netload_error(0x3018, "file exceeds maximum TFTP transfer size");
+		return -117;
+	} else if (rc <= -10 && rc >= -15) {
+		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;
+		}
+		netload_error(0x3005, "ICMP ERROR \"%s\"", icmp_err_str);
+		return -105;
+	} else if (rc == -40) {
+		netload_error(0x3014, "TFTP error occurred after "
+			"%d bad packets received",
+			tftp_err.bad_tftp_packets);
+		return -113;
+	} else if (rc == -41) {
+		netload_error(0x3015, "TFTP error occurred after "
+			"missing %d responses",
+			tftp_err.no_packets);
+		return -114;
+	} else if (rc == -42) {
+		netload_error(0x3016, "TFTP error missing block %d, "
+			"expected block was %d",
+			tftp_err.blocks_missed,
+			tftp_err.blocks_received);
+		return -115;
+	}
+
+	return rc;
+}
+
 int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 	    int block_size, char *args_fs, int alen)
 {
 	int rc;
 	filename_ip_t fn_ip;
 	int fd_device;
-	tftp_err_t tftp_err;
 	obp_tftp_args_t obp_tftp_args;
 	char null_ip[4] = { 0x00, 0x00, 0x00, 0x00 };
 	char null_ip6[16] = { 0x00, 0x00, 0x00, 0x00,
@@ -633,94 +721,15 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 		printf("%s\n", ip6_str);
 	}
 
-	// accept at most 20 bad packets
-	// wait at most for 40 packets
-	rc = tftp(&fn_ip, (unsigned char *) buffer,
-	          len, obp_tftp_args.tftp_retries,
-	          &tftp_err, huge_load, block_size, ip_version);
+	/* Do the TFTP load and print error message if necessary */
+	rc = tftp_load(&fn_ip, (unsigned char *)buffer, len,
+		       obp_tftp_args.tftp_retries, ip_version);
 
-	if(obp_tftp_args.ip_init == IP_INIT_DHCP)
+	if (obp_tftp_args.ip_init == IP_INIT_DHCP)
 		dhcp_send_release(fn_ip.fd);
 
 	close(fn_ip.fd);
 
-	if (rc > 0) {
-		printf("  TFTP: Received %s (%d KBytes)\n", fn_ip.filename,
-		       rc / 1024);
-	} else if (rc == -1) {
-		netload_error(0x3003, "unknown TFTP error");
-		return -103;
-	} else if (rc == -2) {
-		netload_error(0x3004, "TFTP buffer of %d bytes "
-			"is too small for %s",
-			len, fn_ip.filename);
-		return -104;
-	} else if (rc == -3) {
-		netload_error(0x3009, "file not found: %s",
-			fn_ip.filename);
-		return -108;
-	} else if (rc == -4) {
-		netload_error(0x3010, "TFTP access violation");
-		return -109;
-	} else if (rc == -5) {
-		netload_error(0x3011, "illegal TFTP operation");
-		return -110;
-	} else if (rc == -6) {
-		netload_error(0x3012, "unknown TFTP transfer ID");
-		return -111;
-	} else if (rc == -7) {
-		netload_error(0x3013, "no such TFTP user");
-		return -112;
-	} else if (rc == -8) {
-		netload_error(0x3017, "TFTP blocksize negotiation failed");
-		return -116;
-	} else if (rc == -9) {
-		netload_error(0x3018, "file exceeds maximum TFTP transfer size");
-		return -117;
-	} else if (rc <= -10 && rc >= -15) {
-		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;
-		}
-		netload_error(0x3005, "ICMP ERROR \"%s\"", icmp_err_str);
-		return -105;
-	} else if (rc == -40) {
-		netload_error(0x3014, "TFTP error occurred after "
-			"%d bad packets received",
-			tftp_err.bad_tftp_packets);
-		return -113;
-	} else if (rc == -41) {
-		netload_error(0x3015, "TFTP error occurred after "
-			"missing %d responses",
-			tftp_err.no_packets);
-		return -114;
-	} else if (rc == -42) {
-		netload_error(0x3016, "TFTP error missing block %d, "
-			"expected block was %d",
-			tftp_err.blocks_missed,
-			tftp_err.blocks_received);
-		return -115;
-	}
 	return rc;
 }
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 09/14] pc-bios/s390-ccw: Make the basic libnet code compilable
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (7 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 08/14] libnet: Refactor some code of netload() into a separate function Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 10/14] pc-bios/s390-ccw: Add timer code for the libnet Thomas Huth
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

Adjust the Makefiles, remove non-required code and fix some spots that
generated compiler warnings / errors with the s390-ccw firmware CFLAGS.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 configure                         |   3 +-
 pc-bios/s390-ccw/Makefile         |   6 +-
 pc-bios/s390-ccw/libnet/Makefile  |  38 ++---
 pc-bios/s390-ccw/libnet/netapps.h |   4 +-
 pc-bios/s390-ccw/libnet/netload.c | 319 ++------------------------------------
 pc-bios/s390-ccw/libnet/tftp.c    |   2 +-
 6 files changed, 33 insertions(+), 339 deletions(-)

diff --git a/configure b/configure
index 954c286..0ac761e 100755
--- a/configure
+++ b/configure
@@ -6378,7 +6378,7 @@ fi
 DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
 DIRS="$DIRS docs docs/interop fsdev"
 DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas"
-DIRS="$DIRS pc-bios/s390-ccw pc-bios/s390-ccw/libc"
+DIRS="$DIRS pc-bios/s390-ccw pc-bios/s390-ccw/libc pc-bios/s390-ccw/libnet"
 DIRS="$DIRS roms/seabios roms/vgabios"
 DIRS="$DIRS qapi-generated"
 FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
@@ -6387,6 +6387,7 @@ FILES="$FILES tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile"
 FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
 FILES="$FILES pc-bios/spapr-rtas/Makefile"
 FILES="$FILES pc-bios/s390-ccw/Makefile pc-bios/s390-ccw/libc/Makefile"
+FILES="$FILES pc-bios/s390-ccw/libnet/Makefile"
 FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
 FILES="$FILES pc-bios/qemu-icon.bmp"
 FILES="$FILES .gdbinit scripts" # scripts needed by relative path in .gdbinit
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 8fbefe8..02b9b08 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -7,7 +7,7 @@ include $(SRC_PATH)/rules.mak
 
 $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
 
-.PHONY : all clean build-all libc.a
+.PHONY : all clean build-all libc.a libnet.a
 
 OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o
 OBJECTS += libc.a sbrk.o
@@ -26,6 +26,9 @@ s390-ccw.elf: $(OBJECTS)
 libc.a:
 	@$(MAKE) -C libc V="$(V)"
 
+libnet.a:
+	@$(MAKE) -C libnet V="$(V)"
+
 STRIP ?= strip
 
 s390-ccw.img: s390-ccw.elf
@@ -36,3 +39,4 @@ $(OBJECTS): Makefile
 clean:
 	rm -f *.o *.d *.img *.elf *~
 	@$(MAKE) -C libc clean
+	@$(MAKE) -C libnet clean
diff --git a/pc-bios/s390-ccw/libnet/Makefile b/pc-bios/s390-ccw/libnet/Makefile
index 83ac1e5..72e12d7 100644
--- a/pc-bios/s390-ccw/libnet/Makefile
+++ b/pc-bios/s390-ccw/libnet/Makefile
@@ -10,16 +10,21 @@
 # *     IBM Corporation - initial implementation
 # ****************************************************************************/
 
-ifndef TOP
-  TOP = $(shell while ! test -e make.rules; do cd ..  ; done; pwd)
-  export TOP
-endif
-include $(TOP)/make.rules
+include ../../../config-host.mak
+include $(SRC_PATH)/rules.mak
 
-CFLAGS += -I. -I.. -I../libc/include -I$(TOP)/include
+$(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw/libnet)
 
-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
+QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
+QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
+QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing -Wno-redundant-decls
+QEMU_CFLAGS += -I$(SRC_PATH)/pc-bios/s390-ccw/libnet
+QEMU_CFLAGS += -I$(SRC_PATH)/pc-bios/s390-ccw/libc/include
+QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector)
+LDFLAGS += -Wl,-pie -nostdlib
+
+SRCS =	ethernet.c ipv4.c udp.c tcp.c dns.c dhcp.c tftp.c \
+	ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c args.c
 
 OBJS = $(SRCS:%.c=%.o)
 
@@ -28,23 +33,10 @@ TARGET = ../libnet.a
 all: $(TARGET)
 
 $(TARGET): $(OBJS)
-	$(AR) -rc $@ $(OBJS)
-	$(RANLIB) $@
+	$(call quiet-command,$(AR) -rc $@ $(OBJS),"AR","$(TARGET_DIR)$@")
 
 clean:
-	$(RM) $(TARGET) $(OBJS)
+	rm -f $(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/pc-bios/s390-ccw/libnet/netapps.h b/pc-bios/s390-ccw/libnet/netapps.h
index 2fea4a7..d2283af 100644
--- a/pc-bios/s390-ccw/libnet/netapps.h
+++ b/pc-bios/s390-ccw/libnet/netapps.h
@@ -18,9 +18,7 @@
 
 struct filename_ip;
 
-extern int netload(char *buffer, int len, char *ret_buffer, int huge_load,
-		   int block_size, char *args_fs, int alen);
-extern int ping(char *args_fs, int alen);
+extern int netload(char *buffer, int len, char *ret_buffer);
 extern int dhcp(char *ret_buffer, struct filename_ip *fn_ip,
 		unsigned int retries, int flags);
 
diff --git a/pc-bios/s390-ccw/libnet/netload.c b/pc-bios/s390-ccw/libnet/netload.c
index f872884..eae8333 100644
--- a/pc-bios/s390-ccw/libnet/netload.c
+++ b/pc-bios/s390-ccw/libnet/netload.c
@@ -23,8 +23,6 @@
 #include <time.h>
 #include <stdlib.h>
 #include <sys/socket.h>
-#include <libbootmsg/libbootmsg.h>
-#include <helpers.h>
 #include "args.h"
 #include "netapps.h"
 
@@ -66,266 +64,7 @@ static void netload_error(int errcode, const char *format, ...)
 	vsnprintf(&buf[13], sizeof(buf) - 13, format, vargs);
 	va_end(vargs);
 
-	bootmsg_error(errcode, &buf[7]);
-	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;
-	}
+	puts(buf);
 }
 
 /**
@@ -361,10 +100,6 @@ int dhcp(char *ret_buffer, struct filename_ip *fn_ip, unsigned int retries,
 
 	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;
@@ -396,9 +131,9 @@ int dhcp(char *ret_buffer, struct filename_ip *fn_ip, unsigned int retries,
  */
 static void seed_rng(uint8_t mac[])
 {
-	unsigned int seed;
+	uint64_t seed;
 
-	asm volatile("mftbl %0" : "=r"(seed));
+	asm volatile(" stck %0 " : : "Q"(seed) : "memory");
 	seed ^= (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5];
 	srand(seed);
 }
@@ -445,7 +180,7 @@ static int tftp_load(filename_ip_t *fnip, unsigned char *buffer, int len,
 		netload_error(0x3018, "file exceeds maximum TFTP transfer size");
 		return -117;
 	} else if (rc <= -10 && rc >= -15) {
-		char *icmp_err_str;
+		const char *icmp_err_str;
 		switch (rc) {
 		case -ICMP_NET_UNREACHABLE - 10:
 			icmp_err_str = "net unreachable";
@@ -492,8 +227,7 @@ static int tftp_load(filename_ip_t *fnip, unsigned char *buffer, int len,
 	return rc;
 }
 
-int netload(char *buffer, int len, char *ret_buffer, int huge_load,
-	    int block_size, char *args_fs, int alen)
+int netload(char *buffer, int len, char *ret_buffer)
 {
 	int rc;
 	filename_ip_t fn_ip;
@@ -524,10 +258,6 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 		fd_device = socket(0, 0, 0, (char*) own_mac);
 		if(fd_device != -2)
 			break;
-		if(getchar() == 27) {
-			fd_device = -2;
-			break;
-		}
 	}
 
 	if (fd_device == -1) {
@@ -551,27 +281,10 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 
 	seed_rng(own_mac);
 
-	if (alen > 0) {
-		char args[256];
-		if (alen > sizeof(args) - 1) {
-			puts("ERROR: Parameter string is too long.");
-			return -7;
-		}
-		/* 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;
-	}
+	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
@@ -602,20 +315,6 @@ int netload(char *buffer, int len, char *ret_buffer, int huge_load,
 
 	// 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(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries);
-		break;
 	case IP_INIT_DHCP:
 		rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4);
 		break;
diff --git a/pc-bios/s390-ccw/libnet/tftp.c b/pc-bios/s390-ccw/libnet/tftp.c
index d0c2f13..34f448c 100644
--- a/pc-bios/s390-ccw/libnet/tftp.c
+++ b/pc-bios/s390-ccw/libnet/tftp.c
@@ -265,7 +265,7 @@ static void print_progress(int urgent, int received_bytes)
 			sprintf(buffer, "%d KBytes", (progress_last_bytes >> 10));
 			for(ptr = buffer; *ptr != 0; ++ptr)
 				*ptr = '\b';
-			printf(buffer);
+			printf("%s", buffer);
 		}
 		printf("%d KBytes", (received_bytes >> 10));
 		i = 1;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 10/14] pc-bios/s390-ccw: Add timer code for the libnet
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (8 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 09/14] pc-bios/s390-ccw: Make the basic libnet code compilable Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 11/14] pc-bios/s390-ccw: Add virtio-net driver code Thomas Huth
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

The libnet expects certain timer functions to exist, so that it
is able to deal with timeouts etc.
This patch implements these timer functions via the STORE CLOCK (stck)
CPU instruction.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/libnet/Makefile |  2 +-
 pc-bios/s390-ccw/libnet/timer.c  | 40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)
 create mode 100644 pc-bios/s390-ccw/libnet/timer.c

diff --git a/pc-bios/s390-ccw/libnet/Makefile b/pc-bios/s390-ccw/libnet/Makefile
index 72e12d7..c8235f3 100644
--- a/pc-bios/s390-ccw/libnet/Makefile
+++ b/pc-bios/s390-ccw/libnet/Makefile
@@ -24,7 +24,7 @@ QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector)
 LDFLAGS += -Wl,-pie -nostdlib
 
 SRCS =	ethernet.c ipv4.c udp.c tcp.c dns.c dhcp.c tftp.c \
-	ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c args.c
+	ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c args.c timer.c
 
 OBJS = $(SRCS:%.c=%.o)
 
diff --git a/pc-bios/s390-ccw/libnet/timer.c b/pc-bios/s390-ccw/libnet/timer.c
new file mode 100644
index 0000000..ddbd7a2
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/timer.c
@@ -0,0 +1,40 @@
+/*
+ * Timer functions for libnet
+ *
+ * Copyright 2017 Thomas Huth, Red Hat Inc.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <stdint.h>
+#include "time.h"
+
+static uint64_t dest_timer;
+
+static uint64_t get_timer_ms(void)
+{
+	uint64_t clk;
+
+	asm volatile(" stck %0 " : : "Q"(clk) : "memory");
+
+	/* Bit 51 is incrememented each microsecond */
+	return (clk >> (63 - 51)) / 1000;
+}
+
+void set_timer(int val)
+{
+	dest_timer = get_timer_ms() + val;
+}
+
+int get_timer(void)
+{
+	return dest_timer - get_timer_ms();
+}
+
+int get_sec_ticks(void)
+{
+	return 1000;    /* number of ticks in 1 second */
+}
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 11/14] pc-bios/s390-ccw: Add virtio-net driver code
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (9 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 10/14] pc-bios/s390-ccw: Add timer code for the libnet Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 12/14] pc-bios/s390-ccw: Load file via an intermediate .INS file Thomas Huth
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

This virtio-net driver contains the recv() and send() functions
that are required by libnet to receive and send packets.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/Makefile     |   2 +-
 pc-bios/s390-ccw/virtio-net.c | 125 ++++++++++++++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/virtio.c     |  16 ++++--
 pc-bios/s390-ccw/virtio.h     |  11 ++++
 4 files changed, 149 insertions(+), 5 deletions(-)
 create mode 100644 pc-bios/s390-ccw/virtio-net.c

diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 02b9b08..369bd65 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
 
 .PHONY : all clean build-all libc.a libnet.a
 
-OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o
+OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-net.o
 OBJECTS += libc.a sbrk.o
 QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
 QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
new file mode 100644
index 0000000..5c2f439
--- /dev/null
+++ b/pc-bios/s390-ccw/virtio-net.c
@@ -0,0 +1,125 @@
+/*
+ * Virtio-net driver for the s390-ccw firmware
+ *
+ * Copyright 2017 Thomas Huth, Red Hat Inc.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include "libnet/ethernet.h"
+#include "virtio.h"
+
+#define VQ_RX 0         /* Receive queue */
+#define VQ_TX 1         /* Transmit queue */
+
+struct VirtioNetHdr {
+    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 with VIRTIO_NET_F_MRG_RXBUF or VIRTIO1 */
+};
+typedef struct VirtioNetHdr VirtioNetHdr;
+
+static uint16_t rx_last_idx;  /* Last index in receive queue "used" ring */
+
+int socket(int domain, int type, int proto, char *mac_addr)
+{
+    VDev *vdev = virtio_get_device();
+    VRing *rxvq = &vdev->vrings[VQ_RX];
+    void *buf;
+    int i;
+
+    memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
+
+    for (i = 0; i < 64; i++) {
+        buf = malloc(ETH_MTU_SIZE + sizeof(VirtioNetHdr));
+        IPL_assert(buf != NULL, "Can not allocate memory for receive buffers");
+        vring_send_buf(rxvq, buf, ETH_MTU_SIZE + sizeof(VirtioNetHdr),
+                       VRING_DESC_F_WRITE);
+    }
+    vring_notify(rxvq);
+
+    return 0;
+}
+
+int send(int fd, const void *buf, int len, int flags)
+{
+    VirtioNetHdr tx_hdr;
+    VDev *vdev = virtio_get_device();
+    VRing *txvq = &vdev->vrings[VQ_TX];
+
+    /* Set up header - we do not use anything special, so simply clear it */
+    memset(&tx_hdr, 0, sizeof(tx_hdr));
+
+    vring_send_buf(txvq, &tx_hdr, sizeof(tx_hdr), VRING_DESC_F_NEXT);
+    vring_send_buf(txvq, (void *)buf, len, VRING_HIDDEN_IS_CHAIN);
+    while (!vr_poll(txvq)) {
+        yield();
+    }
+    if (drain_irqs(txvq->schid)) {
+        puts("send: drain irqs failed");
+        return -1;
+    }
+
+    return len;
+}
+
+int recv(int fd, void *buf, int maxlen, int flags)
+{
+    VDev *vdev = virtio_get_device();
+    VRing *rxvq = &vdev->vrings[VQ_RX];
+    int len, id;
+    uint8_t *pkt;
+
+    if (rx_last_idx == rxvq->used->idx) {
+        return 0;
+    }
+
+    len = rxvq->used->ring[rx_last_idx % rxvq->num].len - sizeof(VirtioNetHdr);
+    if (len > maxlen) {
+        puts("virtio-net: Receive buffer too small");
+        len = maxlen;
+    }
+    id = rxvq->used->ring[rx_last_idx % rxvq->num].id % rxvq->num;
+    pkt = (uint8_t *)(rxvq->desc[id].addr + sizeof(VirtioNetHdr));
+
+#if 0
+    /* Dump packet */
+    printf("\nbuf %p: len=%i\n", (void*)rxvq->desc[id].addr, len);
+    int i;
+    for (i = 0; i < 64; i++) {
+        printf(" %02x", pkt[i]);
+        if ((i%16)==15)
+            printf("\n");
+    }
+    printf("\n");
+#endif
+
+    /* Copy data to destination buffer */
+    memcpy(buf, pkt, len);
+
+    /* Mark buffer as available to the host again */
+    rxvq->avail->ring[rxvq->avail->idx % rxvq->num] = id;
+    rxvq->avail->idx = rxvq->avail->idx + 1;
+    vring_notify(rxvq);
+
+    /* Move index to next entry */
+    rx_last_idx = rx_last_idx + 1;
+
+    return len;
+}
+
+int close(int fd)
+{
+    return 0;
+}
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index 6ee93d5..b5219aa 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -69,7 +69,7 @@ static long virtio_notify(SubChannelId schid, int vq_idx, long cookie)
  *             Virtio functions                *
  ***********************************************/
 
-static int drain_irqs(SubChannelId schid)
+int drain_irqs(SubChannelId schid)
 {
     Irb irb = {};
     int r = 0;
@@ -148,13 +148,13 @@ static void vring_init(VRing *vr, VqInfo *info)
     debug_print_addr("init vr", vr);
 }
 
-static bool vring_notify(VRing *vr)
+bool vring_notify(VRing *vr)
 {
     vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie);
     return vr->cookie >= 0;
 }
 
-static void vring_send_buf(VRing *vr, void *p, int len, int flags)
+void vring_send_buf(VRing *vr, void *p, int len, int flags)
 {
     /* For follow-up chains we need to keep the first entry point */
     if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
@@ -187,7 +187,7 @@ ulong get_second(void)
     return (get_clock() >> 12) / 1000000;
 }
 
-static int vr_poll(VRing *vr)
+int vr_poll(VRing *vr)
 {
     if (vr->used->idx == vr->used_idx) {
         vring_notify(vr);
@@ -495,6 +495,11 @@ static void virtio_setup_ccw(VDev *vdev)
     run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0);
 
     switch (vdev->senseid.cu_model) {
+    case VIRTIO_ID_NET:
+        vdev->nr_vqs = 3;
+        vdev->cmd_vr_idx = 0;
+        cfg_size = sizeof(vdev->config.net);
+        break;
     case VIRTIO_ID_BLOCK:
         vdev->nr_vqs = 1;
         vdev->cmd_vr_idx = 0;
@@ -549,6 +554,9 @@ void virtio_setup_device(SubChannelId schid)
     virtio_setup_ccw(&vdev);
 
     switch (vdev.senseid.cu_model) {
+    case VIRTIO_ID_NET:
+        sclp_print("Using virtio-net.\n");
+        break;
     case VIRTIO_ID_BLOCK:
         sclp_print("Using virtio-blk.\n");
         if (!virtio_ipl_disk_is_valid()) {
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index 1eaf865..b4e0f41 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -254,6 +254,12 @@ struct ScsiDevice {
 };
 typedef struct ScsiDevice ScsiDevice;
 
+struct VirtioNetConfig {
+     uint8_t mac[6];
+     uint16_t status;
+};
+typedef struct VirtioNetConfig VirtioNetConfig;
+
 struct VDev {
     int nr_vqs;
     VRing *vrings;
@@ -266,6 +272,7 @@ struct VDev {
     union {
         VirtioBlkConfig blk;
         VirtioScsiConfig scsi;
+        VirtioNetConfig net;
     } config;
     ScsiDevice *scsi_device;
     bool is_cdrom;
@@ -291,6 +298,10 @@ struct VirtioCmd {
 };
 typedef struct VirtioCmd VirtioCmd;
 
+bool vring_notify(VRing *vr);
+int drain_irqs(SubChannelId schid);
+void vring_send_buf(VRing *vr, void *p, int len, int flags);
+int vr_poll(VRing *vr);
 int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd);
 
 #endif /* VIRTIO_H */
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 12/14] pc-bios/s390-ccw: Load file via an intermediate .INS file
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (10 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 11/14] pc-bios/s390-ccw: Add virtio-net driver code Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 13/14] pc-bios/s390-ccw: Allow loading to address 0 Thomas Huth
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

We need to know which files have to be loaded at which address.
Normally you need at least two files, a kernel image and an initrd
image. Since the normal DHCP information only provides one file,
we now load an intermediate .INS file first which has to contain
the information about the other files. The .INS file has the
same syntax as the .INS files that are already used on s390x
Linux distribution CD-ROMs: First line is the title (starting
with "* "), and in the following lines you can find the file
names followed by the address where they should be loaded.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/libnet/netapps.h |  2 +-
 pc-bios/s390-ccw/libnet/netload.c | 72 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/pc-bios/s390-ccw/libnet/netapps.h b/pc-bios/s390-ccw/libnet/netapps.h
index d2283af..61e8a11 100644
--- a/pc-bios/s390-ccw/libnet/netapps.h
+++ b/pc-bios/s390-ccw/libnet/netapps.h
@@ -18,7 +18,7 @@
 
 struct filename_ip;
 
-extern int netload(char *buffer, int len, char *ret_buffer);
+extern int netload(void);
 extern int dhcp(char *ret_buffer, struct filename_ip *fn_ip,
 		unsigned int retries, int flags);
 
diff --git a/pc-bios/s390-ccw/libnet/netload.c b/pc-bios/s390-ccw/libnet/netload.c
index eae8333..28e8711 100644
--- a/pc-bios/s390-ccw/libnet/netload.c
+++ b/pc-bios/s390-ccw/libnet/netload.c
@@ -26,6 +26,8 @@
 #include "args.h"
 #include "netapps.h"
 
+#define MAX_INS_FILE_LEN 16384
+
 #define IP_INIT_DEFAULT 5
 #define IP_INIT_NONE    0
 #define IP_INIT_BOOTP   1
@@ -138,7 +140,7 @@ static void seed_rng(uint8_t mac[])
 	srand(seed);
 }
 
-static int tftp_load(filename_ip_t *fnip, unsigned char *buffer, int len,
+static int tftp_load(filename_ip_t *fnip, void *buffer, int len,
 		     unsigned int retries, int ip_vers)
 {
 	tftp_err_t tftp_err;
@@ -227,7 +229,56 @@ static int tftp_load(filename_ip_t *fnip, unsigned char *buffer, int len,
 	return rc;
 }
 
-int netload(char *buffer, int len, char *ret_buffer)
+static int load_from_ins_file(char *insbuf, filename_ip_t *fn_ip, int retries,
+			      int ip_version)
+{
+	char *ptr;
+	int rc = -1, llen;
+	void *destaddr;
+
+	ptr = strchr(insbuf, '\n');
+
+	if (!ptr || insbuf[0] != '*' || insbuf[1] != ' ') {
+		puts("Does not seem to be a valid .INS file");
+		return -1;
+	}
+
+	*ptr = 0;
+	printf("\nParsing .INS file:\n  %s\n", &insbuf[2]);
+
+	insbuf = ptr + 1;
+	while (*insbuf) {
+		ptr = strchr(insbuf, '\n');
+		if (ptr) {
+			*ptr = 0;
+		}
+		llen = strlen(insbuf);
+		if (!llen) {
+			insbuf = ptr + 1;
+			continue;
+		}
+		ptr = strchr(insbuf, ' ');
+		if (!ptr) {
+			puts("Missing space separator in .INS file");
+			return -1;
+		}
+		*ptr = 0;
+		strncpy((char *)fn_ip->filename, insbuf,
+			sizeof(fn_ip->filename));
+		destaddr = (char *)atol(ptr + 1);
+		printf("\n  Loading file \"%s\" via TFTP to %p\n", insbuf,
+		       destaddr);
+		rc = tftp_load(fn_ip, destaddr, 50000000, retries, ip_version);
+		if (rc <= 0) {
+			break;
+		}
+		insbuf += llen + 1;
+	}
+
+	return rc;
+}
+
+int netload(void)
 {
 	int rc;
 	filename_ip_t fn_ip;
@@ -239,6 +290,7 @@ int netload(char *buffer, int len, char *ret_buffer)
 			     0x00, 0x00, 0x00, 0x00, 
 			     0x00, 0x00, 0x00, 0x00 };
 	uint8_t own_mac[6];
+	char *ins_buf, *ret_buffer = NULL;
 
 	puts("\n Initializing NIC");
 	memset(&fn_ip, 0, sizeof(filename_ip_t));
@@ -420,9 +472,19 @@ int netload(char *buffer, int len, char *ret_buffer)
 		printf("%s\n", ip6_str);
 	}
 
-	/* Do the TFTP load and print error message if necessary */
-	rc = tftp_load(&fn_ip, (unsigned char *)buffer, len,
-		       obp_tftp_args.tftp_retries, ip_version);
+	ins_buf = malloc(MAX_INS_FILE_LEN);
+	if (!ins_buf) {
+		puts("Failed to allocate memory for the .INS file");
+		return -1;
+	}
+	memset(ins_buf, 0, MAX_INS_FILE_LEN);
+	rc = tftp_load(&fn_ip, ins_buf, MAX_INS_FILE_LEN - 1,
+	               obp_tftp_args.tftp_retries, ip_version);
+	if (rc > 0) {
+		rc = load_from_ins_file(ins_buf, &fn_ip,
+					obp_tftp_args.tftp_retries, ip_version);
+	}
+	free(ins_buf);
 
 	if (obp_tftp_args.ip_init == IP_INIT_DHCP)
 		dhcp_send_release(fn_ip.fd);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 13/14] pc-bios/s390-ccw: Allow loading to address 0
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (11 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 12/14] pc-bios/s390-ccw: Load file via an intermediate .INS file Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 14/14] pc-bios/s390-ccw: Wire up the netload code Thomas Huth
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

Kernels are normally loaded to address 0, so the check for NULL
in the TFTP code has to be replaced.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/libnet/tftp.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/pc-bios/s390-ccw/libnet/tftp.c b/pc-bios/s390-ccw/libnet/tftp.c
index 34f448c..108092b 100644
--- a/pc-bios/s390-ccw/libnet/tftp.c
+++ b/pc-bios/s390-ccw/libnet/tftp.c
@@ -26,6 +26,7 @@
 
 #define MAX_BLOCKSIZE 1428
 #define BUFFER_LEN 256
+#define INVALID_BUFFER ((void *)-1L)
 
 #define ENOTFOUND 1
 #define EACCESS   2
@@ -45,7 +46,7 @@
 
 /* Local variables */
 static unsigned char packet[BUFFER_LEN];
-static unsigned char  *buffer = NULL;
+static unsigned char  *buffer = INVALID_BUFFER;
 static unsigned short block;
 static unsigned short blocksize;
 static char blocksize_str[6];    /* Blocksize string for read request */
@@ -337,7 +338,7 @@ int32_t handle_tftp(int fd, uint8_t *pkt, int32_t packetsize)
 	struct tftphdr *tftp;
 
 	/* buffer is only set if we are handling TFTP */
-	if (buffer == NULL )
+	if (buffer == INVALID_BUFFER)
 		return 0;
 
 #ifndef __DEBUG__
@@ -536,7 +537,7 @@ int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len,
 	printf("  Receiving data:  ");
 	print_progress(-1, 0);
 
-	// Setting buffer to a non-zero address enabled handling of received TFTP packets.
+	/* Set buffer to a valid address, enables handling of received packets */
 	buffer = _buffer;
 
 	set_timer(TICKS_SEC);
@@ -579,8 +580,8 @@ int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len,
 		}
 	}
 
-	// Setting buffer to NULL disables handling of received TFTP packets.
-	buffer = NULL;
+	/* Setting buffer invalid to disable handling of received packets */
+	buffer = INVALID_BUFFER;
 
 	if (tftp_errno)
 		return tftp_errno;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [RFC PATCH 14/14] pc-bios/s390-ccw: Wire up the netload code
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (12 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 13/14] pc-bios/s390-ccw: Allow loading to address 0 Thomas Huth
@ 2017-06-27 11:48 ` Thomas Huth
  2017-06-27 15:41 ` [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS David Hildenbrand
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 11:48 UTC (permalink / raw)
  To: qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

Link the libnet.a to the s390-ccw.elf file, call netload() instead of
the external network boot image, and jump to loaded kernel at address 0,
so that we can finally do a full network boot with the s390-ccw firmware
now.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/Makefile  |  2 +-
 pc-bios/s390-ccw/bootmap.c | 10 +++++++++-
 pc-bios/s390-ccw/main.c    |  3 +--
 3 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 369bd65..827d7be 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
 .PHONY : all clean build-all libc.a libnet.a
 
 OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-net.o
-OBJECTS += libc.a sbrk.o
+OBJECTS += libnet.a libc.a sbrk.o
 QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
 QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
 QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 523fa78..32befda 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -11,6 +11,7 @@
 #include "s390-ccw.h"
 #include "bootmap.h"
 #include "virtio.h"
+#include "libnet/netapps.h"
 
 #ifdef DEBUG
 /* #define DEBUG_FALLBACK */
@@ -744,7 +745,14 @@ void zipl_load(void)
     }
 
     if (virtio_get_device_type() == VIRTIO_ID_NET) {
-        jump_to_IPL_code(vdev->netboot_start_addr);
+        long len;
+
+        len = netload();
+        if (len < 0) {
+            panic("Network loading failed");
+        }
+        sclp_print("Netload done, starting kernel...\n");
+        asm volatile (" lpsw 0(%0) " : : "r"(0) : "memory");
     }
 
     ipl_scsi();
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 1cacc1b..b058e1b 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -150,12 +150,11 @@ static void virtio_setup(void)
 
     IPL_assert(found, "No virtio device found");
 
+    virtio_setup_device(blk_schid);
     if (virtio_get_device_type() == VIRTIO_ID_NET) {
         sclp_print("Network boot device detected\n");
         vdev->netboot_start_addr = iplb.ccw.netboot_start_addr;
     } else {
-        virtio_setup_device(blk_schid);
-
         IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected");
     }
 }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware Thomas Huth
@ 2017-06-27 15:32   ` David Hildenbrand
  2017-06-27 22:14     ` Thomas Huth
  0 siblings, 1 reply; 35+ messages in thread
From: David Hildenbrand @ 2017-06-27 15:32 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, Jens Freimann, Eric Farman

On 27.06.2017 13:48, Thomas Huth wrote:
> To be able to use some more advanced libc functions in the s390-ccw
> firmware, like printf() and malloc(), we need a better libc here.
> This patch adds the C library from the SLOF firmware (taken from
> the SLOF commit ID 62674aabe20612a9786fa03e87cf6916ba97a99a). The
> files are copied without modifications here and will be adapted for
> the s390-ccw firmware by the next patch. I just removed the getopt()
> and scanf()-like functions from the libc since we likely do not need
> them in the s390-ccw firmware.
> 

We have SLOF as a git submodule in roms/SLOF.

I wonder if there would a way to avoid duplicating files.

E.g. build s390x-ccw.img only if roms/SLOF is checked out and link/copy
the right folder.

Then, also the question regarding coding style is gone.

Would something like that work?


-- 

Thanks,

David

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (13 preceding siblings ...)
  2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 14/14] pc-bios/s390-ccw: Wire up the netload code Thomas Huth
@ 2017-06-27 15:41 ` David Hildenbrand
  2017-06-27 15:50 ` Viktor Mihajlovski
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 35+ messages in thread
From: David Hildenbrand @ 2017-06-27 15:41 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, Jens Freimann, Eric Farman

On 27.06.2017 13:48, Thomas Huth wrote:
> It's already possible to do a network boot of an s390x guest with an
> external netboot image (based on a Linux installation), but it would
> be much more convenient if the s390-ccw firmware supported network
> booting right out of the box, without the need to assemble such an
> external image first.
> 
> This patch series now introduces network booting via DHCP and TFTP
> directly into the s390-ccw firmware by re-using the networking stack
> from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
> and adds a driver for virtio-net-ccw devices.
> 
> Once the patches have been applied, you can download an .INS file
> via TFTP which contains the information about the further files
> that have to be loaded - kernel, initrd, etc. For example, you can
> use the built-in TFTP and DHCP server of QEMU for this by starting
> QEMU with:

Pretty cool stuff!

> 
>  qemu-system-s390x ... -device virtio-net,netdev=n1,bootindex=1 \
>        -netdev user,id=n1,tftp=/path/to/tftp,bootfile=generic.ins
> 
> The .INS file has to use the same syntax as the .INS files that can
> already be found on s390x Linux distribution installation CD-ROMs.
> 
> The patches are still in a rough shape, but before I continue here,
> I though I'd get some feedback first. Specifically:
> 
> - This adds a lot of additional code to the s390-ccw firmware (and
>   the binary is afterwards three times as big as before, 75k instead
>   of 25k) ... is that still acceptable?

As mentioned in patch #1, would it somehow be able to include code
rather than copying it and maintaining it separately?

> 
> - Is it OK to require loading an .INS file first? Or does anybody
>   have a better idea how to load multiple files (kernel, initrd,
>   etc. ...)?

Guess this is the way to go for now.

> 
> - The code from SLOF uses a different coding style (TABs instead
>   of space) ... is it OK to keep that coding style here so we
>   can share patches between SLOF and s390-ccw more easily?
> 
> - The code only supports TFTP (via UDP) ... I think that is OK for
>   most use-cases, but if we ever want to support network booting
>   via HTTP or something else that is based on TCP, we would need to
>   use something else instead... Should we maybe rather head towards
>   grub2, petitboot or something different instead?

Good question, I think all these solutions will require way more effort.

> 
>  Thomas

-- 

Thanks,

David

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (14 preceding siblings ...)
  2017-06-27 15:41 ` [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS David Hildenbrand
@ 2017-06-27 15:50 ` Viktor Mihajlovski
  2017-06-27 21:40   ` Thomas Huth
  2017-06-27 16:50 ` Farhan Ali
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 35+ messages in thread
From: Viktor Mihajlovski @ 2017-06-27 15:50 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 27.06.2017 13:48, Thomas Huth wrote:
> It's already possible to do a network boot of an s390x guest with an
> external netboot image (based on a Linux installation), but it would
> be much more convenient if the s390-ccw firmware supported network
> booting right out of the box, without the need to assemble such an
> external image first.
> 
> This patch series now introduces network booting via DHCP and TFTP
> directly into the s390-ccw firmware by re-using the networking stack
> from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
> and adds a driver for virtio-net-ccw devices.Neat :)
> 
> Once the patches have been applied, you can download an .INS file
> via TFTP which contains the information about the further files
> that have to be loaded - kernel, initrd, etc. For example, you can
> use the built-in TFTP and DHCP server of QEMU for this by starting
> QEMU with:
> 
>  qemu-system-s390x ... -device virtio-net,netdev=n1,bootindex=1 \
>        -netdev user,id=n1,tftp=/path/to/tftp,bootfile=generic.ins
> 
or the dnsmasq tftp server configured by libvirt (SCNR)
> The .INS file has to use the same syntax as the .INS files that can
> already be found on s390x Linux distribution installation CD-ROMs.
> 
> The patches are still in a rough shape, but before I continue here,
> I though I'd get some feedback first. Specifically:
> 
> - This adds a lot of additional code to the s390-ccw firmware (and
>   the binary is afterwards three times as big as before, 75k instead
>   of 25k) ... is that still acceptable?
The size may be less of an issue than the general overhead for
initialization. Since the current approach of the s390-ccw firmware of
lazy loading (only if a network boot is requested) takes care of that,
would it be conceivable that you build a standalone network boot
firmware image instead of incorporating that into the base firmware?
> 
> - Is it OK to require loading an .INS file first? Or does anybody
>   have a better idea how to load multiple files (kernel, initrd,
>   etc. ...)?
It would be nice to support PXE-style boot, because the majority of boot
servers is set up that way. A straightforward way would be to do a PXE
emulation by attempting to download a pxelinux.cfg from the well-known
locations, parsing the content (menu) and finally load the kernel,
initrd and set the kernel command line as specified there. (I know, but
you're already parsing the INS-File).
Alternatively, one could load a single boot image (consisting of kernel
and initrd concatenated, i.e. the bootable ISO format). This could serve
as a more potent "stage 2" boot loader.
> 
> - The code from SLOF uses a different coding style (TABs instead
>   of space) ... is it OK to keep that coding style here so we
>   can share patches between SLOF and s390-ccw more easil>
> - The code only supports TFTP (via UDP) ... I think that is OK for
>   most use-cases, but if we ever want to support network booting
>   via HTTP or something else that is based on TCP, we would need to
>   use something else instead... Should we maybe rather head towards
>   grub2, petitboot or something different instead?
I don't have an opinion on whether HTTP, FTP, etc is needed, but at some
point in time it would definitely be cool to have IPv6 support. Not sure
whether SLOF has that included.
> 
>  Thomas
> 
> 
[...]
-- 

Mit freundlichen Grüßen/Kind Regards
   Viktor Mihajlovski

IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Martina Köderitz
Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen
Registergericht: Amtsgericht Stuttgart, HRB 243294

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (15 preceding siblings ...)
  2017-06-27 15:50 ` Viktor Mihajlovski
@ 2017-06-27 16:50 ` Farhan Ali
  2017-06-28  7:34   ` Thomas Huth
  2017-06-27 21:15 ` Alexander Graf
  2017-06-28  7:43 ` Christian Borntraeger
  18 siblings, 1 reply; 35+ messages in thread
From: Farhan Ali @ 2017-06-27 16:50 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, David Hildenbrand, Jens Freimann, Eric Farman

Hi Thomas,

This is really interesting :)

On 06/27/2017 07:48 AM, Thomas Huth wrote:
> It's already possible to do a network boot of an s390x guest with an
> external netboot image (based on a Linux installation), but it would
> be much more convenient if the s390-ccw firmware supported network
> booting right out of the box, without the need to assemble such an
> external image first.
>
> This patch series now introduces network booting via DHCP and TFTP
> directly into the s390-ccw firmware by re-using the networking stack
> from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
> and adds a driver for virtio-net-ccw devices.
>
> Once the patches have been applied, you can download an .INS file
> via TFTP which contains the information about the further files
> that have to be loaded - kernel, initrd, etc. For example, you can
> use the built-in TFTP and DHCP server of QEMU for this by starting
> QEMU with:
>
>  qemu-system-s390x ... -device virtio-net,netdev=n1,bootindex=1 \
>        -netdev user,id=n1,tftp=/path/to/tftp,bootfile=generic.ins
>
> The .INS file has to use the same syntax as the .INS files that can
> already be found on s390x Linux distribution installation CD-ROMs.
>
> The patches are still in a rough shape, but before I continue here,
> I though I'd get some feedback first. Specifically:
>
> - This adds a lot of additional code to the s390-ccw firmware (and
>   the binary is afterwards three times as big as before, 75k instead
>   of 25k) ... is that still acceptable?

IMHO 75k is not that huge compared to the Linux based netboot image.

>
> - Is it OK to require loading an .INS file first? Or does anybody
>   have a better idea how to load multiple files (kernel, initrd,
>   etc. ...)?
>
> - The code from SLOF uses a different coding style (TABs instead
>   of space) ... is it OK to keep that coding style here so we
>   can share patches between SLOF and s390-ccw more easily?
>
> - The code only supports TFTP (via UDP) ... I think that is OK for
>   most use-cases, but if we ever want to support network booting
>   via HTTP or something else that is based on TCP, we would need to
>   use something else instead... Should we maybe rather head towards
>   grub2, petitboot or something different instead?

Can't it be extended to support HTTP boot?
>
>  Thomas
>
>
> Thomas Huth (14):
>   pc-bios/s390-ccw: Add the libc from the SLOF firmware
>   pc-bios/s390-ccw: Start using the libc from SLOF
>   pc-bios/s390-ccw: Add a write() function for stdio
>   pc-bios/s390-ccw: Add implementation of sbrk()
>   pc-bios/s390-ccw: Add the TFTP network loading stack from SLOF
>   libnet: Remove remainders of netsave code
>   libnet: Rework error message printing
>   libnet: Refactor some code of netload() into a separate function
>   pc-bios/s390-ccw: Make the basic libnet code compilable
>   pc-bios/s390-ccw: Add timer code for the libnet
>   pc-bios/s390-ccw: Add virtio-net driver code
>   pc-bios/s390-ccw: Load file via an intermediate .INS file
>   pc-bios/s390-ccw: Allow loading to address 0
>   pc-bios/s390-ccw: Wire up the netload code
>
>  configure                                  |   6 +-
>  hw/s390x/ipl.c                             |   2 +-
>  pc-bios/s390-ccw/Makefile                  |  14 +-
>  pc-bios/s390-ccw/bootmap.c                 |  10 +-
>  pc-bios/s390-ccw/bootmap.h                 |   1 +
>  pc-bios/s390-ccw/libc/Makefile             |  47 ++
>  pc-bios/s390-ccw/libc/README.txt           |  49 ++
>  pc-bios/s390-ccw/libc/ctype/Makefile.inc   |  21 +
>  pc-bios/s390-ccw/libc/ctype/isdigit.c      |  25 +
>  pc-bios/s390-ccw/libc/ctype/isprint.c      |  18 +
>  pc-bios/s390-ccw/libc/ctype/isspace.c      |  29 +
>  pc-bios/s390-ccw/libc/ctype/isxdigit.c     |  21 +
>  pc-bios/s390-ccw/libc/ctype/tolower.c      |  18 +
>  pc-bios/s390-ccw/libc/ctype/toupper.c      |  21 +
>  pc-bios/s390-ccw/libc/include/ctype.h      |  24 +
>  pc-bios/s390-ccw/libc/include/errno.h      |  34 +
>  pc-bios/s390-ccw/libc/include/limits.h     |  32 +
>  pc-bios/s390-ccw/libc/include/stdarg.h     |  22 +
>  pc-bios/s390-ccw/libc/include/stdbool.h    |  20 +
>  pc-bios/s390-ccw/libc/include/stddef.h     |  25 +
>  pc-bios/s390-ccw/libc/include/stdint.h     |  28 +
>  pc-bios/s390-ccw/libc/include/stdio.h      |  63 ++
>  pc-bios/s390-ccw/libc/include/stdlib.h     |  34 +
>  pc-bios/s390-ccw/libc/include/string.h     |  37 ++
>  pc-bios/s390-ccw/libc/include/sys/socket.h |  53 ++
>  pc-bios/s390-ccw/libc/include/unistd.h     |  28 +
>  pc-bios/s390-ccw/libc/stdio/Makefile.inc   |  24 +
>  pc-bios/s390-ccw/libc/stdio/fileno.c       |  19 +
>  pc-bios/s390-ccw/libc/stdio/fprintf.c      |  26 +
>  pc-bios/s390-ccw/libc/stdio/printf.c       |  27 +
>  pc-bios/s390-ccw/libc/stdio/putc.c         |  25 +
>  pc-bios/s390-ccw/libc/stdio/putchar.c      |  21 +
>  pc-bios/s390-ccw/libc/stdio/puts.c         |  28 +
>  pc-bios/s390-ccw/libc/stdio/setvbuf.c      |  28 +
>  pc-bios/s390-ccw/libc/stdio/sprintf.c      |  30 +
>  pc-bios/s390-ccw/libc/stdio/stdchnls.c     |  23 +
>  pc-bios/s390-ccw/libc/stdio/vfprintf.c     |  27 +
>  pc-bios/s390-ccw/libc/stdio/vsnprintf.c    | 298 +++++++++
>  pc-bios/s390-ccw/libc/stdio/vsprintf.c     |  19 +
>  pc-bios/s390-ccw/libc/stdlib/Makefile.inc  |  23 +
>  pc-bios/s390-ccw/libc/stdlib/atoi.c        |  18 +
>  pc-bios/s390-ccw/libc/stdlib/atol.c        |  18 +
>  pc-bios/s390-ccw/libc/stdlib/error.c       |  15 +
>  pc-bios/s390-ccw/libc/stdlib/free.c        |  26 +
>  pc-bios/s390-ccw/libc/stdlib/malloc.c      | 157 +++++
>  pc-bios/s390-ccw/libc/stdlib/malloc_defs.h |  16 +
>  pc-bios/s390-ccw/libc/stdlib/memalign.c    |  26 +
>  pc-bios/s390-ccw/libc/stdlib/rand.c        |  29 +
>  pc-bios/s390-ccw/libc/stdlib/realloc.c     |  40 ++
>  pc-bios/s390-ccw/libc/stdlib/strtol.c      | 115 ++++
>  pc-bios/s390-ccw/libc/stdlib/strtoul.c     | 105 ++++
>  pc-bios/s390-ccw/libc/string/Makefile.inc  |  23 +
>  pc-bios/s390-ccw/libc/string/memchr.c      |  29 +
>  pc-bios/s390-ccw/libc/string/memcmp.c      |  30 +
>  pc-bios/s390-ccw/libc/string/memcpy.c      |  27 +
>  pc-bios/s390-ccw/libc/string/memmove.c     |  42 ++
>  pc-bios/s390-ccw/libc/string/memset.c      |  25 +
>  pc-bios/s390-ccw/libc/string/strcasecmp.c  |  28 +
>  pc-bios/s390-ccw/libc/string/strcat.c      |  24 +
>  pc-bios/s390-ccw/libc/string/strchr.c      |  28 +
>  pc-bios/s390-ccw/libc/string/strcmp.c      |  28 +
>  pc-bios/s390-ccw/libc/string/strcpy.c      |  25 +
>  pc-bios/s390-ccw/libc/string/strlen.c      |  27 +
>  pc-bios/s390-ccw/libc/string/strncasecmp.c |  32 +
>  pc-bios/s390-ccw/libc/string/strncmp.c     |  31 +
>  pc-bios/s390-ccw/libc/string/strncpy.c     |  33 +
>  pc-bios/s390-ccw/libc/string/strstr.c      |  37 ++
>  pc-bios/s390-ccw/libc/string/strtok.c      |  45 ++
>  pc-bios/s390-ccw/libnet/Makefile           |  42 ++
>  pc-bios/s390-ccw/libnet/args.c             | 179 ++++++
>  pc-bios/s390-ccw/libnet/args.h             |  23 +
>  pc-bios/s390-ccw/libnet/dhcp.c             | 955 +++++++++++++++++++++++++++++
>  pc-bios/s390-ccw/libnet/dhcp.h             |  49 ++
>  pc-bios/s390-ccw/libnet/dhcpv6.c           | 212 +++++++
>  pc-bios/s390-ccw/libnet/dhcpv6.h           | 154 +++++
>  pc-bios/s390-ccw/libnet/dns.c              | 526 ++++++++++++++++
>  pc-bios/s390-ccw/libnet/dns.h              |  28 +
>  pc-bios/s390-ccw/libnet/ethernet.c         | 189 ++++++
>  pc-bios/s390-ccw/libnet/ethernet.h         |  47 ++
>  pc-bios/s390-ccw/libnet/icmpv6.c           | 409 ++++++++++++
>  pc-bios/s390-ccw/libnet/icmpv6.h           | 135 ++++
>  pc-bios/s390-ccw/libnet/ipv4.c             | 898 +++++++++++++++++++++++++++
>  pc-bios/s390-ccw/libnet/ipv4.h             |  97 +++
>  pc-bios/s390-ccw/libnet/ipv6.c             | 774 +++++++++++++++++++++++
>  pc-bios/s390-ccw/libnet/ipv6.h             | 188 ++++++
>  pc-bios/s390-ccw/libnet/ndp.c              | 184 ++++++
>  pc-bios/s390-ccw/libnet/ndp.h              |  72 +++
>  pc-bios/s390-ccw/libnet/netapps.h          |  25 +
>  pc-bios/s390-ccw/libnet/netload.c          | 600 ++++++++++++++++++
>  pc-bios/s390-ccw/libnet/tcp.c              |  46 ++
>  pc-bios/s390-ccw/libnet/tcp.h              |  27 +
>  pc-bios/s390-ccw/libnet/tftp.c             | 595 ++++++++++++++++++
>  pc-bios/s390-ccw/libnet/tftp.h             |  50 ++
>  pc-bios/s390-ccw/libnet/time.h             |   6 +
>  pc-bios/s390-ccw/libnet/timer.c            |  40 ++
>  pc-bios/s390-ccw/libnet/udp.c              | 115 ++++
>  pc-bios/s390-ccw/libnet/udp.h              |  53 ++
>  pc-bios/s390-ccw/main.c                    |   3 +-
>  pc-bios/s390-ccw/s390-ccw.h                |  30 +-
>  pc-bios/s390-ccw/sbrk.c                    |  39 ++
>  pc-bios/s390-ccw/sclp.c                    |  32 +-
>  pc-bios/s390-ccw/virtio-net.c              | 125 ++++
>  pc-bios/s390-ccw/virtio.c                  |  16 +-
>  pc-bios/s390-ccw/virtio.h                  |  11 +
>  104 files changed, 9296 insertions(+), 57 deletions(-)
>  create mode 100644 pc-bios/s390-ccw/libc/Makefile
>  create mode 100644 pc-bios/s390-ccw/libc/README.txt
>  create mode 100644 pc-bios/s390-ccw/libc/ctype/Makefile.inc
>  create mode 100644 pc-bios/s390-ccw/libc/ctype/isdigit.c
>  create mode 100644 pc-bios/s390-ccw/libc/ctype/isprint.c
>  create mode 100644 pc-bios/s390-ccw/libc/ctype/isspace.c
>  create mode 100644 pc-bios/s390-ccw/libc/ctype/isxdigit.c
>  create mode 100644 pc-bios/s390-ccw/libc/ctype/tolower.c
>  create mode 100644 pc-bios/s390-ccw/libc/ctype/toupper.c
>  create mode 100644 pc-bios/s390-ccw/libc/include/ctype.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/errno.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/limits.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/stdarg.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/stdbool.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/stddef.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/stdint.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/stdio.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/stdlib.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/string.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/sys/socket.h
>  create mode 100644 pc-bios/s390-ccw/libc/include/unistd.h
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/Makefile.inc
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/fileno.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/fprintf.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/printf.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/putc.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/putchar.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/puts.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/setvbuf.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/sprintf.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/stdchnls.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/vfprintf.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/vsnprintf.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdio/vsprintf.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/Makefile.inc
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/atoi.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/atol.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/error.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/free.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/malloc.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/malloc_defs.h
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/memalign.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/rand.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/realloc.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/strtol.c
>  create mode 100644 pc-bios/s390-ccw/libc/stdlib/strtoul.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/Makefile.inc
>  create mode 100644 pc-bios/s390-ccw/libc/string/memchr.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/memcmp.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/memcpy.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/memmove.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/memset.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strcasecmp.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strcat.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strchr.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strcmp.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strcpy.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strlen.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strncasecmp.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strncmp.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strncpy.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strstr.c
>  create mode 100644 pc-bios/s390-ccw/libc/string/strtok.c
>  create mode 100644 pc-bios/s390-ccw/libnet/Makefile
>  create mode 100644 pc-bios/s390-ccw/libnet/args.c
>  create mode 100644 pc-bios/s390-ccw/libnet/args.h
>  create mode 100644 pc-bios/s390-ccw/libnet/dhcp.c
>  create mode 100644 pc-bios/s390-ccw/libnet/dhcp.h
>  create mode 100644 pc-bios/s390-ccw/libnet/dhcpv6.c
>  create mode 100644 pc-bios/s390-ccw/libnet/dhcpv6.h
>  create mode 100644 pc-bios/s390-ccw/libnet/dns.c
>  create mode 100644 pc-bios/s390-ccw/libnet/dns.h
>  create mode 100644 pc-bios/s390-ccw/libnet/ethernet.c
>  create mode 100644 pc-bios/s390-ccw/libnet/ethernet.h
>  create mode 100644 pc-bios/s390-ccw/libnet/icmpv6.c
>  create mode 100644 pc-bios/s390-ccw/libnet/icmpv6.h
>  create mode 100644 pc-bios/s390-ccw/libnet/ipv4.c
>  create mode 100644 pc-bios/s390-ccw/libnet/ipv4.h
>  create mode 100644 pc-bios/s390-ccw/libnet/ipv6.c
>  create mode 100644 pc-bios/s390-ccw/libnet/ipv6.h
>  create mode 100644 pc-bios/s390-ccw/libnet/ndp.c
>  create mode 100644 pc-bios/s390-ccw/libnet/ndp.h
>  create mode 100644 pc-bios/s390-ccw/libnet/netapps.h
>  create mode 100644 pc-bios/s390-ccw/libnet/netload.c
>  create mode 100644 pc-bios/s390-ccw/libnet/tcp.c
>  create mode 100644 pc-bios/s390-ccw/libnet/tcp.h
>  create mode 100644 pc-bios/s390-ccw/libnet/tftp.c
>  create mode 100644 pc-bios/s390-ccw/libnet/tftp.h
>  create mode 100644 pc-bios/s390-ccw/libnet/time.h
>  create mode 100644 pc-bios/s390-ccw/libnet/timer.c
>  create mode 100644 pc-bios/s390-ccw/libnet/udp.c
>  create mode 100644 pc-bios/s390-ccw/libnet/udp.h
>  create mode 100644 pc-bios/s390-ccw/sbrk.c
>  create mode 100644 pc-bios/s390-ccw/virtio-net.c
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (16 preceding siblings ...)
  2017-06-27 16:50 ` Farhan Ali
@ 2017-06-27 21:15 ` Alexander Graf
  2017-06-27 21:56   ` Thomas Huth
  2017-06-28  7:43 ` Christian Borntraeger
  18 siblings, 1 reply; 35+ messages in thread
From: Alexander Graf @ 2017-06-27 21:15 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel, Christian Borntraeger
  Cc: Farhan Ali, David Hildenbrand, Jens Freimann, Eric Farman



On 27.06.17 13:48, Thomas Huth wrote:
> It's already possible to do a network boot of an s390x guest with an
> external netboot image (based on a Linux installation), but it would
> be much more convenient if the s390-ccw firmware supported network
> booting right out of the box, without the need to assemble such an
> external image first.
> 
> This patch series now introduces network booting via DHCP and TFTP
> directly into the s390-ccw firmware by re-using the networking stack
> from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
> and adds a driver for virtio-net-ccw devices.
> 
> Once the patches have been applied, you can download an .INS file
> via TFTP which contains the information about the further files
> that have to be loaded - kernel, initrd, etc. For example, you can
> use the built-in TFTP and DHCP server of QEMU for this by starting
> QEMU with:
> 
>   qemu-system-s390x ... -device virtio-net,netdev=n1,bootindex=1 \
>         -netdev user,id=n1,tftp=/path/to/tftp,bootfile=generic.ins
> 
> The .INS file has to use the same syntax as the .INS files that can
> already be found on s390x Linux distribution installation CD-ROMs.
> 
> The patches are still in a rough shape, but before I continue here,
> I though I'd get some feedback first. Specifically:
> 
> - This adds a lot of additional code to the s390-ccw firmware (and
>    the binary is afterwards three times as big as before, 75k instead
>    of 25k) ... is that still acceptable?

I think 75k is still perfectly reasonable, yes.

> 
> - Is it OK to require loading an .INS file first? Or does anybody
>    have a better idea how to load multiple files (kernel, initrd,
>    etc. ...)?
> 
> - The code from SLOF uses a different coding style (TABs instead
>    of space) ... is it OK to keep that coding style here so we
>    can share patches between SLOF and s390-ccw more easily?
> 
> - The code only supports TFTP (via UDP) ... I think that is OK for
>    most use-cases, but if we ever want to support network booting
>    via HTTP or something else that is based on TCP, we would need to
>    use something else instead... Should we maybe rather head towards
>    grub2, petitboot or something different instead?

IMHO the only viable next step would be to support UEFI and build on top 
of that - either by porting edk2 or U-Boot to s390x.

The problem with solutions like petitboot or home grown grub2 targets is 
that it becomes a documentation and knowledge sharing nightmare down the 
road. The less we're different from everyone else, the easier it becomes 
to maintain s390x.


Alex

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 15:50 ` Viktor Mihajlovski
@ 2017-06-27 21:40   ` Thomas Huth
  2017-06-28  7:28     ` Viktor Mihajlovski
  0 siblings, 1 reply; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 21:40 UTC (permalink / raw)
  To: Viktor Mihajlovski, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 27.06.2017 17:50, Viktor Mihajlovski wrote:
> On 27.06.2017 13:48, Thomas Huth wrote:
[...]
>> The patches are still in a rough shape, but before I continue here,
>> I though I'd get some feedback first. Specifically:
>>
>> - This adds a lot of additional code to the s390-ccw firmware (and
>>   the binary is afterwards three times as big as before, 75k instead
>>   of 25k) ... is that still acceptable?
> The size may be less of an issue than the general overhead for
> initialization.

Which concerns with regards to overhead do you have here? Startup time?
As far as I can tell, that's not an issue here - the firmware still
starts very fast.

> Since the current approach of the s390-ccw firmware of
> lazy loading (only if a network boot is requested) takes care of that,
> would it be conceivable that you build a standalone network boot
> firmware image instead of incorporating that into the base firmware?

That would shoot down the big advantage of this approach: Not to have to
fiddle with multiple firmware images - which is my main motivation here.
I'd like to avoid that we have to create yet another firmware package in
the distros. (Otherwise, what's the purpose of this when putting it into
yet another binary? We could then simply continue the current approach
with the Linux binary instead)

>> - Is it OK to require loading an .INS file first? Or does anybody
>>   have a better idea how to load multiple files (kernel, initrd,
>>   etc. ...)?
> It would be nice to support PXE-style boot, because the majority of boot
> servers is set up that way. A straightforward way would be to do a PXE
> emulation by attempting to download a pxelinux.cfg from the well-known
> locations, parsing the content (menu) and finally load the kernel,
> initrd and set the kernel command line as specified there. (I know, but
> you're already parsing the INS-File).

Please, don't mix up PXE and pxelinux (since you've used both terms in
above paragraph). Assuming that you're only talking about pxlinux config
files... are they that common on s390x already? Using the pxelinux
config file syntax sounds like we would be completely bound to only
loading Linux guests to me, since the boot loader has to know where to
load the initrd and how to patch the kernel so that it can find the initrd.
Using .INS files sounds more flexible to me instead, since you can also
specify the addresses here - so you can theoretically also load other
guest kernels, and that's IMHO the better approach since a firmware
should stay as generic as possible.

> Alternatively, one could load a single boot image (consisting of kernel
> and initrd concatenated, i.e. the bootable ISO format). This could serve
> as a more potent "stage 2" boot loader.

Agreed, that's also a common practice when doing network booting. I
guess the firmware could also support both quite easily, direct single
boot images, and .INS files. The latter could be detected via the file
name or with the magic string "* " at the beginning.

>> - The code from SLOF uses a different coding style (TABs instead
>>   of space) ... is it OK to keep that coding style here so we
>>   can share patches between SLOF and s390-ccw more easil>
>> - The code only supports TFTP (via UDP) ... I think that is OK for
>>   most use-cases, but if we ever want to support network booting
>>   via HTTP or something else that is based on TCP, we would need to
>>   use something else instead... Should we maybe rather head towards
>>   grub2, petitboot or something different instead?
> I don't have an opinion on whether HTTP, FTP, etc is needed, but at some
> point in time it would definitely be cool to have IPv6 support. Not sure
> whether SLOF has that included.

Yes, IPv6 is included in this networking stack.

 Thomas

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 21:15 ` Alexander Graf
@ 2017-06-27 21:56   ` Thomas Huth
  2017-06-28  8:06     ` Gerd Hoffmann
  0 siblings, 1 reply; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 21:56 UTC (permalink / raw)
  To: Alexander Graf, qemu-devel, Christian Borntraeger
  Cc: Farhan Ali, David Hildenbrand, Jens Freimann, Eric Farman

On 27.06.2017 23:15, Alexander Graf wrote:
> 
> On 27.06.17 13:48, Thomas Huth wrote:
[...]
>> - The code only supports TFTP (via UDP) ... I think that is OK for
>>    most use-cases, but if we ever want to support network booting
>>    via HTTP or something else that is based on TCP, we would need to
>>    use something else instead... Should we maybe rather head towards
>>    grub2, petitboot or something different instead?
> 
> IMHO the only viable next step would be to support UEFI and build on top
> of that - either by porting edk2 or U-Boot to s390x.

I can't speak of U-Boot, but you certainly don't want to port edk2 to a
big endian machine. Been there, seen that.

> The problem with solutions like petitboot or home grown grub2 targets is
> that it becomes a documentation and knowledge sharing nightmare down the
> road. The less we're different from everyone else, the easier it becomes
> to maintain s390x.

petitboot is basically a Linux system (with a shell, too). And grub2 is
also a common boot loader on x86 nowadays ... so not sure whether they
would really be a knowledge sharing nightmare? I think they would be
more common for "server" people than the U-Boot loader, which is likely
only familiar to "embedded" folks.

 Thomas

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware
  2017-06-27 15:32   ` David Hildenbrand
@ 2017-06-27 22:14     ` Thomas Huth
  0 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-27 22:14 UTC (permalink / raw)
  To: David Hildenbrand, qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, Farhan Ali, Jens Freimann, Eric Farman

On 27.06.2017 17:32, David Hildenbrand wrote:
> On 27.06.2017 13:48, Thomas Huth wrote:
>> To be able to use some more advanced libc functions in the s390-ccw
>> firmware, like printf() and malloc(), we need a better libc here.
>> This patch adds the C library from the SLOF firmware (taken from
>> the SLOF commit ID 62674aabe20612a9786fa03e87cf6916ba97a99a). The
>> files are copied without modifications here and will be adapted for
>> the s390-ccw firmware by the next patch. I just removed the getopt()
>> and scanf()-like functions from the libc since we likely do not need
>> them in the s390-ccw firmware.
>>
> 
> We have SLOF as a git submodule in roms/SLOF.
> 
> I wonder if there would a way to avoid duplicating files.
> 
> E.g. build s390x-ccw.img only if roms/SLOF is checked out and link/copy
> the right folder.
> 
> Then, also the question regarding coding style is gone.
> 
> Would something like that work?

Cool idea, I like it at a first glance. It might be possible ... but
that also creates a lot of dependencies, e.g. each time there is a
related change in the SLOF repository, you run into the problem that it
might affect the s390-ccw firmware, too. Not sure whether we really
always want to deal with that situation...

 Thomas

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 21:40   ` Thomas Huth
@ 2017-06-28  7:28     ` Viktor Mihajlovski
  2017-06-28  8:02       ` Thomas Huth
  0 siblings, 1 reply; 35+ messages in thread
From: Viktor Mihajlovski @ 2017-06-28  7:28 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 27.06.2017 23:40, Thomas Huth wrote:
[...]
>>> - Is it OK to require loading an .INS file first? Or does anybody
>>>   have a better idea how to load multiple files (kernel, initrd,
>>>   etc. ...)?
>> It would be nice to support PXE-style boot, because the majority of boot
>> servers is set up that way. A straightforward way would be to do a PXE
>> emulation by attempting to download a pxelinux.cfg from the well-known
>> locations, parsing the content (menu) and finally load the kernel,
>> initrd and set the kernel command line as specified there. (I know, but
>> you're already parsing the INS-File).
> 
> Please, don't mix up PXE and pxelinux (since you've used both terms in
> above paragraph). Assuming that you're only talking about pxlinux config
> files... are they that common on s390x already? Using the pxelinux
> config file syntax sounds like we would be completely bound to only
> loading Linux guests to me, since the boot loader has to know where to
> load the initrd and how to patch the kernel so that it can find the initrd.
> Using .INS files sounds more flexible to me instead, since you can also
> specify the addresses here - so you can theoretically also load other
> guest kernels, and that's IMHO the better approach since a firmware
> should stay as generic as possible.
> 
In order to be consumable, the network boot should support the most
common configurations. I would think that most network boot servers are
setup as PXE boot servers using pxelinux configs. It will do no good to
tell system administrators to have a totally different setup for s390
boot clients. If the firmware doesn't support it we will have to fall
back to provide a Linux-based fat netboot image to the pxelinux handling
:-(.
>> Alternatively, one could load a single boot image (consisting of kernel
>> and initrd concatenated, i.e. the bootable ISO format). This could serve
>> as a more potent "stage 2" boot loader.
> 
> Agreed, that's also a common practice when doing network booting. I
> guess the firmware could also support both quite easily, direct single
> boot images, and .INS files. The latter could be detected via the file
> name or with the magic string "* " at the beginning.
> >>> - The code from SLOF uses a different coding style (TABs instead
>>>   of space) ... is it OK to keep that coding style here so we
>>>   can share patches between SLOF and s390-ccw more easil>
>>> - The code only supports TFTP (via UDP) ... I think that is OK for
>>>   most use-cases, but if we ever want to support network booting
>>>   via HTTP or something else that is based on TCP, we would need to
>>>   use something else instead... Should we maybe rather head towards
>>>   grub2, petitboot or something different instead?
>> I don't have an opinion on whether HTTP, FTP, etc is needed, but at some
>> point in time it would definitely be cool to have IPv6 support. Not sure
>> whether SLOF has that included.
> 
> Yes, IPv6 is included in this networking stack.
> 
>  Thomas
> 


-- 

Mit freundlichen Grüßen/Kind Regards
   Viktor Mihajlovski

IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Martina Köderitz
Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen
Registergericht: Amtsgericht Stuttgart, HRB 243294

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 16:50 ` Farhan Ali
@ 2017-06-28  7:34   ` Thomas Huth
  0 siblings, 0 replies; 35+ messages in thread
From: Thomas Huth @ 2017-06-28  7:34 UTC (permalink / raw)
  To: Farhan Ali, qemu-devel, Christian Borntraeger
  Cc: Alexander Graf, David Hildenbrand, Jens Freimann, Eric Farman

On 27.06.2017 18:50, Farhan Ali wrote:
[...]
>> The patches are still in a rough shape, but before I continue here,
>> I though I'd get some feedback first. Specifically:
>>
>> - This adds a lot of additional code to the s390-ccw firmware (and
>>   the binary is afterwards three times as big as before, 75k instead
>>   of 25k) ... is that still acceptable?
> 
> IMHO 75k is not that huge compared to the Linux based netboot image.

True. I was just surprised that the size was now three times as big as
before, but when you compare it to the huge size of a Linux kernel +
initrd, 75k are of course close to nothing.

>> - Is it OK to require loading an .INS file first? Or does anybody
>>   have a better idea how to load multiple files (kernel, initrd,
>>   etc. ...)?
>>
>> - The code from SLOF uses a different coding style (TABs instead
>>   of space) ... is it OK to keep that coding style here so we
>>   can share patches between SLOF and s390-ccw more easily?
>>
>> - The code only supports TFTP (via UDP) ... I think that is OK for
>>   most use-cases, but if we ever want to support network booting
>>   via HTTP or something else that is based on TCP, we would need to
>>   use something else instead... Should we maybe rather head towards
>>   grub2, petitboot or something different instead?
> 
> Can't it be extended to support HTTP boot?

Not so easily, since you need TCP support for this first. And TCP is way
more complex to implement when compared with UDP.

 Thomas

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
                   ` (17 preceding siblings ...)
  2017-06-27 21:15 ` Alexander Graf
@ 2017-06-28  7:43 ` Christian Borntraeger
  2017-06-28  8:59   ` Thomas Huth
  18 siblings, 1 reply; 35+ messages in thread
From: Christian Borntraeger @ 2017-06-28  7:43 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

Interesting work, thanks for giving it a ry.

On 06/27/2017 01:48 PM, Thomas Huth wrote:
> It's already possible to do a network boot of an s390x guest with an
> external netboot image (based on a Linux installation), but it would
> be much more convenient if the s390-ccw firmware supported network
> booting right out of the box, without the need to assemble such an
> external image first.
> 
> This patch series now introduces network booting via DHCP and TFTP
> directly into the s390-ccw firmware by re-using the networking stack
> from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
> and adds a driver for virtio-net-ccw devices.

What is the licensing situation with SLOF?
> 
> Once the patches have been applied, you can download an .INS file
> via TFTP which contains the information about the further files
> that have to be loaded - kernel, initrd, etc. For example, you can
> use the built-in TFTP and DHCP server of QEMU for this by starting
> QEMU with:
> 
>  qemu-system-s390x ... -device virtio-net,netdev=n1,bootindex=1 \
>        -netdev user,id=n1,tftp=/path/to/tftp,bootfile=generic.ins
> 
> The .INS file has to use the same syntax as the .INS files that can
> already be found on s390x Linux distribution installation CD-ROMs.
> 
> The patches are still in a rough shape, but before I continue here,
> I though I'd get some feedback first. Specifically:
> 
> - This adds a lot of additional code to the s390-ccw firmware (and
>   the binary is afterwards three times as big as before, 75k instead
>   of 25k) ... is that still acceptable?

Yes, sure. I think up to 1M everything is fine as long as the startup time
stays low (e.g. if we want to do something like clear containers. Right
now I can start a small Linux guest from qemu start to command line in <100ms
without any optimization)

> 
> - Is it OK to require loading an .INS file first? Or does anybody
>   have a better idea how to load multiple files (kernel, initrd,
>   etc. ...)?

I agree with Viktor that supporting a pxelinux config file is probably the
better way, since this is what all exisiting servers admins understand. For
the time being Linux will be the most relevant guest and requiring changes in
management infrastructure will certainly make things very hard.


> - The code from SLOF uses a different coding style (TABs instead
>   of space) ... is it OK to keep that coding style here so we
>   can share patches between SLOF and s390-ccw more easily?

Is it possible to enhance SLOF and then link to the existing SLOF code?
> 
> - The code only supports TFTP (via UDP) ... I think that is OK for
>   most use-cases, but if we ever want to support network booting
>   via HTTP or something else that is based on TCP, we would need to
>   use something else instead... Should we maybe rather head towards
>   grub2, petitboot or something different instead?

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-28  7:28     ` Viktor Mihajlovski
@ 2017-06-28  8:02       ` Thomas Huth
  2017-06-28 10:56         ` Thomas Huth
  0 siblings, 1 reply; 35+ messages in thread
From: Thomas Huth @ 2017-06-28  8:02 UTC (permalink / raw)
  To: Viktor Mihajlovski, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 28.06.2017 09:28, Viktor Mihajlovski wrote:
> On 27.06.2017 23:40, Thomas Huth wrote:
> [...]
>>>> - Is it OK to require loading an .INS file first? Or does anybody
>>>>   have a better idea how to load multiple files (kernel, initrd,
>>>>   etc. ...)?
>>> It would be nice to support PXE-style boot, because the majority of boot
>>> servers is set up that way. A straightforward way would be to do a PXE
>>> emulation by attempting to download a pxelinux.cfg from the well-known
>>> locations, parsing the content (menu) and finally load the kernel,
>>> initrd and set the kernel command line as specified there. (I know, but
>>> you're already parsing the INS-File).
>>
>> Please, don't mix up PXE and pxelinux (since you've used both terms in
>> above paragraph). Assuming that you're only talking about pxlinux config
>> files... are they that common on s390x already? Using the pxelinux
>> config file syntax sounds like we would be completely bound to only
>> loading Linux guests to me, since the boot loader has to know where to
>> load the initrd and how to patch the kernel so that it can find the initrd.
>> Using .INS files sounds more flexible to me instead, since you can also
>> specify the addresses here - so you can theoretically also load other
>> guest kernels, and that's IMHO the better approach since a firmware
>> should stay as generic as possible.
>>
> In order to be consumable, the network boot should support the most
> common configurations. I would think that most network boot servers are
> setup as PXE boot servers using pxelinux configs.

Are you really sure about the popularity of pxelinux? It's just one
flavor of secondary stage network boot loaders - which also only exist
on x86 so far, as far as I know. You can also use other loaders like
grub2 or gPXE there instead. And other architectures also do not use
pxelinux. For example on PowerPC, you normally use yaboot or grub2 as
secondary stage boot loader via network instead.
 > It will do no good to tell system administrators to have a totally>
different setup for s390 boot clients.

Trying to mimic pxelinux in the firmware still sounds wrong to me. That
might also cause some confusion, e.g. if something in the config file is
not fully supported (like menus?), or people might start wondering where
to get the pxelinux binary itself for s390x ...
Maybe it would be better to port the pxelinux binary to s390x?

> If the firmware doesn't support it we will have to fall
> back to provide a Linux-based fat netboot image to the pxelinux handling
> :-(.

So far I haven't seen such a Linux-based netboot image yet ([1] mentions
pxelinux, but not what software should be used to parse the config
file), ... do you have a pointer to the software that parses the
pxelinux config file there?

 Thomas

[1] https://lists.gnu.org/archive/html/qemu-devel/2017-02/msg04333.html

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-27 21:56   ` Thomas Huth
@ 2017-06-28  8:06     ` Gerd Hoffmann
  0 siblings, 0 replies; 35+ messages in thread
From: Gerd Hoffmann @ 2017-06-28  8:06 UTC (permalink / raw)
  To: Thomas Huth, Alexander Graf, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Jens Freimann, David Hildenbrand

  Hi,

> > IMHO the only viable next step would be to support UEFI and build
> > on top
> > of that - either by porting edk2 or U-Boot to s390x.
> 
> I can't speak of U-Boot, but you certainly don't want to port edk2 to
> a big endian machine. Been there, seen that.

> would really be a knowledge sharing nightmare? I think they would be
> more common for "server" people than the U-Boot loader, which is
> likely
> only familiar to "embedded" folks.

Well, u-boot supports some[1] EFI interfaces these days, so you can run
standard efi grub2 on embedded arm boards.  From that point of view
porting uboot to s390 might not be as silly as it initially looks like.

cheers,
  Gerd

[1] barely enough to make grub2 happy.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-28  7:43 ` Christian Borntraeger
@ 2017-06-28  8:59   ` Thomas Huth
  2017-06-29  8:17     ` Thomas Huth
  0 siblings, 1 reply; 35+ messages in thread
From: Thomas Huth @ 2017-06-28  8:59 UTC (permalink / raw)
  To: Christian Borntraeger, qemu-devel
  Cc: Alexander Graf, Farhan Ali, David Hildenbrand, Jens Freimann,
	Eric Farman

On 28.06.2017 09:43, Christian Borntraeger wrote:
> Interesting work, thanks for giving it a ry.
> 
> On 06/27/2017 01:48 PM, Thomas Huth wrote:
>> It's already possible to do a network boot of an s390x guest with an
>> external netboot image (based on a Linux installation), but it would
>> be much more convenient if the s390-ccw firmware supported network
>> booting right out of the box, without the need to assemble such an
>> external image first.
>>
>> This patch series now introduces network booting via DHCP and TFTP
>> directly into the s390-ccw firmware by re-using the networking stack
>> from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
>> and adds a driver for virtio-net-ccw devices.
> 
> What is the licensing situation with SLOF?

All its code is licensed under the 3-Clause BSD License:

 https://github.com/aik/SLOF/blob/master/LICENSE

AFAIK it should be fine to use such code in GPL-ed sources like the
s390-ccw firmware, shouldn't it?

>> - Is it OK to require loading an .INS file first? Or does anybody
>>   have a better idea how to load multiple files (kernel, initrd,
>>   etc. ...)?
> 
> I agree with Viktor that supporting a pxelinux config file is probably the
> better way, since this is what all exisiting servers admins understand. For
> the time being Linux will be the most relevant guest and requiring changes in
> management infrastructure will certainly make things very hard.

I have to say, the more I read about pxelinux, the more I think we
should *not* directly support this in the firmware. pxelinux is clearly
a secondary stage bootloader, with a rather complex config file, and
features like config file name guessing via MAC-address or IP-address
... if we really want to support that on s390x, too, I think it should
stay in a external binary instead, and not directly incorporated into
the s390-ccw firmware (so that users finally have the choice whether
they want to use pxelinux-style config files or grub2 or something
different one day).

I guess the .INS file parsing in firmware was already a bad idea... all
other typical firmware implementations can also only load one file - and
if you need to load multiple files, you've got to use a secondary stage
bootloader like pxelinux, yaboot or grub2. So if we agree to add network
booting directly into the s390-ccw firmware, I think we should do the
same here and only support loading of one file (without an additional
config file). But then the question is, of course, whether it makes
sense to add that support to the firmware at all, or whether we should
simply continue with the current "s390-netboot.img" secondary-loader
approach...

>> - The code from SLOF uses a different coding style (TABs instead
>>   of space) ... is it OK to keep that coding style here so we
>>   can share patches between SLOF and s390-ccw more easily?
> 
> Is it possible to enhance SLOF and then link to the existing SLOF code?

I already submitted some of the clean-up patches to the SLOF mailing
list, and Alexey has merged them now, e.g.:

 https://github.com/aik/SLOF/commit/140c3f39db4ce4c0

And as I already mentioned in my reply to David, it should theoretically
be possible to use the code from the roms/SLOF submodule in QEMU ...
but then we've got to deal with sudden changes in the SLOF repository
which might cause unwanted problems in the s390-ccw firmware. I guess we
could give it a try (the libc code is very, very stable in SLOF, and the
libnet code also changes only very seldomly) - of course only if we
really decide that we want to have TFTP support directly in the
firmware. If we rather want to continue with the s390-netboot.img
approach instead, I have got to reconsider whether I continue with my
efforts by putting that stuff into an external binary, or whether it
makes more sense to look into porting pxelinux, grub2 or petitboot
instead...

 Thomas

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-28  8:02       ` Thomas Huth
@ 2017-06-28 10:56         ` Thomas Huth
  2017-06-28 15:02           ` Viktor Mihajlovski
  0 siblings, 1 reply; 35+ messages in thread
From: Thomas Huth @ 2017-06-28 10:56 UTC (permalink / raw)
  To: Viktor Mihajlovski, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 28.06.2017 10:02, Thomas Huth wrote:
> On 28.06.2017 09:28, Viktor Mihajlovski wrote:
>> On 27.06.2017 23:40, Thomas Huth wrote:
>> [...]
>>>>> - Is it OK to require loading an .INS file first? Or does anybody
>>>>>   have a better idea how to load multiple files (kernel, initrd,
>>>>>   etc. ...)?
>>>> It would be nice to support PXE-style boot, because the majority of boot
>>>> servers is set up that way. A straightforward way would be to do a PXE
>>>> emulation by attempting to download a pxelinux.cfg from the well-known
>>>> locations, parsing the content (menu) and finally load the kernel,
>>>> initrd and set the kernel command line as specified there. (I know, but
>>>> you're already parsing the INS-File).
>>>
>>> Please, don't mix up PXE and pxelinux (since you've used both terms in
>>> above paragraph). Assuming that you're only talking about pxlinux config
>>> files... are they that common on s390x already? Using the pxelinux
>>> config file syntax sounds like we would be completely bound to only
>>> loading Linux guests to me, since the boot loader has to know where to
>>> load the initrd and how to patch the kernel so that it can find the initrd.
>>> Using .INS files sounds more flexible to me instead, since you can also
>>> specify the addresses here - so you can theoretically also load other
>>> guest kernels, and that's IMHO the better approach since a firmware
>>> should stay as generic as possible.
>>>
>> In order to be consumable, the network boot should support the most
>> common configurations. I would think that most network boot servers are
>> setup as PXE boot servers using pxelinux configs.
> 
> Are you really sure about the popularity of pxelinux? It's just one
> flavor of secondary stage network boot loaders - which also only exist
> on x86 so far, as far as I know.

And it seems like it also only works with legacy BIOSes, i.e. you can
not use it on EFI-only systems, if I've got that right:

https://github.com/openSUSE/kiwi/wiki/Setup-PXE-boot-with-EFI-Using-GRUB2

So I guess the significance of pxelinux will very likely decrease in
the next years...

 Thomas

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-28 10:56         ` Thomas Huth
@ 2017-06-28 15:02           ` Viktor Mihajlovski
  2017-06-29  7:58             ` Thomas Huth
  0 siblings, 1 reply; 35+ messages in thread
From: Viktor Mihajlovski @ 2017-06-28 15:02 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 28.06.2017 12:56, Thomas Huth wrote:
> On 28.06.2017 10:02, Thomas Huth wrote:
>> On 28.06.2017 09:28, Viktor Mihajlovski wrote:
>>> On 27.06.2017 23:40, Thomas Huth wrote:
>>> [...]
>>>>>> - Is it OK to require loading an .INS file first? Or does anybody
>>>>>>   have a better idea how to load multiple files (kernel, initrd,
>>>>>>   etc. ...)?
>>>>> It would be nice to support PXE-style boot, because the majority of boot
>>>>> servers is set up that way. A straightforward way would be to do a PXE
>>>>> emulation by attempting to download a pxelinux.cfg from the well-known
>>>>> locations, parsing the content (menu) and finally load the kernel,
>>>>> initrd and set the kernel command line as specified there. (I know, but
>>>>> you're already parsing the INS-File).
>>>>
>>>> Please, don't mix up PXE and pxelinux (since you've used both terms in
>>>> above paragraph). Assuming that you're only talking about pxlinux config
>>>> files... are they that common on s390x already? Using the pxelinux
>>>> config file syntax sounds like we would be completely bound to only
>>>> loading Linux guests to me, since the boot loader has to know where to
>>>> load the initrd and how to patch the kernel so that it can find the initrd.
>>>> Using .INS files sounds more flexible to me instead, since you can also
>>>> specify the addresses here - so you can theoretically also load other
>>>> guest kernels, and that's IMHO the better approach since a firmware
>>>> should stay as generic as possible.
>>>>
>>> In order to be consumable, the network boot should support the most
>>> common configurations. I would think that most network boot servers are
>>> setup as PXE boot servers using pxelinux configs.
>>
>> Are you really sure about the popularity of pxelinux? It's just one
>> flavor of secondary stage network boot loaders - which also only exist
>> on x86 so far, as far as I know.
> 
> And it seems like it also only works with legacy BIOSes, i.e. you can
> not use it on EFI-only systems, if I've got that right:
> 
> https://github.com/openSUSE/kiwi/wiki/Setup-PXE-boot-with-EFI-Using-GRUB2
> 
> So I guess the significance of pxelinux will very likely decrease in
> the next years...
> 
Maybe, but supposed goners tend to linger more often than not. I.e., the
syslinux project offers a EFI bootloader called syslinux.efi equivalent
to the pexelinux.0 BIOS loader.
Further, the OPAL firmware of newer POWER systems embeds petitboot and
thus offers PXELINUX-compatible network boot as well.

I appreciate the idea of a proper BOOTP implementation though, so maybe
a compromise could be to start off with your proposal with the slight
modification that the final boot action is controlled by the bootfile
content (file magic), similar to what you suggested in order to support
both insfile and binary kernel. PXELINUX emulation could be triggered by
a specially crafted bootfile then. What do you think?
>  Thomas
> 


-- 

Mit freundlichen Grüßen/Kind Regards
   Viktor Mihajlovski

IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Martina Köderitz
Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen
Registergericht: Amtsgericht Stuttgart, HRB 243294

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-28 15:02           ` Viktor Mihajlovski
@ 2017-06-29  7:58             ` Thomas Huth
  2017-06-29  8:10               ` Viktor Mihajlovski
  0 siblings, 1 reply; 35+ messages in thread
From: Thomas Huth @ 2017-06-29  7:58 UTC (permalink / raw)
  To: Viktor Mihajlovski, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 28.06.2017 17:02, Viktor Mihajlovski wrote:
> On 28.06.2017 12:56, Thomas Huth wrote:
>> On 28.06.2017 10:02, Thomas Huth wrote:
>>> On 28.06.2017 09:28, Viktor Mihajlovski wrote:
>>>> On 27.06.2017 23:40, Thomas Huth wrote:
>>>> [...]
>>>>>>> - Is it OK to require loading an .INS file first? Or does anybody
>>>>>>>   have a better idea how to load multiple files (kernel, initrd,
>>>>>>>   etc. ...)?
>>>>>> It would be nice to support PXE-style boot, because the majority of boot
>>>>>> servers is set up that way. A straightforward way would be to do a PXE
>>>>>> emulation by attempting to download a pxelinux.cfg from the well-known
>>>>>> locations, parsing the content (menu) and finally load the kernel,
>>>>>> initrd and set the kernel command line as specified there. (I know, but
>>>>>> you're already parsing the INS-File).
>>>>>
>>>>> Please, don't mix up PXE and pxelinux (since you've used both terms in
>>>>> above paragraph). Assuming that you're only talking about pxlinux config
>>>>> files... are they that common on s390x already? Using the pxelinux
>>>>> config file syntax sounds like we would be completely bound to only
>>>>> loading Linux guests to me, since the boot loader has to know where to
>>>>> load the initrd and how to patch the kernel so that it can find the initrd.
>>>>> Using .INS files sounds more flexible to me instead, since you can also
>>>>> specify the addresses here - so you can theoretically also load other
>>>>> guest kernels, and that's IMHO the better approach since a firmware
>>>>> should stay as generic as possible.
>>>>>
>>>> In order to be consumable, the network boot should support the most
>>>> common configurations. I would think that most network boot servers are
>>>> setup as PXE boot servers using pxelinux configs.
>>>
>>> Are you really sure about the popularity of pxelinux? It's just one
>>> flavor of secondary stage network boot loaders - which also only exist
>>> on x86 so far, as far as I know.
>>
>> And it seems like it also only works with legacy BIOSes, i.e. you can
>> not use it on EFI-only systems, if I've got that right:
>>
>> https://github.com/openSUSE/kiwi/wiki/Setup-PXE-boot-with-EFI-Using-GRUB2
>>
>> So I guess the significance of pxelinux will very likely decrease in
>> the next years...
>>
> Maybe, but supposed goners tend to linger more often than not. I.e., the
> syslinux project offers a EFI bootloader called syslinux.efi equivalent
> to the pexelinux.0 BIOS loader.
> Further, the OPAL firmware of newer POWER systems embeds petitboot and
> thus offers PXELINUX-compatible network boot as well.

OK, true, ... you're slowly get me convinced that this pxelinux.cfg
stuff is maybe not such a bad idea after all ... So I guess supporting
at least the basic commands from the pxelinux config file would be
appropriate... (the full set of commands is huge, see
http://www.syslinux.org/wiki/index.php?title=Config )

> I appreciate the idea of a proper BOOTP implementation though, so maybe
> a compromise could be to start off with your proposal with the slight
> modification that the final boot action is controlled by the bootfile
> content (file magic), similar to what you suggested in order to support
> both insfile and binary kernel. PXELINUX emulation could be triggered by
> a specially crafted bootfile then. What do you think?

Yes, something like this could work:

1) Do DHCP to get TFTP server address and boot file name
2) Load the boot file from the TFTP server to address 0
3) If the boot file name ended with ".ins" or ".INS" (and the content
   starts with the "* " magic), treat it as an .INS file and load the
   files that are listed in there accordingly
4) If the boot file looks like a kernel, start it directly
5) If not successful in 3 or 4, start looking for a pxelinux config
   file by trying to download the config files as specified in
   http://www.syslinux.org/wiki/index.php?title=PXELINUX#Configuration
   and then parse the file and load the kernel + initrd accordingly.

Quite a bit of work, so I'll continue to ignore 5 for the first
versions, but I agree now that it can certainly be added later.

 Thomas

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-29  7:58             ` Thomas Huth
@ 2017-06-29  8:10               ` Viktor Mihajlovski
  0 siblings, 0 replies; 35+ messages in thread
From: Viktor Mihajlovski @ 2017-06-29  8:10 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel, Christian Borntraeger
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 29.06.2017 09:58, Thomas Huth wrote:
> On 28.06.2017 17:02, Viktor Mihajlovski wrote:
>> On 28.06.2017 12:56, Thomas Huth wrote:
>>> On 28.06.2017 10:02, Thomas Huth wrote:
>>>> On 28.06.2017 09:28, Viktor Mihajlovski wrote:
>>>>> On 27.06.2017 23:40, Thomas Huth wrote:
>>>>> [...]
>>>>>>>> - Is it OK to require loading an .INS file first? Or does anybody
>>>>>>>>   have a better idea how to load multiple files (kernel, initrd,
>>>>>>>>   etc. ...)?
>>>>>>> It would be nice to support PXE-style boot, because the majority of boot
>>>>>>> servers is set up that way. A straightforward way would be to do a PXE
>>>>>>> emulation by attempting to download a pxelinux.cfg from the well-known
>>>>>>> locations, parsing the content (menu) and finally load the kernel,
>>>>>>> initrd and set the kernel command line as specified there. (I know, but
>>>>>>> you're already parsing the INS-File).
>>>>>>
>>>>>> Please, don't mix up PXE and pxelinux (since you've used both terms in
>>>>>> above paragraph). Assuming that you're only talking about pxlinux config
>>>>>> files... are they that common on s390x already? Using the pxelinux
>>>>>> config file syntax sounds like we would be completely bound to only
>>>>>> loading Linux guests to me, since the boot loader has to know where to
>>>>>> load the initrd and how to patch the kernel so that it can find the initrd.
>>>>>> Using .INS files sounds more flexible to me instead, since you can also
>>>>>> specify the addresses here - so you can theoretically also load other
>>>>>> guest kernels, and that's IMHO the better approach since a firmware
>>>>>> should stay as generic as possible.
>>>>>>
>>>>> In order to be consumable, the network boot should support the most
>>>>> common configurations. I would think that most network boot servers are
>>>>> setup as PXE boot servers using pxelinux configs.
>>>>
>>>> Are you really sure about the popularity of pxelinux? It's just one
>>>> flavor of secondary stage network boot loaders - which also only exist
>>>> on x86 so far, as far as I know.
>>>
>>> And it seems like it also only works with legacy BIOSes, i.e. you can
>>> not use it on EFI-only systems, if I've got that right:
>>>
>>> https://github.com/openSUSE/kiwi/wiki/Setup-PXE-boot-with-EFI-Using-GRUB2
>>>
>>> So I guess the significance of pxelinux will very likely decrease in
>>> the next years...
>>>
>> Maybe, but supposed goners tend to linger more often than not. I.e., the
>> syslinux project offers a EFI bootloader called syslinux.efi equivalent
>> to the pexelinux.0 BIOS loader.
>> Further, the OPAL firmware of newer POWER systems embeds petitboot and
>> thus offers PXELINUX-compatible network boot as well.
> 
> OK, true, ... you're slowly get me convinced that this pxelinux.cfg
> stuff is maybe not such a bad idea after all ... So I guess supporting
> at least the basic commands from the pxelinux config file would be
> appropriate... (the full set of commands is huge, see
> http://www.syslinux.org/wiki/index.php?title=Config )
> 
>> I appreciate the idea of a proper BOOTP implementation though, so maybe
>> a compromise could be to start off with your proposal with the slight
>> modification that the final boot action is controlled by the bootfile
>> content (file magic), similar to what you suggested in order to support
>> both insfile and binary kernel. PXELINUX emulation could be triggered by
>> a specially crafted bootfile then. What do you think?
> 
> Yes, something like this could work:
> 
> 1) Do DHCP to get TFTP server address and boot file name
> 2) Load the boot file from the TFTP server to address 0
> 3) If the boot file name ended with ".ins" or ".INS" (and the content
>    starts with the "* " magic), treat it as an .INS file and load the
>    files that are listed in there accordingly
> 4) If the boot file looks like a kernel, start it directly
> 5) If not successful in 3 or 4, start looking for a pxelinux config
>    file by trying to download the config files as specified in
>    http://www.syslinux.org/wiki/index.php?title=PXELINUX#Configuration
>    and then parse the file and load the kernel + initrd accordingly.
> 
> Quite a bit of work, so I'll continue to ignore 5 for the first
> versions, but I agree now that it can certainly be added later.
That sounds more than fair. Thanks!
> 
>  Thomas
> 


-- 

Mit freundlichen Grüßen/Kind Regards
   Viktor Mihajlovski

IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Martina Köderitz
Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen
Registergericht: Amtsgericht Stuttgart, HRB 243294

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-28  8:59   ` Thomas Huth
@ 2017-06-29  8:17     ` Thomas Huth
  2017-06-29  8:39       ` Christian Borntraeger
  0 siblings, 1 reply; 35+ messages in thread
From: Thomas Huth @ 2017-06-29  8:17 UTC (permalink / raw)
  To: Christian Borntraeger, qemu-devel
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand

On 28.06.2017 10:59, Thomas Huth wrote:
> On 28.06.2017 09:43, Christian Borntraeger wrote:
>> Interesting work, thanks for giving it a ry.
>>
>> On 06/27/2017 01:48 PM, Thomas Huth wrote:
>>> It's already possible to do a network boot of an s390x guest with an
>>> external netboot image (based on a Linux installation), but it would
>>> be much more convenient if the s390-ccw firmware supported network
>>> booting right out of the box, without the need to assemble such an
>>> external image first.
>>>
>>> This patch series now introduces network booting via DHCP and TFTP
>>> directly into the s390-ccw firmware by re-using the networking stack
>>> from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
>>> and adds a driver for virtio-net-ccw devices.
>>
>> What is the licensing situation with SLOF?
> 
> All its code is licensed under the 3-Clause BSD License:
> 
>  https://github.com/aik/SLOF/blob/master/LICENSE
> 
> AFAIK it should be fine to use such code in GPL-ed sources like the
> s390-ccw firmware, shouldn't it?
> 
>>> - Is it OK to require loading an .INS file first? Or does anybody
>>>   have a better idea how to load multiple files (kernel, initrd,
>>>   etc. ...)?
>>
>> I agree with Viktor that supporting a pxelinux config file is probably the
>> better way, since this is what all exisiting servers admins understand. For
>> the time being Linux will be the most relevant guest and requiring changes in
>> management infrastructure will certainly make things very hard.
> 
> I have to say, the more I read about pxelinux, the more I think we
> should *not* directly support this in the firmware. pxelinux is clearly
> a secondary stage bootloader, with a rather complex config file, and
> features like config file name guessing via MAC-address or IP-address
> ... if we really want to support that on s390x, too, I think it should
> stay in a external binary instead, and not directly incorporated into
> the s390-ccw firmware (so that users finally have the choice whether
> they want to use pxelinux-style config files or grub2 or something
> different one day).
> 
> I guess the .INS file parsing in firmware was already a bad idea... all
> other typical firmware implementations can also only load one file - and
> if you need to load multiple files, you've got to use a secondary stage
> bootloader like pxelinux, yaboot or grub2. So if we agree to add network
> booting directly into the s390-ccw firmware, I think we should do the
> same here and only support loading of one file (without an additional
> config file). But then the question is, of course, whether it makes
> sense to add that support to the firmware at all, or whether we should
> simply continue with the current "s390-netboot.img" secondary-loader
> approach...
> 
>>> - The code from SLOF uses a different coding style (TABs instead
>>>   of space) ... is it OK to keep that coding style here so we
>>>   can share patches between SLOF and s390-ccw more easily?
>>
>> Is it possible to enhance SLOF and then link to the existing SLOF code?
> 
> I already submitted some of the clean-up patches to the SLOF mailing
> list, and Alexey has merged them now, e.g.:
> 
>  https://github.com/aik/SLOF/commit/140c3f39db4ce4c0
> 
> And as I already mentioned in my reply to David, it should theoretically
> be possible to use the code from the roms/SLOF submodule in QEMU ...
> but then we've got to deal with sudden changes in the SLOF repository
> which might cause unwanted problems in the s390-ccw firmware. I guess we
> could give it a try (the libc code is very, very stable in SLOF, and the
> libnet code also changes only very seldomly) - of course only if we
> really decide that we want to have TFTP support directly in the
> firmware. If we rather want to continue with the s390-netboot.img
> approach instead, I have got to reconsider whether I continue with my
> efforts by putting that stuff into an external binary, or whether it
> makes more sense to look into porting pxelinux, grub2 or petitboot
> instead...

OK, thinking about all of this again, what do you think of the following
approach:

Let's do not include network booting code into the s390-ccw.img, but
keep the way that it uses an external s390-netboot.img for this job.

I'll then try to add an additional s390-netboot.img target to the
Makefile, which will only be built if the roms/SLOF submodule has been
checked out. That target then uses the libc and libnet from the SLOF
submodule and links it with my virtio-net driver and some other required
code from the s390-ccw bios into a s390-netboot.img binary that can be
used for network booting.

This way ...
- the main s390-ccw.img stays independent from the changes in the SLOF,
and we can tackle possible problems in the s390-netboot.img
independently. And we don't have to deal with coding style issues in the
libc and libnet.
- we can ship a s390-netboot.img with QEMU directly, so that network
booting is possible out of the box without forcing the users /
downstream distros to figure out how to build a netboot.img on their own
- we keep the possibility to use alternative s390-netboot.imgs in case
the users need advanced features like booting via HTTP

Does that sound like an acceptable strategy?

 Thomas

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS
  2017-06-29  8:17     ` Thomas Huth
@ 2017-06-29  8:39       ` Christian Borntraeger
  0 siblings, 0 replies; 35+ messages in thread
From: Christian Borntraeger @ 2017-06-29  8:39 UTC (permalink / raw)
  To: Thomas Huth, qemu-devel
  Cc: Eric Farman, Farhan Ali, Alexander Graf, Jens Freimann,
	David Hildenbrand, Viktor Mihajlovski

On 06/29/2017 10:17 AM, Thomas Huth wrote:
> On 28.06.2017 10:59, Thomas Huth wrote:
>> On 28.06.2017 09:43, Christian Borntraeger wrote:
>>> Interesting work, thanks for giving it a ry.
>>>
>>> On 06/27/2017 01:48 PM, Thomas Huth wrote:
>>>> It's already possible to do a network boot of an s390x guest with an
>>>> external netboot image (based on a Linux installation), but it would
>>>> be much more convenient if the s390-ccw firmware supported network
>>>> booting right out of the box, without the need to assemble such an
>>>> external image first.
>>>>
>>>> This patch series now introduces network booting via DHCP and TFTP
>>>> directly into the s390-ccw firmware by re-using the networking stack
>>>> from the SLOF firmware (see https://github.com/aik/SLOF/ for details),
>>>> and adds a driver for virtio-net-ccw devices.
>>>
>>> What is the licensing situation with SLOF?
>>
>> All its code is licensed under the 3-Clause BSD License:
>>
>>  https://github.com/aik/SLOF/blob/master/LICENSE
>>
>> AFAIK it should be fine to use such code in GPL-ed sources like the
>> s390-ccw firmware, shouldn't it?
>>
>>>> - Is it OK to require loading an .INS file first? Or does anybody
>>>>   have a better idea how to load multiple files (kernel, initrd,
>>>>   etc. ...)?
>>>
>>> I agree with Viktor that supporting a pxelinux config file is probably the
>>> better way, since this is what all exisiting servers admins understand. For
>>> the time being Linux will be the most relevant guest and requiring changes in
>>> management infrastructure will certainly make things very hard.
>>
>> I have to say, the more I read about pxelinux, the more I think we
>> should *not* directly support this in the firmware. pxelinux is clearly
>> a secondary stage bootloader, with a rather complex config file, and
>> features like config file name guessing via MAC-address or IP-address
>> ... if we really want to support that on s390x, too, I think it should
>> stay in a external binary instead, and not directly incorporated into
>> the s390-ccw firmware (so that users finally have the choice whether
>> they want to use pxelinux-style config files or grub2 or something
>> different one day).
>>
>> I guess the .INS file parsing in firmware was already a bad idea... all
>> other typical firmware implementations can also only load one file - and
>> if you need to load multiple files, you've got to use a secondary stage
>> bootloader like pxelinux, yaboot or grub2. So if we agree to add network
>> booting directly into the s390-ccw firmware, I think we should do the
>> same here and only support loading of one file (without an additional
>> config file). But then the question is, of course, whether it makes
>> sense to add that support to the firmware at all, or whether we should
>> simply continue with the current "s390-netboot.img" secondary-loader
>> approach...
>>
>>>> - The code from SLOF uses a different coding style (TABs instead
>>>>   of space) ... is it OK to keep that coding style here so we
>>>>   can share patches between SLOF and s390-ccw more easily?
>>>
>>> Is it possible to enhance SLOF and then link to the existing SLOF code?
>>
>> I already submitted some of the clean-up patches to the SLOF mailing
>> list, and Alexey has merged them now, e.g.:
>>
>>  https://github.com/aik/SLOF/commit/140c3f39db4ce4c0
>>
>> And as I already mentioned in my reply to David, it should theoretically
>> be possible to use the code from the roms/SLOF submodule in QEMU ...
>> but then we've got to deal with sudden changes in the SLOF repository
>> which might cause unwanted problems in the s390-ccw firmware. I guess we
>> could give it a try (the libc code is very, very stable in SLOF, and the
>> libnet code also changes only very seldomly) - of course only if we
>> really decide that we want to have TFTP support directly in the
>> firmware. If we rather want to continue with the s390-netboot.img
>> approach instead, I have got to reconsider whether I continue with my
>> efforts by putting that stuff into an external binary, or whether it
>> makes more sense to look into porting pxelinux, grub2 or petitboot
>> instead...
> 
> OK, thinking about all of this again, what do you think of the following
> approach:
> 
> Let's do not include network booting code into the s390-ccw.img, but
> keep the way that it uses an external s390-netboot.img for this job.
> 
> I'll then try to add an additional s390-netboot.img target to the
> Makefile, which will only be built if the roms/SLOF submodule has been
> checked out. That target then uses the libc and libnet from the SLOF
> submodule and links it with my virtio-net driver and some other required
> code from the s390-ccw bios into a s390-netboot.img binary that can be
> used for network booting.
> 
> This way ...
> - the main s390-ccw.img stays independent from the changes in the SLOF,
> and we can tackle possible problems in the s390-netboot.img
> independently. And we don't have to deal with coding style issues in the
> libc and libnet.
> - we can ship a s390-netboot.img with QEMU directly, so that network
> booting is possible out of the box without forcing the users /
> downstream distros to figure out how to build a netboot.img on their own
> - we keep the possibility to use alternative s390-netboot.imgs in case
> the users need advanced features like booting via HTTP
> 
> Does that sound like an acceptable strategy?

Sounds perfectly fine to me. Thanks a lot for doing this.

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2017-06-29  8:39 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware Thomas Huth
2017-06-27 15:32   ` David Hildenbrand
2017-06-27 22:14     ` Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 02/14] pc-bios/s390-ccw: Start using the libc from SLOF Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 03/14] pc-bios/s390-ccw: Add a write() function for stdio Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 04/14] pc-bios/s390-ccw: Add implementation of sbrk() Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 05/14] pc-bios/s390-ccw: Add the TFTP network loading stack from SLOF Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 06/14] libnet: Remove remainders of netsave code Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 07/14] libnet: Rework error message printing Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 08/14] libnet: Refactor some code of netload() into a separate function Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 09/14] pc-bios/s390-ccw: Make the basic libnet code compilable Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 10/14] pc-bios/s390-ccw: Add timer code for the libnet Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 11/14] pc-bios/s390-ccw: Add virtio-net driver code Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 12/14] pc-bios/s390-ccw: Load file via an intermediate .INS file Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 13/14] pc-bios/s390-ccw: Allow loading to address 0 Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 14/14] pc-bios/s390-ccw: Wire up the netload code Thomas Huth
2017-06-27 15:41 ` [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS David Hildenbrand
2017-06-27 15:50 ` Viktor Mihajlovski
2017-06-27 21:40   ` Thomas Huth
2017-06-28  7:28     ` Viktor Mihajlovski
2017-06-28  8:02       ` Thomas Huth
2017-06-28 10:56         ` Thomas Huth
2017-06-28 15:02           ` Viktor Mihajlovski
2017-06-29  7:58             ` Thomas Huth
2017-06-29  8:10               ` Viktor Mihajlovski
2017-06-27 16:50 ` Farhan Ali
2017-06-28  7:34   ` Thomas Huth
2017-06-27 21:15 ` Alexander Graf
2017-06-27 21:56   ` Thomas Huth
2017-06-28  8:06     ` Gerd Hoffmann
2017-06-28  7:43 ` Christian Borntraeger
2017-06-28  8:59   ` Thomas Huth
2017-06-29  8:17     ` Thomas Huth
2017-06-29  8:39       ` Christian Borntraeger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.