ata.c 16.5 KB
Newer Older
1 2 3
/* ata.c - ATA disk access.  */
/*
 *  GRUB  --  GRand Unified Bootloader
4
 *  Copyright (C) 2007, 2008, 2009  Free Software Foundation, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 *  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 <http://www.gnu.org/licenses/>.
 */

20
#include <grub/ata.h>
21 22 23
#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/mm.h>
24
#include <grub/scsi.h>
25

26 27
GRUB_MOD_LICENSE ("GPLv3+");

28
static grub_ata_dev_t grub_ata_dev_list;
29

30
/* Byteorder has to be changed before strings can be read.  */
31
static void
32
grub_ata_strncpy (grub_uint16_t *dst16, grub_uint16_t *src16, grub_size_t len)
33 34 35 36
{
  unsigned int i;

  for (i = 0; i < len / 2; i++)
37
    *(dst16++) = grub_swap_bytes16 (*(src16++));
38
  *dst16 = 0;
39 40
}

41
static void
42
grub_ata_dumpinfo (struct grub_ata *dev, grub_uint16_t *info)
43
{
44
  grub_uint16_t text[21];
45 46

  /* The device information was read, dump it for debugging.  */
47 48 49 50 51 52
  grub_ata_strncpy (text, info + 10, 20);
  grub_dprintf ("ata", "Serial: %s\n", (char *) text);
  grub_ata_strncpy (text, info + 23, 8);
  grub_dprintf ("ata", "Firmware: %s\n", (char *) text);
  grub_ata_strncpy (text, info + 27, 40);
  grub_dprintf ("ata", "Model: %s\n", (char *) text);
53

54 55
  if (! dev->atapi)
    {
56
      grub_dprintf ("ata", "Addressing: %d\n", dev->addr);
57
      grub_dprintf ("ata", "Sectors: %lld\n", (unsigned long long) dev->size);
58
      grub_dprintf ("ata", "Sector size: %u\n", 1U << dev->log_sector_size);
59
    }
60 61 62
}

static grub_err_t
63
grub_atapi_identify (struct grub_ata *dev)
64
{
65
  struct grub_disk_ata_pass_through_parms parms;
66
  grub_uint16_t *info;
67
  grub_err_t err;
68

69
  info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
70 71 72
  if (! info)
    return grub_errno;

73
  grub_memset (&parms, 0, sizeof (parms));
74
  parms.taskfile.disk = 0xE0;
75
  parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE;
76 77
  parms.size = GRUB_DISK_SECTOR_SIZE;
  parms.buffer = info;
78

79
  err = dev->dev->readwrite (dev, &parms, *dev->present);
80
  if (err)
81
    {
82 83
      *dev->present = 0;
      return err;
84
    }
85

86
  if (parms.size != GRUB_DISK_SECTOR_SIZE)
87
    {
88 89 90
      *dev->present = 0;
      return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
			 "device cannot be identified");
91
    }
92 93 94 95 96 97 98

  dev->atapi = 1;

  grub_ata_dumpinfo (dev, info);

  grub_free (info);

99
  return GRUB_ERR_NONE;
100 101 102
}

static grub_err_t
103
grub_ata_identify (struct grub_ata *dev)
104
{
105
  struct grub_disk_ata_pass_through_parms parms;
106 107
  grub_uint64_t *info64;
  grub_uint32_t *info32;
108
  grub_uint16_t *info16;
109
  grub_err_t err;
110

111 112 113
  if (dev->atapi)
    return grub_atapi_identify (dev);

114 115 116 117
  info64 = grub_malloc (GRUB_DISK_SECTOR_SIZE);
  info32 = (grub_uint32_t *) info64;
  info16 = (grub_uint16_t *) info64;
  if (! info16)
118 119
    return grub_errno;

120
  grub_memset (&parms, 0, sizeof (parms));
121
  parms.buffer = info16;
122
  parms.size = GRUB_DISK_SECTOR_SIZE;
123
  parms.taskfile.disk = 0xE0;
124

125
  parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE;
126

127
  err = dev->dev->readwrite (dev, &parms, *dev->present);
128

129
  if (err || parms.size != GRUB_DISK_SECTOR_SIZE)
130
    {
131
      grub_uint8_t sts = parms.taskfile.status;
132
      grub_free (info16);
133 134
      grub_errno = GRUB_ERR_NONE;
      if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ
135
		   | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR
136
	  && (parms.taskfile.error & 0x04 /* ABRT */))
137 138 139 140
	/* Device without ATA IDENTIFY, try ATAPI.  */
	return grub_atapi_identify (dev);

      else if (sts == 0x00)
141
	{
142
	  *dev->present = 0;
143 144 145
	  /* No device, return error but don't print message.  */
	  return GRUB_ERR_UNKNOWN_DEVICE;
	}
146
      else
147
	{
148
	  *dev->present = 0;
149 150 151 152
	  /* Other Error.  */
	  return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
			     "device cannot be identified");
	}
153 154
    }

155 156 157 158 159 160 161
  /* Now it is certain that this is not an ATAPI device.  */
  dev->atapi = 0;

  /* CHS is always supported.  */
  dev->addr = GRUB_ATA_CHS;

  /* Check if LBA is supported.  */
162
  if (info16[49] & grub_cpu_to_le16_compile_time ((1 << 9)))
163 164
    {
      /* Check if LBA48 is supported.  */
165
      if (info16[83] & grub_cpu_to_le16_compile_time ((1 << 10)))
166 167 168 169 170 171 172
	dev->addr = GRUB_ATA_LBA48;
      else
	dev->addr = GRUB_ATA_LBA;
    }

  /* Determine the amount of sectors.  */
  if (dev->addr != GRUB_ATA_LBA48)
173
    dev->size = grub_le_to_cpu32 (info32[30]);
174
  else
175
    dev->size = grub_le_to_cpu64 (info64[25]);
176

177
  if (info16[106] & grub_cpu_to_le16_compile_time ((1 << 12)))
178 179
    {
      grub_uint32_t secsize;
180
      secsize = grub_le_to_cpu32 (grub_get_unaligned32 (&info16[117]));
181 182 183 184 185 186 187 188 189 190 191
      if (secsize & (secsize - 1) || !secsize
	  || secsize > 1048576)
	secsize = 256;
      for (dev->log_sector_size = 0;
	   (1U << dev->log_sector_size) < secsize;
	   dev->log_sector_size++);
      dev->log_sector_size++;
    }
  else
    dev->log_sector_size = 9;

192
  /* Read CHS information.  */
193 194 195
  dev->cylinders = grub_le_to_cpu16 (info16[1]);
  dev->heads = grub_le_to_cpu16 (info16[3]);
  dev->sectors_per_track = grub_le_to_cpu16 (info16[6]);
196

197
  grub_ata_dumpinfo (dev, info16);
198

199
  grub_free (info16);
200 201 202 203 204

  return 0;
}

static grub_err_t
205 206
grub_ata_setaddress (struct grub_ata *dev,
		     struct grub_disk_ata_pass_through_parms *parms,
207
		     grub_disk_addr_t sector,
208 209
		     grub_size_t size,
		     grub_ata_addressing_t addressing)
210 211 212 213 214 215 216 217 218
{
  switch (addressing)
    {
    case GRUB_ATA_CHS:
      {
	unsigned int cylinder;
	unsigned int head;
	unsigned int sect;

219 220 221 222 223 224
	if (dev->sectors_per_track == 0
	    || dev->heads == 0)
	  return grub_error (GRUB_ERR_OUT_OF_RANGE,
			     "sector %d cannot be addressed "
			     "using CHS addressing", sector);

225 226 227 228 229 230 231 232 233 234
	/* Calculate the sector, cylinder and head to use.  */
	sect = ((grub_uint32_t) sector % dev->sectors_per_track) + 1;
	cylinder = (((grub_uint32_t) sector / dev->sectors_per_track)
		    / dev->heads);
	head = ((grub_uint32_t) sector / dev->sectors_per_track) % dev->heads;

	if (sect > dev->sectors_per_track
	    || cylinder > dev->cylinders
	    || head > dev->heads)
	  return grub_error (GRUB_ERR_OUT_OF_RANGE,
235
			     "sector %d cannot be addressed "
236
			     "using CHS addressing", sector);
237
	
238
	parms->taskfile.disk = 0xE0 | head;
239 240 241
	parms->taskfile.sectnum = sect;
	parms->taskfile.cyllsb = cylinder & 0xFF;
	parms->taskfile.cylmsb = cylinder >> 8;
242 243 244 245 246 247 248

	break;
      }

    case GRUB_ATA_LBA:
      if (size == 256)
	size = 0;
249
      parms->taskfile.disk = 0xE0 | ((sector >> 24) & 0x0F);
250

251 252 253 254
      parms->taskfile.sectors = size;
      parms->taskfile.lba_low = sector & 0xFF;
      parms->taskfile.lba_mid = (sector >> 8) & 0xFF;
      parms->taskfile.lba_high = (sector >> 16) & 0xFF;
255 256 257 258 259 260
      break;

    case GRUB_ATA_LBA48:
      if (size == 65536)
	size = 0;

261
      parms->taskfile.disk = 0xE0;
262

263
      /* Set "Previous".  */
264 265 266 267 268
      parms->taskfile.sectors = size & 0xFF;
      parms->taskfile.lba_low = sector & 0xFF;
      parms->taskfile.lba_mid = (sector >> 8) & 0xFF;
      parms->taskfile.lba_high = (sector >> 16) & 0xFF;

269
      /* Set "Current".  */
270 271 272 273
      parms->taskfile.sectors48 = (size >> 8) & 0xFF;
      parms->taskfile.lba48_low = (sector >> 24) & 0xFF;
      parms->taskfile.lba48_mid = (sector >> 32) & 0xFF;
      parms->taskfile.lba48_high = (sector >> 40) & 0xFF;
274 275 276 277 278 279 280 281 282 283 284

      break;
    }

  return GRUB_ERR_NONE;
}

static grub_err_t
grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
		    grub_size_t size, char *buf, int rw)
{
285
  struct grub_ata *ata = disk->data;
286

287
  grub_ata_addressing_t addressing = ata->addr;
288 289
  grub_size_t batch;
  int cmd, cmd_write;
290
  grub_size_t nsectors = 0;
291

292 293 294
  grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n",
	       (unsigned long long) size, rw);

295 296
  if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
    {
297 298 299 300 301 302 303 304 305 306
      if (ata->dma)
	{
	  cmd = GRUB_ATA_CMD_READ_SECTORS_DMA_EXT;
	  cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA_EXT;
	}
      else
	{
	  cmd = GRUB_ATA_CMD_READ_SECTORS_EXT;
	  cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_EXT;
	}
307 308 309 310 311
    }
  else
    {
      if (addressing == GRUB_ATA_LBA48)
	addressing = GRUB_ATA_LBA;
312 313 314 315 316 317 318 319 320 321
      if (ata->dma)
	{
	  cmd = GRUB_ATA_CMD_READ_SECTORS_DMA;
	  cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA;
	}
      else
	{
	  cmd = GRUB_ATA_CMD_READ_SECTORS;
	  cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
	}
322 323
    }

324 325 326 327
  if (addressing != GRUB_ATA_CHS)
    batch = 256;
  else
    batch = 1;
328

329
  while (nsectors < size)
330
    {
331 332 333
      struct grub_disk_ata_pass_through_parms parms;
      grub_err_t err;

334 335 336
      if (size - nsectors < batch)
	batch = size - nsectors;

337
      grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch);
338
      grub_memset (&parms, 0, sizeof (parms));
339
      grub_ata_setaddress (ata, &parms, sector, batch, addressing);
340 341
      parms.taskfile.cmd = (! rw ? cmd : cmd_write);
      parms.buffer = buf;
342
      parms.size = batch << ata->log_sector_size;
343
      parms.write = rw;
344 345
      if (ata->dma)
	parms.dma = 1;
346
  
347
      err = ata->dev->readwrite (ata, &parms, 0);
348 349
      if (err)
	return err;
350
      if (parms.size != batch << ata->log_sector_size)
351
	return grub_error (GRUB_ERR_READ_ERROR, "incomplete read");
352
      buf += batch << ata->log_sector_size;
353 354
      sector += batch;
      nsectors += batch;
355 356 357 358 359 360 361
    }

  return GRUB_ERR_NONE;
}



362 363
static inline void
grub_ata_real_close (struct grub_ata *ata)
364
{
365 366 367
  if (ata->dev->close)
    ata->dev->close (ata);
}
368

369 370 371 372 373
static struct grub_ata *
grub_ata_real_open (int id, int bus)
{
  struct grub_ata *ata;
  grub_ata_dev_t p;
374

375
  ata = grub_zalloc (sizeof (*ata));
376 377 378
  if (!ata)
    return NULL;
  for (p = grub_ata_dev_list; p; p = p->next)
379
    {
380
      grub_err_t err;
381
      if (p->open (id, bus, ata))
382 383 384 385
	{
	  grub_errno = GRUB_ERR_NONE;
	  continue;
	}
386 387 388 389 390
      ata->dev = p;
      /* Use the IDENTIFY DEVICE command to query the device.  */
      err = grub_ata_identify (ata);
      if (err)
	{
391 392
	  if (!grub_errno)
	    grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device");
393 394 395 396 397 398 399 400 401
	  grub_free (ata);
	  return NULL;
	}
      return ata;
    }
  grub_free (ata);
  grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device");
  return NULL;
}
402

403 404 405 406 407 408 409 410
/* Context for grub_ata_iterate.  */
struct grub_ata_iterate_ctx
{
  grub_disk_dev_iterate_hook_t hook;
  void *hook_data;
};

/* Helper for grub_ata_iterate.  */
411
static int
412
grub_ata_iterate_iter (int id, int bus, void *data)
413
{
414 415 416 417
  struct grub_ata_iterate_ctx *ctx = data;
  struct grub_ata *ata;
  int ret;
  char devname[40];
418

419
  ata = grub_ata_real_open (id, bus);
420

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
  if (!ata)
    {
      grub_errno = GRUB_ERR_NONE;
      return 0;
    }
  if (ata->atapi)
    {
      grub_ata_real_close (ata);
      return 0;
    }
  grub_snprintf (devname, sizeof (devname), 
		 "%s%d", grub_scsi_names[id], bus);
  ret = ctx->hook (devname, ctx->hook_data);
  grub_ata_real_close (ata);
  return ret;
}
437

438 439 440 441 442
static int
grub_ata_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
		  grub_disk_pull_t pull)
{
  struct grub_ata_iterate_ctx ctx = { hook, hook_data };
443 444 445
  grub_ata_dev_t p;
  
  for (p = grub_ata_dev_list; p; p = p->next)
446
    if (p->iterate && p->iterate (grub_ata_iterate_iter, &ctx, pull))
447
      return 1;
448 449 450 451
  return 0;
}

static grub_err_t
452
grub_ata_open (const char *name, grub_disk_t disk)
453
{
454 455
  unsigned id, bus;
  struct grub_ata *ata;
456

457 458 459 460 461 462
  for (id = 0; id < GRUB_SCSI_NUM_SUBSYSTEMS; id++)
    if (grub_strncmp (grub_scsi_names[id], name,
		      grub_strlen (grub_scsi_names[id])) == 0
	&& grub_isdigit (name[grub_strlen (grub_scsi_names[id])]))
      break;
  if (id == GRUB_SCSI_NUM_SUBSYSTEMS)
463
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
464 465 466 467
  bus = grub_strtoul (name + grub_strlen (grub_scsi_names[id]), 0, 0);
  ata = grub_ata_real_open (id, bus);
  if (!ata)
    return grub_errno;
468

469 470
  if (ata->atapi)
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
471

472
  disk->total_sectors = ata->size;
473 474 475 476
  disk->max_agglomerate = (ata->maxbuffer >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
  if (disk->max_agglomerate > (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size)))
    disk->max_agglomerate = (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size));

477
  disk->log_sector_size = ata->log_sector_size;
478

479
  disk->id = grub_make_scsi_id (id, bus, 0);
480

481
  disk->data = ata;
482 483 484 485 486

  return 0;
}

static void
487
grub_ata_close (grub_disk_t disk)
488
{
489 490
  struct grub_ata *ata = disk->data;
  grub_ata_real_close (ata);
491 492
}

493 494 495
static grub_err_t
grub_ata_read (grub_disk_t disk, grub_disk_addr_t sector,
	       grub_size_t size, char *buf)
496
{
497 498
  return grub_ata_readwrite (disk, sector, size, buf, 0);
}
499 500

static grub_err_t
501 502 503 504
grub_ata_write (grub_disk_t disk,
		grub_disk_addr_t sector,
		grub_size_t size,
		const char *buf)
505
{
506 507
  return grub_ata_readwrite (disk, sector, size, (char *) buf, 1);
}
508

509 510 511 512 513 514 515 516 517 518 519
static struct grub_disk_dev grub_atadisk_dev =
  {
    .name = "ATA",
    .id = GRUB_DISK_DEVICE_ATA_ID,
    .iterate = grub_ata_iterate,
    .open = grub_ata_open,
    .close = grub_ata_close,
    .read = grub_ata_read,
    .write = grub_ata_write,
    .next = 0
  };
520

521 522 523 524


/* ATAPI code.  */

525
static grub_err_t
526 527
grub_atapi_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
		 grub_size_t size, char *buf)
528
{
529 530 531
  struct grub_ata *dev = scsi->data;
  struct grub_disk_ata_pass_through_parms parms;
  grub_err_t err;
532

533
  grub_dprintf("ata", "grub_atapi_read (size=%llu)\n", (unsigned long long) size);
534 535 536 537 538 539 540 541 542 543 544 545 546 547
  grub_memset (&parms, 0, sizeof (parms));

  parms.taskfile.disk = 0;
  parms.taskfile.features = 0;
  parms.taskfile.atapi_ireason = 0;
  parms.taskfile.atapi_cnthigh = size >> 8;
  parms.taskfile.atapi_cntlow = size & 0xff;
  parms.taskfile.cmd = GRUB_ATA_CMD_PACKET;
  parms.cmd = cmd;
  parms.cmdsize = cmdsize;

  parms.size = size;
  parms.buffer = buf;
  
548
  err = dev->dev->readwrite (dev, &parms, 0);
549 550
  if (err)
    return err;
551

552 553
  if (parms.size != size)
    return grub_error (GRUB_ERR_READ_ERROR, "incomplete ATAPI read");
554
  return GRUB_ERR_NONE;
555
}
556

557
static grub_err_t
558
grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)),
559
		  grub_size_t cmdsize __attribute__((unused)),
560 561
		  char *cmd __attribute__((unused)),
		  grub_size_t size __attribute__((unused)),
562
		  const char *buf __attribute__((unused)))
563
{
564 565
  // XXX: scsi.mod does not use write yet.
  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ATAPI write not implemented");
566 567 568
}

static grub_err_t
569
grub_atapi_open (int id, int bus, struct grub_scsi *scsi)
570
{
571
  struct grub_ata *ata;
572

573 574 575 576 577 578
  ata = grub_ata_real_open (id, bus);
  if (!ata)
    return grub_errno;
    
  if (! ata->atapi)
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
579

580
  scsi->data = ata;
581
  scsi->luns = 1;
582

583 584
  return GRUB_ERR_NONE;
}
585

586 587 588 589 590 591 592 593
/* Context for grub_atapi_iterate.  */
struct grub_atapi_iterate_ctx
{
  grub_scsi_dev_iterate_hook_t hook;
  void *hook_data;
};

/* Helper for grub_atapi_iterate.  */
594
static int
595
grub_atapi_iterate_iter (int id, int bus, void *data)
596
{
597 598 599
  struct grub_atapi_iterate_ctx *ctx = data;
  struct grub_ata *ata;
  int ret;
600

601
  ata = grub_ata_real_open (id, bus);
602

603 604 605 606 607 608 609 610 611 612 613 614 615 616
  if (!ata)
    {
      grub_errno = GRUB_ERR_NONE;
      return 0;
    }
  if (!ata->atapi)
    {
      grub_ata_real_close (ata);
      return 0;
    }
  ret = ctx->hook (id, bus, 1, ctx->hook_data);
  grub_ata_real_close (ata);
  return ret;
}
617

618 619 620 621 622
static int
grub_atapi_iterate (grub_scsi_dev_iterate_hook_t hook, void *hook_data,
		    grub_disk_pull_t pull)
{
  struct grub_atapi_iterate_ctx ctx = { hook, hook_data };
623 624 625
  grub_ata_dev_t p;
  
  for (p = grub_ata_dev_list; p; p = p->next)
626
    if (p->iterate && p->iterate (grub_atapi_iterate_iter, &ctx, pull))
627 628 629
      return 1;
  return 0;
}
630

631 632 633 634 635
static void
grub_atapi_close (grub_scsi_t disk)
{
  struct grub_ata *ata = disk->data;
  grub_ata_real_close (ata);
636 637
}

638

639 640 641 642 643
void
grub_ata_dev_register (grub_ata_dev_t dev)
{
  dev->next = grub_ata_dev_list;
  grub_ata_dev_list = dev;
644 645
}

646 647 648 649 650 651 652 653 654 655 656 657
void
grub_ata_dev_unregister (grub_ata_dev_t dev)
{
  grub_ata_dev_t *p, q;

  for (p = &grub_ata_dev_list, q = *p; q; p = &(q->next), q = q->next)
    if (q == dev)
      {
        *p = q->next;
	break;
      }
}
658 659

static struct grub_scsi_dev grub_atapi_dev =
660
  {
661 662
    .iterate = grub_atapi_iterate,
    .open = grub_atapi_open,
663
    .close = grub_atapi_close,
664
    .read = grub_atapi_read,
665 666
    .write = grub_atapi_write,
    .next = 0
667
  };
668 669 670 671 672 673



GRUB_MOD_INIT(ata)
{
  grub_disk_dev_register (&grub_atadisk_dev);
674 675 676

  /* ATAPI devices are handled by scsi.mod.  */
  grub_scsi_dev_register (&grub_atapi_dev);
677 678 679 680
}

GRUB_MOD_FINI(ata)
{
681
  grub_scsi_dev_unregister (&grub_atapi_dev);
682 683
  grub_disk_dev_unregister (&grub_atadisk_dev);
}