diff options
Diffstat (limited to 'common/block.c')
-rw-r--r-- | common/block.c | 80 |
1 files changed, 75 insertions, 5 deletions
diff --git a/common/block.c b/common/block.c index 19bb81df2c..c7ca4fb403 100644 --- a/common/block.c +++ b/common/block.c @@ -6,12 +6,12 @@ */ #include <common.h> #include <block.h> +#include <disks.h> #include <malloc.h> #include <linux/err.h> #include <linux/list.h> #include <dma.h> - -#define BLOCKSIZE(blk) (1 << blk->blockbits) +#include <file-list.h> LIST_HEAD(block_device_list); @@ -282,6 +282,17 @@ static ssize_t block_op_write(struct cdev *cdev, const void *buf, size_t count, blkcnt_t blocks; int ret; + /* + * When the offset that is written to is within the first two + * LBAs then the partition table has changed, reparse the partition + * table at close time in this case. A GPT covers more space than + * only the first two LBAs, but a CRC of the remaining pieces is + * written to LBA1, so LBA1 must change as well when the partioning + * is changed. + */ + if (offset < 2 * SECTOR_SIZE) + blk->need_reparse = true; + if (offset & mask) { size_t now = BLOCKSIZE(blk) - (offset & mask); void *iobuf = block_get(blk, block); @@ -339,7 +350,19 @@ static int block_op_flush(struct cdev *cdev) return writebuffer_flush(blk); } -static int block_op_close(struct cdev *cdev) __alias(block_op_flush); +static int block_op_close(struct cdev *cdev) +{ + struct block_device *blk = cdev->priv; + + block_op_flush(cdev); + + if (blk->need_reparse) { + reparse_partition_table(blk); + blk->need_reparse = false; + } + + return 0; +} static int block_op_discard_range(struct cdev *cdev, loff_t count, loff_t offset) { @@ -361,12 +384,12 @@ static struct cdev_operations block_ops = { .discard_range = block_op_discard_range, }; -struct block_device *cdev_get_block_device(struct cdev *cdev) +struct block_device *cdev_get_block_device(const struct cdev *cdev) { if (!cdev || cdev->ops != &block_ops) return NULL; - return container_of(cdev, struct block_device, cdev); + return cdev->priv; } int blockdevice_register(struct block_device *blk) @@ -388,6 +411,11 @@ int blockdevice_register(struct block_device *blk) dev_dbg(blk->dev, "rdbufsize: %d blockbits: %d blkmask: 0x%08x\n", blk->rdbufsize, blk->blockbits, blk->blkmask); + if (!blk->rdbufsize) { + pr_warn("block size of %u not supported\n", BLOCKSIZE(blk)); + return -ENOSYS; + } + for (i = 0; i < 8; i++) { struct chunk *chunk = xzalloc(sizeof(*chunk)); chunk->data = dma_alloc(BUFSIZE); @@ -403,6 +431,9 @@ int blockdevice_register(struct block_device *blk) cdev_create_default_automount(&blk->cdev); + /* Lack of partition table is unusual, but not a failure */ + (void)parse_partition_table(blk); + return 0; } @@ -449,3 +480,42 @@ int block_write(struct block_device *blk, void *buf, sector_t block, blkcnt_t nu return ret < 0 ? ret : 0; } + +unsigned file_list_add_blockdevs(struct file_list *files) +{ + struct block_device *blk; + unsigned count = 0; + int err; + + list_for_each_entry(blk, &block_device_list, list) { + err = file_list_add_cdev_entry(files, &blk->cdev, 0); + if (!err) + count++; + } + + return count; +} + +const char *blk_type_str(enum blk_type type) +{ + switch (type) { + case BLK_TYPE_UNSPEC: + return "unspecified"; + case BLK_TYPE_SD: + return "SD"; + case BLK_TYPE_MMC: + return "MMC"; + case BLK_TYPE_VIRTUAL: + return "virtual"; + case BLK_TYPE_IDE: + return "IDE"; + case BLK_TYPE_AHCI: + return "AHCI"; + case BLK_TYPE_USB: + return "USB"; + case BLK_TYPE_NVME: + return "NVMe"; + default: + return "unknown"; + } +} |