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