1 /* 2 * Copyright (C) 2013 Henrik Nordstrom <henrik@henriknordstrom.net> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <blk.h> 9 #include <dm.h> 10 #include <fdtdec.h> 11 #include <part.h> 12 #include <os.h> 13 #include <malloc.h> 14 #include <sandboxblockdev.h> 15 #include <asm/errno.h> 16 #include <dm/device-internal.h> 17 18 DECLARE_GLOBAL_DATA_PTR; 19 20 static unsigned long host_block_read(struct udevice *dev, 21 unsigned long start, lbaint_t blkcnt, 22 void *buffer) 23 { 24 struct host_block_dev *host_dev = dev_get_priv(dev); 25 struct blk_desc *block_dev = dev_get_uclass_platdata(dev); 26 27 if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) == 28 -1) { 29 printf("ERROR: Invalid block %lx\n", start); 30 return -1; 31 } 32 ssize_t len = os_read(host_dev->fd, buffer, blkcnt * block_dev->blksz); 33 if (len >= 0) 34 return len / block_dev->blksz; 35 return -1; 36 } 37 38 static unsigned long host_block_write(struct udevice *dev, 39 unsigned long start, lbaint_t blkcnt, 40 const void *buffer) 41 { 42 struct host_block_dev *host_dev = dev_get_priv(dev); 43 struct blk_desc *block_dev = dev_get_uclass_platdata(dev); 44 45 if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) == 46 -1) { 47 printf("ERROR: Invalid block %lx\n", start); 48 return -1; 49 } 50 ssize_t len = os_write(host_dev->fd, buffer, blkcnt * block_dev->blksz); 51 if (len >= 0) 52 return len / block_dev->blksz; 53 return -1; 54 } 55 56 int host_dev_bind(int devnum, char *filename) 57 { 58 struct host_block_dev *host_dev; 59 struct udevice *dev; 60 char dev_name[20], *str, *fname; 61 int ret, fd; 62 63 /* Remove and unbind the old device, if any */ 64 ret = blk_get_device(IF_TYPE_HOST, devnum, &dev); 65 if (ret == 0) { 66 ret = device_remove(dev); 67 if (ret) 68 return ret; 69 ret = device_unbind(dev); 70 if (ret) 71 return ret; 72 } else if (ret != -ENODEV) { 73 return ret; 74 } 75 76 if (!filename) 77 return 0; 78 79 snprintf(dev_name, sizeof(dev_name), "host%d", devnum); 80 str = strdup(dev_name); 81 if (!str) 82 return -ENOMEM; 83 fname = strdup(filename); 84 if (!fname) { 85 free(str); 86 return -ENOMEM; 87 } 88 89 fd = os_open(filename, OS_O_RDWR); 90 if (fd == -1) { 91 printf("Failed to access host backing file '%s'\n", filename); 92 ret = -ENOENT; 93 goto err; 94 } 95 ret = blk_create_device(gd->dm_root, "sandbox_host_blk", str, 96 IF_TYPE_HOST, devnum, 512, 97 os_lseek(fd, 0, OS_SEEK_END), &dev); 98 if (ret) 99 goto err_file; 100 ret = device_probe(dev); 101 if (ret) { 102 device_unbind(dev); 103 goto err_file; 104 } 105 106 host_dev = dev_get_priv(dev); 107 host_dev->fd = fd; 108 host_dev->filename = fname; 109 110 return blk_prepare_device(dev); 111 err_file: 112 os_close(fd); 113 err: 114 free(fname); 115 free(str); 116 return ret; 117 } 118 119 int host_get_dev_err(int devnum, struct blk_desc **blk_devp) 120 { 121 struct udevice *dev; 122 int ret; 123 124 ret = blk_get_device(IF_TYPE_HOST, devnum, &dev); 125 if (ret) 126 return ret; 127 *blk_devp = dev_get_uclass_platdata(dev); 128 129 return 0; 130 } 131 132 struct blk_desc *host_get_dev(int dev) 133 { 134 struct blk_desc *blk_dev; 135 136 if (host_get_dev_err(dev, &blk_dev)) 137 return NULL; 138 139 return blk_dev; 140 } 141 142 static const struct blk_ops sandbox_host_blk_ops = { 143 .read = host_block_read, 144 .write = host_block_write, 145 }; 146 147 U_BOOT_DRIVER(sandbox_host_blk) = { 148 .name = "sandbox_host_blk", 149 .id = UCLASS_BLK, 150 .ops = &sandbox_host_blk_ops, 151 .priv_auto_alloc_size = sizeof(struct host_block_dev), 152 }; 153