blob: b3ff3cd1fab921b2ed54cfb0ebcd5095ed41dda9 [file] [log] [blame]
// 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 <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),
};