| /* |
| * This file and its contents are supplied under the terms of the |
| * Common Development and Distribution License ("CDDL"), version 1.0. |
| * You may only use this file in accordance with the terms of version |
| * 1.0 of the CDDL. |
| * |
| * A full copy of the text of the CDDL should have accompanied this |
| * source. A copy of the CDDL is also available via the Internet at |
| * http://www.illumos.org/license/CDDL. |
| */ |
| |
| /* |
| * Copyright 2013 Joyent, Inc. All rights reserved. |
| */ |
| |
| #include <sys/bootconf.h> |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/vnode.h> |
| #include <sys/fs/ufs_fsdir.h> |
| #include <sys/fs/ufs_fs.h> |
| #include <sys/fs/ufs_inode.h> |
| #include <sys/sysmacros.h> |
| #include <sys/bootvfs.h> |
| #include <sys/bootinfo.h> |
| #include <sys/filep.h> |
| |
| #ifdef _BOOT |
| #include "../common/util.h" |
| #else |
| #include <sys/sunddi.h> |
| #endif |
| |
| #define MAX_FILES MAX_BOOT_MODULES |
| #define MAX_FDS 256 |
| |
| extern void *bkmem_alloc(size_t); |
| extern void bkmem_free(void *, size_t); |
| |
| /* |
| * TODO: Replace these declarations with inclusion of the ordinary userland |
| * bootfs headers once they're available. |
| */ |
| typedef struct bfile { |
| char bf_name[MAXPATHLEN]; |
| caddr_t bf_addr; |
| size_t bf_size; |
| struct bfile *bf_next; |
| uint64_t bf_ino; |
| } bfile_t; |
| |
| typedef struct bf_fd { |
| bfile_t *fd_file; |
| off_t fd_pos; |
| } bf_fd_t; |
| |
| static bfile_t *head; |
| static uint_t init_done; |
| static bf_fd_t fds[MAX_FDS]; |
| |
| static char cpath[MAXPATHLEN]; /* For canonicalising filenames */ |
| |
| static void bbootfs_closeall(int); |
| |
| static void |
| canonicalise(const char *fn, char *out) |
| { |
| const char *p; |
| char *q, *s; |
| char *last; |
| char *oc; |
| int is_slash = 0; |
| static char scratch[MAXPATHLEN]; |
| |
| if (fn == NULL) { |
| *out = '\0'; |
| return; |
| } |
| |
| /* |
| * Remove leading slashes and condense all multiple slashes into one. |
| */ |
| p = fn; |
| while (*p == '/') |
| ++p; |
| |
| for (q = scratch; *p != '\0'; p++) { |
| if (*p == '/' && !is_slash) { |
| *q++ = '/'; |
| is_slash = 1; |
| } else if (*p != '/') { |
| *q++ = *p; |
| is_slash = 0; |
| } |
| } |
| *q = '\0'; |
| |
| if (strncmp(scratch, "system/boot/", 12) == 0 || |
| strcmp(scratch, "system/boot") == 0) { |
| s = scratch + 12; |
| } else { |
| s = scratch; |
| } |
| |
| for (last = strsep(&s, "/"), q = oc = out; last != NULL; |
| last = strsep(&s, "/")) { |
| if (strcmp(last, ".") == 0) |
| continue; |
| if (strcmp(last, "..") == 0) { |
| for (oc = q; oc > out && *oc != '/'; oc--) |
| ; |
| q = oc; |
| continue; |
| } |
| if (q > out) |
| *q++ = '/'; |
| q += snprintf(q, MAXPATHLEN - (q - out), "%s", last); |
| } |
| |
| *q = '\0'; |
| } |
| |
| /* ARGSUSED */ |
| static int |
| bbootfs_mountroot(char *str) |
| { |
| return (-1); |
| } |
| |
| static int |
| bbootfs_unmountroot(void) |
| { |
| return (-1); |
| } |
| |
| static int |
| bbootfs_init(void) |
| { |
| bfile_t *fp; |
| char propname[32]; |
| uint64_t propval; |
| uint_t i; |
| |
| for (i = 0; i < MAX_FILES; i++) { |
| (void) snprintf(propname, sizeof (propname), |
| "module-name-%u", i); |
| if (do_bsys_getproplen(NULL, propname) < 0) |
| break; |
| |
| if ((fp = bkmem_alloc(sizeof (bfile_t))) == NULL) { |
| bbootfs_closeall(1); |
| return (-1); |
| } |
| |
| (void) do_bsys_getprop(NULL, propname, cpath); |
| canonicalise(cpath, fp->bf_name); |
| |
| (void) snprintf(propname, sizeof (propname), |
| "module-addr-%u", i); |
| if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) { |
| bkmem_free(fp, sizeof (bfile_t)); |
| continue; |
| } |
| (void) do_bsys_getprop(NULL, propname, &propval); |
| fp->bf_addr = (void *)(uintptr_t)propval; |
| |
| (void) snprintf(propname, sizeof (propname), |
| "module-size-%u", i); |
| if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) { |
| bkmem_free(fp, sizeof (bfile_t)); |
| continue; |
| } |
| (void) do_bsys_getprop(NULL, propname, &propval); |
| fp->bf_size = (size_t)propval; |
| fp->bf_ino = i; |
| |
| fp->bf_next = head; |
| head = fp; |
| } |
| |
| return (0); |
| } |
| |
| /*ARGSUSED*/ |
| static int |
| bbootfs_open(char *fn, int flags) |
| { |
| uint_t i; |
| bfile_t *fp; |
| |
| if (!init_done) { |
| if (bbootfs_init() != 0) |
| return (-1); |
| |
| init_done = 1; |
| } |
| |
| canonicalise(fn, cpath); |
| |
| for (fp = head; fp != NULL; fp = fp->bf_next) { |
| if (strcmp(fp->bf_name, cpath) == 0) |
| break; |
| } |
| |
| if (fp == NULL) |
| return (-1); |
| |
| for (i = 0; i < MAX_FDS; i++) { |
| if (fds[i].fd_file == NULL) { |
| fds[i].fd_file = fp; |
| fds[i].fd_pos = 0; |
| return (i); |
| } |
| } |
| |
| return (-1); |
| } |
| |
| static int |
| bbootfs_close(int fd) |
| { |
| if (fds[fd].fd_file == NULL) |
| return (-1); |
| |
| fds[fd].fd_file = NULL; |
| fds[fd].fd_pos = 0; |
| |
| return (0); |
| } |
| |
| static ssize_t |
| bbootfs_read(int fd, caddr_t buf, size_t size) |
| { |
| ssize_t len; |
| bf_fd_t *fdp = &fds[fd]; |
| |
| if (fdp->fd_file == NULL) |
| return (-1); |
| |
| if (fdp->fd_pos >= fdp->fd_file->bf_size) |
| return (-1); |
| |
| if (fdp->fd_pos + size > fdp->fd_file->bf_size) |
| len = fdp->fd_file->bf_size - fdp->fd_pos; |
| else |
| len = size; |
| |
| bcopy(fdp->fd_file->bf_addr + fdp->fd_pos, buf, len); |
| |
| fdp->fd_pos += len; |
| |
| return (len); |
| } |
| |
| static off_t |
| bbootfs_lseek(int fd, off_t addr, int whence) |
| { |
| bf_fd_t *fdp = &fds[fd]; |
| |
| if (fdp->fd_file == NULL) |
| return (-1); |
| |
| switch (whence) { |
| case SEEK_CUR: |
| fdp->fd_pos += addr; |
| break; |
| case SEEK_SET: |
| fdp->fd_pos = addr; |
| break; |
| case SEEK_END: |
| fdp->fd_pos = fdp->fd_file->bf_size; |
| break; |
| default: |
| return (-1); |
| } |
| |
| return (0); |
| } |
| |
| static int |
| bbootfs_fstat(int fd, struct bootstat *bsp) |
| { |
| bf_fd_t *fdp = &fds[fd]; |
| |
| if (fdp->fd_file == NULL) |
| return (-1); |
| |
| bsp->st_dev = 1; |
| bsp->st_ino = fdp->fd_file->bf_ino; |
| bsp->st_mode = 0444; |
| bsp->st_nlink = 1; |
| bsp->st_uid = bsp->st_gid = 0; |
| bsp->st_rdev = 0; |
| bsp->st_size = fdp->fd_file->bf_size; |
| bsp->st_blksize = 1; |
| bsp->st_blocks = fdp->fd_file->bf_size; |
| (void) strcpy(bsp->st_fstype, "bootfs"); |
| |
| return (0); |
| } |
| |
| /* ARGSUSED */ |
| static void |
| bbootfs_closeall(int flag) |
| { |
| bfile_t *fp; |
| |
| while (head != NULL) { |
| fp = head; |
| head = head->bf_next; |
| |
| bkmem_free(fp, sizeof (bfile_t)); |
| } |
| |
| init_done = 0; |
| } |
| |
| struct boot_fs_ops bbootfs_ops = { |
| "bootfs", |
| bbootfs_mountroot, |
| bbootfs_unmountroot, |
| bbootfs_open, |
| bbootfs_close, |
| bbootfs_read, |
| bbootfs_lseek, |
| bbootfs_fstat, |
| bbootfs_closeall, |
| NULL |
| }; |