
From: Simon Glass <sjg@chromium.org> It is often possible to use virtio-blk for block devices, but it is less flexible than SCSI. SCSI provides a means to probe for multiple disks through the same interface. It also supports hotplug and other features. This series adds a simple virtio-scsi driver for use with QEMU. The new driver creates a SCSI bus when the virtio buses are scanned. That can be scanned in turn, using 'scsi scan', to find the available virtio disks. For this to work, quite a few minor updates are needed in the SCSI implementation: - Increase the buffer sizes for commands and sense buffer - Fix off-by-one error when scanning for devices - Use REPORT LUN to determine what LUNs are present for a target - Clean up some open-coded constants - Make use of a struct to decode the IDENTIFY response - Clean up of the confusing and error-prone SCSI read/write functions - Add support for using a scsi disk to the build-qemu/efi scripts When a SCSI disk is inaccessible (e.g. the LUN is wrong), this is seldom reported as an error. For example 'part list' does not say 'read error' but 'unsupported partition type'. Probing the block device results in each partition-type driver reading blocks, none of which reports the error. So an attempt is made here to clean this up, so that bad LUNs show an error. Another clean-up is to show partition numbers in hex with 'part list', since the current approach conflicts with the 'part' command. Some additional x86 debugging is added for the jump from SPL to U-Boot proper, since this can be confusing when it fails in QEMU. With all of the above, virtio-scsi can be used on qemu-x86_64 for loading an OS and associated files, including with standard boot. Simon Glass (26): x86: Show jump address in SPL when debugging x86: Add some info on the memory map scripts: Move root-disk processing to helper scripts: Support a root disk connected via virtio-scsi part: Detect errors when reading partition tables part: Show partition numbers in hex scsi: Expand the maximum buffer sizes scsi: Add a constant for the tempbuff size scsi: Fix off-by-one error when scanning scsi: Use hex in block-device names scsi: Use REPORT LUNS to scan the correct number of LUNs scsi: Provide some response flags scsi: Use a struct to decode the inquiry response scsi: Rename block_dev and dev_desc to desc scsi: Create a function to add command and LUN to the ccb scsi: Only add the LUN to the command if needed scsi: Check SCSI spec version for LUN handling scsi: Tidy up error returns for read/write scsi: Use a variable for the number of blocks to process scsi: Reorder the local variables scsi: Use mapmem functions scsi: Move loop counters down next to the loop scsi: Adjust loop counters once in the loop virtio: Bring in the virtio_scsi header virtio: Add support for virtio-scsi virtio: Add debugging of driver features and device type arch/x86/lib/spl.c | 8 +- disk/part.c | 28 ++- disk/part_amiga.c | 2 +- disk/part_dos.c | 15 +- disk/part_efi.c | 20 +- disk/part_iso.c | 18 +- disk/part_mac.c | 16 +- doc/arch/x86/x86.rst | 17 ++ drivers/block/blk-uclass.c | 3 +- drivers/block/ide.c | 3 +- drivers/scsi/scsi.c | 348 +++++++++++++++++++++------------ drivers/virtio/Kconfig | 12 ++ drivers/virtio/Makefile | 1 + drivers/virtio/virtio-uclass.c | 6 +- drivers/virtio/virtio_scsi.c | 249 +++++++++++++++++++++++ drivers/virtio/virtio_scsi.h | 175 +++++++++++++++++ include/blk.h | 1 + include/dt-bindings/virtio.h | 1 + include/part.h | 15 +- include/scsi.h | 24 ++- include/virtio.h | 1 + scripts/build-efi | 9 - scripts/build-qemu | 11 +- scripts/build_helper.py | 19 ++ 24 files changed, 817 insertions(+), 185 deletions(-) create mode 100644 drivers/virtio/virtio_scsi.c create mode 100644 drivers/virtio/virtio_scsi.h -- 2.43.0 base-commit: 08a84c7cc73841f24dcdebe876b1aad7c8058890 branch: qeme

From: Simon Glass <sjg@chromium.org> Add debugging info for the jump address and range, to assist in checking the memory map and usage. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/x86/lib/spl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/x86/lib/spl.c b/arch/x86/lib/spl.c index a9abc045ca2..0b64dad6435 100644 --- a/arch/x86/lib/spl.c +++ b/arch/x86/lib/spl.c @@ -261,6 +261,12 @@ static int spl_board_load_image(struct spl_image_info *spl_image, if (spl_image->load_addr != spl_get_image_pos()) { /* Copy U-Boot from ROM */ + log_debug("copy to %lx-%lx from %lx-%lx size %lx\n", + spl_image->load_addr, + spl_image->load_addr + spl_get_image_size(), + spl_get_image_pos(), + spl_get_image_pos() + spl_get_image_size(), + spl_get_image_size()); memcpy((void *)spl_image->load_addr, (void *)spl_get_image_pos(), spl_get_image_size()); } @@ -281,7 +287,7 @@ void __noreturn jump_to_image(struct spl_image_info *spl_image) { int ret; - log_debug("Jumping to 64-bit U-Boot\n"); + log_debug("Jumping to 64-bit U-Boot at %lx\n", spl_image->entry_point); ret = cpu_jump_to_64bit_uboot(spl_image->entry_point); debug("ret=%d\n", ret); hang(); -- 2.43.0

From: Simon Glass <sjg@chromium.org> The memory map is quite complex on x86. Add a few notes into the documentation. Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/arch/x86/x86.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/doc/arch/x86/x86.rst b/doc/arch/x86/x86.rst index f67216d6ce0..8f852a56243 100644 --- a/doc/arch/x86/x86.rst +++ b/doc/arch/x86/x86.rst @@ -475,6 +475,23 @@ CONFIG_GENERATE_SMBIOS_TABLE option. The easiest way to provide the values to use is via the device tree. For details see :download:`smbios.txt <../../device-tree-bindings/sysinfo/smbios.txt>`. +Memory Map +---------- + +The x86 memory map is fairly varied between devices, but the following table +shows a few common elements: + +======== ======================== ========================================== +Addr Config Usage +======== ======================== ========================================== + 0 - Bottom of stack for first phase (e.g. SPL) + 10000 CONFIG_SYS_CAR_ADDR Stack grows down from here + 10000 CONFIG_BLOBLIST_ADDR Blob list + 80000 PAGETABLE_BASE Base address of page tables (24K) + f0000 CONFIG_ROM_TABLE_ADDR Base address of ROM tables +======== ======================== ========================================== + + TODO List --------- - Audio -- 2.43.0

From: Simon Glass <sjg@chromium.org> The build-efi and build-qemu scripts have common code to set up the root disk. Move it to build_helper to avoid duplication. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-efi | 9 --------- scripts/build-qemu | 9 --------- scripts/build_helper.py | 9 +++++++++ 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/scripts/build-efi b/scripts/build-efi index 0ec1e63c950..adf37cd4aef 100755 --- a/scripts/build-efi +++ b/scripts/build-efi @@ -113,15 +113,6 @@ class BuildEfi: if self.args.kvm: extra.extend(['-enable-kvm', '-cpu', 'host']) - img_fname = Path(self.args.disk) if self.args.disk else None - if img_fname: - if img_fname.exists(): - extra.extend([ - '-drive', - f'if=virtio,file={img_fname},format=raw,id=hd1']) - else: - tout.warning(f"Disk image '{img_fname}' not found") - print(f'Running {qemu}{serial_msg}') # Use 512MB since U-Boot EFI likes to have 256MB to play with diff --git a/scripts/build-qemu b/scripts/build-qemu index a6676eca350..be36d0826f4 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -127,7 +127,6 @@ class BuildQemu: f'{bios_override}') self.seq_fname = Path(args.sct_seq) if args.sct_seq else None - self.img_fname = Path(args.disk) if args.disk else None # arch-specific setup if args.arch == 'arm': @@ -284,14 +283,6 @@ class BuildQemu: # Add other parameters gathered from options qemu_cmd.extend(self.qemu_extra) - if self.img_fname: - if self.img_fname.exists(): - qemu_cmd.extend([ - '-drive', - f'if=virtio,file={self.img_fname},format=raw,id=hd1']) - else: - tout.warning(f"Disk image '{self.img_fname}' not found") - sock = Path('/tmp/virtiofs.sock') proc = None if self.args.share_dir: diff --git a/scripts/build_helper.py b/scripts/build_helper.py index 09fac2d2335..86bd1d42df6 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -43,6 +43,7 @@ class Helper: self.os_arch = 'amd64' else: self.os_arch = 'i386' + self.img_fname = Path(args.disk) if args.disk else None def read_settings(self): """Get settings from the settings file""" @@ -160,6 +161,14 @@ sct_mnt = /mnt/sct '-drive', f'if=virtio,file={os_path},format=raw,id=hd0,readonly=on']) + if self.img_fname: + if self.img_fname.exists(): + cmd.extend([ + '-drive', + f'if=virtio,file={self.img_fname},format=raw,id=hd1']) + else: + tout.warning(f"Disk image '{self.img_fname}' not found") + def add_common_args(parser): """Add some arguments which are common to build-efi/qemu scripts -- 2.43.0

From: Simon Glass <sjg@chromium.org> Sometimes it is useful to use SCSI instead of the plain virtio. Add a -S option to connect the root disk via SCSI. Drop the -S for --sct-seq since it is a less common option. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-qemu | 2 +- scripts/build_helper.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index be36d0826f4..c96f65f922f 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -52,7 +52,7 @@ def parse_args(): parser.add_argument('-x', '--xpl', action='store_true', help='Use xPL image rather than U-Boot proper') parser.add_argument( - '-S', '--sct-seq', + '--sct-seq', help='SCT sequence-file to be written into the SCT image if -e') parser.add_argument('-v', '--verbose', action='store_true', help='Show executed commands') diff --git a/scripts/build_helper.py b/scripts/build_helper.py index 86bd1d42df6..160e9014a9a 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -26,6 +26,8 @@ from u_boot_pylib import tools from u_boot_pylib import tout import fs_helper +MODERN_PCI = 'disable-legacy=on,disable-modern=off' + class Helper: def __init__(self, args): @@ -163,13 +165,18 @@ sct_mnt = /mnt/sct if self.img_fname: if self.img_fname.exists(): + iface = 'none' if args.scsi else 'virtio' + if args.scsi: + cmd.extend([ + '-device', + f'virtio-scsi-pci,id=scsi0,{MODERN_PCI}', + '-device', 'scsi-hd,bus=scsi0.0,drive=hd0']) cmd.extend([ '-drive', - f'if=virtio,file={self.img_fname},format=raw,id=hd1']) + f'if={iface},file={self.img_fname},format=raw,id=hd0']) else: tout.warning(f"Disk image '{self.img_fname}' not found") - def add_common_args(parser): """Add some arguments which are common to build-efi/qemu scripts @@ -200,6 +207,9 @@ def add_common_args(parser): help='Select OS release version (e.g, 24.04) Default: 24.04.1') parser.add_argument('-s', '--serial-only', action='store_true', help='Run QEMU with serial only (no display)') + parser.add_argument( + '-S', '--scsi', action='store_true', + help='Attach root disk using virtio-sci instead of virtio-blk') parser.add_argument( '-t', '--root', help='Pass the given root device to linux via root=xxx') -- 2.43.0

From: Simon Glass <sjg@chromium.org> The logic for part_init() is not ideal, since it silently ignores read errors. Each partition type tries to read the disk and presumably fails as well. No error is reported, however, so there is no indication that anything is wrong. Update the function to return an error, and update each of the probe functions to do the same, trying to minimise changes. Signed-off-by: Simon Glass <sjg@chromium.org> --- disk/part.c | 23 +++++++++++++++++------ disk/part_amiga.c | 2 +- disk/part_dos.c | 13 ++++++++----- disk/part_efi.c | 12 +++++++++--- disk/part_iso.c | 16 ++++++++++++++-- disk/part_mac.c | 16 ++++++++++++++-- drivers/block/blk-uclass.c | 3 ++- include/part.h | 15 ++++++++++++--- 8 files changed, 77 insertions(+), 23 deletions(-) diff --git a/disk/part.c b/disk/part.c index 3aab99b069b..d922fddb415 100644 --- a/disk/part.c +++ b/disk/part.c @@ -4,6 +4,8 @@ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. */ +#define LOG_CATEGORY UCLASS_PARTITION + #include <blk.h> #include <command.h> #include <env.h> @@ -276,33 +278,42 @@ void dev_print(struct blk_desc *desc) } } -void part_init(struct blk_desc *desc) +int part_init(struct blk_desc *desc) { struct part_driver *drv = ll_entry_start(struct part_driver, part_driver); const int n_ents = ll_entry_count(struct part_driver, part_driver); struct part_driver *entry; + int ret; blkcache_invalidate(desc->uclass_id, desc->devnum); if (desc->part_type != PART_TYPE_UNKNOWN) { for (entry = drv; entry != drv + n_ents; entry++) { - if (entry->part_type == desc->part_type && !entry->test(desc)) - return; + if (entry->part_type != desc->part_type) + continue; + ret = entry->test(desc); + log_debug("try '%s': ret=%d\n", entry->name, ret); + if (ret == -EIO) + return ret; } } desc->part_type = PART_TYPE_UNKNOWN; for (entry = drv; entry != drv + n_ents; entry++) { - int ret; - ret = entry->test(desc); - debug("%s: try '%s': ret=%d\n", __func__, entry->name, ret); + log_debug("try '%s': ret=%d\n", entry->name, ret); + if (ret == -EIO) + return ret; if (!ret) { desc->part_type = entry->part_type; break; } } + if (ret) + return -ENOENT; + + return 0; } static void print_part_header(const char *type, struct blk_desc *desc) diff --git a/disk/part_amiga.c b/disk/part_amiga.c index 5b8ae5762d3..8921db6495b 100644 --- a/disk/part_amiga.c +++ b/disk/part_amiga.c @@ -227,7 +227,7 @@ static int part_test_amiga(struct blk_desc *desc) else { PRINTF("part_test_amiga: no RDB found\n"); - return -1; + return -ENOENT; } } diff --git a/disk/part_dos.c b/disk/part_dos.c index 96f748702fd..c99138bfb0f 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -20,6 +20,7 @@ #include <vsprintf.h> #include <asm/unaligned.h> #include <linux/compiler.h> +#include <linux/err.h> #include "part_dos.h" #include <part.h> @@ -103,12 +104,14 @@ static int part_test_dos(struct blk_desc *desc) #ifndef CONFIG_XPL_BUILD ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, DIV_ROUND_UP(desc->blksz, sizeof(legacy_mbr))); + long ret; - if (blk_dread(desc, 0, 1, (ulong *)mbr) != 1) - return -1; + ret = blk_dread(desc, 0, 1, (ulong *)mbr); + if (IS_ERR_VALUE(ret)) + return ret; if (test_block_type((unsigned char *)mbr) != DOS_MBR) - return -1; + return -ENOENT; if (desc->sig_type == SIG_TYPE_NONE && mbr->unique_mbr_signature) { desc->sig_type = SIG_TYPE_MBR; @@ -118,10 +121,10 @@ static int part_test_dos(struct blk_desc *desc) ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, desc->blksz); if (blk_dread(desc, 0, 1, (ulong *)buffer) != 1) - return -1; + return -EIO; if (test_block_type(buffer) != DOS_MBR) - return -1; + return -ENOENT; #endif return 0; diff --git a/disk/part_efi.c b/disk/part_efi.c index 932d058c184..fef62689648 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -28,6 +28,7 @@ #include <dm/ofnode.h> #include <linux/compiler.h> #include <linux/ctype.h> +#include <linux/err.h> #include <linux/printk.h> #include <u-boot/crc.h> @@ -315,10 +316,14 @@ static int __maybe_unused part_get_info_efi(struct blk_desc *desc, int part, static int part_test_efi(struct blk_desc *desc) { ALLOC_CACHE_ALIGN_BUFFER_PAD(legacy_mbr, legacymbr, 1, desc->blksz); + long ret; /* Read legacy MBR from block 0 and validate it */ - if ((blk_dread(desc, 0, 1, (ulong *)legacymbr) != 1) - || (is_pmbr_valid(legacymbr) != 1)) { + ret = blk_dread(desc, 0, 1, (ulong *)legacymbr); + if (IS_ERR_VALUE(ret)) + return ret; + + if (ret != 1 || is_pmbr_valid(legacymbr) != 1) { /* * TegraPT is compatible with EFI part, but it * cannot pass the Protective MBR check. Skip it @@ -330,8 +335,9 @@ static int part_test_efi(struct blk_desc *desc) desc->uclass_id == UCLASS_MMC && !desc->devnum) return 0; - return -1; + return -ENOENT; } + return 0; } diff --git a/disk/part_iso.c b/disk/part_iso.c index 6e05b2feffb..3a159f1d023 100644 --- a/disk/part_iso.c +++ b/disk/part_iso.c @@ -9,6 +9,7 @@ #include <part.h> #include <asm/cache.h> #include <asm/unaligned.h> +#include <linux/err.h> #include "part_iso.h" /* #define ISO_PART_DEBUG */ @@ -56,13 +57,17 @@ int part_get_info_iso_verb(struct blk_desc *desc, int part_num, iso_pri_rec_t *ppr = (iso_pri_rec_t *)tmpbuf; /* primary desc */ iso_val_entry_t *pve = (iso_val_entry_t *)tmpbuf; iso_init_def_entry_t *pide; + long ret; if (desc->blksz != CD_SECTSIZE && desc->blksz != 512) return -1; /* the first sector (sector 0x10) must be a primary volume desc */ blkaddr=PVD_OFFSET; - if (iso_dread(desc, PVD_OFFSET, 1, (ulong *)tmpbuf) != 1) + ret = iso_dread(desc, PVD_OFFSET, 1, (ulong *)tmpbuf); + if (IS_ERR_VALUE(ret)) + return ret; + if (ret != 1) return -1; if(ppr->desctype!=0x01) { if(verb) @@ -226,8 +231,15 @@ static void part_print_iso(struct blk_desc *desc) static int part_test_iso(struct blk_desc *desc) { struct disk_partition info; + int ret; - return part_get_info_iso_verb(desc, 1, &info, 0); + ret = part_get_info_iso_verb(desc, 1, &info, 0); + if (ret == -1) + return -ENOENT; + else if (ret) + return ret; + + return 0; } U_BOOT_PART_TYPE(iso) = { diff --git a/disk/part_mac.c b/disk/part_mac.c index 21c85942fd8..93b54df0d39 100644 --- a/disk/part_mac.c +++ b/disk/part_mac.c @@ -18,6 +18,7 @@ #include <ide.h> #include "part_mac.h" #include <part.h> +#include <linux/err.h> /* stdlib.h causes some compatibility problems; should fixe these! -- wd */ #ifndef __ldiv_t_defined @@ -41,8 +42,13 @@ static int part_test_mac(struct blk_desc *desc) ALLOC_CACHE_ALIGN_BUFFER(mac_driver_desc_t, ddesc, 1); ALLOC_CACHE_ALIGN_BUFFER(mac_partition_t, mpart, 1); ulong i, n; + long ret; - if (part_mac_read_ddb(desc, ddesc)) { + ret = part_mac_read_ddb(desc, ddesc); + if (ret && ret != -1) + return ret; + + if (ret) { /* * error reading Driver Descriptor Block, * or no valid Signature @@ -151,7 +157,13 @@ static void part_print_mac(struct blk_desc *desc) */ static int part_mac_read_ddb(struct blk_desc *desc, mac_driver_desc_t *ddb_p) { - if (blk_dread(desc, 0, 1, (ulong *)ddb_p) != 1) { + long ret; + + ret = blk_dread(desc, 0, 1, (ulong *)ddb_p); + if (IS_ERR_VALUE(ret)) + return ret; + + if (ret != 1) { debug("** Can't read Driver Descriptor Block **\n"); return (-1); } diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index f06b2ce3887..0a69dccde84 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -855,7 +855,8 @@ static int blk_post_probe(struct udevice *dev) if (CONFIG_IS_ENABLED(PARTITIONS) && blk_enabled()) { struct blk_desc *desc = dev_get_uclass_plat(dev); - part_init(desc); + if (part_init(desc) == -EIO) + log_warning("Error reading from device\n"); if (desc->part_type != PART_TYPE_UNKNOWN && part_create_block_devices(dev)) diff --git a/include/part.h b/include/part.h index 0fd7c18d6b6..70cb430a0ff 100644 --- a/include/part.h +++ b/include/part.h @@ -232,7 +232,15 @@ int part_get_info_whole_disk(struct blk_desc *desc, struct disk_partition *info); void part_print(struct blk_desc *desc); -void part_init(struct blk_desc *desc); + +/** + * part_init() - Try to find a partition table on a block device + * + * @desc: Block descriptor to check + * Return 0 if found, -ENOENT if not, -EIO if a read failed + */ +int part_init(struct blk_desc *desc); + void dev_print(struct blk_desc *desc); /** @@ -370,7 +378,7 @@ static inline int part_get_info_whole_disk(struct blk_desc *desc, struct disk_partition *info) { return -1; } static inline void part_print(struct blk_desc *desc) {} -static inline void part_init(struct blk_desc *desc) {} +static inline int part_init(struct blk_desc *desc) { return 0; } static inline void dev_print(struct blk_desc *desc) {} static inline int blk_get_device_by_str(const char *ifname, const char *dev_str, struct blk_desc **desc) @@ -488,7 +496,8 @@ struct part_driver { * @test.desc: Block device descriptor * @test.Return: * 0 if the block device appears to contain this partition type, - * -ve if not + * -ENOENT if not, -EIO if there was an I/O error, other value if some + * other error occurred */ int (*test)(struct blk_desc *desc); }; -- 2.43.0

From: Simon Glass <sjg@chromium.org> The 'part' command uses hex for partition numbers, so show them in hex as well, to avoid confusion. Signed-off-by: Simon Glass <sjg@chromium.org> --- disk/part_dos.c | 2 +- disk/part_efi.c | 8 ++++---- disk/part_iso.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/disk/part_dos.c b/disk/part_dos.c index c99138bfb0f..1afb7f77299 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -54,7 +54,7 @@ static void print_one_part(dos_partition_t *p, lbaint_t ext_part_sector, lbaint_t lba_start = ext_part_sector + get_unaligned_le32(p->start4); lbaint_t lba_size = get_unaligned_le32(p->size4); - printf("%3d\t%-10" LBAFlength "u\t%-10" LBAFlength + printf("%3x\t%-10" LBAFlength "u\t%-10" LBAFlength "u\t%08x-%02x\t%02x%s%s\n", part_num, lba_start, lba_size, disksig, part_num, p->sys_ind, (is_extended(p->sys_ind) ? " Extd" : ""), diff --git a/disk/part_efi.c b/disk/part_efi.c index fef62689648..d9994e0f61b 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -240,10 +240,10 @@ static void __maybe_unused part_print_efi(struct blk_desc *desc) if (!is_pte_valid(&gpt_pte[i])) continue; - printf("%3d\t0x%08llx\t0x%08llx\t\"%s\"\n", (i + 1), - le64_to_cpu(gpt_pte[i].starting_lba), - le64_to_cpu(gpt_pte[i].ending_lba), - print_efiname(&gpt_pte[i])); + printf("%3x\t0x%08llx\t0x%08llx\t\"%s\"\n", (i + 1), + le64_to_cpu(gpt_pte[i].starting_lba), + le64_to_cpu(gpt_pte[i].ending_lba), + print_efiname(&gpt_pte[i])); printf("\tattrs:\t0x%016llx\n", gpt_pte[i].attributes.raw); uuid = (unsigned char *)gpt_pte[i].partition_type_guid.b; if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) diff --git a/disk/part_iso.c b/disk/part_iso.c index 3a159f1d023..357f9236f93 100644 --- a/disk/part_iso.c +++ b/disk/part_iso.c @@ -222,7 +222,7 @@ static void part_print_iso(struct blk_desc *desc) printf("Part Start Sect x Size Type\n"); i=1; do { - printf(" %2d %8" LBAFlength "u %8" LBAFlength "u %6ld %.32s\n", + printf(" %2x %8" LBAFlength "u %8" LBAFlength "u %6ld %.32s\n", i, info.start, info.size, info.blksz, info.type); i++; } while (part_get_info_iso_verb(desc, i, &info, 0) != -1); -- 2.43.0

From: Simon Glass <sjg@chromium.org> QEMU's virtio-scsi likes to have larger command/response buffers than U-Boot so increase these accordingly. Add some #defines for the new sizes. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/scsi.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/scsi.h b/include/scsi.h index b18ae37b861..d3bf4ed2b9c 100644 --- a/include/scsi.h +++ b/include/scsi.h @@ -12,6 +12,9 @@ struct udevice; +#define CMD_BUF_SIZE 32 +#define SENSE_BUF_LEN 96 + /** * struct scsi_cmd - information about a SCSI command to be processed * @@ -34,8 +37,8 @@ struct udevice; * @dma_dir: Direction of data structure */ struct scsi_cmd { - unsigned char cmd[16]; - unsigned char sense_buf[64] + unsigned char cmd[CMD_BUF_SIZE]; + unsigned char sense_buf[SENSE_BUF_LEN] __attribute__((aligned(ARCH_DMA_MINALIGN))); unsigned char status; unsigned char target; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Use a constant for this size, rather than open-coding the value. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index bcdeda95ed1..f05ef21cfd0 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -22,7 +22,10 @@ static struct scsi_cmd tempccb; /* temporary scsi command buffer */ -DEFINE_CACHE_ALIGN_BUFFER(u8, tempbuff, 512); /* temporary data buffer */ +/* temporary data buffer */ +#define TEMPBUFF_SIZE 512 + +DEFINE_CACHE_ALIGN_BUFFER(u8, tempbuff, TEMPBUFF_SIZE); /* almost the maximum amount of the scsi_ext command.. */ #define SCSI_MAX_BLK 0xFFFF @@ -404,7 +407,7 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, pccb->target = target; pccb->lun = lun; pccb->pdata = tempbuff; - pccb->datalen = 512; + pccb->datalen = TEMPBUFF_SIZE; pccb->dma_dir = DMA_FROM_DEVICE; scsi_setup_inquiry(pccb); if (scsi_exec(dev, pccb)) { -- 2.43.0

From: Simon Glass <sjg@chromium.org> The maximum values of target and LUN should be used, rather than one less than those values. Fix this. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index f05ef21cfd0..58ac40fb3dd 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -529,6 +529,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) return log_msg_ret("bd", ret); if (verbose) { + log_debug("id %x lun %x:\n", id, lun); printf(" Device %d: ", bdesc->devnum); dev_print(bdesc); } @@ -550,8 +551,8 @@ int scsi_scan_dev(struct udevice *dev, bool verbose) /* Get controller plat */ uc_plat = dev_get_uclass_plat(dev); - for (i = 0; i < uc_plat->max_id; i++) - for (lun = 0; lun < uc_plat->max_lun; lun++) + for (i = 0; i <= uc_plat->max_id; i++) + for (lun = 0; lun <= uc_plat->max_lun; lun++) do_scsi_scan_one(dev, i, lun, verbose); return 0; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Use hex values to fit better with how these devices are accessed with the 'part list' command and others. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 58ac40fb3dd..560f7fe35ec 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -492,7 +492,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) * to make sure that there won't be a lot of * block devices created */ - snprintf(str, sizeof(str), "id%dlun%d", id, lun); + snprintf(str, sizeof(str), "id%xlun%x", id, lun); name = strdup(str); if (!name) return log_msg_ret("nam", -ENOMEM); -- 2.43.0

From: Simon Glass <sjg@chromium.org> The current algortihm scans all LUNs on all targets. Where the potential number is very large, as with QEMU (256 targets each with 4K LUNs) this is a substantial waste of time. Use the REPORT LUNS command to determine the number of LUNs on each target. Where that fails, fall back to scanning everything. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 77 ++++++++++++++++++++++++++++++++++++++++++--- include/scsi.h | 1 + 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 560f7fe35ec..fee0471c2e4 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -17,6 +17,7 @@ #include <part.h> #include <pci.h> #include <scsi.h> +#include <asm/unaligned.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> @@ -383,6 +384,58 @@ static void scsi_init_dev_desc_priv(struct blk_desc *dev_desc) #endif /* CONFIG_BOUNCE_BUFFER */ } +/** + * scsi_count_luns() - Count the number of LUNs on a given target + * + * @dev: SCSI device + * @target: Target to look up + * Return: Number of LUNs on this target, or 0 if None, or -ve on error + */ +static int scsi_count_luns(struct udevice *dev, uint target) +{ + struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; + uint rec_count; + int ret, i, max_lun; + u8 *luns; + + memset(pccb->cmd, '\0', sizeof(pccb->cmd)); + pccb->cmd[0] = SCSI_REPORT_LUNS; + pccb->target = target; + pccb->lun = 0; + + /* Select Report: 0x00 for all LUNs */ + pccb->cmd[2] = 0; + put_unaligned_be32(TEMPBUFF_SIZE, &pccb->cmd[6]); + + pccb->cmdlen = 12; + pccb->pdata = tempbuff; + pccb->datalen = TEMPBUFF_SIZE; + pccb->dma_dir = DMA_FROM_DEVICE; + + ret = scsi_exec(dev, pccb); + if (ret == -ENODEV) { + /* target doesn't exist, return 0 */ + return 0; + } + if (ret) { + scsi_print_error(pccb); + return -EINVAL; + } + + /* find the maximum LUN */ + rec_count = get_unaligned_be32(tempbuff) / 8; + luns = &tempbuff[8]; + + max_lun = -1; + for (i = 0; i < rec_count; i++, luns += 8) { + int lun = get_unaligned_be16(luns) & 0x3fff; + + max_lun = max(max_lun, lun); + } + + return max_lun + 1; +} + /** * scsi_detect_dev - Detect scsi device * @@ -539,9 +592,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) int scsi_scan_dev(struct udevice *dev, bool verbose) { struct scsi_plat *uc_plat; /* scsi controller plat */ - int ret; - int i; - int lun; + int ret, i; /* probe SCSI controller driver */ ret = device_probe(dev); @@ -551,9 +602,25 @@ int scsi_scan_dev(struct udevice *dev, bool verbose) /* Get controller plat */ uc_plat = dev_get_uclass_plat(dev); - for (i = 0; i <= uc_plat->max_id; i++) - for (lun = 0; lun <= uc_plat->max_lun; lun++) + log_debug("max_id %lx max_lun %lx\n", uc_plat->max_id, + uc_plat->max_lun); + + for (i = 0; i <= uc_plat->max_id; i++) { + uint lun_count, lun; + + /* try to count the number of LUNs */ + ret = scsi_count_luns(dev, i); + if (ret < 0) + lun_count = uc_plat->max_lun + 1; + else + lun_count = ret; + if (!lun_count) + continue; + log_debug("Target %x: scanning up to LUN %x\n", i, + lun_count - 1); + for (lun = 0; lun < lun_count; lun++) do_scsi_scan_one(dev, i, lun, verbose); + } return 0; } diff --git a/include/scsi.h b/include/scsi.h index d3bf4ed2b9c..b81bc762677 100644 --- a/include/scsi.h +++ b/include/scsi.h @@ -184,6 +184,7 @@ struct scsi_cmd { #define SCSI_WRT_VERIFY 0x2E /* Write and Verify (O) */ #define SCSI_WRITE_LONG 0x3F /* Write Long (O) */ #define SCSI_WRITE_SAME 0x41 /* Write Same (O) */ +#define SCSI_REPORT_LUNS 0xa0 /* Report LUNs */ /** * enum scsi_cmd_phase - current phase of the SCSI protocol -- 2.43.0

From: Simon Glass <sjg@chromium.org> Rather than open-coding the SCSI-inquiry-response flags, add an enum and use that. Signed-off-by: Simon Glass <sjg@chromium.org> --- disk/part.c | 5 +++-- drivers/block/ide.c | 3 ++- drivers/scsi/scsi.c | 4 ++-- include/scsi.h | 8 ++++++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/disk/part.c b/disk/part.c index d922fddb415..7a3b87825be 100644 --- a/disk/part.c +++ b/disk/part.c @@ -14,6 +14,7 @@ #include <log.h> #include <malloc.h> #include <part.h> +#include <scsi.h> #include <ubifs_uboot.h> #include <dm/uclass.h> @@ -221,7 +222,7 @@ void dev_print(struct blk_desc *desc) puts (" Type: "); if (desc->removable) puts ("Removable "); - switch (desc->type & 0x1F) { + switch (desc->type & SCSIRF_TYPE_MASK) { case DEV_TYPE_HARDDISK: puts ("Hard Disk"); break; @@ -235,7 +236,7 @@ void dev_print(struct blk_desc *desc) puts ("Tape"); break; default: - printf("# %02X #", desc->type & 0x1F); + printf("# %02X #", desc->type & SCSIRF_TYPE_MASK); break; } puts ("\n"); diff --git a/drivers/block/ide.c b/drivers/block/ide.c index cab5e1bc92b..85d7c1f9720 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -13,6 +13,7 @@ #include <ide.h> #include <log.h> #include <part.h> +#include <scsi.h> #include <watchdog.h> #include <asm/io.h> #include <linux/delay.h> @@ -476,7 +477,7 @@ static void atapi_inquiry(struct blk_desc *desc) desc->lba = 0; desc->blksz = 0; desc->log2blksz = LOG2_INVALID(typeof(desc->log2blksz)); - desc->type = iobuf[0] & 0x1f; + desc->type = iobuf[0] & SCSIRF_TYPE_MASK; if (iobuf[1] & 0x80) desc->removable = 1; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index fee0471c2e4..ea4e3e6a2d2 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -478,9 +478,9 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, } perq = tempbuff[0]; modi = tempbuff[1]; - if ((perq & 0x1f) == 0x1f) + if ((perq & SCSIRF_TYPE_MASK) == SCSIRF_TYPE_UNKNOWN) return -ENODEV; /* skip unknown devices */ - if ((modi & 0x80) == 0x80) /* drive is removable */ + if (modi & SCSIRF_FLAGS_REMOVABLE) /* drive is removable */ dev_desc->removable = true; /* get info for this device */ scsi_ident_cpy((unsigned char *)dev_desc->vendor, diff --git a/include/scsi.h b/include/scsi.h index b81bc762677..da4f1ed08b7 100644 --- a/include/scsi.h +++ b/include/scsi.h @@ -8,6 +8,7 @@ #include <asm/cache.h> #include <bouncebuf.h> +#include <linux/bitops.h> #include <linux/dma-direction.h> struct udevice; @@ -199,6 +200,13 @@ enum scsi_cmd_phase { SCSIPH_STATUS, }; +enum scsi_resp_t { + SCSIRF_TYPE_MASK = 0x1f, + SCSIRF_TYPE_UNKNOWN = 0x1f, + + SCSIRF_FLAGS_REMOVABLE = BIT(8), +}; + /** * struct scsi_inquiry_resp - holds a SCSI inquiry command * -- 2.43.0

From: Simon Glass <sjg@chromium.org> We have a struct for the response data now, so use that instead of accessing the bytes directly. Move setting of the device type higher, so that the field is not overwritten by the following commands. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index ea4e3e6a2d2..dd45f1089be 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -264,8 +264,7 @@ static int scsi_buffer_aligned(struct udevice *dev, struct bounce_buffer *state) /* copy src to dest, skipping leading and trailing blanks * and null terminate the string */ -static void scsi_ident_cpy(unsigned char *dest, unsigned char *src, - unsigned int len) +static void scsi_ident_cpy(char *dest, const char *src, uint len) { int start, end; @@ -451,10 +450,10 @@ static int scsi_count_luns(struct udevice *dev, uint target) static int scsi_detect_dev(struct udevice *dev, int target, int lun, struct blk_desc *dev_desc) { - unsigned char perq, modi; lbaint_t capacity; unsigned long blksz; struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; + const struct scsi_inquiry_resp *resp; int count, err; pccb->target = target; @@ -476,21 +475,22 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, scsi_print_error(pccb); return -ENODEV; } - perq = tempbuff[0]; - modi = tempbuff[1]; - if ((perq & SCSIRF_TYPE_MASK) == SCSIRF_TYPE_UNKNOWN) + resp = (const struct scsi_inquiry_resp *)tempbuff; + if ((resp->type & SCSIRF_TYPE_MASK) == SCSIRF_TYPE_UNKNOWN) return -ENODEV; /* skip unknown devices */ - if (modi & SCSIRF_FLAGS_REMOVABLE) /* drive is removable */ + if (resp->flags & SCSIRF_FLAGS_REMOVABLE) /* drive is removable */ dev_desc->removable = true; /* get info for this device */ - scsi_ident_cpy((unsigned char *)dev_desc->vendor, - &tempbuff[8], 8); - scsi_ident_cpy((unsigned char *)dev_desc->product, - &tempbuff[16], 16); - scsi_ident_cpy((unsigned char *)dev_desc->revision, - &tempbuff[32], 4); + scsi_ident_cpy(dev_desc->vendor, resp->vendor, sizeof(resp->vendor)); + scsi_ident_cpy(dev_desc->product, resp->product, sizeof(resp->product)); + scsi_ident_cpy(dev_desc->revision, resp->revision, + sizeof(resp->revision)); dev_desc->target = pccb->target; dev_desc->lun = pccb->lun; + dev_desc->type = resp->type; + + /* this is about to be overwritten by the code below */ + resp = NULL; for (count = 0; count < 3; count++) { pccb->datalen = 0; @@ -501,10 +501,8 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, break; } if (err) { - if (dev_desc->removable) { - dev_desc->type = perq; + if (dev_desc->removable) goto removable; - } scsi_print_error(pccb); return -EINVAL; } @@ -515,7 +513,7 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, dev_desc->lba = capacity; dev_desc->blksz = blksz; dev_desc->log2blksz = LOG2(dev_desc->blksz); - dev_desc->type = perq; + removable: return 0; } -- 2.43.0

From: Simon Glass <sjg@chromium.org> The word 'block_dev' suggests it is a device, but this struct is not the device (now that we use driver model), only some information about it. We use desc (for descriptor) elsewhere in the code, so update scsi to do the same. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 80 ++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index dd45f1089be..ca5e4da627d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -128,7 +128,7 @@ static void scsi_setup_write_ext(struct scsi_cmd *pccb, lbaint_t start, static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, void *buffer) { - struct blk_desc *block_dev = dev_get_uclass_plat(dev); + struct blk_desc *desc = dev_get_uclass_plat(dev); struct udevice *bdev = dev->parent; struct scsi_plat *uc_plat = dev_get_uclass_plat(bdev); lbaint_t start, blks, max_blks; @@ -137,19 +137,19 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; /* Setup device */ - pccb->target = block_dev->target; - pccb->lun = block_dev->lun; + pccb->target = desc->target; + pccb->lun = desc->lun; buf_addr = (unsigned long)buffer; start = blknr; blks = blkcnt; if (uc_plat->max_bytes_per_req) - max_blks = uc_plat->max_bytes_per_req / block_dev->blksz; + max_blks = uc_plat->max_bytes_per_req / desc->blksz; else max_blks = SCSI_MAX_BLK; debug("\nscsi_read: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", - block_dev->devnum, start, blks, (unsigned long)buffer); + desc->devnum, start, blks, (unsigned long)buffer); do { pccb->pdata = (unsigned char *)buf_addr; pccb->dma_dir = DMA_FROM_DEVICE; @@ -157,20 +157,20 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, if (start > SCSI_LBA48_READ) { unsigned long blocks; blocks = min_t(lbaint_t, blks, max_blks); - pccb->datalen = block_dev->blksz * blocks; + pccb->datalen = desc->blksz * blocks; scsi_setup_read16(pccb, start, blocks); start += blocks; blks -= blocks; } else #endif if (blks > max_blks) { - pccb->datalen = block_dev->blksz * max_blks; + pccb->datalen = desc->blksz * max_blks; smallblks = max_blks; scsi_setup_read_ext(pccb, start, smallblks); start += max_blks; blks -= max_blks; } else { - pccb->datalen = block_dev->blksz * blks; + pccb->datalen = desc->blksz * blks; smallblks = (unsigned short)blks; scsi_setup_read_ext(pccb, start, smallblks); start += blks; @@ -198,7 +198,7 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, const void *buffer) { - struct blk_desc *block_dev = dev_get_uclass_plat(dev); + struct blk_desc *desc = dev_get_uclass_plat(dev); struct udevice *bdev = dev->parent; struct scsi_plat *uc_plat = dev_get_uclass_plat(bdev); lbaint_t start, blks, max_blks; @@ -207,29 +207,29 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; /* Setup device */ - pccb->target = block_dev->target; - pccb->lun = block_dev->lun; + pccb->target = desc->target; + pccb->lun = desc->lun; buf_addr = (unsigned long)buffer; start = blknr; blks = blkcnt; if (uc_plat->max_bytes_per_req) - max_blks = uc_plat->max_bytes_per_req / block_dev->blksz; + max_blks = uc_plat->max_bytes_per_req / desc->blksz; else max_blks = SCSI_MAX_BLK; debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", - __func__, block_dev->devnum, start, blks, (unsigned long)buffer); + __func__, desc->devnum, start, blks, (unsigned long)buffer); do { pccb->pdata = (unsigned char *)buf_addr; pccb->dma_dir = DMA_TO_DEVICE; if (blks > max_blks) { - pccb->datalen = block_dev->blksz * max_blks; + pccb->datalen = desc->blksz * max_blks; smallblks = max_blks; scsi_setup_write_ext(pccb, start, smallblks); start += max_blks; blks -= max_blks; } else { - pccb->datalen = block_dev->blksz * blks; + pccb->datalen = desc->blksz * blks; smallblks = (unsigned short)blks; scsi_setup_write_ext(pccb, start, smallblks); start += blks; @@ -366,20 +366,20 @@ static void scsi_setup_test_unit_ready(struct scsi_cmd *pccb) } /** - * scsi_init_dev_desc_priv - initialize only SCSI specific blk_desc properties + * scsi_init_desc_priv - initialize only SCSI specific blk_desc properties * - * @dev_desc: Block device description pointer + * @desc: Block device description pointer */ -static void scsi_init_dev_desc_priv(struct blk_desc *dev_desc) +static void scsi_init_desc_priv(struct blk_desc *desc) { - memset(dev_desc, 0, sizeof(struct blk_desc)); - dev_desc->target = 0xff; - dev_desc->lun = 0xff; - dev_desc->log2blksz = - LOG2_INVALID(typeof(dev_desc->log2blksz)); - dev_desc->type = DEV_TYPE_UNKNOWN; + memset(desc, 0, sizeof(struct blk_desc)); + desc->target = 0xff; + desc->lun = 0xff; + desc->log2blksz = + LOG2_INVALID(typeof(desc->log2blksz)); + desc->type = DEV_TYPE_UNKNOWN; #if IS_ENABLED(CONFIG_BOUNCE_BUFFER) - dev_desc->bb = true; + desc->bb = true; #endif /* CONFIG_BOUNCE_BUFFER */ } @@ -440,15 +440,15 @@ static int scsi_count_luns(struct udevice *dev, uint target) * * @target: target id * @lun: target lun - * @dev_desc: block device description + * @desc: block device description * - * The scsi_detect_dev detects and fills a dev_desc structure when the device is + * The scsi_detect_dev detects and fills a desc structure when the device is * detected. * * Return: 0 on success, error value otherwise */ static int scsi_detect_dev(struct udevice *dev, int target, int lun, - struct blk_desc *dev_desc) + struct blk_desc *desc) { lbaint_t capacity; unsigned long blksz; @@ -479,15 +479,15 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, if ((resp->type & SCSIRF_TYPE_MASK) == SCSIRF_TYPE_UNKNOWN) return -ENODEV; /* skip unknown devices */ if (resp->flags & SCSIRF_FLAGS_REMOVABLE) /* drive is removable */ - dev_desc->removable = true; + desc->removable = true; /* get info for this device */ - scsi_ident_cpy(dev_desc->vendor, resp->vendor, sizeof(resp->vendor)); - scsi_ident_cpy(dev_desc->product, resp->product, sizeof(resp->product)); - scsi_ident_cpy(dev_desc->revision, resp->revision, + scsi_ident_cpy(desc->vendor, resp->vendor, sizeof(resp->vendor)); + scsi_ident_cpy(desc->product, resp->product, sizeof(resp->product)); + scsi_ident_cpy(desc->revision, resp->revision, sizeof(resp->revision)); - dev_desc->target = pccb->target; - dev_desc->lun = pccb->lun; - dev_desc->type = resp->type; + desc->target = pccb->target; + desc->lun = pccb->lun; + desc->type = resp->type; /* this is about to be overwritten by the code below */ resp = NULL; @@ -501,7 +501,7 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, break; } if (err) { - if (dev_desc->removable) + if (desc->removable) goto removable; scsi_print_error(pccb); return -EINVAL; @@ -510,9 +510,9 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, scsi_print_error(pccb); return -EINVAL; } - dev_desc->lba = capacity; - dev_desc->blksz = blksz; - dev_desc->log2blksz = LOG2(dev_desc->blksz); + desc->lba = capacity; + desc->blksz = blksz; + desc->log2blksz = LOG2(desc->blksz); removable: return 0; @@ -534,7 +534,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) * detect the scsi driver to get information about its geometry (block * size, number of blocks) and other parameters (ids, type, ...) */ - scsi_init_dev_desc_priv(&bd); + scsi_init_desc_priv(&bd); if (scsi_detect_dev(dev, id, lun, &bd)) return -ENODEV; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Rather than doing the same shift operation each time, create a function which writes in the correct command and the LUN. This will allows us to handle newer controllers which do not want the LUN in the command. Fix checkpatch warnings about CONFIG_SYS_64BIT_LBA while here. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 63 ++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index ca5e4da627d..df2d9666ec4 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -37,12 +37,18 @@ static void scsi_print_error(struct scsi_cmd *pccb) /* Dummy function that could print an error for debugging */ } -#ifdef CONFIG_SYS_64BIT_LBA -void scsi_setup_read16(struct scsi_cmd *pccb, lbaint_t start, - unsigned long blocks) +static void scsi_setup_cmd(const struct blk_desc *desc, struct scsi_cmd *pccb, + uint cmd) { - pccb->cmd[0] = SCSI_READ16; + pccb->cmd[0] = cmd; pccb->cmd[1] = pccb->lun << 5; +} + +static void scsi_setup_read16(const struct blk_desc *desc, + struct scsi_cmd *pccb, lbaint_t start, + ulong blocks) +{ + scsi_setup_cmd(desc, pccb, SCSI_READ16); pccb->cmd[2] = (unsigned char)(start >> 56) & 0xff; pccb->cmd[3] = (unsigned char)(start >> 48) & 0xff; pccb->cmd[4] = (unsigned char)(start >> 40) & 0xff; @@ -65,12 +71,11 @@ void scsi_setup_read16(struct scsi_cmd *pccb, lbaint_t start, pccb->cmd[6], pccb->cmd[7], pccb->cmd[8], pccb->cmd[9], pccb->cmd[11], pccb->cmd[12], pccb->cmd[13], pccb->cmd[14]); } -#endif -static void scsi_setup_inquiry(struct scsi_cmd *pccb) +static void scsi_setup_inquiry(const struct blk_desc *desc, + struct scsi_cmd *pccb) { - pccb->cmd[0] = SCSI_INQUIRY; - pccb->cmd[1] = pccb->lun << 5; + scsi_setup_cmd(desc, pccb, SCSI_INQUIRY); pccb->cmd[2] = 0; pccb->cmd[3] = 0; if (pccb->datalen > 255) @@ -82,11 +87,11 @@ static void scsi_setup_inquiry(struct scsi_cmd *pccb) pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ } -static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start, +static void scsi_setup_read_ext(const struct blk_desc *desc, + struct scsi_cmd *pccb, lbaint_t start, unsigned short blocks) { - pccb->cmd[0] = SCSI_READ10; - pccb->cmd[1] = pccb->lun << 5; + scsi_setup_cmd(desc, pccb, SCSI_READ10); pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff; pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff; pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff; @@ -103,11 +108,11 @@ static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start, pccb->cmd[7], pccb->cmd[8]); } -static void scsi_setup_write_ext(struct scsi_cmd *pccb, lbaint_t start, +static void scsi_setup_write_ext(const struct blk_desc *desc, + struct scsi_cmd *pccb, lbaint_t start, unsigned short blocks) { - pccb->cmd[0] = SCSI_WRITE10; - pccb->cmd[1] = pccb->lun << 5; + scsi_setup_cmd(desc, pccb, SCSI_WRITE10); pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff; pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff; pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff; @@ -153,26 +158,25 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, do { pccb->pdata = (unsigned char *)buf_addr; pccb->dma_dir = DMA_FROM_DEVICE; -#ifdef CONFIG_SYS_64BIT_LBA - if (start > SCSI_LBA48_READ) { + if (IS_ENABLED(CONFIG_SYS_64BIT_LBA) && + start > SCSI_LBA48_READ) { unsigned long blocks; blocks = min_t(lbaint_t, blks, max_blks); pccb->datalen = desc->blksz * blocks; - scsi_setup_read16(pccb, start, blocks); + scsi_setup_read16(desc, pccb, start, blocks); start += blocks; blks -= blocks; } else -#endif if (blks > max_blks) { pccb->datalen = desc->blksz * max_blks; smallblks = max_blks; - scsi_setup_read_ext(pccb, start, smallblks); + scsi_setup_read_ext(desc, pccb, start, smallblks); start += max_blks; blks -= max_blks; } else { pccb->datalen = desc->blksz * blks; smallblks = (unsigned short)blks; - scsi_setup_read_ext(pccb, start, smallblks); + scsi_setup_read_ext(desc, pccb, start, smallblks); start += blks; blks = 0; } @@ -225,13 +229,13 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, if (blks > max_blks) { pccb->datalen = desc->blksz * max_blks; smallblks = max_blks; - scsi_setup_write_ext(pccb, start, smallblks); + scsi_setup_write_ext(desc, pccb, start, smallblks); start += max_blks; blks -= max_blks; } else { pccb->datalen = desc->blksz * blks; smallblks = (unsigned short)blks; - scsi_setup_write_ext(pccb, start, smallblks); + scsi_setup_write_ext(desc, pccb, start, smallblks); start += blks; blks = 0; } @@ -288,11 +292,12 @@ static void scsi_ident_cpy(char *dest, const char *src, uint len) static int scsi_read_capacity(struct udevice *dev, struct scsi_cmd *pccb, lbaint_t *capacity, unsigned long *blksz) { + const struct blk_desc *desc = dev_get_uclass_plat(dev); + *capacity = 0; memset(pccb->cmd, '\0', sizeof(pccb->cmd)); - pccb->cmd[0] = SCSI_RD_CAPAC10; - pccb->cmd[1] = pccb->lun << 5; + scsi_setup_cmd(desc, pccb, SCSI_RD_CAPAC10); pccb->cmdlen = 10; pccb->dma_dir = DMA_FROM_DEVICE; pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ @@ -353,10 +358,10 @@ static int scsi_read_capacity(struct udevice *dev, struct scsi_cmd *pccb, /* * Some setup (fill-in) routines */ -static void scsi_setup_test_unit_ready(struct scsi_cmd *pccb) +static void scsi_setup_test_unit_ready(const struct blk_desc *desc, + struct scsi_cmd *pccb) { - pccb->cmd[0] = SCSI_TST_U_RDY; - pccb->cmd[1] = pccb->lun << 5; + scsi_setup_cmd(desc, pccb, SCSI_TST_U_RDY); pccb->cmd[2] = 0; pccb->cmd[3] = 0; pccb->cmd[4] = 0; @@ -461,7 +466,7 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, pccb->pdata = tempbuff; pccb->datalen = TEMPBUFF_SIZE; pccb->dma_dir = DMA_FROM_DEVICE; - scsi_setup_inquiry(pccb); + scsi_setup_inquiry(desc, pccb); if (scsi_exec(dev, pccb)) { if (pccb->contr_stat == SCSI_SEL_TIME_OUT) { /* @@ -495,7 +500,7 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, for (count = 0; count < 3; count++) { pccb->datalen = 0; pccb->dma_dir = DMA_NONE; - scsi_setup_test_unit_ready(pccb); + scsi_setup_test_unit_ready(desc, pccb); err = scsi_exec(dev, pccb); if (!err) break; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Newer SCSI controllers handle the LUN in a different place from the SCSI command. Detect this and adjust the command accordingly. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 6 +++++- include/blk.h | 1 + include/scsi.h | 8 ++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index df2d9666ec4..89c94f52d1d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -41,7 +41,7 @@ static void scsi_setup_cmd(const struct blk_desc *desc, struct scsi_cmd *pccb, uint cmd) { pccb->cmd[0] = cmd; - pccb->cmd[1] = pccb->lun << 5; + pccb->cmd[1] = desc->no_lun ? 0 : pccb->lun << 5; } static void scsi_setup_read16(const struct blk_desc *desc, @@ -485,6 +485,9 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, return -ENODEV; /* skip unknown devices */ if (resp->flags & SCSIRF_FLAGS_REMOVABLE) /* drive is removable */ desc->removable = true; + if (resp->eflags & EFLAGS_TPGS_MASK) + desc->no_lun = true; + /* get info for this device */ scsi_ident_cpy(desc->vendor, resp->vendor, sizeof(resp->vendor)); scsi_ident_cpy(desc->product, resp->product, sizeof(resp->product)); @@ -566,6 +569,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) bdesc->removable = bd.removable; bdesc->type = bd.type; bdesc->bb = bd.bb; + bdesc->no_lun = bd.no_lun; memcpy(&bdesc->vendor, &bd.vendor, sizeof(bd.vendor)); memcpy(&bdesc->product, &bd.product, sizeof(bd.product)); memcpy(&bdesc->revision, &bd.revision, sizeof(bd.revision)); diff --git a/include/blk.h b/include/blk.h index 488d04cf32a..84c19ca887d 100644 --- a/include/blk.h +++ b/include/blk.h @@ -69,6 +69,7 @@ struct blk_desc { bool lba48; unsigned char atapi; /* Use ATAPI protocol */ unsigned char bb; /* Use bounce buffer */ + bool no_lun; /* Don't put LUN in the message */ lbaint_t lba; /* number of blocks */ unsigned long blksz; /* block size */ int log2blksz; /* for convenience: log2(blksz) */ diff --git a/include/scsi.h b/include/scsi.h index da4f1ed08b7..d746b0f2fcf 100644 --- a/include/scsi.h +++ b/include/scsi.h @@ -205,6 +205,8 @@ enum scsi_resp_t { SCSIRF_TYPE_UNKNOWN = 0x1f, SCSIRF_FLAGS_REMOVABLE = BIT(8), + + EFLAGS_TPGS_MASK = 0x30, /* LUN is sent in the transport layer */ }; /** @@ -215,7 +217,8 @@ enum scsi_resp_t { * @version; command version * @data_format; data format * @additional_len; additional data length - * @spare[3]; spare bytes + * @eflags: extra flags + * @spare[2]; spare bytes * @vendor[8]; vendor information * @product[16]; production information * @revision[4]; revision information @@ -226,7 +229,8 @@ struct scsi_inquiry_resp { u8 version; u8 data_format; u8 additional_len; - u8 spare[3]; + u8 eflags; + u8 spare[2]; char vendor[8]; char product[16]; char revision[4]; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Recent versions of SCSI handle the LUN outside the command region. Check for this and obey. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 89c94f52d1d..b32eb1be2b3 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -487,6 +487,9 @@ static int scsi_detect_dev(struct udevice *dev, int target, int lun, desc->removable = true; if (resp->eflags & EFLAGS_TPGS_MASK) desc->no_lun = true; + /* check for SPC-3 or greater */ + if (resp->version > 4) + desc->no_lun = true; /* get info for this device */ scsi_ident_cpy(desc->vendor, resp->vendor, sizeof(resp->vendor)); -- 2.43.0

From: Simon Glass <sjg@chromium.org> The current code never returns an error if something goes wrong. While it is normally useful to return the number of blocks processed, when nothing is read/written at all, there is clearly something wrong. Update the logic to return -EIO in this case. Avoid changing blkcnt since it is an input parameter and this is confusing. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index b32eb1be2b3..a1612a4f4c0 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -185,14 +185,18 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, start, smallblks, buf_addr); if (scsi_exec(bdev, pccb)) { scsi_print_error(pccb); - blkcnt -= blks; break; } buf_addr += pccb->datalen; - } while (blks != 0); + } while (blks); debug("scsi_read_ext: end startblk " LBAF ", blccnt %x buffer %lX\n", start, smallblks, buf_addr); - return blkcnt; + + /* Report an I/O error if nothing was read */ + if (blks == blkcnt) + return -EIO; + + return blkcnt - blks; } /******************************************************************************* @@ -243,14 +247,18 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, __func__, start, smallblks, buf_addr); if (scsi_exec(bdev, pccb)) { scsi_print_error(pccb); - blkcnt -= blks; break; } buf_addr += pccb->datalen; - } while (blks != 0); + } while (blks); debug("%s: end startblk " LBAF ", blccnt %x buffer %lX\n", __func__, start, smallblks, buf_addr); - return blkcnt; + + /* Report an I/O error if nothing was written */ + if (blks == blkcnt) + return -EIO; + + return blkcnt - blks; } #if IS_ENABLED(CONFIG_BOUNCE_BUFFER) -- 2.43.0

From: Simon Glass <sjg@chromium.org> Each time around the loop a certain number of blocks are processed. Put this in a variable (to_read/to_write) and avoid changing the input parameter Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 54 +++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index a1612a4f4c0..6deea51bebc 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -156,28 +156,30 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, ", blccnt " LBAF " buffer %lx\n", desc->devnum, start, blks, (unsigned long)buffer); do { + ulong to_read; /* number of blocks to read on this iteration */ + + to_read = blks; pccb->pdata = (unsigned char *)buf_addr; pccb->dma_dir = DMA_FROM_DEVICE; if (IS_ENABLED(CONFIG_SYS_64BIT_LBA) && start > SCSI_LBA48_READ) { - unsigned long blocks; - blocks = min_t(lbaint_t, blks, max_blks); - pccb->datalen = desc->blksz * blocks; - scsi_setup_read16(desc, pccb, start, blocks); - start += blocks; - blks -= blocks; - } else - if (blks > max_blks) { - pccb->datalen = desc->blksz * max_blks; - smallblks = max_blks; + to_read = min_t(lbaint_t, to_read, max_blks); + pccb->datalen = desc->blksz * to_read; + scsi_setup_read16(desc, pccb, start, to_read); + start += to_read; + blks -= to_read; + } else if (to_read > max_blks) { + to_read = max_blks; + pccb->datalen = desc->blksz * to_read; + smallblks = to_read; scsi_setup_read_ext(desc, pccb, start, smallblks); - start += max_blks; - blks -= max_blks; + start += to_read; + blks -= to_read; } else { - pccb->datalen = desc->blksz * blks; - smallblks = (unsigned short)blks; + pccb->datalen = desc->blksz * to_read; + smallblks = (unsigned short)to_read; scsi_setup_read_ext(desc, pccb, start, smallblks); - start += blks; + start += to_read; blks = 0; } debug("scsi_read_ext: startblk " LBAF @@ -228,19 +230,23 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", __func__, desc->devnum, start, blks, (unsigned long)buffer); do { + ulong to_write; /* # blocks to write on this iteration */ + + to_write = blks; pccb->pdata = (unsigned char *)buf_addr; pccb->dma_dir = DMA_TO_DEVICE; - if (blks > max_blks) { - pccb->datalen = desc->blksz * max_blks; - smallblks = max_blks; - scsi_setup_write_ext(desc, pccb, start, smallblks); - start += max_blks; - blks -= max_blks; + if (to_write > max_blks) { + to_write = max_blks; + pccb->datalen = desc->blksz * to_write; + smallblks = to_write; + scsi_setup_write_ext(desc, pccb, start, to_write); + start += to_write; + blks -= to_write; } else { - pccb->datalen = desc->blksz * blks; - smallblks = (unsigned short)blks; + pccb->datalen = desc->blksz * to_write; + smallblks = (unsigned short)to_write; scsi_setup_write_ext(desc, pccb, start, smallblks); - start += blks; + start += to_write; blks = 0; } debug("%s: startblk " LBAF ", blccnt %x buffer %lx\n", -- 2.43.0

From: Simon Glass <sjg@chromium.org> Adjust the ordering of these so they are easier to read. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 6deea51bebc..fa27a353088 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -133,13 +133,13 @@ static void scsi_setup_write_ext(const struct blk_desc *desc, static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, void *buffer) { + struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; struct blk_desc *desc = dev_get_uclass_plat(dev); struct udevice *bdev = dev->parent; struct scsi_plat *uc_plat = dev_get_uclass_plat(bdev); lbaint_t start, blks, max_blks; - uintptr_t buf_addr; unsigned short smallblks = 0; - struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; + uintptr_t buf_addr; /* Setup device */ pccb->target = desc->target; @@ -208,13 +208,13 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, const void *buffer) { + struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; struct blk_desc *desc = dev_get_uclass_plat(dev); struct udevice *bdev = dev->parent; struct scsi_plat *uc_plat = dev_get_uclass_plat(bdev); lbaint_t start, blks, max_blks; - uintptr_t buf_addr; unsigned short smallblks; - struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; + uintptr_t buf_addr; /* Setup device */ pccb->target = desc->target; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Rather than casting between addresses and pointers, use the mapmem functions provided. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index fa27a353088..d16c6b2df3a 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -13,6 +13,7 @@ #include <env.h> #include <libata.h> #include <log.h> +#include <mapmem.h> #include <memalign.h> #include <part.h> #include <pci.h> @@ -144,7 +145,7 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, /* Setup device */ pccb->target = desc->target; pccb->lun = desc->lun; - buf_addr = (unsigned long)buffer; + buf_addr = map_to_sysmem(buffer); start = blknr; blks = blkcnt; if (uc_plat->max_bytes_per_req) @@ -159,7 +160,7 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, ulong to_read; /* number of blocks to read on this iteration */ to_read = blks; - pccb->pdata = (unsigned char *)buf_addr; + pccb->pdata = map_sysmem(buf_addr, 0); pccb->dma_dir = DMA_FROM_DEVICE; if (IS_ENABLED(CONFIG_SYS_64BIT_LBA) && start > SCSI_LBA48_READ) { @@ -219,7 +220,7 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, /* Setup device */ pccb->target = desc->target; pccb->lun = desc->lun; - buf_addr = (unsigned long)buffer; + buf_addr = map_to_sysmem(buffer); start = blknr; blks = blkcnt; if (uc_plat->max_bytes_per_req) @@ -233,7 +234,7 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, ulong to_write; /* # blocks to write on this iteration */ to_write = blks; - pccb->pdata = (unsigned char *)buf_addr; + pccb->pdata = map_sysmem(buf_addr, 0); pccb->dma_dir = DMA_TO_DEVICE; if (to_write > max_blks) { to_write = max_blks; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Move buf_addr, start and blks down a bit so it is clear that they are being set up ready for the loop. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index d16c6b2df3a..e12d52299b4 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -145,14 +145,14 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, /* Setup device */ pccb->target = desc->target; pccb->lun = desc->lun; - buf_addr = map_to_sysmem(buffer); - start = blknr; - blks = blkcnt; if (uc_plat->max_bytes_per_req) max_blks = uc_plat->max_bytes_per_req / desc->blksz; else max_blks = SCSI_MAX_BLK; + start = blknr; /* start block# for each read */ + blks = blkcnt; /* number of blocks remaining to read */ + buf_addr = map_to_sysmem(buffer); debug("\nscsi_read: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", desc->devnum, start, blks, (unsigned long)buffer); @@ -220,14 +220,14 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, /* Setup device */ pccb->target = desc->target; pccb->lun = desc->lun; - buf_addr = map_to_sysmem(buffer); - start = blknr; - blks = blkcnt; if (uc_plat->max_bytes_per_req) max_blks = uc_plat->max_bytes_per_req / desc->blksz; else max_blks = SCSI_MAX_BLK; + start = blknr; + blks = blkcnt; + buf_addr = map_to_sysmem(buffer); debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF " buffer %lx\n", __func__, desc->devnum, start, blks, (unsigned long)buffer); do { -- 2.43.0

From: Simon Glass <sjg@chromium.org> The current logic is fairly confusing. We always add to_read to start and we always subtract it from blks. Even the place where blks is set to 0 is really just subtracting to_read. So move these additions and subtractions out of the if() logic and to the end of the loop. This is much easier to follow. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index e12d52299b4..1f726f79e5d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -167,21 +167,15 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, to_read = min_t(lbaint_t, to_read, max_blks); pccb->datalen = desc->blksz * to_read; scsi_setup_read16(desc, pccb, start, to_read); - start += to_read; - blks -= to_read; } else if (to_read > max_blks) { to_read = max_blks; pccb->datalen = desc->blksz * to_read; smallblks = to_read; scsi_setup_read_ext(desc, pccb, start, smallblks); - start += to_read; - blks -= to_read; } else { pccb->datalen = desc->blksz * to_read; smallblks = (unsigned short)to_read; scsi_setup_read_ext(desc, pccb, start, smallblks); - start += to_read; - blks = 0; } debug("scsi_read_ext: startblk " LBAF ", blccnt %x buffer %lX\n", @@ -190,6 +184,10 @@ static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, scsi_print_error(pccb); break; } + + /* update ready for the next read */ + start += to_read; + blks -= to_read; buf_addr += pccb->datalen; } while (blks); debug("scsi_read_ext: end startblk " LBAF @@ -241,14 +239,10 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, pccb->datalen = desc->blksz * to_write; smallblks = to_write; scsi_setup_write_ext(desc, pccb, start, to_write); - start += to_write; - blks -= to_write; } else { pccb->datalen = desc->blksz * to_write; smallblks = (unsigned short)to_write; scsi_setup_write_ext(desc, pccb, start, smallblks); - start += to_write; - blks = 0; } debug("%s: startblk " LBAF ", blccnt %x buffer %lx\n", __func__, start, smallblks, buf_addr); @@ -256,7 +250,11 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, scsi_print_error(pccb); break; } + + /* update ready for the next write */ buf_addr += pccb->datalen; + start += to_write; + blks -= to_write; } while (blks); debug("%s: end startblk " LBAF ", blccnt %x buffer %lX\n", __func__, start, smallblks, buf_addr); -- 2.43.0

From: Simon Glass <sjg@chromium.org> The file include/uapi/linux/virtio_scsi.h contains some useful definitions for virtio-scsi so bring this in from Linux v6.15 Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/virtio/virtio_scsi.h | 175 +++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 drivers/virtio/virtio_scsi.h diff --git a/drivers/virtio/virtio_scsi.h b/drivers/virtio/virtio_scsi.h new file mode 100644 index 00000000000..420422709bd --- /dev/null +++ b/drivers/virtio/virtio_scsi.h @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LINUX_VIRTIO_SCSI_H +#define _LINUX_VIRTIO_SCSI_H + +#include <virtio_types.h> + +#define VIRTIO_SCSI_F_INOUT 0 + +/* Default values of the CDB and sense data size configuration fields */ +#define VIRTIO_SCSI_CDB_DEFAULT_SIZE 32 +#define VIRTIO_SCSI_SENSE_DEFAULT_SIZE 96 + +#ifndef VIRTIO_SCSI_CDB_SIZE +#define VIRTIO_SCSI_CDB_SIZE VIRTIO_SCSI_CDB_DEFAULT_SIZE +#endif +#ifndef VIRTIO_SCSI_SENSE_SIZE +#define VIRTIO_SCSI_SENSE_SIZE VIRTIO_SCSI_SENSE_DEFAULT_SIZE +#endif + +/* SCSI command request, followed by data-out */ +struct virtio_scsi_cmd_req { + __u8 lun[8]; /* Logical Unit Number */ + __virtio64 tag; /* Command identifier */ + __u8 task_attr; /* Task attribute */ + __u8 prio; /* SAM command priority field */ + __u8 crn; + __u8 cdb[VIRTIO_SCSI_CDB_SIZE]; +} __attribute__((packed)); + +/* SCSI command request, followed by protection information */ +struct virtio_scsi_cmd_req_pi { + __u8 lun[8]; /* Logical Unit Number */ + __virtio64 tag; /* Command identifier */ + __u8 task_attr; /* Task attribute */ + __u8 prio; /* SAM command priority field */ + __u8 crn; + __virtio32 pi_bytesout; /* DataOUT PI Number of bytes */ + __virtio32 pi_bytesin; /* DataIN PI Number of bytes */ + __u8 cdb[VIRTIO_SCSI_CDB_SIZE]; +} __attribute__((packed)); + +/* Response, followed by sense data and data-in */ +struct virtio_scsi_cmd_resp { + __virtio32 sense_len; /* Sense data length */ + __virtio32 resid; /* Residual bytes in data buffer */ + __virtio16 status_qualifier; /* Status qualifier */ + __u8 status; /* Command completion status */ + __u8 response; /* Response values */ + __u8 sense[VIRTIO_SCSI_SENSE_SIZE]; +} __attribute__((packed)); + +/* Task Management Request */ +struct virtio_scsi_ctrl_tmf_req { + __virtio32 type; + __virtio32 subtype; + __u8 lun[8]; + __virtio64 tag; +} __attribute__((packed)); + +struct virtio_scsi_ctrl_tmf_resp { + __u8 response; +} __attribute__((packed)); + +/* Asynchronous notification query/subscription */ +struct virtio_scsi_ctrl_an_req { + __virtio32 type; + __u8 lun[8]; + __virtio32 event_requested; +} __attribute__((packed)); + +struct virtio_scsi_ctrl_an_resp { + __virtio32 event_actual; + __u8 response; +} __attribute__((packed)); + +struct virtio_scsi_event { + __virtio32 event; + __u8 lun[8]; + __virtio32 reason; +} __attribute__((packed)); + +struct virtio_scsi_config { + __virtio32 num_queues; + __virtio32 seg_max; + __virtio32 max_sectors; + __virtio32 cmd_per_lun; + __virtio32 event_info_size; + __virtio32 sense_size; + __virtio32 cdb_size; + __virtio16 max_channel; + __virtio16 max_target; + __virtio32 max_lun; +} __attribute__((packed)); + +/* Feature Bits */ +#define VIRTIO_SCSI_F_INOUT 0 +#define VIRTIO_SCSI_F_HOTPLUG 1 +#define VIRTIO_SCSI_F_CHANGE 2 +#define VIRTIO_SCSI_F_T10_PI 3 + +/* Response codes */ +#define VIRTIO_SCSI_S_OK 0 +#define VIRTIO_SCSI_S_OVERRUN 1 +#define VIRTIO_SCSI_S_ABORTED 2 +#define VIRTIO_SCSI_S_BAD_TARGET 3 +#define VIRTIO_SCSI_S_RESET 4 +#define VIRTIO_SCSI_S_BUSY 5 +#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 +#define VIRTIO_SCSI_S_TARGET_FAILURE 7 +#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 +#define VIRTIO_SCSI_S_FAILURE 9 +#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 +#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 +#define VIRTIO_SCSI_S_INCORRECT_LUN 12 + +/* Controlq type codes. */ +#define VIRTIO_SCSI_T_TMF 0 +#define VIRTIO_SCSI_T_AN_QUERY 1 +#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 + +/* Valid TMF subtypes. */ +#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 +#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 +#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 +#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 +#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 +#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 + +/* Events. */ +#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 +#define VIRTIO_SCSI_T_NO_EVENT 0 +#define VIRTIO_SCSI_T_TRANSPORT_RESET 1 +#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 +#define VIRTIO_SCSI_T_PARAM_CHANGE 3 + +/* Reasons of transport reset event */ +#define VIRTIO_SCSI_EVT_RESET_HARD 0 +#define VIRTIO_SCSI_EVT_RESET_RESCAN 1 +#define VIRTIO_SCSI_EVT_RESET_REMOVED 2 + +#define VIRTIO_SCSI_S_SIMPLE 0 +#define VIRTIO_SCSI_S_ORDERED 1 +#define VIRTIO_SCSI_S_HEAD 2 +#define VIRTIO_SCSI_S_ACA 3 + + +#endif /* _LINUX_VIRTIO_SCSI_H */ -- 2.43.0

From: Simon Glass <sjg@chromium.org> This feature can be useful where the host wants to provide more than one block device, since it allows all of them to be under one virtio device. Add an implementation of virtio-scsi Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/virtio/Kconfig | 12 ++ drivers/virtio/Makefile | 1 + drivers/virtio/virtio-uclass.c | 1 + drivers/virtio/virtio_scsi.c | 249 +++++++++++++++++++++++++++++++++ include/dt-bindings/virtio.h | 1 + include/virtio.h | 1 + 6 files changed, 265 insertions(+) create mode 100644 drivers/virtio/virtio_scsi.c diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index cfebb733cce..eb4ff368f12 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -98,4 +98,16 @@ config VIRTIO_FS A specification for the protocol is available at https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html +config VIRTIO_SCSI + bool "SCSI driver for virtio devices" + depends on SCSI + select VIRTIO + default y + help + This driver provides support for virtio-based paravirtual SCSI. It + allows access to storage devices using the + + A specification for the protocol is available at + https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html + endmenu diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 60ec9b65b17..f101536a863 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o obj-$(CONFIG_VIRTIO_FS) += fs.o fs_dir.o fs_file.o fs_compat.o +obj-$(CONFIG_VIRTIO_SCSI) += virtio_scsi.o diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c index 947d224bbd2..262d8ee4655 100644 --- a/drivers/virtio/virtio-uclass.c +++ b/drivers/virtio/virtio-uclass.c @@ -31,6 +31,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = { [VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME, [VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME, [VIRTIO_ID_FS] = VIRTIO_FS_DRV_NAME, + [VIRTIO_ID_SCSI] = VIRTIO_SCSI_DRV_NAME, }; int virtio_get_config(struct udevice *vdev, unsigned int offset, diff --git a/drivers/virtio/virtio_scsi.c b/drivers/virtio/virtio_scsi.c new file mode 100644 index 00000000000..ee54f1c7449 --- /dev/null +++ b/drivers/virtio/virtio_scsi.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * U-Boot driver for VirtIO SCSI host devices + * + * This driver implements the host-side interface for VirtIO SCSI devices, + * allowing U-Boot to communicate with SCSI LUNs provided by a hypervisor. + * + * Based on the VirtIO v1.3 specification, Section 5.6: SCSI Host Device + */ + +#define LOG_CATEGORY UCLASS_VIRTIO + +#include <blk.h> +#include <dm.h> +#include <malloc.h> +#include <scsi.h> +#include <virtio.h> +#include <virtio_ring.h> +#include "virtio_scsi.h" + +static_assert(CMD_BUF_SIZE >= VIRTIO_SCSI_CDB_SIZE); +static_assert(SENSE_BUF_LEN >= VIRTIO_SCSI_SENSE_SIZE); + +/* + * We need three virtqueues: controlq, eventq, and requestq. + * The request queue is the one we will use for actual SCSI commands. + */ +enum { + QUEUE_CONTROL, + QUEUE_EVENT, + QUEUE_REQUEST, + + QUEUE_COUNT, +}; + +/* + * struct virtio_scsi_cmd - represents a pending SCSI command + * + * The address of this struct is used as a 'cookie' to identify the request in + * the virtqueue, although in practice there is only one pending request at a + * time + */ +struct virtio_scsi_cmd { + struct virtio_scsi_cmd_req req; + struct virtio_scsi_cmd_resp resp; +}; + +/** + * struct virtio_scsi_priv - Per-device private data + * + * @vqs: Array of virtqueues for this device + * @max_target: Maximum target ID supported by the device + * @max_lun: Maximum LUN ID supported by the device + * @v_cmd: Pre-allocated virtio-command struct to avoid allocation in the + * I/O path + * @id: ID number for the next transation + */ +struct virtio_scsi_priv { + struct virtqueue *vqs[QUEUE_COUNT]; + int max_target; + int max_lun; + int id; + /* Pre-allocated command struct to avoid allocation in send_cmd */ + struct virtio_scsi_cmd v_cmd; +}; + +u32 features[] = { + VIRTIO_SCSI_F_INOUT, +}; + +static int virtio_scsi_exec(struct udevice *dev, struct scsi_cmd *cmd) +{ + struct virtio_scsi_priv *priv = dev_get_priv(dev); + struct virtqueue *vq = priv->vqs[QUEUE_REQUEST]; + struct virtio_scsi_cmd *v_cmd = &priv->v_cmd; + struct virtio_sg out_sgs[2], in_sgs[2]; + struct virtio_sg *sgs[4]; + uint out_count = 0, in_count = 0; + int len, i, ret; + uint dummy_len; + + /* Clear the pre-allocated command struct to avoid stale data */ + memset(v_cmd, '\0', sizeof(*v_cmd)); + + /* + * Fill in the request header. + * The LUN is encoded in a specific way for virtio-scsi. + * LUN 0 is encoded as 0x4000. LUNs 1-255 are encoded as themselves. + */ + v_cmd->req.lun[0] = 1; + v_cmd->req.lun[1] = cmd->target; + v_cmd->req.lun[2] = 0x40 | ((cmd->lun >> 8) & 0x3f); + v_cmd->req.lun[3] = cmd->lun & 0xff; + v_cmd->req.tag = priv->id++; + v_cmd->req.task_attr = VIRTIO_SCSI_S_SIMPLE; + + /* Set the command CDB */ + memcpy(v_cmd->req.cdb, cmd->cmd, cmd->cmdlen); + log_debug("cmd %x\n", *cmd->cmd); + + /* + * Set up scatter-gather lists for the request and response. + * The device writes to IN buffers and reads from OUT buffers. + */ + out_sgs[out_count].addr = &v_cmd->req; + out_sgs[out_count].length = sizeof(v_cmd->req); + out_count++; + + in_sgs[in_count].addr = &v_cmd->resp; + in_sgs[in_count].length = sizeof(v_cmd->resp); + in_count++; + + if (cmd->datalen > 0) { + switch (cmd->cmd[0]) { + case SCSI_READ6: + case SCSI_READ10: + case SCSI_READ16: + case SCSI_INQUIRY: + in_sgs[in_count].addr = cmd->pdata; + in_sgs[in_count].length = cmd->datalen; + in_count++; + break; + case SCSI_WRITE6: + case SCSI_WRITE10: + out_sgs[out_count].addr = cmd->pdata; + out_sgs[out_count].length = cmd->datalen; + out_count++; + break; + case SCSI_REPORT_LUNS: + case SCSI_RD_CAPAC: + in_sgs[in_count].addr = cmd->pdata; + in_sgs[in_count].length = cmd->datalen; + in_count++; + break; + default: + printf("Unsupported SCSI command %#02x\n", cmd->cmd[0]); + return -ENOTSUPP; + } + } + + for (i = 0; i < out_count; i++) + sgs[i] = &out_sgs[i]; + for (i = 0; i < in_count; i++) + sgs[out_count + i] = &in_sgs[i]; + + /* Add the buffers to the request virtqueue */ + len = virtqueue_add(vq, sgs, out_count, in_count); + if (len < 0) { + printf("Failed to add buffer to virtqueue: %d\n", len); + return len; + } + + virtqueue_kick(vq); + + log_debug("wait..."); + while (virtqueue_get_buf(vq, &dummy_len) != v_cmd) + ; + log_debug("done\n"); + + /* Process the response */ + ret = 0; + if (v_cmd->resp.response) { + if (v_cmd->resp.response == VIRTIO_SCSI_S_BAD_TARGET) { + /* + * This is an expected result when scanning for a + * non-existent device. Handle it silently. + */ + ret = -ENODEV; + } else { + log_err("virtio-scsi: response error: %#x\n", + v_cmd->resp.response); + ret = -EIO; + } + } else { + ret = v_cmd->resp.status; + if (ret) + log_debug("status %x\n", v_cmd->resp.status); + cmd->sensedatalen = min_t(u32, SENSE_BUF_LEN, + v_cmd->resp.sense_len); + if (cmd->sensedatalen) + memcpy(cmd->sense_buf, v_cmd->resp.sense, + cmd->sensedatalen); + } + + return ret; +} + +static int virtio_scsi_probe(struct udevice *dev) +{ + struct virtio_scsi_priv *priv = dev_get_priv(dev); + struct virtio_scsi_config config; + struct scsi_plat *uc_plat; + int ret; + + /* read the device-specific configuration space */ + virtio_cread_bytes(dev, 0, &config, sizeof(config)); + priv->max_target = config.max_target; + + /* U-Boot only supports up to 8 LUNs */ + priv->max_lun = min(config.max_lun, 7u); + + log_debug("virtio-scsi: max_target=%d, max_lun=%d, sense_size=%d, cdb_size=%d\n", + priv->max_target, priv->max_lun, config.sense_size, + config.cdb_size); + + /* allocate the virtqueues */ + ret = virtio_find_vqs(dev, QUEUE_COUNT, priv->vqs); + if (ret) { + printf("Failed to find vqs\n"); + return -ENOENT; + } + uc_plat = dev_get_uclass_plat(dev); + uc_plat->max_lun = priv->max_lun; + uc_plat->max_id = priv->max_target; + + return 0; +} + +static int virtio_scsi_bind(struct udevice *dev) +{ + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent); + + /* Indicate what driver features we support */ + virtio_driver_features_init(uc_priv, features, ARRAY_SIZE(features), + NULL, 0); + + return 0; +} + +static const struct scsi_ops virtio_scsi_ops = { + .exec = virtio_scsi_exec, +}; + +static const struct udevice_id virtio_scsi_ids[] = { + { .compatible = "virtio,scsi" }, + { } +}; + +U_BOOT_DRIVER(virtio_scsi) = { + .name = VIRTIO_SCSI_DRV_NAME, + .id = UCLASS_SCSI, + .of_match = virtio_scsi_ids, + .priv_auto = sizeof(struct virtio_scsi_priv), + .ops = &virtio_scsi_ops, + .probe = virtio_scsi_probe, + .remove = virtio_reset, + .bind = virtio_scsi_bind, + .flags = DM_FLAG_ACTIVE_DMA, +}; diff --git a/include/dt-bindings/virtio.h b/include/dt-bindings/virtio.h index 8de4a9efcfa..df785b079df 100644 --- a/include/dt-bindings/virtio.h +++ b/include/dt-bindings/virtio.h @@ -14,6 +14,7 @@ #define VIRTIO_ID_NET 1 /* virtio net */ #define VIRTIO_ID_BLOCK 2 /* virtio block */ #define VIRTIO_ID_RNG 4 /* virtio rng */ +#define VIRTIO_ID_SCSI 8 #define VIRTIO_ID_MAX_NUM 27 #define VIRTIO_ID_FS 26 /* virtio filesystem */ diff --git a/include/virtio.h b/include/virtio.h index b0811cbdc24..d30c3eae59b 100644 --- a/include/virtio.h +++ b/include/virtio.h @@ -31,6 +31,7 @@ #define VIRTIO_BLK_DRV_NAME "virtio-blk" #define VIRTIO_RNG_DRV_NAME "virtio-rng" #define VIRTIO_FS_DRV_NAME "virtio-fs" +#define VIRTIO_SCSI_DRV_NAME "virtio-scsi" /* Status byte for guest to report progress, and synchronize features */ -- 2.43.0

From: Simon Glass <sjg@chromium.org> When a device type is not supported by U-Boot it is silently ignored. Add some debugging for this. Also show the driver features to provide a fuller picture of feature negotiation. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/virtio/virtio-uclass.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c index 262d8ee4655..96a17bae940 100644 --- a/drivers/virtio/virtio-uclass.c +++ b/drivers/virtio/virtio-uclass.c @@ -227,8 +227,8 @@ static int virtio_uclass_post_probe(struct udevice *udev) name = virtio_drv_name[uc_priv->device]; if (!name) { - debug("(%s): underlying virtio device driver unavailable\n", - udev->name); + debug("(%s): underlying virtio device driver (type %#x) unavailable\n", + udev->name, uc_priv->device); return 0; } @@ -307,6 +307,7 @@ static int virtio_uclass_child_pre_probe(struct udevice *vdev) WARN_ON(f >= 64); driver_features |= (1ULL << f); } + log_debug("driver_features %016llx\n", driver_features); /* Some drivers have a separate feature table for virtio v1.0 */ if (uc_priv->feature_table_legacy) { -- 2.43.0
participants (1)
-
Simon Glass