From 965c75ca69bbe9bf519977afde57a6ea994ff30f Mon Sep 17 00:00:00 2001 From: marco_g Date: Wed, 27 Aug 2008 15:05:00 +0000 Subject: [PATCH] 2008-08-27 Marco Gerards * conf/common.rmk (pkglib_MODULES): Add scsi.mod. (scsi_mod_SOURCES): New variable. (scsi_mod_CFLAGS): Likewise (scsi_mod_LDFLAGS): Likewise. * disk/scsi.c: New file. * include/grub/scsi.h: Likewise. * include/grub/scsicmd.h: Likewise. * disk/ata.c: Include . (grub_atapi_packet): Do not use grub_ata_cmd, use registers instead. (grub_ata_iterate): Skip ATAPI devices. (grub_ata_open): Only handle ATAPI devices. (struct grub_atapi_read): Removed. (grub_atapi_readsector): Likewise. (grub_ata_read): No longer handle ATAPI devices. (grub_ata_write): Likewise. (grub_atapi_iterate): New function. (grub_atapi_read): Likewise. (grub_atapi_write): Likewise. (grub_atapi_open): Likewise. (grub_atapi_close): Likewise. (grub_atapi_dev): New variable. (GRUB_MOD_INIT(ata)): Register ATAPI as SCSI device. (GRUB_MOD_FINI(ata)): Unregister ATAPI. * include/grub/disk.h (enum grub_disk_dev_id): Add `GRUB_DISK_DEVICE_SCSI_ID'. --- ChangeLog | 34 ++++ conf/common.mk | 61 ++++++- conf/common.rmk | 9 +- disk/ata.c | 196 ++++++++++++--------- disk/scsi.c | 385 +++++++++++++++++++++++++++++++++++++++++ include/grub/disk.h | 1 + include/grub/scsi.h | 88 ++++++++++ include/grub/scsicmd.h | 122 +++++++++++++ 8 files changed, 814 insertions(+), 82 deletions(-) create mode 100644 disk/scsi.c create mode 100644 include/grub/scsi.h create mode 100644 include/grub/scsicmd.h diff --git a/ChangeLog b/ChangeLog index 79c1d435e..1cf7194c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,37 @@ +2008-08-27 Marco Gerards + + * conf/common.rmk (pkglib_MODULES): Add scsi.mod. + (scsi_mod_SOURCES): New variable. + (scsi_mod_CFLAGS): Likewise + (scsi_mod_LDFLAGS): Likewise. + + * disk/scsi.c: New file. + + * include/grub/scsi.h: Likewise. + + * include/grub/scsicmd.h: Likewise. + + * disk/ata.c: Include . + (grub_atapi_packet): Do not use grub_ata_cmd, use registers + instead. + (grub_ata_iterate): Skip ATAPI devices. + (grub_ata_open): Only handle ATAPI devices. + (struct grub_atapi_read): Removed. + (grub_atapi_readsector): Likewise. + (grub_ata_read): No longer handle ATAPI devices. + (grub_ata_write): Likewise. + (grub_atapi_iterate): New function. + (grub_atapi_read): Likewise. + (grub_atapi_write): Likewise. + (grub_atapi_open): Likewise. + (grub_atapi_close): Likewise. + (grub_atapi_dev): New variable. + (GRUB_MOD_INIT(ata)): Register ATAPI as SCSI device. + (GRUB_MOD_FINI(ata)): Unregister ATAPI. + + * include/grub/disk.h (enum grub_disk_dev_id): Add + `GRUB_DISK_DEVICE_SCSI_ID'. + 2008-08-26 Robert Millan * util/biosdisk.c (grub_util_biosdisk_open, open_device) diff --git a/conf/common.mk b/conf/common.mk index b180f051b..d432b0597 100644 --- a/conf/common.mk +++ b/conf/common.mk @@ -1929,10 +1929,10 @@ partmap-gpt_mod-partmap_gpt.lst: partmap/gpt.c $(partmap/gpt.c_DEPENDENCIES) gen gpt_mod_CFLAGS = $(COMMON_CFLAGS) gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) -# Special disk structures +# Special disk structures and generic drivers pkglib_MODULES += raid.mod raid5rec.mod raid6rec.mod mdraid.mod dm_nv.mod \ - lvm.mod + lvm.mod scsi.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -2276,6 +2276,63 @@ partmap-lvm_mod-disk_lvm.lst: disk/lvm.c $(disk/lvm.c_DEPENDENCIES) genpartmapli lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For scsi.mod +scsi_mod_SOURCES = disk/scsi.c +CLEANFILES += scsi.mod mod-scsi.o mod-scsi.c pre-scsi.o scsi_mod-disk_scsi.o und-scsi.lst +ifneq ($(scsi_mod_EXPORTS),no) +CLEANFILES += def-scsi.lst +DEFSYMFILES += def-scsi.lst +endif +MOSTLYCLEANFILES += scsi_mod-disk_scsi.d +UNDSYMFILES += und-scsi.lst + +scsi.mod: pre-scsi.o mod-scsi.o $(TARGET_OBJ2ELF) + -rm -f $@ + $(TARGET_CC) $(scsi_mod_LDFLAGS) $(TARGET_LDFLAGS) $(MODULE_LDFLAGS) -Wl,-r,-d -o $@ pre-scsi.o mod-scsi.o + if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@ + +pre-scsi.o: $(scsi_mod_DEPENDENCIES) scsi_mod-disk_scsi.o + -rm -f $@ + $(TARGET_CC) $(scsi_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ scsi_mod-disk_scsi.o + +mod-scsi.o: mod-scsi.c + $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(scsi_mod_CFLAGS) -c -o $@ $< + +mod-scsi.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'scsi' $< > $@ || (rm -f $@; exit 1) + +ifneq ($(scsi_mod_EXPORTS),no) +def-scsi.lst: pre-scsi.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 scsi/' > $@ +endif + +und-scsi.lst: pre-scsi.o + echo 'scsi' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +scsi_mod-disk_scsi.o: disk/scsi.c $(disk/scsi.c_DEPENDENCIES) + $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(scsi_mod_CFLAGS) -MD -c -o $@ $< +-include scsi_mod-disk_scsi.d + +CLEANFILES += cmd-scsi_mod-disk_scsi.lst fs-scsi_mod-disk_scsi.lst partmap-scsi_mod-disk_scsi.lst +COMMANDFILES += cmd-scsi_mod-disk_scsi.lst +FSFILES += fs-scsi_mod-disk_scsi.lst +PARTMAPFILES += partmap-scsi_mod-disk_scsi.lst + +cmd-scsi_mod-disk_scsi.lst: disk/scsi.c $(disk/scsi.c_DEPENDENCIES) gencmdlist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(scsi_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh scsi > $@ || (rm -f $@; exit 1) + +fs-scsi_mod-disk_scsi.lst: disk/scsi.c $(disk/scsi.c_DEPENDENCIES) genfslist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(scsi_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh scsi > $@ || (rm -f $@; exit 1) + +partmap-scsi_mod-disk_scsi.lst: disk/scsi.c $(disk/scsi.c_DEPENDENCIES) genpartmaplist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(scsi_mod_CFLAGS) -E $< | sh $(srcdir)/genpartmaplist.sh scsi > $@ || (rm -f $@; exit 1) + + +scsi_mod_CFLAGS = $(COMMON_CFLAGS) +scsi_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/conf/common.rmk b/conf/common.rmk index 75863831a..d1c401aec 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -270,10 +270,10 @@ gpt_mod_SOURCES = partmap/gpt.c gpt_mod_CFLAGS = $(COMMON_CFLAGS) gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) -# Special disk structures +# Special disk structures and generic drivers pkglib_MODULES += raid.mod raid5rec.mod raid6rec.mod mdraid.mod dm_nv.mod \ - lvm.mod + lvm.mod scsi.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -305,6 +305,11 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For scsi.mod +scsi_mod_SOURCES = disk/scsi.c +scsi_mod_CFLAGS = $(COMMON_CFLAGS) +scsi_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/disk/ata.c b/disk/ata.c index 4bcb6390b..ab4a1da24 100644 --- a/disk/ata.c +++ b/disk/ata.c @@ -23,6 +23,7 @@ #include #include #include +#include /* XXX: For now this only works on i386. */ #include @@ -305,8 +306,9 @@ grub_atapi_packet (struct grub_ata_device *dev, char *packet) grub_ata_regset (dev, GRUB_ATA_REG_LBAHIGH, 0xFF); grub_ata_regset (dev, GRUB_ATA_REG_LBAMID, 0xFF); - if (grub_ata_cmd (dev, GRUB_ATA_CMD_PACKET)) - return grub_errno; + grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_PACKET); + + grub_ata_wait (); if (grub_ata_pio_write (dev, packet, 12)) return grub_errno; @@ -757,6 +759,9 @@ grub_ata_iterate (int (*hook) (const char *name)) char devname[5]; grub_sprintf (devname, "ata%d", dev->port * 2 + dev->device); + if (dev->atapi) + continue; + if (hook (devname)) return 1; } @@ -781,13 +786,13 @@ grub_ata_open (const char *name, grub_disk_t disk) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Can't open device"); if (dev->atapi) - disk->total_sectors = 9000000; /* XXX */ - else - disk->total_sectors = dev->size; + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk"); + + disk->total_sectors = dev->size; disk->id = (unsigned long) dev; - disk->has_partitions = !dev->atapi; + disk->has_partitions = 1; disk->data = dev; return 0; @@ -799,76 +804,11 @@ grub_ata_close (grub_disk_t disk __attribute__((unused))) } -struct grub_atapi_read -{ - grub_uint8_t code; - grub_uint8_t reserved1; - grub_uint32_t lba; - grub_uint32_t length; - grub_uint8_t reserved2[2]; -} __attribute__((packed)); - -static grub_err_t -grub_atapi_readsector (struct grub_ata_device *dev, - char *buf, grub_disk_addr_t sector) -{ - struct grub_atapi_read readcmd; - - readcmd.code = 0xA8; - readcmd.lba = grub_cpu_to_be32 (sector); - readcmd.length = grub_cpu_to_be32 (1); - - grub_atapi_packet (dev, (char *) &readcmd); - grub_ata_wait (); - if (grub_ata_pio_read (dev, buf, GRUB_CDROM_SECTOR_SIZE)) - return grub_errno; - - return 0; -} - static grub_err_t grub_ata_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) { - struct grub_ata_device *dev = (struct grub_ata_device *) disk->data; - int cdsector; - char *sbuf; - - if (! dev->atapi) - return grub_ata_readwrite (disk, sector, size, buf, 0); - - /* ATAPI is being used, so try to read from CDROM using ATAPI. */ - - sbuf = grub_malloc (GRUB_CDROM_SECTOR_SIZE); - if (! sbuf) - return grub_errno; - - /* CDROMs have sectors of 2048 bytes, so chop them into pieces of - 512 bytes. */ - while (size > 0) - { - int rsize; - int offset; - int max; - - cdsector = sector >> 2; - rsize = ((size * GRUB_DISK_SECTOR_SIZE > GRUB_CDROM_SECTOR_SIZE) - ? GRUB_CDROM_SECTOR_SIZE : size * GRUB_DISK_SECTOR_SIZE); - offset = (sector & 3) * GRUB_DISK_SECTOR_SIZE; - max = GRUB_CDROM_SECTOR_SIZE - offset; - rsize = (rsize > max) ? max : rsize; - - grub_atapi_readsector (dev, sbuf, cdsector); - grub_memcpy (buf + offset, sbuf, rsize); - - buf += rsize; - size -= rsize / GRUB_DISK_SECTOR_SIZE; - sector += rsize / GRUB_DISK_SECTOR_SIZE; - } - - grub_free (sbuf); - - return 0; + return grub_ata_readwrite (disk, sector, size, buf, 0); } static grub_err_t @@ -877,12 +817,7 @@ grub_ata_write (grub_disk_t disk, grub_size_t size, const char *buf) { - struct grub_ata_device *dev = (struct grub_ata_device *) disk->data; - - if (! dev->atapi) - return grub_ata_readwrite (disk, sector, size, (char *) buf, 1); - - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ATAPI write not supported"); + return grub_ata_readwrite (disk, sector, size, (char *) buf, 1); } static struct grub_disk_dev grub_atadisk_dev = @@ -897,6 +832,107 @@ static struct grub_disk_dev grub_atadisk_dev = .next = 0 }; + + +/* ATAPI code. */ + +static int +grub_atapi_iterate (int (*hook) (const char *name, int luns)) +{ + struct grub_ata_device *dev; + + for (dev = grub_ata_devices; dev; dev = dev->next) + { + char devname[7]; + grub_sprintf (devname, "ata%d", dev->port * 2 + dev->device); + + if (! dev->atapi) + continue; + + if (hook (devname, 1)) + return 1; + } + + return 0; + +} + +static grub_err_t +grub_atapi_read (struct grub_scsi *scsi, + grub_size_t cmdsize __attribute__((unused)), + char *cmd, grub_size_t size, char *buf) +{ + struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data; + + if (grub_atapi_packet (dev, cmd)) + return grub_errno; + + grub_ata_wait (); /* XXX */ + + return grub_ata_pio_read (dev, buf, size); +} + +static grub_err_t +grub_atapi_write (struct grub_scsi *scsi, + grub_size_t cmdsize __attribute__((unused)), + char *cmd, grub_size_t size, char *buf) +{ + struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data; + + if (grub_atapi_packet (dev, cmd)) + return grub_errno; + + grub_ata_wait (); /* XXX */ + + return grub_ata_pio_write (dev, buf, size); +} + +static grub_err_t +grub_atapi_open (const char *name, struct grub_scsi *scsi) +{ + struct grub_ata_device *dev; + struct grub_ata_device *devfnd; + + for (dev = grub_ata_devices; dev; dev = dev->next) + { + char devname[7]; + grub_sprintf (devname, "ata%d", dev->port * 2 + dev->device); + + if (!grub_strcmp (devname, name)) + { + devfnd = dev; + break; + } + } + + grub_dprintf ("ata", "opening ATAPI dev `%s'\n", name); + + if (! devfnd) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such ATAPI device"); + + scsi->data = devfnd; + scsi->name = grub_strdup (name); + scsi->luns = 1; + + return GRUB_ERR_NONE; +} + +static void +grub_atapi_close (struct grub_scsi *scsi) +{ + grub_free (scsi->name); +} + +static struct grub_scsi_dev grub_atapi_dev = + { + .name = "ATAPI", + .iterate = grub_atapi_iterate, + .open = grub_atapi_open, + .close = grub_atapi_close, + .read = grub_atapi_read, + .write = grub_atapi_write + }; + GRUB_MOD_INIT(ata) @@ -915,9 +951,13 @@ GRUB_MOD_INIT(ata) grub_ata_initialize (); grub_disk_dev_register (&grub_atadisk_dev); + + /* ATAPI devices are handled by scsi.mod. */ + grub_scsi_dev_register (&grub_atapi_dev); } GRUB_MOD_FINI(ata) { + grub_scsi_dev_unregister (&grub_atapi_dev); grub_disk_dev_unregister (&grub_atadisk_dev); } diff --git a/disk/scsi.c b/disk/scsi.c new file mode 100644 index 000000000..01ef04ee7 --- /dev/null +++ b/disk/scsi.c @@ -0,0 +1,385 @@ +/* scsi.c - scsi support. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static grub_scsi_dev_t grub_scsi_dev_list; + +void +grub_scsi_dev_register (grub_scsi_dev_t dev) +{ + dev->next = grub_scsi_dev_list; + grub_scsi_dev_list = dev; +} + +void +grub_scsi_dev_unregister (grub_scsi_dev_t dev) +{ + grub_scsi_dev_t *p, q; + + for (p = &grub_scsi_dev_list, q = *p; q; p = &(q->next), q = q->next) + if (q == dev) + { + *p = q->next; + break; + } +} + + +/* Determine the the device is removable and the type of the device + SCSI. */ +static grub_err_t +grub_scsi_inquiry (grub_scsi_t scsi) +{ + struct grub_scsi_inquiry iq; + struct grub_scsi_inquiry_data iqd; + grub_err_t err; + + iq.opcode = grub_scsi_cmd_inquiry; + iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + iq.reserved = 0; + iq.alloc_length = 0x24; /* XXX: Hardcoded for now */ + iq.reserved2 = 0; + + err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq, + sizeof (iqd), (char *) &iqd); + if (err) + return err; + + scsi->devtype = iqd.devtype & GRUB_SCSI_DEVTYPE_MASK; + scsi->removable = iqd.rmb >> GRUB_SCSI_REMOVABLE_BIT; + + return GRUB_ERR_NONE; +} + +/* Read the capacity and block size of SCSI. */ +static grub_err_t +grub_scsi_read_capacity (grub_scsi_t scsi) +{ + struct grub_scsi_read_capacity rc; + struct grub_scsi_read_capacity_data rcd; + grub_err_t err; + + rc.opcode = grub_scsi_cmd_read_capacity; + rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + grub_memset (rc.reserved, 0, sizeof (rc.reserved)); + + err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc, + sizeof (rcd), (char *) &rcd); + if (err) + return err; + + scsi->size = grub_be_to_cpu32 (rcd.size); + scsi->blocksize = grub_be_to_cpu32 (rcd.blocksize); + + return GRUB_ERR_NONE; +} + +/* Send a SCSI request for DISK: read SIZE sectors starting with + sector SECTOR to BUF. */ +static grub_err_t +grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_read10 rd; + + scsi = disk->data; + + rd.opcode = grub_scsi_cmd_read10; + rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rd.lba = grub_cpu_to_be32 (sector); + rd.reserved = 0; + rd.size = grub_cpu_to_be16 (size); + rd.reserved2 = 0; + rd.pad = 0; + + return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * 512, buf); +} + +/* Send a SCSI request for DISK: read SIZE sectors starting with + sector SECTOR to BUF. */ +static grub_err_t +grub_scsi_read12 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_read12 rd; + + scsi = disk->data; + + rd.opcode = grub_scsi_cmd_read12; + rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rd.lba = grub_cpu_to_be32 (sector); + rd.size = grub_cpu_to_be32 (size); + rd.reserved = 0; + rd.control = 0; + + return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * 512, buf); +} + +#if 0 +/* Send a SCSI request for DISK: write the data stored in BUF to SIZE + sectors starting with SECTOR. */ +static grub_err_t +grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_write10 wr; + + scsi = disk->data; + + wr.opcode = grub_scsi_cmd_write10; + wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + wr.lba = grub_cpu_to_be32 (sector); + wr.reserved = 0; + wr.size = grub_cpu_to_be16 (size); + wr.reserved2 = 0; + wr.pad = 0; + + return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * 512, buf); +} + +/* Send a SCSI request for DISK: write the data stored in BUF to SIZE + sectors starting with SECTOR. */ +static grub_err_t +grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_write10 wr; + + scsi = disk->data; + + wr.opcode = grub_scsi_cmd_write12; + wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + wr.lba = grub_cpu_to_be32 (sector); + wr.size = grub_cpu_to_be32 (size); + wr.reserved = 0; + wr.pad = 0; + + return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * 512, buf); +} +#endif + + +static int +grub_scsi_iterate (int (*hook) (const char *name)) +{ + grub_scsi_dev_t p; + + auto int scsi_iterate (const char *name, int luns); + + int scsi_iterate (const char *name, int luns) + { + char sname[40]; + int i; + + /* In case of a single LUN, just return `usbX'. */ + if (luns == 1) + return hook (name); + + /* In case of multiple LUNs, every LUN will get a prefix to + distinguish it. */ + for (i = 0; i < luns; i++) + { + grub_sprintf (sname, "%s%c", name, 'a' + i); + if (hook (sname)) + return 1; + } + return 0; + } + + for (p = grub_scsi_dev_list; p; p = p->next) + if (p->iterate && (p->iterate) (scsi_iterate)) + return 1; + + return 0; +} + +static grub_err_t +grub_scsi_open (const char *name, grub_disk_t disk) +{ + grub_scsi_dev_t p; + grub_scsi_t scsi; + grub_err_t err; + int len; + int lun; + + scsi = grub_malloc (sizeof (*scsi)); + if (! scsi) + return grub_errno; + + len = grub_strlen (name); + lun = name[len - 1] - 'a'; + + /* Try to detect a LUN ('a'-'z'), otherwise just use the first + LUN. */ + if (lun < 0 || lun > 26) + lun = 0; + + for (p = grub_scsi_dev_list; p; p = p->next) + { + if (! p->open (name, scsi)) + { + disk->id = (unsigned long) "scsi"; /* XXX */ + disk->data = scsi; + scsi->dev = p; + scsi->lun = lun; + scsi->name = grub_strdup (name); + if (! scsi->name) + { + p->close (scsi); + return grub_errno; + } + + grub_dprintf ("scsi", "dev opened\n"); + + err = grub_scsi_inquiry (scsi); + if (err) + { + grub_dprintf ("scsi", "inquiry failed\n"); + p->close (scsi); + return grub_errno; + } + + grub_dprintf ("scsi", "inquiry: devtype=0x%02x removable=%d\n", + scsi->devtype, scsi->removable); + + /* Try to be conservative about the device types + supported. */ + if (scsi->devtype != grub_scsi_devtype_direct + && scsi->devtype != grub_scsi_devtype_cdrom) + { + p->close (scsi); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "unknown SCSI device"); + } + + if (scsi->devtype == grub_scsi_devtype_cdrom) + disk->has_partitions = 0; + else + disk->has_partitions = 1; + + err = grub_scsi_read_capacity (scsi); + if (err) + { + grub_dprintf ("scsi", "READ CAPACITY failed\n"); + p->close (scsi); + return grub_errno; + } + + /* SCSI blocks can be something else than 512, although GRUB + wants 512 byte blocks. */ + disk->total_sectors = ((scsi->size * scsi->blocksize) + << GRUB_DISK_SECTOR_BITS); + + grub_dprintf ("scsi", "capacity=%d, blksize=%d\n", + (int) disk->total_sectors, scsi->blocksize); + + return GRUB_ERR_NONE; + } + } + + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk"); +} + +static void +grub_scsi_close (grub_disk_t disk) +{ + grub_scsi_t scsi; + + scsi = disk->data; + return scsi->dev->close (scsi); +} + +static grub_err_t +grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + + scsi = disk->data; + + /* SCSI sectors are variable in size. GRUB uses 512 byte + sectors. */ + sector = grub_divmod64 (sector, scsi->blocksize >> GRUB_DISK_SECTOR_BITS, + NULL); + + /* Depending on the type, select a read function. */ + switch (scsi->devtype) + { + case grub_scsi_devtype_direct: + return grub_scsi_read10 (disk, sector, size, buf); + + case grub_scsi_devtype_cdrom: + return grub_scsi_read12 (disk, sector, size, buf); + } + + /* XXX: Never reached. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_scsi_write (grub_disk_t disk __attribute((unused)), + grub_disk_addr_t sector __attribute((unused)), + grub_size_t size __attribute((unused)), + const char *buf __attribute((unused))) +{ +#if 0 + /* XXX: Not tested yet! */ + + /* XXX: This should depend on the device type? */ + return grub_scsi_write10 (disk, sector, size, buf); +#endif + return GRUB_ERR_NOT_IMPLEMENTED_YET; +} + + +static struct grub_disk_dev grub_scsi_dev = + { + .name = "scsi", + .id = GRUB_DISK_DEVICE_SCSI_ID, + .iterate = grub_scsi_iterate, + .open = grub_scsi_open, + .close = grub_scsi_close, + .read = grub_scsi_read, + .write = grub_scsi_write, + .next = 0 + }; + +GRUB_MOD_INIT(scsi) +{ + grub_disk_dev_register (&grub_scsi_dev); +} + +GRUB_MOD_FINI(scsi) +{ + grub_disk_dev_unregister (&grub_scsi_dev); +} diff --git a/include/grub/disk.h b/include/grub/disk.h index 049cc91b9..a39afe2ff 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -40,6 +40,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_NAND_ID, GRUB_DISK_DEVICE_UUID_ID, GRUB_DISK_DEVICE_PXE_ID, + GRUB_DISK_DEVICE_SCSI_ID, }; struct grub_disk; diff --git a/include/grub/scsi.h b/include/grub/scsi.h new file mode 100644 index 000000000..fbe4582ca --- /dev/null +++ b/include/grub/scsi.h @@ -0,0 +1,88 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_SCSI_H +#define GRUB_SCSI_H 1 + +typedef struct grub_scsi_dev *grub_scsi_dev_t; + +void grub_scsi_dev_register (grub_scsi_dev_t dev); +void grub_scsi_dev_unregister (grub_scsi_dev_t dev); + +struct grub_scsi; + +struct grub_scsi_dev +{ + /* The device name. */ + const char *name; + + /* Call HOOK with each device name, until HOOK returns non-zero. */ + int (*iterate) (int (*hook) (const char *name, int luns)); + + /* Open the device named NAME, and set up SCSI. */ + grub_err_t (*open) (const char *name, struct grub_scsi *scsi); + + /* Close the scsi device SCSI. */ + void (*close) (struct grub_scsi *scsi); + + /* Read SIZE bytes from the device SCSI into BUF after sending the + command CMD of size CMDSIZE. */ + grub_err_t (*read) (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf); + + /* Write SIZE bytes from BUF to the device SCSI after sending the + command CMD of size CMDSIZE. */ + grub_err_t (*write) (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf); + + /* The next scsi device. */ + struct grub_scsi_dev *next; +}; + +struct grub_scsi +{ + /* The scsi device name. */ + char *name; + + /* The underlying scsi device. */ + grub_scsi_dev_t dev; + + /* Type of SCSI device. XXX: Make enum. */ + grub_uint8_t devtype; + + /* Number of LUNs. */ + int luns; + + /* LUN for this `struct grub_scsi'. */ + int lun; + + /* Set to 0 when not removable, 1 when removable. */ + int removable; + + /* Size of the device in blocks. */ + int size; + + /* Size of one block. */ + int blocksize; + + /* Device-specific data. */ + void *data; +}; +typedef struct grub_scsi *grub_scsi_t; + +#endif /* GRUB_SCSI_H */ diff --git a/include/grub/scsicmd.h b/include/grub/scsicmd.h new file mode 100644 index 000000000..40f237a17 --- /dev/null +++ b/include/grub/scsicmd.h @@ -0,0 +1,122 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_SCSICMD_H +#define GRUB_SCSICMD_H 1 + +#include + +#define GRUB_SCSI_DEVTYPE_MASK 31 +#define GRUB_SCSI_REMOVABLE_BIT 7 +#define GRUB_SCSI_LUN_SHIFT 5 + +struct grub_scsi_inquiry +{ + grub_uint8_t opcode; + grub_uint8_t lun; + grub_uint16_t reserved; + grub_uint16_t alloc_length; + grub_uint8_t reserved2; + grub_uint8_t pad[5]; +} __attribute__((packed)); + +struct grub_scsi_inquiry_data +{ + grub_uint8_t devtype; + grub_uint8_t rmb; + grub_uint16_t reserved; + grub_uint8_t length; + grub_uint8_t reserved2[3]; + char vendor[8]; + char prodid[16]; + char prodrev[4]; +} __attribute__((packed)); + +struct grub_scsi_read_capacity +{ + grub_uint8_t opcode; + grub_uint8_t lun; + grub_uint8_t reserved[8]; + grub_uint8_t pad[2]; +} __attribute__((packed)); + +struct grub_scsi_read_capacity_data +{ + grub_uint32_t size; + grub_uint32_t blocksize; +} __attribute__((packed)); + +struct grub_scsi_read10 +{ + grub_uint8_t opcode; + grub_uint8_t lun; + grub_uint32_t lba; + grub_uint8_t reserved; + grub_uint16_t size; + grub_uint8_t reserved2; + grub_uint16_t pad; +} __attribute__((packed)); + +struct grub_scsi_read12 +{ + grub_uint8_t opcode; + grub_uint8_t lun; + grub_uint32_t lba; + grub_uint32_t size; + grub_uint8_t reserved; + grub_uint8_t control; +} __attribute__((packed)); + +struct grub_scsi_write10 +{ + grub_uint8_t opcode; + grub_uint8_t lun; + grub_uint32_t lba; + grub_uint8_t reserved; + grub_uint16_t size; + grub_uint8_t reserved2; + grub_uint16_t pad; +} __attribute__((packed)); + +struct grub_scsi_write12 +{ + grub_uint8_t opcode; + grub_uint8_t lun; + grub_uint32_t lba; + grub_uint32_t size; + grub_uint8_t reserved; + grub_uint8_t control; +} __attribute__((packed)); + +typedef enum + { + grub_scsi_cmd_inquiry = 0x12, + grub_scsi_cmd_read_capacity = 0x25, + grub_scsi_cmd_read10 = 0x28, + grub_scsi_cmd_write10 = 0x2a, + grub_scsi_cmd_read12 = 0xa8, + grub_scsi_cmd_write12 = 0xaa + } grub_scsi_cmd_t; + +typedef enum + { + grub_scsi_devtype_direct = 0x00, + grub_scsi_devtype_cdrom = 0x05 + } grub_scsi_devtype_t; + +#endif /* GRUB_SCSICMD_H */