1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12/*
13 * Copyright 2013 Joyent, Inc.  All rights reserved.
14 */
15
16#include <sys/bootconf.h>
17#include <sys/types.h>
18#include <sys/param.h>
19#include <sys/vnode.h>
20#include <sys/fs/ufs_fsdir.h>
21#include <sys/fs/ufs_fs.h>
22#include <sys/fs/ufs_inode.h>
23#include <sys/sysmacros.h>
24#include <sys/bootvfs.h>
25#include <sys/bootinfo.h>
26#include <sys/filep.h>
27
28#ifdef	_BOOT
29#include "../common/util.h"
30#else
31#include <sys/sunddi.h>
32#endif
33
34#define	MAX_FILES	MAX_BOOT_MODULES
35#define	MAX_FDS		256
36
37extern void *bkmem_alloc(size_t);
38extern void bkmem_free(void *, size_t);
39
40/*
41 * TODO: Replace these declarations with inclusion of the ordinary userland
42 * bootfs headers once they're available.
43 */
44typedef struct bfile {
45	char bf_name[MAXPATHLEN];
46	caddr_t bf_addr;
47	size_t bf_size;
48	struct bfile *bf_next;
49	uint64_t bf_ino;
50} bfile_t;
51
52typedef struct bf_fd {
53	bfile_t *fd_file;
54	off_t fd_pos;
55} bf_fd_t;
56
57static bfile_t *head;
58static uint_t init_done;
59static bf_fd_t fds[MAX_FDS];
60
61static char cpath[MAXPATHLEN];	/* For canonicalising filenames */
62
63static void bbootfs_closeall(int);
64
65static void
66canonicalise(const char *fn, char *out)
67{
68	const char *p;
69	char *q, *s;
70	char *last;
71	char *oc;
72	int is_slash = 0;
73	static char scratch[MAXPATHLEN];
74
75	if (fn == NULL) {
76		*out = '\0';
77		return;
78	}
79
80	/*
81	 * Remove leading slashes and condense all multiple slashes into one.
82	 */
83	p = fn;
84	while (*p == '/')
85		++p;
86
87	for (q = scratch; *p != '\0'; p++) {
88		if (*p == '/' && !is_slash) {
89			*q++ = '/';
90			is_slash = 1;
91		} else if (*p != '/') {
92			*q++ = *p;
93			is_slash = 0;
94		}
95	}
96	*q = '\0';
97
98	if (strncmp(scratch, "system/boot/", 12) == 0 ||
99	    strcmp(scratch, "system/boot") == 0) {
100		s = scratch + 12;
101	} else {
102		s = scratch;
103	}
104
105	for (last = strsep(&s, "/"), q = oc = out; last != NULL;
106	    last = strsep(&s, "/")) {
107		if (strcmp(last, ".") == 0)
108			continue;
109		if (strcmp(last, "..") == 0) {
110			for (oc = q; oc > out && *oc != '/'; oc--)
111				;
112			q = oc;
113			continue;
114		}
115		if (q > out)
116			*q++ = '/';
117		q += snprintf(q, MAXPATHLEN - (q - out), "%s", last);
118	}
119
120	*q = '\0';
121}
122
123/* ARGSUSED */
124static int
125bbootfs_mountroot(char *str)
126{
127	return (-1);
128}
129
130static int
131bbootfs_unmountroot(void)
132{
133	return (-1);
134}
135
136static int
137bbootfs_init(void)
138{
139	bfile_t *fp;
140	char propname[32];
141	uint64_t propval;
142	uint_t i;
143
144	for (i = 0; i < MAX_FILES; i++) {
145		(void) snprintf(propname, sizeof (propname),
146		    "module-name-%u", i);
147		if (do_bsys_getproplen(NULL, propname) < 0)
148			break;
149
150		if ((fp = bkmem_alloc(sizeof (bfile_t))) == NULL) {
151			bbootfs_closeall(1);
152			return (-1);
153		}
154
155		(void) do_bsys_getprop(NULL, propname, cpath);
156		canonicalise(cpath, fp->bf_name);
157
158		(void) snprintf(propname, sizeof (propname),
159		    "module-addr-%u", i);
160		if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) {
161			bkmem_free(fp, sizeof (bfile_t));
162			continue;
163		}
164		(void) do_bsys_getprop(NULL, propname, &propval);
165		fp->bf_addr = (void *)(uintptr_t)propval;
166
167		(void) snprintf(propname, sizeof (propname),
168		    "module-size-%u", i);
169		if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) {
170			bkmem_free(fp, sizeof (bfile_t));
171			continue;
172		}
173		(void) do_bsys_getprop(NULL, propname, &propval);
174		fp->bf_size = (size_t)propval;
175		fp->bf_ino = i;
176
177		fp->bf_next = head;
178		head = fp;
179	}
180
181	return (0);
182}
183
184/*ARGSUSED*/
185static int
186bbootfs_open(char *fn, int flags)
187{
188	uint_t i;
189	bfile_t *fp;
190
191	if (!init_done) {
192		if (bbootfs_init() != 0)
193			return (-1);
194
195		init_done = 1;
196	}
197
198	canonicalise(fn, cpath);
199
200	for (fp = head; fp != NULL; fp = fp->bf_next) {
201		if (strcmp(fp->bf_name, cpath) == 0)
202			break;
203	}
204
205	if (fp == NULL)
206		return (-1);
207
208	for (i = 0; i < MAX_FDS; i++) {
209		if (fds[i].fd_file == NULL) {
210			fds[i].fd_file = fp;
211			fds[i].fd_pos = 0;
212			return (i);
213		}
214	}
215
216	return (-1);
217}
218
219static int
220bbootfs_close(int fd)
221{
222	if (fds[fd].fd_file == NULL)
223		return (-1);
224
225	fds[fd].fd_file = NULL;
226	fds[fd].fd_pos = 0;
227
228	return (0);
229}
230
231static ssize_t
232bbootfs_read(int fd, caddr_t buf, size_t size)
233{
234	ssize_t len;
235	bf_fd_t *fdp = &fds[fd];
236
237	if (fdp->fd_file == NULL)
238		return (-1);
239
240	if (fdp->fd_pos >= fdp->fd_file->bf_size)
241		return (-1);
242
243	if (fdp->fd_pos + size > fdp->fd_file->bf_size)
244		len = fdp->fd_file->bf_size - fdp->fd_pos;
245	else
246		len = size;
247
248	bcopy(fdp->fd_file->bf_addr + fdp->fd_pos, buf, len);
249
250	fdp->fd_pos += len;
251
252	return (len);
253}
254
255static off_t
256bbootfs_lseek(int fd, off_t addr, int whence)
257{
258	bf_fd_t *fdp = &fds[fd];
259
260	if (fdp->fd_file == NULL)
261		return (-1);
262
263	switch (whence) {
264	case SEEK_CUR:
265		fdp->fd_pos += addr;
266		break;
267	case SEEK_SET:
268		fdp->fd_pos = addr;
269		break;
270	case SEEK_END:
271		fdp->fd_pos = fdp->fd_file->bf_size;
272		break;
273	default:
274		return (-1);
275	}
276
277	return (0);
278}
279
280static int
281bbootfs_fstat(int fd, struct bootstat *bsp)
282{
283	bf_fd_t *fdp = &fds[fd];
284
285	if (fdp->fd_file == NULL)
286		return (-1);
287
288	bsp->st_dev = 1;
289	bsp->st_ino = fdp->fd_file->bf_ino;
290	bsp->st_mode = 0444;
291	bsp->st_nlink = 1;
292	bsp->st_uid = bsp->st_gid = 0;
293	bsp->st_rdev = 0;
294	bsp->st_size = fdp->fd_file->bf_size;
295	bsp->st_blksize = 1;
296	bsp->st_blocks = fdp->fd_file->bf_size;
297	(void) strcpy(bsp->st_fstype, "bootfs");
298
299	return (0);
300}
301
302/* ARGSUSED */
303static void
304bbootfs_closeall(int flag)
305{
306	bfile_t *fp;
307
308	while (head != NULL) {
309		fp = head;
310		head = head->bf_next;
311
312		bkmem_free(fp, sizeof (bfile_t));
313	}
314
315	init_done = 0;
316}
317
318struct boot_fs_ops bbootfs_ops = {
319	"bootfs",
320	bbootfs_mountroot,
321	bbootfs_unmountroot,
322	bbootfs_open,
323	bbootfs_close,
324	bbootfs_read,
325	bbootfs_lseek,
326	bbootfs_fstat,
327	bbootfs_closeall,
328	NULL
329};
330