From 442b86de324626399d15755258cbabb849db02b7 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sun, 27 Oct 2013 15:44:55 +0100 Subject: [PATCH] Move grub_disk_write out of kernel into disk.mod. --- ChangeLog | 4 + Makefile.util.def | 1 + grub-core/Makefile.core.def | 5 + grub-core/disk/cryptodisk.c | 14 ++- grub-core/kern/disk.c | 187 ++--------------------------------- grub-core/kern/disk_common.c | 55 +++++++++++ grub-core/lib/disk.c | 154 +++++++++++++++++++++++++++++ include/grub/disk.h | 28 +++++- 8 files changed, 259 insertions(+), 189 deletions(-) create mode 100644 grub-core/kern/disk_common.c create mode 100644 grub-core/lib/disk.c diff --git a/ChangeLog b/ChangeLog index 7ce7c3a0c..46e3cce73 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-10-27 Vladimir Serbinenko + + Move grub_disk_write out of kernel into disk.mod. + 2013-10-27 Vladimir Serbinenko * grub-core/kern/misc.c (grub_vsnprintf_real): Unify int and wchar diff --git a/Makefile.util.def b/Makefile.util.def index 193bc4531..2c38b53dd 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -9,6 +9,7 @@ library = { common = grub-core/kern/command.c; common = grub-core/kern/device.c; common = grub-core/kern/disk.c; + common = grub-core/lib/disk.c; common = util/getroot.c; common = grub-core/osdep/unix/getroot.c; common = grub-core/osdep/getroot.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index abd54bae4..2fb1cc944 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -455,6 +455,11 @@ image = { enable = mips_loongson; }; +module = { + name = disk; + common = lib/disk.c; +}; + module = { name = trig; common_nodist = trigtables.c; diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 5856dfffa..673101001 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -631,10 +631,16 @@ grub_cryptodisk_write (grub_disk_t disk, grub_disk_addr_t sector, return grub_crypto_gcry_error (gcry_err); } - err = grub_disk_write (dev->source_disk, - (sector << (disk->log_sector_size - - GRUB_DISK_SECTOR_BITS)) + dev->offset, - 0, size << disk->log_sector_size, tmp); + /* Since ->write was called so disk.mod is loaded but be paranoid */ + + if (grub_disk_write_weak) + err = grub_disk_write_weak (dev->source_disk, + (sector << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) + + dev->offset, + 0, size << disk->log_sector_size, tmp); + else + err = grub_error (GRUB_ERR_BUG, "disk.mod not loaded"); grub_free (tmp); return err; } diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index b0b271512..1d0a11cb2 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -31,18 +31,7 @@ /* The last time the disk was used. */ static grub_uint64_t grub_last_time = 0; - -/* Disk cache. */ -struct grub_disk_cache -{ - enum grub_disk_dev_id dev_id; - unsigned long disk_id; - grub_disk_addr_t sector; - char *data; - int lock; -}; - -static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM]; +struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM]; void (*grub_disk_firmware_fini) (void); int grub_disk_firmware_is_tainted; @@ -59,35 +48,12 @@ grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses) } #endif -static unsigned -grub_disk_cache_get_index (unsigned long dev_id, unsigned long disk_id, - grub_disk_addr_t sector) -{ - return ((dev_id * 524287UL + disk_id * 2606459UL - + ((unsigned) (sector >> GRUB_DISK_CACHE_BITS))) - % GRUB_DISK_CACHE_NUM); -} - -static void -grub_disk_cache_invalidate (unsigned long dev_id, unsigned long disk_id, - grub_disk_addr_t sector) -{ - unsigned cache_index; - struct grub_disk_cache *cache; - - sector &= ~(GRUB_DISK_CACHE_SIZE - 1); - cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector); - cache = grub_disk_cache_table + cache_index; - - if (cache->dev_id == dev_id && cache->disk_id == disk_id - && cache->sector == sector && cache->data) - { - cache->lock = 1; - grub_free (cache->data); - cache->data = 0; - cache->lock = 0; - } -} +grub_err_t (*grub_disk_write_weak) (grub_disk_t disk, + grub_disk_addr_t sector, + grub_off_t offset, + grub_size_t size, + const void *buf); +#include "disk_common.c" void grub_disk_cache_invalidate_all (void) @@ -341,53 +307,6 @@ grub_disk_close (grub_disk_t disk) grub_free (disk); } -/* This function performs three tasks: - - Make sectors disk relative from partition relative. - - Normalize offset to be less than the sector size. - - Verify that the range is inside the partition. */ -static grub_err_t -grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector, - grub_off_t *offset, grub_size_t size) -{ - grub_partition_t part; - *sector += *offset >> GRUB_DISK_SECTOR_BITS; - *offset &= GRUB_DISK_SECTOR_SIZE - 1; - - for (part = disk->partition; part; part = part->parent) - { - grub_disk_addr_t start; - grub_uint64_t len; - - start = part->start; - len = part->len; - - if (*sector >= len - || len - *sector < ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) - >> GRUB_DISK_SECTOR_BITS)) - return grub_error (GRUB_ERR_OUT_OF_RANGE, - N_("attempt to read or write outside of partition")); - - *sector += start; - } - - 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) - >> GRUB_DISK_SECTOR_BITS) > (disk->total_sectors - << (disk->log_sector_size - - GRUB_DISK_SECTOR_BITS)) - *sector)) - return grub_error (GRUB_ERR_OUT_OF_RANGE, - N_("attempt to read or write outside of disk `%s'"), disk->name); - - 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. */ @@ -613,98 +532,6 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, return grub_errno; } -grub_err_t -grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector, - grub_off_t offset, grub_size_t size, const void *buf) -{ - unsigned real_offset; - grub_disk_addr_t aligned_sector; - - grub_dprintf ("disk", "Writing `%s'...\n", disk->name); - - if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) - return -1; - - 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) - { - if (real_offset != 0 || (size < (1U << disk->log_sector_size) - && size != 0)) - { - char *tmp_buf; - grub_size_t len; - grub_partition_t part; - - tmp_buf = grub_malloc (1 << disk->log_sector_size); - if (!tmp_buf) - return grub_errno; - - part = disk->partition; - disk->partition = 0; - if (grub_disk_read (disk, sector, - 0, (1 << disk->log_sector_size), tmp_buf) - != GRUB_ERR_NONE) - { - disk->partition = part; - grub_free (tmp_buf); - goto finish; - } - disk->partition = part; - - len = (1 << disk->log_sector_size) - real_offset; - if (len > size) - len = size; - - grub_memcpy (tmp_buf + real_offset, buf, len); - - grub_disk_cache_invalidate (disk->dev->id, disk->id, sector); - - if ((disk->dev->write) (disk, transform_sector (disk, sector), - 1, tmp_buf) != GRUB_ERR_NONE) - { - grub_free (tmp_buf); - goto finish; - } - - grub_free (tmp_buf); - - sector += (1 << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); - buf = (const char *) buf + len; - size -= len; - real_offset = 0; - } - else - { - grub_size_t len; - grub_size_t n; - - len = size & ~((1 << disk->log_sector_size) - 1); - n = size >> disk->log_sector_size; - - if ((disk->dev->write) (disk, transform_sector (disk, sector), - n, buf) != GRUB_ERR_NONE) - goto finish; - - while (n--) - { - grub_disk_cache_invalidate (disk->dev->id, disk->id, sector); - sector += (1 << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); - } - - buf = (const char *) buf + len; - size -= len; - } - } - - finish: - - return grub_errno; -} - grub_uint64_t grub_disk_get_size (grub_disk_t disk) { diff --git a/grub-core/kern/disk_common.c b/grub-core/kern/disk_common.c new file mode 100644 index 000000000..fb19778ae --- /dev/null +++ b/grub-core/kern/disk_common.c @@ -0,0 +1,55 @@ +/* This function performs three tasks: + - Make sectors disk relative from partition relative. + - Normalize offset to be less than the sector size. + - Verify that the range is inside the partition. */ +static grub_err_t +grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector, + grub_off_t *offset, grub_size_t size) +{ + grub_partition_t part; + *sector += *offset >> GRUB_DISK_SECTOR_BITS; + *offset &= GRUB_DISK_SECTOR_SIZE - 1; + + for (part = disk->partition; part; part = part->parent) + { + grub_disk_addr_t start; + grub_uint64_t len; + + start = part->start; + len = part->len; + + if (*sector >= len + || len - *sector < ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) + >> GRUB_DISK_SECTOR_BITS)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read or write outside of partition")); + + *sector += start; + } + + 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) + >> GRUB_DISK_SECTOR_BITS) > (disk->total_sectors + << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) - *sector)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read or write outside of disk `%s'"), disk->name); + + 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); +} + +static unsigned +grub_disk_cache_get_index (unsigned long dev_id, unsigned long disk_id, + grub_disk_addr_t sector) +{ + return ((dev_id * 524287UL + disk_id * 2606459UL + + ((unsigned) (sector >> GRUB_DISK_CACHE_BITS))) + % GRUB_DISK_CACHE_NUM); +} diff --git a/grub-core/lib/disk.c b/grub-core/lib/disk.c new file mode 100644 index 000000000..9527e2956 --- /dev/null +++ b/grub-core/lib/disk.c @@ -0,0 +1,154 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#include "../kern/disk_common.c" + +static void +grub_disk_cache_invalidate (unsigned long dev_id, unsigned long disk_id, + grub_disk_addr_t sector) +{ + unsigned cache_index; + struct grub_disk_cache *cache; + + sector &= ~(GRUB_DISK_CACHE_SIZE - 1); + cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector); + cache = grub_disk_cache_table + cache_index; + + if (cache->dev_id == dev_id && cache->disk_id == disk_id + && cache->sector == sector && cache->data) + { + cache->lock = 1; + grub_free (cache->data); + cache->data = 0; + cache->lock = 0; + } +} + +grub_err_t +grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_off_t offset, grub_size_t size, const void *buf) +{ + unsigned real_offset; + grub_disk_addr_t aligned_sector; + + grub_dprintf ("disk", "Writing `%s'...\n", disk->name); + + if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) + return -1; + + 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) + { + if (real_offset != 0 || (size < (1U << disk->log_sector_size) + && size != 0)) + { + char *tmp_buf; + grub_size_t len; + grub_partition_t part; + + tmp_buf = grub_malloc (1 << disk->log_sector_size); + if (!tmp_buf) + return grub_errno; + + part = disk->partition; + disk->partition = 0; + if (grub_disk_read (disk, sector, + 0, (1 << disk->log_sector_size), tmp_buf) + != GRUB_ERR_NONE) + { + disk->partition = part; + grub_free (tmp_buf); + goto finish; + } + disk->partition = part; + + len = (1 << disk->log_sector_size) - real_offset; + if (len > size) + len = size; + + grub_memcpy (tmp_buf + real_offset, buf, len); + + grub_disk_cache_invalidate (disk->dev->id, disk->id, sector); + + if ((disk->dev->write) (disk, transform_sector (disk, sector), + 1, tmp_buf) != GRUB_ERR_NONE) + { + grub_free (tmp_buf); + goto finish; + } + + grub_free (tmp_buf); + + sector += (1 << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); + buf = (const char *) buf + len; + size -= len; + real_offset = 0; + } + else + { + grub_size_t len; + grub_size_t n; + + len = size & ~((1 << disk->log_sector_size) - 1); + n = size >> disk->log_sector_size; + + if ((disk->dev->write) (disk, transform_sector (disk, sector), + n, buf) != GRUB_ERR_NONE) + goto finish; + + while (n--) + { + grub_disk_cache_invalidate (disk->dev->id, disk->id, sector); + sector += (1 << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); + } + + buf = (const char *) buf + len; + size -= len; + } + } + + finish: + + return grub_errno; +} + +GRUB_MOD_INIT(disk) +{ + grub_disk_write_weak = grub_disk_write; +} + +GRUB_MOD_FINI(disk) +{ + grub_disk_write_weak = NULL; +} diff --git a/include/grub/disk.h b/include/grub/disk.h index bf21473ca..42d170e31 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -193,11 +193,17 @@ grub_err_t EXPORT_FUNC(grub_disk_read) (grub_disk_t disk, grub_off_t offset, grub_size_t size, void *buf); -grub_err_t EXPORT_FUNC(grub_disk_write) (grub_disk_t disk, - grub_disk_addr_t sector, - grub_off_t offset, - grub_size_t size, - const void *buf); +grub_err_t grub_disk_write (grub_disk_t disk, + grub_disk_addr_t sector, + grub_off_t offset, + grub_size_t size, + const void *buf); +extern grub_err_t (*EXPORT_VAR(grub_disk_write_weak)) (grub_disk_t disk, + grub_disk_addr_t sector, + grub_off_t offset, + grub_size_t size, + const void *buf); + grub_uint64_t EXPORT_FUNC(grub_disk_get_size) (grub_disk_t disk); @@ -221,6 +227,18 @@ grub_stop_disk_firmware (void) } } +/* Disk cache. */ +struct grub_disk_cache +{ + enum grub_disk_dev_id dev_id; + unsigned long disk_id; + grub_disk_addr_t sector; + char *data; + int lock; +}; + +extern struct grub_disk_cache EXPORT_VAR(grub_disk_cache_table)[GRUB_DISK_CACHE_NUM]; + #if defined (GRUB_UTIL) void grub_lvm_init (void); void grub_ldm_init (void);