static grub_err_t grub_biosdisk_open (const char *name, grub_disk_t disk) { grub_uint64_t total_sectors = 0; int drive; struct grub_biosdisk_data *data; drive = grub_biosdisk_get_drive (name); if (drive < 0) return grub_errno; disk->has_partitions = ((drive & 0x80) && (drive != cd_drive)); disk->id = drive; data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data)); if (! data) return grub_errno; data->drive = drive; if ((cd_drive) && (drive == cd_drive)) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; total_sectors = GRUB_ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { /* HDD */ int version; version = grub_biosdisk_check_int13_extensions (drive); if (version) { struct grub_biosdisk_drp *drp = (struct grub_biosdisk_drp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; /* Clear out the DRP. */ grub_memset (drp, 0, sizeof (*drp)); drp->size = sizeof (*drp); if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp)) { data->flags = GRUB_BIOSDISK_FLAG_LBA; if (drp->total_sectors) total_sectors = drp->total_sectors; else /* Some buggy BIOSes doesn't return the total sectors correctly but returns zero. So if it is zero, compute it by C/H/S returned by the LBA BIOS call. */ total_sectors = drp->cylinders * drp->heads * drp->sectors; } } } if (! (data->flags & GRUB_BIOSDISK_FLAG_CDROM)) { if (grub_biosdisk_get_diskinfo_standard (drive, &data->cylinders, &data->heads, &data->sectors) != 0) { if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA)) { data->sectors = 63; data->heads = 255; data->cylinders = grub_divmod64 (total_sectors + data->heads * data->sectors - 1, data->heads * data->sectors, 0); } else { grub_free (data); return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name); } } if (! total_sectors) total_sectors = data->cylinders * data->heads * data->sectors; } disk->total_sectors = total_sectors; disk->data = data; return GRUB_ERR_NONE; }
static struct grub_biosdisk_data * get_drive_geom (int drive) { int i; struct grub_biosdisk_data *data = grub_biosdisk_geom; grub_uint64_t total_sectors = 0; unsigned long cylinders; int is_fb = 0; struct fb_mbr *m; for (i = 0; i < grub_biosdisk_num; i++, data++) if (data->drive == drive) return data; m = (struct fb_mbr *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; if (drive != cd_drive) { if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1, GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0) return 0; is_fb = ((m->fb_magic == FB_MAGIC_LONG) && (m->end_magic == 0xaa55)); } else is_fb = 0; if ((grub_biosdisk_num & (DISK_NUM_INC - 1)) == 0) { grub_biosdisk_geom = grub_realloc (grub_biosdisk_geom, (grub_biosdisk_num + DISK_NUM_INC) * sizeof (*data)); if (! grub_biosdisk_geom) return 0; } data = &grub_biosdisk_geom[grub_biosdisk_num++]; grub_memset (data, 0, sizeof (*data)); data->drive = drive; if (drive == cd_drive) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->max_sectors = data->sectors = 32; data->total_sectors = GRUB_ULONG_MAX; /* TODO: get the correct size. */ return data; } if (drive & 0x80) { /* HDD */ int version; version = grub_biosdisk_check_int13_extensions (drive); if (version) { struct grub_biosdisk_drp *drp = (struct grub_biosdisk_drp *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 512); /* Clear out the DRP. */ grub_memset (drp, 0, sizeof (*drp)); drp->size = sizeof (*drp); if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp)) { data->flags = GRUB_BIOSDISK_FLAG_LBA; if (drp->cylinders == 65535) total_sectors = GRUB_ULONG_MAX; else if (drp->total_sectors) total_sectors = drp->total_sectors; else /* Some buggy BIOSes doesn't return the total sectors correctly but returns zero. So if it is zero, compute it by C/H/S returned by the LBA BIOS call. */ total_sectors = drp->cylinders * drp->heads * drp->sectors; } } data->sectors = 63; data->heads = 255; cylinders = 0; grub_biosdisk_get_diskinfo_standard (drive, &cylinders, &data->heads, &data->sectors); } else { grub_uint16_t t; t = *((grub_uint16_t *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 0x18)); data->sectors = ((t > 0) && (t <= 63)) ? t : 18; t = *((grub_uint16_t *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + 0x1a)); data->heads = ((t > 0) && (t <= 255)) ? t : 2; cylinders = 1024; } data->max_sectors = 63; if (is_fb) { grub_uint16_t ofs = m->lba; data->max_sectors = m->max_sec; if (data->max_sectors >= 0x80) { data->max_sectors &= 0x7f; data->flags &= ~GRUB_BIOSDISK_FLAG_LBA; } if (! (data->flags & GRUB_BIOSDISK_FLAG_LBA)) { grub_uint16_t lba; if (grub_biosdisk_rw_standard (0x02, drive, 0, 1, 1, 1, GRUB_MEMORY_MACHINE_SCRATCH_SEG)) goto next; lba = m->end_magic; if (lba == 0xaa55) { if (m->fb_magic != FB_MAGIC_LONG) goto next; else lba = m->lba; } data->sectors = lba - ofs; if (grub_biosdisk_rw_standard (0x02, drive, 1, 0, 1, 1, GRUB_MEMORY_MACHINE_SCRATCH_SEG)) goto next; lba = m->end_magic; if (lba == 0xaa55) goto next; data->heads = (lba - ofs) / data->sectors; } data->flags |= GRUB_BIOSDISK_FLAG_FB; } if ((data->flags & GRUB_BIOSDISK_FLAG_LBA) && (data->max_sectors == 63)) data->max_sectors = GRUB_BIOSDISK_MAX_SECTORS; next: cylinders *= data->heads * data->sectors; if (total_sectors < cylinders) total_sectors = cylinders; data->total_sectors = total_sectors; return data; }