merge 4096 into luks

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-04-24 03:12:06 +02:00
commit 572e3ea650
9 changed files with 334 additions and 221 deletions

View file

@ -460,11 +460,12 @@ grub_cryptodisk_read (grub_disk_t disk, grub_disk_addr_t sector,
#ifdef GRUB_UTIL #ifdef GRUB_UTIL
if (dev->cheat) if (dev->cheat)
{ {
err = grub_util_fd_sector_seek (dev->cheat_fd, dev->cheat, sector); err = grub_util_fd_seek (dev->cheat_fd, dev->cheat,
sector << disk->log_sector_size);
if (err) if (err)
return err; return err;
if (grub_util_fd_read (dev->cheat_fd, buf, size << GRUB_DISK_SECTOR_BITS) if (grub_util_fd_read (dev->cheat_fd, buf, size << disk->log_sector_size)
!= (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) != (ssize_t) (size << disk->log_sector_size))
return grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", return grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'",
dev->cheat); dev->cheat);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
@ -476,15 +477,17 @@ grub_cryptodisk_read (grub_disk_t disk, grub_disk_addr_t sector,
PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n", PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n",
size, sector, dev->offset); size, sector, dev->offset);
err = grub_disk_read (dev->source_disk, sector + dev->offset, 0, err = grub_disk_read (dev->source_disk,
size << GRUB_DISK_SECTOR_BITS, buf); (sector << (disk->log_sector_size
- GRUB_DISK_SECTOR_BITS)) + dev->offset, 0,
size << disk->log_sector_size, buf);
if (err) if (err)
{ {
grub_dprintf ("cryptodisk", "grub_disk_read failed with error %d\n", err); grub_dprintf ("cryptodisk", "grub_disk_read failed with error %d\n", err);
return err; return err;
} }
gcry_err = grub_cryptodisk_decrypt (dev, (grub_uint8_t *) buf, gcry_err = grub_cryptodisk_decrypt (dev, (grub_uint8_t *) buf,
size << GRUB_DISK_SECTOR_BITS, size << disk->log_sector_size,
sector); sector);
return grub_crypto_gcry_error (gcry_err); return grub_crypto_gcry_error (gcry_err);
} }

View file

@ -33,12 +33,10 @@ struct grub_efidisk_data
grub_efi_device_path_t *device_path; grub_efi_device_path_t *device_path;
grub_efi_device_path_t *last_device_path; grub_efi_device_path_t *last_device_path;
grub_efi_block_io_t *block_io; grub_efi_block_io_t *block_io;
grub_efi_disk_io_t *disk_io;
struct grub_efidisk_data *next; struct grub_efidisk_data *next;
}; };
/* GUIDs. */ /* GUID. */
static grub_efi_guid_t disk_io_guid = GRUB_EFI_DISK_IO_GUID;
static grub_efi_guid_t block_io_guid = GRUB_EFI_BLOCK_IO_GUID; static grub_efi_guid_t block_io_guid = GRUB_EFI_BLOCK_IO_GUID;
static struct grub_efidisk_data *fd_devices; static struct grub_efidisk_data *fd_devices;
@ -143,7 +141,7 @@ make_devices (void)
struct grub_efidisk_data *devices = 0; struct grub_efidisk_data *devices = 0;
/* Find handles which support the disk io interface. */ /* Find handles which support the disk io interface. */
handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &disk_io_guid, handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &block_io_guid,
0, &num_handles); 0, &num_handles);
if (! handles) if (! handles)
return 0; return 0;
@ -155,7 +153,6 @@ make_devices (void)
grub_efi_device_path_t *ldp; grub_efi_device_path_t *ldp;
struct grub_efidisk_data *d; struct grub_efidisk_data *d;
grub_efi_block_io_t *bio; grub_efi_block_io_t *bio;
grub_efi_disk_io_t *dio;
dp = grub_efi_get_device_path (*handle); dp = grub_efi_get_device_path (*handle);
if (! dp) if (! dp)
@ -168,9 +165,7 @@ make_devices (void)
bio = grub_efi_open_protocol (*handle, &block_io_guid, bio = grub_efi_open_protocol (*handle, &block_io_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
dio = grub_efi_open_protocol (*handle, &disk_io_guid, if (! bio)
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (! bio || ! dio)
/* This should not happen... Why? */ /* This should not happen... Why? */
continue; continue;
@ -186,7 +181,6 @@ make_devices (void)
d->device_path = dp; d->device_path = dp;
d->last_device_path = ldp; d->last_device_path = ldp;
d->block_io = bio; d->block_io = bio;
d->disk_io = dio;
d->next = devices; d->next = devices;
devices = d; devices = d;
} }
@ -536,8 +530,13 @@ grub_efidisk_open (const char *name, struct grub_disk *disk)
and total sectors should be replaced with total blocks. */ and total sectors should be replaced with total blocks. */
grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n", grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n",
m, (unsigned long long) m->last_block, m->block_size); m, (unsigned long long) m->last_block, m->block_size);
disk->total_sectors = (m->last_block disk->total_sectors = m->last_block;
* (m->block_size >> GRUB_DISK_SECTOR_BITS)); if (m->block_size & (m->block_size - 1) || !m->block_size)
return grub_error (GRUB_ERR_IO, "invalid sector size %d",
m->block_size);
for (disk->log_sector_size = 0;
(1U << disk->log_sector_size) < m->block_size;
disk->log_sector_size++);
disk->data = d; disk->data = d;
grub_dprintf ("efidisk", "opening %s succeeded\n", name); grub_dprintf ("efidisk", "opening %s succeeded\n", name);
@ -558,21 +557,19 @@ grub_efidisk_read (struct grub_disk *disk, grub_disk_addr_t sector,
{ {
/* For now, use the disk io interface rather than the block io's. */ /* For now, use the disk io interface rather than the block io's. */
struct grub_efidisk_data *d; struct grub_efidisk_data *d;
grub_efi_disk_io_t *dio;
grub_efi_block_io_t *bio; grub_efi_block_io_t *bio;
grub_efi_status_t status; grub_efi_status_t status;
d = disk->data; d = disk->data;
dio = d->disk_io;
bio = d->block_io; bio = d->block_io;
grub_dprintf ("efidisk", grub_dprintf ("efidisk",
"reading 0x%lx sectors at the sector 0x%llx from %s\n", "reading 0x%lx sectors at the sector 0x%llx from %s\n",
(unsigned long) size, (unsigned long long) sector, disk->name); (unsigned long) size, (unsigned long long) sector, disk->name);
status = efi_call_5 (dio->read, dio, bio->media->media_id, status = efi_call_5 (bio->read_blocks, bio, bio->media->media_id,
(grub_efi_uint64_t) sector << GRUB_DISK_SECTOR_BITS, (grub_efi_uint64_t) sector,
(grub_efi_uintn_t) size << GRUB_DISK_SECTOR_BITS, (grub_efi_uintn_t) size << disk->log_sector_size,
buf); buf);
if (status != GRUB_EFI_SUCCESS) if (status != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_READ_ERROR, "efidisk read error"); return grub_error (GRUB_ERR_READ_ERROR, "efidisk read error");
@ -586,21 +583,19 @@ grub_efidisk_write (struct grub_disk *disk, grub_disk_addr_t sector,
{ {
/* For now, use the disk io interface rather than the block io's. */ /* For now, use the disk io interface rather than the block io's. */
struct grub_efidisk_data *d; struct grub_efidisk_data *d;
grub_efi_disk_io_t *dio;
grub_efi_block_io_t *bio; grub_efi_block_io_t *bio;
grub_efi_status_t status; grub_efi_status_t status;
d = disk->data; d = disk->data;
dio = d->disk_io;
bio = d->block_io; bio = d->block_io;
grub_dprintf ("efidisk", grub_dprintf ("efidisk",
"writing 0x%lx sectors at the sector 0x%llx to %s\n", "writing 0x%lx sectors at the sector 0x%llx to %s\n",
(unsigned long) size, (unsigned long long) sector, disk->name); (unsigned long) size, (unsigned long long) sector, disk->name);
status = efi_call_5 (dio->write, dio, bio->media->media_id, status = efi_call_5 (bio->write_blocks, bio, bio->media->media_id,
(grub_efi_uint64_t) sector << GRUB_DISK_SECTOR_BITS, (grub_efi_uint64_t) sector,
(grub_efi_uintn_t) size << GRUB_DISK_SECTOR_BITS, (grub_efi_uintn_t) size << disk->log_sector_size,
(void *) buf); (void *) buf);
if (status != GRUB_EFI_SUCCESS) if (status != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_WRITE_ERROR, "efidisk write error"); return grub_error (GRUB_ERR_WRITE_ERROR, "efidisk write error");

View file

@ -350,7 +350,8 @@ grub_biosdisk_open (const char *name, grub_disk_t disk,
if ((cd_drive) && (drive == cd_drive)) if ((cd_drive) && (drive == cd_drive))
{ {
data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM;
data->sectors = 32; data->sectors = 8;
disk->log_sector_size = 11;
/* TODO: get the correct size. */ /* TODO: get the correct size. */
total_sectors = GRUB_DISK_SIZE_UNKNOWN; total_sectors = GRUB_DISK_SIZE_UNKNOWN;
} }
@ -359,6 +360,8 @@ grub_biosdisk_open (const char *name, grub_disk_t disk,
/* HDD */ /* HDD */
int version; int version;
disk->log_sector_size = 9;
version = grub_biosdisk_check_int13_extensions (drive); version = grub_biosdisk_check_int13_extensions (drive);
if (version) if (version)
{ {
@ -379,6 +382,15 @@ grub_biosdisk_open (const char *name, grub_disk_t disk,
correctly but returns zero. So if it is zero, compute correctly but returns zero. So if it is zero, compute
it by C/H/S returned by the LBA BIOS call. */ it by C/H/S returned by the LBA BIOS call. */
total_sectors = drp->cylinders * drp->heads * drp->sectors; total_sectors = drp->cylinders * drp->heads * drp->sectors;
if (drp->bytes_per_sector
&& !(drp->bytes_per_sector & (drp->bytes_per_sector - 1))
&& drp->bytes_per_sector >= 512
&& drp->bytes_per_sector <= 16384)
{
for (disk->log_sector_size = 0;
(1 << disk->log_sector_size) < drp->bytes_per_sector;
disk->log_sector_size++);
}
} }
} }
} }
@ -441,7 +453,7 @@ grub_biosdisk_rw (int cmd, grub_disk_t disk,
dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
+ (data->sectors + (data->sectors
<< GRUB_DISK_SECTOR_BITS)); << disk->log_sector_size));
dap->length = sizeof (*dap); dap->length = sizeof (*dap);
dap->reserved = 0; dap->reserved = 0;
dap->blocks = size; dap->blocks = size;
@ -455,9 +467,6 @@ grub_biosdisk_rw (int cmd, grub_disk_t disk,
if (cmd) if (cmd)
return grub_error (GRUB_ERR_WRITE_ERROR, "can\'t write to cdrom"); return grub_error (GRUB_ERR_WRITE_ERROR, "can\'t write to cdrom");
dap->blocks = ALIGN_UP (dap->blocks, 4) >> 2;
dap->block >>= 2;
for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++) for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++)
if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap)) if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap))
break; break;
@ -513,10 +522,12 @@ grub_biosdisk_rw (int cmd, grub_disk_t disk,
/* Return the number of sectors which can be read safely at a time. */ /* Return the number of sectors which can be read safely at a time. */
static grub_size_t static grub_size_t
get_safe_sectors (grub_disk_addr_t sector, grub_uint32_t sectors) get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector)
{ {
grub_size_t size; grub_size_t size;
grub_uint32_t offset; grub_uint32_t offset;
struct grub_biosdisk_data *data = disk->data;
grub_uint32_t sectors = data->sectors;
/* OFFSET = SECTOR % SECTORS */ /* OFFSET = SECTOR % SECTORS */
grub_divmod64 (sector, sectors, &offset); grub_divmod64 (sector, sectors, &offset);
@ -524,8 +535,8 @@ get_safe_sectors (grub_disk_addr_t sector, grub_uint32_t sectors)
size = sectors - offset; size = sectors - offset;
/* Limit the max to 0x7f because of Phoenix EDD. */ /* Limit the max to 0x7f because of Phoenix EDD. */
if (size > 0x7f) if (size > ((0x7fU << GRUB_DISK_SECTOR_BITS) >> disk->log_sector_size))
size = 0x7f; size = ((0x7fU << GRUB_DISK_SECTOR_BITS) >> disk->log_sector_size);
return size; return size;
} }
@ -534,21 +545,11 @@ static grub_err_t
grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf) grub_size_t size, char *buf)
{ {
struct grub_biosdisk_data *data = disk->data;
while (size) while (size)
{ {
grub_size_t len; grub_size_t len;
grub_size_t cdoff = 0;
len = get_safe_sectors (sector, data->sectors); len = get_safe_sectors (disk, sector);
if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
{
cdoff = (sector & 3) << GRUB_DISK_SECTOR_BITS;
len = ALIGN_UP (sector + len, 4) - (sector & ~3);
sector &= ~3;
}
if (len > size) if (len > size)
len = size; len = size;
@ -557,9 +558,10 @@ grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
GRUB_MEMORY_MACHINE_SCRATCH_SEG)) GRUB_MEMORY_MACHINE_SCRATCH_SEG))
return grub_errno; return grub_errno;
grub_memcpy (buf, (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + cdoff), grub_memcpy (buf, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
len << GRUB_DISK_SECTOR_BITS); len << disk->log_sector_size);
buf += len << GRUB_DISK_SECTOR_BITS;
buf += len << disk->log_sector_size;
sector += len; sector += len;
size -= len; size -= len;
} }
@ -580,18 +582,18 @@ grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
{ {
grub_size_t len; grub_size_t len;
len = get_safe_sectors (sector, data->sectors); len = get_safe_sectors (disk, sector);
if (len > size) if (len > size)
len = size; len = size;
grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf, grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf,
len << GRUB_DISK_SECTOR_BITS); len << disk->log_sector_size);
if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len, if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len,
GRUB_MEMORY_MACHINE_SCRATCH_SEG)) GRUB_MEMORY_MACHINE_SCRATCH_SEG))
return grub_errno; return grub_errno;
buf += len << GRUB_DISK_SECTOR_BITS; buf += len << disk->log_sector_size;
sector += len; sector += len;
size -= len; size -= len;
} }

View file

@ -470,15 +470,20 @@ grub_scsi_open (const char *name, grub_disk_t disk,
return err; return err;
} }
/* SCSI blocks can be something else than 512, although GRUB disk->total_sectors = scsi->size;
wants 512 byte blocks. */ if (scsi->blocksize & (scsi->blocksize - 1) || !scsi->blocksize)
disk->total_sectors = ((grub_uint64_t)scsi->size {
* (grub_uint64_t)scsi->blocksize) grub_free (scsi);
>> GRUB_DISK_SECTOR_BITS; return grub_error (GRUB_ERR_IO, "invalid sector size %d",
scsi->blocksize);
}
for (disk->log_sector_size = 0;
(1 << disk->log_sector_size) < scsi->blocksize;
disk->log_sector_size++);
grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n", grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
scsi->size, scsi->blocksize); scsi->size, scsi->blocksize);
grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n", grub_dprintf ("scsi", "Disk total sectors = %llu\n",
(unsigned long long) disk->total_sectors); (unsigned long long) disk->total_sectors);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
@ -508,25 +513,6 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
scsi = disk->data; scsi = disk->data;
/* SCSI sectors are variable in size. GRUB uses 512 byte
sectors. */
if (scsi->blocksize != GRUB_DISK_SECTOR_SIZE)
{
unsigned spb = scsi->blocksize >> GRUB_DISK_SECTOR_BITS;
if (spb == 0 || (scsi->blocksize & (GRUB_DISK_SECTOR_SIZE - 1)) != 0)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"unsupported SCSI block size");
grub_uint32_t sector_mod = 0;
sector = grub_divmod64 (sector, spb, &sector_mod);
if (! (sector_mod == 0 && size % spb == 0))
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"unaligned SCSI read not supported");
size /= spb;
}
/* Depending on the type, select a read function. */ /* Depending on the type, select a read function. */
switch (scsi->devtype) switch (scsi->devtype)
{ {

View file

@ -254,6 +254,7 @@ grub_disk_open (const char *name)
disk = (grub_disk_t) grub_zalloc (sizeof (*disk)); disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
if (! disk) if (! disk)
return 0; return 0;
disk->log_sector_size = GRUB_DISK_SECTOR_BITS;
p = find_part_sep (name); p = find_part_sep (name);
if (p) if (p)
@ -293,6 +294,14 @@ grub_disk_open (const char *name)
grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such disk"); grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such disk");
goto fail; goto fail;
} }
if (disk->log_sector_size > GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
|| disk->log_sector_size < GRUB_DISK_SECTOR_BITS)
{
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"sector sizes of %d bytes aren't supported yet",
(1 << disk->log_sector_size));
goto fail;
}
disk->dev = dev; disk->dev = dev;
@ -384,21 +393,110 @@ grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector,
*sector += start; *sector += start;
} }
if (disk->total_sectors <= *sector if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN
&& ((disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)) <= *sector
|| ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1)
>> GRUB_DISK_SECTOR_BITS) > disk->total_sectors - *sector) >> GRUB_DISK_SECTOR_BITS) > (disk->total_sectors
<< (disk->log_sector_size
- GRUB_DISK_SECTOR_BITS)) - *sector))
return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of disk"); return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of disk");
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
static inline grub_disk_addr_t
transform_sector (grub_disk_t disk, grub_disk_addr_t sector)
{
return sector >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
}
/* Small read (less than cache size and not pass across cache unit boundaries).
sector is already adjusted and is divisible by cache unit size.
*/
static grub_err_t
grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, void *buf)
{
char *data;
char *tmp_buf;
/* Fetch the cache. */
data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector);
if (data)
{
/* Just copy it! */
grub_memcpy (buf, data + offset, size);
grub_disk_cache_unlock (disk->dev->id, disk->id, sector);
return GRUB_ERR_NONE;
}
/* Allocate a temporary buffer. */
tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
if (! tmp_buf)
return grub_errno;
/* Otherwise read data from the disk actually. */
if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN
|| sector + GRUB_DISK_CACHE_SIZE
< (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
{
grub_err_t err;
err = (disk->dev->read) (disk, transform_sector (disk, sector),
1 << (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS
- disk->log_sector_size), tmp_buf);
if (!err)
{
/* Copy it and store it in the disk cache. */
grub_memcpy (buf, tmp_buf + offset, size);
grub_disk_cache_store (disk->dev->id, disk->id,
sector, tmp_buf);
grub_free (tmp_buf);
return GRUB_ERR_NONE;
}
}
grub_errno = GRUB_ERR_NONE;
{
/* Uggh... Failed. Instead, just read necessary data. */
unsigned num;
grub_disk_addr_t aligned_sector;
sector += (offset >> GRUB_DISK_SECTOR_BITS);
offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
aligned_sector = (sector & ~((1 << (disk->log_sector_size
- GRUB_DISK_SECTOR_BITS))
- 1));
offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
num = ((size + offset + (1 << (disk->log_sector_size))
- 1) >> (disk->log_sector_size));
tmp_buf = grub_malloc (num << disk->log_sector_size);
if (!tmp_buf)
return grub_errno;
if ((disk->dev->read) (disk, transform_sector (disk, aligned_sector),
num, tmp_buf))
{
grub_error_push ();
grub_dprintf ("disk", "%s read failed\n", disk->name);
grub_error_pop ();
return grub_errno;
}
grub_memcpy (buf, tmp_buf + offset, size);
return GRUB_ERR_NONE;
}
}
/* Read data from the disk. */ /* Read data from the disk. */
grub_err_t grub_err_t
grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, void *buf) grub_off_t offset, grub_size_t size, void *buf)
{ {
char *tmp_buf; grub_off_t real_offset;
unsigned real_offset; grub_disk_addr_t real_sector;
grub_size_t real_size;
/* First of all, check if the region is within the disk. */ /* First of all, check if the region is within the disk. */
if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE) if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
@ -410,127 +508,119 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
return grub_errno; return grub_errno;
} }
real_sector = sector;
real_offset = offset; real_offset = offset;
real_size = size;
/* Allocate a temporary buffer. */ /* First read until first cache boundary. */
tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
if (! tmp_buf)
return grub_errno;
/* Until SIZE is zero... */
while (size)
{ {
char *data;
grub_disk_addr_t start_sector; grub_disk_addr_t start_sector;
grub_size_t len;
grub_size_t pos; grub_size_t pos;
grub_err_t err;
grub_size_t len;
/* For reading bulk data. */
start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1); start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1);
pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS; pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS) len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
- pos - real_offset); - pos - offset);
if (len > size) if (len > size)
len = size; len = size;
err = grub_disk_read_small (disk, start_sector,
offset + pos, len, buf);
if (err)
return err;
buf = (char *) buf + len;
size -= len;
offset += len;
sector += (offset >> GRUB_DISK_SECTOR_BITS);
offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
}
/* Until SIZE is zero... */
while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS))
{
char *data = NULL;
grub_disk_addr_t agglomerate;
grub_err_t err;
/* agglomerate read until we find a first cached entry. */
for (agglomerate = 0; agglomerate
< (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS));
agglomerate++)
{
data = grub_disk_cache_fetch (disk->dev->id, disk->id,
sector + (agglomerate
<< GRUB_DISK_CACHE_BITS));
if (data)
break;
}
if (agglomerate)
{
grub_disk_addr_t i;
err = (disk->dev->read) (disk, transform_sector (disk, sector),
agglomerate << (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS
- disk->log_sector_size),
buf);
if (err)
return err;
for (i = 0; i < agglomerate; i ++)
grub_disk_cache_store (disk->dev->id, disk->id,
sector + (i << GRUB_DISK_CACHE_BITS),
(char *) buf
+ (i << (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS)));
sector += agglomerate << GRUB_DISK_CACHE_BITS;
size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
buf = (char *) buf
+ (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
}
/* Fetch the cache. */
data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector);
if (data) if (data)
{ {
/* Just copy it! */ grub_memcpy (buf, data, GRUB_DISK_CACHE_SIZE);
grub_memcpy (buf, data + pos + real_offset, len); sector += GRUB_DISK_CACHE_SIZE;
grub_disk_cache_unlock (disk->dev->id, disk->id, start_sector); buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
grub_disk_cache_unlock (disk->dev->id, disk->id,
sector + (agglomerate
<< GRUB_DISK_CACHE_BITS));
} }
else
{
/* Otherwise read data from the disk actually. */
if (start_sector + GRUB_DISK_CACHE_SIZE > disk->total_sectors
|| (disk->dev->read) (disk, start_sector,
GRUB_DISK_CACHE_SIZE, tmp_buf)
!= GRUB_ERR_NONE)
{
/* Uggh... Failed. Instead, just read necessary data. */
unsigned num;
char *p;
grub_errno = GRUB_ERR_NONE;
num = ((size + real_offset + GRUB_DISK_SECTOR_SIZE - 1)
>> GRUB_DISK_SECTOR_BITS);
p = grub_realloc (tmp_buf, num << GRUB_DISK_SECTOR_BITS);
if (!p)
goto finish;
tmp_buf = p;
if ((disk->dev->read) (disk, sector, num, tmp_buf))
{
grub_error_push ();
grub_dprintf ("disk", "%s read failed\n", disk->name);
grub_error_pop ();
goto finish;
} }
grub_memcpy (buf, tmp_buf + real_offset, size); /* And now read the last part. */
if (size)
/* Call the read hook, if any. */
if (disk->read_hook)
while (size)
{ {
grub_size_t to_read = (size > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : size; grub_err_t err;
(disk->read_hook) (sector, real_offset, err = grub_disk_read_small (disk, sector, 0, size, buf);
to_read); if (err)
if (grub_errno != GRUB_ERR_NONE) return err;
goto finish;
sector++;
size -= to_read - real_offset;
real_offset = 0;
}
/* This must be the end. */
goto finish;
}
/* Copy it and store it in the disk cache. */
grub_memcpy (buf, tmp_buf + pos + real_offset, len);
grub_disk_cache_store (disk->dev->id, disk->id,
start_sector, tmp_buf);
} }
/* Call the read hook, if any. */ /* Call the read hook, if any. */
if (disk->read_hook) if (disk->read_hook)
{ {
grub_disk_addr_t s = sector; grub_disk_addr_t s = real_sector;
grub_size_t l = len; grub_size_t l = real_size;
grub_off_t o = real_offset;
while (l) while (l)
{ {
(disk->read_hook) (s, real_offset, (disk->read_hook) (s, o,
((l > GRUB_DISK_SECTOR_SIZE) ((l > GRUB_DISK_SECTOR_SIZE)
? GRUB_DISK_SECTOR_SIZE ? GRUB_DISK_SECTOR_SIZE
: l)); : l));
if (l < GRUB_DISK_SECTOR_SIZE - real_offset)
break;
s++; s++;
l -= GRUB_DISK_SECTOR_SIZE - real_offset; l -= GRUB_DISK_SECTOR_SIZE - o;
real_offset = 0; o = 0;
} }
} }
sector = start_sector + GRUB_DISK_CACHE_SIZE;
buf = (char *) buf + len;
size -= len;
real_offset = 0;
}
finish:
grub_free (tmp_buf);
return grub_errno; return grub_errno;
} }
@ -539,25 +629,31 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, const void *buf) grub_off_t offset, grub_size_t size, const void *buf)
{ {
unsigned real_offset; unsigned real_offset;
grub_disk_addr_t aligned_sector;
grub_dprintf ("disk", "Writing `%s'...\n", disk->name); grub_dprintf ("disk", "Writing `%s'...\n", disk->name);
if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE) if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
return -1; return -1;
real_offset = offset; aligned_sector = (sector & ~((1 << (disk->log_sector_size
- GRUB_DISK_SECTOR_BITS)) - 1));
real_offset = offset + ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
sector = aligned_sector;
while (size) while (size)
{ {
if (real_offset != 0 || (size < GRUB_DISK_SECTOR_SIZE && size != 0)) if (real_offset != 0 || (size < (1U << disk->log_sector_size)
&& size != 0))
{ {
char tmp_buf[GRUB_DISK_SECTOR_SIZE]; char tmp_buf[1 << disk->log_sector_size];
grub_size_t len; grub_size_t len;
grub_partition_t part; grub_partition_t part;
part = disk->partition; part = disk->partition;
disk->partition = 0; disk->partition = 0;
if (grub_disk_read (disk, sector, 0, GRUB_DISK_SECTOR_SIZE, tmp_buf) if (grub_disk_read (disk, sector,
0, (1 << disk->log_sector_size), tmp_buf)
!= GRUB_ERR_NONE) != GRUB_ERR_NONE)
{ {
disk->partition = part; disk->partition = part;
@ -565,7 +661,7 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
} }
disk->partition = part; disk->partition = part;
len = GRUB_DISK_SECTOR_SIZE - real_offset; len = (1 << disk->log_sector_size) - real_offset;
if (len > size) if (len > size)
len = size; len = size;
@ -576,7 +672,7 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
if ((disk->dev->write) (disk, sector, 1, tmp_buf) != GRUB_ERR_NONE) if ((disk->dev->write) (disk, sector, 1, tmp_buf) != GRUB_ERR_NONE)
goto finish; goto finish;
sector++; sector += (1 << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
buf = (char *) buf + len; buf = (char *) buf + len;
size -= len; size -= len;
real_offset = 0; real_offset = 0;
@ -586,8 +682,8 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t len; grub_size_t len;
grub_size_t n; grub_size_t n;
len = size & ~(GRUB_DISK_SECTOR_SIZE - 1); len = size & ~((1 << disk->log_sector_size) - 1);
n = size >> GRUB_DISK_SECTOR_BITS; n = size >> disk->log_sector_size;
if ((disk->dev->write) (disk, sector, n, buf) != GRUB_ERR_NONE) if ((disk->dev->write) (disk, sector, n, buf) != GRUB_ERR_NONE)
goto finish; goto finish;
@ -610,6 +706,8 @@ grub_disk_get_size (grub_disk_t disk)
{ {
if (disk->partition) if (disk->partition)
return grub_partition_get_len (disk->partition); return grub_partition_get_len (disk->partition);
else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN)
return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
else else
return disk->total_sectors; return GRUB_DISK_SIZE_UNKNOWN;
} }

View file

@ -43,6 +43,7 @@
#ifdef __linux__ #ifdef __linux__
# include <sys/ioctl.h> /* ioctl */ # include <sys/ioctl.h> /* ioctl */
# include <sys/mount.h>
# if !defined(__GLIBC__) || \ # if !defined(__GLIBC__) || \
((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
/* Maybe libc doesn't have large file support. */ /* Maybe libc doesn't have large file support. */
@ -269,6 +270,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk,
# else # else
unsigned long long nr; unsigned long long nr;
# endif # endif
int sector_size;
int fd; int fd;
fd = open (map[drive].device, O_RDONLY); fd = open (map[drive].device, O_RDONLY);
@ -301,16 +303,28 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk,
goto fail; goto fail;
} }
if (ioctl (fd, BLKSSZGET, &sector_size))
{
close (fd); close (fd);
goto fail;
}
close (fd);
if (sector_size & (sector_size - 1) || !sector_size)
goto fail;
for (disk->log_sector_size = 0;
(1 << disk->log_sector_size) < sector_size;
disk->log_sector_size++);
# if defined (__APPLE__) # if defined (__APPLE__)
disk->total_sectors = nr; disk->total_sectors = nr;
# elif defined(__NetBSD__) # elif defined(__NetBSD__)
disk->total_sectors = label.d_secperunit; disk->total_sectors = label.d_secperunit;
# else # else
disk->total_sectors = nr / 512; disk->total_sectors = nr >> disk->log_sector_size;
if (nr % 512) if (nr & ((1 << disk->log_sector_size) - 1))
grub_util_error ("unaligned device size"); grub_util_error ("unaligned device size");
# endif # endif
@ -327,7 +341,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk,
if (stat (map[drive].device, &st) < 0) if (stat (map[drive].device, &st) < 0)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device); return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device);
disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS; disk->total_sectors = st.st_size >> disk->log_sector_size;
grub_util_info ("the size of %s is %lu", name, disk->total_sectors); grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
@ -635,7 +649,7 @@ linux_find_partition (char *dev, unsigned long sector)
((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
/* Maybe libc doesn't have large file support. */ /* Maybe libc doesn't have large file support. */
grub_err_t grub_err_t
grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector) grub_util_fd_seek (int fd, const char *name, grub_uint64_t off)
{ {
loff_t offset, result; loff_t offset, result;
static int _llseek (uint filedes, ulong hi, ulong lo, static int _llseek (uint filedes, ulong hi, ulong lo,
@ -643,7 +657,7 @@ grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector)
_syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
loff_t *, res, uint, wh); loff_t *, res, uint, wh);
offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS; offset = (loff_t) off;
if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
{ {
return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name); return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name);
@ -652,9 +666,9 @@ grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector)
} }
#else #else
grub_err_t grub_err_t
grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector) grub_util_fd_seek (int fd, const char *name, grub_uint64_t off)
{ {
off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS; off_t offset = (off_t) off;
if (lseek (fd, offset, SEEK_SET) != offset) if (lseek (fd, offset, SEEK_SET) != offset)
return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name); return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name);
@ -812,7 +826,8 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
configure_device_driver (fd); configure_device_driver (fd);
#endif /* defined(__NetBSD__) */ #endif /* defined(__NetBSD__) */
if (grub_util_fd_sector_seek (fd, map[disk->id].device, sector)) if (grub_util_fd_seek (fd, map[disk->id].device,
sector << disk->log_sector_size))
{ {
close (fd); close (fd);
return -1; return -1;
@ -907,20 +922,20 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
sectors that are read together with the MBR in one read. It sectors that are read together with the MBR in one read. It
should only remap the MBR, so we split the read in two should only remap the MBR, so we split the read in two
parts. -jochen */ parts. -jochen */
if (grub_util_fd_read (fd, buf, GRUB_DISK_SECTOR_SIZE) if (grub_util_fd_read (fd, buf, (1 << disk->log_sector_size))
!= GRUB_DISK_SECTOR_SIZE) != (1 << disk->log_sector_size))
{ {
grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device); grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
return grub_errno; return grub_errno;
} }
buf += GRUB_DISK_SECTOR_SIZE; buf += (1 << disk->log_sector_size);
size--; size--;
} }
#endif /* __linux__ */ #endif /* __linux__ */
if (grub_util_fd_read (fd, buf, size << GRUB_DISK_SECTOR_BITS) if (grub_util_fd_read (fd, buf, size << disk->log_sector_size)
!= (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) != (ssize_t) (size << disk->log_sector_size))
grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device); grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
return grub_errno; return grub_errno;
@ -953,8 +968,8 @@ grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
if (fd < 0) if (fd < 0)
return grub_errno; return grub_errno;
if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS) if (nwrite (fd, buf, size << disk->log_sector_size)
!= (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) != (ssize_t) (size << disk->log_sector_size))
grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device); grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
return grub_errno; return grub_errno;

View file

@ -92,8 +92,11 @@ grub_partition_msdos_iterate (grub_disk_t disk,
{ {
e = mbr.entries + p.index; e = mbr.entries + p.index;
p.start = p.offset + grub_le_to_cpu32 (e->start) - delta; p.start = p.offset
p.len = grub_le_to_cpu32 (e->length); + (grub_le_to_cpu32 (e->start)
<< (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)) - delta;
p.len = grub_le_to_cpu32 (e->length)
<< (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
p.msdostype = e->type; p.msdostype = e->type;
grub_dprintf ("partition", grub_dprintf ("partition",
@ -128,7 +131,9 @@ grub_partition_msdos_iterate (grub_disk_t disk,
if (grub_msdos_partition_is_extended (e->type)) if (grub_msdos_partition_is_extended (e->type))
{ {
p.offset = ext_offset + grub_le_to_cpu32 (e->start); p.offset = ext_offset
+ (grub_le_to_cpu32 (e->start)
<< (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
if (! ext_offset) if (! ext_offset)
ext_offset = p.offset; ext_offset = p.offset;
@ -206,8 +211,11 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
e = mbr.entries + i; e = mbr.entries + i;
if (!grub_msdos_partition_is_empty (e->type) if (!grub_msdos_partition_is_empty (e->type)
&& end > offset + grub_le_to_cpu32 (e->start)) && end > offset
end = offset + grub_le_to_cpu32 (e->start); + (grub_le_to_cpu32 (e->start)
<< (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
end = offset + (grub_le_to_cpu32 (e->start)
<< (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
/* If this is a GPT partition, this MBR is just a dummy. */ /* If this is a GPT partition, this MBR is just a dummy. */
if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0) if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0)
@ -221,7 +229,9 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
if (grub_msdos_partition_is_extended (e->type)) if (grub_msdos_partition_is_extended (e->type))
{ {
offset = ext_offset + grub_le_to_cpu32 (e->start); offset = ext_offset
+ (grub_le_to_cpu32 (e->start)
<< (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
if (! ext_offset) if (! ext_offset)
ext_offset = offset; ext_offset = offset;

View file

@ -111,6 +111,9 @@ struct grub_disk
/* The total number of sectors. */ /* The total number of sectors. */
grub_uint64_t total_sectors; grub_uint64_t total_sectors;
/* Logarithm of sector size. */
unsigned int log_sector_size;
/* The id used by the disk cache manager. */ /* The id used by the disk cache manager. */
unsigned long id; unsigned long id;
@ -143,9 +146,10 @@ typedef struct grub_disk_memberlist *grub_disk_memberlist_t;
/* The maximum number of disk caches. */ /* The maximum number of disk caches. */
#define GRUB_DISK_CACHE_NUM 1021 #define GRUB_DISK_CACHE_NUM 1021
/* The size of a disk cache in sector units. */ /* The size of a disk cache in 512B units. Must be at least as big as the
#define GRUB_DISK_CACHE_SIZE 8 largest supported sector size, currently 16K. */
#define GRUB_DISK_CACHE_BITS 3 #define GRUB_DISK_CACHE_BITS 6
#define GRUB_DISK_CACHE_SIZE (1 << GRUB_DISK_CACHE_BITS)
/* Return value of grub_disk_get_size() in case disk size is unknown. */ /* Return value of grub_disk_get_size() in case disk size is unknown. */
#define GRUB_DISK_SIZE_UNKNOWN 0xffffffffffffffffULL #define GRUB_DISK_SIZE_UNKNOWN 0xffffffffffffffffULL

View file

@ -32,7 +32,7 @@ int grub_util_biosdisk_is_floppy (grub_disk_t disk);
grub_err_t grub_util_biosdisk_flush (struct grub_disk *disk); grub_err_t grub_util_biosdisk_flush (struct grub_disk *disk);
void grub_util_pull_device (const char *osname); void grub_util_pull_device (const char *osname);
grub_err_t grub_err_t
grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector); grub_util_fd_seek (int fd, const char *name, grub_uint64_t sector);
ssize_t grub_util_fd_read (int fd, char *buf, size_t len); ssize_t grub_util_fd_read (int fd, char *buf, size_t len);
grub_err_t grub_err_t
grub_luks_cheat_mount (const char *sourcedev, const char *cheat); grub_luks_cheat_mount (const char *sourcedev, const char *cheat);