1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Driver for sandbox host interface, used to access files on the host which
4  * contain partitions and filesystem
5  *
6  * Copyright 2022 Google LLC
7  * Written by Simon Glass <sjg@chromium.org>
8  */
9 
10 #define LOG_CATEGORY UCLASS_HOST
11 
12 #include <blk.h>
13 #include <bootdev.h>
14 #include <dm.h>
15 #include <log.h>
16 #include <malloc.h>
17 #include <os.h>
18 #include <sandbox_host.h>
19 #include <dm/device-internal.h>
20 
host_sb_attach_file(struct udevice * dev,const char * filename)21 static int host_sb_attach_file(struct udevice *dev, const char *filename)
22 {
23 	struct host_sb_plat *plat = dev_get_plat(dev);
24 	struct blk_desc *desc;
25 	struct udevice *blk;
26 	int ret, fd;
27 	off_t size;
28 	char *fname;
29 
30 	if (!filename)
31 		return -EINVAL;
32 
33 	if (plat->fd)
34 		return log_msg_ret("fd", -EEXIST);
35 
36 	/* Sanity check that host_sb_bind() has been used */
37 	ret = blk_find_from_parent(dev, &blk);
38 	if (ret)
39 		return ret;
40 
41 	fd = os_open(filename, OS_O_RDWR);
42 	if (fd == -1) {
43 		printf("Failed to access host backing file '%s', trying read-only\n",
44 		       filename);
45 		fd = os_open(filename, OS_O_RDONLY);
46 		if (fd == -1) {
47 			printf("- still failed\n");
48 			return log_msg_ret("open", -ENOENT);
49 		}
50 	}
51 
52 	fname = strdup(filename);
53 	if (!fname) {
54 		ret = -ENOMEM;
55 		goto err_fname;
56 	}
57 
58 	size = os_filesize(fd);
59 	desc = dev_get_uclass_plat(blk);
60 	if (size % desc->blksz) {
61 		printf("The size of host backing file '%s' is not multiple of "
62 		       "the device block size\n", filename);
63 		ret = -EINVAL;
64 		goto err_fname;
65 	}
66 	desc->lba = size / desc->blksz;
67 
68 	/* write this in last, when nothing can go wrong */
69 	plat = dev_get_plat(dev);
70 	plat->fd = fd;
71 	plat->filename = fname;
72 
73 	return 0;
74 
75 err_fname:
76 	os_close(fd);
77 
78 	return ret;
79 }
80 
host_sb_detach_file(struct udevice * dev)81 static int host_sb_detach_file(struct udevice *dev)
82 {
83 	struct host_sb_plat *plat = dev_get_plat(dev);
84 	int ret;
85 
86 	if (!plat->fd)
87 		return log_msg_ret("fd", -ENOENT);
88 
89 	ret = device_remove(dev, DM_REMOVE_NORMAL);
90 	if (ret)
91 		return log_msg_ret("rem", ret);
92 
93 	/* Unbind all children */
94 	ret = device_chld_unbind(dev, NULL);
95 	if (ret)
96 		return log_msg_ret("unb", ret);
97 
98 	os_close(plat->fd);
99 	plat->fd = 0;
100 	free(plat->filename);
101 	free(plat->label);
102 
103 	return 0;
104 }
105 
host_sb_bind(struct udevice * dev)106 static int host_sb_bind(struct udevice *dev)
107 {
108 	struct udevice *blk, *bdev;
109 	struct blk_desc *desc;
110 	int ret;
111 
112 	ret = blk_create_devicef(dev, "sandbox_host_blk", "blk", UCLASS_HOST,
113 				 dev_seq(dev), DEFAULT_BLKSZ, 0, &blk);
114 	if (ret)
115 		return log_msg_ret("blk", ret);
116 
117 	desc = dev_get_uclass_plat(blk);
118 	snprintf(desc->vendor, BLK_VEN_SIZE, "U-Boot");
119 	snprintf(desc->product, BLK_PRD_SIZE, "hostfile");
120 	snprintf(desc->revision, BLK_REV_SIZE, "1.0");
121 
122 	if (CONFIG_IS_ENABLED(BOOTSTD)) {
123 		ret = bootdev_bind(dev, "host_bootdev", "bootdev", &bdev);
124 		if (ret)
125 			return log_msg_ret("bd", ret);
126 	}
127 
128 	return 0;
129 }
130 
131 static struct host_ops host_sb_ops = {
132 	.attach_file	= host_sb_attach_file,
133 	.detach_file	= host_sb_detach_file,
134 };
135 
136 static const struct udevice_id host_ids[] = {
137 	{ .compatible = "sandbox,host" },
138 	{ }
139 };
140 
141 U_BOOT_DRIVER(host_sb_drv) = {
142 	.name		= "host_sb_drv",
143 	.id		= UCLASS_HOST,
144 	.of_match	= host_ids,
145 	.ops		= &host_sb_ops,
146 	.bind		= host_sb_bind,
147 	.plat_auto	= sizeof(struct host_sb_plat),
148 };
149