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 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 120 bbootfs_mountroot(char *str __unused) 121 { 122 return (-1); 123 } 124 125 static int 126 bbootfs_unmountroot(void) 127 { 128 return (-1); 129 } 130 131 static int 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 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 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 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 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 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 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