aboutsummaryrefslogtreecommitdiff
path: root/drivers/block/host_dev.c
blob: b3ff3cd1fab921b2ed54cfb0ebcd5095ed41dda9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// 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),
};