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
37 extern void *bkmem_alloc(size_t);
38 extern 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 */
44 typedef 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
52 typedef struct bf_fd {
53 bfile_t *fd_file;
54 off_t fd_pos;
55 } bf_fd_t;
56
57 static bfile_t *head;
58 static uint_t init_done;
59 static bf_fd_t fds[MAX_FDS];
60
61 static char cpath[MAXPATHLEN]; /* For canonicalising filenames */
62
63 static void bbootfs_closeall(int);
64
65 static void
canonicalise(const char * fn,char * out)66 canonicalise(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 */
124 static int
bbootfs_mountroot(char * str)125 bbootfs_mountroot(char *str)
126 {
127 return (-1);
128 }
129
130 static int
bbootfs_unmountroot(void)131 bbootfs_unmountroot(void)
132 {
133 return (-1);
134 }
135
136 static int
bbootfs_init(void)137 bbootfs_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*/
185 static int
bbootfs_open(char * fn,int flags)186 bbootfs_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
219 static int
bbootfs_close(int fd)220 bbootfs_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
231 static ssize_t
bbootfs_read(int fd,caddr_t buf,size_t size)232 bbootfs_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
255 static off_t
bbootfs_lseek(int fd,off_t addr,int whence)256 bbootfs_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
280 static int
bbootfs_fstat(int fd,struct bootstat * bsp)281 bbootfs_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 */
303 static void
bbootfs_closeall(int flag)304 bbootfs_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
318 struct 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