diff --git a/.seabios.metadata b/.seabios.metadata
new file mode 100644
index 0000000..0166abd
--- /dev/null
+++ b/.seabios.metadata
@@ -0,0 +1 @@
+4d50b512c589fc7786d398fbc6e3935d4fa66530 SOURCES/seabios-1.7.2.2.tar.gz
diff --git a/README.md b/README.md
deleted file mode 100644
index 0e7897f..0000000
--- a/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-The master branch has no content
- 
-Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6
- 
-If you find this file in a distro specific branch, it means that no content has been checked in yet
diff --git a/SOURCES/Add-pvpanic-device-driver.patch b/SOURCES/Add-pvpanic-device-driver.patch
new file mode 100644
index 0000000..85b58a2
--- /dev/null
+++ b/SOURCES/Add-pvpanic-device-driver.patch
@@ -0,0 +1,111 @@
+Add pvpanic device driver
+
+Message-id: <1370958628-888-1-git-send-email-lersek@redhat.com>
+Patchwork-id: 51850
+O-Subject: [RHEL7 seabios PATCH] Add pvpanic device driver
+Bugzilla: 967777
+RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
+
+From: Hu Tao <hutao@cn.fujitsu.com>
+
+Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=967777
+Brew:     https://brewweb.devel.redhat.com/taskinfo?taskID=5890930
+Testing:  https://bugzilla.redhat.com/show_bug.cgi?id=967777#c3
+
+pvpanic device is used to notify host(qemu) when guest panic happens.
+
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+Signed-off-by: Hu Tao <hutao@cn.fujitsu.com>
+(cherry picked from commit e9725dd76d5d7212cb4a97fd18ff2599538955cf)
+
+Conflicts:
+
+	src/acpi.c
+
+RHEL-7 note:
+
+Gerd already ported this patch to 1.7.2 (which is where RHEL-7 is at
+currently). See <https://bugzilla.redhat.com/show_bug.cgi?id=831210#c17>
+and <https://bugzilla.redhat.com/show_bug.cgi?id=831210#c18> for details.
+
+I cherry picked his port from <git://git.kraxel.org/seabios>, commit
+5d01ca924a5c8fcd898ddc5813d88b3c32043882. Unfortunately this seems to
+introduce a compile error. I fixed that up.
+
+Signed-off-by: Laszlo Ersek <lersek@redhat.com>
+---
+ src/acpi.c        |    4 ++++
+ src/ssdt-susp.dsl |   46 ++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 50 insertions(+), 0 deletions(-)
+diff --git a/src/acpi.c b/src/acpi.c
+index 6267d7b..9b09118 100644
+--- a/src/acpi.c
++++ b/src/acpi.c
+@@ -535,6 +535,10 @@ build_ssdt(void)
+         ssdt_ptr[acpi_s4_name[0]] = 'X';
+     else
+         ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt[acpi_s4_pkg[0] + 3] = sys_states[4] & 127;
++
++    int pvpanic_port = romfile_loadint("etc/pvpanic-port", 0x0);
++    *(u16 *)(ssdt_ptr + *ssdt_isa_pest) = pvpanic_port;
++
+     ssdt_ptr += sizeof(ssdp_susp_aml);
+ 
+     // build Scope(_SB_) header
+diff --git a/src/ssdt-susp.dsl b/src/ssdt-susp.dsl
+index ca9428f..b8e9cf4 100644
+--- a/src/ssdt-susp.dsl
++++ b/src/ssdt-susp.dsl
+@@ -35,4 +35,50 @@ DefinitionBlock ("ssdt-susp.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1)
+             Zero   /* reserved */
+         })
+     }
++
++    External(\_SB.PCI0, DeviceObj)
++    External(\_SB.PCI0.ISA, DeviceObj)
++
++    Scope(\_SB.PCI0.ISA) {
++        Device(PEVT) {
++            Name(_HID, "QEMU0001")
++            /* PEST will be patched to be Zero if no such device */
++            ACPI_EXTRACT_NAME_WORD_CONST ssdt_isa_pest
++            Name(PEST, 0xFFFF)
++            OperationRegion(PEOR, SystemIO, PEST, 0x01)
++            Field(PEOR, ByteAcc, NoLock, Preserve) {
++                PEPT,   8,
++            }
++
++            Method(_STA, 0, NotSerialized) {
++                Store(PEST, Local0)
++                If (LEqual(Local0, Zero)) {
++                    Return (0x00)
++                } Else {
++                    Return (0x0F)
++                }
++            }
++
++            Method(RDPT, 0, NotSerialized) {
++                Store(PEPT, Local0)
++                Return (Local0)
++            }
++
++            Method(WRPT, 1, NotSerialized) {
++                Store(Arg0, PEPT)
++            }
++
++            Name(_CRS, ResourceTemplate() {
++                IO(Decode16, 0x00, 0x00, 0x01, 0x01, IO)
++            })
++
++            CreateWordField(_CRS, IO._MIN, IOMN)
++            CreateWordField(_CRS, IO._MAX, IOMX)
++
++            Method(_INI, 0, NotSerialized) {
++                Store(PEST, IOMN)
++                Store(PEST, IOMX)
++            }
++        }
++    }
+ }
diff --git a/SOURCES/config.base b/SOURCES/config.base
new file mode 100644
index 0000000..7d8b9dd
--- /dev/null
+++ b/SOURCES/config.base
@@ -0,0 +1,6 @@
+CONFIG_XEN=n
+CONFIG_ESP_SCSI=n
+CONFIG_LSI_SCSI=n
+CONFIG_USB_OHCI=n
+CONFIG_BOOTSPLASH=n
+CONFIG_MEGASAS=n
diff --git a/SOURCES/config.vga.cirrus b/SOURCES/config.vga.cirrus
new file mode 100644
index 0000000..c8fe582
--- /dev/null
+++ b/SOURCES/config.vga.cirrus
@@ -0,0 +1,3 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_CIRRUS=y
+CONFIG_VGA_PCI=y
diff --git a/SOURCES/config.vga.isavga b/SOURCES/config.vga.isavga
new file mode 100644
index 0000000..e55e294
--- /dev/null
+++ b/SOURCES/config.vga.isavga
@@ -0,0 +1,3 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=n
diff --git a/SOURCES/config.vga.qxl b/SOURCES/config.vga.qxl
new file mode 100644
index 0000000..d393f0c
--- /dev/null
+++ b/SOURCES/config.vga.qxl
@@ -0,0 +1,6 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
+CONFIG_OVERRIDE_PCI_ID=y
+CONFIG_VGA_VID=0x1b36
+CONFIG_VGA_DID=0x0100
diff --git a/SOURCES/config.vga.stdvga b/SOURCES/config.vga.stdvga
new file mode 100644
index 0000000..7d063b7
--- /dev/null
+++ b/SOURCES/config.vga.stdvga
@@ -0,0 +1,3 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
diff --git a/SOURCES/config.vga.vmware b/SOURCES/config.vga.vmware
new file mode 100644
index 0000000..eb10427
--- /dev/null
+++ b/SOURCES/config.vga.vmware
@@ -0,0 +1,6 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
+CONFIG_OVERRIDE_PCI_ID=y
+CONFIG_VGA_VID=0x15ad
+CONFIG_VGA_DID=0x0405
diff --git a/SOURCES/seabios-Another-fix-for-hlist_for_each_entry_safe.patch b/SOURCES/seabios-Another-fix-for-hlist_for_each_entry_safe.patch
new file mode 100644
index 0000000..9735c36
--- /dev/null
+++ b/SOURCES/seabios-Another-fix-for-hlist_for_each_entry_safe.patch
@@ -0,0 +1,63 @@
+From 71db0de897afe747d396134334e87a806be0beff Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 2 Oct 2013 07:00:15 +0200
+Subject: [PATCH 3/8] Another fix for hlist_for_each_entry_safe.
+
+RH-Author: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: <1380697219-1860-4-git-send-email-kraxel@redhat.com>
+Patchwork-id: 54627
+O-Subject: [RHEL-7 seabios PATCH 3/7] Another fix for hlist_for_each_entry_safe.
+Bugzilla: 947051
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+
+From: Kevin O'Connor <kevin@koconnor.net>
+
+Although the previous patch does fix hlist_for_each_entry_safe for the
+common case, it doesn't work correctly when deleting the current
+node.  To fix this, introduce two macros - hlist_for_each_entry_safe
+for iterating through a list that can be modified, and
+hlist_for_each_entry_pprev for those users that only need access to
+the "pprev" pointer.
+
+Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
+(cherry picked from commit 030a58a05e595694dccfa958563103d2f0644231)
+
+[ rhel-6: pick list.h changes only ]
+
+Conflicts:
+	src/boot.c
+	src/pciinit.c
+	src/pmm.c
+---
+ src/list.h | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/list.h |    8 +++++++-
+ 1 files changed, 7 insertions(+), 1 deletions(-)
+
+diff --git a/src/list.h b/src/list.h
+index 0f0909b..de656b9 100644
+--- a/src/list.h
++++ b/src/list.h
+@@ -66,7 +66,13 @@ hlist_add_after(struct hlist_node *n, struct hlist_node *prev)
+          ; pos != container_of(NULL, typeof(*pos), member)              \
+          ; pos = container_of(pos->member.next, typeof(*pos), member))
+ 
+-#define hlist_for_each_entry_safe(pos, pprev, head, member)             \
++#define hlist_for_each_entry_safe(pos, n, head, member)                 \
++    for (pos = container_of((head)->first, typeof(*pos), member)        \
++         ; pos != container_of(NULL, typeof(*pos), member)              \
++             && ({ n = pos->member.next; 1; })                          \
++         ; pos = container_of(n, typeof(*pos), member))
++
++#define hlist_for_each_entry_pprev(pos, pprev, head, member)            \
+     for (pprev = &(head)->first                                         \
+          ; *pprev && ({ pos=container_of(*pprev, typeof(*pos), member); 1; }) \
+          ; pprev = &(*pprev)->next)
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-Fix-error-in-hlist_for_each_entry_safe-macro.patch b/SOURCES/seabios-Fix-error-in-hlist_for_each_entry_safe-macro.patch
new file mode 100644
index 0000000..3860efb
--- /dev/null
+++ b/SOURCES/seabios-Fix-error-in-hlist_for_each_entry_safe-macro.patch
@@ -0,0 +1,46 @@
+From 8cd6a54fc05d726cc84c6c5b3dc551d208d1ddc1 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 2 Oct 2013 07:00:14 +0200
+Subject: [PATCH 2/8] Fix error in hlist_for_each_entry_safe macro.
+
+RH-Author: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: <1380697219-1860-3-git-send-email-kraxel@redhat.com>
+Patchwork-id: 54629
+O-Subject: [RHEL-7 seabios PATCH 2/7] Fix error in hlist_for_each_entry_safe macro.
+Bugzilla: 947051
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+
+From: Kevin O'Connor <kevin@koconnor.net>
+
+Fix broken macro - it did not work correctly at all.
+
+Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
+(cherry picked from commit 9539e41c4d8701c19434457288c38109da163ffc)
+---
+ src/list.h | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/list.h |    3 +--
+ 1 files changed, 1 insertions(+), 2 deletions(-)
+
+diff --git a/src/list.h b/src/list.h
+index db7e962..0f0909b 100644
+--- a/src/list.h
++++ b/src/list.h
+@@ -68,8 +68,7 @@ hlist_add_after(struct hlist_node *n, struct hlist_node *prev)
+ 
+ #define hlist_for_each_entry_safe(pos, pprev, head, member)             \
+     for (pprev = &(head)->first                                         \
+-         ; *pprev                                                       \
+-           && ({ pos=container_of((*pprev)->next, typeof(*pos), member); 1; }) \
++         ; *pprev && ({ pos=container_of(*pprev, typeof(*pos), member); 1; }) \
+          ; pprev = &(*pprev)->next)
+ 
+ 
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-Introduce-and-convert-pmm-code-to-use-standard-list-.patch b/SOURCES/seabios-Introduce-and-convert-pmm-code-to-use-standard-list-.patch
new file mode 100644
index 0000000..f8a8246
--- /dev/null
+++ b/SOURCES/seabios-Introduce-and-convert-pmm-code-to-use-standard-list-.patch
@@ -0,0 +1,135 @@
+From cd24f3be4bcb388fd6b4ed48fc9a076ef0e1cee9 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 2 Oct 2013 07:00:13 +0200
+Subject: [PATCH 1/8] Introduce and convert pmm code to use standard list helpers.
+
+RH-Author: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: <1380697219-1860-2-git-send-email-kraxel@redhat.com>
+Patchwork-id: 54628
+O-Subject: [RHEL-7 seabios PATCH 1/7] Introduce and convert pmm code to use standard list helpers.
+Bugzilla: 947051
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+
+From: Kevin O'Connor <kevin@koconnor.net>
+
+Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
+(cherry picked from commit e097a75ef0de08ad6d8660c41efe10c1133f1865)
+
+[ rhel6: skip ppm.c changes ]
+
+Conflicts:
+	src/pmm.c
+---
+ src/list.h  | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/types.h |  3 +++
+ 2 files changed, 79 insertions(+)
+ create mode 100644 src/list.h
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/list.h  |   76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/types.h |    3 ++
+ 2 files changed, 79 insertions(+), 0 deletions(-)
+ create mode 100644 src/list.h
+
+diff --git a/src/list.h b/src/list.h
+new file mode 100644
+index 0000000..db7e962
+--- /dev/null
++++ b/src/list.h
+@@ -0,0 +1,76 @@
++#ifndef __LIST_H
++#define __LIST_H
++
++#include "types.h" // container_of
++
++
++/****************************************************************
++ * hlist - Double linked lists with a single pointer list head
++ ****************************************************************/
++
++struct hlist_node {
++    struct hlist_node *next, **pprev;
++};
++
++struct hlist_head {
++    struct hlist_node *first;
++};
++
++static inline int
++hlist_empty(const struct hlist_head *h)
++{
++    return !h->first;
++}
++
++static inline void
++hlist_del(struct hlist_node *n)
++{
++    struct hlist_node *next = n->next;
++    struct hlist_node **pprev = n->pprev;
++    *pprev = next;
++    if (next)
++        next->pprev = pprev;
++}
++
++static inline void
++hlist_add(struct hlist_node *n, struct hlist_node **pprev)
++{
++    struct hlist_node *next = *pprev;
++    n->pprev = pprev;
++    n->next = next;
++    if (next)
++        next->pprev = &n->next;
++    *pprev = n;
++}
++
++static inline void
++hlist_add_head(struct hlist_node *n, struct hlist_head *h)
++{
++    hlist_add(n, &h->first);
++}
++
++static inline void
++hlist_add_before(struct hlist_node *n, struct hlist_node *next)
++{
++    hlist_add(n, next->pprev);
++}
++
++static inline void
++hlist_add_after(struct hlist_node *n, struct hlist_node *prev)
++{
++    hlist_add(n, &prev->next);
++}
++
++#define hlist_for_each_entry(pos, head, member)                         \
++    for (pos = container_of((head)->first, typeof(*pos), member)        \
++         ; pos != container_of(NULL, typeof(*pos), member)              \
++         ; pos = container_of(pos->member.next, typeof(*pos), member))
++
++#define hlist_for_each_entry_safe(pos, pprev, head, member)             \
++    for (pprev = &(head)->first                                         \
++         ; *pprev                                                       \
++           && ({ pos=container_of((*pprev)->next, typeof(*pos), member); 1; }) \
++         ; pprev = &(*pprev)->next)
++
++
++#endif // list.h
+diff --git a/src/types.h b/src/types.h
+index 24b078e..e5ab4bf 100644
+--- a/src/types.h
++++ b/src/types.h
+@@ -121,6 +121,9 @@ extern void __force_link_error__only_in_16bit(void) __noreturn;
+ #define container_of(ptr, type, member) ({                      \
+         const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+         (type *)( (char *)__mptr - offsetof(type,member) );})
++#define container_of_or_null(ptr, type, member) ({              \
++        const typeof( ((type *)0)->member ) *___mptr = (ptr);   \
++        ___mptr ? container_of(___mptr, type, member) : NULL; })
+ 
+ #define likely(x)       __builtin_expect(!!(x), 1)
+ #define unlikely(x)     __builtin_expect(!!(x), 0)
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-Place-rpm-version-info-into-version-banner.patch b/SOURCES/seabios-Place-rpm-version-info-into-version-banner.patch
new file mode 100644
index 0000000..3eeb0bb
--- /dev/null
+++ b/SOURCES/seabios-Place-rpm-version-info-into-version-banner.patch
@@ -0,0 +1,54 @@
+From 25c134c1c2e5966ed9ba604fb73e4c828131146f Mon Sep 17 00:00:00 2001
+From: Miroslav Rezanina <mrezanin@redhat.com>
+Date: Thu, 22 Aug 2013 12:06:40 +0200
+Subject: [PATCH 4/5] Place rpm version info into version banner
+
+RH-Author: Miroslav Rezanina <mrezanin@redhat.com>
+Message-id: <1377173200-17433-1-git-send-email-mrezanin@redhat.com>
+Patchwork-id: 53692
+O-Subject: [RHEL7 seabios PATCH] Place rpm version info into version banner
+Bugzilla: 894979
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Michal Novotny <minovotn@redhat.com>
+
+From: Miroslav Rezanina <mrezanin@redhat.com>
+
+Bugzilla: 894979
+Brew: http://brewweb.devel.redhat.com/brew/taskinfo?taskID=6199274
+
+This patch introduces two changes:
+
+1) Do not append date and hostname to version string.
+
+2) Use rpm version insted of git commit as the version string.
+
+Both changes are trivial so they are not posted in separate patches.
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ redhat/seabios.spec.template | 5 ++---
+ tools/buildversion.sh        | 1 -
+ 2 files changed, 2 insertions(+), 4 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ redhat/seabios.spec.template |    5 ++---
+ tools/buildversion.sh        |    1 -
+ 2 files changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/tools/buildversion.sh b/tools/buildversion.sh
+index c8c1725..395e6d6 100755
+--- a/tools/buildversion.sh
++++ b/tools/buildversion.sh
+@@ -11,7 +11,6 @@ elif [ -f .version ]; then
+ else
+     VERSION="?"
+ fi
+-VERSION="${VERSION}-`date +"%Y%m%d_%H%M%S"`-`hostname`"
+ echo "Version: ${VERSION}"
+ 
+ # Build header file
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-ahci-add-missing-check-for-allocation-failure.patch b/SOURCES/seabios-ahci-add-missing-check-for-allocation-failure.patch
new file mode 100644
index 0000000..c83356c
--- /dev/null
+++ b/SOURCES/seabios-ahci-add-missing-check-for-allocation-failure.patch
@@ -0,0 +1,87 @@
+From 48c296e7b9851c07ef277d7d37a05e20da3ef76d Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 17 Sep 2013 09:42:30 +0200
+Subject: [PATCH 5/5] ahci: add missing check for allocation failure
+
+RH-Author: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: <1379410950-22494-2-git-send-email-kraxel@redhat.com>
+Patchwork-id: 54411
+O-Subject: [RHEL-7 seabios PATCH 1/1] ahci: add missing check for allocation failure
+Bugzilla: 1005747
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
+
+Triggerable by creating a virtual machine with
+*lots* of ahci controllers and disks.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+(cherry picked from commit ce12eaf2044d6aae08795403ecbb888d2b6527ff)
+---
+ src/ahci.c | 25 ++++++++++++++++---------
+ 1 file changed, 16 insertions(+), 9 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/ahci.c |   25 ++++++++++++++++---------
+ 1 files changed, 16 insertions(+), 9 deletions(-)
+
+diff --git a/src/ahci.c b/src/ahci.c
+index 4a8eafb..056d894 100644
+--- a/src/ahci.c
++++ b/src/ahci.c
+@@ -381,12 +381,26 @@ ahci_port_alloc(struct ahci_ctrl_s *ctrl, u32 pnr)
+     return port;
+ }
+ 
++static void ahci_port_release(struct ahci_port_s *port)
++{
++    ahci_port_reset(port->ctrl, port->pnr);
++    free(port->list);
++    free(port->fis);
++    free(port->cmd);
++    free(port);
++}
++
+ static struct ahci_port_s* ahci_port_realloc(struct ahci_port_s *port)
+ {
+     struct ahci_port_s *tmp;
+     u32 cmd;
+ 
+     tmp = malloc_fseg(sizeof(*port));
++    if (!tmp) {
++        warn_noalloc();
++        ahci_port_release(port);
++        return NULL;
++    }
+     *tmp = *port;
+     free(port);
+     port = tmp;
+@@ -410,15 +424,6 @@ static struct ahci_port_s* ahci_port_realloc(struct ahci_port_s *port)
+     return port;
+ }
+ 
+-static void ahci_port_release(struct ahci_port_s *port)
+-{
+-    ahci_port_reset(port->ctrl, port->pnr);
+-    free(port->list);
+-    free(port->fis);
+-    free(port->cmd);
+-    free(port);
+-}
+-
+ #define MAXMODEL 40
+ 
+ /* See ahci spec chapter 10.1 "Software Initialization of HBA" */
+@@ -554,6 +559,8 @@ ahci_port_detect(void *data)
+         ahci_port_release(port);
+     else {
+         port = ahci_port_realloc(port);
++        if (port == NULL)
++            return;
+         dprintf(1, "AHCI/%d: registering: \"%s\"\n", port->pnr, port->desc);
+         if (!port->atapi) {
+             // Register with bcv system.
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-allow-1TB-of-RAM.patch b/SOURCES/seabios-allow-1TB-of-RAM.patch
new file mode 100644
index 0000000..017b051
--- /dev/null
+++ b/SOURCES/seabios-allow-1TB-of-RAM.patch
@@ -0,0 +1,67 @@
+From 90414ef38b1f32b5b844e1fc27d30bd6d229f5de Mon Sep 17 00:00:00 2001
+From: Andrea Arcangeli <aarcange@redhat.com>
+Date: Tue, 8 Oct 2013 17:07:23 +0200
+Subject: [PATCH 8/8] allow >1TB of RAM
+
+RH-Author: Andrea Arcangeli <aarcange@redhat.com>
+Message-id: <1381252043-13480-2-git-send-email-aarcange@redhat.com>
+Patchwork-id: 54785
+O-Subject: [RHEL-7.0 seabios PATCH] allow >1TB of RAM
+Bugzilla: 1016974
+RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
+RH-Acked-by: Gleb Natapov <gleb@redhat.com>
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+
+Receive bits 40-48 from qemu to setup e820 maps with more than 1TB of ram.
+
+Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
+---
+ src/cmos.h | 7 ++++---
+ src/post.c | 7 ++++---
+ 2 files changed, 8 insertions(+), 6 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/cmos.h |    7 ++++---
+ src/post.c |    7 ++++---
+ 2 files changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/src/cmos.h b/src/cmos.h
+index e4b6462..e810534 100644
+--- a/src/cmos.h
++++ b/src/cmos.h
+@@ -36,9 +36,10 @@
+ #define CMOS_BIOS_BOOTFLAG1      0x38
+ #define CMOS_BIOS_DISKTRANSFLAG  0x39
+ #define CMOS_BIOS_BOOTFLAG2      0x3d
+-#define CMOS_MEM_HIGHMEM_LOW     0x5b
+-#define CMOS_MEM_HIGHMEM_MID     0x5c
+-#define CMOS_MEM_HIGHMEM_HIGH    0x5d
++#define CMOS_MEM_HIGHMEM_16      0x5b
++#define CMOS_MEM_HIGHMEM_24      0x5c
++#define CMOS_MEM_HIGHMEM_32      0x5d
++#define CMOS_MEM_HIGHMEM_40      0x5e
+ #define CMOS_BIOS_SMP_COUNT      0x5f
+ 
+ // CMOS_FLOPPY_DRIVE_TYPE bitdefs
+diff --git a/src/post.c b/src/post.c
+index f3b56b8..737c16d 100644
+--- a/src/post.c
++++ b/src/post.c
+@@ -122,9 +122,10 @@ ram_probe(void)
+         add_e820(0, rs, E820_RAM);
+ 
+         // Check for memory over 4Gig
+-        u64 high = ((inb_cmos(CMOS_MEM_HIGHMEM_LOW) << 16)
+-                    | ((u32)inb_cmos(CMOS_MEM_HIGHMEM_MID) << 24)
+-                    | ((u64)inb_cmos(CMOS_MEM_HIGHMEM_HIGH) << 32));
++        u64 high = ((inb_cmos(CMOS_MEM_HIGHMEM_16) << 16)
++                    | ((u32)inb_cmos(CMOS_MEM_HIGHMEM_24) << 24)
++                    | ((u64)inb_cmos(CMOS_MEM_HIGHMEM_32) << 32)
++		    | ((u64)inb_cmos(CMOS_MEM_HIGHMEM_40) << 40));
+         RamSizeOver4G = high;
+         add_e820(0x100000000ull, high, E820_RAM);
+ 
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-floppy-Cleanup-floppy-irq-wait-handling.patch b/SOURCES/seabios-floppy-Cleanup-floppy-irq-wait-handling.patch
new file mode 100644
index 0000000..bc4e9c2
--- /dev/null
+++ b/SOURCES/seabios-floppy-Cleanup-floppy-irq-wait-handling.patch
@@ -0,0 +1,203 @@
+From 46ceee9e43c842627ac0f684e1d9d6244edb2a10 Mon Sep 17 00:00:00 2001
+From: Fam Zheng <famz@redhat.com>
+Date: Thu, 22 Aug 2013 02:15:58 +0200
+Subject: [PATCH 2/5] floppy: Cleanup floppy irq wait handling.
+
+RH-Author: Fam Zheng <famz@redhat.com>
+Message-id: <1377137759-16089-3-git-send-email-famz@redhat.com>
+Patchwork-id: 53680
+O-Subject: [RHEL-7 seabios PATCH 2/3] floppy: Cleanup floppy irq wait handling.
+Bugzilla: 920140
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
+RH-Acked-by: Markus Armbruster <armbru@redhat.com>
+
+From: Kevin O'Connor <kevin@koconnor.net>
+
+Rename FRS_TIMEOUT to FRS_IRQ - the flag indicates that an irq has
+been received - it isn't directly related to timeouts.
+
+On a timeout event, disable the floppy controller instead of doing a
+full reset.  Also, perform the disable directly in floppy_wait_irq().
+
+Always wait for the floppy irq after enabling the controller and after
+a recalibrate command.
+
+Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
+(cherry picked from commit 6e529bdae4922f48c0d7eaa31613b7a9230e8f95)
+Signed-off-by: Fam Zheng <famz@redhat.com>
+---
+ src/biosvar.h |  2 +-
+ src/floppy.c  | 88 +++++++++++++++++++++++++++++------------------------------
+ 2 files changed, 45 insertions(+), 45 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/biosvar.h |    2 +-
+ src/floppy.c  |   88 ++++++++++++++++++++++++++++----------------------------
+ 2 files changed, 45 insertions(+), 45 deletions(-)
+
+diff --git a/src/biosvar.h b/src/biosvar.h
+index f0a0fd2..252d4d1 100644
+--- a/src/biosvar.h
++++ b/src/biosvar.h
+@@ -120,7 +120,7 @@ struct bios_data_area_s {
+ } PACKED;
+ 
+ // BDA floppy_recalibration_status bitdefs
+-#define FRS_TIMEOUT (1<<7)
++#define FRS_IRQ (1<<7)
+ 
+ // BDA rtc_wait_flag bitdefs
+ #define RWS_WAIT_PENDING (1<<0)
+diff --git a/src/floppy.c b/src/floppy.c
+index 458bb7a..429ccb5 100644
+--- a/src/floppy.c
++++ b/src/floppy.c
+@@ -164,63 +164,59 @@ find_floppy_type(u32 size)
+  ****************************************************************/
+ 
+ static void
+-floppy_reset_controller(void)
++floppy_disable_controller(void)
+ {
+-    // Reset controller
+-    u8 val8 = inb(PORT_FD_DOR);
+-    outb(val8 & ~0x04, PORT_FD_DOR);
+-    outb(val8 | 0x04, PORT_FD_DOR);
+-
+-    // Wait for controller to come out of reset
+-    while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
+-        ;
++    outb(inb(PORT_FD_DOR) & ~0x04, PORT_FD_DOR);
+ }
+ 
+ static int
+-wait_floppy_irq(void)
++floppy_wait_irq(void)
+ {
+-    ASSERT16();
+-    u8 frs;
++    u8 frs = GET_BDA(floppy_recalibration_status);
++    SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
+     for (;;) {
+-        if (!GET_BDA(floppy_motor_counter))
+-            return -1;
++        if (!GET_BDA(floppy_motor_counter)) {
++            floppy_disable_controller();
++            return DISK_RET_ETIMEOUT;
++        }
+         frs = GET_BDA(floppy_recalibration_status);
+-        if (frs & FRS_TIMEOUT)
++        if (frs & FRS_IRQ)
+             break;
+         // Could use yield_toirq() here, but that causes issues on
+         // bochs, so use yield() instead.
+         yield();
+     }
+ 
+-    frs &= ~FRS_TIMEOUT;
+-    SET_BDA(floppy_recalibration_status, frs);
+-    return 0;
++    SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
++    return DISK_RET_SUCCESS;
+ }
+ 
+-static void
+-floppy_prepare_controller(u8 floppyid)
++static int
++floppy_enable_controller(void)
+ {
+-    u8 frs = GET_BDA(floppy_recalibration_status);
+-    SET_BDA(floppy_recalibration_status, frs & ~FRS_TIMEOUT);
+-
+-    // turn on motor of selected drive, DMA & int enabled, normal operation
+-    u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
+-    u8 dor = 0x10;
+-    if (floppyid)
+-        dor = 0x20;
+-    dor |= 0x0c;
+-    dor |= floppyid;
+-    outb(dor, PORT_FD_DOR);
++    outb(inb(PORT_FD_DOR) | 0x04, PORT_FD_DOR);
++    return floppy_wait_irq();
++}
+ 
++static int
++floppy_select_drive(u8 floppyid)
++{
+     // reset the disk motor timeout value of INT 08
+     SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
+ 
+-    // wait for drive readiness
+-    while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
+-        ;
++    // Enable controller if it isn't running.
++    u8 dor = inb(PORT_FD_DOR);
++    if (!(dor & 0x04)) {
++        int ret = floppy_enable_controller();
++        if (ret)
++            return ret;
++    }
+ 
+-    if (!prev_reset)
+-        wait_floppy_irq();
++    // Turn on motor of selected drive, DMA & int enabled, normal operation
++    dor = (floppyid ? 0x20 : 0x10) | 0x0c | floppyid;
++    outb(dor, PORT_FD_DOR);
++
++    return DISK_RET_SUCCESS;
+ }
+ 
+ struct floppy_pio_s {
+@@ -233,7 +229,13 @@ struct floppy_pio_s {
+ static int
+ floppy_pio(struct floppy_pio_s *pio)
+ {
+-    floppy_prepare_controller(pio->data[1] & 1);
++    int ret = floppy_select_drive(pio->data[1] & 1);
++    if (ret)
++        return ret;
++
++    // wait for drive readiness
++    while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
++        ;
+ 
+     // send command to controller
+     int i;
+@@ -241,11 +243,9 @@ floppy_pio(struct floppy_pio_s *pio)
+         outb(pio->data[i], PORT_FD_DATA);
+ 
+     if (pio->waitirq) {
+-        int ret = wait_floppy_irq();
+-        if (ret) {
+-            floppy_reset_controller();
+-            return DISK_RET_ETIMEOUT;
+-        }
++        int ret = floppy_wait_irq();
++        if (ret)
++            return ret;
+     }
+ 
+     if (!pio->resplen)
+@@ -330,7 +330,7 @@ floppy_drive_recal(u8 floppyid)
+     struct floppy_pio_s pio;
+     pio.cmdlen = 2;
+     pio.resplen = 0;
+-    pio.waitirq = 0;
++    pio.waitirq = 1;
+     pio.data[0] = 0x07;  // 07: Recalibrate
+     pio.data[1] = floppyid; // 0=drive0, 1=drive1
+     floppy_pio(&pio);
+@@ -617,7 +617,7 @@ handle_0e(void)
+     }
+     // diskette interrupt has occurred
+     u8 frs = GET_BDA(floppy_recalibration_status);
+-    SET_BDA(floppy_recalibration_status, frs | FRS_TIMEOUT);
++    SET_BDA(floppy_recalibration_status, frs | FRS_IRQ);
+ 
+     eoi_pic1();
+ }
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-floppy-Implement-media-format-sensing.patch b/SOURCES/seabios-floppy-Implement-media-format-sensing.patch
new file mode 100644
index 0000000..a0acc37
--- /dev/null
+++ b/SOURCES/seabios-floppy-Implement-media-format-sensing.patch
@@ -0,0 +1,199 @@
+From 51d219681f4e073bbaf0a6e540f42447f1a18edf Mon Sep 17 00:00:00 2001
+From: Fam Zheng <famz@redhat.com>
+Date: Thu, 22 Aug 2013 02:15:59 +0200
+Subject: [PATCH 3/5] floppy: Implement media format sensing.
+
+RH-Author: Fam Zheng <famz@redhat.com>
+Message-id: <1377137759-16089-4-git-send-email-famz@redhat.com>
+Patchwork-id: 53681
+O-Subject: [RHEL-7 seabios PATCH 3/3] floppy: Implement media format sensing.
+Bugzilla: 920140
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
+RH-Acked-by: Markus Armbruster <armbru@redhat.com>
+
+From: Kevin O'Connor <kevin@koconnor.net>
+
+Check for lower capacity media in the floppy drive and set the
+corresponding controller data rate for it.
+
+Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
+(cherry picked from commit e7c5a7ef782673f00faf3371721562806e931cdb)
+Signed-off-by: Fam Zheng <famz@redhat.com>
+
+Conflicts:
+	src/floppy.c
+            Context conflict. Upstream 89a2f96d converted VAR16VISIBLE
+            to VARFSEG, which we don't have yet. So keep the original
+            type.
+
+Signed-off-by: Fam Zheng <famz@redhat.com>
+---
+ src/floppy.c | 123 +++++++++++++++++++++++++++++++++++++----------------------
+ 1 file changed, 78 insertions(+), 45 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/floppy.c |  123 ++++++++++++++++++++++++++++++++++++---------------------
+ 1 files changed, 78 insertions(+), 45 deletions(-)
+
+diff --git a/src/floppy.c b/src/floppy.c
+index 429ccb5..f00349d 100644
+--- a/src/floppy.c
++++ b/src/floppy.c
+@@ -51,29 +51,37 @@ struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7);
+ 
+ struct floppyinfo_s {
+     struct chs_s chs;
+-    u8 config_data;
+-    u8 media_state;
++    u8 floppy_size;
++    u8 data_rate;
+ };
+ 
++#define FLOPPY_SIZE_525 0x01
++#define FLOPPY_SIZE_350 0x02
++
++#define FLOPPY_RATE_500K 0x00
++#define FLOPPY_RATE_300K 0x01
++#define FLOPPY_RATE_250K 0x02
++#define FLOPPY_RATE_1M   0x03
++
+ struct floppyinfo_s FloppyInfo[] VAR16VISIBLE = {
+     // Unknown
+     { {0, 0, 0}, 0x00, 0x00},
+     // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
+-    { {2, 40, 9}, 0x00, 0x25},
++    { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
+     // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
+-    { {2, 80, 15}, 0x00, 0x25},
++    { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K},
+     // 3 - 720KB, 3.5"  - 2 heads, 80 tracks, 9 sectors
+-    { {2, 80, 9}, 0x00, 0x17},
++    { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K},
+     // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
+-    { {2, 80, 18}, 0x00, 0x17},
++    { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K},
+     // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
+-    { {2, 80, 36}, 0xCC, 0xD7},
++    { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M},
+     // 6 - 160k, 5.25"  - 1 heads, 40 tracks, 8 sectors
+-    { {1, 40, 8}, 0x00, 0x27},
++    { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
+     // 7 - 180k, 5.25"  - 1 heads, 40 tracks, 9 sectors
+-    { {1, 40, 9}, 0x00, 0x27},
++    { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
+     // 8 - 320k, 5.25"  - 2 heads, 40 tracks, 8 sectors
+-    { {2, 40, 8}, 0x00, 0x27},
++    { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
+ };
+ 
+ struct drive_s *
+@@ -341,44 +349,69 @@ floppy_drive_recal(u8 floppyid)
+ }
+ 
+ static int
++floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head)
++{
++    int ret = floppy_select_drive(floppyid);
++    if (ret)
++        return ret;
++
++    // Set data rate.
++    outb(data_rate, PORT_FD_DIR);
++
++    // send Read Sector Id command
++    struct floppy_pio_s pio;
++    pio.cmdlen = 2;
++    pio.resplen = 7;
++    pio.waitirq = 1;
++    pio.data[0] = 0x4a;  // 0a: Read Sector Id
++    pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
++    ret = floppy_pio(&pio);
++    if (ret)
++        return ret;
++    if (pio.data[0] & 0xc0)
++        return -1;
++    return 0;
++}
++
++static int
+ floppy_media_sense(struct drive_s *drive_g)
+ {
+-    // for now cheat and get drive type from CMOS,
+-    // assume media is same as drive type
+-
+-    // ** config_data **
+-    // Bitfields for diskette media control:
+-    // Bit(s)  Description (Table M0028)
+-    //  7-6  last data rate set by controller
+-    //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+-    //  5-4  last diskette drive step rate selected
+-    //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
+-    //  3-2  {data rate at start of operation}
+-    //  1-0  reserved
+-
+-    // ** media_state **
+-    // Bitfields for diskette drive media state:
+-    // Bit(s)  Description (Table M0030)
+-    //  7-6  data rate
+-    //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+-    //  5  double stepping required (e.g. 360kB in 1.2MB)
+-    //  4  media type established
+-    //  3  drive capable of supporting 4MB media
+-    //  2-0  on exit from BIOS, contains
+-    //    000 trying 360kB in 360kB
+-    //    001 trying 360kB in 1.2MB
+-    //    010 trying 1.2MB in 1.2MB
+-    //    011 360kB in 360kB established
+-    //    100 360kB in 1.2MB established
+-    //    101 1.2MB in 1.2MB established
+-    //    110 reserved
+-    //    111 all other formats/drives
+-
+-    u8 ftype = GET_GLOBAL(drive_g->floppy_type);
+-    SET_BDA(floppy_last_data_rate, GET_GLOBAL(FloppyInfo[ftype].config_data));
++    u8 ftype = GET_GLOBAL(drive_g->floppy_type), stype = ftype;
+     u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
+-    SET_BDA(floppy_media_state[floppyid]
+-            , GET_GLOBAL(FloppyInfo[ftype].media_state));
++
++    u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
++    int ret = floppy_drive_readid(floppyid, data_rate, 0);
++    if (ret) {
++        // Attempt media sense.
++        for (stype=1; ; stype++) {
++            if (stype >= ARRAY_SIZE(FloppyInfo))
++                return DISK_RET_EMEDIA;
++            if (stype==ftype
++                || (GET_GLOBAL(FloppyInfo[stype].floppy_size)
++                    != GET_GLOBAL(FloppyInfo[ftype].floppy_size))
++                || (GET_GLOBAL(FloppyInfo[stype].chs.heads)
++                    > GET_GLOBAL(FloppyInfo[ftype].chs.heads))
++                || (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
++                    > GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
++                || (GET_GLOBAL(FloppyInfo[stype].chs.spt)
++                    > GET_GLOBAL(FloppyInfo[ftype].chs.spt)))
++                continue;
++            data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
++            ret = floppy_drive_readid(floppyid, data_rate, 0);
++            if (!ret)
++                break;
++        }
++    }
++
++    u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6;
++    SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6));
++    u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07));
++    u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media;
++    if (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
++        < GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
++        fms |= FMS_DOUBLE_STEPPING;
++    SET_BDA(floppy_media_state[floppyid], fms);
++
+     return DISK_RET_SUCCESS;
+ }
+ 
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-floppy-Introduce-struct-floppy_pio_s-for-floppy-PIO-.patch b/SOURCES/seabios-floppy-Introduce-struct-floppy_pio_s-for-floppy-PIO-.patch
new file mode 100644
index 0000000..d3aec63
--- /dev/null
+++ b/SOURCES/seabios-floppy-Introduce-struct-floppy_pio_s-for-floppy-PIO-.patch
@@ -0,0 +1,265 @@
+From 946dd96997625598fd814500e5fbfd457c6d670d Mon Sep 17 00:00:00 2001
+From: Fam Zheng <famz@redhat.com>
+Date: Thu, 22 Aug 2013 02:15:57 +0200
+Subject: [PATCH 1/5] floppy: Introduce 'struct floppy_pio_s' for floppy PIO ops.
+
+RH-Author: Fam Zheng <famz@redhat.com>
+Message-id: <1377137759-16089-2-git-send-email-famz@redhat.com>
+Patchwork-id: 53679
+O-Subject: [RHEL-7 seabios PATCH 1/3] floppy: Introduce 'struct floppy_pio_s' for floppy PIO ops.
+Bugzilla: 920140
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
+RH-Acked-by: Markus Armbruster <armbru@redhat.com>
+
+From: Kevin O'Connor <kevin@koconnor.net>
+
+Populate a struct for the PIO operations and move the PIO manipulation
+done in floppy_cmd() to floppy_pio().
+
+Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
+(cherry picked from commit 9ba374ce1d864b7a50f3657e6b06e7b2d1ec2934)
+Signed-off-by: Fam Zheng <famz@redhat.com>
+---
+ src/floppy.c | 158 ++++++++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 90 insertions(+), 68 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/floppy.c |  158 +++++++++++++++++++++++++++++++++-------------------------
+ 1 files changed, 90 insertions(+), 68 deletions(-)
+
+diff --git a/src/floppy.c b/src/floppy.c
+index e9f8916..458bb7a 100644
+--- a/src/floppy.c
++++ b/src/floppy.c
+@@ -223,27 +223,48 @@ floppy_prepare_controller(u8 floppyid)
+         wait_floppy_irq();
+ }
+ 
++struct floppy_pio_s {
++    u8 cmdlen;
++    u8 resplen;
++    u8 waitirq;
++    u8 data[9];
++};
++
+ static int
+-floppy_pio(u8 *cmd, u8 cmdlen)
++floppy_pio(struct floppy_pio_s *pio)
+ {
+-    floppy_prepare_controller(cmd[1] & 1);
++    floppy_prepare_controller(pio->data[1] & 1);
+ 
+     // send command to controller
+-    u8 i;
+-    for (i=0; i<cmdlen; i++)
+-        outb(cmd[i], PORT_FD_DATA);
+-
+-    int ret = wait_floppy_irq();
+-    if (ret) {
+-        floppy_reset_controller();
+-        return -1;
++    int i;
++    for (i=0; i<pio->cmdlen; i++)
++        outb(pio->data[i], PORT_FD_DATA);
++
++    if (pio->waitirq) {
++        int ret = wait_floppy_irq();
++        if (ret) {
++            floppy_reset_controller();
++            return DISK_RET_ETIMEOUT;
++        }
+     }
+ 
+-    return 0;
++    if (!pio->resplen)
++        return DISK_RET_SUCCESS;
++
++    // check port 3f4 for accessibility to status bytes
++    if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
++        return DISK_RET_ECONTROLLER;
++
++    // read return status bytes from controller
++    for (i=0; i<pio->resplen; i++)
++        pio->data[i] = inb(PORT_FD_DATA);
++
++    return DISK_RET_SUCCESS;
+ }
+ 
++// Perform a floppy transfer command (setup DMA and issue PIO).
+ static int
+-floppy_cmd(struct disk_op_s *op, u16 count, u8 *cmd, u8 cmdlen)
++floppy_cmd(struct disk_op_s *op, int count, struct floppy_pio_s *pio)
+ {
+     // es:bx = pointer to where to place information from diskette
+     u32 addr = (u32)op->buf_fl;
+@@ -255,7 +276,7 @@ floppy_cmd(struct disk_op_s *op, u16 count, u8 *cmd, u8 cmdlen)
+         return DISK_RET_EBOUNDARY;
+ 
+     u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
+-    if (cmd[0] == 0xe6)
++    if (pio->data[0] == 0xe6)
+         // read
+         mode_register = 0x46;
+ 
+@@ -277,21 +298,16 @@ floppy_cmd(struct disk_op_s *op, u16 count, u8 *cmd, u8 cmdlen)
+ 
+     outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
+ 
+-    int ret = floppy_pio(cmd, cmdlen);
++    pio->resplen = 7;
++    pio->waitirq = 1;
++    int ret = floppy_pio(pio);
+     if (ret)
+-        return DISK_RET_ETIMEOUT;
+-
+-    // check port 3f4 for accessibility to status bytes
+-    if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
+-        return DISK_RET_ECONTROLLER;
++        return ret;
+ 
+-    // read 7 return status bytes from controller
+-    u8 i;
+-    for (i=0; i<7; i++) {
+-        u8 v = inb(PORT_FD_DATA);
+-        cmd[i] = v;
+-        SET_BDA(floppy_return_status[i], v);
+-    }
++    // Populate floppy_return_status in BDA
++    int i;
++    for (i=0; i<7; i++)
++        SET_BDA(floppy_return_status[i], pio->data[i]);
+ 
+     return DISK_RET_SUCCESS;
+ }
+@@ -311,10 +327,13 @@ static void
+ floppy_drive_recal(u8 floppyid)
+ {
+     // send Recalibrate command (2 bytes) to controller
+-    u8 data[12];
+-    data[0] = 0x07;  // 07: Recalibrate
+-    data[1] = floppyid; // 0=drive0, 1=drive1
+-    floppy_pio(data, 2);
++    struct floppy_pio_s pio;
++    pio.cmdlen = 2;
++    pio.resplen = 0;
++    pio.waitirq = 0;
++    pio.data[0] = 0x07;  // 07: Recalibrate
++    pio.data[1] = floppyid; // 0=drive0, 1=drive1
++    floppy_pio(&pio);
+ 
+     u8 frs = GET_BDA(floppy_recalibration_status);
+     SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
+@@ -423,22 +442,23 @@ floppy_read(struct disk_op_s *op)
+ 
+     // send read-normal-data command (9 bytes) to controller
+     u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
+-    u8 data[12];
+-    data[0] = 0xe6; // e6: read normal data
+-    data[1] = (head << 2) | floppyid; // HD DR1 DR2
+-    data[2] = track;
+-    data[3] = head;
+-    data[4] = sector;
+-    data[5] = FLOPPY_SIZE_CODE;
+-    data[6] = sector + op->count - 1; // last sector to read on track
+-    data[7] = FLOPPY_GAPLEN;
+-    data[8] = FLOPPY_DATALEN;
+-
+-    res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
++    struct floppy_pio_s pio;
++    pio.cmdlen = 9;
++    pio.data[0] = 0xe6; // e6: read normal data
++    pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
++    pio.data[2] = track;
++    pio.data[3] = head;
++    pio.data[4] = sector;
++    pio.data[5] = FLOPPY_SIZE_CODE;
++    pio.data[6] = sector + op->count - 1; // last sector to read on track
++    pio.data[7] = FLOPPY_GAPLEN;
++    pio.data[8] = FLOPPY_DATALEN;
++
++    res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, &pio);
+     if (res)
+         goto fail;
+ 
+-    if (data[0] & 0xc0) {
++    if (pio.data[0] & 0xc0) {
+         res = DISK_RET_ECONTROLLER;
+         goto fail;
+     }
+@@ -464,23 +484,24 @@ floppy_write(struct disk_op_s *op)
+ 
+     // send write-normal-data command (9 bytes) to controller
+     u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
+-    u8 data[12];
+-    data[0] = 0xc5; // c5: write normal data
+-    data[1] = (head << 2) | floppyid; // HD DR1 DR2
+-    data[2] = track;
+-    data[3] = head;
+-    data[4] = sector;
+-    data[5] = FLOPPY_SIZE_CODE;
+-    data[6] = sector + op->count - 1; // last sector to write on track
+-    data[7] = FLOPPY_GAPLEN;
+-    data[8] = FLOPPY_DATALEN;
+-
+-    res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
++    struct floppy_pio_s pio;
++    pio.cmdlen = 9;
++    pio.data[0] = 0xc5; // c5: write normal data
++    pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
++    pio.data[2] = track;
++    pio.data[3] = head;
++    pio.data[4] = sector;
++    pio.data[5] = FLOPPY_SIZE_CODE;
++    pio.data[6] = sector + op->count - 1; // last sector to write on track
++    pio.data[7] = FLOPPY_GAPLEN;
++    pio.data[8] = FLOPPY_DATALEN;
++
++    res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, &pio);
+     if (res)
+         goto fail;
+ 
+-    if (data[0] & 0xc0) {
+-        if (data[1] & 0x02)
++    if (pio.data[0] & 0xc0) {
++        if (pio.data[1] & 0x02)
+             res = DISK_RET_EWRITEPROTECT;
+         else
+             res = DISK_RET_ECONTROLLER;
+@@ -527,20 +548,21 @@ floppy_format(struct disk_op_s *op)
+ 
+     // send format-track command (6 bytes) to controller
+     u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
+-    u8 data[12];
+-    data[0] = 0x4d; // 4d: format track
+-    data[1] = (head << 2) | floppyid; // HD DR1 DR2
+-    data[2] = FLOPPY_SIZE_CODE;
+-    data[3] = op->count; // number of sectors per track
+-    data[4] = FLOPPY_FORMAT_GAPLEN;
+-    data[5] = FLOPPY_FILLBYTE;
+-
+-    ret = floppy_cmd(op, op->count * 4, data, 6);
++    struct floppy_pio_s pio;
++    pio.cmdlen = 6;
++    pio.data[0] = 0x4d; // 4d: format track
++    pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
++    pio.data[2] = FLOPPY_SIZE_CODE;
++    pio.data[3] = op->count; // number of sectors per track
++    pio.data[4] = FLOPPY_FORMAT_GAPLEN;
++    pio.data[5] = FLOPPY_FILLBYTE;
++
++    ret = floppy_cmd(op, op->count * 4, &pio);
+     if (ret)
+         return ret;
+ 
+-    if (data[0] & 0xc0) {
+-        if (data[1] & 0x02)
++    if (pio.data[0] & 0xc0) {
++        if (pio.data[1] & 0x02)
+             return DISK_RET_EWRITEPROTECT;
+         return DISK_RET_ECONTROLLER;
+     }
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-uas-add-temporary-superspeed-stopgap.patch b/SOURCES/seabios-uas-add-temporary-superspeed-stopgap.patch
new file mode 100644
index 0000000..91eb4c7
--- /dev/null
+++ b/SOURCES/seabios-uas-add-temporary-superspeed-stopgap.patch
@@ -0,0 +1,57 @@
+From 50c3f2680f910216492622858884acd5f1eba9d9 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 2 Oct 2013 07:00:16 +0200
+Subject: [PATCH 4/8] uas: add (temporary) superspeed stopgap
+
+RH-Author: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: <1380697219-1860-5-git-send-email-kraxel@redhat.com>
+Patchwork-id: 54630
+O-Subject: [RHEL-7 seabios PATCH 4/7] uas: add (temporary) superspeed stopgap
+Bugzilla: 947051
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+(cherry picked from commit ee9b84f61c55a67b477a019e937460be13ccbfa1)
+---
+ src/usb-uas.c | 4 ++++
+ src/usb.h     | 1 +
+ 2 files changed, 5 insertions(+)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/usb-uas.c |    4 ++++
+ src/usb.h     |    1 +
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/src/usb-uas.c b/src/usb-uas.c
+index be153ed..2133c0e 100644
+--- a/src/usb-uas.c
++++ b/src/usb-uas.c
+@@ -220,6 +220,10 @@ usb_uas_init(struct usbdevice_s *usbdev)
+         case USB_DT_ENDPOINT:
+             ep = (void*)desc;
+             break;
++        case USB_DT_ENDPOINT_COMPANION:
++            /* No support (yet) for usb3 streams */
++            dprintf(1, "Superspeed UAS devices not supported (yet)\n");
++            goto fail;
+         case 0x24:
+             switch (desc[2]) {
+             case UAS_PIPE_ID_COMMAND:
+diff --git a/src/usb.h b/src/usb.h
+index a43e829..d9eadd7 100644
+--- a/src/usb.h
++++ b/src/usb.h
+@@ -130,6 +130,7 @@ struct usb_ctrlrequest {
+ #define USB_DT_ENDPOINT                 0x05
+ #define USB_DT_DEVICE_QUALIFIER         0x06
+ #define USB_DT_OTHER_SPEED_CONFIG       0x07
++#define USB_DT_ENDPOINT_COMPANION       0x30
+ 
+ struct usb_device_descriptor {
+     u8  bLength;
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-usb-add-usb_update_pipe.patch b/SOURCES/seabios-usb-add-usb_update_pipe.patch
new file mode 100644
index 0000000..86f26da
--- /dev/null
+++ b/SOURCES/seabios-usb-add-usb_update_pipe.patch
@@ -0,0 +1,88 @@
+From 0966143d780d669d8208ab2ac77254fb20f779d2 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 2 Oct 2013 07:00:17 +0200
+Subject: [PATCH 5/8] usb: add usb_update_pipe()
+
+RH-Author: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: <1380697219-1860-6-git-send-email-kraxel@redhat.com>
+Patchwork-id: 54632
+O-Subject: [RHEL-7 seabios PATCH 5/7] usb: add usb_update_pipe()
+Bugzilla: 947051
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+
+Preparation for better xhci support: allows to notify host controllers
+instead of going through a free+alloc cycle.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+(cherry picked from commit 63cbab1628dc406ed5d765bd85bae3a1a525a2c0)
+---
+ src/usb.c | 22 +++++++++++++++++-----
+ 1 file changed, 17 insertions(+), 5 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/usb.c |   22 +++++++++++++++++-----
+ 1 files changed, 17 insertions(+), 5 deletions(-)
+
+diff --git a/src/usb.c b/src/usb.c
+index 421d0b8..3efa367 100644
+--- a/src/usb.c
++++ b/src/usb.c
+@@ -40,6 +40,18 @@ usb_alloc_pipe(struct usbdevice_s *usbdev
+     }
+ }
+ 
++// Update an pipe (used for control only)
++struct usb_pipe *
++usb_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe
++                , struct usb_endpoint_descriptor *epdesc)
++{
++    switch (usbdev->hub->cntl->type) {
++    default:
++        free_pipe(pipe);
++        return usb_alloc_pipe(usbdev, epdesc);
++    }
++}
++
+ // Send a message on a control pipe using the default control descriptor.
+ static int
+ send_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize
+@@ -258,15 +270,16 @@ usb_set_address(struct usbdevice_s *usbdev)
+     req.wIndex = 0;
+     req.wLength = 0;
+     int ret = send_default_control(usbdev->defpipe, &req, NULL);
+-    free_pipe(usbdev->defpipe);
+-    if (ret)
++    if (ret) {
++        free_pipe(usbdev->defpipe);
+         return -1;
++    }
+ 
+     msleep(USB_TIME_SETADDR_RECOVERY);
+ 
+     cntl->maxaddr++;
+     usbdev->devaddr = cntl->maxaddr;
+-    usbdev->defpipe = usb_alloc_pipe(usbdev, &epdesc);
++    usbdev->defpipe = usb_update_pipe(usbdev, usbdev->defpipe, &epdesc);
+     if (!usbdev->defpipe)
+         return -1;
+     return 0;
+@@ -290,12 +303,11 @@ configure_usb_device(struct usbdevice_s *usbdev)
+             , dinfo.bDeviceProtocol, dinfo.bMaxPacketSize0);
+     if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64)
+         return 0;
+-    free_pipe(usbdev->defpipe);
+     struct usb_endpoint_descriptor epdesc = {
+         .wMaxPacketSize = dinfo.bMaxPacketSize0,
+         .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+     };
+-    usbdev->defpipe = usb_alloc_pipe(usbdev, &epdesc);
++    usbdev->defpipe = usb_update_pipe(usbdev, usbdev->defpipe, &epdesc);
+     if (!usbdev->defpipe)
+         return -1;
+ 
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-usb-add-xhci-support.patch b/SOURCES/seabios-usb-add-xhci-support.patch
new file mode 100644
index 0000000..d047843
--- /dev/null
+++ b/SOURCES/seabios-usb-add-xhci-support.patch
@@ -0,0 +1,1472 @@
+From c1d503b5c244efa1daf29fa297314e7cc82175af Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 2 Oct 2013 07:00:18 +0200
+Subject: [PATCH 6/8] usb: add xhci support
+
+RH-Author: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: <1380697219-1860-7-git-send-email-kraxel@redhat.com>
+Patchwork-id: 54633
+O-Subject: [RHEL-7 seabios PATCH 6/7] usb: add xhci support
+Bugzilla: 947051
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+
+$subject says all.  Support for usb3 streams is not implemented yet,
+otherwise it is fully functional.  Tested all usb devices supported
+by qemu (keyboard, storage, usb hubs), except for usb attached scsi
+in usb3 mode (which needs streams).
+
+Tested on qemu only, tagged with QEMU_HARDWARE because of that.
+Testing with physical hardware to be done.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+(cherry picked from commit e144bb7af49ca8756b7222a75811f3b85b0bc1f5)
+
+Conflicts:
+	Makefile
+	src/usb.c
+---
+ Makefile       |    2 +-
+ src/Kconfig    |    6 +
+ src/pci_ids.h  |    1 +
+ src/usb-xhci.c | 1124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/usb-xhci.h |  144 ++++++++
+ src/usb.c      |   13 +
+ src/usb.h      |   16 +-
+ 7 files changed, 1298 insertions(+), 8 deletions(-)
+ create mode 100644 src/usb-xhci.c
+ create mode 100644 src/usb-xhci.h
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ Makefile       |    2 +-
+ src/Kconfig    |    6 +
+ src/pci_ids.h  |    1 +
+ src/usb-xhci.c | 1124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/usb-xhci.h |  144 ++++++++
+ src/usb.c      |   13 +
+ src/usb.h      |   16 +-
+ 7 files changed, 1298 insertions(+), 8 deletions(-)
+ create mode 100644 src/usb-xhci.c
+ create mode 100644 src/usb-xhci.h
+
+diff --git a/Makefile b/Makefile
+index 20da6d0..409aefa 100644
+--- a/Makefile
++++ b/Makefile
+@@ -11,7 +11,7 @@ OUT=out/
+ SRCBOTH=misc.c stacks.c pmm.c output.c util.c block.c floppy.c ata.c mouse.c \
+     kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \
+     pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \
+-    usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \
++    usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-xhci.c usb-hid.c usb-msc.c \
+     virtio-ring.c virtio-pci.c virtio-blk.c virtio-scsi.c apm.c ahci.c \
+     usb-uas.c lsi-scsi.c esp-scsi.c megasas.c
+ SRC16=$(SRCBOTH) system.c disk.c font.c
+diff --git a/src/Kconfig b/src/Kconfig
+index 2c9100d..b059a73 100644
+--- a/src/Kconfig
++++ b/src/Kconfig
+@@ -184,6 +184,12 @@ menu "Hardware support"
+         default y
+         help
+             Support USB EHCI controllers.
++    config USB_XHCI
++        depends on USB && QEMU_HARDWARE
++        bool "USB XHCI controllers"
++        default y
++        help
++            Support USB XHCI controllers.
+     config USB_MSC
+         depends on USB && DRIVES
+         bool "USB drives"
+diff --git a/src/pci_ids.h b/src/pci_ids.h
+index 665e945..322d156 100644
+--- a/src/pci_ids.h
++++ b/src/pci_ids.h
+@@ -104,6 +104,7 @@
+ #define PCI_CLASS_SERIAL_USB_UHCI	0x0c0300
+ #define PCI_CLASS_SERIAL_USB_OHCI	0x0c0310
+ #define PCI_CLASS_SERIAL_USB_EHCI	0x0c0320
++#define PCI_CLASS_SERIAL_USB_XHCI	0x0c0330
+ #define PCI_CLASS_SERIAL_FIBER		0x0c04
+ #define PCI_CLASS_SERIAL_SMBUS		0x0c05
+ 
+diff --git a/src/usb-xhci.c b/src/usb-xhci.c
+new file mode 100644
+index 0000000..5be0b25
+--- /dev/null
++++ b/src/usb-xhci.c
+@@ -0,0 +1,1124 @@
++#include "config.h" // CONFIG_*
++#include "util.h" // timer_calc
++#include "pci.h" // pci_bdf_to_bus
++#include "pci_regs.h" // PCI_BASE_ADDRESS_0
++#include "usb.h" // struct usb_s
++#include "usb-xhci.h" // struct ehci_qh
++#include "biosvar.h" // GET_LOWFLAT
++
++// --------------------------------------------------------------
++// configuration
++
++#define XHCI_RING_ITEMS          16
++#define XHCI_RING_SIZE           (XHCI_RING_ITEMS*sizeof(struct xhci_trb))
++
++/*
++ *  xhci_ring structs are allocated with XHCI_RING_SIZE alignment,
++ *  then we can get it from a trb pointer (provided by evt ring).
++ */
++#define XHCI_RING(_trb)          \
++    ((struct xhci_ring*)((u32)(_trb) & ~(XHCI_RING_SIZE-1)))
++
++// --------------------------------------------------------------
++// bit definitions
++
++#define XHCI_CMD_RS              (1<<0)
++#define XHCI_CMD_HCRST           (1<<1)
++#define XHCI_CMD_INTE            (1<<2)
++#define XHCI_CMD_HSEE            (1<<3)
++#define XHCI_CMD_LHCRST          (1<<7)
++#define XHCI_CMD_CSS             (1<<8)
++#define XHCI_CMD_CRS             (1<<9)
++#define XHCI_CMD_EWE             (1<<10)
++#define XHCI_CMD_EU3S            (1<<11)
++
++#define XHCI_STS_HCH             (1<<0)
++#define XHCI_STS_HSE             (1<<2)
++#define XHCI_STS_EINT            (1<<3)
++#define XHCI_STS_PCD             (1<<4)
++#define XHCI_STS_SSS             (1<<8)
++#define XHCI_STS_RSS             (1<<9)
++#define XHCI_STS_SRE             (1<<10)
++#define XHCI_STS_CNR             (1<<11)
++#define XHCI_STS_HCE             (1<<12)
++
++#define XHCI_PORTSC_CCS          (1<<0)
++#define XHCI_PORTSC_PED          (1<<1)
++#define XHCI_PORTSC_OCA          (1<<3)
++#define XHCI_PORTSC_PR           (1<<4)
++#define XHCI_PORTSC_PLS_SHIFT        5
++#define XHCI_PORTSC_PLS_MASK     0xf
++#define XHCI_PORTSC_PP           (1<<9)
++#define XHCI_PORTSC_SPEED_SHIFT      10
++#define XHCI_PORTSC_SPEED_MASK   0xf
++#define XHCI_PORTSC_SPEED_FULL   (1<<10)
++#define XHCI_PORTSC_SPEED_LOW    (2<<10)
++#define XHCI_PORTSC_SPEED_HIGH   (3<<10)
++#define XHCI_PORTSC_SPEED_SUPER  (4<<10)
++#define XHCI_PORTSC_PIC_SHIFT        14
++#define XHCI_PORTSC_PIC_MASK     0x3
++#define XHCI_PORTSC_LWS          (1<<16)
++#define XHCI_PORTSC_CSC          (1<<17)
++#define XHCI_PORTSC_PEC          (1<<18)
++#define XHCI_PORTSC_WRC          (1<<19)
++#define XHCI_PORTSC_OCC          (1<<20)
++#define XHCI_PORTSC_PRC          (1<<21)
++#define XHCI_PORTSC_PLC          (1<<22)
++#define XHCI_PORTSC_CEC          (1<<23)
++#define XHCI_PORTSC_CAS          (1<<24)
++#define XHCI_PORTSC_WCE          (1<<25)
++#define XHCI_PORTSC_WDE          (1<<26)
++#define XHCI_PORTSC_WOE          (1<<27)
++#define XHCI_PORTSC_DR           (1<<30)
++#define XHCI_PORTSC_WPR          (1<<31)
++
++#define TRB_C               (1<<0)
++#define TRB_TYPE_SHIFT          10
++#define TRB_TYPE_MASK       0x3f
++#define TRB_TYPE(t)         (((t) >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
++
++#define TRB_EV_ED           (1<<2)
++
++#define TRB_TR_ENT          (1<<1)
++#define TRB_TR_ISP          (1<<2)
++#define TRB_TR_NS           (1<<3)
++#define TRB_TR_CH           (1<<4)
++#define TRB_TR_IOC          (1<<5)
++#define TRB_TR_IDT          (1<<6)
++#define TRB_TR_TBC_SHIFT        7
++#define TRB_TR_TBC_MASK     0x3
++#define TRB_TR_BEI          (1<<9)
++#define TRB_TR_TLBPC_SHIFT      16
++#define TRB_TR_TLBPC_MASK   0xf
++#define TRB_TR_FRAMEID_SHIFT    20
++#define TRB_TR_FRAMEID_MASK 0x7ff
++#define TRB_TR_SIA          (1<<31)
++
++#define TRB_TR_DIR          (1<<16)
++
++#define TRB_CR_SLOTID_SHIFT     24
++#define TRB_CR_SLOTID_MASK  0xff
++#define TRB_CR_EPID_SHIFT       16
++#define TRB_CR_EPID_MASK    0x1f
++
++#define TRB_CR_BSR          (1<<9)
++#define TRB_CR_DC           (1<<9)
++
++#define TRB_LK_TC           (1<<1)
++
++#define TRB_INTR_SHIFT          22
++#define TRB_INTR_MASK       0x3ff
++#define TRB_INTR(t)         (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK)
++
++typedef enum TRBType {
++    TRB_RESERVED = 0,
++    TR_NORMAL,
++    TR_SETUP,
++    TR_DATA,
++    TR_STATUS,
++    TR_ISOCH,
++    TR_LINK,
++    TR_EVDATA,
++    TR_NOOP,
++    CR_ENABLE_SLOT,
++    CR_DISABLE_SLOT,
++    CR_ADDRESS_DEVICE,
++    CR_CONFIGURE_ENDPOINT,
++    CR_EVALUATE_CONTEXT,
++    CR_RESET_ENDPOINT,
++    CR_STOP_ENDPOINT,
++    CR_SET_TR_DEQUEUE,
++    CR_RESET_DEVICE,
++    CR_FORCE_EVENT,
++    CR_NEGOTIATE_BW,
++    CR_SET_LATENCY_TOLERANCE,
++    CR_GET_PORT_BANDWIDTH,
++    CR_FORCE_HEADER,
++    CR_NOOP,
++    ER_TRANSFER = 32,
++    ER_COMMAND_COMPLETE,
++    ER_PORT_STATUS_CHANGE,
++    ER_BANDWIDTH_REQUEST,
++    ER_DOORBELL,
++    ER_HOST_CONTROLLER,
++    ER_DEVICE_NOTIFICATION,
++    ER_MFINDEX_WRAP,
++} TRBType;
++
++typedef enum TRBCCode {
++    CC_INVALID = 0,
++    CC_SUCCESS,
++    CC_DATA_BUFFER_ERROR,
++    CC_BABBLE_DETECTED,
++    CC_USB_TRANSACTION_ERROR,
++    CC_TRB_ERROR,
++    CC_STALL_ERROR,
++    CC_RESOURCE_ERROR,
++    CC_BANDWIDTH_ERROR,
++    CC_NO_SLOTS_ERROR,
++    CC_INVALID_STREAM_TYPE_ERROR,
++    CC_SLOT_NOT_ENABLED_ERROR,
++    CC_EP_NOT_ENABLED_ERROR,
++    CC_SHORT_PACKET,
++    CC_RING_UNDERRUN,
++    CC_RING_OVERRUN,
++    CC_VF_ER_FULL,
++    CC_PARAMETER_ERROR,
++    CC_BANDWIDTH_OVERRUN,
++    CC_CONTEXT_STATE_ERROR,
++    CC_NO_PING_RESPONSE_ERROR,
++    CC_EVENT_RING_FULL_ERROR,
++    CC_INCOMPATIBLE_DEVICE_ERROR,
++    CC_MISSED_SERVICE_ERROR,
++    CC_COMMAND_RING_STOPPED,
++    CC_COMMAND_ABORTED,
++    CC_STOPPED,
++    CC_STOPPED_LENGTH_INVALID,
++    CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29,
++    CC_ISOCH_BUFFER_OVERRUN = 31,
++    CC_EVENT_LOST_ERROR,
++    CC_UNDEFINED_ERROR,
++    CC_INVALID_STREAM_ID_ERROR,
++    CC_SECONDARY_BANDWIDTH_ERROR,
++    CC_SPLIT_TRANSACTION_ERROR
++} TRBCCode;
++
++enum {
++    PLS_U0              =  0,
++    PLS_U1              =  1,
++    PLS_U2              =  2,
++    PLS_U3              =  3,
++    PLS_DISABLED        =  4,
++    PLS_RX_DETECT       =  5,
++    PLS_INACTIVE        =  6,
++    PLS_POLLING         =  7,
++    PLS_RECOVERY        =  8,
++    PLS_HOT_RESET       =  9,
++    PLS_COMPILANCE_MODE = 10,
++    PLS_TEST_MODE       = 11,
++    PLS_RESUME          = 15,
++};
++
++#define xhci_get_field(data, field)             \
++    (((data) >> field##_SHIFT) & field##_MASK)
++
++// --------------------------------------------------------------
++// state structs
++
++struct xhci_ring {
++    struct xhci_trb      ring[XHCI_RING_ITEMS];
++    struct xhci_trb      evt;
++    u32                  eidx;
++    u32                  nidx;
++    u32                  cs;
++    struct mutex_s       lock;
++};
++
++struct usb_xhci_s {
++    struct usb_s         usb;
++    struct usbhub_s      hub;
++
++    /* devinfo */
++    u32                  baseaddr;
++    u32                  xcap;
++    u32                  ports;
++    u32                  slots;
++
++    /* xhci registers */
++    struct xhci_caps     *caps;
++    struct xhci_op       *op;
++    struct xhci_pr       *pr;
++    struct xhci_ir       *ir;
++    struct xhci_db       *db;
++
++    /* xhci data structures */
++    struct xhci_devlist  *devs;
++    struct xhci_ring     *cmds;
++    struct xhci_ring     *evts;
++    struct xhci_er_seg   *eseg;
++
++    /* usb devices */
++    struct hlist_head    list;
++};
++
++struct xhci_device {
++    struct xhci_devctx   devctx;
++    struct xhci_inctx    inctx;
++
++    struct usbdevice_s   *usbdev;
++    struct usb_xhci_s    *xhci;
++    u32                  slotid;
++    struct hlist_node    next;
++};
++
++struct xhci_pipe {
++    struct xhci_ring     reqs;
++
++    struct usb_pipe      pipe;
++    struct xhci_device   *dev;
++    u32                  epid;
++    void                 *buf;
++    int                  bufused;
++};
++
++// --------------------------------------------------------------
++// tables
++
++static const char *speed_name[16] = {
++    [ 0 ] = " - ",
++    [ 1 ] = "Full",
++    [ 2 ] = "Low",
++    [ 3 ] = "High",
++    [ 4 ] = "Super",
++};
++
++static const int speed_from_xhci[16] = {
++    [ 0 ... 15 ] = -1,
++    [ 1 ] = USB_FULLSPEED,
++    [ 2 ] = USB_LOWSPEED,
++    [ 3 ] = USB_HIGHSPEED,
++    [ 4 ] = USB_SUPERSPEED,
++};
++
++static const int speed_to_xhci[] = {
++    [ USB_FULLSPEED  ] = 1,
++    [ USB_LOWSPEED   ] = 2,
++    [ USB_HIGHSPEED  ] = 3,
++    [ USB_SUPERSPEED ] = 4,
++};
++
++static const int speed_to_ctlsize[] = {
++    [ USB_FULLSPEED  ] = 8,
++    [ USB_LOWSPEED   ] = 8,
++    [ USB_HIGHSPEED  ] = 64,
++    [ USB_SUPERSPEED ] = 256,
++};
++
++static const int eptype_to_xhci_in[] = {
++    [ USB_ENDPOINT_XFER_CONTROL] = 4,
++    [ USB_ENDPOINT_XFER_ISOC   ] = 5,
++    [ USB_ENDPOINT_XFER_BULK   ] = 6,
++    [ USB_ENDPOINT_XFER_INT    ] = 7,
++};
++
++static const int eptype_to_xhci_out[] = {
++    [ USB_ENDPOINT_XFER_CONTROL] = 4,
++    [ USB_ENDPOINT_XFER_ISOC   ] = 1,
++    [ USB_ENDPOINT_XFER_BULK   ] = 2,
++    [ USB_ENDPOINT_XFER_INT    ] = 3,
++};
++
++// --------------------------------------------------------------
++// internal functions, 16bit + 32bit
++
++static void xhci_doorbell(struct usb_xhci_s *xhci, u32 slotid, u32 value)
++{
++    struct xhci_db *db = GET_LOWFLAT(xhci->db);
++    u32 addr = (u32)(&db[slotid].doorbell);
++    pci_writel(addr, value);
++}
++
++static void xhci_process_events(struct usb_xhci_s *xhci)
++{
++    struct xhci_ring *evts = GET_LOWFLAT(xhci->evts);
++
++    for (;;) {
++        /* check for event */
++        u32 nidx = GET_LOWFLAT(evts->nidx);
++        u32 cs = GET_LOWFLAT(evts->cs);
++        struct xhci_trb *etrb = evts->ring + nidx;
++        u32 control = GET_LOWFLAT(etrb->control);
++        if ((control & TRB_C) != (cs ? 1 : 0))
++            return;
++
++        /* process event */
++        u32 evt_type = TRB_TYPE(control);
++        u32 evt_cc = (GET_LOWFLAT(etrb->status) >> 24) & 0xff;
++        switch (evt_type) {
++        case ER_TRANSFER:
++        case ER_COMMAND_COMPLETE:
++        {
++            struct xhci_trb  *rtrb = (void*)GET_LOWFLAT(etrb->ptr_low);
++            struct xhci_ring *ring = XHCI_RING(rtrb);
++            struct xhci_trb  *evt = &ring->evt;
++            u32 eidx = rtrb - ring->ring + 1;
++            dprintf(5, "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n",
++                    __func__, ring, rtrb, evt, evt_type, eidx, evt_cc);
++            memcpy_fl(evt, etrb, sizeof(*etrb));
++            SET_LOWFLAT(ring->eidx, eidx);
++            break;
++        }
++        case ER_PORT_STATUS_CHANGE:
++        {
++            u32 portid = (GET_LOWFLAT(etrb->ptr_low) >> 24) & 0xff;
++            dprintf(3, "%s: status change port #%d\n",
++                    __func__, portid);
++            break;
++        }
++        default:
++            dprintf(1, "%s: unknown event, type %d, cc %d\n",
++                    __func__, evt_type, evt_cc);
++            break;
++        }
++
++        /* move ring index, notify xhci */
++        nidx++;
++        if (nidx == XHCI_RING_ITEMS) {
++            nidx = 0;
++            cs = cs ? 0 : 1;
++            SET_LOWFLAT(evts->cs, cs);
++        }
++        SET_LOWFLAT(evts->nidx, nidx);
++        struct xhci_ir *ir = GET_LOWFLAT(xhci->ir);
++        u32 addr = (u32)(&ir->erdp_low);
++        u32 erdp = (u32)(evts->ring + nidx);
++        pci_writel(addr, erdp);
++    }
++}
++
++static int xhci_ring_busy(struct xhci_ring *ring)
++{
++    u32 eidx = GET_LOWFLAT(ring->eidx);
++    u32 nidx = GET_LOWFLAT(ring->nidx);
++    return (eidx != nidx);
++}
++
++static int xhci_event_wait(struct usb_xhci_s *xhci,
++                           struct xhci_ring *ring,
++                           u32 timeout)
++{
++    u32 end = timer_calc(timeout);
++
++    for (;;) {
++        xhci_process_events(xhci);
++        if (!xhci_ring_busy(ring)) {
++            u32 status = GET_LOWFLAT(ring->evt.status);
++            return (status >> 24) & 0xff;
++        }
++        if (timer_check(end)) {
++            warn_timeout();
++            return -1;
++        }
++        yield();
++    }
++}
++
++static void xhci_trb_queue(struct xhci_ring *ring,
++                           struct xhci_trb *trb)
++{
++    u32 nidx = GET_LOWFLAT(ring->nidx);
++    u32 cs   = GET_LOWFLAT(ring->cs);
++    struct xhci_trb *dst;
++    u32 control;
++
++    if (nidx == XHCI_RING_ITEMS-1) {
++        dst = ring->ring + nidx;
++        control  = (TR_LINK << 10); // trb type
++        control |= TRB_LK_TC;
++        control |= (cs ? TRB_C : 0);
++        SET_LOWFLAT(dst->ptr_low,  (u32)&ring[0]);
++        SET_LOWFLAT(dst->ptr_high, 0);
++        SET_LOWFLAT(dst->status,   0);
++        SET_LOWFLAT(dst->control,  control);
++        nidx = 0;
++        cs = cs ? 0 : 1;
++        SET_LOWFLAT(ring->nidx, nidx);
++        SET_LOWFLAT(ring->cs,   cs);
++
++        dprintf(5, "%s: ring %p [linked]\n", __func__, ring);
++    }
++
++    dst = ring->ring + nidx;
++    control = GET_LOWFLAT(trb->control) | (cs ? TRB_C : 0);
++
++    SET_LOWFLAT(dst->ptr_low,  GET_LOWFLAT(trb->ptr_low));
++    SET_LOWFLAT(dst->ptr_high, GET_LOWFLAT(trb->ptr_high));
++    SET_LOWFLAT(dst->status,   GET_LOWFLAT(trb->status));
++    SET_LOWFLAT(dst->control,  control);
++    nidx++;
++    SET_LOWFLAT(ring->nidx, nidx);
++
++    dprintf(5, "%s: ring %p [nidx %d, len %d]\n",
++            __func__, ring, nidx,
++            GET_LOWFLAT(trb->status) & 0xffff);
++}
++
++static void xhci_xfer_queue(struct xhci_pipe *pipe,
++                            struct xhci_trb *trb)
++{
++    xhci_trb_queue(&pipe->reqs, trb);
++}
++
++static void xhci_xfer_kick(struct xhci_pipe *pipe)
++{
++    struct xhci_device *dev = GET_LOWFLAT(pipe->dev);
++    struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci);
++    u32 slotid = GET_LOWFLAT(dev->slotid);
++    u32 epid = GET_LOWFLAT(pipe->epid);
++
++    dprintf(5, "%s: ring %p, slotid %d, epid %d\n",
++            __func__, &pipe->reqs, slotid, epid);
++    xhci_doorbell(xhci, slotid, epid);
++}
++
++static void xhci_xfer_normal(struct xhci_pipe *pipe,
++                             void *data, int datalen)
++{
++    struct xhci_trb trb;
++
++    memset(&trb, 0, sizeof(trb));
++    trb.ptr_low  = (u32)data;
++    trb.status   = datalen;
++    trb.control  |= (TR_NORMAL << 10); // trb type
++    trb.control  |= TRB_TR_IOC;
++
++    xhci_xfer_queue(pipe, MAKE_FLATPTR(GET_SEG(SS), &trb));
++    xhci_xfer_kick(pipe);
++}
++
++// --------------------------------------------------------------
++// internal functions, pure 32bit
++
++static int wait_bit(u32 *reg, u32 mask, int value, u32 timeout)
++{
++    ASSERT32FLAT();
++    u32 end = timer_calc(timeout);
++
++    while ((readl(reg) & mask) != value) {
++        if (timer_check(end)) {
++            warn_timeout();
++            return -1;
++        }
++        yield();
++    }
++    return 0;
++}
++
++static int xhci_cmd_submit(struct usb_xhci_s *xhci,
++                           struct xhci_trb *cmd)
++{
++    ASSERT32FLAT();
++    int rc;
++
++    mutex_lock(&xhci->cmds->lock);
++    xhci_trb_queue(xhci->cmds, cmd);
++    xhci_doorbell(xhci, 0, 0);
++    rc = xhci_event_wait(xhci, xhci->cmds, 1000);
++    mutex_unlock(&xhci->cmds->lock);
++    return rc;
++}
++
++static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci)
++{
++    ASSERT32FLAT();
++    struct xhci_trb cmd = {
++        .ptr_low  = 0,
++        .ptr_high = 0,
++        .status   = 0,
++        .control  = (CR_ENABLE_SLOT << 10)
++    };
++    dprintf(3, "%s:\n", __func__);
++    int cc = xhci_cmd_submit(xhci, &cmd);
++    if (cc != CC_SUCCESS)
++        return -1;
++    return (xhci->cmds->evt.control >> 24) & 0xff;
++}
++
++static int xhci_cmd_disable_slot(struct xhci_device *dev)
++{
++    ASSERT32FLAT();
++    struct xhci_trb cmd = {
++        .ptr_low  = 0,
++        .ptr_high = 0,
++        .status   = 0,
++        .control  = (dev->slotid << 24) | (CR_DISABLE_SLOT << 10)
++    };
++    dprintf(3, "%s: slotid %d\n", __func__, dev->slotid);
++    return xhci_cmd_submit(dev->xhci, &cmd);
++}
++
++static int xhci_cmd_address_device(struct xhci_device *dev)
++{
++    ASSERT32FLAT();
++    struct xhci_trb cmd = {
++        .ptr_low  = (u32)&dev->inctx,
++        .ptr_high = 0,
++        .status   = 0,
++        .control  = (dev->slotid << 24) | (CR_ADDRESS_DEVICE << 10)
++    };
++    dprintf(3, "%s: slotid %d\n", __func__, dev->slotid);
++    return xhci_cmd_submit(dev->xhci, &cmd);
++}
++
++static int xhci_cmd_configure_endpoint(struct xhci_device *dev)
++{
++    ASSERT32FLAT();
++    struct xhci_trb cmd = {
++        .ptr_low  = (u32)&dev->inctx,
++        .ptr_high = 0,
++        .status   = 0,
++        .control  = (dev->slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10)
++    };
++    dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__,
++            dev->slotid, dev->inctx.add, dev->inctx.del);
++    return xhci_cmd_submit(dev->xhci, &cmd);
++}
++
++static int xhci_cmd_evaluate_context(struct xhci_device *dev)
++{
++    ASSERT32FLAT();
++    struct xhci_trb cmd = {
++        .ptr_low  = (u32)&dev->inctx,
++        .ptr_high = 0,
++        .status   = 0,
++        .control  = (dev->slotid << 24) | (CR_EVALUATE_CONTEXT << 10)
++    };
++    dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__,
++            dev->slotid, dev->inctx.add, dev->inctx.del);
++    return xhci_cmd_submit(dev->xhci, &cmd);
++}
++
++static void xhci_xfer_setup(struct xhci_pipe *pipe,
++                            const struct usb_ctrlrequest *req,
++                            int dir, int datalen)
++{
++    ASSERT32FLAT();
++    struct xhci_trb trb;
++
++    memset(&trb, 0, sizeof(trb));
++    trb.ptr_low  |= req->bRequestType;
++    trb.ptr_low  |= (req->bRequest) << 8;
++    trb.ptr_low  |= (req->wValue) << 16;
++    trb.ptr_high |= req->wIndex;
++    trb.ptr_high |= (req->wLength) << 16;
++    trb.status   |= 8;                // length
++    trb.control  |= (TR_SETUP << 10); // trb type
++    trb.control  |= TRB_TR_IDT;
++    if (datalen)
++        trb.control |= (dir ? 3 : 2) << 16; // transfer type
++    xhci_xfer_queue(pipe, &trb);
++}
++
++static void xhci_xfer_data(struct xhci_pipe *pipe,
++                           int dir, void *data, int datalen)
++{
++    ASSERT32FLAT();
++    struct xhci_trb trb;
++
++    memset(&trb, 0, sizeof(trb));
++    trb.ptr_low  = (u32)data;
++    trb.status   = datalen;
++    trb.control  |= (TR_DATA << 10); // trb type
++    if (dir)
++        trb.control |= (1 << 16);
++    xhci_xfer_queue(pipe, &trb);
++}
++
++static void xhci_xfer_status(struct xhci_pipe *pipe, int dir)
++{
++    ASSERT32FLAT();
++    struct xhci_trb trb;
++
++    memset(&trb, 0, sizeof(trb));
++    trb.control  |= (TR_STATUS << 10); // trb type
++    trb.control  |= TRB_TR_IOC;
++    if (dir)
++        trb.control |= (1 << 16);
++
++    xhci_xfer_queue(pipe, &trb);
++    xhci_xfer_kick(pipe);
++}
++
++static struct xhci_device *xhci_find_alloc_device(struct usb_xhci_s *xhci,
++                                                  struct usbdevice_s *usbdev)
++{
++    ASSERT32FLAT();
++    struct xhci_device *dev;
++
++    hlist_for_each_entry(dev, &xhci->list, next) {
++        if (dev->usbdev == usbdev) {
++            return dev;
++        }
++    }
++
++    dev = memalign_low(64, sizeof(*dev));
++    if (!dev) {
++        warn_noalloc();
++        return NULL;
++    }
++    memset(dev, 0, sizeof(*dev));
++    dev->usbdev = usbdev;
++    dev->xhci = xhci;
++    hlist_add_head(&dev->next, &xhci->list);
++    return dev;
++}
++
++static void
++configure_xhci(void *data)
++{
++    ASSERT32FLAT();
++    struct usb_xhci_s *xhci = data;
++    u32 reg;
++
++    xhci->devs = memalign_high(64, sizeof(*xhci->devs) * (xhci->slots + 1));
++    xhci->eseg = memalign_high(64, sizeof(*xhci->eseg));
++    xhci->cmds = memalign_high(XHCI_RING_SIZE, sizeof(*xhci->cmds));
++    xhci->evts = memalign_low(XHCI_RING_SIZE, sizeof(*xhci->evts));
++    if (!xhci->devs || !xhci->cmds || !xhci->evts || !xhci->eseg) {
++        warn_noalloc();
++        goto fail;
++    }
++    memset(xhci->devs, 0, sizeof(*xhci->devs) * (xhci->slots + 1));
++    memset(xhci->cmds, 0, sizeof(*xhci->cmds));
++    memset(xhci->evts, 0, sizeof(*xhci->evts));
++    memset(xhci->eseg, 0, sizeof(*xhci->eseg));
++
++    reg = readl(&xhci->op->usbcmd);
++    if (reg & XHCI_CMD_RS) {
++        reg &= ~XHCI_CMD_RS;
++        writel(&xhci->op->usbcmd, reg);
++        if (wait_bit(&xhci->op->usbsts, XHCI_STS_HCH, XHCI_STS_HCH, 32) != 0)
++            goto fail;
++    }
++
++    dprintf(3, "%s: resetting\n", __func__);
++    writel(&xhci->op->usbcmd, XHCI_CMD_HCRST);
++    if (wait_bit(&xhci->op->usbcmd, XHCI_CMD_HCRST, 0, 100) != 0)
++        goto fail;
++    if (wait_bit(&xhci->op->usbsts, XHCI_STS_CNR, 0, 100) != 0)
++        goto fail;
++
++    writel(&xhci->op->config, xhci->slots);
++    writel(&xhci->op->dcbaap_low, (u32)xhci->devs);
++    writel(&xhci->op->dcbaap_high, 0);
++    writel(&xhci->op->crcr_low, (u32)xhci->cmds | 1);
++    writel(&xhci->op->crcr_high, 0);
++    xhci->cmds->cs = 1;
++
++    xhci->eseg->ptr_low = (u32)xhci->evts;
++    xhci->eseg->ptr_high = 0;
++    xhci->eseg->size = XHCI_RING_ITEMS;
++    writel(&xhci->ir->erstsz, 1);
++    writel(&xhci->ir->erdp_low, (u32)xhci->evts);
++    writel(&xhci->ir->erdp_high, 0);
++    writel(&xhci->ir->erstba_low, (u32)xhci->eseg);
++    writel(&xhci->ir->erstba_high, 0);
++    xhci->evts->cs = 1;
++
++    reg = readl(&xhci->op->usbcmd);
++    reg |= XHCI_CMD_RS;
++    writel(&xhci->op->usbcmd, reg);
++
++    // FIXME: try find a more elegant way than a fixed delay
++    mdelay(100);
++
++    usb_enumerate(&xhci->hub);
++    if (xhci->hub.devcount)
++        return;
++
++    // No devices found - shutdown and free controller.
++    dprintf(1, "XHCI no devices found\n");
++    reg = readl(&xhci->op->usbcmd);
++    reg &= ~XHCI_CMD_RS;
++    writel(&xhci->op->usbcmd, reg);
++    wait_bit(&xhci->op->usbsts, XHCI_STS_HCH, XHCI_STS_HCH, 32);
++
++fail:
++    free(xhci->eseg);
++    free(xhci->evts);
++    free(xhci->cmds);
++    free(xhci->devs);
++    free(xhci);
++}
++
++// --------------------------------------------------------------
++// xhci root hub
++
++// Check if device attached to port
++static void
++xhci_print_port_state(int loglevel, const char *prefix, u32 port, u32 portsc)
++{
++    ASSERT32FLAT();
++    u32 pls = xhci_get_field(portsc, XHCI_PORTSC_PLS);
++    u32 speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED);
++
++    dprintf(loglevel, "%s port #%d: 0x%08x,%s%s pls %d, speed %d [%s]\n",
++            prefix, port + 1, portsc,
++            (portsc & XHCI_PORTSC_PP)  ? " powered," : "",
++            (portsc & XHCI_PORTSC_PED) ? " enabled," : "",
++            pls, speed, speed_name[speed]);
++}
++
++static int
++xhci_hub_detect(struct usbhub_s *hub, u32 port)
++{
++    ASSERT32FLAT();
++    struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb);
++    u32 portsc = readl(&xhci->pr[port].portsc);
++
++    xhci_print_port_state(3, __func__, port, portsc);
++    switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) {
++    case PLS_U0:
++    case PLS_POLLING:
++        return 0;
++    default:
++        return -1;
++    }
++}
++
++// Reset device on port
++static int
++xhci_hub_reset(struct usbhub_s *hub, u32 port)
++{
++    ASSERT32FLAT();
++    struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb);
++    u32 portsc = readl(&xhci->pr[port].portsc);
++    int rc;
++
++    switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) {
++    case PLS_U0:
++        rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)];
++        break;
++    case PLS_POLLING:
++        xhci_print_port_state(3, __func__, port, portsc);
++        portsc |= XHCI_PORTSC_PR;
++        writel(&xhci->pr[port].portsc, portsc);
++        if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0)
++            return -1;
++        portsc = readl(&xhci->pr[port].portsc);
++        rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)];
++        break;
++    default:
++        rc = -1;
++        break;
++    }
++
++    xhci_print_port_state(1, "XHCI", port, portsc);
++    return rc;
++}
++
++static void
++xhci_hub_disconnect(struct usbhub_s *hub, u32 port)
++{
++    ASSERT32FLAT();
++    struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb);
++    struct xhci_device *dev;
++
++    hlist_for_each_entry(dev, &xhci->list, next) {
++        if (dev->usbdev->hub == hub &&
++            dev->usbdev->port == port &&
++            dev->slotid != 0) {
++            xhci_cmd_disable_slot(dev);
++            hlist_del(&dev->next);
++            return;
++        }
++    }
++}
++
++static struct usbhub_op_s xhci_hub_ops = {
++    .detect = xhci_hub_detect,
++    .reset = xhci_hub_reset,
++    .disconnect = xhci_hub_disconnect,
++};
++
++// --------------------------------------------------------------
++// external interface
++
++struct usb_pipe *
++xhci_alloc_pipe(struct usbdevice_s *usbdev
++                , struct usb_endpoint_descriptor *epdesc)
++{
++    ASSERT32FLAT();
++    if (!CONFIG_USB_XHCI)
++        return NULL;
++    u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++    struct usb_xhci_s *xhci = container_of(
++        usbdev->hub->cntl, struct usb_xhci_s, usb);
++    struct xhci_pipe *pipe;
++    u32 epid;
++
++    if (epdesc->bEndpointAddress == 0) {
++        epid = 1;
++    } else {
++        epid = (epdesc->bEndpointAddress & 0x0f) * 2;
++        epid += (epdesc->bEndpointAddress & USB_DIR_IN) ? 1 : 0;
++    }
++
++    if (eptype == USB_ENDPOINT_XFER_CONTROL)
++        pipe = memalign_high(XHCI_RING_SIZE, sizeof(*pipe));
++    else
++        pipe = memalign_low(XHCI_RING_SIZE, sizeof(*pipe));
++    if (!pipe) {
++        warn_noalloc();
++        return NULL;
++    }
++    memset(pipe, 0, sizeof(*pipe));
++
++    usb_desc2pipe(&pipe->pipe, usbdev, epdesc);
++    pipe->dev = xhci_find_alloc_device(xhci, usbdev);
++    if (!pipe->dev) {
++        free(pipe);
++        return NULL;
++    }
++    pipe->epid = epid;
++    pipe->reqs.cs = 1;
++    if (eptype == USB_ENDPOINT_XFER_INT)
++        pipe->buf = malloc_low(pipe->pipe.maxpacket);
++
++    dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__,
++            usbdev, &pipe->reqs, pipe->dev->slotid, pipe->epid);
++    if (pipe->epid > 1 && pipe->dev->slotid) {
++        struct xhci_inctx *in = &pipe->dev->inctx;
++        in->add = (1 << pipe->epid) | 1;
++        in->del = 0;
++
++        in->slot.ctx[0]    |= (31 << 27); // context entries
++
++        int e = pipe->epid-1;
++        in->ep[e].ctx[1]   |= (eptype << 3);
++        if (epdesc->bEndpointAddress & USB_DIR_IN)
++            in->ep[e].ctx[1] |= (1 << 5);
++        in->ep[e].ctx[1]   |= (pipe->pipe.maxpacket << 16);
++        in->ep[e].deq_low  = (u32)&pipe->reqs.ring[0];
++        in->ep[e].deq_low  |= 1;         // dcs
++        in->ep[e].deq_high = 0;
++        in->ep[e].length   = pipe->pipe.maxpacket;
++
++        int cc = xhci_cmd_configure_endpoint(pipe->dev);
++        if (cc != CC_SUCCESS) {
++            dprintf(1, "%s: configure endpoint: failed (cc %d)\n", __func__, cc);
++            free(pipe);
++            return NULL;
++        }
++    }
++
++    return &pipe->pipe;
++}
++
++struct usb_pipe *
++xhci_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe
++                , struct usb_endpoint_descriptor *epdesc)
++{
++    ASSERT32FLAT();
++    if (!CONFIG_USB_XHCI)
++        return NULL;
++    u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++    struct xhci_pipe *pipe = container_of(upipe, struct xhci_pipe, pipe);
++    dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__,
++            usbdev, &pipe->reqs, pipe->dev->slotid, pipe->epid);
++    if (eptype == USB_ENDPOINT_XFER_CONTROL &&
++        pipe->pipe.maxpacket !=  epdesc->wMaxPacketSize) {
++        dprintf(1, "%s: reconf ctl endpoint pkt size: %d -> %d\n",
++                __func__, pipe->pipe.maxpacket, epdesc->wMaxPacketSize);
++        pipe->pipe.maxpacket = epdesc->wMaxPacketSize;
++        struct xhci_inctx *in = &pipe->dev->inctx;
++        in->add = (1 << 1);
++        in->del = 0;
++        in->ep[0].ctx[1] &= 0xffff;
++        in->ep[0].ctx[1] |= (pipe->pipe.maxpacket << 16);
++        int cc = xhci_cmd_evaluate_context(pipe->dev);
++        if (cc != CC_SUCCESS) {
++            dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n",
++                    __func__, cc);
++        }
++    }
++    return upipe;
++}
++
++int
++xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
++             , void *data, int datalen)
++{
++    ASSERT32FLAT();
++    if (!CONFIG_USB_XHCI)
++        return -1;
++    const struct usb_ctrlrequest *req = cmd;
++    struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe);
++    struct usb_xhci_s *xhci = pipe->dev->xhci;
++    int cc;
++
++    if (req->bRequest == USB_REQ_SET_ADDRESS) {
++        int slotid = xhci_cmd_enable_slot(xhci);
++        if (slotid < 0) {
++            dprintf(1, "%s: enable slot: failed\n", __func__);
++            return -1;
++        }
++        dprintf(3, "%s: enable slot: got slotid %d\n", __func__, slotid);
++        pipe->dev->slotid = slotid;
++        xhci->devs[slotid].ptr_low = (u32)&pipe->dev->devctx;
++        xhci->devs[slotid].ptr_high = 0;
++
++        struct usbdevice_s *usbdev = pipe->dev->usbdev;
++        u32 route = 0;
++        while (usbdev->hub->usbdev) {
++            route <<= 4;
++            route |= (usbdev->port+1) & 0xf;
++            usbdev = usbdev->hub->usbdev;
++        }
++        dprintf(3, "%s: root port %d, route 0x%x\n",
++                __func__, usbdev->port+1, route);
++
++        struct xhci_inctx *in = &pipe->dev->inctx;
++        in->add = 0x03;
++        in->slot.ctx[0]    |= (1 << 27); // context entries
++        in->slot.ctx[0]    |= speed_to_xhci[pipe->dev->usbdev->speed] << 20;
++        in->slot.ctx[0]    |= route;
++        in->slot.ctx[1]    |= (usbdev->port+1) << 16;
++        /* TODO ctx0: hub bit */
++        /* TODO ctx1: hub ports */
++
++        in->ep[0].ctx[0]   |= (3 << 16); // interval: 1ms
++        in->ep[0].ctx[1]   |= (4 << 3);  // control pipe
++        in->ep[0].ctx[1]   |= (speed_to_ctlsize[pipe->dev->usbdev->speed] << 16);
++
++        in->ep[0].deq_low  = (u32)&pipe->reqs.ring[0];
++        in->ep[0].deq_low  |= 1;         // dcs
++        in->ep[0].deq_high = 0;
++        in->ep[0].length   = 8;
++
++        cc = xhci_cmd_address_device(pipe->dev);
++        if (cc != CC_SUCCESS) {
++            dprintf(1, "%s: address device: failed (cc %d)\n", __func__, cc);
++            return -1;
++        }
++        return 0;
++    }
++
++    xhci_xfer_setup(pipe, req, dir, datalen);
++    if (datalen)
++        xhci_xfer_data(pipe, dir, data, datalen);
++    xhci_xfer_status(pipe, dir);
++
++    cc = xhci_event_wait(xhci, &pipe->reqs, 1000);
++    if (cc != CC_SUCCESS) {
++        dprintf(1, "%s: control xfer failed (cc %d)\n", __func__, cc);
++        return -1;
++    }
++
++    return 0;
++}
++
++int
++xhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datalen)
++{
++    if (!CONFIG_USB_XHCI)
++        return -1;
++
++    struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe);
++    struct xhci_device *dev = GET_LOWFLAT(pipe->dev);
++    struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci);
++
++    xhci_xfer_normal(pipe, data, datalen);
++    int cc = xhci_event_wait(xhci, &pipe->reqs, 1000);
++    if (cc != CC_SUCCESS) {
++        dprintf(1, "%s: bulk xfer failed (cc %d)\n", __func__, cc);
++        return -1;
++    }
++    return 0;
++}
++
++int
++xhci_poll_intr(struct usb_pipe *p, void *data)
++{
++    if (!CONFIG_USB_XHCI)
++        return -1;
++
++    struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe);
++    struct xhci_device *dev = GET_LOWFLAT(pipe->dev);
++    struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci);
++    u32 len = GET_LOWFLAT(pipe->pipe.maxpacket);
++    void *buf = GET_LOWFLAT(pipe->buf);
++    int bufused = GET_LOWFLAT(pipe->bufused);
++
++    if (!bufused) {
++        xhci_xfer_normal(pipe, buf, len);
++        bufused = 1;
++        SET_LOWFLAT(pipe->bufused, bufused);
++        return -1;
++    }
++
++    xhci_process_events(xhci);
++    if (xhci_ring_busy(&pipe->reqs))
++        return -1;
++    dprintf(5, "%s: st %x ct %x [ %p <= %p / %d ]\n", __func__,
++            GET_LOWFLAT(pipe->reqs.evt.status),
++            GET_LOWFLAT(pipe->reqs.evt.control),
++            MAKE_FLATPTR(GET_SEG(SS), data), buf, len);
++    memcpy_fl(MAKE_FLATPTR(GET_SEG(SS), data), buf, len);
++    xhci_xfer_normal(pipe, buf, len);
++    return 0;
++}
++
++int
++xhci_setup(struct pci_device *pci, int busid)
++{
++    ASSERT32FLAT();
++    if (!CONFIG_USB_XHCI)
++        return -1;
++
++    struct usb_xhci_s *xhci = malloc_low(sizeof(*xhci));
++    if (!xhci) {
++        warn_noalloc();
++        return -1;
++    }
++    memset(xhci, 0, sizeof(*xhci));
++
++    xhci->baseaddr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0)
++        & PCI_BASE_ADDRESS_MEM_MASK;
++    xhci->caps  = (void*)(xhci->baseaddr);
++    xhci->op    = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength));
++    xhci->pr    = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength) + 0x400);
++    xhci->db    = (void*)(xhci->baseaddr + readl(&xhci->caps->dboff));
++    xhci->ir    = (void*)(xhci->baseaddr + readl(&xhci->caps->rtsoff) + 0x20);
++
++    u32 hcs1 = readl(&xhci->caps->hcsparams1);
++    u32 hcc  = readl(&xhci->caps->hccparams);
++    xhci->ports = (hcs1 >> 24) & 0xff;
++    xhci->slots = hcs1         & 0xff;
++    xhci->xcap  = ((hcc >> 16) & 0xffff) << 2;
++
++    xhci->usb.busid = busid;
++    xhci->usb.pci = pci;
++    xhci->usb.type = USB_TYPE_XHCI;
++    xhci->hub.cntl = &xhci->usb;
++    xhci->hub.portcount = xhci->ports;
++    xhci->hub.op = &xhci_hub_ops;
++
++    dprintf(1, "XHCI init on dev %02x:%02x.%x: regs @ %p, %d ports, %d slots\n"
++            , pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf)
++            , pci_bdf_to_fn(pci->bdf), xhci->caps
++            , xhci->ports, xhci->slots);
++
++    if (xhci->xcap) {
++        u32 off, addr = xhci->baseaddr + xhci->xcap;
++        do {
++            struct xhci_xcap *xcap = (void*)addr;
++            u32 ports, name, cap = readl(&xcap->cap);
++            switch (cap & 0xff) {
++            case 0x02:
++                name  = readl(&xcap->data[0]);
++                ports = readl(&xcap->data[1]);
++                dprintf(1, "XHCI    protocol %c%c%c%c %x.%02x, %d ports (offset %d)\n"
++                        , (name >>  0) & 0xff
++                        , (name >>  8) & 0xff
++                        , (name >> 16) & 0xff
++                        , (name >> 24) & 0xff
++                        , (cap >> 24) & 0xff
++                        , (cap >> 16) & 0xff
++                        , (ports >>  8) & 0xff
++                        , (ports >>  0) & 0xff);
++                break;
++            default:
++                dprintf(1, "XHCI    extcap 0x%x @ %x\n", cap & 0xff, addr);
++                break;
++            }
++            off = (cap >> 8) & 0xff;
++            addr += off << 2;
++        } while (off > 0);
++    }
++
++    pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
++
++    run_thread(configure_xhci, xhci);
++    return 0;
++}
+diff --git a/src/usb-xhci.h b/src/usb-xhci.h
+new file mode 100644
+index 0000000..64ee82c
+--- /dev/null
++++ b/src/usb-xhci.h
+@@ -0,0 +1,144 @@
++#ifndef __USB_XHCI_H
++#define __USB_XHCI_H
++
++struct usbdevice_s;
++struct usb_endpoint_descriptor;
++struct usb_pipe;
++
++// --------------------------------------------------------------
++
++// usb-xhci.c
++int xhci_setup(struct pci_device *pci, int busid);
++struct usb_pipe *xhci_alloc_pipe(struct usbdevice_s *usbdev
++                                 , struct usb_endpoint_descriptor *epdesc);
++struct usb_pipe *xhci_update_pipe(struct usbdevice_s *usbdev
++                                  , struct usb_pipe *pipe
++                                  , struct usb_endpoint_descriptor *epdesc);
++int xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
++                 , void *data, int datasize);
++int xhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize);
++int xhci_poll_intr(struct usb_pipe *p, void *data);
++
++// --------------------------------------------------------------
++// register interface
++
++// capabilities
++struct xhci_caps {
++    u8  caplength;
++    u8  reserved_01;
++    u16 hciversion;
++    u32 hcsparams1;
++    u32 hcsparams2;
++    u32 hcsparams3;
++    u32 hccparams;
++    u32 dboff;
++    u32 rtsoff;
++} PACKED;
++
++// extended capabilities
++struct xhci_xcap {
++    u32 cap;
++    u32 data[];
++} PACKED;
++
++// operational registers
++struct xhci_op {
++    u32 usbcmd;
++    u32 usbsts;
++    u32 pagesize;
++    u32 reserved_01[2];
++    u32 dnctl;
++    u32 crcr_low;
++    u32 crcr_high;
++    u32 reserved_02[4];
++    u32 dcbaap_low;
++    u32 dcbaap_high;
++    u32 config;
++} PACKED;
++
++// port registers
++struct xhci_pr {
++    u32 portsc;
++    u32 portpmsc;
++    u32 portli;
++    u32 reserved_01;
++} PACKED;
++
++// doorbell registers
++struct xhci_db {
++    u32 doorbell;
++} PACKED;
++
++// runtime registers
++struct xhci_rts {
++    u32 mfindex;
++} PACKED;
++
++// interrupter registers
++struct xhci_ir {
++    u32 iman;
++    u32 imod;
++    u32 erstsz;
++    u32 reserved_01;
++    u32 erstba_low;
++    u32 erstba_high;
++    u32 erdp_low;
++    u32 erdp_high;
++} PACKED;
++
++// --------------------------------------------------------------
++// memory data structs
++
++// slot context
++struct xhci_slotctx {
++    u32 ctx[4];
++    u32 reserved_01[4];
++} PACKED;
++
++// endpoint context
++struct xhci_epctx {
++    u32 ctx[2];
++    u32 deq_low;
++    u32 deq_high;
++    u32 length;
++    u32 reserved_01[3];
++} PACKED;
++
++// device context
++struct xhci_devctx {
++    struct xhci_slotctx slot;
++    struct xhci_epctx   ep[31];
++} PACKED;
++
++// device context array element
++struct xhci_devlist {
++    u32 ptr_low;
++    u32 ptr_high;
++} PACKED;
++
++// input context
++struct xhci_inctx {
++    u32 del;
++    u32 add;
++    u32 reserved_01[6];
++    struct xhci_slotctx slot;
++    struct xhci_epctx   ep[31];
++} PACKED;
++
++// transfer block (ring element)
++struct xhci_trb {
++    u32 ptr_low;
++    u32 ptr_high;
++    u32 status;
++    u32 control;
++} PACKED;
++
++// event ring segment
++struct xhci_er_seg {
++    u32 ptr_low;
++    u32 ptr_high;
++    u32 size;
++    u32 reserved_01;
++} PACKED;
++
++#endif // usb-xhci.h
+diff --git a/src/usb.c b/src/usb.c
+index 3efa367..1cdb612 100644
+--- a/src/usb.c
++++ b/src/usb.c
+@@ -12,6 +12,7 @@
+ #include "usb-uhci.h" // uhci_init
+ #include "usb-ohci.h" // ohci_init
+ #include "usb-ehci.h" // ehci_init
++#include "usb-xhci.h" // xhci_setup
+ #include "usb-hid.h" // usb_keyboard_setup
+ #include "usb-hub.h" // usb_hub_init
+ #include "usb-msc.h" // usb_msc_init
+@@ -37,6 +38,8 @@ usb_alloc_pipe(struct usbdevice_s *usbdev
+         return ohci_alloc_pipe(usbdev, epdesc);
+     case USB_TYPE_EHCI:
+         return ehci_alloc_pipe(usbdev, epdesc);
++    case USB_TYPE_XHCI:
++        return xhci_alloc_pipe(usbdev, epdesc);
+     }
+ }
+ 
+@@ -46,6 +49,8 @@ usb_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe
+                 , struct usb_endpoint_descriptor *epdesc)
+ {
+     switch (usbdev->hub->cntl->type) {
++    case USB_TYPE_XHCI:
++        return xhci_update_pipe(usbdev, pipe, epdesc);
+     default:
+         free_pipe(pipe);
+         return usb_alloc_pipe(usbdev, epdesc);
+@@ -66,6 +71,8 @@ send_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize
+         return ohci_control(pipe, dir, cmd, cmdsize, data, datasize);
+     case USB_TYPE_EHCI:
+         return ehci_control(pipe, dir, cmd, cmdsize, data, datasize);
++    case USB_TYPE_XHCI:
++        return xhci_control(pipe, dir, cmd, cmdsize, data, datasize);
+     }
+ }
+ 
+@@ -80,6 +87,8 @@ usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize)
+         return ohci_send_bulk(pipe_fl, dir, data, datasize);
+     case USB_TYPE_EHCI:
+         return ehci_send_bulk(pipe_fl, dir, data, datasize);
++    case USB_TYPE_XHCI:
++        return xhci_send_bulk(pipe_fl, dir, data, datasize);
+     }
+ }
+ 
+@@ -94,6 +103,8 @@ usb_poll_intr(struct usb_pipe *pipe_fl, void *data)
+         return ohci_poll_intr(pipe_fl, data);
+     case USB_TYPE_EHCI:
+         return ehci_poll_intr(pipe_fl, data);
++    case USB_TYPE_XHCI:
++        return xhci_poll_intr(pipe_fl, data);
+     }
+ }
+ 
+@@ -469,5 +480,7 @@ usb_setup(void)
+             uhci_init(pci, count++);
+         else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI)
+             ohci_init(pci, count++);
++        else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI)
++            xhci_setup(pci, count++);
+     }
+ }
+diff --git a/src/usb.h b/src/usb.h
+index d9eadd7..a034aa6 100644
+--- a/src/usb.h
++++ b/src/usb.h
+@@ -60,15 +60,17 @@ struct usbhub_op_s {
+     void (*disconnect)(struct usbhub_s *hub, u32 port);
+ };
+ 
+-#define USB_TYPE_UHCI 1
+-#define USB_TYPE_OHCI 2
+-#define USB_TYPE_EHCI 3
++#define USB_TYPE_UHCI  1
++#define USB_TYPE_OHCI  2
++#define USB_TYPE_EHCI  3
++#define USB_TYPE_XHCI  4
+ 
+-#define USB_FULLSPEED 0
+-#define USB_LOWSPEED  1
+-#define USB_HIGHSPEED 2
++#define USB_FULLSPEED  0
++#define USB_LOWSPEED   1
++#define USB_HIGHSPEED  2
++#define USB_SUPERSPEED 3
+ 
+-#define USB_MAXADDR 127
++#define USB_MAXADDR  127
+ 
+ 
+ /****************************************************************
+-- 
+1.7.1
+
diff --git a/SOURCES/seabios-xhci-adaptions-for-old-rhel7-seabios-codebase.patch b/SOURCES/seabios-xhci-adaptions-for-old-rhel7-seabios-codebase.patch
new file mode 100644
index 0000000..879ee68
--- /dev/null
+++ b/SOURCES/seabios-xhci-adaptions-for-old-rhel7-seabios-codebase.patch
@@ -0,0 +1,87 @@
+From 9636beca521c90babc3424789dd13317806d36f7 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 2 Oct 2013 07:00:19 +0200
+Subject: [PATCH 7/8] xhci adaptions for old rhel7 seabios codebase
+
+RH-Author: Gerd Hoffmann <kraxel@redhat.com>
+Message-id: <1380697219-1860-8-git-send-email-kraxel@redhat.com>
+Patchwork-id: 54631
+O-Subject: [RHEL-7 seabios PATCH 7/7] xhci adaptions for old rhel7 seabios codebase
+Bugzilla: 947051
+RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
+RH-Acked-by: Bandan Das <bsd@redhat.com>
+RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
+
+Kept as separate commit to make review easier.
+rhel-only patch for obvious reasons.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ src/Kconfig    | 2 +-
+ src/usb-xhci.c | 9 +++++----
+ 2 files changed, 6 insertions(+), 5 deletions(-)
+
+Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
+---
+ src/Kconfig    |    2 +-
+ src/usb-xhci.c |    9 +++++----
+ 2 files changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/src/Kconfig b/src/Kconfig
+index b059a73..d00c66c 100644
+--- a/src/Kconfig
++++ b/src/Kconfig
+@@ -185,7 +185,7 @@ menu "Hardware support"
+         help
+             Support USB EHCI controllers.
+     config USB_XHCI
+-        depends on USB && QEMU_HARDWARE
++        depends on USB && QEMU
+         bool "USB XHCI controllers"
+         default y
+         help
+diff --git a/src/usb-xhci.c b/src/usb-xhci.c
+index 5be0b25..3a4f3e0 100644
+--- a/src/usb-xhci.c
++++ b/src/usb-xhci.c
+@@ -1,5 +1,6 @@
+ #include "config.h" // CONFIG_*
+ #include "util.h" // timer_calc
++#include "list.h" // timer_calc
+ #include "pci.h" // pci_bdf_to_bus
+ #include "pci_regs.h" // PCI_BASE_ADDRESS_0
+ #include "usb.h" // struct usb_s
+@@ -387,7 +388,7 @@ static int xhci_event_wait(struct usb_xhci_s *xhci,
+                            struct xhci_ring *ring,
+                            u32 timeout)
+ {
+-    u32 end = timer_calc(timeout);
++    u64 end = calc_future_tsc(timeout);
+ 
+     for (;;) {
+         xhci_process_events(xhci);
+@@ -395,7 +396,7 @@ static int xhci_event_wait(struct usb_xhci_s *xhci,
+             u32 status = GET_LOWFLAT(ring->evt.status);
+             return (status >> 24) & 0xff;
+         }
+-        if (timer_check(end)) {
++        if (check_tsc(end)) {
+             warn_timeout();
+             return -1;
+         }
+@@ -482,10 +483,10 @@ static void xhci_xfer_normal(struct xhci_pipe *pipe,
+ static int wait_bit(u32 *reg, u32 mask, int value, u32 timeout)
+ {
+     ASSERT32FLAT();
+-    u32 end = timer_calc(timeout);
++    u64 end = calc_future_tsc(timeout);
+ 
+     while ((readl(reg) & mask) != value) {
+-        if (timer_check(end)) {
++        if (check_tsc(end)) {
+             warn_timeout();
+             return -1;
+         }
+-- 
+1.7.1
+
diff --git a/SPECS/seabios.spec b/SPECS/seabios.spec
new file mode 100644
index 0000000..4e6fdce
--- /dev/null
+++ b/SPECS/seabios.spec
@@ -0,0 +1,276 @@
+Name:           seabios
+Version:        1.7.2.2
+Release:        4%{?dist}
+Summary:        Open-source legacy BIOS implementation
+
+Group:          Applications/Emulators
+License:        LGPLv3
+URL:            http://www.coreboot.org/SeaBIOS
+
+
+# No source releases of seabios stable. To generate:
+# git clone git://git.seabios.org/seabios.git && cd seabios
+# git archive --output seabios-1.7.2.2.tar.gz --prefix seabios-1.7.2.2/ rel-1.7.2.2
+#Source0:        http://code.coreboot.org/p/seabios/downloads/get/%{name}-%{version}.tar.gz
+Source0: seabios-%{version}.tar.gz
+
+Source10:       config.vga.cirrus
+Source11:       config.vga.isavga
+Source12:       config.vga.qxl
+Source13:       config.vga.stdvga
+Source14:       config.vga.vmware
+Source15:       config.base
+
+# Add pvpanic device driver (rhbz 967777)
+Patch1: Add-pvpanic-device-driver.patch
+# For bz#920140 - qemu-kvm emulation of 2.88M floppy fails
+Patch2: seabios-floppy-Introduce-struct-floppy_pio_s-for-floppy-PIO-.patch
+# For bz#920140 - qemu-kvm emulation of 2.88M floppy fails
+Patch3: seabios-floppy-Cleanup-floppy-irq-wait-handling.patch
+# For bz#920140 - qemu-kvm emulation of 2.88M floppy fails
+Patch4: seabios-floppy-Implement-media-format-sensing.patch
+# For bz#894979 - place rpm version info into version banner
+Patch5: seabios-Place-rpm-version-info-into-version-banner.patch
+# For bz#1005747 - fail to boot rhel7 guest with >126(21 ahci controller) ahci disks
+Patch6: seabios-ahci-add-missing-check-for-allocation-failure.patch
+# For bz#947051 - [RFE] implement xhci support in seabios
+Patch7: seabios-Introduce-and-convert-pmm-code-to-use-standard-list-.patch
+# For bz#947051 - [RFE] implement xhci support in seabios
+Patch8: seabios-Fix-error-in-hlist_for_each_entry_safe-macro.patch
+# For bz#947051 - [RFE] implement xhci support in seabios
+Patch9: seabios-Another-fix-for-hlist_for_each_entry_safe.patch
+# For bz#947051 - [RFE] implement xhci support in seabios
+Patch10: seabios-uas-add-temporary-superspeed-stopgap.patch
+# For bz#947051 - [RFE] implement xhci support in seabios
+Patch11: seabios-usb-add-usb_update_pipe.patch
+# For bz#947051 - [RFE] implement xhci support in seabios
+Patch12: seabios-usb-add-xhci-support.patch
+# For bz#947051 - [RFE] implement xhci support in seabios
+Patch13: seabios-xhci-adaptions-for-old-rhel7-seabios-codebase.patch
+# For bz#1016974 - [HP 7.0 FEAT]: Increase KVM guest supported memory to 4TiB
+Patch14: seabios-allow-1TB-of-RAM.patch
+
+BuildRequires: python iasl
+ExclusiveArch: x86_64
+
+Requires: %{name}-bin = %{version}-%{release}
+Requires: seavgabios-bin = %{version}-%{release}
+Buildarch: x86_64
+
+# Seabios is noarch, but required on architectures which cannot build it.
+# Disable debuginfo because it is of no use to us.
+%global debug_package %{nil}
+
+# You can build a debugging version of the BIOS by setting this to a
+# value > 1.  See src/config.h for possible values, but setting it to
+# a number like 99 will enable all possible debugging.  Note that
+# debugging goes to a special qemu port that you have to enable.  See
+# the SeaBIOS top-level README file for the magic qemu invocation to
+# enable this.
+%global debug_level 1
+
+
+%description
+SeaBIOS is an open-source legacy BIOS implementation which can be used as
+a coreboot payload. It implements the standard BIOS calling interfaces
+that a typical x86 proprietary BIOS implements.
+
+
+%package bin
+Summary: Seabios for x86
+Buildarch: noarch
+
+
+%description bin
+SeaBIOS is an open-source legacy BIOS implementation which can be used as
+a coreboot payload. It implements the standard BIOS calling interfaces
+that a typical x86 proprietary BIOS implements.
+
+
+%package -n seavgabios-bin
+Summary: Seavgabios for x86
+Buildarch: noarch
+
+Obsoletes: vgabios < vgabios-0.6c-10
+Provides: vgabios = vgabios-0.6c-10
+
+
+%description -n seavgabios-bin
+SeaVGABIOS is an open-source VGABIOS implementation.
+
+
+%prep
+%setup -q
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1
+
+# Store version to be used
+echo "%{name}-%{version}-%release" > .version
+
+%build
+cp %{SOURCE15} .config
+make oldnoconfig V=1
+sed -i 's,CONFIG_DEBUG_LEVEL=.*,CONFIG_DEBUG_LEVEL=%{debug_level},g' .config
+
+export CFLAGS="$RPM_OPT_FLAGS"
+mkdir binaries
+
+# seabios
+make
+cp out/bios.bin binaries
+
+# seavgabios
+for config in %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14}; do
+	name=${config#*config.vga.}
+	make clean distclean
+	cp ${config} .config
+	echo "CONFIG_DEBUG_LEVEL=%{debug_level}" >> .config
+	make oldnoconfig
+	make
+	cp out/vgabios.bin binaries/vgabios-${name}.bin
+done
+
+
+%install
+mkdir -p $RPM_BUILD_ROOT%{_datadir}/seabios
+mkdir -p $RPM_BUILD_ROOT%{_datadir}/seavgabios
+install -m 0644 binaries/bios.bin $RPM_BUILD_ROOT%{_datadir}/seabios
+install -m 0644 binaries/vgabios*.bin $RPM_BUILD_ROOT%{_datadir}/seavgabios
+
+
+%files
+%doc COPYING COPYING.LESSER README TODO
+
+
+%files bin
+%dir %{_datadir}/seabios/
+%{_datadir}/seabios/bios.bin
+
+%files -n seavgabios-bin
+%dir %{_datadir}/seavgabios/
+%{_datadir}/seavgabios/vgabios*.bin
+
+
+%changelog
+* Tue Nov 05 2013 Miroslav Rezanina <mrezanin@redhat.com> - 1.7.2.2-4.el7
+- seabios-Introduce-and-convert-pmm-code-to-use-standard-list-.patch [bz#947051]
+- seabios-Fix-error-in-hlist_for_each_entry_safe-macro.patch [bz#947051]
+- seabios-Another-fix-for-hlist_for_each_entry_safe.patch [bz#947051]
+- seabios-uas-add-temporary-superspeed-stopgap.patch [bz#947051]
+- seabios-usb-add-usb_update_pipe.patch [bz#947051]
+- seabios-usb-add-xhci-support.patch [bz#947051]
+- seabios-xhci-adaptions-for-old-rhel7-seabios-codebase.patch [bz#947051]
+- seabios-allow-1TB-of-RAM.patch [bz#1016974]
+- Resolves: bz#1016974
+  ([HP 7.0 FEAT]: Increase KVM guest supported memory to 4TiB)
+- Resolves: bz#947051
+  ([RFE] implement xhci support in seabios)
+
+* Tue Sep 24 2013 Miroslav Rezanina <mrezanin@redhat.com> - seabios-1.7.2.2-3.el7
+- seabios-floppy-Introduce-struct-floppy_pio_s-for-floppy-PIO-.patch [bz#920140]
+- seabios-floppy-Cleanup-floppy-irq-wait-handling.patch [bz#920140]
+- seabios-floppy-Implement-media-format-sensing.patch [bz#920140]
+- seabios-Place-rpm-version-info-into-version-banner.patch [bz#894979]
+- seabios-ahci-add-missing-check-for-allocation-failure.patch [bz#1005747]
+- Resolves: bz#1005747
+  (fail to boot rhel7 guest with >126(21 ahci controller) ahci disks)
+- Resolves: bz#894979
+  (place rpm version info into version banner)
+- Resolves: bz#920140
+  (qemu-kvm emulation of 2.88M floppy fails)
+
+* Wed Jun 26 2013 Miroslav Rezanina <mrezanin@redhat.com> - 1.7.2.2-2
+- Disable options not used / not supported by RHEL-7 (rhbz 927582)
+- Add pvpanic device driver (rhbz 967777)
+- Obsolete vgabios (rhbz 976340)
+
+* Tue Jun 04 2013 Miroslav Rezanina <mrezanin@redhat.com> - 1.7.2.2-1
+- Rebase to 1.7.2.2
+
+* Tue Dec 18 2012 Michal Novotny <minovotn@redhat.com> - 1.7.1-5
+- Remove the cross compilation code as we compile it on x86_64 always
+
+* Thu Dec  6 2012 Peter Robinson <pbrobinson@fedoraproject.org> 1.7.1-4
+- Root seabios package is noarch too because it only contains docs
+
+* Fri Oct 19 2012 Cole Robinson <crobinso@redhat.com> - 1.7.1-3
+- Add seavgabios subpackage
+
+* Wed Oct 17 2012 Paolo Bonzini <pbonzini@redhat.com> - 1.7.1-2
+- Build with cross compiler.  Resolves: #866664.
+
+* Wed Sep 05 2012 Cole Robinson <crobinso@redhat.com> - 1.7.1-1
+- Rebased to version 1.7.1
+- Initial support for booting from USB attached scsi (USB UAS) drives
+- USB EHCI 64bit controller support
+- USB MSC multi-LUN device support
+- Support for booting from LSI SCSI controllers on emulators
+- Support for booting from AMD PCscsi controllers on emulators
+
+* Mon Aug 13 2012 Richard W.M. Jones <rjones@redhat.com> - 1.7.0-4
+- Modernise and tidy up the RPM.
+- Allow debug versions of SeaBIOS to be built easily.
+
+* Mon Aug 06 2012 Cole Robinson <crobinso@redhat.com> - 1.7.0-3
+- Enable S3/S4 support for guests (it's an F18 feature after all)
+
+* Sat Jul 21 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Mon May 28 2012 Cole Robinson <crobinso@redhat.com> - 1.7.0-1
+- Rebased to version 1.7.0
+- Support for virtio-scsi
+- Improved USB drive support
+- Several USB controller bug fixes and improvements
+
+* Wed Mar 28 2012 Paolo Bonzini <pbonzini@redhat.com> - 1.6.3-2
+- Fix bugs in booting from host (or redirected) USB pen drives
+
+* Wed Feb 08 2012 Justin M. Forbes <jforbes@redhat.com> - 1.6.3-1
+- Update to 1.6.3 upstream
+- Add virtio-scsi
+
+* Sat Jan 14 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.2-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Wed Oct 05 2011 Justin M. Forbes <jforbes@redhat.com> - 0.6.2-3
+- Stop advertising S3 and S4 in DSDT (bz#741375)
+- incdule iasl buildreq
+
+* Wed Jul 13 2011 Justin M. Forbes <jforbes@redhat.com> - 0.6.2-2
+- Fix QXL bug in 0.6.2
+
+* Wed Jul 13 2011 Justin M. forbes <jforbes@redhat.com> - 0.6.2-1
+- Update to 0.6.2 upstream for a number of bugfixes
+
+* Mon Feb 14 2011 Justin M. forbes <jforbes@redhat.com> - 0.6.1-1
+- Update to 0.6.1 upstream for a number of bugfixes
+
+* Wed Feb 09 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Tue Aug 10 2010 Justin M. Forbes <jforbes@redhat.com> 0.6.0-1
+- Update seabios to latest stable so we can drop patches.
+
+* Tue Apr 20 2010 Justin M. Forbes <jforbes@redhat.com> 0.5.1-2
+- Ugly hacks to make package noarch and available for arch that cannot build it.
+- Disable useless debuginfo
+
+* Wed Mar 03 2010 Justin M. Forbes <jforbes@redhat.com> 0.5.1-1
+- Update to 0.5.1 stable release
+- Pick up patches required for current qemu
+
+* Thu Jan 07 2010 Justin M. Forbes <jforbes@redhat.com> 0.5.1-0.1.20100108git669c991
+- Created initial package