diff --git a/Makefile b/Makefile
index fe974f7..8fa5c91 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,8 @@ 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 \
-    virtio-ring.c virtio-pci.c virtio-blk.c virtio-scsi.c apm.c ahci.c
+    virtio-ring.c virtio-pci.c virtio-blk.c virtio-scsi.c apm.c ahci.c \
+    usb-uas.c
 SRC16=$(SRCBOTH) system.c disk.c font.c
 SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \
     acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \
diff --git a/src/Kconfig b/src/Kconfig
index 8932c9e..a798d9f 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -161,7 +161,13 @@ menu "Hardware support"
         bool "USB drives"
         default y
         help
-            Support USB disks.
+            Support USB BOT (bulk-only transport) disks.
+    config USB_UAS
+        depends on USB && DRIVES
+        bool "UAS drives"
+        default y
+        help
+            Support USB UAS (usb attached scsi) disks.
     config USB_HUB
         depends on USB
         bool "USB hubs"
diff --git a/src/block.c b/src/block.c
index 7a62787..50d7bb4 100644
--- a/src/block.c
+++ b/src/block.c
@@ -280,7 +280,7 @@ map_floppy_drive(struct drive_s *drive_g)
 static int
 process_scsi_op(struct disk_op_s *op)
 {
-    if (!CONFIG_VIRTIO_SCSI && !CONFIG_USB_MSC)
+    if (!CONFIG_VIRTIO_SCSI && !CONFIG_USB_MSC && !CONFIG_USB_UAS)
         return 0;
     switch (op->command) {
     case CMD_READ:
@@ -321,6 +321,7 @@ process_op(struct disk_op_s *op)
     case DTYPE_AHCI:
         return process_ahci_op(op);
     case DTYPE_USB:
+    case DTYPE_UAS:
     case DTYPE_VIRTIO_SCSI:
         return process_scsi_op(op);
     default:
diff --git a/src/blockcmd.c b/src/blockcmd.c
index 2dda04d..2998a7c 100644
--- a/src/blockcmd.c
+++ b/src/blockcmd.c
@@ -12,6 +12,7 @@
 #include "ata.h" // atapi_cmd_data
 #include "ahci.h" // atapi_cmd_data
 #include "usb-msc.h" // usb_cmd_data
+#include "usb-uas.h" // usb_cmd_data
 #include "virtio-scsi.h" // virtio_scsi_cmd_data
 #include "boot.h" // boot_add_hd
 
@@ -25,6 +26,8 @@ cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
         return atapi_cmd_data(op, cdbcmd, blocksize);
     case DTYPE_USB:
         return usb_cmd_data(op, cdbcmd, blocksize);
+    case DTYPE_UAS:
+        return uas_cmd_data(op, cdbcmd, blocksize);
     case DTYPE_AHCI:
         return ahci_cmd_data(op, cdbcmd, blocksize);
     case DTYPE_VIRTIO_SCSI:
@@ -90,7 +93,7 @@ scsi_is_ready(struct disk_op_s *op)
 int
 scsi_init_drive(struct drive_s *drive, const char *s, int prio)
 {
-    if (!CONFIG_USB_MSC && !CONFIG_VIRTIO_SCSI)
+    if (!CONFIG_USB_UAS && !CONFIG_USB_MSC && !CONFIG_VIRTIO_SCSI)
         return 0;
 
     struct disk_op_s dop;
diff --git a/src/disk.h b/src/disk.h
index 6776ee6..787b5b4 100644
--- a/src/disk.h
+++ b/src/disk.h
@@ -231,6 +231,7 @@ struct drive_s {
 #define DTYPE_VIRTIO_SCSI  0x07
 #define DTYPE_VIRTIO_BLK   0x08
 #define DTYPE_USB          0x09
+#define DTYPE_UAS          0x0a
 
 #define MAXDESCSIZE 80
 
diff --git a/src/usb-uas.c b/src/usb-uas.c
new file mode 100644
index 0000000..1ef2841
--- /dev/null
+++ b/src/usb-uas.c
@@ -0,0 +1,264 @@
+// Code for handling usb attached scsi devices.
+//
+// only usb 2.0 for now.
+//
+// once we have xhci driver with usb 3.0 support this must
+// be updated to use usb3 streams so booting from usb3
+// devices actually works.
+//
+// Authors:
+//  Gerd Hoffmann <kraxel@redhat.com>
+//
+// based on usb-msc.c which is written by:
+//  Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "util.h" // dprintf
+#include "config.h" // CONFIG_USB_UAS
+#include "usb.h" // struct usb_s
+#include "biosvar.h" // GET_GLOBAL
+#include "blockcmd.h" // cdb_read
+#include "disk.h" // DTYPE_UAS
+#include "boot.h" // bootprio_find_usb
+#include "usb-uas.h" // usb_uas_init
+
+#define UAS_UI_COMMAND              0x01
+#define UAS_UI_SENSE                0x03
+#define UAS_UI_RESPONSE             0x04
+#define UAS_UI_TASK_MGMT            0x05
+#define UAS_UI_READ_READY           0x06
+#define UAS_UI_WRITE_READY          0x07
+
+#define UAS_PIPE_ID_COMMAND         0x01
+#define UAS_PIPE_ID_STATUS          0x02
+#define UAS_PIPE_ID_DATA_IN         0x03
+#define UAS_PIPE_ID_DATA_OUT        0x04
+
+typedef struct {
+    u8    id;
+    u8    reserved;
+    u16   tag;
+} PACKED  uas_ui_header;
+
+typedef struct {
+    u8    prio_taskattr;   /* 6:3 priority, 2:0 task attribute   */
+    u8    reserved_1;
+    u8    add_cdb_length;  /* 7:2 additional adb length (dwords) */
+    u8    reserved_2;
+    u8    lun[8];
+    u8    cdb[16];
+    u8    add_cdb[];
+} PACKED  uas_ui_command;
+
+typedef struct {
+    u16   status_qualifier;
+    u8    status;
+    u8    reserved[7];
+    u16   sense_length;
+    u8    sense_data[18];
+} PACKED  uas_ui_sense;
+
+typedef struct {
+    u16   add_response_info;
+    u8    response_code;
+} PACKED  uas_ui_response;
+
+typedef struct {
+    u8    function;
+    u8    reserved;
+    u16   task_tag;
+    u8    lun[8];
+} PACKED  uas_ui_task_mgmt;
+
+typedef struct {
+    uas_ui_header  hdr;
+    union {
+        uas_ui_command   command;
+        uas_ui_sense     sense;
+        uas_ui_task_mgmt task;
+        uas_ui_response  response;
+    };
+} PACKED  uas_ui;
+
+struct uasdrive_s {
+    struct drive_s drive;
+    struct usb_pipe *command, *status, *data_in, *data_out;
+    int lun;
+};
+
+int
+uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
+{
+    if (!CONFIG_USB_UAS)
+        return DISK_RET_EBADTRACK;
+
+    struct uasdrive_s *drive = container_of(
+        op->drive_g, struct uasdrive_s, drive);
+
+    uas_ui ui;
+    memset(&ui, 0, sizeof(ui));
+    ui.hdr.id = UAS_UI_COMMAND;
+    ui.hdr.tag = 0xdead;
+    ui.command.lun[1] = drive->lun;
+    memcpy(ui.command.cdb, cdbcmd, sizeof(ui.command.cdb));
+    int ret = usb_send_bulk(GET_GLOBAL(drive->command),
+                            USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui),
+                            sizeof(ui.hdr) + sizeof(ui.command));
+    if (ret) {
+        dprintf(1, "uas: command send fail");
+        goto fail;
+    }
+
+    memset(&ui, 0xff, sizeof(ui));
+    ret = usb_send_bulk(GET_GLOBAL(drive->status),
+                        USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
+    if (ret) {
+        dprintf(1, "uas: status recv fail");
+        goto fail;
+    }
+
+    switch (ui.hdr.id) {
+    case UAS_UI_SENSE:
+        goto have_sense;
+    case UAS_UI_READ_READY:
+        ret = usb_send_bulk(GET_GLOBAL(drive->data_in),
+                            USB_DIR_IN, op->buf_fl, op->count * blocksize);
+        if (ret) {
+            dprintf(1, "uas: data read fail");
+            goto fail;
+        }
+        break;
+    case UAS_UI_WRITE_READY:
+        ret = usb_send_bulk(GET_GLOBAL(drive->data_out),
+                            USB_DIR_OUT, op->buf_fl, op->count * blocksize);
+        if (ret) {
+            dprintf(1, "uas: data write fail");
+            goto fail;
+        }
+        break;
+    default:
+        dprintf(1, "uas: unknown status ui id %d", ui.hdr.id);
+        goto fail;
+    }
+
+    memset(&ui, 0xff, sizeof(ui));
+    ret = usb_send_bulk(GET_GLOBAL(drive->status),
+                        USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
+    if (ret) {
+        dprintf(1, "uas: status recv fail");
+        goto fail;
+    }
+    if (ui.hdr.id != UAS_UI_SENSE) {
+        dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id);
+        goto fail;
+    }
+
+have_sense:
+    if (ui.sense.status == 0) {
+        return DISK_RET_SUCCESS;
+    }
+
+fail:
+    return DISK_RET_EBADTRACK;
+}
+
+static int
+uas_init_lun(struct usbdevice_s *usbdev,
+             struct usb_pipe *command, struct usb_pipe *status,
+             struct usb_pipe *data_in, struct usb_pipe *data_out,
+             int lun)
+{
+    // Allocate drive structure.
+    struct uasdrive_s *drive = malloc_fseg(sizeof(*drive));
+    if (!drive) {
+        warn_noalloc();
+        return -1;
+    }
+    memset(drive, 0, sizeof(*drive));
+    drive->drive.type = DTYPE_UAS;
+    drive->command = command;
+    drive->status = status;
+    drive->data_in = data_in;
+    drive->data_out = data_out;
+    drive->lun = lun;
+
+    int prio = bootprio_find_usb(usbdev, lun);
+    int ret = scsi_init_drive(&drive->drive, "USB UAS", prio);
+    if (ret) {
+        free(drive);
+        return -1;
+    }
+    return 0;
+}
+
+int
+usb_uas_init(struct usbdevice_s *usbdev)
+{
+    if (!CONFIG_USB_UAS)
+        return -1;
+
+    // Verify right kind of device
+    struct usb_interface_descriptor *iface = usbdev->iface;
+    if (iface->bInterfaceSubClass != 0x06 ||
+        iface->bInterfaceProtocol != 0x62) {
+        dprintf(1, "Unsupported UAS device (subclass=%02x proto=%02x)\n"
+                , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
+        return -1;
+    }
+
+    /* find & allocate pipes */
+    struct usb_endpoint_descriptor *ep = NULL;
+    struct usb_pipe *command = NULL;
+    struct usb_pipe *status = NULL;
+    struct usb_pipe *data_in = NULL;
+    struct usb_pipe *data_out = NULL;
+    u8 *desc = (u8*)iface;
+    while (desc) {
+        desc += desc[0];
+        switch (desc[1]) {
+        case USB_DT_ENDPOINT:
+            ep = (void*)desc;
+            break;
+        case 0x24:
+            switch (desc[2]) {
+            case UAS_PIPE_ID_COMMAND:
+                command = usb_alloc_pipe(usbdev, ep);
+                break;
+            case UAS_PIPE_ID_STATUS:
+                status = usb_alloc_pipe(usbdev, ep);
+                break;
+            case UAS_PIPE_ID_DATA_IN:
+                data_in = usb_alloc_pipe(usbdev, ep);
+                break;
+            case UAS_PIPE_ID_DATA_OUT:
+                data_out = usb_alloc_pipe(usbdev, ep);
+                break;
+            default:
+                goto fail;
+            }
+            break;
+        default:
+            desc = NULL;
+            break;
+        }
+    }
+    if (!command || !status || !data_in || !data_out)
+        goto fail;
+
+    /* TODO: send REPORT LUNS.  For now, only LUN 0 is recognized.  */
+    int ret = uas_init_lun(usbdev, command, status, data_in, data_out, 0);
+    if (ret < 0) {
+        dprintf(1, "Unable to configure UAS drive.\n");
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    free_pipe(command);
+    free_pipe(status);
+    free_pipe(data_in);
+    free_pipe(data_out);
+    return -1;
+}
diff --git a/src/usb-uas.h b/src/usb-uas.h
new file mode 100644
index 0000000..93951f2
--- /dev/null
+++ b/src/usb-uas.h
@@ -0,0 +1,2 @@
+int uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize);
+int usb_uas_init(struct usbdevice_s *usbdev);
diff --git a/src/usb.c b/src/usb.c
index bde7a58..c241958 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -15,6 +15,7 @@
 #include "usb-hid.h" // usb_keyboard_setup
 #include "usb-hub.h" // usb_hub_init
 #include "usb-msc.h" // usb_msc_init
+#include "usb-uas.h" // usb_uas_init
 #include "usb.h" // struct usb_s
 #include "biosvar.h" // GET_GLOBAL
 
@@ -323,9 +324,12 @@ configure_usb_device(struct usbdevice_s *usbdev)
     usbdev->imax = (void*)config + config->wTotalLength - (void*)iface;
     if (iface->bInterfaceClass == USB_CLASS_HUB)
         ret = usb_hub_init(usbdev);
-    else if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE)
-        ret = usb_msc_init(usbdev);
-    else
+    else if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE) {
+        if (iface->bInterfaceProtocol == 0x50)
+            ret = usb_msc_init(usbdev);
+        if (iface->bInterfaceProtocol == 0x62)
+            ret = usb_uas_init(usbdev);
+    } else
         ret = usb_hid_init(usbdev);
     if (ret)
         goto fail;
