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/scsi.h>
27 #include <grub/scsicmd.h>
30 static grub_scsi_dev_t grub_scsi_dev_list
;
33 grub_scsi_dev_register (grub_scsi_dev_t dev
)
35 dev
->next
= grub_scsi_dev_list
;
36 grub_scsi_dev_list
= dev
;
40 grub_scsi_dev_unregister (grub_scsi_dev_t dev
)
42 grub_scsi_dev_t
*p
, q
;
44 for (p
= &grub_scsi_dev_list
, q
= *p
; q
; p
= &(q
->next
), q
= q
->next
)
53 /* Determine the the device is removable and the type of the device
56 grub_scsi_inquiry (grub_scsi_t scsi
)
58 struct grub_scsi_inquiry iq
;
59 struct grub_scsi_inquiry_data iqd
;
62 iq
.opcode
= grub_scsi_cmd_inquiry
;
63 iq
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
65 iq
.alloc_length
= 0x24; /* XXX: Hardcoded for now */
68 err
= scsi
->dev
->read (scsi
, sizeof (iq
), (char *) &iq
,
69 sizeof (iqd
), (char *) &iqd
);
73 scsi
->devtype
= iqd
.devtype
& GRUB_SCSI_DEVTYPE_MASK
;
74 scsi
->removable
= iqd
.rmb
>> GRUB_SCSI_REMOVABLE_BIT
;
79 /* Read the capacity and block size of SCSI. */
81 grub_scsi_read_capacity (grub_scsi_t scsi
)
83 struct grub_scsi_read_capacity rc
;
84 struct grub_scsi_read_capacity_data rcd
;
87 rc
.opcode
= grub_scsi_cmd_read_capacity
;
88 rc
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
89 grub_memset (rc
.reserved
, 0, sizeof (rc
.reserved
));
91 err
= scsi
->dev
->read (scsi
, sizeof (rc
), (char *) &rc
,
92 sizeof (rcd
), (char *) &rcd
);
96 scsi
->size
= grub_be_to_cpu32 (rcd
.size
);
97 scsi
->blocksize
= grub_be_to_cpu32 (rcd
.blocksize
);
102 /* Send a SCSI request for DISK: read SIZE sectors starting with
103 sector SECTOR to BUF. */
105 grub_scsi_read10 (grub_disk_t disk
, grub_disk_addr_t sector
,
106 grub_size_t size
, char *buf
)
109 struct grub_scsi_read10 rd
;
113 rd
.opcode
= grub_scsi_cmd_read10
;
114 rd
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
115 rd
.lba
= grub_cpu_to_be32 (sector
);
117 rd
.size
= grub_cpu_to_be16 (size
);
121 return scsi
->dev
->read (scsi
, sizeof (rd
), (char *) &rd
, size
* scsi
->blocksize
, buf
);
124 /* Send a SCSI request for DISK: read SIZE sectors starting with
125 sector SECTOR to BUF. */
127 grub_scsi_read12 (grub_disk_t disk
, grub_disk_addr_t sector
,
128 grub_size_t size
, char *buf
)
131 struct grub_scsi_read12 rd
;
135 rd
.opcode
= grub_scsi_cmd_read12
;
136 rd
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
137 rd
.lba
= grub_cpu_to_be32 (sector
);
138 rd
.size
= grub_cpu_to_be32 (size
);
142 return scsi
->dev
->read (scsi
, sizeof (rd
), (char *) &rd
, size
* scsi
->blocksize
, buf
);
146 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
147 sectors starting with SECTOR. */
149 grub_scsi_write10 (grub_disk_t disk
, grub_disk_addr_t sector
,
150 grub_size_t size
, char *buf
)
153 struct grub_scsi_write10 wr
;
157 wr
.opcode
= grub_scsi_cmd_write10
;
158 wr
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
159 wr
.lba
= grub_cpu_to_be32 (sector
);
161 wr
.size
= grub_cpu_to_be16 (size
);
165 return scsi
->dev
->write (scsi
, sizeof (wr
), (char *) &wr
, size
* scsi
->blocksize
, buf
);
168 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
169 sectors starting with SECTOR. */
171 grub_scsi_write12 (grub_disk_t disk
, grub_disk_addr_t sector
,
172 grub_size_t size
, char *buf
)
175 struct grub_scsi_write10 wr
;
179 wr
.opcode
= grub_scsi_cmd_write12
;
180 wr
.lun
= scsi
->lun
<< GRUB_SCSI_LUN_SHIFT
;
181 wr
.lba
= grub_cpu_to_be32 (sector
);
182 wr
.size
= grub_cpu_to_be32 (size
);
186 return scsi
->dev
->write (scsi
, sizeof (wr
), (char *) &wr
, size
* scsi
->blocksize
, buf
);
192 grub_scsi_iterate (int (*hook
) (const char *name
))
196 auto int scsi_iterate (const char *name
, int luns
);
198 int scsi_iterate (const char *name
, int luns
)
203 /* In case of a single LUN, just return `usbX'. */
207 /* In case of multiple LUNs, every LUN will get a prefix to
209 for (i
= 0; i
< luns
; i
++)
211 grub_sprintf (sname
, "%s%c", name
, 'a' + i
);
218 for (p
= grub_scsi_dev_list
; p
; p
= p
->next
)
219 if (p
->iterate
&& (p
->iterate
) (scsi_iterate
))
226 grub_scsi_open (const char *name
, grub_disk_t disk
)
234 scsi
= grub_malloc (sizeof (*scsi
));
238 len
= grub_strlen (name
);
239 lun
= name
[len
- 1] - 'a';
241 /* Try to detect a LUN ('a'-'z'), otherwise just use the first
243 if (lun
< 0 || lun
> 26)
246 for (p
= grub_scsi_dev_list
; p
; p
= p
->next
)
248 if (p
->open (name
, scsi
))
251 disk
->id
= (unsigned long) "scsi"; /* XXX */
255 scsi
->name
= grub_strdup (name
);
262 grub_dprintf ("scsi", "dev opened\n");
264 err
= grub_scsi_inquiry (scsi
);
268 grub_dprintf ("scsi", "inquiry failed\n");
272 grub_dprintf ("scsi", "inquiry: devtype=0x%02x removable=%d\n",
273 scsi
->devtype
, scsi
->removable
);
275 /* Try to be conservative about the device types
277 if (scsi
->devtype
!= grub_scsi_devtype_direct
278 && scsi
->devtype
!= grub_scsi_devtype_cdrom
)
281 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
,
282 "unknown SCSI device");
285 if (scsi
->devtype
== grub_scsi_devtype_cdrom
)
286 disk
->has_partitions
= 0;
288 disk
->has_partitions
= 1;
290 err
= grub_scsi_read_capacity (scsi
);
294 grub_dprintf ("scsi", "READ CAPACITY failed\n");
298 /* SCSI blocks can be something else than 512, although GRUB
299 wants 512 byte blocks. */
300 disk
->total_sectors
= ((scsi
->size
* scsi
->blocksize
)
301 << GRUB_DISK_SECTOR_BITS
);
303 grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
304 (unsigned long long) disk
->total_sectors
,
307 return GRUB_ERR_NONE
;
312 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "not a SCSI disk");
316 grub_scsi_close (grub_disk_t disk
)
321 scsi
->dev
->close (scsi
);
326 grub_scsi_read (grub_disk_t disk
, grub_disk_addr_t sector
,
327 grub_size_t size
, char *buf
)
333 /* SCSI sectors are variable in size. GRUB uses 512 byte
335 if (scsi
->blocksize
!= GRUB_DISK_SECTOR_SIZE
)
337 unsigned spb
= scsi
->blocksize
>> GRUB_DISK_SECTOR_BITS
;
338 if (! (spb
!= 0 && (scsi
->blocksize
& GRUB_DISK_SECTOR_SIZE
) == 0))
339 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
340 "Unsupported SCSI block size");
342 grub_uint32_t sector_mod
= 0;
343 sector
= grub_divmod64 (sector
, spb
, §or_mod
);
345 if (! (sector_mod
== 0 && size
% spb
== 0))
346 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
347 "Unaligned SCSI read not supported");
352 /* Depending on the type, select a read function. */
353 switch (scsi
->devtype
)
355 case grub_scsi_devtype_direct
:
356 return grub_scsi_read10 (disk
, sector
, size
, buf
);
358 case grub_scsi_devtype_cdrom
:
359 return grub_scsi_read12 (disk
, sector
, size
, buf
);
362 /* XXX: Never reached. */
363 return GRUB_ERR_NONE
;
367 grub_scsi_write (grub_disk_t disk
__attribute((unused
)),
368 grub_disk_addr_t sector
__attribute((unused
)),
369 grub_size_t size
__attribute((unused
)),
370 const char *buf
__attribute((unused
)))
373 /* XXX: Not tested yet! */
375 /* XXX: This should depend on the device type? */
376 return grub_scsi_write10 (disk
, sector
, size
, buf
);
378 return GRUB_ERR_NOT_IMPLEMENTED_YET
;
382 static struct grub_disk_dev grub_scsi_dev
=
385 .id
= GRUB_DISK_DEVICE_SCSI_ID
,
386 .iterate
= grub_scsi_iterate
,
387 .open
= grub_scsi_open
,
388 .close
= grub_scsi_close
,
389 .read
= grub_scsi_read
,
390 .write
= grub_scsi_write
,
396 grub_disk_dev_register (&grub_scsi_dev
);
401 grub_disk_dev_unregister (&grub_scsi_dev
);