From 456d0bab4f280e3fe4b8041a89385547b02454e0 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Tue, 9 May 2017 11:24:35 +0200
Subject: [PATCH 2/4] blockcmd: generic SCSI luns enumeration
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
Message-id: <20170509112437.30666-3-pbonzini@redhat.com>
Patchwork-id: 75051
O-Subject: [RHEL7.4 seabios PATCH 2/4] blockcmd: generic SCSI luns enumeration
Bugzilla: 1020622
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
RH-Acked-by: Fam Zheng <famz@redhat.com>
From: Roman Kagan <rkagan@virtuozzo.com>
Add two generic functions to discover active LUNs on a SCSI target.
The functions take a temporary drive descriptor on the target, and a
callback to create a new drive descriptor with a new LUN using the
temporary one as a template.
One of the functions performs REPORT LUNS on the temporary drive to
obtain the list of candidate luns; the other sequentially iterates the
lun numbers up to the given maximum, and is meant as a fallback. Both
functions return the number of successfully created drive descriptors,
or a negative number if an error occured.
This will allow to lift the limitation of most of the SCSI drivers that
support booting off the LUN #0 only.
Signed-off-by: Roman Kagan <rkagan@virtuozzo.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 750188dfb35f61f1533f1138d6972b19f36f1a2c)
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
src/hw/blockcmd.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/hw/blockcmd.h | 4 +++
2 files changed, 98 insertions(+)
diff --git a/src/hw/blockcmd.c b/src/hw/blockcmd.c
index 5ad128e..324188d 100644
--- a/src/hw/blockcmd.c
+++ b/src/hw/blockcmd.c
@@ -13,6 +13,7 @@
#include "std/disk.h" // DISK_RET_EPARAM
#include "string.h" // memset
#include "util.h" // timer_calc
+#include "malloc.h"
/****************************************************************
@@ -181,6 +182,99 @@ scsi_is_ready(struct disk_op_s *op)
return 0;
}
+#define CDB_CMD_REPORT_LUNS 0xA0
+
+struct cdb_report_luns {
+ u8 command;
+ u8 reserved_01[5];
+ u32 length;
+ u8 pad[6];
+} PACKED;
+
+struct scsi_lun {
+ u16 lun[4];
+};
+
+struct cdbres_report_luns {
+ u32 length;
+ u32 reserved;
+ struct scsi_lun luns[];
+};
+
+static u64 scsilun2u64(struct scsi_lun *scsi_lun)
+{
+ int i;
+ u64 ret = 0;
+ for (i = 0; i < ARRAY_SIZE(scsi_lun->lun); i++)
+ ret |= be16_to_cpu(scsi_lun->lun[i]) << (16 * i);
+ return ret;
+}
+
+// Issue REPORT LUNS on a temporary drive and iterate reported luns calling
+// @add_lun for each
+int scsi_rep_luns_scan(struct drive_s *tmp_drive, scsi_add_lun add_lun)
+{
+ int ret = -1;
+ u32 maxluns = 511;
+ u32 nluns, i;
+ struct cdb_report_luns cdb = {
+ .command = CDB_CMD_REPORT_LUNS,
+ };
+ struct disk_op_s op = {
+ .drive_gf = tmp_drive,
+ .command = CMD_SCSI,
+ .count = 1,
+ .cdbcmd = &cdb,
+ };
+ struct cdbres_report_luns *resp;
+
+ ASSERT32FLAT();
+
+ while (1) {
+ op.blocksize = sizeof(struct cdbres_report_luns) +
+ maxluns * sizeof(struct scsi_lun);
+ op.buf_fl = malloc_tmp(op.blocksize);
+ if (!op.buf_fl) {
+ warn_noalloc();
+ return -1;
+ }
+
+ cdb.length = cpu_to_be32(op.blocksize);
+ if (process_op(&op) != DISK_RET_SUCCESS)
+ goto out;
+
+ resp = op.buf_fl;
+ nluns = be32_to_cpu(resp->length) / sizeof(struct scsi_lun);
+ if (nluns <= maxluns)
+ break;
+
+ free(op.buf_fl);
+ maxluns = nluns;
+ }
+
+ for (i = 0, ret = 0; i < nluns; i++) {
+ u64 lun = scsilun2u64(&resp->luns[i]);
+ if (lun >> 32)
+ continue;
+ ret += !add_lun((u32)lun, tmp_drive);
+ }
+out:
+ free(op.buf_fl);
+ return ret;
+}
+
+// Iterate LUNs on the target and call @add_lun for each
+int scsi_sequential_scan(struct drive_s *tmp_drive, u32 maxluns,
+ scsi_add_lun add_lun)
+{
+ int ret;
+ u32 lun;
+
+ for (lun = 0, ret = 0; lun < maxluns; lun++)
+ ret += !add_lun(lun, tmp_drive);
+ return ret;
+}
+
// Validate drive, find block size / sector count, and register drive.
int
scsi_drive_setup(struct drive_s *drive, const char *s, int prio)
diff --git a/src/hw/blockcmd.h b/src/hw/blockcmd.h
index b543f85..f18543e 100644
--- a/src/hw/blockcmd.h
+++ b/src/hw/blockcmd.h
@@ -106,5 +106,9 @@ int scsi_is_read(struct disk_op_s *op);
int scsi_is_ready(struct disk_op_s *op);
struct drive_s;
int scsi_drive_setup(struct drive_s *drive, const char *s, int prio);
+typedef int (*scsi_add_lun)(u32 lun, struct drive_s *tmpl_drv);
+int scsi_rep_luns_scan(struct drive_s *tmp_drive, scsi_add_lun add_lun);
+int scsi_sequential_scan(struct drive_s *tmp_drive, u32 maxluns,
+ scsi_add_lun add_lun);
#endif // blockcmd.h
--
1.8.3.1