1 /* scsi.c - scsi support. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/disk.h>
22 #include <grub/kernel.h>
23 #include <grub/misc.h>
25 #include <grub/types.h>
26 #include <grub/machine/kernel.h>
27 #include <grub/scsi.h>
28 #include <grub/scsicmd.h>
31 static grub_scsi_dev_t grub_scsi_dev_list
;
34 grub_scsi_dev_register (grub_scsi_dev_t dev
)
36 dev
->next
= grub_scsi_dev_list
;
37 grub_scsi_dev_list
= dev
;
41 grub_scsi_dev_unregister (grub_scsi_dev_t dev
)
43 grub_scsi_dev_t
*p
, q
;
45 for (p
= &grub_scsi_dev_list
, q
= *p
; q
; p
= &(q
->next
), q
= q
->next
)
54 /* Determine the the device is removable and the type of the device
57 grub_scsi_inquiry (grub_scsi_t scsi
)
59 struct grub_scsi_inquiry iq
;
60 struct grub_scsi_inquiry_data iqd
;
63 iq
.opcode
= grub_scsi_cmd_inquiry
;
64 iq
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
66 iq
.alloc_length
= 0x24; /* XXX: Hardcoded for now */
69 err
= scsi
->dev
->read (scsi
, sizeof (iq
), (char *) &iq
,
70 sizeof (iqd
), (char *) &iqd
);
74 scsi
->devtype
= iqd
.devtype
& GRUB_SCSI_DEVTYPE_MASK
;
75 scsi
->removable
= iqd
.rmb
>> GRUB_SCSI_REMOVABLE_BIT
;
80 /* Read the capacity and block size of SCSI. */
82 grub_scsi_read_capacity (grub_scsi_t scsi
)
84 struct grub_scsi_read_capacity rc
;
85 struct grub_scsi_read_capacity_data rcd
;
88 rc
.opcode
= grub_scsi_cmd_read_capacity
;
89 rc
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
90 grub_memset (rc
.reserved
, 0, sizeof (rc
.reserved
));
92 err
= scsi
->dev
->read (scsi
, sizeof (rc
), (char *) &rc
,
93 sizeof (rcd
), (char *) &rcd
);
97 scsi
->size
= grub_be_to_cpu32 (rcd
.size
);
98 scsi
->blocksize
= grub_be_to_cpu32 (rcd
.blocksize
);
100 return GRUB_ERR_NONE
;
103 /* Send a SCSI request for DISK: read SIZE sectors starting with
104 sector SECTOR to BUF. */
106 grub_scsi_read10 (grub_disk_t disk
, grub_disk_addr_t sector
,
107 grub_size_t size
, char *buf
)
110 struct grub_scsi_read10 rd
;
114 rd
.opcode
= grub_scsi_cmd_read10
;
115 rd
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
116 rd
.lba
= grub_cpu_to_be32 (sector
);
118 rd
.size
= grub_cpu_to_be16 (size
);
122 return scsi
->dev
->read (scsi
, sizeof (rd
), (char *) &rd
, size
* scsi
->blocksize
, buf
);
125 /* Send a SCSI request for DISK: read SIZE sectors starting with
126 sector SECTOR to BUF. */
128 grub_scsi_read12 (grub_disk_t disk
, grub_disk_addr_t sector
,
129 grub_size_t size
, char *buf
)
132 struct grub_scsi_read12 rd
;
136 rd
.opcode
= grub_scsi_cmd_read12
;
137 rd
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
138 rd
.lba
= grub_cpu_to_be32 (sector
);
139 rd
.size
= grub_cpu_to_be32 (size
);
143 return scsi
->dev
->read (scsi
, sizeof (rd
), (char *) &rd
, size
* scsi
->blocksize
, buf
);
147 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
148 sectors starting with SECTOR. */
150 grub_scsi_write10 (grub_disk_t disk
, grub_disk_addr_t sector
,
151 grub_size_t size
, char *buf
)
154 struct grub_scsi_write10 wr
;
158 wr
.opcode
= grub_scsi_cmd_write10
;
159 wr
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
160 wr
.lba
= grub_cpu_to_be32 (sector
);
162 wr
.size
= grub_cpu_to_be16 (size
);
166 return scsi
->dev
->write (scsi
, sizeof (wr
), (char *) &wr
, size
* scsi
->blocksize
, buf
);
169 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
170 sectors starting with SECTOR. */
172 grub_scsi_write12 (grub_disk_t disk
, grub_disk_addr_t sector
,
173 grub_size_t size
, char *buf
)
176 struct grub_scsi_write10 wr
;
180 wr
.opcode
= grub_scsi_cmd_write12
;
181 wr
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
182 wr
.lba
= grub_cpu_to_be32 (sector
);
183 wr
.size
= grub_cpu_to_be32 (size
);
187 return scsi
->dev
->write (scsi
, sizeof (wr
), (char *) &wr
, size
* scsi
->blocksize
, buf
);
193 grub_scsi_iterate (int (*hook
) (const char *name
))
197 auto int scsi_iterate (const char *name
, int luns
);
199 int scsi_iterate (const char *name
, int luns
)
204 /* In case of a single LUN, just return `usbX'. */
208 /* In case of multiple LUNs, every LUN will get a prefix to
210 for (i
= 0; i
< luns
; i
++)
212 grub_sprintf (sname
, "%s%c", name
, 'a' + i
);
219 for (p
= grub_scsi_dev_list
; p
; p
= p
->next
)
220 if (p
->iterate
&& (p
->iterate
) (scsi_iterate
))
227 grub_scsi_open (const char *name
, grub_disk_t disk
)
235 scsi
= grub_malloc (sizeof (*scsi
));
239 len
= grub_strlen (name
);
240 lun
= name
[len
- 1] - 'a';
242 /* Try to detect a LUN ('a'-'z'), otherwise just use the first
244 if (lun
< 0 || lun
> 26)
247 for (p
= grub_scsi_dev_list
; p
; p
= p
->next
)
249 if (p
->open (name
, scsi
))
252 disk
->id
= (unsigned long) "scsi"; /* XXX */
256 scsi
->name
= grub_strdup (name
);
263 grub_dprintf ("scsi", "dev opened\n");
265 err
= grub_scsi_inquiry (scsi
);
269 grub_dprintf ("scsi", "inquiry failed\n");
273 grub_dprintf ("scsi", "inquiry: devtype=0x%02x removable=%d\n",
274 scsi
->devtype
, scsi
->removable
);
276 /* Try to be conservative about the device types
278 if (scsi
->devtype
!= grub_scsi_devtype_direct
279 && scsi
->devtype
!= grub_scsi_devtype_cdrom
)
282 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
,
283 "unknown SCSI device");
286 if (scsi
->devtype
== grub_scsi_devtype_cdrom
)
287 disk
->has_partitions
= 0;
289 disk
->has_partitions
= 1;
291 err
= grub_scsi_read_capacity (scsi
);
295 grub_dprintf ("scsi", "READ CAPACITY failed\n");
299 /* SCSI blocks can be something else than 512, although GRUB
300 wants 512 byte blocks. */
301 disk
->total_sectors
= ((scsi
->size
* scsi
->blocksize
)
302 << GRUB_DISK_SECTOR_BITS
);
304 grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
305 (unsigned long long) disk
->total_sectors
,
308 return GRUB_ERR_NONE
;
313 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "not a SCSI disk");
317 grub_scsi_close (grub_disk_t disk
)
322 scsi
->dev
->close (scsi
);
327 grub_scsi_read (grub_disk_t disk
, grub_disk_addr_t sector
,
328 grub_size_t size
, char *buf
)
332 /* SCSI sectors are variable in size. GRUB uses 512 byte
334 if (scsi
->blocksize
!= GRUB_DISK_SECTOR_SIZE
)
336 unsigned spb
= scsi
->blocksize
>> GRUB_DISK_SECTOR_BITS
;
337 grub_uint32_t sector_mod
;
339 if (! (spb
!= 0 && (scsi
->blocksize
& GRUB_DISK_SECTOR_SIZE
) == 0))
340 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
341 "Unsupported SCSI block size");
344 sector
= grub_divmod64 (sector
, spb
, §or_mod
);
346 if (! (sector_mod
== 0 && size
% spb
== 0))
347 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
348 "Unaligned SCSI read not supported");
355 /* Depending on the type, select a read function. */
356 switch (scsi
->devtype
)
358 case grub_scsi_devtype_direct
:
359 return grub_scsi_read10 (disk
, sector
, size
, buf
);
361 case grub_scsi_devtype_cdrom
:
362 return grub_scsi_read12 (disk
, sector
, size
, buf
);
365 /* XXX: Never reached. */
366 return GRUB_ERR_NONE
;
370 grub_scsi_write (grub_disk_t disk
__attribute((unused
)),
371 grub_disk_addr_t sector
__attribute((unused
)),
372 grub_size_t size
__attribute((unused
)),
373 const char *buf
__attribute((unused
)))
376 /* XXX: Not tested yet! */
378 /* XXX: This should depend on the device type? */
379 return grub_scsi_write10 (disk
, sector
, size
, buf
);
381 return GRUB_ERR_NOT_IMPLEMENTED_YET
;
385 static struct grub_disk_dev grub_scsi_dev
=
388 .id
= GRUB_DISK_DEVICE_SCSI_ID
,
389 .iterate
= grub_scsi_iterate
,
390 .open
= grub_scsi_open
,
391 .close
= grub_scsi_close
,
392 .read
= grub_scsi_read
,
393 .write
= grub_scsi_write
,
399 grub_disk_dev_register (&grub_scsi_dev
);
404 grub_disk_dev_unregister (&grub_scsi_dev
);