| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Driver for sandbox host interface, used to access files on the host which |
| * contain partitions and filesystem |
| * |
| * Copyright 2022 Google LLC |
| * Written by Simon Glass <sjg@chromium.org> |
| */ |
| |
| #define LOG_CATEGORY UCLASS_HOST |
| |
| #include <common.h> |
| #include <blk.h> |
| #include <bootdev.h> |
| #include <dm.h> |
| #include <log.h> |
| #include <malloc.h> |
| #include <os.h> |
| #include <sandbox_host.h> |
| #include <dm/device-internal.h> |
| |
| static int host_sb_attach_file(struct udevice *dev, const char *filename) |
| { |
| struct host_sb_plat *plat = dev_get_plat(dev); |
| struct blk_desc *desc; |
| struct udevice *blk; |
| int ret, fd; |
| off_t size; |
| char *fname; |
| |
| if (!filename) |
| return -EINVAL; |
| |
| if (plat->fd) |
| return log_msg_ret("fd", -EEXIST); |
| |
| /* Sanity check that host_sb_bind() has been used */ |
| ret = blk_find_from_parent(dev, &blk); |
| if (ret) |
| return ret; |
| |
| fd = os_open(filename, OS_O_RDWR); |
| if (fd == -1) { |
| printf("Failed to access host backing file '%s', trying read-only\n", |
| filename); |
| fd = os_open(filename, OS_O_RDONLY); |
| if (fd == -1) { |
| printf("- still failed\n"); |
| return log_msg_ret("open", -ENOENT); |
| } |
| } |
| |
| fname = strdup(filename); |
| if (!fname) { |
| ret = -ENOMEM; |
| goto err_fname; |
| } |
| |
| size = os_filesize(fd); |
| desc = dev_get_uclass_plat(blk); |
| if (size % desc->blksz) { |
| printf("The size of host backing file '%s' is not multiple of " |
| "the device block size\n", filename); |
| ret = -EINVAL; |
| goto err_fname; |
| } |
| desc->lba = size / desc->blksz; |
| |
| /* write this in last, when nothing can go wrong */ |
| plat = dev_get_plat(dev); |
| plat->fd = fd; |
| plat->filename = fname; |
| |
| return 0; |
| |
| err_fname: |
| os_close(fd); |
| |
| return ret; |
| } |
| |
| static int host_sb_detach_file(struct udevice *dev) |
| { |
| struct host_sb_plat *plat = dev_get_plat(dev); |
| int ret; |
| |
| if (!plat->fd) |
| return log_msg_ret("fd", -ENOENT); |
| |
| ret = device_remove(dev, DM_REMOVE_NORMAL); |
| if (ret) |
| return log_msg_ret("rem", ret); |
| |
| /* Unbind all children */ |
| ret = device_chld_unbind(dev, NULL); |
| if (ret) |
| return log_msg_ret("unb", ret); |
| |
| os_close(plat->fd); |
| plat->fd = 0; |
| free(plat->filename); |
| free(plat->label); |
| |
| return 0; |
| } |
| |
| static int host_sb_bind(struct udevice *dev) |
| { |
| struct udevice *blk, *bdev; |
| struct blk_desc *desc; |
| int ret; |
| |
| ret = blk_create_devicef(dev, "sandbox_host_blk", "blk", UCLASS_HOST, |
| dev_seq(dev), DEFAULT_BLKSZ, 0, &blk); |
| if (ret) |
| return log_msg_ret("blk", ret); |
| |
| desc = dev_get_uclass_plat(blk); |
| snprintf(desc->vendor, BLK_VEN_SIZE, "U-Boot"); |
| snprintf(desc->product, BLK_PRD_SIZE, "hostfile"); |
| snprintf(desc->revision, BLK_REV_SIZE, "1.0"); |
| |
| if (CONFIG_IS_ENABLED(BOOTSTD)) { |
| ret = bootdev_bind(dev, "host_bootdev", "bootdev", &bdev); |
| if (ret) |
| return log_msg_ret("bd", ret); |
| } |
| |
| return 0; |
| } |
| |
| static struct host_ops host_sb_ops = { |
| .attach_file = host_sb_attach_file, |
| .detach_file = host_sb_detach_file, |
| }; |
| |
| static const struct udevice_id host_ids[] = { |
| { .compatible = "sandbox,host" }, |
| { } |
| }; |
| |
| U_BOOT_DRIVER(host_sb_drv) = { |
| .name = "host_sb_drv", |
| .id = UCLASS_HOST, |
| .of_match = host_ids, |
| .ops = &host_sb_ops, |
| .bind = host_sb_bind, |
| .plat_auto = sizeof(struct host_sb_plat), |
| }; |