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